diff --git a/examples/jsm/webxr/OculusHandModel.js b/examples/jsm/webxr/OculusHandModel.js index f3ddfe397874b7..a91e4a7306f86b 100644 --- a/examples/jsm/webxr/OculusHandModel.js +++ b/examples/jsm/webxr/OculusHandModel.js @@ -1,84 +1,122 @@ -import { Object3D, Sphere, Box3 } from "../../../build/three.module.js"; +import { Object3D, Sphere, Box3 } from '../../../build/three.module.js'; import { fetchProfile } from '../libs/motion-controllers.module.js'; -import { XRHandMeshModel } from "./XRHandMeshModel.js"; +import { XRHandMeshModel } from './XRHandMeshModel.js'; const DEFAULT_PROFILES_PATH = 'https://cdn.jsdelivr.net/npm/@webxr-input-profiles/assets@1.0/dist/profiles'; const DEFAULT_PROFILE = 'generic-hand'; -const POINTING_JOINT = "index-finger-tip"; +const POINTING_JOINT = 'index-finger-tip'; class OculusHandModel extends Object3D { - constructor(controller) { - super(); - - this.controller = controller; - this.motionController = null; - this.envMap = null; - - this.mesh = null; - - controller.addEventListener("connected", (event) => { - const xrInputSource = event.data; - if (xrInputSource.hand && !this.motionController) { - this.visible = true; - this.xrInputSource = xrInputSource; - fetchProfile(xrInputSource, DEFAULT_PROFILES_PATH, DEFAULT_PROFILE).then(({ profile, assetPath }) => { - this.motionController = new XRHandMeshModel( - this, - controller, - assetPath - ); - }).catch((err) => { - console.warn(err); - }); - } - }); - - controller.addEventListener("disconnected", () => { - this.clear(); - this.motionController = null; - }) - } - - updateMatrixWorld(force) { - super.updateMatrixWorld(force); - - if (this.motionController) { - this.motionController.updateMesh(); - } - } - - getPointerPosition() { - let indexFingerTip = this.controller.joints[POINTING_JOINT]; - if (indexFingerTip) { - return indexFingerTip.position; - } else { - return null; - } - } - - intersectBoxObject(boxObject) { - let pointerPosition = this.getPointerPosition(); - if (pointerPosition) { - let indexSphere = new Sphere(pointerPosition, TOUCH_RADIUS); - let box = new Box3().setFromObject(boxObject); - return indexSphere.intersectsBox(box); - } else { - return false; - } - } - - checkButton(button) { - if (this.intersectBoxObject(button)) { - button.onPress(); - } else { - button.onClear(); - } - - if (button.isPressed()) { - button.whilePressed(); - } - } + + constructor( controller ) { + + super(); + + this.controller = controller; + this.motionController = null; + this.envMap = null; + + this.mesh = null; + + controller.addEventListener( 'connected', ( event ) => { + + const xrInputSource = event.data; + if ( xrInputSource.hand && ! this.motionController ) { + + this.visible = true; + this.xrInputSource = xrInputSource; + fetchProfile( xrInputSource, DEFAULT_PROFILES_PATH, DEFAULT_PROFILE ).then( ( { profile, assetPath } ) => { + + this.motionController = new XRHandMeshModel( + this, + controller, + assetPath + ); + + } ).catch( ( err ) => { + + console.warn( err ); + + } ); + + } + + } ); + + controller.addEventListener( 'disconnected', () => { + + this.clear(); + this.motionController = null; + + } ); + + } + + updateMatrixWorld( force ) { + + super.updateMatrixWorld( force ); + + if ( this.motionController ) { + + this.motionController.updateMesh(); + + } + + } + + getPointerPosition() { + + const indexFingerTip = this.controller.joints[ POINTING_JOINT ]; + if ( indexFingerTip ) { + + return indexFingerTip.position; + + } else { + + return null; + + } + + } + + intersectBoxObject( boxObject ) { + + const pointerPosition = this.getPointerPosition(); + if ( pointerPosition ) { + + const indexSphere = new Sphere( pointerPosition, TOUCH_RADIUS ); + const box = new Box3().setFromObject( boxObject ); + return indexSphere.intersectsBox( box ); + + } else { + + return false; + + } + + } + + checkButton( button ) { + + if ( this.intersectBoxObject( button ) ) { + + button.onPress(); + + } else { + + button.onClear(); + + } + + if ( button.isPressed() ) { + + button.whilePressed(); + + } + + } + } export { OculusHandModel }; diff --git a/examples/jsm/webxr/OculusHandPointerModel.js b/examples/jsm/webxr/OculusHandPointerModel.js index c33848d4732cdf..7adef411bb6d3f 100644 --- a/examples/jsm/webxr/OculusHandPointerModel.js +++ b/examples/jsm/webxr/OculusHandPointerModel.js @@ -1,4 +1,4 @@ -import * as THREE from "../../../build/three.module.js"; +import * as THREE from '../../../build/three.module.js'; const PINCH_MAX = 0.05; const PINCH_THRESHOLD = 0.02; @@ -13,302 +13,375 @@ 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 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); - let 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 + + + 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 = + 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) { - if (this.raycaster) { - return this.raycaster.intersectObject(object); - } - } - - intersectObjects(objects) { - if (this.raycaster) { - return this.raycaster.intersectObjects(objects); - } - } - - checkIntersections(objects) { - if (this.raycaster && !this.attached) { - let intersections = this.raycaster.intersectObjects(objects); - let direction = new THREE.Vector3(0, 0, -1); - if (intersections.length > 0) { - let intersection = intersections[0]; - let distance = intersection.distance; - this.cursorObject.position.copy(direction.multiplyScalar(distance)); - } else { - this.cursorObject.position.copy(direction.multiplyScalar(CURSOR_MAX_DISTANCE)); - } - } - } - - setCursor(distance) { - let direction = new THREE.Vector3(0, 0, -1); - if (this.raycaster && !this.attached) { - this.cursorObject.position.copy(direction.multiplyScalar(distance)); - } - } + ( 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 ) { + + if ( this.raycaster ) { + + return this.raycaster.intersectObject( object ); + + } + + } + + intersectObjects( objects ) { + + if ( this.raycaster ) { + + return this.raycaster.intersectObjects( objects ); + + } + + } + + checkIntersections( objects ) { + + if ( this.raycaster && ! this.attached ) { + + const intersections = this.raycaster.intersectObjects( objects ); + 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 }; diff --git a/examples/jsm/webxr/Text2D.js b/examples/jsm/webxr/Text2D.js index 8cd32d140c3ee6..0fd1aca8760182 100644 --- a/examples/jsm/webxr/Text2D.js +++ b/examples/jsm/webxr/Text2D.js @@ -1,36 +1,38 @@ -import * as THREE from "../../../build/three.module.js"; +import * as THREE from '../../../build/three.module.js'; -function createText(message, height) { - const canvas = document.createElement("canvas"); - const context = canvas.getContext("2d"); - let metrics = null, - textHeight = 100; - context.font = "normal " + textHeight + "px Arial"; - metrics = context.measureText(message); - const textWidth = metrics.width; - canvas.width = textWidth; - canvas.height = textHeight; - context.font = "normal " + textHeight + "px Arial"; - context.textAlign = "center"; - context.textBaseline = "middle"; - context.fillStyle = "#ffffff"; - context.fillText(message, textWidth / 2, textHeight / 2); +function createText( message, height ) { + + const canvas = document.createElement( 'canvas' ); + const context = canvas.getContext( '2d' ); + let metrics = null; + const textHeight = 100; + context.font = 'normal ' + textHeight + 'px Arial'; + metrics = context.measureText( message ); + const textWidth = metrics.width; + canvas.width = textWidth; + canvas.height = textHeight; + context.font = 'normal ' + textHeight + 'px Arial'; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + context.fillStyle = '#ffffff'; + context.fillText( message, textWidth / 2, textHeight / 2 ); + + const texture = new THREE.Texture( canvas ); + texture.needsUpdate = true; + //var spriteAlignment = new THREE.Vector2(0,0) ; + const material = new THREE.MeshBasicMaterial( { + color: 0xffffff, + side: THREE.DoubleSide, + map: texture, + transparent: true, + } ); + const geometry = new THREE.PlaneGeometry( + ( height * textWidth ) / textHeight, + height + ); + const plane = new THREE.Mesh( geometry, material ); + return plane; - const texture = new THREE.Texture(canvas); - texture.needsUpdate = true; - //var spriteAlignment = new THREE.Vector2(0,0) ; - const material = new THREE.MeshBasicMaterial({ - color: 0xffffff, - side: THREE.DoubleSide, - map: texture, - transparent: true, - }); - const geometry = new THREE.PlaneGeometry( - (height * textWidth) / textHeight, - height - ); - let plane = new THREE.Mesh(geometry, material); - return plane; } export { createText }; diff --git a/examples/jsm/webxr/XRHandMeshModel.js b/examples/jsm/webxr/XRHandMeshModel.js index 48c13f58bc1c31..994338d7c391f1 100644 --- a/examples/jsm/webxr/XRHandMeshModel.js +++ b/examples/jsm/webxr/XRHandMeshModel.js @@ -2,82 +2,103 @@ import { GLTFLoader } from '../loaders/GLTFLoader.js'; class XRHandMeshModel { - constructor(handModel, controller, assetUrl) { - - this.controller = controller; - this.handModel = handModel; - - this.bones = []; - const loader = new GLTFLoader(); - - loader.setPath(''); - loader.load(assetUrl, gltf => { - const object = gltf.scene.children[0]; - this.handModel.add(object); - - const mesh = object.getObjectByProperty('type', 'SkinnedMesh'); - mesh.frustumCulled = false; - mesh.castShadow = true; - mesh.receiveShadow = true; - - const joints = [ - 'wrist', - 'thumb-metacarpal', - 'thumb-phalanx-proximal', - 'thumb-phalanx-distal', - 'thumb-tip', - 'index-finger-metacarpal', - 'index-finger-phalanx-proximal', - 'index-finger-phalanx-intermediate', - 'index-finger-phalanx-distal', - 'index-finger-tip', - 'middle-finger-metacarpal', - 'middle-finger-phalanx-proximal', - 'middle-finger-phalanx-intermediate', - 'middle-finger-phalanx-distal', - 'middle-finger-tip', - 'ring-finger-metacarpal', - 'ring-finger-phalanx-proximal', - 'ring-finger-phalanx-intermediate', - 'ring-finger-phalanx-distal', - 'ring-finger-tip', - 'pinky-finger-metacarpal', - 'pinky-finger-phalanx-proximal', - 'pinky-finger-phalanx-intermediate', - 'pinky-finger-phalanx-distal', - 'pinky-finger-tip', - ]; - - joints.forEach(jointName => { - const bone = object.getObjectByName(jointName); - if (bone !== undefined) { - bone.jointName = jointName; - } else { - console.warn(`Couldn't find ${jointName} in ${handedness} hand mesh`); - } - this.bones.push(bone); - }); - }); - } - - updateMesh() { - // XR Joints - const XRJoints = this.controller.joints; - for (let i = 0; i < this.bones.length; i++) { - const bone = this.bones[i]; - if (bone) { - const XRJoint = XRJoints[bone.jointName]; - if (XRJoint.visible) { - const position = XRJoint.position; - if (bone) { - bone.position.copy(position); - bone.quaternion.copy(XRJoint.quaternion); - // bone.scale.setScalar( XRJoint.jointRadius || defaultRadius ); - } - } - } - } - } + constructor( handModel, controller, assetUrl ) { + + this.controller = controller; + this.handModel = handModel; + + this.bones = []; + const loader = new GLTFLoader(); + + loader.setPath( '' ); + loader.load( assetUrl, gltf => { + + const object = gltf.scene.children[ 0 ]; + this.handModel.add( object ); + + const mesh = object.getObjectByProperty( 'type', 'SkinnedMesh' ); + mesh.frustumCulled = false; + mesh.castShadow = true; + mesh.receiveShadow = true; + + const joints = [ + 'wrist', + 'thumb-metacarpal', + 'thumb-phalanx-proximal', + 'thumb-phalanx-distal', + 'thumb-tip', + 'index-finger-metacarpal', + 'index-finger-phalanx-proximal', + 'index-finger-phalanx-intermediate', + 'index-finger-phalanx-distal', + 'index-finger-tip', + 'middle-finger-metacarpal', + 'middle-finger-phalanx-proximal', + 'middle-finger-phalanx-intermediate', + 'middle-finger-phalanx-distal', + 'middle-finger-tip', + 'ring-finger-metacarpal', + 'ring-finger-phalanx-proximal', + 'ring-finger-phalanx-intermediate', + 'ring-finger-phalanx-distal', + 'ring-finger-tip', + 'pinky-finger-metacarpal', + 'pinky-finger-phalanx-proximal', + 'pinky-finger-phalanx-intermediate', + 'pinky-finger-phalanx-distal', + 'pinky-finger-tip', + ]; + + joints.forEach( jointName => { + + const bone = object.getObjectByName( jointName ); + if ( bone !== undefined ) { + + bone.jointName = jointName; + + } else { + + console.warn( `Couldn't find ${jointName} in ${handedness} hand mesh` ); + + } + + this.bones.push( bone ); + + } ); + + } ); + + } + + updateMesh() { + + // XR Joints + const XRJoints = this.controller.joints; + for ( let i = 0; i < this.bones.length; i ++ ) { + + const bone = this.bones[ i ]; + if ( bone ) { + + const XRJoint = XRJoints[ bone.jointName ]; + if ( XRJoint.visible ) { + + const position = XRJoint.position; + if ( bone ) { + + bone.position.copy( position ); + bone.quaternion.copy( XRJoint.quaternion ); + // bone.scale.setScalar( XRJoint.jointRadius || defaultRadius ); + + } + + } + + } + + } + + } + } export { XRHandMeshModel }; diff --git a/examples/jsm/webxr/XRHandModelFactory.js b/examples/jsm/webxr/XRHandModelFactory.js index 38c24b3d17c654..a340c188a1d8f5 100644 --- a/examples/jsm/webxr/XRHandModelFactory.js +++ b/examples/jsm/webxr/XRHandModelFactory.js @@ -6,12 +6,12 @@ import { XRHandPrimitiveModel } from './XRHandPrimitiveModel.js'; -import { - XRHandMeshModel -} from "./XRHandMeshModel.js"; +import { + XRHandMeshModel +} from './XRHandMeshModel.js'; -import { - fetchProfile +import { + fetchProfile } from '../libs/motion-controllers.module.js'; const DEFAULT_PROFILES_PATH = 'https://cdn.jsdelivr.net/npm/@webxr-input-profiles/assets@1.0/dist/profiles'; @@ -84,15 +84,15 @@ class XRHandModelFactory { } else if ( profile === 'oculus' ) { - fetchProfile(xrInputSource, DEFAULT_PROFILES_PATH, DEFAULT_PROFILE).then(({ profile, assetPath }) => { - - handModel.motionController = new XRHandMeshModel( handModel, controller, assetPath); + fetchProfile( xrInputSource, DEFAULT_PROFILES_PATH, DEFAULT_PROFILE ).then( ( { profile, assetPath } ) => { + + handModel.motionController = new XRHandMeshModel( handModel, controller, assetPath ); - }).catch((err) => { + } ).catch( ( err ) => { - console.warn(err); + console.warn( err ); - }); + } ); }