Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebXR DOM Overlay #2614

Merged
merged 2 commits into from
Dec 29, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ export { XrLightEstimation } from './xr/xr-light-estimation.js';
export { XrManager } from './xr/xr-manager.js';
export { XrHitTest } from './xr/xr-hit-test.js';
export { XrHitTestSource } from './xr/xr-hit-test-source.js';
export { XrDomOverlay } from './xr/xr-dom-overlay.js';

// BACKWARDS COMPATIBILITY
export * from './deprecated.js';
71 changes: 71 additions & 0 deletions src/xr/xr-dom-overlay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* @class
* @name pc.XrDomOverlay
* @classdesc DOM Overlay provides ability to use DOM elements as overlay in WebXR AR session. It requires that root DOM element is provided for session start. That way input source select events, first are tested against DOM Elements first, and then propagated down to XR Session. If this propagation is not desirable, use `beforexrselect` event on DOM element, and `preventDefault` function to stop propagation.
* @description DOM Overlay provides ability to use DOM elements as overlay in WebXR AR session. It requires that root DOM element is provided for session start. That way input source select events, first are tested against DOM Elements first, and then propagated down to XR Session. If this propagation is not desirable, use `beforexrselect` event on DOM element, and `preventDefault` function to stop propagation.
* @param {pc.XrManager} manager - WebXR Manager.
* @property {boolean} supported True if DOM Overlay is supported.
* @property {boolean} available True if DOM Overlay is available. It can only be available if it is supported, during valid WebXR session and if valid root element is provided.
* @property {string|null} state State of the DOM Overlay, which defines how root DOM element is rendered. Possible options:
*
* * screen: Screen - this type indicates that DOM element is covering whole physical screen, mathcing XR viewports.
* * floating: Floating - indicates that underlying platform renders DOM element as floating in space, which can move during WebXR session or allow developer to move element.
* * head-locked: Head Locked - indicates that DOM element follows the user’s head movement consistently, appearing similar to a helmet heads-up display.
*
* @example
* app.xr.domOverlay.root = element;
* app.xr.start(camera, pc.XRTYPE_AR, pc.XRSPACE_LOCALFLOOR);
* @example
* // disable input source firing `select` event when some descendant element of DOM overlay root is touched/clicked. This is usefull when user interacts with UI elements, and there should not be `select` events behind UI.
* someElement.addEventListener('beforexrselect', function (evt) {
* evt.preventDefault();
* });
*/
function XrDomOverlay(manager) {
this._manager = manager;
this._supported = !! window.XRDOMOverlayState;
this._root = null;
}

Object.defineProperty(XrDomOverlay.prototype, 'supported', {
get: function () {
return this._supported;
}
});

Object.defineProperty(XrDomOverlay.prototype, 'available', {
get: function () {
return this._supported && this._manager.active && this._manager._session.domOverlayState !== null;
}
});

Object.defineProperty(XrDomOverlay.prototype, 'state', {
get: function () {
if (! this._supported || ! this._manager.active || ! this._manager._session.domOverlayState)
return null;

return this._manager._session.domOverlayState.type;
}
});

/**
* @name pc.XrDomOverlay#root
* @type {object|null}
* @description DOM element to be used as root for DOM Overlay. Can be changed only outside of active WebXR session.
* @example
* app.xr.domOverlay.root = element;
* app.xr.start(camera, pc.XRTYPE_AR, pc.XRSPACE_LOCALFLOOR);
*/
Object.defineProperty(XrDomOverlay.prototype, 'root', {
set: function (value) {
if (! this._supported || this._manager.active)
return;

this._root = value;
},
get: function () {
return this._root;
}
});

export { XrDomOverlay };
25 changes: 16 additions & 9 deletions src/xr/xr-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { XRTYPE_INLINE, XRTYPE_VR, XRTYPE_AR } from './constants.js';
import { XrHitTest } from './xr-hit-test.js';
import { XrInput } from './xr-input.js';
import { XrLightEstimation } from './xr-light-estimation.js';
import { XrDomOverlay } from './xr-dom-overlay.js';

/**
* @class
Expand Down Expand Up @@ -54,6 +55,7 @@ function XrManager(app) {
this.input = new XrInput(this);
this.hitTest = new XrHitTest(this);
this.lightEstimation = new XrLightEstimation(this);
this.domOverlay = new XrDomOverlay(this);

this._camera = null;
this.views = [];
Expand Down Expand Up @@ -207,23 +209,28 @@ XrManager.prototype.start = function (camera, type, spaceType, options) {
// 3. probably immersive-vr will fail to be created
// 4. call makeXRCompatible, very likely will lead to context loss

var optionalFeatures = [];
var opts = {
requiredFeatures: [spaceType],
optionalFeatures: []
};

if (type === XRTYPE_AR) {
optionalFeatures.push('light-estimation');
optionalFeatures.push('hit-test');
opts.optionalFeatures.push('light-estimation');
opts.optionalFeatures.push('hit-test');

if (this.domOverlay.root) {
opts.optionalFeatures.push('dom-overlay');
opts.domOverlay = { root: this.domOverlay.root };
}
} else if (type === XRTYPE_VR) {
optionalFeatures.push('hand-tracking');
opts.optionalFeatures.push('hand-tracking');
}

if (options && options.optionalFeatures) {
optionalFeatures = optionalFeatures.concat(options.optionalFeatures);
opts.optionalFeatures = opts.optionalFeatures.concat(options.optionalFeatures);
}

navigator.xr.requestSession(type, {
requiredFeatures: [spaceType],
optionalFeatures: optionalFeatures
}).then(function (session) {
navigator.xr.requestSession(type, opts).then(function (session) {
self._onSessionStart(session, spaceType, callback);
}).catch(function (ex) {
self._camera.camera.xr = null;
Expand Down