浏览代码

Merge pull request #18238 from mrdoob/webxr

WebXRManager: Refactoring and added connected/disconnected controller events.
Mr.doob 5 年之前
父节点
当前提交
5b13bed97b

+ 0 - 1
examples/jsm/webxr/ARButton.js

@@ -33,7 +33,6 @@ var ARButton = {
 
 
 				currentSession.removeEventListener( 'end', onSessionEnded );
 				currentSession.removeEventListener( 'end', onSessionEnded );
 
 
-				renderer.xr.setSession( null );
 				button.textContent = 'START AR';
 				button.textContent = 'START AR';
 
 
 				currentSession = null;
 				currentSession = null;

+ 0 - 1
examples/jsm/webxr/VRButton.js

@@ -32,7 +32,6 @@ var VRButton = {
 
 
 				currentSession.removeEventListener( 'end', onSessionEnded );
 				currentSession.removeEventListener( 'end', onSessionEnded );
 
 
-				renderer.xr.setSession( null );
 				button.textContent = 'ENTER VR';
 				button.textContent = 'ENTER VR';
 
 
 				currentSession = null;
 				currentSession = null;

+ 41 - 8
examples/webxr_vr_ballshooter.html

@@ -103,23 +103,32 @@
 				controller1 = renderer.xr.getController( 0 );
 				controller1 = renderer.xr.getController( 0 );
 				controller1.addEventListener( 'selectstart', onSelectStart );
 				controller1.addEventListener( 'selectstart', onSelectStart );
 				controller1.addEventListener( 'selectend', onSelectEnd );
 				controller1.addEventListener( 'selectend', onSelectEnd );
+				controller1.addEventListener( 'connected', function ( event ) {
+
+					this.add( buildController( event.data ) );
+
+				} );
+				controller1.addEventListener( 'disconnected', function () {
+
+					this.remove( this.children[ 0 ] );
+
+				} );
 				scene.add( controller1 );
 				scene.add( controller1 );
 
 
 				controller2 = renderer.xr.getController( 1 );
 				controller2 = renderer.xr.getController( 1 );
 				controller2.addEventListener( 'selectstart', onSelectStart );
 				controller2.addEventListener( 'selectstart', onSelectStart );
 				controller2.addEventListener( 'selectend', onSelectEnd );
 				controller2.addEventListener( 'selectend', onSelectEnd );
-				scene.add( controller2 );
+				controller2.addEventListener( 'connected', function ( event ) {
 
 
-				// helpers
+					this.add( buildController( event.data ) );
 
 
-				var geometry = new THREE.BufferGeometry();
-				geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 0, 0, - 1 ], 3 ) );
-				geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( [ 0.5, 0.5, 0.5, 0, 0, 0 ], 3 ) );
+				} );
+				controller2.addEventListener( 'disconnected', function () {
 
 
-				var material = new THREE.LineBasicMaterial( { vertexColors: true, blending: THREE.AdditiveBlending } );
+					this.remove( this.children[ 0 ] );
 
 
-				controller1.add( new THREE.Line( geometry, material ) );
-				controller2.add( new THREE.Line( geometry, material ) );
+				} );
+				scene.add( controller2 );
 
 
 				//
 				//
 
 
@@ -127,6 +136,30 @@
 
 
 			}
 			}
 
 
