UNPKG

@wonderlandengine/components

Version:

Wonderland Engine's official component library.

165 lines 6.62 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { Component, Emitter } from '@wonderlandengine/api'; import { property } from '@wonderlandengine/api/decorators.js'; import { setXRRigidTransformLocal } from './utils/webxr.js'; var tempVec3 = new Float32Array(3); var tempQuat = new Float32Array(4); /** * Sets the location of the object to the location of an XRAnchor * * Create anchors using the `Anchor.create()` static function. * * Example for use with cursor: * ```js * cursorTarget.onClick.add((object, cursor, originalEvent) => { * /* Only events in XR will have a frame attached *\/ * if(!originalEvent.frame) return; * Anchor.create(anchorObject, {uuid: id, persist: true}, originalEvent.frame); * }); * ``` */ class Anchor extends Component { static TypeName = 'anchor'; /* Static management of all anchors */ static #anchors = []; persist = false; /** Unique identifier to load a persistent anchor from, or empty/null if unknown */ uuid = null; /** The xrAnchor, if created */ xrAnchor = null; /** Emits events when the anchor is created either by being restored or newly created */ onCreate = new Emitter(); /** Whether the anchor is currently being tracked */ visible = false; /** Emits an event when this anchor starts tracking */ onTrackingFound = new Emitter(); /** Emits an event when this anchor stops tracking */ onTrackingLost = new Emitter(); /** XRFrame to use for creating the anchor */ xrFrame = null; /** XRHitTestResult to use for creating the anchor */ xrHitResult = null; /** Retrieve all anchors of the current scene */ static getAllAnchors() { return Anchor.#anchors; } static #addAnchor(anchor) { Anchor.#anchors.push(anchor); } static #removeAnchor(anchor) { const index = Anchor.#anchors.indexOf(anchor); if (index < 0) return; Anchor.#anchors.splice(index, 1); } /** * Create a new anchor * * @param o Object to attach the component to * @param params Parameters for the anchor component * @param frame XRFrame to use for anchor cration, if null, will use the current frame if available * @param hitResult Optional hit-test result to create the anchor with * @returns Promise for the newly created anchor component */ static create(o, params, frame, hitResult) { const a = o.addComponent(Anchor, { ...params, active: false }); if (a === null) return null; a.xrHitResult = hitResult ?? null; a.xrFrame = frame ?? null; a.onCreate.once(() => ((a.xrFrame = null), (a.xrHitResult = null))); a.active = true; return a.onCreate.promise(); } #getFrame() { return this.xrFrame || this.engine.xr.frame; } async #createAnchor() { if (!this.#getFrame().createAnchor) { throw new Error("Cannot create anchor - anchors not supported, did you enable the 'anchors' WebXR feature?"); } if (this.xrHitResult) { if (this.xrHitResult.createAnchor === undefined) { throw new Error('Requested anchor on XRHitTestResult, but WebXR hit-test feature is not available.'); } return this.xrHitResult.createAnchor(); } else { this.object.getTranslationWorld(tempVec3); tempQuat.set(this.object.rotationWorld); const rotation = tempQuat; const anchorPose = new XRRigidTransform({ x: tempVec3[0], y: tempVec3[1], z: tempVec3[2] }, { x: rotation[0], y: rotation[1], z: rotation[2], w: rotation[3] }); return this.#getFrame()?.createAnchor(anchorPose, this.engine.xr.currentReferenceSpace); } } #onAddAnchor(anchor) { if (!anchor) return; if (this.persist) { if (anchor.requestPersistentHandle !== undefined) { anchor.requestPersistentHandle().then((uuid) => { this.uuid = uuid; this.#onCreate(anchor); Anchor.#addAnchor(this); }); return; } else { console.warn('anchor: Persistent anchors are not supported by your client. Ignoring persist property.'); } } this.#onCreate(anchor); } #onRestoreAnchor(anchor) { this.#onCreate(anchor); } #onCreate(anchor) { this.xrAnchor = anchor; this.onCreate.notify(this); } start() { if (this.uuid && this.engine.xr) { this.persist = true; if (this.engine.xr.session.restorePersistentAnchor === undefined) { console.warn('anchor: Persistent anchors are not supported by your client. Ignoring persist property.'); } this.engine.xr.session.restorePersistentAnchor(this.uuid).then(this.#onRestoreAnchor.bind(this)); } else if (this.#getFrame()) { this.#createAnchor().then(this.#onAddAnchor.bind(this)); } else { throw new Error('Anchors can only be created during the XR frame in an active XR session'); } } update() { if (!this.xrAnchor || !this.engine.xr) return; /* We need to use the actual frame from the draw callback here */ const pose = this.engine.xr.frame.getPose(this.xrAnchor.anchorSpace, this.engine.xr.currentReferenceSpace); const visible = !!pose; if (visible != this.visible) { this.visible = visible; (visible ? this.onTrackingFound : this.onTrackingLost).notify(this); } if (pose) { setXRRigidTransformLocal(this.object, pose.transform); } } onDestroy() { Anchor.#removeAnchor(this); } } __decorate([ property.bool(false) ], Anchor.prototype, "persist", void 0); __decorate([ property.string() ], Anchor.prototype, "uuid", void 0); export { Anchor }; //# sourceMappingURL=anchor.js.map