瀏覽代碼

Update with support for WebVR "1.1" (#9655)

* Updating VREffect and VRControls to support WebVR 1.1

* Stripped out really old WebVR code path that's no longer used.

I'd be shocked if anyone was still attempting to use the browser builds
that the old code path was supporting.

* Ensured that VREffect uses the right eye transforms

Since some view matrices may include orientations as well as
translations we need to extract the head-to-eye transforms from the view
matrices to ensure accurate results. This isn't normally necessary but
is required in THREE.js because the camera is already transformed into
the head position with the VRController.
Brandon Jones 9 年之前
父節點
當前提交
6280a45266
共有 2 個文件被更改,包括 118 次插入187 次删除
  1. 23 59
      examples/js/controls/VRControls.js
  2. 95 128
      examples/js/effects/VREffect.js

+ 23 - 59
examples/js/controls/VRControls.js

@@ -11,23 +11,20 @@ THREE.VRControls = function ( object, onError ) {
 
 
 	var standingMatrix = new THREE.Matrix4();
 	var standingMatrix = new THREE.Matrix4();
 
 
+	var frameData = null;
+	if ( 'VRFrameData' in window ) {
+		frameData = new VRFrameData();
+	}
+
 	function gotVRDisplays( displays ) {
 	function gotVRDisplays( displays ) {
 
 
 		vrDisplays = displays;
 		vrDisplays = displays;
 
 
-		for ( var i = 0; i < displays.length; i ++ ) {
+		if ( displays.length > 0 ) {
 
 
-			if ( ( 'VRDisplay' in window && displays[ i ] instanceof VRDisplay ) ||
-				 ( 'PositionSensorVRDevice' in window && displays[ i ] instanceof PositionSensorVRDevice ) ) {
+			vrDisplay = displays[ 0 ];
 
 
-				vrDisplay = displays[ i ];
-				break;  // We keep the first we encounter
-
-			}
-
-		}
-
-		if ( vrDisplay === undefined ) {
+		} else {
 
 
 			if ( onError ) onError( 'VR input not available.' );
 			if ( onError ) onError( 'VR input not available.' );
 
 
@@ -39,11 +36,6 @@ THREE.VRControls = function ( object, onError ) {
 
 
 		navigator.getVRDisplays().then( gotVRDisplays );
 		navigator.getVRDisplays().then( gotVRDisplays );
 
 
-	} else if ( navigator.getVRDevices ) {
-
-		// Deprecated API.
-		navigator.getVRDevices().then( gotVRDisplays );
-
 	}
 	}
 
 
 	// the Rift SDK returns the position in meters
 	// the Rift SDK returns the position in meters
@@ -82,46 +74,32 @@ THREE.VRControls = function ( object, onError ) {
 
 
 		if ( vrDisplay ) {
 		if ( vrDisplay ) {
 
 
-			if ( vrDisplay.getPose ) {
+			var pose;
 
 
-				var pose = vrDisplay.getPose();
+			if ( vrDisplay.getFrameData ) {
 
 
-				if ( pose.orientation !== null ) {
+				vrDisplay.getFrameData( frameData );
+				pose = frameData.pose;
 
 
-					object.quaternion.fromArray( pose.orientation );
+			} else if ( vrDisplay.getPose ) {
 
 
-				}
-
-				if ( pose.position !== null ) {
-
-					object.position.fromArray( pose.position );
-
-				} else {
-
-					object.position.set( 0, 0, 0 );
-
-				}
+				pose = vrDisplay.getPose();
 
 
-			} else {
-
-				// Deprecated API.
-				var state = vrDisplay.getState();
-
-				if ( state.orientation !== null ) {
+			}
 
 
-					object.quaternion.copy( state.orientation );
+			if ( pose.orientation !== null ) {
 
 
-				}
+				object.quaternion.fromArray( pose.orientation );
 
 
-				if ( state.position !== null ) {
+			}
 
 
-					object.position.copy( state.position );
+			if ( pose.position !== null ) {
 
 
-				} else {
+				object.position.fromArray( pose.position );
 
 
-					object.position.set( 0, 0, 0 );
+			} else {
 
 
-				}
+				object.position.set( 0, 0, 0 );
 
 
 			}
 			}
 
 
@@ -152,21 +130,7 @@ THREE.VRControls = function ( object, onError ) {
 
 
 		if ( vrDisplay ) {
 		if ( vrDisplay ) {
 
 
-			if ( vrDisplay.resetPose !== undefined ) {
-
-				vrDisplay.resetPose();
-
-			} else if ( vrDisplay.resetSensor !== undefined ) {
-
-				// Deprecated API.
-				vrDisplay.resetSensor();
-
-			} else if ( vrDisplay.zeroSensor !== undefined ) {
-
-				// Really deprecated API.
-				vrDisplay.zeroSensor();
-
-			}
+			vrDisplay.resetPose();
 
 
 		}
 		}
 
 

+ 95 - 128
examples/js/effects/VREffect.js

@@ -5,43 +5,36 @@
  * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html
  * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html
  *
  *
  * Firefox: http://mozvr.com/downloads/
  * Firefox: http://mozvr.com/downloads/
- * Chromium: https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list
+ * Chromium: https://webvr.info/get-chrome
  *
  *
  */
  */
 
 
 THREE.VREffect = function ( renderer, onError ) {
 THREE.VREffect = function ( renderer, onError ) {
 
 
-	var isWebVR1 = true;
-
 	var vrDisplay, vrDisplays;
 	var vrDisplay, vrDisplays;
 	var eyeTranslationL = new THREE.Vector3();
 	var eyeTranslationL = new THREE.Vector3();
 	var eyeTranslationR = new THREE.Vector3();
 	var eyeTranslationR = new THREE.Vector3();
 	var renderRectL, renderRectR;
 	var renderRectL, renderRectR;
-	var eyeFOVL, eyeFOVR;
-
-	function gotVRDisplays( displays ) {
+	var headMatrix = new THREE.Matrix4();
+	var headToEyeMatrixL = new THREE.Matrix4();
+	var headToEyeMatrixR = new THREE.Matrix4();
 
 
-		vrDisplays = displays;
-
-		for ( var i = 0; i < displays.length; i ++ ) {
+	var frameData = null;
+	if ( 'VRFrameData' in window ) {
 
 
-			if ( 'VRDisplay' in window && displays[ i ] instanceof VRDisplay ) {
+		frameData = new VRFrameData();
 
 
-				vrDisplay = displays[ i ];
-				isWebVR1 = true;
-				break; // We keep the first we encounter
+	}
 
 
-			} else if ( 'HMDVRDevice' in window && displays[ i ] instanceof HMDVRDevice ) {
+	function gotVRDisplays( displays ) {
 
 
-				vrDisplay = displays[ i ];
-				isWebVR1 = false;
-				break; // We keep the first we encounter
+		vrDisplays = displays;
 
 
-			}
+		if ( displays.length > 0 ) {
 
 
-		}
+			vrDisplay = displays[ 0 ];
 
 
-		if ( vrDisplay === undefined ) {
+		} else {
 
 
 			if ( onError ) onError( 'HMD not available' );
 			if ( onError ) onError( 'HMD not available' );
 
 
@@ -53,11 +46,6 @@ THREE.VREffect = function ( renderer, onError ) {
 
 
 		navigator.getVRDisplays().then( gotVRDisplays );
 		navigator.getVRDisplays().then( gotVRDisplays );
 
 
-	} else if ( navigator.getVRDevices ) {
-
-		// Deprecated API.
-		navigator.getVRDevices().then( gotVRDisplays );
-
 	}
 	}
 
 
 	//
 	//
@@ -90,16 +78,7 @@ THREE.VREffect = function ( renderer, onError ) {
 
 
 			var eyeParamsL = vrDisplay.getEyeParameters( 'left' );
 			var eyeParamsL = vrDisplay.getEyeParameters( 'left' );
 			renderer.setPixelRatio( 1 );
 			renderer.setPixelRatio( 1 );
-
-			if ( isWebVR1 ) {
-
-				renderer.setSize( eyeParamsL.renderWidth * 2, eyeParamsL.renderHeight, false );
-
-			} else {
-
-				renderer.setSize( eyeParamsL.renderRect.width * 2, eyeParamsL.renderRect.height, false );
-
-			}
+			renderer.setSize( eyeParamsL.renderWidth * 2, eyeParamsL.renderHeight, false );
 
 
 		} else {
 		} else {
 
 
@@ -122,34 +101,19 @@ THREE.VREffect = function ( renderer, onError ) {
 	function onFullscreenChange() {
 	function onFullscreenChange() {
 
 
 		var wasPresenting = scope.isPresenting;
 		var wasPresenting = scope.isPresenting;
-		scope.isPresenting = vrDisplay !== undefined && ( vrDisplay.isPresenting || ( ! isWebVR1 && document[ fullscreenElement ] instanceof window.HTMLElement ) );
+		scope.isPresenting = vrDisplay !== undefined && vrDisplay.isPresenting;
 
 
 		if ( scope.isPresenting ) {
 		if ( scope.isPresenting ) {
 
 
 			var eyeParamsL = vrDisplay.getEyeParameters( 'left' );
 			var eyeParamsL = vrDisplay.getEyeParameters( 'left' );
-			var eyeWidth, eyeHeight;
-
-			if ( isWebVR1 ) {
+			var eyeWidth = eyeParamsL.renderWidth;
+			var eyeHeight = eyeParamsL.renderHeight;
 
 
-				eyeWidth = eyeParamsL.renderWidth;
-				eyeHeight = eyeParamsL.renderHeight;
+			var layers = vrDisplay.getLayers();
+			if ( layers.length ) {
 
 
-				if ( vrDisplay.getLayers ) {
-
-					var layers = vrDisplay.getLayers();
-					if ( layers.length ) {
-
-						leftBounds = layers[0].leftBounds || [ 0.0, 0.0, 0.5, 1.0 ];
-						rightBounds = layers[0].rightBounds || [ 0.5, 0.0, 0.5, 1.0 ];
-
-					}
-
-				}
-
-			} else {
-
-				eyeWidth = eyeParamsL.renderRect.width;
-				eyeHeight = eyeParamsL.renderRect.height;
+				leftBounds = layers[0].leftBounds || [ 0.0, 0.0, 0.5, 1.0 ];
+				rightBounds = layers[0].rightBounds || [ 0.5, 0.0, 0.5, 1.0 ];
 
 
 			}
 			}
 
 
@@ -172,29 +136,6 @@ THREE.VREffect = function ( renderer, onError ) {
 
 
 	}
 	}
 
 
-	if ( canvas.requestFullscreen ) {
-
-		requestFullscreen = 'requestFullscreen';
-		fullscreenElement = 'fullscreenElement';
-		exitFullscreen = 'exitFullscreen';
-		document.addEventListener( 'fullscreenchange', onFullscreenChange, false );
-
-	} else if ( canvas.mozRequestFullScreen ) {
-
-		requestFullscreen = 'mozRequestFullScreen';
-		fullscreenElement = 'mozFullScreenElement';
-		exitFullscreen = 'mozCancelFullScreen';
-		document.addEventListener( 'mozfullscreenchange', onFullscreenChange, false );
-
-	} else {
-
-		requestFullscreen = 'webkitRequestFullscreen';
-		fullscreenElement = 'webkitFullscreenElement';
-		exitFullscreen = 'webkitExitFullscreen';
-		document.addEventListener( 'webkitfullscreenchange', onFullscreenChange, false );
-
-	}
-
 	window.addEventListener( 'vrdisplaypresentchange', onFullscreenChange, false );
 	window.addEventListener( 'vrdisplaypresentchange', onFullscreenChange, false );
 
 
 	this.setFullScreen = function ( boolean ) {
 	this.setFullScreen = function ( boolean ) {
@@ -215,31 +156,13 @@ THREE.VREffect = function ( renderer, onError ) {
 
 
 			}
 			}
 
 
-			if ( isWebVR1 ) {
+			if ( boolean ) {
 
 
-				if ( boolean ) {
-
-					resolve( vrDisplay.requestPresent( [ { source: canvas } ] ) );
-
-				} else {
-
-					resolve( vrDisplay.exitPresent() );
-
-				}
+				resolve( vrDisplay.requestPresent( [ { source: canvas } ] ) );
 
 
 			} else {
 			} else {
 
 
-				if ( canvas[ requestFullscreen ] ) {
-
-					canvas[ boolean ? requestFullscreen : exitFullscreen ]( { vrDisplay: vrDisplay } );
-					resolve();
-
-				} else {
-
-					console.error( 'No compatible requestFullscreen method found.' );
-					reject( new Error( 'No compatible requestFullscreen method found.' ) );
-
-				}
+				resolve( vrDisplay.exitPresent() );
 
 
 			}
 			}
 
 
@@ -261,7 +184,7 @@ THREE.VREffect = function ( renderer, onError ) {
 
 
 	this.requestAnimationFrame = function ( f ) {
 	this.requestAnimationFrame = function ( f ) {
 
 
-		if ( isWebVR1 && vrDisplay !== undefined ) {
+		if ( vrDisplay !== undefined ) {
 
 
 			return vrDisplay.requestAnimationFrame( f );
 			return vrDisplay.requestAnimationFrame( f );
 
 
@@ -275,7 +198,7 @@ THREE.VREffect = function ( renderer, onError ) {
 
 
 	this.cancelAnimationFrame = function ( h ) {
 	this.cancelAnimationFrame = function ( h ) {
 
 
-		if ( isWebVR1 && vrDisplay !== undefined ) {
+		if ( vrDisplay !== undefined ) {
 
 
 			vrDisplay.cancelAnimationFrame( h );
 			vrDisplay.cancelAnimationFrame( h );
 
 
@@ -289,7 +212,7 @@ THREE.VREffect = function ( renderer, onError ) {
 
 
 	this.submitFrame = function () {
 	this.submitFrame = function () {
 
 
-		if ( isWebVR1 && vrDisplay !== undefined && scope.isPresenting ) {
+		if ( vrDisplay !== undefined && scope.isPresenting ) {
 
 
 			vrDisplay.submitFrame();
 			vrDisplay.submitFrame();
 
 
@@ -323,21 +246,8 @@ THREE.VREffect = function ( renderer, onError ) {
 			var eyeParamsL = vrDisplay.getEyeParameters( 'left' );
 			var eyeParamsL = vrDisplay.getEyeParameters( 'left' );
 			var eyeParamsR = vrDisplay.getEyeParameters( 'right' );
 			var eyeParamsR = vrDisplay.getEyeParameters( 'right' );
 
 
-			if ( isWebVR1 ) {
-
-				eyeTranslationL.fromArray( eyeParamsL.offset );
-				eyeTranslationR.fromArray( eyeParamsR.offset );
-				eyeFOVL = eyeParamsL.fieldOfView;
-				eyeFOVR = eyeParamsR.fieldOfView;
-
-			} else {
-
-				eyeTranslationL.copy( eyeParamsL.eyeTranslation );
-				eyeTranslationR.copy( eyeParamsR.eyeTranslation );
-				eyeFOVL = eyeParamsL.recommendedFieldOfView;
-				eyeFOVR = eyeParamsR.recommendedFieldOfView;
-
-			}
+			eyeTranslationL.fromArray( eyeParamsL.offset );
+			eyeTranslationR.fromArray( eyeParamsR.offset );
 
 
 			if ( Array.isArray( scene ) ) {
 			if ( Array.isArray( scene ) ) {
 
 
@@ -370,23 +280,44 @@ THREE.VREffect = function ( renderer, onError ) {
 			} else  {
 			} else  {
 
 
 				renderer.setScissorTest( true );
 				renderer.setScissorTest( true );
-			
+
 			}
 			}
 
 
 			if ( renderer.autoClear || forceClear ) renderer.clear();
 			if ( renderer.autoClear || forceClear ) renderer.clear();
 
 
 			if ( camera.parent === null ) camera.updateMatrixWorld();
 			if ( camera.parent === null ) camera.updateMatrixWorld();
 
 
-			cameraL.projectionMatrix = fovToProjection( eyeFOVL, true, camera.near, camera.far );
-			cameraR.projectionMatrix = fovToProjection( eyeFOVR, true, camera.near, camera.far );
-
 			camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale );
 			camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale );
 			camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale );
 			camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale );
 
 
-			var scale = this.scale;
-			cameraL.translateOnAxis( eyeTranslationL, scale );
-			cameraR.translateOnAxis( eyeTranslationR, scale );
+			if ( vrDisplay.getFrameData ) {
+
+				vrDisplay.depthNear = camera.near;
+				vrDisplay.depthFar = camera.far;
+
+				vrDisplay.getFrameData( frameData );
+
+				cameraL.projectionMatrix.elements = frameData.leftProjectionMatrix;
+				cameraR.projectionMatrix.elements = frameData.rightProjectionMatrix;
+
+				getHeadToEyeMatrices( frameData );
+
+				cameraL.updateMatrix();
+				cameraL.applyMatrix( headToEyeMatrixL );
+
+				cameraR.updateMatrix();
+				cameraR.applyMatrix( headToEyeMatrixR );
+
+			} else {
+
+				cameraL.projectionMatrix = fovToProjection( eyeParamsL.fieldOfView, true, camera.near, camera.far );
+				cameraR.projectionMatrix = fovToProjection( eyeParamsR.fieldOfView, true, camera.near, camera.far );
 
 
+				var scale = this.scale;
+				cameraL.translateOnAxis( eyeTranslationL, scale );
+				cameraR.translateOnAxis( eyeTranslationR, scale );
+
+			}
 
 
 			// render left eye
 			// render left eye
 			if ( renderTarget ) {
 			if ( renderTarget ) {
@@ -424,11 +355,11 @@ THREE.VREffect = function ( renderer, onError ) {
 				renderer.setRenderTarget( null );
 				renderer.setRenderTarget( null );
 
 
 			} else {
 			} else {
-				
+
 				renderer.setScissorTest( false );
 				renderer.setScissorTest( false );
 
 
 			}
 			}
-			
+
 			if ( autoUpdate ) {
 			if ( autoUpdate ) {
 
 
 				scene.autoUpdate = true;
 				scene.autoUpdate = true;
@@ -453,6 +384,42 @@ THREE.VREffect = function ( renderer, onError ) {
 
 
 	//
 	//
 
 
+	var poseOrientation = new THREE.Quaternion();
+	var posePosition = new THREE.Vector3();
+
+	function getHeadToEyeMatrices( frameData ) {
+
+		// Compute the matrix for the position of the head based on the pose
+		if ( frameData.pose.orientation ) {
+
+			poseOrientation.fromArray( frameData.pose.orientation );
+			headMatrix.makeRotationFromQuaternion( poseOrientation );
+
+		}	else {
+
+			headMatrix.identity();
+
+		}
+
+		if ( frameData.pose.position ) {
+
+			posePosition.fromArray( frameData.pose.position );
+			headMatrix.setPosition( posePosition );
+
+		}
+
+		// Take the view matricies and multiply them by the head matrix, which
+		// leaves only the head-to-eye transform.
+		headToEyeMatrixL.fromArray( frameData.leftViewMatrix );
+		headToEyeMatrixL.premultiply( headMatrix );
+		headToEyeMatrixL.getInverse( headToEyeMatrixL );
+
+		headToEyeMatrixR.fromArray( frameData.rightViewMatrix );
+		headToEyeMatrixR.premultiply( headMatrix );
+		headToEyeMatrixR.getInverse( headToEyeMatrixR );
+
+	}
+
 	function fovToNDCScaleOffset( fov ) {
 	function fovToNDCScaleOffset( fov ) {
 
 
 		var pxscale = 2.0 / ( fov.leftTan + fov.rightTan );
 		var pxscale = 2.0 / ( fov.leftTan + fov.rightTan );