<div class="canvas-container" id="canvas_container">
<div class="banner"><div class="text">Device is presenting</div></div>
<canvas id="webglCanvas"></canvas>
</div>
<div class="buttons">
<button onclick="tryRequestPresent()">Request Present & Pointerlock</button>
<button onclick="tryExitPresent()">Exit Present</button>
<button onclick="webglCanvas.requestPointerLock()">Request Pointerlock</button>
</div>
<h3>Log</h3>
<div id="log"></div>
body {
font-family: segoe ui;
}
h3 {
font-size: 20px;
}
.fail {
word-wrap: break-word;
color: red;
font-weight: bold;
}
div {
font-size: 12px;
}
.note {
word-wrap: break-word;
color: blue;
font-weight: bold;
}
#canvas_container {
height: 200px;
width: 400px;
float: right;
}
#canvas_container canvas {
height: 100%;
width: 100%;
}
#canvas_container .banner {
display: none;
height: 200px;
width: 400px;
background: grey;
position: absolute;
}
#canvas_container .banner .text {
color: white;
font-weight: bold;
padding: 15px;
}
#canvas_container.presenting.external .banner {
display: inherit;
}
"use strict";
var webglCanvas = document.getElementById("webglCanvas"),
gl = webglCanvas.getContext("experimental-webgl"),
animationFrameId,
vrDisplay = null,
mousePressed = null,
vrFrameData = new VRFrameData(),
pulse = 0;
function initPointerLock() {
// Register for events that are fired when the VRDisplay takes input from & returns input to the page.
// We need to request & release pointerlock in response to these, otherwise mouse input is lost.
window.addEventListener('vrdisplaypointerrestricted', onvrdisplaypointerrestricted, false);
window.addEventListener('vrdisplaypointerunrestricted', onvrdisplaypointerunrestricted, false);
window.addEventListener('vrdisplaypresentchange', onvrdisplaypresentchange, false);
displayNote('Registered for pointer restriction events.');
function onvrdisplaypointerrestricted(e) {
displayNote('Requesting Pointerlock in response to VDisplayPointerRestricted event.');
webglCanvas.requestPointerLock();
}
function onvrdisplaypointerunrestricted() {
displayNote('Exiting Pointerlock in response to VDisplayPointerUnrestricted event.');
document.exitPointerLock();
}
function onvrdisplaypresentchange() {
// Part 1/2 of Fallback method for browsers that do not raise pointerrestricted events.
// We request pointer lock below, in tryRequestPresent()
if (!vrDisplay.isPresenting) document.exitPointerLock();
}
// Listen for pointerlock change events, so we can in turn start listening for mouse click.
let mousedownListener = (e) => mousePressed = e.button,
mouseupListener = () => mousePressed = null;
document.addEventListener("pointerlockchange", (e) => {
if (document.pointerLockElement) {
displayNote('pointerlockchange event handler - pointerlock engaged');
webglCanvas.addEventListener('mousedown', mousedownListener);
webglCanvas.addEventListener('mouseup', mouseupListener);
}
else {
displayNote('pointerlockchange event handler - pointerlock disabled');
webglCanvas.removeEventListener('mousedown', mousedownListener);
webglCanvas.removeEventListener('mouseup', mouseupListener);
}
});
}
// Clear the screen color based on the mouse button state.
function draw() {
pulse = ++pulse % 100;
let val = Math.sin(pulse * 0.06283) * 0.25 + 0.75;
gl.clearColor(
mousePressed !== null ? 0.0 : val, // No Mouse Button
mousePressed === 0 ? val : 0.0, // Mouse Button 0
mousePressed >= 1 ? val : 0.0, // Mouse Button 1+
1.0
);
gl.clear(gl.COLOR_BUFFER_BIT);
}
function tryRequestPresent() {
if (!vrDisplay) return displayRejection(null, "Please plug in an HMD");
// Part 2/2 of Fallback method for browsers that do not raise pointerrestricted events.
if (typeof(window.onvrdisplaypointerrestricted) === 'undefined') {
displayNote('requesting pointerlock in absense of window.vrdisplaypointerrestricted');
webglCanvas.requestPointerLock();
}
vrDisplay.requestPresent([{source: webglCanvas}]).then(() => {
displayNote('Entered VR on ' + vrDisplay.displayName);
}, (e) => {
displayRejection(e, "requestPresent rejected");
});
}
// !!!!!!!!!!
// Helper functions below here!
// !!!!!!!!!!
function onAnimationFrame() {
draw();
// Indicate that we are ready to present the rendered frame to the VRDisplay
if (vrDisplay) {
vrDisplay.requestAnimationFrame(onAnimationFrame);
vrDisplay.getFrameData(vrFrameData);
if (vrDisplay.isPresenting) {
vrDisplay.submitFrame();
}
} else {
window.requestAnimationFrame(onAnimationFrame);
}
}
function initVR() {
window.addEventListener('vrdisplayconnect', (event) => {
vrDisplay = event.display;
displayNote('VRDisplayConnect: ' + (event&&event.display?event.display.displayName:'Unknown'));
}, false );
window.addEventListener('vrdisplaydisconnect', (event) => vrdisplay = null, false);
window.addEventListener('vrdisplaypresentchange', (d) => {
let className = '';
if (vrDisplay.isPresenting) {
className += 'presenting';
className += vrDisplay.capabilities.hasExternalDisplay ? ' external' : ' non-external';
} else {
className = '';
}
document.getElementById('canvas_container').className = className;
});
navigator.getVRDisplays().then((displays) => {
if (displays.length !== 1) {
return displayRejection(null, "getVRDisplays.length is 0 - plug in your HMD now.");
}
vrDisplay = displays[0];
}, (e) => {
displayRejection(e, "getVRDisplays rejected");
});
window.requestAnimationFrame(onAnimationFrame);
}
function tryExitPresent() {
vrDisplay && vrDisplay.exitPresent().then(() => displayNote('Exited VR.'), displayRejection);
}
function displayNote(message) {
let elem = document.getElementById('log');
elem.innerHTML = '<div class="note">' + message + '</div>' + elem.innerHTML;
}
function displayRejection(e, message) {
let output = message + (e ? (': ' + (e.message || e)) : ''),
elem = document.getElementById('log');
elem.innerHTML = '<div class="fail">' + output + '</div>' + elem.innerHTML;
}
initVR();
initPointerLock();
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.