Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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 5ac52f3
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 52 deletions.
66 changes: 48 additions & 18 deletions examples/js/controls/VRControls.js
Expand Up @@ -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 );

}
Expand All @@ -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 );

}

}

Expand All @@ -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();

}
Expand All @@ -94,7 +124,7 @@ THREE.VRControls = function ( object, onError ) {

this.dispose = function () {

vrInputs = [];
vrInput = null;

};

Expand Down
160 changes: 128 additions & 32 deletions examples/js/effects/VREffect.js
Expand Up @@ -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

}
Expand All @@ -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 );

}
Expand All @@ -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 );

};

Expand All @@ -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 ) ) {

Expand All @@ -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();
Expand All @@ -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;

}
Expand Down
2 changes: 1 addition & 1 deletion examples/webvr_cubes.html
Expand Up @@ -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');
Expand Down
2 changes: 1 addition & 1 deletion examples/webvr_stereo_pano.html
Expand Up @@ -59,7 +59,7 @@
effect = new THREE.VREffect( renderer );


if ( navigator.getVRDevices ) {
if ( navigator.getVRDisplays || navigator.getVRDevices ) {

vrControls = new THREE.VRControls( camera, vrFallback );

Expand Down

0 comments on commit 5ac52f3

Please sign in to comment.