@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.
Files changed (103) hide show
  1. package/package.json +1 -1
  2. package/src/engine/Engine.d.ts +2 -0
  3. package/src/engine/Engine.d.ts.map +1 -1
  4. package/src/engine/Engine.js +5 -1
  5. package/src/engine/input/devices/XRInputDevice.d.ts +91 -0
  6. package/src/engine/input/devices/XRInputDevice.d.ts.map +1 -0
  7. package/src/engine/input/devices/XRInputDevice.js +479 -0
  8. package/src/engine/input/devices/xr/XRGamepadMapping.d.ts +14 -0
  9. package/src/engine/input/devices/xr/XRGamepadMapping.d.ts.map +1 -0
  10. package/src/engine/input/devices/xr/XRGamepadMapping.js +9 -0
  11. package/src/engine/input/devices/xr/XRHandedness.d.ts +15 -0
  12. package/src/engine/input/devices/xr/XRHandedness.d.ts.map +1 -0
  13. package/src/engine/input/devices/xr/XRHandedness.js +10 -0
  14. package/src/engine/input/devices/xr/XRInputAction.d.ts +50 -0
  15. package/src/engine/input/devices/xr/XRInputAction.d.ts.map +1 -0
  16. package/src/engine/input/devices/xr/XRInputAction.js +116 -0
  17. package/src/engine/input/devices/xr/XRInputButton.d.ts +26 -0
  18. package/src/engine/input/devices/xr/XRInputButton.d.ts.map +1 -0
  19. package/src/engine/input/devices/xr/XRInputButton.js +53 -0
  20. package/src/engine/input/devices/xr/XRInputCoordinates.d.ts +23 -0
  21. package/src/engine/input/devices/xr/XRInputCoordinates.d.ts.map +1 -0
  22. package/src/engine/input/devices/xr/XRInputCoordinates.js +35 -0
  23. package/src/engine/input/devices/xr/XRInputHandle.d.ts +93 -0
  24. package/src/engine/input/devices/xr/XRInputHandle.d.ts.map +1 -0
  25. package/src/engine/input/devices/xr/XRInputHandle.js +268 -0
  26. package/src/engine/input/devices/xr/XRInputPose.d.ts +59 -0
  27. package/src/engine/input/devices/xr/XRInputPose.d.ts.map +1 -0
  28. package/src/engine/input/devices/xr/XRInputPose.js +77 -0
  29. package/src/engine/input/devices/xr/XRInputTypes.d.ts +76 -0
  30. package/src/engine/input/devices/xr/XRInputTypes.d.ts.map +1 -0
  31. package/src/engine/input/devices/xr/XRInputTypes.js +31 -0
  32. package/src/engine/input/devices/xr/XRInputValueSource.d.ts +11 -0
  33. package/src/engine/input/devices/xr/XRInputValueSource.d.ts.map +1 -0
  34. package/src/engine/input/devices/xr/XRInputValueSource.js +11 -0
  35. package/src/engine/input/devices/xr/XRTargetRayMode.d.ts +17 -0
  36. package/src/engine/input/devices/xr/XRTargetRayMode.d.ts.map +1 -0
  37. package/src/engine/input/devices/xr/XRTargetRayMode.js +12 -0
  38. package/src/engine/input/devices/xr/XRVisibilityState.d.ts +15 -0
  39. package/src/engine/input/devices/xr/XRVisibilityState.d.ts.map +1 -0
  40. package/src/engine/input/devices/xr/XRVisibilityState.js +10 -0
  41. package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.d.ts +15 -1
  42. package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.d.ts.map +1 -1
  43. package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.js +9 -0
  44. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.d.ts +17 -4
  45. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.d.ts.map +1 -1
  46. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.js +26 -0
  47. package/src/engine/input/ecs/ism/coordinate/InputCoordinateSwitches.d.ts +17 -2
  48. package/src/engine/input/ecs/ism/coordinate/InputCoordinateSwitches.d.ts.map +1 -1
  49. package/src/engine/input/ecs/ism/device/GamepadInputDeviceAdapter.d.ts +5 -0
  50. package/src/engine/input/ecs/ism/device/GamepadInputDeviceAdapter.d.ts.map +1 -1
  51. package/src/engine/input/ecs/ism/device/InputDeviceAdapter.d.ts +2 -0
  52. package/src/engine/input/ecs/ism/device/InputDeviceAdapter.d.ts.map +1 -1
  53. package/src/engine/input/ecs/ism/device/InputDeviceAdapter.js +2 -0
  54. package/src/engine/input/ecs/ism/device/KeyboardInputDeviceAdapter.d.ts +7 -0
  55. package/src/engine/input/ecs/ism/device/KeyboardInputDeviceAdapter.d.ts.map +1 -1
  56. package/src/engine/input/ecs/ism/device/ManualInputDeviceAdapter.d.ts +5 -0
  57. package/src/engine/input/ecs/ism/device/ManualInputDeviceAdapter.d.ts.map +1 -1
  58. package/src/engine/input/ecs/ism/device/PointerInputDeviceAdapter.d.ts +5 -0
  59. package/src/engine/input/ecs/ism/device/PointerInputDeviceAdapter.d.ts.map +1 -1
  60. package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.d.ts +30 -0
  61. package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.d.ts.map +1 -0
  62. package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.js +87 -0
  63. package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.d.ts +15 -0
  64. package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.d.ts.map +1 -0
  65. package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.js +22 -0
  66. package/src/engine/input/ecs/ism/trigger/InputTrigger.d.ts +11 -1
  67. package/src/engine/input/ecs/ism/trigger/InputTrigger.d.ts.map +1 -1
  68. package/src/engine/input/ecs/ism/trigger/InputTrigger.js +7 -0
  69. package/src/engine/input/ecs/ism/trigger/InputTriggerChord.d.ts +16 -2
  70. package/src/engine/input/ecs/ism/trigger/InputTriggerChord.d.ts.map +1 -1
  71. package/src/engine/input/ecs/ism/trigger/InputTriggerHold.d.ts +22 -2
  72. package/src/engine/input/ecs/ism/trigger/InputTriggerHold.d.ts.map +1 -1
  73. package/src/engine/input/ecs/ism/trigger/InputTriggerRelease.d.ts +16 -2
  74. package/src/engine/input/ecs/ism/trigger/InputTriggerRelease.d.ts.map +1 -1
  75. package/src/engine/input/ecs/ism/trigger/InputTriggerSequence.d.ts +24 -4
  76. package/src/engine/input/ecs/ism/trigger/InputTriggerSequence.d.ts.map +1 -1
  77. package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.d.ts +6 -8
  78. package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.d.ts.map +1 -1
  79. package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.js +5 -0
  80. package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.d.ts +15 -0
  81. package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.d.ts.map +1 -0
  82. package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.js +25 -0
  83. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  84. package/src/shade/renderer/animation/chunk_pose_accumulator.js +14 -24
  85. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_reduce.js +16 -15
  86. package/src/shade/renderer/deferred/indirect/chunk_lpv_sample_light.js +1 -1
  87. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +1123 -1122
  88. package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +202 -201
  89. package/src/shade/renderer/light/environment/chunk_roughness_to_mip_ratio.js +24 -0
  90. package/src/shade/renderer/light/environment/chunk_sphere_probe_roughness_to_lod.js +2 -25
  91. package/src/shade/renderer/light/environment/shader_project_environment_sh.d.ts +2 -2
  92. package/src/shade/renderer/light/environment/shader_project_environment_sh.d.ts.map +1 -1
  93. package/src/shade/renderer/light/environment/shader_project_environment_sh.js +50 -19
  94. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  95. package/src/shade/renderer/particles/DESIGN.md +1 -1
  96. package/src/shade/renderer/particles/bounds/chunk_particle_bounds.js +10 -9
  97. package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.js +10 -8
  98. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_add_f32_storage.js +25 -0
  99. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_max_f32_storage.js +28 -0
  100. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_min_f32_storage.js +28 -0
  101. package/src/shade/renderer/shader/chunk/geometry/plane/{plane_ray_raycast.js → chunk_plane_ray_raycast.js} +1 -1
  102. package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.js +3 -3
  103. package/src/shade/renderer/animation/skinning/bounds_refresh/chunk_aabb_atomic_f32.js +0 -59
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "description": "Advanced 3D game engine",
7
7
  "type": "module",
8
8
  "author": "Alexander Goldring",
9
- "version": "3.35.0",
9
+ "version": "3.37.0",
10
10
  "exports": {
11
11
  "./build/*": "./build/*",
12
12
  "./src/*": "./src/*",
@@ -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":";6BAoCa,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;OA+OjB;IApSD;;;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;;;;MAIC;IA6BD;;;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,sBA+DC;IAED,aAEC;IAED;;OAEG;IACH,4BAOC;IAIL;;;OAGG;IACH,mBAFU,OAAO,CAEQ;;CAPxB;+BA94B8B,+BAA+B;wBAUtC,wBAAwB;0BApBtB,0CAA0C;wCAcN,iDAAiD;oCAG3E,iCAAiC;mBAElD,wBAAwB;+BArBZ,gDAAgD;iCAO9C,+CAA+C;8BAGlD,wBAAwB;6BALzB,yBAAyB;uCAIf,6DAA6D;4BASxE,0BAA0B;+BApBvB,iCAAiC;4CAapB,qDAAqD;AAejG;;GAEG;AAGH;IACI,gDAA2D;IAC3D,0BAAkC;IAClC,iCAAyC;CAC5C;6BAtC4B,sCAAsC;sBA2B7C,mBAAmB;yBAJhB,yBAAyB;8BALpB,kCAAkC;2BADrC,mCAAmC;8BADhC,kCAAkC;sBAZ1C,+BAA+B;+BAKtB,qCAAqC;4BAPxC,kCAAkC;oBAH1C,yBAAyB"}
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"}
@@ -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,9 @@
1
+ /**
2
+ * Mapping values exposed by an XR input source's gamepad.
3
+ * @readonly
4
+ * @enum {string}
5
+ */
6
+ export const XRGamepadMapping = Object.freeze({
7
+ Unmapped: '',
8
+ Standard: 'xr-standard'
9
+ });
@@ -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