-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* WebXR Anchors * better wording * better parameter name * fix * fixes * lint * Update xr-manager.js * Update src/xr/xr-anchors.js * Update src/xr/xr-anchors.js * Update src/xr/xr-anchors.js * Update src/xr/xr-anchors.js * Update src/xr/xr-anchors.js * Update src/xr/xr-anchors.js * Update src/xr/xr-anchors.js * Update src/xr/xr-anchor.js * Update src/xr/xr-anchor.js * Update src/xr/xr-anchor.js * Update src/xr/xr-anchor.js * Update src/xr/xr-anchor.js * Rename xr-anchors.js to xr-anchor.js * Update xr-anchors.js * Update xr-anchors.js * fix warning * hide constructors from docs * update PR to match recent engine guidlines and some bug fixes * ts.. * Update src/framework/xr/xr-anchors.js Co-authored-by: Will Eastcott <will@playcanvas.com> * Update src/framework/xr/xr-anchors.js Co-authored-by: Will Eastcott <will@playcanvas.com> * Update src/framework/xr/xr-anchors.js Co-authored-by: Will Eastcott <will@playcanvas.com> * small PR corrections --------- Co-authored-by: Will Eastcott <will@playcanvas.com>
- Loading branch information
1 parent
f951576
commit dcd21ca
Showing
5 changed files
with
372 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import { EventHandler } from '../../core/event-handler.js'; | ||
|
||
import { Vec3 } from '../../core/math/vec3.js'; | ||
import { Quat } from '../../core/math/quat.js'; | ||
|
||
/** | ||
* An anchor keeps track of a position and rotation that is fixed relative to the real world. | ||
* This allows the application to adjust the location of the virtual objects placed in the | ||
* scene in a way that helps with maintaining the illusion that the placed objects are really | ||
* present in the user’s environment. | ||
* | ||
* @augments EventHandler | ||
* @category XR | ||
*/ | ||
class XrAnchor extends EventHandler { | ||
/** | ||
* @type {Vec3} | ||
* @private | ||
*/ | ||
_position = new Vec3(); | ||
|
||
/** | ||
* @type {Quat} | ||
* @private | ||
*/ | ||
_rotation = new Quat(); | ||
|
||
/** | ||
* @param {import('./xr-anchors.js').XrAnchors} anchors - Anchor manager. | ||
* @param {object} xrAnchor - native XRAnchor object that is provided by WebXR API | ||
* @hideconstructor | ||
*/ | ||
constructor(anchors, xrAnchor) { | ||
super(); | ||
|
||
this._anchors = anchors; | ||
this._xrAnchor = xrAnchor; | ||
} | ||
|
||
/** | ||
* Fired when an {@link XrAnchor} is destroyed. | ||
* | ||
* @event XrAnchor#destroy | ||
* @example | ||
* // once anchor is destroyed | ||
* anchor.once('destroy', function () { | ||
* // destroy its related entity | ||
* entity.destroy(); | ||
* }); | ||
*/ | ||
|
||
/** | ||
* Fired when an {@link XrAnchor}'s position and/or rotation is changed. | ||
* | ||
* @event XrAnchor#change | ||
* @example | ||
* anchor.on('change', function () { | ||
* // anchor has been updated | ||
* entity.setPosition(anchor.getPosition()); | ||
* entity.setRotation(anchor.getRotation()); | ||
* }); | ||
*/ | ||
|
||
/** | ||
* Destroy an anchor. | ||
*/ | ||
destroy() { | ||
if (!this._xrAnchor) return; | ||
this._anchors._index.delete(this._xrAnchor); | ||
|
||
const ind = this._anchors._list.indexOf(this); | ||
if (ind !== -1) this._anchors._list.splice(ind, 1); | ||
|
||
this._xrAnchor.delete(); | ||
this._xrAnchor = null; | ||
|
||
this.fire('destroy'); | ||
this._anchors.fire('destroy', this); | ||
} | ||
|
||
/** | ||
* @param {*} frame - XRFrame from requestAnimationFrame callback. | ||
* @ignore | ||
*/ | ||
update(frame) { | ||
if (!this._xrAnchor) | ||
return; | ||
|
||
const pose = frame.getPose(this._xrAnchor.anchorSpace, this._anchors.manager._referenceSpace); | ||
if (pose) { | ||
if (this._position.equals(pose.transform.position) && this._rotation.equals(pose.transform.orientation)) | ||
return; | ||
|
||
this._position.copy(pose.transform.position); | ||
this._rotation.copy(pose.transform.orientation); | ||
this.fire('change'); | ||
} | ||
} | ||
|
||
/** | ||
* Get the world space position of an anchor. | ||
* | ||
* @returns {Vec3} The world space position of an anchor. | ||
*/ | ||
getPosition() { | ||
return this._position; | ||
} | ||
|
||
/** | ||
* Get the world space rotation of an anchor. | ||
* | ||
* @returns {Quat} The world space rotation of an anchor. | ||
*/ | ||
getRotation() { | ||
return this._rotation; | ||
} | ||
} | ||
|
||
export { XrAnchor }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
import { EventHandler } from '../../core/event-handler.js'; | ||
import { platform } from '../../core/platform.js'; | ||
import { XrAnchor } from './xr-anchor.js'; | ||
|
||
/** | ||
* Callback used by {@link XrAnchors#create}. | ||
* | ||
* @callback XrAnchorCreate | ||
* @param {Error|null} err - The Error object if failed to create an anchor or null. | ||
* @param {XrAnchor|null} anchor - The anchor that is tracked against real world geometry. | ||
*/ | ||
|
||
/** | ||
* Anchors provide an ability to specify a point in the world that needs to be updated to | ||
* correctly reflect the evolving understanding of the world by the underlying AR system, | ||
* such that the anchor remains aligned with the same place in the physical world. | ||
* Anchors tend to persist better relative to the real world, especially during a longer | ||
* session with lots of movement. | ||
* | ||
* ```javascript | ||
* app.xr.start(camera, pc.XRTYPE_AR, pc.XRSPACE_LOCALFLOOR, { | ||
* anchors: true | ||
* }); | ||
* ``` | ||
* @augments EventHandler | ||
* @category XR | ||
*/ | ||
class XrAnchors extends EventHandler { | ||
/** | ||
* @type {boolean} | ||
* @private | ||
*/ | ||
_supported = platform.browser && !!window.XRAnchor; | ||
|
||
/** | ||
* List of anchor creation requests. | ||
* | ||
* @type {Array<object>} | ||
* @private | ||
*/ | ||
_creationQueue = []; | ||
|
||
/** | ||
* Index of XrAnchors, with XRAnchor (native handle) used as a key. | ||
* | ||
* @type {Map<XRAnchor,XrAnchor>} | ||
* @ignore | ||
*/ | ||
_index = new Map(); | ||
|
||
/** | ||
* @type {Array<XrAnchor>} | ||
* @ignore | ||
*/ | ||
_list = []; | ||
|
||
/** | ||
* Map of callbacks to XRAnchors so that we can call its callback once | ||
* an anchor is updated with a pose for the first time. | ||
* | ||
* @type {Map<XrAnchor,XrAnchorCreate>} | ||
* @private | ||
*/ | ||
_callbacksAnchors = new Map(); | ||
|
||
/** | ||
* @param {import('./xr-manager.js').XrManager} manager - WebXR Manager. | ||
* @hideconstructor | ||
*/ | ||
constructor(manager) { | ||
super(); | ||
|
||
this.manager = manager; | ||
|
||
if (this._supported) { | ||
this.manager.on('end', this._onSessionEnd, this); | ||
} | ||
} | ||
|
||
/** | ||
* Fired when anchor failed to be created. | ||
* | ||
* @event XrAnchors#error | ||
* @param {Error} error - Error object related to a failure of anchors. | ||
*/ | ||
|
||
/** | ||
* Fired when a new {@link XrAnchor} is added. | ||
* | ||
* @event XrAnchors#add | ||
* @param {XrAnchor} anchor - Anchor that has been added. | ||
* @example | ||
* app.xr.anchors.on('add', function (anchor) { | ||
* // new anchor is added | ||
* }); | ||
*/ | ||
|
||
/** | ||
* Fired when an {@link XrAnchor} is destroyed. | ||
* | ||
* @event XrAnchors#destroy | ||
* @param {XrAnchor} anchor - Anchor that has been destroyed. | ||
* @example | ||
* app.xr.anchors.on('destroy', function (anchor) { | ||
* // anchor that is destroyed | ||
* }); | ||
*/ | ||
|
||
/** @private */ | ||
_onSessionEnd() { | ||
// clear anchor creation queue | ||
for (let i = 0; i < this._creationQueue.length; i++) { | ||
if (!this._creationQueue[i].callback) | ||
continue; | ||
|
||
this._creationQueue[i].callback(new Error('session ended'), null); | ||
} | ||
this._creationQueue.length = 0; | ||
|
||
// destroy all anchors | ||
if (this._list) { | ||
let i = this._list.length; | ||
while (i--) { | ||
this._list[i].destroy(); | ||
} | ||
this._list.length = 0; | ||
} | ||
} | ||
|
||
/** | ||
* Create anchor with position, rotation and a callback. | ||
* | ||
* @param {import('../../core/math/vec3.js').Vec3} position - Position for an anchor. | ||
* @param {import('../../core/math/quat.js').Quat} [rotation] - Rotation for an anchor. | ||
* @param {XrAnchorCreate} [callback] - Callback to fire when anchor was created or failed to be created. | ||
* @example | ||
* app.xr.anchors.create(position, rotation, function (err, anchor) { | ||
* if (!err) { | ||
* // new anchor has been created | ||
* } | ||
* }); | ||
*/ | ||
create(position, rotation, callback) { | ||
this._creationQueue.push({ | ||
transform: new XRRigidTransform(position, rotation), // eslint-disable-line no-undef | ||
callback: callback | ||
}); | ||
} | ||
|
||
/** | ||
* @param {*} frame - XRFrame from requestAnimationFrame callback. | ||
* @ignore | ||
*/ | ||
update(frame) { | ||
// check if need to create anchors | ||
if (this._creationQueue.length) { | ||
for (let i = 0; i < this._creationQueue.length; i++) { | ||
const request = this._creationQueue[i]; | ||
|
||
frame.createAnchor(request.transform, this.manager._referenceSpace) | ||
.then((xrAnchor) => { | ||
if (request.callback) | ||
this._callbacksAnchors.set(xrAnchor, request.callback); | ||
}) | ||
.catch((ex) => { | ||
if (request.callback) | ||
request.callback(ex, null); | ||
|
||
this.fire('error', ex); | ||
}); | ||
} | ||
|
||
this._creationQueue.length = 0; | ||
} | ||
|
||
// check if destroyed | ||
for (const [xrAnchor, anchor] of this._index) { | ||
if (frame.trackedAnchors.has(xrAnchor)) | ||
continue; | ||
|
||
anchor.destroy(); | ||
} | ||
|
||
// update existing anchors | ||
for (let i = 0; i < this._list.length; i++) { | ||
this._list[i].update(frame); | ||
} | ||
|
||
// check if added | ||
for (const xrAnchor of frame.trackedAnchors) { | ||
if (this._index.has(xrAnchor)) | ||
continue; | ||
|
||
try { | ||
const tmp = xrAnchor.anchorSpace; // eslint-disable-line no-unused-vars | ||
} catch (ex) { | ||
// if anchorSpace is not available, then anchor is invalid | ||
// and should not be created | ||
continue; | ||
} | ||
|
||
const anchor = new XrAnchor(this, xrAnchor); | ||
this._index.set(xrAnchor, anchor); | ||
this._list.push(anchor); | ||
anchor.update(frame); | ||
|
||
const callback = this._callbacksAnchors.get(xrAnchor); | ||
if (callback) { | ||
this._callbacksAnchors.delete(xrAnchor); | ||
callback(null, anchor); | ||
} | ||
|
||
this.fire('add', anchor); | ||
} | ||
} | ||
|
||
/** | ||
* True if Anchors are supported. | ||
* | ||
* @type {boolean} | ||
*/ | ||
get supported() { | ||
return this._supported; | ||
} | ||
|
||
/** | ||
* List of available {@link XrAnchor}s. | ||
* | ||
* @type {Array<XrAnchor>} | ||
*/ | ||
get list() { | ||
return this._list; | ||
} | ||
} | ||
|
||
export { XrAnchors }; |
Oops, something went wrong.