+			function buildController( data ) {
+
+				switch ( data.targetRayMode ) {
+
+					case 'tracked-pointer':
+
+						var geometry = new THREE.BufferGeometry();
+						geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 0, 0, - 1 ], 3 ) );
+						geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( [ 0.5, 0.5, 0.5, 0, 0, 0 ], 3 ) );
+
+						var material = new THREE.LineBasicMaterial( { vertexColors: true, blending: THREE.AdditiveBlending } );
+
+						return new THREE.Line( geometry, material );
+
+					case 'gaze':
+
+						var geometry = new THREE.RingBufferGeometry( 0.02, 0.04, 32 ).translate( 0, 0, - 1 );
+						var material = new THREE.MeshBasicMaterial( { opacity: 0.5, transparent: true } );
+						return new THREE.Mesh( geometry, material );
+
+				}
+
+			}
+
 			function onWindowResize() {
 			function onWindowResize() {
 
 
 				camera.aspect = window.innerWidth / window.innerHeight;
 				camera.aspect = window.innerWidth / window.innerHeight;

+ 48 - 49
examples/webxr_vr_cubes.html

@@ -27,10 +27,9 @@
 			var camera, scene, raycaster, renderer;
 			var camera, scene, raycaster, renderer;
 
 
 			var room;
 			var room;
-			var isMouseDown = false;
 
 
+			var controller, tempMatrix = new THREE.Matrix4();
 			var INTERSECTED;
 			var INTERSECTED;
-			var crosshair;
 
 
 			init();
 			init();
 			animate();
 			animate();
@@ -47,22 +46,10 @@
 				camera.position.set( 0, 1.6, 3 );
 				camera.position.set( 0, 1.6, 3 );
 				scene.add( camera );
 				scene.add( camera );
 
 
-				crosshair = new THREE.Mesh(
-					new THREE.RingBufferGeometry( 0.02, 0.04, 32 ),
-					new THREE.MeshBasicMaterial( {
-						color: 0xffffff,
-						opacity: 0.5,
-						transparent: true
-					} )
-				);
-				crosshair.position.z = - 2;
-				camera.add( crosshair );
-
 				room = new THREE.LineSegments(
 				room = new THREE.LineSegments(
-					new BoxLineGeometry( 6, 6, 6, 10, 10, 10 ),
+					new BoxLineGeometry( 6, 6, 6, 10, 10, 10 ).translate( 0, 3, 0 ),
 					new THREE.LineBasicMaterial( { color: 0x808080 } )
 					new THREE.LineBasicMaterial( { color: 0x808080 } )
 				);
 				);
-				room.position.y = 3;
 				scene.add( room );
 				scene.add( room );
 
 
 				scene.add( new THREE.HemisphereLight( 0x606060, 0x404040 ) );
 				scene.add( new THREE.HemisphereLight( 0x606060, 0x404040 ) );
@@ -78,7 +65,7 @@
 					var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
 					var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
 
 
 					object.position.x = Math.random() * 4 - 2;
 					object.position.x = Math.random() * 4 - 2;
-					object.position.y = Math.random() * 4 - 2;
+					object.position.y = Math.random() * 4;
 					object.position.z = Math.random() * 4 - 2;
 					object.position.z = Math.random() * 4 - 2;
 
 
 					object.rotation.x = Math.random() * 2 * Math.PI;
 					object.rotation.x = Math.random() * 2 * Math.PI;
@@ -106,52 +93,62 @@
 				renderer.xr.enabled = true;
 				renderer.xr.enabled = true;
 				container.appendChild( renderer.domElement );
 				container.appendChild( renderer.domElement );
 
 
-				renderer.domElement.addEventListener( 'mousedown', onMouseDown, false );
-				renderer.domElement.addEventListener( 'mouseup', onMouseUp, false );
-				renderer.domElement.addEventListener( 'touchstart', onMouseDown, false );
-				renderer.domElement.addEventListener( 'touchend', onMouseUp, false );
+				//
 
 
-				window.addEventListener( 'resize', onWindowResize, false );
+				function onSelectStart() {
 
 
-				//
+					this.userData.isSelecting = true;
 
 
-				window.addEventListener( 'vrdisplaypointerrestricted', onPointerRestricted, false );
-				window.addEventListener( 'vrdisplaypointerunrestricted', onPointerUnrestricted, false );
+				}
 
 
-				document.body.appendChild( VRButton.createButton( renderer ) );
+				function onSelectEnd() {
 
 
-			}
+					this.userData.isSelecting = false;
 
 
-			function onMouseDown() {
+				}
 
 
-				isMouseDown = true;
+				controller = renderer.xr.getController( 0 );
+				controller.addEventListener( 'selectstart', onSelectStart );
+				controller.addEventListener( 'selectend', onSelectEnd );
+				controller.addEventListener( 'connected', function ( event ) {
 
 
-			}
+					this.add( buildController( event.data ) );
 
 
-			function onMouseUp() {
+				} );
+				controller.addEventListener( 'disconnected', function () {
 
 
-				isMouseDown = false;
+					this.remove( this.children[ 0 ] );
+
+				} );
+				scene.add( controller );
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+				//
+
+				document.body.appendChild( VRButton.createButton( renderer ) );
 
 
 			}
 			}
 
 
