Dies ist das Repository meines kleinen Portfolios.
Im Hintergrund läuft eine Planetensimulation, geschrieben in JavaScript und Three.js.
Die zu sehenden Texturen stammen von:
https://www.solarsystemscope.com/textures/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
9.2 KiB
387 lines
9.2 KiB
import * as THREE from 'three'; |
|
|
|
const PINCH_MAX = 0.05; |
|
const PINCH_THRESHOLD = 0.02; |
|
const PINCH_MIN = 0.01; |
|
const POINTER_ADVANCE_MAX = 0.02; |
|
const POINTER_OPACITY_MAX = 1; |
|
const POINTER_OPACITY_MIN = 0.4; |
|
const POINTER_FRONT_RADIUS = 0.002; |
|
const POINTER_REAR_RADIUS = 0.01; |
|
const POINTER_REAR_RADIUS_MIN = 0.003; |
|
const POINTER_LENGTH = 0.035; |
|
const POINTER_SEGMENTS = 16; |
|
const POINTER_RINGS = 12; |
|
const POINTER_HEMISPHERE_ANGLE = 110; |
|
const YAXIS = new THREE.Vector3( 0, 1, 0 ); |
|
const ZAXIS = new THREE.Vector3( 0, 0, 1 ); |
|
|
|
const CURSOR_RADIUS = 0.02; |
|
const CURSOR_MAX_DISTANCE = 1.5; |
|
|
|
class OculusHandPointerModel extends THREE.Object3D { |
|
|
|
constructor( hand, controller ) { |
|
|
|
super(); |
|
|
|
this.hand = hand; |
|
this.controller = controller; |
|
this.motionController = null; |
|
this.envMap = null; |
|
|
|
this.mesh = null; |
|
|
|
this.pointerGeometry = null; |
|
this.pointerMesh = null; |
|
this.pointerObject = null; |
|
|
|
this.pinched = false; |
|
this.attached = false; |
|
|
|
this.cursorObject = null; |
|
|
|
this.raycaster = null; |
|
|
|
hand.addEventListener( 'connected', ( event ) => { |
|
|
|
const xrInputSource = event.data; |
|
if ( xrInputSource.hand ) { |
|
|
|
this.visible = true; |
|
this.xrInputSource = xrInputSource; |
|
|
|
this.createPointer(); |
|
|
|
} |
|
|
|
} ); |
|
|
|
} |
|
|
|
_drawVerticesRing( vertices, baseVector, ringIndex ) { |
|
|
|
const segmentVector = baseVector.clone(); |
|
for ( var i = 0; i < POINTER_SEGMENTS; i ++ ) { |
|
|
|
segmentVector.applyAxisAngle( ZAXIS, ( Math.PI * 2 ) / POINTER_SEGMENTS ); |
|
const vid = ringIndex * POINTER_SEGMENTS + i; |
|
vertices[ 3 * vid ] = segmentVector.x; |
|
vertices[ 3 * vid + 1 ] = segmentVector.y; |
|
vertices[ 3 * vid + 2 ] = segmentVector.z; |
|
|
|
} |
|
|
|
} |
|
|
|
_updatePointerVertices( rearRadius ) { |
|
|
|
const vertices = this.pointerGeometry.attributes.position.array; |
|
// first ring for front face |
|
const frontFaceBase = new THREE.Vector3( |
|
POINTER_FRONT_RADIUS, |
|
0, |
|
- 1 * ( POINTER_LENGTH - rearRadius ) |
|
); |
|
this._drawVerticesRing( vertices, frontFaceBase, 0 ); |
|
|
|
// rings for rear hemisphere |
|
const rearBase = new THREE.Vector3( |
|
Math.sin( ( Math.PI * POINTER_HEMISPHERE_ANGLE ) / 180 ) * rearRadius, |
|
Math.cos( ( Math.PI * POINTER_HEMISPHERE_ANGLE ) / 180 ) * rearRadius, |
|
0 |
|
); |
|
for ( var i = 0; i < POINTER_RINGS; i ++ ) { |
|
|
|
this._drawVerticesRing( vertices, rearBase, i + 1 ); |
|
rearBase.applyAxisAngle( |
|
YAXIS, |
|
( Math.PI * POINTER_HEMISPHERE_ANGLE ) / 180 / ( POINTER_RINGS * - 2 ) |
|
); |
|
|
|
} |
|
|
|
// front and rear face center vertices |
|
const frontCenterIndex = POINTER_SEGMENTS * ( 1 + POINTER_RINGS ); |
|
const rearCenterIndex = POINTER_SEGMENTS * ( 1 + POINTER_RINGS ) + 1; |
|
const frontCenter = new THREE.Vector3( |
|
0, |
|
0, |
|
- 1 * ( POINTER_LENGTH - rearRadius ) |
|
); |
|
vertices[ frontCenterIndex * 3 ] = frontCenter.x; |
|
vertices[ frontCenterIndex * 3 + 1 ] = frontCenter.y; |
|
vertices[ frontCenterIndex * 3 + 2 ] = frontCenter.z; |
|
const rearCenter = new THREE.Vector3( 0, 0, rearRadius ); |
|
vertices[ rearCenterIndex * 3 ] = rearCenter.x; |
|
vertices[ rearCenterIndex * 3 + 1 ] = rearCenter.y; |
|
vertices[ rearCenterIndex * 3 + 2 ] = rearCenter.z; |
|
|
|
this.pointerGeometry.setAttribute( |
|
'position', |
|
new THREE.Float32BufferAttribute( vertices, 3 ) |
|
); |
|
// verticesNeedUpdate = true; |
|
|
|
} |
|
|
|
createPointer() { |
|
|
|
var i, j; |
|
const vertices = new Array( |
|
( ( POINTER_RINGS + 1 ) * POINTER_SEGMENTS + 2 ) * 3 |
|
).fill( 0 ); |
|
// const vertices = []; |
|
const indices = []; |
|
this.pointerGeometry = new THREE.BufferGeometry(); |
|
|
|
this.pointerGeometry.setAttribute( |
|
'position', |
|
new THREE.Float32BufferAttribute( vertices, 3 ) |
|
); |
|
|
|
this._updatePointerVertices( POINTER_REAR_RADIUS ); |
|
|
|
// construct faces to connect rings |
|
for ( i = 0; i < POINTER_RINGS; i ++ ) { |
|
|
|
for ( j = 0; j < POINTER_SEGMENTS - 1; j ++ ) { |
|
|
|
indices.push( |
|
i * POINTER_SEGMENTS + j, |
|
i * POINTER_SEGMENTS + j + 1, |
|
( i + 1 ) * POINTER_SEGMENTS + j |
|
); |
|
indices.push( |
|
i * POINTER_SEGMENTS + j + 1, |
|
( i + 1 ) * POINTER_SEGMENTS + j + 1, |
|
( i + 1 ) * POINTER_SEGMENTS + j |
|
); |
|
|
|
} |
|
|
|
indices.push( |
|
( i + 1 ) * POINTER_SEGMENTS - 1, |
|
i * POINTER_SEGMENTS, |
|
( i + 2 ) * POINTER_SEGMENTS - 1 |
|
); |
|
indices.push( |
|
i * POINTER_SEGMENTS, |
|
( i + 1 ) * POINTER_SEGMENTS, |
|
( i + 2 ) * POINTER_SEGMENTS - 1 |
|
); |
|
|
|
} |
|
|
|
// construct front and rear face |
|
const frontCenterIndex = POINTER_SEGMENTS * ( 1 + POINTER_RINGS ); |
|
const rearCenterIndex = POINTER_SEGMENTS * ( 1 + POINTER_RINGS ) + 1; |
|
|
|
for ( i = 0; i < POINTER_SEGMENTS - 1; i ++ ) { |
|
|
|
indices.push( frontCenterIndex, i + 1, i ); |
|
indices.push( |
|
rearCenterIndex, |
|
i + POINTER_SEGMENTS * POINTER_RINGS, |
|
i + POINTER_SEGMENTS * POINTER_RINGS + 1 |
|
); |
|
|
|
} |
|
|
|
indices.push( frontCenterIndex, 0, POINTER_SEGMENTS - 1 ); |
|
indices.push( |
|
rearCenterIndex, |
|
POINTER_SEGMENTS * ( POINTER_RINGS + 1 ) - 1, |
|
POINTER_SEGMENTS * POINTER_RINGS |
|
); |
|
|
|
const material = new THREE.MeshBasicMaterial(); |
|
material.transparent = true; |
|
material.opacity = POINTER_OPACITY_MIN; |
|
|
|
this.pointerGeometry.setIndex( indices ); |
|
|
|
this.pointerMesh = new THREE.Mesh( this.pointerGeometry, material ); |
|
|
|
this.pointerMesh.position.set( 0, 0, - 1 * POINTER_REAR_RADIUS ); |
|
this.pointerObject = new THREE.Object3D(); |
|
this.pointerObject.add( this.pointerMesh ); |
|
|
|
this.raycaster = new THREE.Raycaster(); |
|
|
|
// create cursor |
|
const cursorGeometry = new THREE.SphereGeometry( CURSOR_RADIUS, 10, 10 ); |
|
const cursorMaterial = new THREE.MeshBasicMaterial(); |
|
cursorMaterial.transparent = true; |
|
cursorMaterial.opacity = POINTER_OPACITY_MIN; |
|
|
|
this.cursorObject = new THREE.Mesh( cursorGeometry, cursorMaterial ); |
|
this.pointerObject.add( this.cursorObject ); |
|
|
|
this.add( this.pointerObject ); |
|
|
|
} |
|
|
|
_updateRaycaster() { |
|
|
|
if ( this.raycaster ) { |
|
|
|
const pointerMatrix = this.pointerObject.matrixWorld; |
|
const tempMatrix = new THREE.Matrix4(); |
|
tempMatrix.identity().extractRotation( pointerMatrix ); |
|
this.raycaster.ray.origin.setFromMatrixPosition( pointerMatrix ); |
|
this.raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix ); |
|
|
|
} |
|
|
|
} |
|
|
|
_updatePointer() { |
|
|
|
this.pointerObject.visible = this.controller.visible; |
|
const indexTip = this.hand.joints[ 'index-finger-tip' ]; |
|
const thumbTip = this.hand.joints[ 'thumb-tip' ]; |
|
const distance = indexTip.position.distanceTo( thumbTip.position ); |
|
const position = indexTip.position |
|
.clone() |
|
.add( thumbTip.position ) |
|
.multiplyScalar( 0.5 ); |
|
this.pointerObject.position.copy( position ); |
|
this.pointerObject.quaternion.copy( this.controller.quaternion ); |
|
|
|
this.pinched = distance <= PINCH_THRESHOLD; |
|
|
|
const pinchScale = ( distance - PINCH_MIN ) / ( PINCH_MAX - PINCH_MIN ); |
|
const focusScale = ( distance - PINCH_MIN ) / ( PINCH_THRESHOLD - PINCH_MIN ); |
|
if ( pinchScale > 1 ) { |
|
|
|
this._updatePointerVertices( POINTER_REAR_RADIUS ); |
|
this.pointerMesh.position.set( 0, 0, - 1 * POINTER_REAR_RADIUS ); |
|
this.pointerMesh.material.opacity = POINTER_OPACITY_MIN; |
|
|
|
} else if ( pinchScale > 0 ) { |
|
|
|
const rearRadius = |
|
( POINTER_REAR_RADIUS - POINTER_REAR_RADIUS_MIN ) * pinchScale + |
|
POINTER_REAR_RADIUS_MIN; |
|
this._updatePointerVertices( rearRadius ); |
|
if ( focusScale < 1 ) { |
|
|
|
this.pointerMesh.position.set( |
|
0, |
|
0, |
|
- 1 * rearRadius - ( 1 - focusScale ) * POINTER_ADVANCE_MAX |
|
); |
|
this.pointerMesh.material.opacity = |
|
POINTER_OPACITY_MIN + |
|
( 1 - focusScale ) * ( POINTER_OPACITY_MAX - POINTER_OPACITY_MIN ); |
|
|
|
} else { |
|
|
|
this.pointerMesh.position.set( 0, 0, - 1 * rearRadius ); |
|
this.pointerMesh.material.opacity = POINTER_OPACITY_MIN; |
|
|
|
} |
|
|
|
} else { |
|
|
|
this._updatePointerVertices( POINTER_REAR_RADIUS_MIN ); |
|
this.pointerMesh.position.set( |
|
0, |
|
0, |
|
- 1 * POINTER_REAR_RADIUS_MIN - POINTER_ADVANCE_MAX |
|
); |
|
this.pointerMesh.material.opacity = POINTER_OPACITY_MAX; |
|
|
|
} |
|
|
|
this.cursorObject.material.opacity = this.pointerMesh.material.opacity; |
|
|
|
} |
|
|
|
updateMatrixWorld( force ) { |
|
|
|
super.updateMatrixWorld( force ); |
|
if ( this.pointerGeometry ) { |
|
|
|
this._updatePointer(); |
|
this._updateRaycaster(); |
|
|
|
} |
|
|
|
} |
|
|
|
isPinched() { |
|
|
|
return this.pinched; |
|
|
|
} |
|
|
|
setAttached( attached ) { |
|
|
|
this.attached = attached; |
|
|
|
} |
|
|
|
isAttached() { |
|
|
|
return this.attached; |
|
|
|
} |
|
|
|
intersectObject( object, recursive = true ) { |
|
|
|
if ( this.raycaster ) { |
|
|
|
return this.raycaster.intersectObject( object, recursive ); |
|
|
|
} |
|
|
|
} |
|
|
|
intersectObjects( objects, recursive = true ) { |
|
|
|
if ( this.raycaster ) { |
|
|
|
return this.raycaster.intersectObjects( objects, recursive ); |
|
|
|
} |
|
|
|
} |
|
|
|
checkIntersections( objects, recursive = false ) { |
|
|
|
if ( this.raycaster && ! this.attached ) { |
|
|
|
const intersections = this.raycaster.intersectObjects( objects, recursive ); |
|
const direction = new THREE.Vector3( 0, 0, - 1 ); |
|
if ( intersections.length > 0 ) { |
|
|
|
const intersection = intersections[ 0 ]; |
|
const distance = intersection.distance; |
|
this.cursorObject.position.copy( direction.multiplyScalar( distance ) ); |
|
|
|
} else { |
|
|
|
this.cursorObject.position.copy( direction.multiplyScalar( CURSOR_MAX_DISTANCE ) ); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
setCursor( distance ) { |
|
|
|
const direction = new THREE.Vector3( 0, 0, - 1 ); |
|
if ( this.raycaster && ! this.attached ) { |
|
|
|
this.cursorObject.position.copy( direction.multiplyScalar( distance ) ); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
export { OculusHandPointerModel };
|
|
|