From b13328929d0bcca1976a297396b05fac483da310 Mon Sep 17 00:00:00 2001 From: "C. Van Wiemeersch" Date: Fri, 4 May 2018 19:00:01 -0700 Subject: [PATCH] handle inbound navigation -> VR presentation (fixes issue #247) (#257) r+=@caseyyee --- Assets/WebGLTemplates/WebVR/index.html | 21 +++++ Assets/WebGLTemplates/WebVR/styles/webvr.css | 1 + Assets/WebGLTemplates/WebVR/webvr.js | 83 ++++++++++++++------ Build/index.html | 21 +++++ Build/styles/webvr.css | 1 + Build/webvr.js | 83 ++++++++++++++------ 6 files changed, 162 insertions(+), 48 deletions(-) diff --git a/Assets/WebGLTemplates/WebVR/index.html b/Assets/WebGLTemplates/WebVR/index.html index 01878ae..3cef4c3 100755 --- a/Assets/WebGLTemplates/WebVR/index.html +++ b/Assets/WebGLTemplates/WebVR/index.html @@ -38,6 +38,27 @@ onProgress: unityProgress }); + function onActivate (evt) { + return new Promise(function (resolve, reject) { + if (!evt.display) { + return reject(new Error('No `display` property found on event')); + } + if (evt.reason && evt.reason !== 'navigation') { + return reject(new Error("Unexpected `reason` (expected to be 'navigation')")); + } + if (!evt.display.capabilities || !evt.display.capabilities.canPresent) { + return reject(new Error('VR display is not capable of presenting')); + } + gameInstance.vrDisplay = evt.display; + return evt.display.requestPresent([{source: gameInstance.Module.canvas}]).then(function () { + console.log('Entered VR mode'); + }).catch(function (err) { + console.error('Unable to enter VR mode:', err); + }); + }); + } + window.addEventListener('vrdisplayactivate', onActivate); + function unityProgress (gameInstance, progress) { if (!gameInstance.progress) { gameInstance.loader = document.getElementById('loader'); diff --git a/Assets/WebGLTemplates/WebVR/styles/webvr.css b/Assets/WebGLTemplates/WebVR/styles/webvr.css index a8c0b36..7304ce1 100755 --- a/Assets/WebGLTemplates/WebVR/styles/webvr.css +++ b/Assets/WebGLTemplates/WebVR/styles/webvr.css @@ -1,4 +1,5 @@ html, body { + background-color: #000; font-size: 16px; overflow: hidden; /* NOTE: Don't use `-apple-system` at the head of a shorthand font declaration. diff --git a/Assets/WebGLTemplates/WebVR/webvr.js b/Assets/WebGLTemplates/WebVR/webvr.js index ec46a7d..082e99e 100644 --- a/Assets/WebGLTemplates/WebVR/webvr.js +++ b/Assets/WebGLTemplates/WebVR/webvr.js @@ -25,6 +25,8 @@ var vrGamepads = []; var toggleVRKeyName = ''; var vrPolyfill = new WebVRPolyfill(); + var unityLoaded = false; + var submittingFrames = false; if ('serviceWorker' in navigator && 'isSecureContext' in window && !window.isSecureContext) { console.warn('The site is insecure; Service Workers will not work and the site will not be recognized as a PWA'); @@ -40,12 +42,25 @@ } } + function onUnityPresented () { + onResize(); + } + function onUnityLoaded () { MozillaResearch.telemetry.performance.measure('LoadingTime', 'LoadingStart'); canvas = document.getElementById('#canvas'); document.body.dataset.unityLoaded = 'true'; onResize(); - getVRDisplay(); + unityLoaded = true; + if (gameInstance.vrDisplay) { + vrDisplay = gameInstance.vrDisplay; + } + return getVRDisplay().then(function (display) { + vrDisplay = display; + onResize(); + }).catch(function (err) { + console.error('Error occurred upon scene load:\n', err); + }); } function onUnity (msg) { @@ -64,6 +79,11 @@ submitNextFrame = vrDisplay && vrDisplay.isPresenting; if (submitNextFrame) { vrDisplay.requestAnimationFrame(onAnimate); + if (!submittingFrames && unityLoaded) { + submittingFrames = true; + onResize(); + gameInstance.SendMessage('WebVRCameraSet', 'OnStartVR'); + } } } @@ -91,18 +111,20 @@ } function onRequestPresent() { - if (!vrDisplay) { - throw new Error('No VR display available to enter VR mode'); - } - if (!vrDisplay.capabilities || !vrDisplay.capabilities.canPresent) { - throw new Error('VR display is not capable of presenting'); - } - return vrDisplay.requestPresent([{source: canvas}]).then(function () { - // Start stereo rendering in Unity. - console.log('Entered VR mode'); - gameInstance.SendMessage('WebVRCameraSet', 'OnStartVR'); - }).catch(function (err) { - console.error('Unable to enter VR mode:', err); + return new Promise(function (resolve, reject) { + if (!vrDisplay) { + return reject(new Error('No VR display available to enter VR mode')); + } + if (!vrDisplay.capabilities || !vrDisplay.capabilities.canPresent) { + return reject(new Error('VR display is not capable of presenting')); + } + return vrDisplay.requestPresent([{source: canvas}]).then(function () { + // Start stereo rendering in Unity. + console.log('Entered VR mode'); + gameInstance.SendMessage('WebVRCameraSet', 'OnStartVR'); + }).catch(function (err) { + console.error('Unable to enter VR mode:', err); + }); }); } @@ -126,6 +148,11 @@ } function onAnimate () { + if (!vrDisplay && gameInstance.vrDisplay) { + frameData = new VRFrameData(); + vrDisplay = gameInstance.vrDisplay; + } + if (!vrDisplay || !vrDisplay.isPresenting) { windowRaf(onAnimate); } @@ -304,23 +331,24 @@ function getVRDisplay () { if (!navigator.getVRDisplays) { - console.warn('Your browser does not support WebVR'); - return; + var err = new Error('Your browser does not support WebVR'); + console.warn(err.message); + return Promise.reject(err); } + frameData = new VRFrameData(); - return navigator.getVRDisplays().then(function(displays) { + function handleDisplay (display) { var canPresent = false; var hasPosition = false; var hasOrientation = false; var hasExternalDisplay = false; - if (displays.length) { - vrDisplay = displays[displays.length - 1]; - canPresent = vrDisplay.capabilities.canPresent; - hasPosition = vrDisplay.capabilities.hasPosition; - hasOrientation = vrDisplay.capabilities.hasOrientation; - hasExternalDisplay = vrDisplay.capabilities.hasExternalDisplay; + if (display) { + canPresent = display.capabilities.canPresent; + hasPosition = display.capabilities.hasPosition; + hasOrientation = display.capabilities.hasOrientation; + hasExternalDisplay = display.capabilities.hasExternalDisplay; } if (canPresent) { @@ -339,7 +367,15 @@ }) ); - return vrDisplay; + return Promise.resolve(display); + } + + if (gameInstance.vrDisplay) { + return handleDisplay(gameInstance.vrDisplay); + } + + return navigator.getVRDisplays().then(function(displays) { + return handleDisplay(displays[displays.length - 1]); }).catch(function (err) { console.error('Error occurred getting VR display:', err); }); @@ -361,7 +397,6 @@ window.addEventListener('resize', onResize, true); window.addEventListener('vrdisplaypresentchange', onResize, false); - window.addEventListener('vrdisplayactivate', onRequestPresent, false); window.addEventListener('vrdisplaydeactivate', onExitPresent, false); window.addEventListener('keyup', onKeyUp, false); document.addEventListener('UnityLoaded', onUnityLoaded, false); diff --git a/Build/index.html b/Build/index.html index 32e0938..bde1cde 100755 --- a/Build/index.html +++ b/Build/index.html @@ -38,6 +38,27 @@ onProgress: unityProgress }); + function onActivate (evt) { + return new Promise(function (resolve, reject) { + if (!evt.display) { + return reject(new Error('No `display` property found on event')); + } + if (evt.reason && evt.reason !== 'navigation') { + return reject(new Error("Unexpected `reason` (expected to be 'navigation')")); + } + if (!evt.display.capabilities || !evt.display.capabilities.canPresent) { + return reject(new Error('VR display is not capable of presenting')); + } + gameInstance.vrDisplay = evt.display; + return evt.display.requestPresent([{source: gameInstance.Module.canvas}]).then(function () { + console.log('Entered VR mode'); + }).catch(function (err) { + console.error('Unable to enter VR mode:', err); + }); + }); + } + window.addEventListener('vrdisplayactivate', onActivate); + function unityProgress (gameInstance, progress) { if (!gameInstance.progress) { gameInstance.loader = document.getElementById('loader'); diff --git a/Build/styles/webvr.css b/Build/styles/webvr.css index a8c0b36..7304ce1 100755 --- a/Build/styles/webvr.css +++ b/Build/styles/webvr.css @@ -1,4 +1,5 @@ html, body { + background-color: #000; font-size: 16px; overflow: hidden; /* NOTE: Don't use `-apple-system` at the head of a shorthand font declaration. diff --git a/Build/webvr.js b/Build/webvr.js index ec46a7d..082e99e 100644 --- a/Build/webvr.js +++ b/Build/webvr.js @@ -25,6 +25,8 @@ var vrGamepads = []; var toggleVRKeyName = ''; var vrPolyfill = new WebVRPolyfill(); + var unityLoaded = false; + var submittingFrames = false; if ('serviceWorker' in navigator && 'isSecureContext' in window && !window.isSecureContext) { console.warn('The site is insecure; Service Workers will not work and the site will not be recognized as a PWA'); @@ -40,12 +42,25 @@ } } + function onUnityPresented () { + onResize(); + } + function onUnityLoaded () { MozillaResearch.telemetry.performance.measure('LoadingTime', 'LoadingStart'); canvas = document.getElementById('#canvas'); document.body.dataset.unityLoaded = 'true'; onResize(); - getVRDisplay(); + unityLoaded = true; + if (gameInstance.vrDisplay) { + vrDisplay = gameInstance.vrDisplay; + } + return getVRDisplay().then(function (display) { + vrDisplay = display; + onResize(); + }).catch(function (err) { + console.error('Error occurred upon scene load:\n', err); + }); } function onUnity (msg) { @@ -64,6 +79,11 @@ submitNextFrame = vrDisplay && vrDisplay.isPresenting; if (submitNextFrame) { vrDisplay.requestAnimationFrame(onAnimate); + if (!submittingFrames && unityLoaded) { + submittingFrames = true; + onResize(); + gameInstance.SendMessage('WebVRCameraSet', 'OnStartVR'); + } } } @@ -91,18 +111,20 @@ } function onRequestPresent() { - if (!vrDisplay) { - throw new Error('No VR display available to enter VR mode'); - } - if (!vrDisplay.capabilities || !vrDisplay.capabilities.canPresent) { - throw new Error('VR display is not capable of presenting'); - } - return vrDisplay.requestPresent([{source: canvas}]).then(function () { - // Start stereo rendering in Unity. - console.log('Entered VR mode'); - gameInstance.SendMessage('WebVRCameraSet', 'OnStartVR'); - }).catch(function (err) { - console.error('Unable to enter VR mode:', err); + return new Promise(function (resolve, reject) { + if (!vrDisplay) { + return reject(new Error('No VR display available to enter VR mode')); + } + if (!vrDisplay.capabilities || !vrDisplay.capabilities.canPresent) { + return reject(new Error('VR display is not capable of presenting')); + } + return vrDisplay.requestPresent([{source: canvas}]).then(function () { + // Start stereo rendering in Unity. + console.log('Entered VR mode'); + gameInstance.SendMessage('WebVRCameraSet', 'OnStartVR'); + }).catch(function (err) { + console.error('Unable to enter VR mode:', err); + }); }); } @@ -126,6 +148,11 @@ } function onAnimate () { + if (!vrDisplay && gameInstance.vrDisplay) { + frameData = new VRFrameData(); + vrDisplay = gameInstance.vrDisplay; + } + if (!vrDisplay || !vrDisplay.isPresenting) { windowRaf(onAnimate); } @@ -304,23 +331,24 @@ function getVRDisplay () { if (!navigator.getVRDisplays) { - console.warn('Your browser does not support WebVR'); - return; + var err = new Error('Your browser does not support WebVR'); + console.warn(err.message); + return Promise.reject(err); } + frameData = new VRFrameData(); - return navigator.getVRDisplays().then(function(displays) { + function handleDisplay (display) { var canPresent = false; var hasPosition = false; var hasOrientation = false; var hasExternalDisplay = false; - if (displays.length) { - vrDisplay = displays[displays.length - 1]; - canPresent = vrDisplay.capabilities.canPresent; - hasPosition = vrDisplay.capabilities.hasPosition; - hasOrientation = vrDisplay.capabilities.hasOrientation; - hasExternalDisplay = vrDisplay.capabilities.hasExternalDisplay; + if (display) { + canPresent = display.capabilities.canPresent; + hasPosition = display.capabilities.hasPosition; + hasOrientation = display.capabilities.hasOrientation; + hasExternalDisplay = display.capabilities.hasExternalDisplay; } if (canPresent) { @@ -339,7 +367,15 @@ }) ); - return vrDisplay; + return Promise.resolve(display); + } + + if (gameInstance.vrDisplay) { + return handleDisplay(gameInstance.vrDisplay); + } + + return navigator.getVRDisplays().then(function(displays) { + return handleDisplay(displays[displays.length - 1]); }).catch(function (err) { console.error('Error occurred getting VR display:', err); }); @@ -361,7 +397,6 @@ window.addEventListener('resize', onResize, true); window.addEventListener('vrdisplaypresentchange', onResize, false); - window.addEventListener('vrdisplayactivate', onRequestPresent, false); window.addEventListener('vrdisplaydeactivate', onExitPresent, false); window.addEventListener('keyup', onKeyUp, false); document.addEventListener('UnityLoaded', onUnityLoaded, false);