UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

281 lines (280 loc) 9.34 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); import { EventHandler } from "../../core/event-handler.js"; import { platform } from "../../core/platform.js"; import { XrInputSource } from "./xr-input-source.js"; class XrInput extends EventHandler { /** * Create a new XrInput instance. * * @param {XrManager} manager - WebXR Manager. * @ignore */ constructor(manager) { super(); /** * @type {XrManager} * @private */ __publicField(this, "manager"); /** * @type {XrInputSource[]} * @private */ __publicField(this, "_inputSources", []); /** * @type {Function} * @private */ __publicField(this, "_onInputSourcesChangeEvt"); /** @ignore */ __publicField(this, "velocitiesSupported", false); this.manager = manager; this.velocitiesSupported = !!(platform.browser && window.XRPose?.prototype?.hasOwnProperty("linearVelocity")); this._onInputSourcesChangeEvt = (evt) => { this._onInputSourcesChange(evt); }; this.manager.on("start", this._onSessionStart, this); this.manager.on("end", this._onSessionEnd, this); } /** @private */ _onSessionStart() { const session = this.manager.session; session.addEventListener("inputsourceschange", this._onInputSourcesChangeEvt); session.addEventListener("select", (evt) => { const inputSource = this._getByInputSource(evt.inputSource); inputSource.update(evt.frame); inputSource.fire("select", evt); this.fire("select", inputSource, evt); }); session.addEventListener("selectstart", (evt) => { const inputSource = this._getByInputSource(evt.inputSource); inputSource.update(evt.frame); inputSource._selecting = true; inputSource.fire("selectstart", evt); this.fire("selectstart", inputSource, evt); }); session.addEventListener("selectend", (evt) => { const inputSource = this._getByInputSource(evt.inputSource); inputSource.update(evt.frame); inputSource._selecting = false; inputSource.fire("selectend", evt); this.fire("selectend", inputSource, evt); }); session.addEventListener("squeeze", (evt) => { const inputSource = this._getByInputSource(evt.inputSource); inputSource.update(evt.frame); inputSource.fire("squeeze", evt); this.fire("squeeze", inputSource, evt); }); session.addEventListener("squeezestart", (evt) => { const inputSource = this._getByInputSource(evt.inputSource); inputSource.update(evt.frame); inputSource._squeezing = true; inputSource.fire("squeezestart", evt); this.fire("squeezestart", inputSource, evt); }); session.addEventListener("squeezeend", (evt) => { const inputSource = this._getByInputSource(evt.inputSource); inputSource.update(evt.frame); inputSource._squeezing = false; inputSource.fire("squeezeend", evt); this.fire("squeezeend", inputSource, evt); }); const inputSources = session.inputSources; for (let i = 0; i < inputSources.length; i++) { this._addInputSource(inputSources[i]); } } /** @private */ _onSessionEnd() { let i = this._inputSources.length; while (i--) { const inputSource = this._inputSources[i]; this._inputSources.splice(i, 1); inputSource.fire("remove"); this.fire("remove", inputSource); } const session = this.manager.session; session.removeEventListener("inputsourceschange", this._onInputSourcesChangeEvt); } /** * @param {XRInputSourcesChangeEvent} evt - WebXR input sources change event. * @private */ _onInputSourcesChange(evt) { for (let i = 0; i < evt.removed.length; i++) { this._removeInputSource(evt.removed[i]); } for (let i = 0; i < evt.added.length; i++) { this._addInputSource(evt.added[i]); } } /** * @param {XRInputSource} xrInputSource - Input source to search for. * @returns {XrInputSource|null} The input source that matches the given WebXR input source or * null if no match is found. * @private */ _getByInputSource(xrInputSource) { for (let i = 0; i < this._inputSources.length; i++) { if (this._inputSources[i].inputSource === xrInputSource) { return this._inputSources[i]; } } return null; } /** * @param {XRInputSource} xrInputSource - Input source to add. * @private */ _addInputSource(xrInputSource) { if (this._getByInputSource(xrInputSource)) { return; } const inputSource = new XrInputSource(this.manager, xrInputSource); this._inputSources.push(inputSource); this.fire("add", inputSource); } /** * @param {XRInputSource} xrInputSource - Input source to remove. * @private */ _removeInputSource(xrInputSource) { for (let i = 0; i < this._inputSources.length; i++) { if (this._inputSources[i].inputSource !== xrInputSource) { continue; } const inputSource = this._inputSources[i]; this._inputSources.splice(i, 1); let h = inputSource.hitTestSources.length; while (h--) { inputSource.hitTestSources[h].remove(); } inputSource.fire("remove"); this.fire("remove", inputSource); return; } } /** * @param {XRFrame} frame - XRFrame from requestAnimationFrame callback. * @ignore */ update(frame) { for (let i = 0; i < this._inputSources.length; i++) { this._inputSources[i].update(frame); } } /** * List of active {@link XrInputSource} instances. * * @type {XrInputSource[]} */ get inputSources() { return this._inputSources; } } /** * Fired when a new {@link XrInputSource} is added to the list. The handler is passed the * {@link XrInputSource} that has been added. * * @event * @example * app.xr.input.on('add', (inputSource) => { * // new input source is added * }); */ __publicField(XrInput, "EVENT_ADD", "add"); /** * Fired when an {@link XrInputSource} is removed from the list. The handler is passed the * {@link XrInputSource} that has been removed. * * @event * @example * app.xr.input.on('remove', (inputSource) => { * // input source is removed * }); */ __publicField(XrInput, "EVENT_REMOVE", "remove"); /** * Fired when {@link XrInputSource} has triggered primary action. This could be pressing a * trigger button, or touching a screen. The handler is passed the {@link XrInputSource} that * triggered the `select` event and the XRInputSourceEvent event from the WebXR API. * * @event * @example * const ray = new pc.Ray(); * app.xr.input.on('select', (inputSource, evt) => { * ray.set(inputSource.getOrigin(), inputSource.getDirection()); * if (obj.intersectsRay(ray)) { * // selected an object with input source * } * }); */ __publicField(XrInput, "EVENT_SELECT", "select"); /** * Fired when {@link XrInputSource} has started to trigger primary action. The handler is * passed the {@link XrInputSource} that triggered the `selectstart` event and the * XRInputSourceEvent event from the WebXR API. * * @event * @example * app.xr.input.on('selectstart', (inputSource, evt) => { * console.log('Select started'); * }); */ __publicField(XrInput, "EVENT_SELECTSTART", "selectstart"); /** * Fired when {@link XrInputSource} has ended triggering primary action. The handler is passed * the {@link XrInputSource} that triggered the `selectend` event and the XRInputSourceEvent * event from the WebXR API. * * @event * @example * app.xr.input.on('selectend', (inputSource, evt) => { * console.log('Select ended'); * }); */ __publicField(XrInput, "EVENT_SELECTEND", "selectend"); /** * Fired when {@link XrInputSource} has triggered squeeze action. This is associated with * "grabbing" action on the controllers. The handler is passed the {@link XrInputSource} that * triggered the `squeeze` event and the XRInputSourceEvent event from the WebXR API. * * @event * @example * app.xr.input.on('squeeze', (inputSource, evt) => { * console.log('Squeeze'); * }); */ __publicField(XrInput, "EVENT_SQUEEZE", "squeeze"); /** * Fired when {@link XrInputSource} has started to trigger squeeze action. The handler is * passed the {@link XrInputSource} that triggered the `squeezestart` event and the * XRInputSourceEvent event from the WebXR API. * * @event * @example * app.xr.input.on('squeezestart', (inputSource, evt) => { * if (obj.containsPoint(inputSource.getPosition())) { * // grabbed an object * } * }); */ __publicField(XrInput, "EVENT_SQUEEZESTART", "squeezestart"); /** * Fired when {@link XrInputSource} has ended triggering squeeze action. The handler is passed * the {@link XrInputSource} that triggered the `squeezeend` event and the XRInputSourceEvent * event from the WebXR API. * * @event * @example * app.xr.input.on('squeezeend', (inputSource, evt) => { * console.log('Squeeze ended'); * }); */ __publicField(XrInput, "EVENT_SQUEEZEEND", "squeezeend"); export { XrInput };