@woosh/meep-engine 3.36.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 (64) hide show
  1. package/package.json +1 -1
  2. package/src/engine/input/devices/XRInputDevice.d.ts +51 -23
  3. package/src/engine/input/devices/XRInputDevice.d.ts.map +1 -1
  4. package/src/engine/input/devices/XRInputDevice.js +145 -52
  5. package/src/engine/input/devices/xr/XRGamepadMapping.d.ts +14 -0
  6. package/src/engine/input/devices/xr/XRGamepadMapping.d.ts.map +1 -0
  7. package/src/engine/input/devices/xr/XRGamepadMapping.js +9 -0
  8. package/src/engine/input/devices/xr/XRHandedness.d.ts +15 -0
  9. package/src/engine/input/devices/xr/XRHandedness.d.ts.map +1 -0
  10. package/src/engine/input/devices/xr/XRHandedness.js +10 -0
  11. package/src/engine/input/devices/xr/XRInputAction.d.ts +24 -11
  12. package/src/engine/input/devices/xr/XRInputAction.d.ts.map +1 -1
  13. package/src/engine/input/devices/xr/XRInputAction.js +55 -24
  14. package/src/engine/input/devices/xr/XRInputButton.d.ts +7 -9
  15. package/src/engine/input/devices/xr/XRInputButton.d.ts.map +1 -1
  16. package/src/engine/input/devices/xr/XRInputButton.js +18 -8
  17. package/src/engine/input/devices/xr/XRInputCoordinates.d.ts +11 -3
  18. package/src/engine/input/devices/xr/XRInputCoordinates.d.ts.map +1 -1
  19. package/src/engine/input/devices/xr/XRInputCoordinates.js +17 -3
  20. package/src/engine/input/devices/xr/XRInputHandle.d.ts +41 -11
  21. package/src/engine/input/devices/xr/XRInputHandle.d.ts.map +1 -1
  22. package/src/engine/input/devices/xr/XRInputHandle.js +129 -32
  23. package/src/engine/input/devices/xr/XRInputPose.d.ts +41 -5
  24. package/src/engine/input/devices/xr/XRInputPose.d.ts.map +1 -1
  25. package/src/engine/input/devices/xr/XRInputPose.js +57 -16
  26. package/src/engine/input/devices/xr/XRInputTypes.d.ts +18 -18
  27. package/src/engine/input/devices/xr/XRInputTypes.js +3 -3
  28. package/src/engine/input/devices/xr/XRTargetRayMode.d.ts +17 -0
  29. package/src/engine/input/devices/xr/XRTargetRayMode.d.ts.map +1 -0
  30. package/src/engine/input/devices/xr/XRTargetRayMode.js +12 -0
  31. package/src/engine/input/devices/xr/XRVisibilityState.d.ts +15 -0
  32. package/src/engine/input/devices/xr/XRVisibilityState.d.ts.map +1 -0
  33. package/src/engine/input/devices/xr/XRVisibilityState.js +10 -0
  34. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.d.ts +3 -1
  35. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.d.ts.map +1 -1
  36. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.js +5 -1
  37. package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.d.ts +19 -5
  38. package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.d.ts.map +1 -1
  39. package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.js +42 -9
  40. package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.d.ts +8 -2
  41. package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.d.ts.map +1 -1
  42. package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.js +19 -9
  43. package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.d.ts +10 -2
  44. package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.d.ts.map +1 -1
  45. package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.js +12 -2
  46. package/src/shade/renderer/animation/chunk_pose_accumulator.js +14 -24
  47. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_reduce.js +16 -15
  48. package/src/shade/renderer/deferred/indirect/chunk_lpv_sample_light.js +1 -1
  49. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +1123 -1122
  50. package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +202 -201
  51. package/src/shade/renderer/light/environment/chunk_roughness_to_mip_ratio.js +24 -0
  52. package/src/shade/renderer/light/environment/chunk_sphere_probe_roughness_to_lod.js +2 -25
  53. package/src/shade/renderer/light/environment/shader_project_environment_sh.d.ts +2 -2
  54. package/src/shade/renderer/light/environment/shader_project_environment_sh.d.ts.map +1 -1
  55. package/src/shade/renderer/light/environment/shader_project_environment_sh.js +50 -19
  56. package/src/shade/renderer/particles/DESIGN.md +1 -1
  57. package/src/shade/renderer/particles/bounds/chunk_particle_bounds.js +10 -9
  58. package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.js +10 -8
  59. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_add_f32_storage.js +25 -0
  60. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_max_f32_storage.js +28 -0
  61. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_min_f32_storage.js +28 -0
  62. package/src/shade/renderer/shader/chunk/geometry/plane/{plane_ray_raycast.js → chunk_plane_ray_raycast.js} +1 -1
  63. package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.js +3 -3
  64. 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.36.0",
