Skip to content

Commit

Permalink
Merge 73df522 into f1ebdf7
Browse files Browse the repository at this point in the history
  • Loading branch information
georgios-uber committed Mar 25, 2019
2 parents f1ebdf7 + 73df522 commit d75e18d
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 10 deletions.
7 changes: 7 additions & 0 deletions docs/whats-new.md
Expand Up @@ -4,6 +4,13 @@

Target Date: April 15, 2019

### WebVR Support (experimental)

Just replace your `AnimationLoop` with `VRAnimationLoop` from `@luma.gl/addons`.
Works with [Firefox Reality](https://mixedreality.mozilla.org/firefox-reality/).

### "Asynchronous" Textures

<table style="border: 0;" align="center">
<tbody>
<tr>
Expand Down
17 changes: 10 additions & 7 deletions examples/core/gltf/app.js
Expand Up @@ -2,13 +2,13 @@
import {GLTFParser} from '@loaders.gl/gltf';
import {DracoDecoder} from '@loaders.gl/draco';
import {
AnimationLoop,
setParameters,
clear,
log,
createGLTFObjects,
_GLTFEnvironment as GLTFEnvironment
} from '@luma.gl/core';
import {VRAnimationLoop} from '@luma.gl/addons';
import GL from '@luma.gl/constants';
import {Matrix4, radians} from 'math.gl';

Expand All @@ -30,6 +30,7 @@ const GLTF_MODEL_INDEX = `${GLTF_BASE_URL}model-index.json`;
const INFO_HTML = `
<p><b>glTF</b> rendering.</p>
<p>A luma.gl <code>glTF</code> renderer.</p>
<p><img src="https://img.shields.io/badge/WebVR-Supported-orange.svg" /></p>
<div>
Model
<select id="modelSelector">
Expand Down Expand Up @@ -287,7 +288,7 @@ export class DemoApp {
};
}

onInitialize({gl, canvas}) {
onInitialize({gl, canvas, _loop: animationLoop}) {
setParameters(gl, {
depthTest: true,
blend: false
Expand Down Expand Up @@ -398,8 +399,7 @@ export class DemoApp {
});
}

onRender({gl, time, width, height, aspect}) {
gl.viewport(0, 0, width, height);
onRender({gl, time, aspect, vrViewMatrix, vrProjectionMatrix}) {
clear(gl, {color: [0.2, 0.2, 0.2, 1.0], depth: true});

const [pitch, roll] = this.rotation;
Expand All @@ -409,12 +409,15 @@ export class DemoApp {
this.translate * Math.cos(roll) * Math.cos(-pitch)
];

const uView = new Matrix4()
// TODO: find how to avoid using Array.from() to convert TypedArray to regular array
const uView = new Matrix4(vrViewMatrix ? Array.from(vrViewMatrix) : null)
.translate([0, 0, -this.translate])
.rotateX(pitch)
.rotateY(roll);

const uProjection = new Matrix4().perspective({fov: radians(40), aspect, near: 0.1, far: 9000});
const uProjection = vrProjectionMatrix
? new Matrix4(Array.from(vrProjectionMatrix))
: new Matrix4().perspective({fov: radians(40), aspect, near: 0.1, far: 9000});

if (!this.scenes.length) return false;

Expand Down Expand Up @@ -448,7 +451,7 @@ export class DemoApp {
}
}

const animationLoop = new AnimationLoop(new DemoApp());
const animationLoop = new VRAnimationLoop(new DemoApp());

animationLoop.getInfo = () => INFO_HTML;

Expand Down
4 changes: 3 additions & 1 deletion examples/core/gltf/package.json
Expand Up @@ -6,11 +6,13 @@
],
"scripts": {
"start": "webpack-dev-server --progress --hot --open -d",
"start-local": "webpack-dev-server --env.local --progress --hot --open -d"
"start-local": "webpack-dev-server --env.local --progress --hot --open -d",
"open-vr": "adb reverse tcp:8080 tcp:8080 && adb shell am start -a android.intent.action.VIEW -d http://localhost:8080/"
},
"dependencies": {
"@loaders.gl/draco": "^0.8.0",
"@loaders.gl/gltf": "^0.8.1",
"@luma.gl/addons": "^7.0.0-alpha.16",
"@luma.gl/constants": "^7.0.0-alpha.3",
"@luma.gl/core": "^7.0.0-alpha",
"math.gl": "^2.3.1"
Expand Down
4 changes: 4 additions & 0 deletions modules/addons/src/index.js
@@ -1 +1,5 @@
// VR
export {default as VRAnimationLoop} from './webvr/vr-animation-loop';

// Misc
export {addEvents} from './events/add-events';
31 changes: 31 additions & 0 deletions modules/addons/src/webvr/utils.js
@@ -0,0 +1,31 @@
/* global document */

export function createEnterVRButton({canvas, title}) {
const {top, left, width, height} = canvas.getBoundingClientRect();

const container = document.createElement('div');
container.style.position = 'absolute';
container.style.top = `${top}px`;
container.style.left = `${left}px`;
container.style.width = `${width}px`;
container.style.height = `${height}px`;
container.style.pointerEvents = 'none';
container.style.zIndex = '999';
document.body.appendChild(container);

const button = document.createElement('button');
button.style.padding = '16px';
button.style.border = '1px solid #fff';
button.style.borderRadius = '8px';
button.style.background = 'rgba(0,0,0,0.5)';
button.style.color = '#fff';
button.style.font = 'normal 20px sans-serif';
button.style.cursor = 'pointer';
button.style.margin = '20px auto';
button.style.display = 'block';
button.style.pointerEvents = 'all';
button.textContent = title;
container.appendChild(button);

return button;
}
124 changes: 124 additions & 0 deletions modules/addons/src/webvr/vr-animation-loop.js
@@ -0,0 +1,124 @@
/* global window, navigator */
import {AnimationLoop, withParameters, log} from '@luma.gl/core';
import {createEnterVRButton} from './utils';

export default class VRAnimationLoop extends AnimationLoop {
onInitialize(...args) {
this._enableWebVR();
return super.onInitialize(...args);
}

onRender(options) {
const {width, height} = options;

// Need both vrPresenting and vrFrame
// to avoid race conditions when we exit VR
// after we schedule an animation frame
if (this.vrPresenting && this.vrFrame) {
this.vrDisplay.getFrameData(this.vrFrameData);

const {
leftProjectionMatrix,
leftViewMatrix,
rightProjectionMatrix,
rightViewMatrix
} = this.vrFrameData;

const leftEyeParams = Object.assign({}, options, {
vrEye: 'left',
vrProjectionMatrix: leftProjectionMatrix,
vrViewMatrix: leftViewMatrix
});
withParameters(
this.gl,
{
viewport: [0, 0, width * 0.5, height],
scissor: [0, 0, width * 0.5, height],
scissorTest: true
},
() => super.onRender(leftEyeParams)
);

const rightEyeParams = Object.assign({}, options, {
vrEye: 'right',
vrProjectionMatrix: rightProjectionMatrix,
vrViewMatrix: rightViewMatrix
});
withParameters(
this.gl,
{
viewport: [width * 0.5, 0, width * 0.5, height],
scissor: [width * 0.5, 0, width * 0.5, height],
scissorTest: true
},
() => super.onRender(rightEyeParams)
);

this.vrDisplay.submitFrame();
return true;
}

this.gl.viewport(0, 0, width, height);
return super.onRender(options);
}

_vrDisplayPresentChange() {
if (this.vrDisplay.isPresenting) {
log.info(2, 'Entering VR')();

this.vrPresenting = true;
this.vrButton.style.display = 'none';
} else {
log.info(2, 'Exiting VR')();

this.vrPresenting = false;
this.vrButton.style.display = 'block';
}
}

async _enableWebVR() {
this.vrSupported = 'getVRDisplays' in navigator && window.VRFrameData;
if (!this.vrSupported) return;

this.vrFrameData = new window.VRFrameData();
this.vrPresenting = false;
this.vrFrame = false;

const displays = await navigator.getVRDisplays();
if (displays && displays.length) {
log.info(2, 'Found VR Displays', displays)();

// TODO: Consider resizing canvas to match vrDisplay.getEyeParameters()
// TODO: Maybe allow to select display?

this.vrDisplay = displays[0];
this.vrButton = createEnterVRButton({
canvas: this.gl.canvas,
title: `Enter VR (${this.vrDisplay.displayName})`
});
this.vrButton.onclick = () => this.enterWebVR();

window.addEventListener('vrdisplaypresentchange', this._vrDisplayPresentChange.bind(this));
}
}

_requestAnimationFrame(renderFrame) {
if (this.vrPresenting) {
this.vrDisplay.requestAnimationFrame(() => {
this.vrFrame = true;
renderFrame();
this.vrFrame = false;
});
} else {
super._requestAnimationFrame(renderFrame);
}
}

enterWebVR() {
this.vrDisplay.requestPresent([
{
source: this.gl.canvas
}
]);
}
}
8 changes: 6 additions & 2 deletions modules/core/src/core/animation-loop.js
Expand Up @@ -257,12 +257,16 @@ export default class AnimationLoop {
return;
}
this.redraw();
this._animationFrameId = requestAnimationFrame(renderFrame);
this._animationFrameId = this._requestAnimationFrame(renderFrame);
};

// cancel any pending renders to ensure only one loop can ever run
cancelAnimationFrame(this._animationFrameId);
this._animationFrameId = requestAnimationFrame(renderFrame);
this._animationFrameId = this._requestAnimationFrame(renderFrame);
}

_requestAnimationFrame(callback) {
requestAnimationFrame(callback);
}

_clearNeedsRedraw() {
Expand Down

0 comments on commit d75e18d

Please sign in to comment.