Created
February 10, 2018 22:26
-
-
Save cvan/3f4789fde917032c76a1b15aca40eb10 to your computer and use it in GitHub Desktop.
WebVR v1.1 -> WebXR Migration Guide: code samples for methods of acquiring devices and presenting content
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 1. Acquire a VR display. Handle event listeners. | |
function getDisplay () { | |
return navigator.getVRDisplays().then(displays => { | |
if (!displays.length) { | |
throw new Error('No VR display found'); | |
} | |
return displays[0]; | |
}); | |
} | |
var vrDisplay = null; | |
function updateDisplay () { | |
return getDisplay().then(display => { | |
vrDisplay = display; | |
}).catch(err => { | |
console.error('Could not get VR display:', err); | |
}); | |
} | |
if (navigator.getVRDisplays) { | |
// Call `navigator.getVRDisplays` (before Firefox 59). | |
updateDisplay(); | |
// As of Firefox 59, it's preferred to also wait for the `vrdisplayconnect` event to fire. | |
window.addEventListener('vrdisplayconnect', updateDisplay); | |
window.addEventListener('vrdisplaydisconnect', e => console.log.bind(console)); | |
window.addEventListener('vrdisplayactivate', e => console.log.bind(console)); | |
window.addEventListener('vrdisplaydeactivate', e => console.log.bind(console)); | |
window.addEventListener('vrdisplayblur', e => console.log.bind(console)); | |
window.addEventListener('vrdisplayfocus', e => console.log.bind(console)); | |
window.addEventListener('vrdisplaypointerrestricted', e => console.log.bind(console)); | |
window.addEventListener('vrdisplaypointerunrestricted', e => console.log.bind(console)); | |
window.addEventListener('vrdisplaypresentchange', e => console.log.bind(console)) | |
} | |
// 2. Get active VR displays. | |
console.log(navigator.activeVRDisplays); // NOTE: This is deprecated in WebXR! | |
// 3. Enter/Exit VR mode. | |
var vrButtonEl = document.querySelector('button#vr-button'); | |
if (!vrButtonEl) { | |
vrButtonEl = document.createElement('button'); | |
vrButtonEl.innerHTML = 'VR Mode'; | |
document.body.appendChild(vrButtonEl); | |
} | |
vrButtonEl.addEventListener('click', toggleVR); | |
function toggleVR () { | |
if (!vrDisplay) { | |
return updateDisplay().then(next); | |
} | |
next(); | |
function next () { | |
if (vrDisplay.isPresenting) { | |
enterVR().catch(err => { | |
console.error('Could not enter VR mode:', err); | |
}); | |
} else { | |
exitVR().catch(err => { | |
console.error('Could not exit VR mode:', err); | |
}); | |
} | |
} | |
} | |
function enterVR () { | |
var frameData = new VRFrameData(); | |
// Render a single frame of VR data. | |
function onVRFrame () { | |
// Schedule the next frame's callback. | |
raf = vrDisplay.requestAnimationFrame(onVRFrame); | |
// Poll the VRDisplay for the current frame's matrices and pose. | |
vrDisplay.getFrameData(frameData); | |
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); | |
// Render to the left eye's view to the left half of the canvas. | |
gl.viewport(0, 0, canvas.width * 0.5, canvas.height); | |
gl.uniformMatrix4fv(projectionMatrixLocation, false, frameData.leftProjectionMatrix); | |
gl.uniformMatrix4fv(viewMatrixLocation, false, frameData.leftViewMatrix); | |
drawGeometry(); | |
// Render to the right eye's view to the right half of the canvas. | |
gl.viewport(canvas.width * 0.5, 0, canvas.width * 0.5, canvas.height); | |
gl.uniformMatrix4fv(projectionMatrixLocation, false, frameData.rightProjectionMatrix); | |
gl.uniformMatrix4fv(viewMatrixLocation, false, frameData.rightViewMatrix); | |
drawGeometry(); | |
// Indicate that we are ready to present the rendered frame to the `VRDisplay`. | |
vrDisplay.submitFrame(); | |
} | |
// NOTE: `requestPresent` must be called within a user-activation gesture | |
// (exception being navigating to another document whilst in VR mode). | |
return vrDisplay.requestPresent([{source: document.querySelector('canvas')}]).then(() => { | |
vrDisplay.requestAnimationFrame(onVRFrame); | |
}); | |
} | |
function exitVR () { | |
return vrDisplay.exitPresent(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 1. Acquire a VR device. | |
function getDevice () { | |
return navigator.xr.requestDevice().then(onXRAvailable); | |
} | |
var xrDevice = null; | |
function updateDevice () { | |
return getDevice().then(display => { | |
xrDevice = display; | |
}).catch(err => { | |
if (err.name === 'NotFoundError') { | |
// No XR devices available. | |
console.error('No XR devices available:', err); | |
} else { | |
// An error occurred while requesting an XR device. | |
console.error('Requesting XR device failed:', err); | |
} | |
}); | |
} | |
if (navigator.xr) { | |
updateDevice(); | |
// This is the the only event now, but each `VRDevice` instance extends from `EventTarget` | |
// (so you can do `addEventListener`, `removeEventListener`, etc.). | |
navigator.xr.addEventListener('devicechange', updateDevice); | |
} | |
// 3. Enter/Exit VR mode. | |
let xrDevice = null; | |
async function onXRAvailable (device) { | |
xrDevice = device; | |
// Most (but not all) `XRDevice`s are capable of granting exclusive access to | |
// the device, which is necessary to show imagery in a headset. If the device | |
// has that capability, the page will want to add an "Enter VR" button (similar | |
// to "Enter Fullscreen") that triggers the page to begin showing imagery on | |
// the headset. | |
return xrDevice.supportsSession({exclusive: true}).then(() => { | |
var xrButtonEl = document.querySelector('button#xr-button'); | |
if (!xrButtonEl) { | |
xrButtonEl = document.createElement('button'); | |
xrButtonEl.innerHTML = 'VR Mode'; | |
document.body.appendChild(xrButtonEl); | |
} | |
xrButtonEl.addEventListener('click', beginXRSession); | |
}).catch(reason => { | |
console.log('Session not supported:', reason); | |
}); | |
} | |
function beginXRSession () { | |
// `requestSession` must be called within a user-activation event (e.g., `click` or `touch`) when requesting | |
// exclusive access. | |
return xrDevice.requestSession({exclusive: true}).then(onSessionStarted) | |
.catch(err => { | |
// May fail for a variety of reasons. Probably just want to | |
// render the scene normally without any tracking at this point. | |
window.requestAnimationFrame(onDrawFrame); | |
}); | |
} | |
let xrSession = null; | |
let xrFrameOfRef = null; | |
function onSessionStarted (session) { | |
// Store the session for use later. | |
xrSession = session; | |
// The depth range of the scene should be set such that the projection matrices returned by the session are correct. | |
xrSession.depthNear = 0.1; | |
xrSession.depthFar = 100.0; | |
// The `XRFrameOfReference` provides the coordinate system in which | |
// `getViewMatrix()` and the `poseModelMatrix` are defined. For more info: | |
// https://github.com/immersive-web/webxr/blob/master/explainer.md#orientation-only-tracking | |
return xrSession.requestFrameOfReference('headModel').then(frameOfRef => { | |
xrFrameOfRef = frameOfRef; | |
}).then(setupWebGLLayer) // Create a compatible XRWebGLLayer. | |
.then(() => { | |
// Start the render loop. | |
xrSession.requestAnimationFrame(onDrawFrame); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment