@woosh/meep-engine 3.35.0 → 3.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/engine/Engine.d.ts +2 -0
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +5 -1
- package/src/engine/input/devices/XRInputDevice.d.ts +91 -0
- package/src/engine/input/devices/XRInputDevice.d.ts.map +1 -0
- package/src/engine/input/devices/XRInputDevice.js +479 -0
- package/src/engine/input/devices/xr/XRGamepadMapping.d.ts +14 -0
- package/src/engine/input/devices/xr/XRGamepadMapping.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRGamepadMapping.js +9 -0
- package/src/engine/input/devices/xr/XRHandedness.d.ts +15 -0
- package/src/engine/input/devices/xr/XRHandedness.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRHandedness.js +10 -0
- package/src/engine/input/devices/xr/XRInputAction.d.ts +50 -0
- package/src/engine/input/devices/xr/XRInputAction.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputAction.js +116 -0
- package/src/engine/input/devices/xr/XRInputButton.d.ts +26 -0
- package/src/engine/input/devices/xr/XRInputButton.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputButton.js +53 -0
- package/src/engine/input/devices/xr/XRInputCoordinates.d.ts +23 -0
- package/src/engine/input/devices/xr/XRInputCoordinates.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputCoordinates.js +35 -0
- package/src/engine/input/devices/xr/XRInputHandle.d.ts +93 -0
- package/src/engine/input/devices/xr/XRInputHandle.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputHandle.js +268 -0
- package/src/engine/input/devices/xr/XRInputPose.d.ts +59 -0
- package/src/engine/input/devices/xr/XRInputPose.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputPose.js +77 -0
- package/src/engine/input/devices/xr/XRInputTypes.d.ts +76 -0
- package/src/engine/input/devices/xr/XRInputTypes.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputTypes.js +31 -0
- package/src/engine/input/devices/xr/XRInputValueSource.d.ts +11 -0
- package/src/engine/input/devices/xr/XRInputValueSource.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputValueSource.js +11 -0
- package/src/engine/input/devices/xr/XRTargetRayMode.d.ts +17 -0
- package/src/engine/input/devices/xr/XRTargetRayMode.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRTargetRayMode.js +12 -0
- package/src/engine/input/devices/xr/XRVisibilityState.d.ts +15 -0
- package/src/engine/input/devices/xr/XRVisibilityState.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRVisibilityState.js +10 -0
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.d.ts +15 -1
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.js +9 -0
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.d.ts +17 -4
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.js +26 -0
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateSwitches.d.ts +17 -2
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateSwitches.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/device/GamepadInputDeviceAdapter.d.ts +5 -0
- package/src/engine/input/ecs/ism/device/GamepadInputDeviceAdapter.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/device/InputDeviceAdapter.d.ts +2 -0
- package/src/engine/input/ecs/ism/device/InputDeviceAdapter.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/device/InputDeviceAdapter.js +2 -0
- package/src/engine/input/ecs/ism/device/KeyboardInputDeviceAdapter.d.ts +7 -0
- package/src/engine/input/ecs/ism/device/KeyboardInputDeviceAdapter.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/device/ManualInputDeviceAdapter.d.ts +5 -0
- package/src/engine/input/ecs/ism/device/ManualInputDeviceAdapter.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/device/PointerInputDeviceAdapter.d.ts +5 -0
- package/src/engine/input/ecs/ism/device/PointerInputDeviceAdapter.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.d.ts +30 -0
- package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.d.ts.map +1 -0
- package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.js +87 -0
- package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.d.ts +15 -0
- package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.d.ts.map +1 -0
- package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.js +22 -0
- package/src/engine/input/ecs/ism/trigger/InputTrigger.d.ts +11 -1
- package/src/engine/input/ecs/ism/trigger/InputTrigger.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/trigger/InputTrigger.js +7 -0
- package/src/engine/input/ecs/ism/trigger/InputTriggerChord.d.ts +16 -2
- package/src/engine/input/ecs/ism/trigger/InputTriggerChord.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/trigger/InputTriggerHold.d.ts +22 -2
- package/src/engine/input/ecs/ism/trigger/InputTriggerHold.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/trigger/InputTriggerRelease.d.ts +16 -2
- package/src/engine/input/ecs/ism/trigger/InputTriggerRelease.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/trigger/InputTriggerSequence.d.ts +24 -4
- package/src/engine/input/ecs/ism/trigger/InputTriggerSequence.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.d.ts +6 -8
- package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.js +5 -0
- package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.d.ts +15 -0
- package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.d.ts.map +1 -0
- package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.js +25 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/renderer/animation/chunk_pose_accumulator.js +14 -24
- package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_reduce.js +16 -15
- package/src/shade/renderer/deferred/indirect/chunk_lpv_sample_light.js +1 -1
- package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +1123 -1122
- package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +202 -201
- package/src/shade/renderer/light/environment/chunk_roughness_to_mip_ratio.js +24 -0
- package/src/shade/renderer/light/environment/chunk_sphere_probe_roughness_to_lod.js +2 -25
- package/src/shade/renderer/light/environment/shader_project_environment_sh.d.ts +2 -2
- package/src/shade/renderer/light/environment/shader_project_environment_sh.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/shader_project_environment_sh.js +50 -19
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/particles/DESIGN.md +1 -1
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds.js +10 -9
- package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.js +10 -8
- package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_add_f32_storage.js +25 -0
- package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_max_f32_storage.js +28 -0
- package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_min_f32_storage.js +28 -0
- package/src/shade/renderer/shader/chunk/geometry/plane/{plane_ray_raycast.js → chunk_plane_ray_raycast.js} +1 -1
- package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.js +3 -3
- package/src/shade/renderer/animation/skinning/bounds_refresh/chunk_aabb_atomic_f32.js +0 -59
package/package.json
CHANGED
package/src/engine/Engine.d.ts
CHANGED
|
@@ -114,6 +114,7 @@ declare class Engine {
|
|
|
114
114
|
pointer: PointerDevice;
|
|
115
115
|
keyboard: KeyboardDevice;
|
|
116
116
|
gamepad: GamepadDevice;
|
|
117
|
+
xr: XRInputDevice;
|
|
117
118
|
};
|
|
118
119
|
/**
|
|
119
120
|
* Toggles GraphicsEngine rendering on and off
|
|
@@ -179,6 +180,7 @@ import SceneManager from "./scene/SceneManager.js";
|
|
|
179
180
|
import { PointerDevice } from "./input/devices/PointerDevice.js";
|
|
180
181
|
import KeyboardDevice from "./input/devices/KeyboardDevice.js";
|
|
181
182
|
import { GamepadDevice } from "./input/devices/GamepadDevice.js";
|
|
183
|
+
import { XRInputDevice } from "./input/devices/XRInputDevice.js";
|
|
182
184
|
import EmptyView from "../view/elements/EmptyView.js";
|
|
183
185
|
import { AssetPreloader } from "./asset/preloader/AssetPreloader.js";
|
|
184
186
|
import ObservedBoolean from "../core/model/ObservedBoolean.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../../src/engine/Engine.js"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../../src/engine/Engine.js"],"names":[],"mappings":";6BAqCa,OAAO,8BAA8B,EAAE,cAAc;AAwClE;IAiGI;;;;;;;;;OASG;IACH,sBARW,cAAc;QAEU,aAAa,GAArC,aAAa;QACK,cAAc,GAAhC,OAAO;QACW,WAAW,GAA7B,OAAO;QACW,KAAK,GAAvB,OAAO;OAiPjB;IAtSD;;;OAGG;IACH,UAFU,cAAc,GAAC,IAAI,CAEb;IAEhB;;;OAGG;IACH,OAFU,WAAW,GAAC,IAAI,CAEb;IAEb,qBAA4B;IAE5B;;;;;;;;;OASG;IACH,0BAFU,uBAAuB,CAEe;IAEhD;;;OAGG;IACH,kBAFU,mBAAmB,CAEO;IAEpC;;;;OAIG;IACH,iBAFU,MAAM,CAEM;IAEtB;;;;OAIG;IACH,mBAFU,kBAAkB,CAEa;IAoBrC;;;OAGG;IACH,UAFU,cAAc,CAEA;IAKxB;;;OAGG;IACH,aAFU,gBAAgB,CAEe;IAGzC,yCAAkE;IAK9D,6BAAkC;IAMtC;;;OAGG;IACH,cAFU,aAAa,MAAM,CAAC,CAK5B;IAEF,8CAiCE;IAgCF;;;OAGG;IACH,SAFU,WAAW,CAEoB;IAEzC;;;OAGG;IACH,oCAA0C;IAE1C;;;OAGG;IACH,sCAFU,2BAA2B,CAE+B;IAEpE,yBAAoC;IAGpC;;OAEG;IACH,aAAkB;IAElB;;;OAGG;IACH,SAFU,OAAO,CAEwB;IAGzC,2BAAsC;IAuCtC;;;OAGG;IACH,KAFU,SAAS,CAEO;IAE1B;;;OAGG;IACH,uBAFU,YAAY,CAEqD;IAI3E;;;;;MAKC;IA8BD;;;OAGG;IACH,kBAFU,OAAO,CAEW;IAwIhC,wBAgCC;IApBG,oBAAwB;IAsB5B,2BA8CC;IAED,oBAiBC;IAED;;;OAGG;IACH,+CAUC;IAED,eAkDC;IAED;;;OAGG;IACH,sBAuGC;IAqBD;;OAEG;IACH,sBAgEC;IAED,aAEC;IAED;;OAEG;IACH,4BAOC;IAIL;;;OAGG;IACH,mBAFU,OAAO,CAEQ;;CAPxB;+BAl5B8B,+BAA+B;wBAWtC,wBAAwB;0BArBtB,0CAA0C;wCAeN,iDAAiD;oCAG3E,iCAAiC;mBAElD,wBAAwB;+BAtBZ,gDAAgD;iCAO9C,+CAA+C;8BAGlD,wBAAwB;6BALzB,yBAAyB;uCAIf,6DAA6D;4BAUxE,0BAA0B;+BArBvB,iCAAiC;4CAapB,qDAAqD;AAgBjG;;GAEG;AAGH;IACI,gDAA2D;IAC3D,0BAAkC;IAClC,iCAAyC;CAC5C;6BAvC4B,sCAAsC;sBA4B7C,mBAAmB;yBAJhB,yBAAyB;8BALpB,kCAAkC;2BADrC,mCAAmC;8BAFhC,kCAAkC;8BAClC,kCAAkC;sBAb1C,+BAA+B;+BAKtB,qCAAqC;4BAPxC,kCAAkC;oBAH1C,yBAAyB"}
|
package/src/engine/Engine.js
CHANGED
|
@@ -21,6 +21,7 @@ import { EntityManager } from "./ecs/EntityManager.js";
|
|
|
21
21
|
import { BinarySerializationRegistry } from "./ecs/storage/binary/BinarySerializationRegistry.js";
|
|
22
22
|
import { GraphicsEngine } from './graphics3/GraphicsEngine.js';
|
|
23
23
|
import { GamepadDevice } from "./input/devices/GamepadDevice.js";
|
|
24
|
+
import { XRInputDevice } from "./input/devices/XRInputDevice.js";
|
|
24
25
|
import KeyboardDevice from "./input/devices/KeyboardDevice.js";
|
|
25
26
|
import { PointerDevice } from "./input/devices/PointerDevice.js";
|
|
26
27
|
import { StaticKnowledgeDatabase, StaticKnowledgeState } from "./knowledge/database/StaticKnowledgeDatabase.js";
|
|
@@ -379,12 +380,14 @@ class Engine {
|
|
|
379
380
|
this.devices = {
|
|
380
381
|
pointer: new PointerDevice(this.viewStack.el),
|
|
381
382
|
keyboard: new KeyboardDevice(this.viewStack.el),
|
|
382
|
-
gamepad: new GamepadDevice()
|
|
383
|
+
gamepad: new GamepadDevice(),
|
|
384
|
+
xr: new XRInputDevice()
|
|
383
385
|
};
|
|
384
386
|
|
|
385
387
|
this.devices.pointer.start();
|
|
386
388
|
this.devices.keyboard.start();
|
|
387
389
|
this.devices.gamepad.start();
|
|
390
|
+
this.devices.xr.start();
|
|
388
391
|
|
|
389
392
|
/*
|
|
390
393
|
Only the focused element receives keyboard events. `ViewStack` sets `tabindex` on its
|
|
@@ -869,6 +872,7 @@ class Engine {
|
|
|
869
872
|
this.devices.pointer.stop();
|
|
870
873
|
this.devices.keyboard.stop();
|
|
871
874
|
this.devices.gamepad.stop();
|
|
875
|
+
this.devices.xr.stop();
|
|
872
876
|
|
|
873
877
|
// shutdown entity manager
|
|
874
878
|
if (!this.__using_external_entity_manager) {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebXR input from an application-owned session and frame loop. Construction and {@link start} work
|
|
3
|
+
* without a browser, XR support, or hardware; all controls remain neutral until a session is attached.
|
|
4
|
+
* Engine starts this device automatically. Call {@link start} when using it independently.
|
|
5
|
+
*
|
|
6
|
+
* {@link left}, {@link right} and {@link none} are stable slots, each retaining the oldest available
|
|
7
|
+
* source of that handedness. {@link sources} contains a separate handle for every live source.
|
|
8
|
+
* Control objects retain their identity when a slot changes source, so bindings survive disconnection.
|
|
9
|
+
*
|
|
10
|
+
* Removal, session replacement/end, {@link stop}, and a blurred or hidden session clear controls and
|
|
11
|
+
* cancel unfinished actions. Completed actions are not cancelled. Binding a slot midway through an
|
|
12
|
+
* action does not replay it. Tracking loss clears poses while preserving controls and action state.
|
|
13
|
+
*
|
|
14
|
+
* Lifecycle changes requested inside a signal handler take effect after the current update.
|
|
15
|
+
* Recursive {@link poll} calls are ignored. Frames are consumed synchronously and never retained.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* const xr = engine.devices.xr;
|
|
19
|
+
* // Bind application callbacks before or after attaching a session.
|
|
20
|
+
* xr.right.select.onChanged.add(setToolPressure);
|
|
21
|
+
* xr.right.select.completed.add(useTool);
|
|
22
|
+
* xr.setSession(session, referenceSpace);
|
|
23
|
+
* // In the session's existing requestAnimationFrame callback, before reading input:
|
|
24
|
+
* xr.poll(frame);
|
|
25
|
+
*/
|
|
26
|
+
export class XRInputDevice {
|
|
27
|
+
/** @readonly */
|
|
28
|
+
readonly left: XRInputHandle;
|
|
29
|
+
/** @readonly */
|
|
30
|
+
readonly right: XRInputHandle;
|
|
31
|
+
/** @readonly */
|
|
32
|
+
readonly none: XRInputHandle;
|
|
33
|
+
/**
|
|
34
|
+
* All connected source handles. Do not modify membership.
|
|
35
|
+
* @readonly
|
|
36
|
+
* @type {XRInputHandle[]}
|
|
37
|
+
*/
|
|
38
|
+
readonly sources: XRInputHandle[];
|
|
39
|
+
/** @readonly */
|
|
40
|
+
readonly on: {
|
|
41
|
+
/**
|
|
42
|
+
* Dispatches the connected XRInputHandle after its initial sample.
|
|
43
|
+
* @type {Signal}
|
|
44
|
+
*/
|
|
45
|
+
connected: Signal;
|
|
46
|
+
/**
|
|
47
|
+
* Dispatches the XRInputHandle after its controls are cleared and its source is detached.
|
|
48
|
+
* @type {Signal}
|
|
49
|
+
*/
|
|
50
|
+
disconnected: Signal;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Attached session, or null. Change through {@link setSession}.
|
|
54
|
+
* @readonly
|
|
55
|
+
* @type {import('./xr/XRInputTypes.js').XRSession|null}
|
|
56
|
+
*/
|
|
57
|
+
readonly session: import('./xr/XRInputTypes.js').XRSession | null;
|
|
58
|
+
/**
|
|
59
|
+
* Space in which poses are reported, or null. Change through {@link setReferenceSpace}.
|
|
60
|
+
* @readonly
|
|
61
|
+
* @type {EventTarget|null}
|
|
62
|
+
*/
|
|
63
|
+
readonly referenceSpace: EventTarget | null;
|
|
64
|
+
get isRunning(): boolean;
|
|
65
|
+
/** Start listening to the configured session. Safe to call repeatedly. */
|
|
66
|
+
start(): void;
|
|
67
|
+
/** Detach without ending the application's native XRSession. */
|
|
68
|
+
stop(): void;
|
|
69
|
+
/**
|
|
70
|
+
* Attach an existing session. May be called before {@link start}. Pass null to detach.
|
|
71
|
+
* The application remains responsible for requesting and ending the native session.
|
|
72
|
+
* @param {import('./xr/XRInputTypes.js').XRSession|null} session
|
|
73
|
+
* @param {EventTarget|null} [referenceSpace=null] Null still samples controls, with invalid poses.
|
|
74
|
+
*/
|
|
75
|
+
setSession(session: import('./xr/XRInputTypes.js').XRSession | null, referenceSpace?: EventTarget | null): void;
|
|
76
|
+
/**
|
|
77
|
+
* Change the space used for poses. Existing poses become invalid until the next frame.
|
|
78
|
+
* Pass null to disable pose sampling while continuing to sample controls.
|
|
79
|
+
* @param {EventTarget|null} referenceSpace
|
|
80
|
+
*/
|
|
81
|
+
setReferenceSpace(referenceSpace: EventTarget | null): void;
|
|
82
|
+
/**
|
|
83
|
+
* No-op without a running session. Otherwise call only within that session's active frame callback.
|
|
84
|
+
* @param {import('./xr/XRInputTypes.js').XRFrame} [frame]
|
|
85
|
+
*/
|
|
86
|
+
poll(frame?: import('./xr/XRInputTypes.js').XRFrame): void;
|
|
87
|
+
#private;
|
|
88
|
+
}
|
|
89
|
+
import { XRInputHandle } from "./xr/XRInputHandle.js";
|
|
90
|
+
import Signal from "../../../core/events/signal/Signal.js";
|
|
91
|
+
//# sourceMappingURL=XRInputDevice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XRInputDevice.d.ts","sourceRoot":"","sources":["../../../../../src/engine/input/devices/XRInputDevice.js"],"names":[],"mappings":"AAeA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH;IACI,gBAAgB;IAChB,6BAA2B;IAE3B,gBAAgB;IAChB,8BAA4B;IAE5B,gBAAgB;IAChB,6BAA2B;IAE3B;;;;OAIG;IACH,kBAFU,aAAa,EAAE,CAEZ;IAEb,gBAAgB;IAChB;QACI;;;WAGG;mBADO,MAAM;QAIhB;;;WAGG;sBADO,MAAM;MAGlB;IAUF;;;;OAIG;IACH,kBAFU,OAAO,sBAAsB,EAAE,SAAS,GAAC,IAAI,CAExC;IAEf;;;;OAIG;IACH,yBAFU,WAAW,GAAC,IAAI,CAEJ;IAYtB,yBAEC;IAED,0EAA0E;IAC1E,cAiBC;IAED,gEAAgE;IAChE,aAYC;IAED;;;;;OAKG;IACH,oBAHW,OAAO,sBAAsB,EAAE,SAAS,GAAC,IAAI,mBAC7C,WAAW,GAAC,IAAI,QAwC1B;IAED;;;;OAIG;IACH,kCAFW,WAAW,GAAC,IAAI,QA8B1B;IAED;;;OAGG;IACH,aAFW,OAAO,sBAAsB,EAAE,OAAO,QAkBhD;;CA0OJ;8BA3d6B,uBAAuB;mBADlC,uCAAuC"}
|
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
import { assert } from "../../../core/assert.js";
|
|
2
|
+
import { Deque } from "../../../core/collection/queue/Deque.js";
|
|
3
|
+
import Signal from "../../../core/events/signal/Signal.js";
|
|
4
|
+
import { XRInputHandle } from "./xr/XRInputHandle.js";
|
|
5
|
+
import { XRHandedness } from "./xr/XRHandedness.js";
|
|
6
|
+
import { XRVisibilityState } from "./xr/XRVisibilityState.js";
|
|
7
|
+
|
|
8
|
+
const SESSION_EVENTS = [
|
|
9
|
+
'inputsourceschange',
|
|
10
|
+
'selectstart', 'select', 'selectend',
|
|
11
|
+
'squeezestart', 'squeeze', 'squeezeend',
|
|
12
|
+
'visibilitychange',
|
|
13
|
+
'end'
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* WebXR input from an application-owned session and frame loop. Construction and {@link start} work
|
|
18
|
+
* without a browser, XR support, or hardware; all controls remain neutral until a session is attached.
|
|
19
|
+
* Engine starts this device automatically. Call {@link start} when using it independently.
|
|
20
|
+
*
|
|
21
|
+
* {@link left}, {@link right} and {@link none} are stable slots, each retaining the oldest available
|
|
22
|
+
* source of that handedness. {@link sources} contains a separate handle for every live source.
|
|
23
|
+
* Control objects retain their identity when a slot changes source, so bindings survive disconnection.
|
|
24
|
+
*
|
|
25
|
+
* Removal, session replacement/end, {@link stop}, and a blurred or hidden session clear controls and
|
|
26
|
+
* cancel unfinished actions. Completed actions are not cancelled. Binding a slot midway through an
|
|
27
|
+
* action does not replay it. Tracking loss clears poses while preserving controls and action state.
|
|
28
|
+
*
|
|
29
|
+
* Lifecycle changes requested inside a signal handler take effect after the current update.
|
|
30
|
+
* Recursive {@link poll} calls are ignored. Frames are consumed synchronously and never retained.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* const xr = engine.devices.xr;
|
|
34
|
+
* // Bind application callbacks before or after attaching a session.
|
|
35
|
+
* xr.right.select.onChanged.add(setToolPressure);
|
|
36
|
+
* xr.right.select.completed.add(useTool);
|
|
37
|
+
* xr.setSession(session, referenceSpace);
|
|
38
|
+
* // In the session's existing requestAnimationFrame callback, before reading input:
|
|
39
|
+
* xr.poll(frame);
|
|
40
|
+
*/
|
|
41
|
+
export class XRInputDevice {
|
|
42
|
+
/** @readonly */
|
|
43
|
+
left = new XRInputHandle();
|
|
44
|
+
|
|
45
|
+
/** @readonly */
|
|
46
|
+
right = new XRInputHandle();
|
|
47
|
+
|
|
48
|
+
/** @readonly */
|
|
49
|
+
none = new XRInputHandle();
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* All connected source handles. Do not modify membership.
|
|
53
|
+
* @readonly
|
|
54
|
+
* @type {XRInputHandle[]}
|
|
55
|
+
*/
|
|
56
|
+
sources = [];
|
|
57
|
+
|
|
58
|
+
/** @readonly */
|
|
59
|
+
on = {
|
|
60
|
+
/**
|
|
61
|
+
* Dispatches the connected XRInputHandle after its initial sample.
|
|
62
|
+
* @type {Signal}
|
|
63
|
+
*/
|
|
64
|
+
connected: new Signal(),
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Dispatches the XRInputHandle after its controls are cleared and its source is detached.
|
|
68
|
+
* @type {Signal}
|
|
69
|
+
*/
|
|
70
|
+
disconnected: new Signal()
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
#slots = [this.left, this.right, this.none];
|
|
74
|
+
|
|
75
|
+
/** @type {Map<import('./xr/XRInputTypes.js').XRInputSource, XRInputHandle>} */
|
|
76
|
+
#handles = new Map();
|
|
77
|
+
|
|
78
|
+
/** @type {XRInputHandle[]} */
|
|
79
|
+
#connected = [];
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Attached session, or null. Change through {@link setSession}.
|
|
83
|
+
* @readonly
|
|
84
|
+
* @type {import('./xr/XRInputTypes.js').XRSession|null}
|
|
85
|
+
*/
|
|
86
|
+
session = null;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Space in which poses are reported, or null. Change through {@link setReferenceSpace}.
|
|
90
|
+
* @readonly
|
|
91
|
+
* @type {EventTarget|null}
|
|
92
|
+
*/
|
|
93
|
+
referenceSpace = null;
|
|
94
|
+
|
|
95
|
+
#running = false;
|
|
96
|
+
#busy = false;
|
|
97
|
+
#draining = false;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Configuration changes requested during an update; frames are never queued.
|
|
101
|
+
* @type {Deque<()=>void>}
|
|
102
|
+
*/
|
|
103
|
+
#pending = new Deque();
|
|
104
|
+
|
|
105
|
+
get isRunning() {
|
|
106
|
+
return this.#running;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Start listening to the configured session. Safe to call repeatedly. */
|
|
110
|
+
start() {
|
|
111
|
+
if (this.#busy) {
|
|
112
|
+
this.#pending.addLast(() => this.start());
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (this.#running) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
this.#busy = true;
|
|
119
|
+
try {
|
|
120
|
+
this.#running = true;
|
|
121
|
+
this.#listen(true);
|
|
122
|
+
this.#reconcile();
|
|
123
|
+
this.#sample(null);
|
|
124
|
+
} finally {
|
|
125
|
+
this.#finish();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Detach without ending the application's native XRSession. */
|
|
130
|
+
stop() {
|
|
131
|
+
if (this.#busy) {
|
|
132
|
+
this.#pending.addLast(() => this.stop());
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this.#busy = true;
|
|
136
|
+
try {
|
|
137
|
+
this.#detach();
|
|
138
|
+
this.#running = false;
|
|
139
|
+
} finally {
|
|
140
|
+
this.#finish();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Attach an existing session. May be called before {@link start}. Pass null to detach.
|
|
146
|
+
* The application remains responsible for requesting and ending the native session.
|
|
147
|
+
* @param {import('./xr/XRInputTypes.js').XRSession|null} session
|
|
148
|
+
* @param {EventTarget|null} [referenceSpace=null] Null still samples controls, with invalid poses.
|
|
149
|
+
*/
|
|
150
|
+
setSession(session, referenceSpace = null) {
|
|
151
|
+
assert.defined(session, 'session');
|
|
152
|
+
|
|
153
|
+
if (session === null) {
|
|
154
|
+
referenceSpace = null;
|
|
155
|
+
} else {
|
|
156
|
+
assert.isFunction(session.addEventListener, 'session.addEventListener');
|
|
157
|
+
assert.isFunction(session.removeEventListener, 'session.removeEventListener');
|
|
158
|
+
assert.isArrayLike(session.inputSources, 'session.inputSources');
|
|
159
|
+
assert.enum(session.visibilityState, XRVisibilityState, 'session.visibilityState');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (referenceSpace !== null) {
|
|
163
|
+
assert.isFunction(referenceSpace.addEventListener, 'referenceSpace.addEventListener');
|
|
164
|
+
assert.isFunction(referenceSpace.removeEventListener, 'referenceSpace.removeEventListener');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (this.#busy) {
|
|
168
|
+
this.#pending.addLast(() => this.setSession(session, referenceSpace));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (session === this.session) {
|
|
172
|
+
this.setReferenceSpace(referenceSpace);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
this.#busy = true;
|
|
176
|
+
try {
|
|
177
|
+
this.#detach();
|
|
178
|
+
this.session = session;
|
|
179
|
+
this.referenceSpace = referenceSpace;
|
|
180
|
+
if (this.#running) {
|
|
181
|
+
this.#listen(true);
|
|
182
|
+
this.#reconcile();
|
|
183
|
+
this.#sample(null);
|
|
184
|
+
}
|
|
185
|
+
} finally {
|
|
186
|
+
this.#finish();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Change the space used for poses. Existing poses become invalid until the next frame.
|
|
192
|
+
* Pass null to disable pose sampling while continuing to sample controls.
|
|
193
|
+
* @param {EventTarget|null} referenceSpace
|
|
194
|
+
*/
|
|
195
|
+
setReferenceSpace(referenceSpace) {
|
|
196
|
+
assert.defined(referenceSpace, 'referenceSpace');
|
|
197
|
+
|
|
198
|
+
if (referenceSpace !== null) {
|
|
199
|
+
assert.isFunction(referenceSpace.addEventListener, 'referenceSpace.addEventListener');
|
|
200
|
+
assert.isFunction(referenceSpace.removeEventListener, 'referenceSpace.removeEventListener');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (this.#busy) {
|
|
204
|
+
this.#pending.addLast(() => this.setReferenceSpace(referenceSpace));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (referenceSpace === this.referenceSpace) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
this.#busy = true;
|
|
211
|
+
try {
|
|
212
|
+
if (this.referenceSpace !== null && this.#running) {
|
|
213
|
+
this.referenceSpace.removeEventListener('reset', this.#onReset);
|
|
214
|
+
}
|
|
215
|
+
this.referenceSpace = referenceSpace;
|
|
216
|
+
if (referenceSpace !== null && this.#running) {
|
|
217
|
+
referenceSpace.addEventListener('reset', this.#onReset);
|
|
218
|
+
}
|
|
219
|
+
this.#invalidatePoses();
|
|
220
|
+
} finally {
|
|
221
|
+
this.#finish();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* No-op without a running session. Otherwise call only within that session's active frame callback.
|
|
227
|
+
* @param {import('./xr/XRInputTypes.js').XRFrame} [frame]
|
|
228
|
+
*/
|
|
229
|
+
poll(frame) {
|
|
230
|
+
if (!this.#running || this.session === null || this.#busy) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
assert.defined(frame, 'frame');
|
|
234
|
+
assert.notNull(frame, 'frame');
|
|
235
|
+
assert.equal(frame.session, this.session, 'frame.session');
|
|
236
|
+
assert.isFunction(frame.getPose, 'frame.getPose');
|
|
237
|
+
|
|
238
|
+
this.#busy = true;
|
|
239
|
+
try {
|
|
240
|
+
this.#reconcile();
|
|
241
|
+
this.#sample(frame);
|
|
242
|
+
} finally {
|
|
243
|
+
this.#finish();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** @param {boolean} add */
|
|
248
|
+
#listen(add) {
|
|
249
|
+
const method = add ? 'addEventListener' : 'removeEventListener';
|
|
250
|
+
if (this.session !== null) {
|
|
251
|
+
for (let i = 0; i < SESSION_EVENTS.length; i++) {
|
|
252
|
+
this.session[method](SESSION_EVENTS[i], this.#onEvent);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (this.referenceSpace !== null) {
|
|
256
|
+
this.referenceSpace[method]('reset', this.#onReset);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
#detach() {
|
|
261
|
+
this.#listen(false);
|
|
262
|
+
this.session = null;
|
|
263
|
+
this.referenceSpace = null;
|
|
264
|
+
this.#clear();
|
|
265
|
+
for (let i = 0; i < this.#slots.length; i++) {
|
|
266
|
+
this.#slots[i].bind(null);
|
|
267
|
+
}
|
|
268
|
+
while (this.sources.length > 0) {
|
|
269
|
+
const handle = this.sources.pop();
|
|
270
|
+
this.#handles.delete(handle.nativeSource);
|
|
271
|
+
handle.bind(null);
|
|
272
|
+
this.on.disconnected.send1(handle);
|
|
273
|
+
}
|
|
274
|
+
this.#connected.length = 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** @param {import('./xr/XRInputTypes.js').XRInputSource} source */
|
|
278
|
+
#contains(source) {
|
|
279
|
+
const sources = this.session.inputSources;
|
|
280
|
+
for (let i = 0; i < sources.length; i++) {
|
|
281
|
+
if (sources[i] === source) {
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
#reconcile() {
|
|
289
|
+
if (this.session === null) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
for (let i = this.sources.length - 1; i >= 0; i--) {
|
|
293
|
+
const handle = this.sources[i];
|
|
294
|
+
const source = handle.nativeSource;
|
|
295
|
+
if (!this.#contains(source)) {
|
|
296
|
+
this.sources.splice(i, 1);
|
|
297
|
+
this.#handles.delete(source);
|
|
298
|
+
for (let j = 0; j < this.#slots.length; j++) {
|
|
299
|
+
if (this.#slots[j].nativeSource === source) {
|
|
300
|
+
this.#slots[j].bind(null);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
handle.bind(null);
|
|
304
|
+
this.on.disconnected.send1(handle);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const sources = this.session.inputSources;
|
|
308
|
+
for (let i = 0; i < sources.length; i++) {
|
|
309
|
+
const source = sources[i];
|
|
310
|
+
if (!this.#handles.has(source)) {
|
|
311
|
+
const handle = new XRInputHandle();
|
|
312
|
+
handle.bind(source);
|
|
313
|
+
this.#handles.set(source, handle);
|
|
314
|
+
this.sources.push(handle);
|
|
315
|
+
this.#connected.push(handle);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
this.#bindSlot(this.left, XRHandedness.Left);
|
|
319
|
+
this.#bindSlot(this.right, XRHandedness.Right);
|
|
320
|
+
this.#bindSlot(this.none, XRHandedness.None);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* @param {XRInputHandle} slot
|
|
325
|
+
* @param {string} handedness
|
|
326
|
+
*/
|
|
327
|
+
#bindSlot(slot, handedness) {
|
|
328
|
+
if (slot.connected) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
332
|
+
const source = this.sources[i].nativeSource;
|
|
333
|
+
if (source.handedness === handedness) {
|
|
334
|
+
slot.bind(source);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** @param {import('./xr/XRInputTypes.js').XRFrame|null} frame */
|
|
341
|
+
#sample(frame) {
|
|
342
|
+
if (this.session !== null && this.session.visibilityState === XRVisibilityState.Visible) {
|
|
343
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
344
|
+
this.sources[i].sample(frame, this.referenceSpace);
|
|
345
|
+
}
|
|
346
|
+
for (let i = 0; i < this.#slots.length; i++) {
|
|
347
|
+
this.#slots[i].sample(frame, this.referenceSpace);
|
|
348
|
+
}
|
|
349
|
+
this.#flush();
|
|
350
|
+
} else {
|
|
351
|
+
this.#clear();
|
|
352
|
+
}
|
|
353
|
+
for (let i = 0; i < this.#connected.length; i++) {
|
|
354
|
+
this.on.connected.send1(this.#connected[i]);
|
|
355
|
+
}
|
|
356
|
+
this.#connected.length = 0;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
#clear() {
|
|
360
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
361
|
+
this.sources[i].clear();
|
|
362
|
+
}
|
|
363
|
+
for (let i = 0; i < this.#slots.length; i++) {
|
|
364
|
+
this.#slots[i].clear();
|
|
365
|
+
}
|
|
366
|
+
this.#flush();
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
#flush() {
|
|
370
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
371
|
+
this.sources[i].flush();
|
|
372
|
+
}
|
|
373
|
+
for (let i = 0; i < this.#slots.length; i++) {
|
|
374
|
+
this.#slots[i].flush();
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
#invalidatePoses() {
|
|
379
|
+
for (let i = 0; i < this.sources.length; i++) {
|
|
380
|
+
this.sources[i].invalidatePoses();
|
|
381
|
+
}
|
|
382
|
+
for (let i = 0; i < this.#slots.length; i++) {
|
|
383
|
+
this.#slots[i].invalidatePoses();
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
#onReset = () => {
|
|
388
|
+
if (this.#busy) {
|
|
389
|
+
this.#pending.addLast(() => this.#onReset());
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
this.#busy = true;
|
|
393
|
+
try {
|
|
394
|
+
this.#invalidatePoses();
|
|
395
|
+
} finally {
|
|
396
|
+
this.#finish();
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
/** @param {import('./xr/XRInputTypes.js').XRSessionInputEvent} event */
|
|
401
|
+
#onEvent = (event) => {
|
|
402
|
+
if (!this.#running || event.currentTarget !== this.session || this.#busy) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
this.#busy = true;
|
|
406
|
+
try {
|
|
407
|
+
if (event.type === 'end') {
|
|
408
|
+
this.#detach();
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
this.#reconcile();
|
|
412
|
+
const frame = event.frame ?? null;
|
|
413
|
+
const inputSource = event.inputSource ?? null;
|
|
414
|
+
if (frame !== null && frame.session !== this.session) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
this.#sample(frame);
|
|
418
|
+
if (this.session.visibilityState !== XRVisibilityState.Visible || inputSource === null) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const handle = this.#handles.get(inputSource);
|
|
422
|
+
if (handle === undefined) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
this.#actionEvent(handle, event.type);
|
|
426
|
+
for (let i = 0; i < this.#slots.length; i++) {
|
|
427
|
+
const slot = this.#slots[i];
|
|
428
|
+
if (slot.nativeSource === inputSource) {
|
|
429
|
+
this.#actionEvent(slot, event.type);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
} finally {
|
|
433
|
+
this.#finish();
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* @param {XRInputHandle} handle
|
|
439
|
+
* @param {string} type
|
|
440
|
+
*/
|
|
441
|
+
#actionEvent(handle, type) {
|
|
442
|
+
switch (type) {
|
|
443
|
+
case 'selectstart':
|
|
444
|
+
handle.select.begin();
|
|
445
|
+
break;
|
|
446
|
+
case 'select':
|
|
447
|
+
handle.select.complete();
|
|
448
|
+
break;
|
|
449
|
+
case 'selectend':
|
|
450
|
+
handle.select.end();
|
|
451
|
+
break;
|
|
452
|
+
case 'squeezestart':
|
|
453
|
+
handle.squeeze.begin();
|
|
454
|
+
break;
|
|
455
|
+
case 'squeeze':
|
|
456
|
+
handle.squeeze.complete();
|
|
457
|
+
break;
|
|
458
|
+
case 'squeezeend':
|
|
459
|
+
handle.squeeze.end();
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
#finish() {
|
|
465
|
+
this.#busy = false;
|
|
466
|
+
if (this.#draining) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
this.#draining = true;
|
|
470
|
+
try {
|
|
471
|
+
while (!this.#pending.isEmpty()) {
|
|
472
|
+
const change = this.#pending.removeFirst();
|
|
473
|
+
change();
|
|
474
|
+
}
|
|
475
|
+
} finally {
|
|
476
|
+
this.#draining = false;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mapping values exposed by an XR input source's gamepad.
|
|
3
|
+
*/
|
|
4
|
+
export type XRGamepadMapping = string;
|
|
5
|
+
/**
|
|
6
|
+
* Mapping values exposed by an XR input source's gamepad.
|
|
7
|
+
* @readonly
|
|
8
|
+
* @enum {string}
|
|
9
|
+
*/
|
|
10
|
+
export const XRGamepadMapping: Readonly<{
|
|
11
|
+
Unmapped: "";
|
|
12
|
+
Standard: "xr-standard";
|
|
13
|
+
}>;
|
|
14
|
+
//# sourceMappingURL=XRGamepadMapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"XRGamepadMapping.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/input/devices/xr/XRGamepadMapping.js"],"names":[],"mappings":";;;+BAGU,MAAM;AAHhB;;;;GAIG;AACH;;;GAGG"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native XRInputSource handedness values.
|
|
3
|
+
*/
|
|
4
|
+
export type XRHandedness = string;
|
|
5
|
+
/**
|
|
6
|
+
* Native XRInputSource handedness values.
|
|
7
|
+
* @readonly
|
|
8
|
+
* @enum {string}
|
|
9
|
+
*/
|
|
10
|
+
export const XRHandedness: Readonly<{
|
|
11
|
+
None: "none";
|
|
12
|
+
Left: "left";
|
|
13
|
+
Right: "right";
|
|
14
|
+
}>;
|
|
15
|
+
//# sourceMappingURL=XRHandedness.d.ts.map
|