-
Notifications
You must be signed in to change notification settings - Fork 1.7k
WebXR Anchors #3091
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
Merged
Merged
WebXR Anchors #3091
Changes from all commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
f94e7f7
WebXR Anchors
Maksims 571f5d5
better wording
Maksims ab82296
better parameter name
Maksims 7e018ee
fix
Maksims 9060be7
Merge branch 'master' of github.com:playcanvas/engine into webxr-anchors
Maksims 9cd379f
merge
Maksims 1ce9230
fixes
Maksims a5c6059
lint
Maksims fc93ac2
Merge branch 'master' into webxr-anchors
Maksims c46e237
Update xr-manager.js
Maksims e138780
Update src/xr/xr-anchors.js
willeastcott fead641
Update src/xr/xr-anchors.js
willeastcott 16d5603
Update src/xr/xr-anchors.js
willeastcott 3d45f16
Update src/xr/xr-anchors.js
willeastcott ab7d2fa
Update src/xr/xr-anchors.js
willeastcott 1e9106a
Update src/xr/xr-anchors.js
willeastcott c9a1ee0
Update src/xr/xr-anchors.js
willeastcott 1bf096e
Update src/xr/xr-anchor.js
willeastcott ca749af
Update src/xr/xr-anchor.js
willeastcott 27a2e0a
Update src/xr/xr-anchor.js
willeastcott b62c049
Update src/xr/xr-anchor.js
willeastcott 56a1d85
Update src/xr/xr-anchor.js
willeastcott a5838ba
merge
Maksims b0d9495
Rename xr-anchors.js to xr-anchor.js
Maksims 3b1f0aa
Update xr-anchors.js
Maksims 47a2af1
Update xr-anchors.js
Maksims be3dca4
Merge branch 'master' of github.com:playcanvas/engine into webxr-anchors
Maksims 0c73876
Merge branch 'webxr-anchors' of github.com:Maksims/engine-1 into webx…
Maksims 5625bbc
fix warning
Maksims a907725
hide constructors from docs
Maksims f820ca4
merge
Maksims d9974bf
update PR to match recent engine guidlines and some bug fixes
Maksims 2dc0c04
ts..
Maksims e5d56cd
Merge branch 'main' into webxr-anchors
Maksims 5fad7ed
Update src/framework/xr/xr-anchors.js
Maksims 324a623
Update src/framework/xr/xr-anchors.js
Maksims e3dafe3
Update src/framework/xr/xr-anchors.js
Maksims 0cb4042
small PR corrections
Maksims File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 | ||
willeastcott marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| */ | ||
| _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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.