-			function onPointerRestricted() {
+			function buildController( data ) {
 
 
-				var pointerLockElement = renderer.domElement;
-				if ( pointerLockElement && typeof ( pointerLockElement.requestPointerLock ) === 'function' ) {
+				switch ( data.targetRayMode ) {
 
 
-					pointerLockElement.requestPointerLock();
+					case 'tracked-pointer':
 
 
-				}
+						var geometry = new THREE.BufferGeometry();
+						geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 0, 0, - 1 ], 3 ) );
+						geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( [ 0.5, 0.5, 0.5, 0, 0, 0 ], 3 ) );
 
 
-			}
+						var material = new THREE.LineBasicMaterial( { vertexColors: true, blending: THREE.AdditiveBlending } );
 
 
-			function onPointerUnrestricted() {
+						return new THREE.Line( geometry, material );
 
 
-				var currentPointerLockElement = document.pointerLockElement;
-				var expectedPointerLockElement = renderer.domElement;
-				if ( currentPointerLockElement && currentPointerLockElement === expectedPointerLockElement && typeof ( document.exitPointerLock ) === 'function' ) {
+					case 'gaze':
 
 
-					document.exitPointerLock();
+						var geometry = new THREE.RingBufferGeometry( 0.02, 0.04, 32 ).translate( 0, 0, - 1 );
+						var material = new THREE.MeshBasicMaterial( { opacity: 0.5, transparent: true } );
+						return new THREE.Mesh( geometry, material );
 
 
 				}
 				}
 
 
@@ -178,24 +175,26 @@
 
 
 				var delta = clock.getDelta() * 60;
 				var delta = clock.getDelta() * 60;
 
 
-				if ( isMouseDown === true ) {
+				if ( controller.userData.isSelecting === true ) {
 
 
 					var cube = room.children[ 0 ];
 					var cube = room.children[ 0 ];
 					room.remove( cube );
 					room.remove( cube );
 
 
-					cube.position.set( 0, 0, - 0.75 );
-					cube.position.applyQuaternion( camera.quaternion );
+					cube.position.copy( controller.position );
 					cube.userData.velocity.x = ( Math.random() - 0.5 ) * 0.02 * delta;
 					cube.userData.velocity.x = ( Math.random() - 0.5 ) * 0.02 * delta;
 					cube.userData.velocity.y = ( Math.random() - 0.5 ) * 0.02 * delta;
 					cube.userData.velocity.y = ( Math.random() - 0.5 ) * 0.02 * delta;
 					cube.userData.velocity.z = ( Math.random() * 0.01 - 0.05 ) * delta;
 					cube.userData.velocity.z = ( Math.random() * 0.01 - 0.05 ) * delta;
-					cube.userData.velocity.applyQuaternion( camera.quaternion );
+					cube.userData.velocity.applyQuaternion( controller.quaternion );
 					room.add( cube );
 					room.add( cube );
 
 
 				}
 				}
 
 
 				// find intersections
 				// find intersections
 
 
-				raycaster.setFromCamera( { x: 0, y: 0 }, camera );
+				tempMatrix.identity().extractRotation( controller.matrixWorld );
+
+				raycaster.ray.origin.setFromMatrixPosition( controller.matrixWorld );
+				raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix );
 
 
 				var intersects = raycaster.intersectObjects( room.children );
 				var intersects = raycaster.intersectObjects( room.children );
 
 
@@ -236,9 +235,9 @@
 
 
 					}
 					}
 
 
-					if ( cube.position.y < - 3 || cube.position.y > 3 ) {
+					if ( cube.position.y < 0 || cube.position.y > 6 ) {
 
 
-						cube.position.y = THREE.Math.clamp( cube.position.y, - 3, 3 );
+						cube.position.y = THREE.Math.clamp( cube.position.y, 0, 6 );
 						cube.userData.velocity.y = - cube.userData.velocity.y;
 						cube.userData.velocity.y = - cube.userData.velocity.y;
 
 
 					}
 					}

