|
@@ -5,43 +5,36 @@
|
|
|
* WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html
|
|
|
*
|
|
|
* 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 ) {
|
|
|
|
|
|
- var isWebVR1 = true;
|
|
|
-
|
|
|
var vrDisplay, vrDisplays;
|
|
|
var eyeTranslationL = new THREE.Vector3();
|
|
|
var eyeTranslationR = new THREE.Vector3();
|
|
|
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' );
|
|
|
|
|
@@ -53,11 +46,6 @@ THREE.VREffect = function ( renderer, onError ) {
|
|
|
|
|
|
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' );
|
|
|
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 {
|
|
|
|
|
@@ -122,34 +101,19 @@ THREE.VREffect = function ( renderer, onError ) {
|
|
|
function onFullscreenChange() {
|
|
|
|
|
|
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 ) {
|
|
|
|
|
|
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 );
|
|
|
|
|
|
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 {
|
|
|
|
|
|
- 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 ) {
|
|
|
|
|
|
- if ( isWebVR1 && vrDisplay !== undefined ) {
|
|
|
+ if ( vrDisplay !== undefined ) {
|
|
|
|
|
|
return vrDisplay.requestAnimationFrame( f );
|
|
|
|
|
@@ -275,7 +198,7 @@ THREE.VREffect = function ( renderer, onError ) {
|
|
|
|
|
|
this.cancelAnimationFrame = function ( h ) {
|
|
|
|
|
|
- if ( isWebVR1 && vrDisplay !== undefined ) {
|
|
|
+ if ( vrDisplay !== undefined ) {
|
|
|
|
|
|
vrDisplay.cancelAnimationFrame( h );
|
|
|
|
|
@@ -289,7 +212,7 @@ THREE.VREffect = function ( renderer, onError ) {
|
|
|
|
|
|
this.submitFrame = function () {
|
|
|
|
|
|
- if ( isWebVR1 && vrDisplay !== undefined && scope.isPresenting ) {
|
|
|
+ if ( vrDisplay !== undefined && scope.isPresenting ) {
|
|
|
|
|
|
vrDisplay.submitFrame();
|
|
|
|
|
@@ -323,21 +246,8 @@ THREE.VREffect = function ( renderer, onError ) {
|
|
|
var eyeParamsL = vrDisplay.getEyeParameters( 'left' );
|
|
|
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 ) ) {
|
|
|
|
|
@@ -370,23 +280,44 @@ THREE.VREffect = function ( renderer, onError ) {
|
|
|
} else {
|
|
|
|
|
|
renderer.setScissorTest( true );
|
|
|
-
|
|
|
+
|
|
|
}
|
|
|
|
|
|
if ( renderer.autoClear || forceClear ) renderer.clear();
|
|
|
|
|
|
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( 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
|
|
|
if ( renderTarget ) {
|
|
@@ -424,11 +355,11 @@ THREE.VREffect = function ( renderer, onError ) {
|
|
|
renderer.setRenderTarget( null );
|
|
|
|
|
|
} else {
|
|
|
-
|
|
|
+
|
|
|
renderer.setScissorTest( false );
|
|
|
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if ( autoUpdate ) {
|
|
|
|
|
|
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 ) {
|
|
|
|
|
|
var pxscale = 2.0 / ( fov.leftTan + fov.rightTan );
|