Permalink
Browse files

Merge pull request #8221 from toji/webvr_v1

Updated VRControls and VREffect to support WebVR v1
  • Loading branch information...
mrdoob committed Mar 3, 2016
2 parents b17ffc4 + ecc10c4 commit 5ac52f320ef375868f31336d9b64133f2db3ff42
Showing with 178 additions and 52 deletions.
  1. +48 −18 examples/js/controls/VRControls.js
  2. +128 −32 examples/js/effects/VREffect.js
  3. +1 −1 examples/webvr_cubes.html
  4. +1 −1 examples/webvr_stereo_pano.html
@@ -7,30 +7,37 @@ THREE.VRControls = function ( object, onError ) {
var scope = this;
- var vrInputs = [];
+ var vrInput;
function gotVRDevices( devices ) {
for ( var i = 0; i < devices.length; i ++ ) {
- if ( devices[ i ] instanceof PositionSensorVRDevice ) {
+ if ( ( 'VRDisplay' in window && devices[ i ] instanceof VRDisplay ) ||
+ ( 'PositionSensorVRDevice' in window && devices[ i ] instanceof PositionSensorVRDevice ) ) {
- vrInputs.push( devices[ i ] );
+ vrInput = devices[ i ];
+ break; // We keep the first we encounter
}
}
- if ( vrInputs.length === 0 ) {
+ if ( !vrInput ) {
- if ( onError ) onError( 'PositionSensorVRDevice not available' );
+ if ( onError ) onError( 'VR input not available.' );
}
}
- if ( navigator.getVRDevices ) {
+ if ( navigator.getVRDisplays ) {
+ navigator.getVRDisplays().then( gotVRDevices );
+
+ } else if ( navigator.getVRDevices ) {
+
+ // Deprecated API.
navigator.getVRDevices().then( gotVRDevices );
}
@@ -43,21 +50,40 @@ THREE.VRControls = function ( object, onError ) {
this.update = function () {
- for ( var i = 0; i < vrInputs.length; i ++ ) {
+ if ( vrInput ) {
- var vrInput = vrInputs[ i ];
+ if ( vrInput.getPose ) {
- var state = vrInput.getState();
+ var pose = vrInput.getPose();
- if ( state.orientation !== null ) {
+ if ( pose.orientation !== null ) {
- object.quaternion.copy( state.orientation );
+ object.quaternion.fromArray( pose.orientation );
- }
+ }
+
+ if ( pose.position !== null ) {
+
+ object.position.fromArray( pose.position ).multiplyScalar( scope.scale );
+
+ }
+
+ } else {
- if ( state.position !== null ) {
+ // Deprecated API.
+ var state = vrInput.getState();
- object.position.copy( state.position ).multiplyScalar( scope.scale );
+ if ( state.orientation !== null ) {
+
+ object.quaternion.copy( state.orientation );
+
+ }
+
+ if ( state.position !== null ) {
+
+ object.position.copy( state.position ).multiplyScalar( scope.scale );
+
+ }
}
@@ -67,16 +93,20 @@ THREE.VRControls = function ( object, onError ) {
this.resetSensor = function () {
- for ( var i = 0; i < vrInputs.length; i ++ ) {
+ if ( vrInput ) {
+
+ if ( vrInput.resetPose !== undefined ) {
- var vrInput = vrInputs[ i ];
+ vrInput.resetPose();
- if ( vrInput.resetSensor !== undefined ) {
+ } else if ( vrInput.resetSensor !== undefined ) {
+ // Deprecated API.
vrInput.resetSensor();
} else if ( vrInput.zeroSensor !== undefined ) {
+ // Really deprecated API.
vrInput.zeroSensor();
}
@@ -94,7 +124,7 @@ THREE.VRControls = function ( object, onError ) {
this.dispose = function () {
- vrInputs = [];
+ vrInput = null;
};
@@ -12,17 +12,26 @@
THREE.VREffect = function ( renderer, onError ) {
var vrHMD;
- var eyeTranslationL, eyeFOVL, renderRectL;
- var eyeTranslationR, eyeFOVR, renderRectR;
+ var deprecatedAPI = false;
+ var eyeTranslationL = new THREE.Vector3();
+ var eyeTranslationR = new THREE.Vector3();
+ var renderRectL, renderRectR;
+ var eyeFOVL, eyeFOVR;
function gotVRDevices( devices ) {
for ( var i = 0; i < devices.length; i ++ ) {
- if ( devices[ i ] instanceof HMDVRDevice ) {
+ if ( 'VRDisplay' in window && devices[ i ] instanceof VRDisplay ) {
vrHMD = devices[ i ];
+ deprecatedAPI = false;
+ break; // We keep the first we encounter
+
+ } else if ( 'HMDVRDevice' in window && devices[ i ] instanceof HMDVRDevice ) {
+ vrHMD = devices[ i ];
+ deprecatedAPI = true;
break; // We keep the first we encounter
}
@@ -37,8 +46,13 @@ THREE.VREffect = function ( renderer, onError ) {
}
- if ( navigator.getVRDevices ) {
+ if ( navigator.getVRDisplays ) {
+
+ navigator.getVRDisplays().then( gotVRDevices );
+
+ } else if ( navigator.getVRDevices ) {
+ // Deprecated API.
navigator.getVRDevices().then( gotVRDevices );
}
@@ -55,31 +69,90 @@ THREE.VREffect = function ( renderer, onError ) {
// fullscreen
- var isFullscreen = false;
+ var isPresenting = false;
var canvas = renderer.domElement;
var fullscreenchange = canvas.mozRequestFullScreen ? 'mozfullscreenchange' : 'webkitfullscreenchange';
- document.addEventListener( fullscreenchange, function ( event ) {
+ document.addEventListener( fullscreenchange, function () {
- isFullscreen = document.mozFullScreenElement || document.webkitFullscreenElement;
+ if ( vrHMD && deprecatedAPI ) {
+
+ isPresenting = document.mozFullScreenElement || document.webkitFullscreenElement;
+
+ }
+
+ }, false );
+
+ window.addEventListener( 'vrdisplaypresentchange', function () {
+
+ isPresenting = vrHMD && vrHMD.isPresenting;
}, false );
this.setFullScreen = function ( boolean ) {
- if ( vrHMD === undefined ) return;
- if ( isFullscreen === boolean ) return;
+ return new Promise( function ( resolve, reject ) {
+
+ if ( vrHMD === undefined ) {
- if ( canvas.mozRequestFullScreen ) {
+ reject( new Error( 'No VR hardware found.' ) );
+ return;
- canvas.mozRequestFullScreen( { vrDisplay: vrHMD } );
+ }
+ if ( isPresenting === boolean ) {
+
+ resolve();
+ return;
- } else if ( canvas.webkitRequestFullscreen ) {
+ }
- canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } );
+ if ( !deprecatedAPI ) {
- }
+ if ( boolean ) {
+
+ resolve( vrHMD.requestPresent( { source: canvas } ) );
+
+ } else {
+
+ resolve( vrHMD.exitPresent() );
+
+ }
+
+ } else {
+
+ if ( canvas.mozRequestFullScreen ) {
+
+ canvas.mozRequestFullScreen( { vrDisplay: vrHMD } );
+ resolve();
+
+ } else if ( canvas.webkitRequestFullscreen ) {
+
+ canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } );
+ resolve();
+
+ } else {
+
+ console.error( 'No compatible requestFullscreen method found.' );
+ reject( new Error( 'No compatible requestFullscreen method found.' ) );
+
+ }
+
+ }
+
+ });
+
+ };
+
+ this.requestPresent = function () {
+
+ return this.setFullScreen( true );
+
+ };
+
+ this.exitPresent = function () {
+
+ return this.setFullScreen( false );
};
@@ -93,17 +166,35 @@ THREE.VREffect = function ( renderer, onError ) {
this.render = function ( scene, camera ) {
- if ( vrHMD ) {
+ if ( vrHMD && isPresenting ) {
+
+ var autoUpdate = scene.autoUpdate;
+
+ if ( autoUpdate ) {
+
+ scene.updateMatrixWorld();
+ scene.autoUpdate = false;
+
+ }
var eyeParamsL = vrHMD.getEyeParameters( 'left' );
var eyeParamsR = vrHMD.getEyeParameters( 'right' );
- eyeTranslationL = eyeParamsL.eyeTranslation;
- eyeTranslationR = eyeParamsR.eyeTranslation;
- eyeFOVL = eyeParamsL.recommendedFieldOfView;
- eyeFOVR = eyeParamsR.recommendedFieldOfView;
- renderRectL = eyeParamsL.renderRect;
- renderRectR = eyeParamsR.renderRect;
+ if ( !deprecatedAPI ) {
+
+ 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;
+
+ }
if ( Array.isArray( scene ) ) {
@@ -112,7 +203,11 @@ THREE.VREffect = function ( renderer, onError ) {
}
+ // When rendering we don't care what the recommended size is, only what the actual size
+ // of the backbuffer is.
var size = renderer.getSize();
+ renderRectL = { x: 0, y: 0, width: size.width / 2, height: size.height };
+ renderRectR = { x: size.width / 2, y: 0, width: size.width / 2, height: size.height };
renderer.setScissorTest( true );
renderer.clear();
@@ -129,28 +224,29 @@ THREE.VREffect = function ( renderer, onError ) {
cameraR.translateX( eyeTranslationR.x * this.scale );
// render left eye
- if ( renderRectL === undefined ) {
-
- renderRectL = { x: 0, y: 0, width: size.width / 2, height: size.height };
-
- }
renderer.setViewport( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height );
renderer.setScissor( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height );
renderer.render( scene, cameraL );
// render right eye
- if ( renderRectR === undefined ) {
-
- renderRectR = { x: size.width / 2, y: 0, width: size.width / 2, height: size.height };
-
- }
-
renderer.setViewport( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height );
renderer.setScissor( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height );
renderer.render( scene, cameraR );
renderer.setScissorTest( false );
+ if ( autoUpdate ) {
+
+ scene.autoUpdate = true;
+
+ }
+
+ if ( !deprecatedAPI ) {
+
+ vrHMD.submitFrame();
+
+ }
+
return;
}
@@ -127,7 +127,7 @@
var fullScreenButton = document.querySelector( '.full-screen' );
- if ( navigator.getVRDevices === undefined ) {
+ if ( navigator.getVRDisplays === undefined && navigator.getVRDevices === undefined ) {
fullScreenButton.innerHTML = 'Your browser doesn\'t support WebVR';
fullScreenButton.classList.add('error');
@@ -59,7 +59,7 @@
effect = new THREE.VREffect( renderer );
- if ( navigator.getVRDevices ) {
+ if ( navigator.getVRDisplays || navigator.getVRDevices ) {
vrControls = new THREE.VRControls( camera, vrFallback );

0 comments on commit 5ac52f3

Please sign in to comment.