+ 3 - 3
src/renderers/WebGLRenderer.js

@@ -382,7 +382,7 @@ function WebGLRenderer( parameters ) {
 
 
 	this.setSize = function ( width, height, updateStyle ) {
 	this.setSize = function ( width, height, updateStyle ) {
 
 
-		if ( xr.isPresenting() ) {
+		if ( xr.isPresenting ) {
 
 
 			console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' );
 			console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' );
 			return;
 			return;
@@ -1087,7 +1087,7 @@ function WebGLRenderer( parameters ) {
 
 
 	function onAnimationFrame( time ) {
 	function onAnimationFrame( time ) {
 
 
-		if ( xr.isPresenting() ) return;
+		if ( xr.isPresenting ) return;
 		if ( onAnimationFrameCallback ) onAnimationFrameCallback( time );
 		if ( onAnimationFrameCallback ) onAnimationFrameCallback( time );
 
 
 	}
 	}
@@ -1151,7 +1151,7 @@ function WebGLRenderer( parameters ) {
 
 
 		if ( camera.parent === null ) camera.updateMatrixWorld();
 		if ( camera.parent === null ) camera.updateMatrixWorld();
 
 
-		if ( xr.enabled && xr.isPresenting() ) {
+		if ( xr.enabled && xr.isPresenting ) {
 
 
 			camera = xr.getCamera( camera );
 			camera = xr.getCamera( camera );
 
 

+ 55 - 33
src/renderers/webxr/WebXRManager.js

@@ -24,13 +24,7 @@ function WebXRManager( renderer, gl ) {
 	var pose = null;
 	var pose = null;
 
 
 	var controllers = [];
 	var controllers = [];
-	var sortedInputSources = [];
-
-	function isPresenting() {
-
-		return session !== null && referenceSpace !== null;
-
-	}
+	var inputSourcesMap = new Map();
 
 
 	//
 	//
 
 
@@ -50,6 +44,8 @@ function WebXRManager( renderer, gl ) {
 
 
 	this.enabled = false;
 	this.enabled = false;
 
 
+	this.isPresenting = false;
+
 	this.getController = function ( id ) {
 	this.getController = function ( id ) {
 
 
 		var controller = controllers[ id ];
 		var controller = controllers[ id ];
@@ -72,13 +68,11 @@ function WebXRManager( renderer, gl ) {
 
 
 	function onSessionEvent( event ) {
 	function onSessionEvent( event ) {
 
 
-		for ( var i = 0; i < controllers.length; i ++ ) {
+		var controller = inputSourcesMap.get( event.inputSource );
 
 
-			if ( sortedInputSources[ i ] === event.inputSource ) {
+		if ( controller ) {
 
 
-				controllers[ i ].dispatchEvent( { type: event.type } );
-
-			}
+			controller.dispatchEvent( { type: event.type } );
 
 
 		}
 		}
 
 
@@ -86,12 +80,24 @@ function WebXRManager( renderer, gl ) {
 
 
 	function onSessionEnd() {
 	function onSessionEnd() {
 
 
+		inputSourcesMap.forEach( function ( controller, inputSource ) {
+
+			controller.dispatchEvent( { type: 'disconnected', data: inputSource } );
+
+		} );
+
+		inputSourcesMap.clear();
+
+		//
+
 		renderer.setFramebuffer( null );
 		renderer.setFramebuffer( null );
 		renderer.setRenderTarget( renderer.getRenderTarget() ); // Hack #15830
 		renderer.setRenderTarget( renderer.getRenderTarget() ); // Hack #15830
 		animation.stop();
 		animation.stop();
 
 
 		scope.dispatchEvent( { type: 'sessionend' } );
 		scope.dispatchEvent( { type: 'sessionend' } );
 
 
+		scope.isPresenting = false;
+
 	}
 	}
 
 
 	function onRequestReferenceSpace( value ) {
 	function onRequestReferenceSpace( value ) {
@@ -103,6 +109,8 @@ function WebXRManager( renderer, gl ) {
 
 
 		scope.dispatchEvent( { type: 'sessionstart' } );
 		scope.dispatchEvent( { type: 'sessionstart' } );
 
 
+		scope.isPresenting = true;
+
 	}
 	}
 
 
 	this.setFramebufferScaleFactor = function ( /* value */ ) {
 	this.setFramebufferScaleFactor = function ( /* value */ ) {
@@ -157,33 +165,52 @@ function WebXRManager( renderer, gl ) {
 
 
 			session.addEventListener( 'inputsourceschange', updateInputSources );
 			session.addEventListener( 'inputsourceschange', updateInputSources );
 
 
-			updateInputSources();
-
 		}
 		}
 
 
 	};
 	};
 
 
-	function updateInputSources() {
+	function updateInputSources( event ) {
+
+		console.log( 'inputsourceschange', event, session.inputSources );
+
+		var inputSources = session.inputSources;
+
+		// Assign inputSources to available controllers
 
 
 		for ( var i = 0; i < controllers.length; i ++ ) {
 		for ( var i = 0; i < controllers.length; i ++ ) {
 
 
-			sortedInputSources[ i ] = findInputSource( i );
+			inputSourcesMap.set( inputSources[ i ], controllers[ i ] );
 
 
 		}
 		}
 
 
-	}
+		// Notify disconnected
 
 
-	function findInputSource( id ) {
+		for ( var i = 0; i < event.removed.length; i ++ ) {
 
 
-		var inputSources = session.inputSources;
+			var inputSource = event.removed[ i ];
+			var controller = inputSourcesMap.get( inputSource );
 
 
-		for ( var i = 0; i < inputSources.length; i ++ ) {
+			if ( controller ) {
 
 
-			var inputSource = inputSources[ i ];
-			var handedness = inputSource.handedness;
+				controller.dispatchEvent( { type: 'disconnected', data: inputSource } );
+				inputSourcesMap.delete( inputSource );
+
+			}
+
+		}
+
+		// Notify connected
+
+		for ( var i = 0; i < event.added.length; i ++ ) {
+
+			var inputSource = event.added[ i ];
+			var controller = inputSourcesMap.get( inputSource );
 
 
-			if ( id === 0 && ( handedness === 'none' || handedness === 'right' ) ) return inputSource;
-			if ( id === 1 && ( handedness === 'left' ) ) return inputSource;
+			if ( controller ) {
+
+				controller.dispatchEvent( { type: 'connected', data: inputSource } );
+
+			}
 
 
 		}
 		}
 
 
@@ -298,8 +325,6 @@ function WebXRManager( renderer, gl ) {
 
 
 	};
 	};
 
 
-	this.isPresenting = isPresenting;
-
 	// Animation Loop
 	// Animation Loop
 
 
 	var onAnimationFrameCallback = null;
 	var onAnimationFrameCallback = null;
@@ -338,11 +363,13 @@ function WebXRManager( renderer, gl ) {
 
 
 		//
 		//
 
 
+		var inputSources = session.inputSources;
+
 		for ( var i = 0; i < controllers.length; i ++ ) {
 		for ( var i = 0; i < controllers.length; i ++ ) {
 
 
 			var controller = controllers[ i ];
 			var controller = controllers[ i ];
 
 
-			var inputSource = sortedInputSources[ i ];
+			var inputSource = inputSources[ i ];
 
 
 			if ( inputSource ) {
 			if ( inputSource ) {
 
 
@@ -352,12 +379,7 @@ function WebXRManager( renderer, gl ) {
 
 
 					controller.matrix.fromArray( inputPose.transform.matrix );
 					controller.matrix.fromArray( inputPose.transform.matrix );
 					controller.matrix.decompose( controller.position, controller.rotation, controller.scale );
 					controller.matrix.decompose( controller.position, controller.rotation, controller.scale );
-
-					if ( inputSource.targetRayMode === 'pointing' ) {
-
-						controller.visible = true;
-
-					}
+					controller.visible = true;
 
 
 					continue;
 					continue;