Skip to content

Commit

Permalink
Update the samples to match the VR Complete version of the spec (#37)
Browse files Browse the repository at this point in the history
Very large squashed commit to bring the samples up-to-date with the "VR Complete" version of the spec. The primary changes include:

 - All samples updated to conform to the latest WebXR spec
 - Samples run on Chrome 76+'s WebXR implementation (via a minor shim) and any WebVR-compatible browsers via the most recent polyfill work by @jacobcdewitt.
 - Running with the polyfill is now the main mode, with a link to explicitly run without the polyfill provided.
 - New samples for gamepad state and teleportation, based on samples built to test Chrome by @klausw and @jacobcdewitt.
 - Click-and-drag viewing for all inline samples.
 - General code cleanup to try and make reading the samples a bit easier.
 - The "Cottontail" library has been renamed to simply "renderer" (to prevent confusion).
 - All samples use JS modules to import the renderer and other utility libraries now, eliminating the previous build step required to use them. (All modern browser support JS modules at this point).
 - Some of the test pages now use a new WebXRSampleApp class to simplify the page code while still allowing whatever sample-specific code needs to be shown.
 - New logo!

Co-Authored-By: jacobcdewitt <[email protected]>
Co-Authored-By: Klaus Weidner <[email protected]>
  • Loading branch information
3 people committed Aug 5, 2019
1 parent e7e4540 commit ce86880
Show file tree
Hide file tree
Showing 94 changed files with 12,864 additions and 21,799 deletions.
113 changes: 43 additions & 70 deletions 360-photos.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,12 @@
<meta name='viewport' content='width=device-width, initial-scale=1, user-scalable=no'>
<meta name='mobile-web-app-capable' content='yes'>
<meta name='apple-mobile-web-app-capable' content='yes'>

<!-- Origin Trial Token, feature = WebXR Device API, origin = https://immersive-web.github.io, expires = 2018-08-28 -->
<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-08-28" content="AnNpu7ceXvLew05ccD8Zr1OZsdZiB2hLQKK82kTTMDwF7oRKtP3QEJ4RzkeHrmB8Sq0vSV6ZNmszpBCZ0I8p9gAAAABceyJvcmlnaW4iOiJodHRwczovL2ltbWVyc2l2ZS13ZWIuZ2l0aHViLmlvOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUzNTQxNDQwMH0=">
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="favicon-96x96.png">

<title>360 Photos</title>

<link href='css/common.css' rel='stylesheet'></link>

<!--The polyfill is not needed for browser that have native API support,
but is linked by these samples for wider compatibility.-->
<!--script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script-->
<script src='js/webxr-polyfill.js'></script>

<!--This script patches up around implementation differences in past browser versions
so that the samples can always be written against the most recent spec changes.
It won't be necessary after the API has been officially shipped for a bit.-->
<script src='js/webxr-version-shim.js'></script>

<script src='js/cottontail/build/cottontail.js'></script>

<script src='js/webxr-button.js'></script>
</head>
<body>
<header>
Expand All @@ -59,22 +44,26 @@
</p>
</details>
</header>
<script>
(function () {
'use strict';

// If requested, initialize the WebXR polyfill
if (QueryArgs.getBool('allowPolyfill', false)) {
var polyfill = new WebXRPolyfill();
<script type="module">
import {WebXRButton} from './js/util/webxr-button.js';
import {Scene, WebXRView} from './js/render/scenes/scene.js';
import {Renderer, createWebGLContext} from './js/render/core/renderer.js';
import {Gltf2Node} from './js/render/nodes/gltf2.js';
import {SkyboxNode} from './js/render/nodes/skybox.js';
import {InlineViewerHelper} from './js/util/inline-viewer-helper.js';
import {QueryArgs} from './js/util/query-args.js';

// If requested, use the polyfill to provide support for mobile devices
// and devices which only support WebVR.
import WebXRPolyfill from './js/third-party/webxr-polyfill/build/webxr-polyfill.module.js';
if (QueryArgs.getBool('usePolyfill', true)) {
let polyfill = new WebXRPolyfill();
}
// Apply the version shim after the polyfill is instantiated, to ensure
// that the polyfill also gets patched if necessary.
var versionShim = new WebXRVersionShim();

// XR globals.
let xrButton = null;
let xrImmersiveRefSpace = null;
let xrNonImmersiveRefSpace = null;
let inlineViewerHelper = null;

// WebGL scene globals.
let gl = null;
Expand All @@ -86,58 +75,46 @@
}));

function initXR() {
xrButton = new XRDeviceButton({
xrButton = new WebXRButton({
onRequestSession: onRequestSession,
onEndSession: onEndSession
});
document.querySelector('header').appendChild(xrButton.domElement);

if (navigator.xr) {
navigator.xr.supportsSessionMode('immersive-vr').then(() => {
navigator.xr.supportsSession('immersive-vr').then(() => {
xrButton.enabled = true;
});

let outputCanvas = document.createElement('canvas');
let ctx = outputCanvas.getContext('xrpresent');

navigator.xr.requestSession({ outputContext: ctx })
.then((session) => {
document.body.appendChild(outputCanvas);
onSessionStarted(session);
});
} else {
initFallback();
navigator.xr.requestSession('inline').then(onSessionStarted);
}
}

function initFallback() {
initGL();
document.body.appendChild(gl.canvas);
let fallbackHelper = new FallbackHelper(scene, gl);
}

function initGL() {
if (gl)
return;

gl = createWebGLContext({
xrCompatible: true
});
document.body.appendChild(gl.canvas);

function onResize() {
gl.canvas.width = gl.canvas.clientWidth * window.devicePixelRatio;
gl.canvas.height = gl.canvas.clientHeight * window.devicePixelRatio;
}
window.addEventListener('resize', onResize);
onResize();

renderer = new Renderer(gl);
scene.setRenderer(renderer);
scene.inputRenderer.setControllerMesh(new Gltf2Node({url: 'media/gltf/controller/controller.gltf'}));
}

function onRequestSession() {
// Set up a mirror canvas
let mirrorCanvas = document.createElement('canvas');
let ctx = mirrorCanvas.getContext('xrpresent');
mirrorCanvas.setAttribute('id', 'mirror-canvas');
document.body.appendChild(mirrorCanvas);

navigator.xr.requestSession({ mode: 'immersive-vr', outputContext: ctx }).then((session) => {
navigator.xr.requestSession('immersive-vr').then((session) => {
xrButton.setSession(session);
session.isImmersive = true;
onSessionStarted(session);
});
}
Expand All @@ -147,21 +124,22 @@

initGL();

session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
let glLayer = new XRWebGLLayer(session, gl);
session.updateRenderState({ baseLayer: glLayer });

// When rendering 360 photos/videos you want to ensure that the user's
// head is always at the center of the rendered media. Otherwise users
// with 6DoF hardware could walk towards the edges and see a very skewed
// or outright broken view of the image. To prevent that, we request a
// 'head-model' frame of reference, which suppresses any positional
// information from the headset in favor of a head and neck model based
// solely on the device orientation. (As an added bonus this mode may
// be more power efficient on some hardware!)
session.requestReferenceSpace({ type: 'stationary', subtype: 'position-disabled' }).then((refSpace) => {
if (session.mode == "immersive-vr") {
// 'position-disabled' reference space, which suppresses any positional
// information from the headset. (As an added bonus this mode may be
// more power efficient on some hardware!)
let refSpaceType = session.isImmersive ? 'local' : 'viewer';
session.requestReferenceSpace(refSpaceType).then((refSpace) => {
if (session.isImmersive) {
xrImmersiveRefSpace = refSpace;
} else {
xrNonImmersiveRefSpace = refSpace;
inlineViewerHelper = new InlineViewerHelper(gl.canvas, refSpace);
}
session.requestAnimationFrame(onXRFrame);
});
Expand All @@ -172,17 +150,16 @@
}

function onSessionEnded(event) {
if (event.session.mode == "immersive-vr") {
document.body.removeChild(document.querySelector('#mirror-canvas'));
if (event.session.isImmersive) {
xrButton.setSession(null);
}
}

function onXRFrame(t, frame) {
let session = frame.session;
let refSpace = session.mode == "immersive-vr" ?
let refSpace = session.isImmersive ?
xrImmersiveRefSpace :
xrNonImmersiveRefSpace;
inlineViewerHelper.referenceSpace;
let pose = frame.getViewerPose(refSpace);

scene.startFrame();
Expand All @@ -196,10 +173,7 @@
if (pose) {
let views = [];
for (let view of pose.views) {
let renderView = new WebXRView();
renderView.projectionMatrix = view.projectionMatrix;
renderView.viewMatrix = view.viewMatrix;
renderView.viewport = glLayer.getViewport(view);
let renderView = new WebXRView(view, glLayer);

// It's important to take into account which eye the view is
// associated with in cases like this, since it informs which half
Expand All @@ -218,7 +192,6 @@

// Start the XR application.
initXR();
})();
</script>
</body>
</html>
Loading

0 comments on commit ce86880

Please sign in to comment.