Skip to content

Instantly share code, notes, and snippets.

@cvan
Created February 10, 2018 22:26
Show Gist options
  • Save cvan/3f4789fde917032c76a1b15aca40eb10 to your computer and use it in GitHub Desktop.
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
// 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();
}
// 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