Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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';
72 changes: 72 additions & 0 deletions src/xr/xr-dom-overlay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* @class
* @name pc.XrDomOverlay
* @classdesc DOM Overlay provides the ability to use DOM elements as an overlay in a WebXR AR session. It requires that the root DOM element is provided for session start. That way, input source select events are first tested against DOM Elements and then propagated down to the XR Session. If this propagation is not desirable, use the `beforexrselect` event on a DOM element and the `preventDefault` function to stop propagation.
* @description DOM Overlay provides the ability to use DOM elements as an overlay in a WebXR AR session. It requires that the root DOM element is provided for session start. That way, input source select events are first tested against DOM Elements and then propagated down to the XR Session. If this propagation is not desirable, use the `beforexrselect` event on a DOM element and the `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 a valid WebXR session and if a valid root element is provided.
* @property {string|null} state State of the DOM Overlay, which defines how the root DOM element is rendered. Possible options:
*
* * screen: Screen - indicates that the DOM element is covering whole physical screen, matching XR viewports.
* * floating: Floating - indicates that the underlying platform renders the DOM element as floating in space, which can move during the WebXR session or allow the application to move the element.
* * head-locked: Head Locked - indicates that the 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 useful when the 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 The DOM element to be used as the root for DOM Overlay. Can be changed only outside of an 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