9
+ "version": "3.37.0",
10
10
  "exports": {
11
11
  "./build/*": "./build/*",
12
12
  "./src/*": "./src/*",
@@ -1,23 +1,27 @@
1
1
  /**
2
- * Session-owned WebXR input. Safe to construct/start without a browser, XR support, or hardware.
3
- * This device never requests a session or schedules frames. The application owns both:
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.
4
5
  *
5
- * @example
6
- * engine.devices.xr.setSession(session, referenceSpace);
7
- * // In the session's requestAnimationFrame callback, before gameplay reads input:
8
- * engine.devices.xr.poll(frame);
9
- * // Ordinary Vector1 and Signal bindings, valid even before a session exists:
10
- * engine.devices.xr.right.select.onChanged.add(amount => { });
11
- * engine.devices.xr.right.select.completed.add(() => { });
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.
12
13
  *
13
- * left/right/none are stable slots, each retaining the oldest available source of that handedness.
14
- * sources contains every live source, keyed internally by native object identity (never gamepad.index).
15
- * A slot swap neutralises its old controls and cancels unfinished actions before adopting new hardware.
16
- * Completed actions are not cancelled. Binding a slot midway through an action does not replay it.
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.
17
16
  *
18
- * Lifecycle changes requested by signal handlers take effect after the current update. This keeps a
19
- * stop/session replacement from being overwritten by the remainder of that update. Native frames are
20
- * consumed synchronously and never retained. Recursive poll calls are ignored.
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);
21
25
  */
22
26
  export class XRInputDevice {
23
27
  /** @readonly */
@@ -26,30 +30,54 @@ export class XRInputDevice {
26
30
  readonly right: XRInputHandle;
27
31
  /** @readonly */
28
32
  readonly none: XRInputHandle;
29
- /** @readonly @type {XRInputHandle[]} Read only; membership is owned by the device. */
33
+ /**
34
+ * All connected source handles. Do not modify membership.
35
+ * @readonly
36
+ * @type {XRInputHandle[]}
37
+ */
30
38
  readonly sources: XRInputHandle[];
31
39
  /** @readonly */
32
40
  readonly on: {
33
- /** @type {Signal} Dispatches the connected XRInputHandle. */
41
+ /**
42
+ * Dispatches the connected XRInputHandle after its initial sample.
43
+ * @type {Signal}
44
+ */
34
45
  connected: Signal;
35
- /** @type {Signal} Dispatches the XRInputHandle, already neutral and detached. */
46
+ /**
47
+ * Dispatches the XRInputHandle after its controls are cleared and its source is detached.
48
+ * @type {Signal}
49
+ */
36
50
  disconnected: Signal;
37
51
  };
38
- /** @readonly @type {import('./xr/XRInputTypes.js').XRSession|null} Change through setSession. */
52
+ /**
53
+ * Attached session, or null. Change through {@link setSession}.
54
+ * @readonly
55
+ * @type {import('./xr/XRInputTypes.js').XRSession|null}
56
+ */
39
57
  readonly session: import('./xr/XRInputTypes.js').XRSession | null;
40
- /** @readonly @type {EventTarget|null} Change through setReferenceSpace. */
58
+ /**
59
+ * Space in which poses are reported, or null. Change through {@link setReferenceSpace}.
60
+ * @readonly
61
+ * @type {EventTarget|null}
62
+ */
41
63
  readonly referenceSpace: EventTarget | null;
42
64
  get isRunning(): boolean;
65
+ /** Start listening to the configured session. Safe to call repeatedly. */
43
66
  start(): void;
44
67
  /** Detach without ending the application's native XRSession. */
45
68
  stop(): void;
46
69
  /**
47
- * May be called before start. Null detaches; attaching does not request permission or own session.end().
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.
48
72
  * @param {import('./xr/XRInputTypes.js').XRSession|null} session
49
73
  * @param {EventTarget|null} [referenceSpace=null] Null still samples controls, with invalid poses.
50
74
  */
51
75
  setSession(session: import('./xr/XRInputTypes.js').XRSession | null, referenceSpace?: EventTarget | null): void;
52
- /** @param {EventTarget|null} referenceSpace */
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
+ */
53
81
  setReferenceSpace(referenceSpace: EventTarget | null): void;
54
82
  /**
55
83
  * No-op without a running session. Otherwise call only within that session's active frame callback.
@@ -1 +1 @@
1
- {"version":3,"file":"XRInputDevice.d.ts","sourceRoot":"","sources":["../../../../../src/engine/input/devices/XRInputDevice.js"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;IACI,gBAAgB;IAChB,6BAA2B;IAC3B,gBAAgB;IAChB,8BAA4B;IAC5B,gBAAgB;IAChB,6BAA2B;IAC3B,sFAAsF;IACtF,kBADqB,aAAa,EAAE,CACvB;IACb,gBAAgB;IAChB;QACI,6DAA6D;mBAAlD,MAAM;QAEjB,iFAAiF;sBAAtE,MAAM;MAEnB;IAOF,iGAAiG;IACjG,kBADqB,OAAO,sBAAsB,EAAE,SAAS,GAAC,IAAI,CACnD;IACf,2EAA2E;IAC3E,yBADqB,WAAW,GAAC,IAAI,CACf;IAOtB,yBAAyC;IAEzC,cAiBC;IAED,gEAAgE;IAChE,aAYC;IAED;;;;OAIG;IACH,oBAHW,OAAO,sBAAsB,EAAE,SAAS,GAAC,IAAI,mBAC7C,WAAW,GAAC,IAAI,QA4B1B;IAED,+CAA+C;IAC/C,kCADY,WAAW,GAAC,IAAI,QAuB3B;IAED;;;OAGG;IACH,aAFW,OAAO,sBAAsB,EAAE,OAAO,QAehD;;CAsNJ;8BA/X6B,uBAAuB;mBADlC,uCAAuC"}
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"}
@@ -1,68 +1,115 @@
1
1
  import { assert } from "../../../core/assert.js";
2
+ import { Deque } from "../../../core/collection/queue/Deque.js";
2
3
  import Signal from "../../../core/events/signal/Signal.js";
3
4
  import { XRInputHandle } from "./xr/XRInputHandle.js";
5
+ import { XRHandedness } from "./xr/XRHandedness.js";
6
+ import { XRVisibilityState } from "./xr/XRVisibilityState.js";
4
7
 
5
- const SESSION_EVENTS = ['inputsourceschange', 'selectstart', 'select', 'selectend',
6
- 'squeezestart', 'squeeze', 'squeezeend', 'visibilitychange', 'end'];
8
+ const SESSION_EVENTS = [
9
+ 'inputsourceschange',
10
+ 'selectstart', 'select', 'selectend',
11
+ 'squeezestart', 'squeeze', 'squeezeend',
12
+ 'visibilitychange',
13
+ 'end'
14
+ ];
7
15
 
8
16
  /**
9
- * Session-owned WebXR input. Safe to construct/start without a browser, XR support, or hardware.
10
- * This device never requests a session or schedules frames. The application owns both:
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.
11
20
  *
12
- * @example
13
- * engine.devices.xr.setSession(session, referenceSpace);
14
- * // In the session's requestAnimationFrame callback, before gameplay reads input:
15
- * engine.devices.xr.poll(frame);
16
- * // Ordinary Vector1 and Signal bindings, valid even before a session exists:
17
- * engine.devices.xr.right.select.onChanged.add(amount => { });
18
- * engine.devices.xr.right.select.completed.add(() => { });
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.
19
28
  *
20
- * left/right/none are stable slots, each retaining the oldest available source of that handedness.
21
- * sources contains every live source, keyed internally by native object identity (never gamepad.index).
22
- * A slot swap neutralises its old controls and cancels unfinished actions before adopting new hardware.
23
- * Completed actions are not cancelled. Binding a slot midway through an action does not replay it.
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.
24
31
  *
25
- * Lifecycle changes requested by signal handlers take effect after the current update. This keeps a
26
- * stop/session replacement from being overwritten by the remainder of that update. Native frames are
27
- * consumed synchronously and never retained. Recursive poll calls are ignored.
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);
28
40
  */
29
41
  export class XRInputDevice {
30
42
  /** @readonly */
31
43
  left = new XRInputHandle();
44
+
32
45
  /** @readonly */
33
46
  right = new XRInputHandle();
47
+
34
48
  /** @readonly */
35
49
  none = new XRInputHandle();
36
- /** @readonly @type {XRInputHandle[]} Read only; membership is owned by the device. */
50
+
51
+ /**
52
+ * All connected source handles. Do not modify membership.
53
+ * @readonly
54
+ * @type {XRInputHandle[]}
55
+ */
37
56
  sources = [];
57
+
38
58
  /** @readonly */
39
59
  on = {
40
- /** @type {Signal} Dispatches the connected XRInputHandle. */
60
+ /**
61
+ * Dispatches the connected XRInputHandle after its initial sample.
62
+ * @type {Signal}
63
+ */
41
64
  connected: new Signal(),
42
- /** @type {Signal} Dispatches the XRInputHandle, already neutral and detached. */
65
+
66
+ /**
67
+ * Dispatches the XRInputHandle after its controls are cleared and its source is detached.
68
+ * @type {Signal}
69
+ */
43
70
  disconnected: new Signal()
44
71
  };
45
72
 
46
73
  #slots = [this.left, this.right, this.none];
74
+
47
75
  /** @type {Map<import('./xr/XRInputTypes.js').XRInputSource, XRInputHandle>} */
48
76
  #handles = new Map();
77
+
49
78
  /** @type {XRInputHandle[]} */
50
79
  #connected = [];
51
- /** @readonly @type {import('./xr/XRInputTypes.js').XRSession|null} Change through setSession. */
80
+
81
+ /**
82
+ * Attached session, or null. Change through {@link setSession}.
83
+ * @readonly
84
+ * @type {import('./xr/XRInputTypes.js').XRSession|null}
85
+ */
52
86
  session = null;
53
- /** @readonly @type {EventTarget|null} Change through setReferenceSpace. */
87
+
88
+ /**
89
+ * Space in which poses are reported, or null. Change through {@link setReferenceSpace}.
90
+ * @readonly
91
+ * @type {EventTarget|null}
92
+ */
54
93
  referenceSpace = null;
94
+
55
95
  #running = false;
56
96
  #busy = false;
57
97
  #draining = false;
58
- /** @type {Array<()=>void>} Only reentrant configuration changes are queued, never frames. */
59
- #pending = [];
60
98
 
61
- get isRunning() { return this.#running; }
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
+ }
62
108
 
109
+ /** Start listening to the configured session. Safe to call repeatedly. */
63
110
  start() {
64
111
  if (this.#busy) {
65
- this.#pending.push(() => this.start());
112
+ this.#pending.addLast(() => this.start());
66
113
  return;
67
114
  }
68
115
  if (this.#running) {
@@ -82,7 +129,7 @@ export class XRInputDevice {
82
129
  /** Detach without ending the application's native XRSession. */
83
130
  stop() {
84
131
  if (this.#busy) {
85
- this.#pending.push(() => this.stop());
132
+ this.#pending.addLast(() => this.stop());
86
133
  return;
87
134
  }
88
135
  this.#busy = true;
@@ -95,17 +142,30 @@ export class XRInputDevice {
95
142
  }
96
143
 
97
144
  /**
98
- * May be called before start. Null detaches; attaching does not request permission or own session.end().
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.
99
147
  * @param {import('./xr/XRInputTypes.js').XRSession|null} session
100
148
  * @param {EventTarget|null} [referenceSpace=null] Null still samples controls, with invalid poses.
101
149
  */
102
150
  setSession(session, referenceSpace = null) {
103
151
  assert.defined(session, 'session');
152
+
104
153
  if (session === null) {
105
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');
106
160
  }
161
+
162
+ if (referenceSpace !== null) {
163
+ assert.isFunction(referenceSpace.addEventListener, 'referenceSpace.addEventListener');
164
+ assert.isFunction(referenceSpace.removeEventListener, 'referenceSpace.removeEventListener');
165
+ }
166
+
107
167
  if (this.#busy) {
108
- this.#pending.push(() => this.setSession(session, referenceSpace));
168
+ this.#pending.addLast(() => this.setSession(session, referenceSpace));
109
169
  return;
110
170
  }
111
171
  if (session === this.session) {
@@ -116,7 +176,7 @@ export class XRInputDevice {
116
176
  try {
117
177
  this.#detach();
118
178
  this.session = session;
119
- this.referenceSpace = session === null ? null : referenceSpace;
179
+ this.referenceSpace = referenceSpace;
120
180
  if (this.#running) {
121
181
  this.#listen(true);
122
182
  this.#reconcile();
@@ -127,11 +187,21 @@ export class XRInputDevice {
127
187
  }
128
188
  }
129
189
 
130
- /** @param {EventTarget|null} referenceSpace */
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
+ */
131
195
  setReferenceSpace(referenceSpace) {
132
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
+
133
203
  if (this.#busy) {
134
- this.#pending.push(() => this.setReferenceSpace(referenceSpace));
204
+ this.#pending.addLast(() => this.setReferenceSpace(referenceSpace));
135
205
  return;
136
206
  }
137
207
  if (referenceSpace === this.referenceSpace) {
@@ -161,7 +231,10 @@ export class XRInputDevice {
161
231
  return;
162
232
  }
163
233
  assert.defined(frame, 'frame');
234
+ assert.notNull(frame, 'frame');
164
235
  assert.equal(frame.session, this.session, 'frame.session');
236
+ assert.isFunction(frame.getPose, 'frame.getPose');
237
+
165
238
  this.#busy = true;
166
239
  try {
167
240
  this.#reconcile();
@@ -242,12 +315,15 @@ export class XRInputDevice {
242
315
  this.#connected.push(handle);
243
316
  }
244
317
  }
245
- this.#bindSlot(this.left, 'left');
246
- this.#bindSlot(this.right, 'right');
247
- this.#bindSlot(this.none, 'none');
318
+ this.#bindSlot(this.left, XRHandedness.Left);
319
+ this.#bindSlot(this.right, XRHandedness.Right);
320
+ this.#bindSlot(this.none, XRHandedness.None);
248
321
  }
249
322
 
250
- /** @param {XRInputHandle} slot @param {string} handedness */
323
+ /**
324
+ * @param {XRInputHandle} slot
325
+ * @param {string} handedness
326
+ */
251
327
  #bindSlot(slot, handedness) {
252
328
  if (slot.connected) {
253
329
  return;
@@ -263,7 +339,7 @@ export class XRInputDevice {
263
339
 
264
340
  /** @param {import('./xr/XRInputTypes.js').XRFrame|null} frame */
265
341
  #sample(frame) {
266
- if (this.session !== null && this.session.visibilityState === 'visible') {
342
+ if (this.session !== null && this.session.visibilityState === XRVisibilityState.Visible) {
267
343
  for (let i = 0; i < this.sources.length; i++) {
268
344
  this.sources[i].sample(frame, this.referenceSpace);
269
345
  }
@@ -310,7 +386,7 @@ export class XRInputDevice {
310
386
 
311
387
  #onReset = () => {
312
388
  if (this.#busy) {
313
- this.#pending.push(() => this.#onReset());
389
+ this.#pending.addLast(() => this.#onReset());
314
390
  return;
315
391
  }
316
392
  this.#busy = true;
@@ -333,22 +409,23 @@ export class XRInputDevice {
333
409
  return;
334
410
  }
335
411
  this.#reconcile();
336
- const frame = event.frame === undefined ? null : event.frame;
412
+ const frame = event.frame ?? null;
413
+ const inputSource = event.inputSource ?? null;
337
414
  if (frame !== null && frame.session !== this.session) {
338
415
  return;
339
416
  }
340
417
  this.#sample(frame);
341
- if (this.session.visibilityState !== 'visible' || event.inputSource === undefined) {
418
+ if (this.session.visibilityState !== XRVisibilityState.Visible || inputSource === null) {
342
419
  return;
343
420
  }
344
- const handle = this.#handles.get(event.inputSource);
421
+ const handle = this.#handles.get(inputSource);
345
422
  if (handle === undefined) {
346
423
  return;
347
424
  }
348
425
  this.#actionEvent(handle, event.type);
349
426
  for (let i = 0; i < this.#slots.length; i++) {
350
427
  const slot = this.#slots[i];
351
- if (slot.nativeSource === event.inputSource) {
428
+ if (slot.nativeSource === inputSource) {
352
429
  this.#actionEvent(slot, event.type);
353
430
  }
354
431
  }
@@ -357,15 +434,30 @@ export class XRInputDevice {
357
434
  }
358
435
  };
359
436
 
360
- /** @param {XRInputHandle} handle @param {string} type */
437
+ /**
438
+ * @param {XRInputHandle} handle
439
+ * @param {string} type
440
+ */
361
441
  #actionEvent(handle, type) {
362
442
  switch (type) {
363
- case 'selectstart': handle.select.begin(); break;
364
- case 'select': handle.select.complete(); break;
365
- case 'selectend': handle.select.end(); break;
366
- case 'squeezestart': handle.squeeze.begin(); break;
367
- case 'squeeze': handle.squeeze.complete(); break;
368
- case 'squeezeend': handle.squeeze.end(); break;
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;
369
461
  }
370
462
  }
371
463
 
@@ -376,8 +468,9 @@ export class XRInputDevice {
376
468
  }
377
469
  this.#draining = true;
378
470
  try {
379
- while (this.#pending.length > 0) {
380
- this.#pending.shift()();
471
+ while (!this.#pending.isEmpty()) {
472
+ const change = this.#pending.removeFirst();
473
+ change();
381
474
  }
382
475
  } finally {
383
476
  this.#draining = false;
@@ -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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"XRHandedness.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/input/devices/xr/XRHandedness.js"],"names":[],"mappings":";;;2BAGU,MAAM;AAHhB;;;;GAIG;AACH;;;;GAIG"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Native XRInputSource handedness values.
3
+ * @readonly
4
+ * @enum {string}
5
+ */
6
+ export const XRHandedness = Object.freeze({
7
+ None: 'none',
8
+ Left: 'left',
9
+ Right: 'right'
10
+ });
@@ -1,24 +1,37 @@
1
1
  /**
2
- * Observable amount of a select/squeeze action, in [0, 1]. Read x and subscribe to onChanged as with
3
- * any Vector1. The device owns mutation. Scalar changes never manufacture an action outcome.
2
+ * Observable amount of a select/squeeze action, in [0, 1]. Read {@link x} and subscribe to
3
+ * {@link onChanged} as with any Vector1. The device owns mutation.
4
4
  *
5
- * Completion/cancellation describe a browser interaction, independently of the amount. There is no
6
- * public up/down state or inferred threshold. The private lifecycle only pairs native events so an
7
- * interrupted interaction is cancelled once and an already completed one is never cancelled.
5
+ * {@link valueSource} identifies a native button amount or an event-derived 0/1 value.
6
+ * {@link completed} fires on native action completion; {@link cancelled} fires when an unfinished
7
+ * interaction ends or is interrupted. Each interaction has at most one outcome. Amount changes alone
8
+ * do not complete or cancel an action.
8
9
  */
9
10
  export class XRInputAction extends Vector1 {
10
- /** @readonly @type {Signal} No arguments. */
11
+ /**
12
+ * Successful native action completion. Dispatches no arguments.
13
+ * @readonly
14
+ * @type {Signal}
15
+ */
11
16
  readonly completed: Signal;
12
- /** @readonly @type {Signal} No arguments. */
17
+ /**
18
+ * An unfinished interaction ended or was interrupted. Dispatches no arguments.
19
+ * @readonly
20
+ * @type {Signal}
21
+ */
13
22
  readonly cancelled: Signal;
14
- /** @type {XRInputValueSource} Do not modify directly. */
23
+ /**
24
+ * Origin of the current amount. Do not modify directly.
25
+ * @type {XRInputValueSource}
26
+ */
15
27
  valueSource: XRInputValueSource;
16
28
  /**
29
+ * Store the supplied amount and its source.
17
30
  * @internal
18
- * @param {XRInputValueSource} source
19
- * @param {number} value
31
+ * @param {number} [value=0]
32
+ * @param {XRInputValueSource} [source=XRInputValueSource.Unavailable]
20
33
  */
21
- update(source: XRInputValueSource, value: number): void;
34
+ update(value?: number, source?: XRInputValueSource): void;
22
35
  /** @internal Called only for a native start, never inferred from the amount. */
23
36
  begin(): void;
24
37
  /** @internal A completion without an observed start is not replayed after a rebind/interruption. */
@@ -1 +1 @@
1
- {"version":3,"file":"XRInputAction.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/input/devices/xr/XRInputAction.js"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH;IACI,6CAA6C;IAC7C,oBADqB,MAAM,CACF;IAEzB,6CAA6C;IAC7C,oBADqB,MAAM,CACF;IAEzB,yDAAyD;IACzD,aADW,kBAAkB,CACgB;IAO7C;;;;OAIG;IACH,eAHW,kBAAkB,SAClB,MAAM,QAKhB;IAED,gFAAgF;IAChF,cAQC;IAED,oGAAoG;IACpG,iBAMC;IAED,gBAAgB;IAChB,YASC;IAED,mGAAmG;IACnG,cAKC;IAED,gBAAgB;IAChB,0BAKC;;CACJ;wBAnFuB,kCAAkC;mBADvC,0CAA0C;mCAE1B,yBAAyB"}
1
+ {"version":3,"file":"XRInputAction.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/input/devices/xr/XRInputAction.js"],"names":[],"mappings":"AAgBA;;;;;;;;GAQG;AACH;IACI;;;;OAIG;IACH,oBAFU,MAAM,CAES;IAEzB;;;;OAIG;IACH,oBAFU,MAAM,CAES;IAEzB;;;OAGG;IACH,aAFU,kBAAkB,CAEiB;IAM7C;;;;;OAKG;IACH,eAHW,MAAM,WACN,kBAAkB,QAS5B;IAED,gFAAgF;IAChF,cASC;IAED,oGAAoG;IACpG,iBAOC;IAED,gBAAgB;IAChB,YAWC;IAED,mGAAmG;IACnG,cAIC;IAED,gBAAgB;IAChB,0BAKC;;CACJ;wBAjHuB,kCAAkC;mBADvC,0CAA0C;mCAE1B,yBAAyB"}