@woosh/meep-engine 3.36.0 → 3.38.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 (169) hide show
  1. package/package.json +1 -1
  2. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  3. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +2 -0
  4. package/src/engine/ecs/transform/TRANSFORM64_EVENT_PLACED.d.ts +11 -0
  5. package/src/engine/ecs/transform/TRANSFORM64_EVENT_PLACED.d.ts.map +1 -0
  6. package/src/engine/ecs/transform/TRANSFORM64_EVENT_PLACED.js +10 -0
  7. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.d.ts.map +1 -1
  8. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +16 -0
  9. package/src/engine/graphics/ecs/water/Water.d.ts +30 -0
  10. package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
  11. package/src/engine/graphics/ecs/water/Water.js +125 -104
  12. package/src/engine/graphics/ecs/water/WaterSerializationAdapter.d.ts.map +1 -1
  13. package/src/engine/graphics/ecs/water/WaterSerializationAdapter.js +13 -1
  14. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_1_2.d.ts +6 -0
  15. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_1_2.d.ts.map +1 -0
  16. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_1_2.js +18 -0
  17. package/src/engine/graphics3/WaterSystem.d.ts +22 -6
  18. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  19. package/src/engine/graphics3/WaterSystem.js +314 -293
  20. package/src/engine/input/devices/XRInputDevice.d.ts +51 -23
  21. package/src/engine/input/devices/XRInputDevice.d.ts.map +1 -1
  22. package/src/engine/input/devices/XRInputDevice.js +145 -52
  23. package/src/engine/input/devices/xr/XRGamepadMapping.d.ts +14 -0
  24. package/src/engine/input/devices/xr/XRGamepadMapping.d.ts.map +1 -0
  25. package/src/engine/input/devices/xr/XRGamepadMapping.js +9 -0
  26. package/src/engine/input/devices/xr/XRHandedness.d.ts +15 -0
  27. package/src/engine/input/devices/xr/XRHandedness.d.ts.map +1 -0
  28. package/src/engine/input/devices/xr/XRHandedness.js +10 -0
  29. package/src/engine/input/devices/xr/XRInputAction.d.ts +24 -11
  30. package/src/engine/input/devices/xr/XRInputAction.d.ts.map +1 -1
  31. package/src/engine/input/devices/xr/XRInputAction.js +55 -24
  32. package/src/engine/input/devices/xr/XRInputButton.d.ts +7 -9
  33. package/src/engine/input/devices/xr/XRInputButton.d.ts.map +1 -1
  34. package/src/engine/input/devices/xr/XRInputButton.js +18 -8
  35. package/src/engine/input/devices/xr/XRInputCoordinates.d.ts +11 -3
  36. package/src/engine/input/devices/xr/XRInputCoordinates.d.ts.map +1 -1
  37. package/src/engine/input/devices/xr/XRInputCoordinates.js +17 -3
  38. package/src/engine/input/devices/xr/XRInputHandle.d.ts +41 -11
  39. package/src/engine/input/devices/xr/XRInputHandle.d.ts.map +1 -1
  40. package/src/engine/input/devices/xr/XRInputHandle.js +129 -32
  41. package/src/engine/input/devices/xr/XRInputPose.d.ts +41 -5
  42. package/src/engine/input/devices/xr/XRInputPose.d.ts.map +1 -1
  43. package/src/engine/input/devices/xr/XRInputPose.js +57 -16
  44. package/src/engine/input/devices/xr/XRInputTypes.d.ts +18 -18
  45. package/src/engine/input/devices/xr/XRInputTypes.js +3 -3
  46. package/src/engine/input/devices/xr/XRTargetRayMode.d.ts +17 -0
  47. package/src/engine/input/devices/xr/XRTargetRayMode.d.ts.map +1 -0
  48. package/src/engine/input/devices/xr/XRTargetRayMode.js +12 -0
  49. package/src/engine/input/devices/xr/XRVisibilityState.d.ts +15 -0
  50. package/src/engine/input/devices/xr/XRVisibilityState.d.ts.map +1 -0
  51. package/src/engine/input/devices/xr/XRVisibilityState.js +10 -0
  52. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.d.ts +3 -1
  53. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.d.ts.map +1 -1
  54. package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.js +5 -1
  55. package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.d.ts +19 -5
  56. package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.d.ts.map +1 -1
  57. package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.js +42 -9
  58. package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.d.ts +8 -2
  59. package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.d.ts.map +1 -1
  60. package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.js +19 -9
  61. package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.d.ts +10 -2
  62. package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.d.ts.map +1 -1
  63. package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.js +12 -2
  64. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts +13 -14
  65. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts.map +1 -1
  66. package/src/engine/physics/cloth/collider/ClothColliderIndex.js +137 -36
  67. package/src/engine/physics/cloth/collider/cloth_collider_bounds.d.ts +30 -0
  68. package/src/engine/physics/cloth/collider/cloth_collider_bounds.d.ts.map +1 -1
  69. package/src/engine/physics/cloth/collider/cloth_collider_bounds.js +33 -0
  70. package/src/engine/physics/cloth/ecs/ClothColliderFlags.js +2 -1
  71. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts +10 -1
  72. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts.map +1 -1
  73. package/src/engine/physics/cloth/ecs/ClothColliderSystem.js +36 -14
  74. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +15 -2
  75. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
  76. package/src/engine/physics/cloth/ecs/ClothInstance.js +17 -2
  77. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
  78. package/src/engine/physics/cloth/ecs/ClothWorld.js +19 -6
  79. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +18 -20
  80. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -1
  81. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +139 -89
  82. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  83. package/src/engine/physics/ecs/PhysicsSystem.js +32 -16
  84. package/src/shade/playground/water_refraction/README.md +149 -0
  85. package/src/shade/playground/water_refraction/index.html +42 -0
  86. package/src/shade/playground/water_refraction/main.d.ts +2 -0
  87. package/src/shade/playground/water_refraction/main.d.ts.map +1 -0
  88. package/src/shade/playground/water_refraction/main.js +201 -0
  89. package/src/shade/playground/water_refraction/world.d.ts +37 -0
  90. package/src/shade/playground/water_refraction/world.d.ts.map +1 -0
  91. package/src/shade/playground/water_refraction/world.js +137 -0
  92. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  93. package/src/shade/renderer/Renderer.js +4 -0
  94. package/src/shade/renderer/animation/chunk_pose_accumulator.d.ts +2 -3
  95. package/src/shade/renderer/animation/chunk_pose_accumulator.d.ts.map +1 -1
  96. package/src/shade/renderer/animation/chunk_pose_accumulator.js +14 -24
  97. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_reduce.d.ts.map +1 -1
  98. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_reduce.js +16 -15
  99. package/src/shade/renderer/deferred/indirect/chunk_lpv_sample_light.js +1 -1
  100. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +1123 -1122
  101. package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +202 -201
  102. package/src/shade/renderer/light/environment/chunk_roughness_to_mip_ratio.d.ts +8 -0
  103. package/src/shade/renderer/light/environment/chunk_roughness_to_mip_ratio.d.ts.map +1 -0
  104. package/src/shade/renderer/light/environment/chunk_roughness_to_mip_ratio.js +24 -0
  105. package/src/shade/renderer/light/environment/chunk_sphere_probe_roughness_to_lod.d.ts +0 -6
  106. package/src/shade/renderer/light/environment/chunk_sphere_probe_roughness_to_lod.d.ts.map +1 -1
  107. package/src/shade/renderer/light/environment/chunk_sphere_probe_roughness_to_lod.js +2 -25
  108. package/src/shade/renderer/light/environment/shader_project_environment_sh.d.ts +2 -2
  109. package/src/shade/renderer/light/environment/shader_project_environment_sh.d.ts.map +1 -1
  110. package/src/shade/renderer/light/environment/shader_project_environment_sh.js +50 -19
  111. package/src/shade/renderer/particles/DESIGN.md +1 -1
  112. package/src/shade/renderer/particles/bounds/chunk_particle_bounds.d.ts +1 -1
  113. package/src/shade/renderer/particles/bounds/chunk_particle_bounds.d.ts.map +1 -1
  114. package/src/shade/renderer/particles/bounds/chunk_particle_bounds.js +10 -9
  115. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts +17 -0
  116. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts.map +1 -1
  117. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js +7 -0
  118. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_renormalization.d.ts +4 -0
  119. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_renormalization.d.ts.map +1 -0
  120. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_renormalization.js +11 -0
  121. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_rgb_contribution.d.ts +4 -0
  122. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_rgb_contribution.d.ts.map +1 -0
  123. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_rgb_contribution.js +19 -0
  124. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_splat_slice.d.ts.map +1 -1
  125. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_splat_slice.js +5 -3
  126. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_split_counts.d.ts +4 -0
  127. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_split_counts.d.ts.map +1 -0
  128. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_split_counts.js +8 -0
  129. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts +1 -1
  130. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  131. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +21 -2
  132. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_resolve.d.ts.map +1 -1
  133. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_resolve.js +4 -8
  134. package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.d.ts.map +1 -1
  135. package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.js +10 -8
  136. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_add_f32_storage.d.ts +12 -0
  137. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_add_f32_storage.d.ts.map +1 -0
  138. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_add_f32_storage.js +25 -0
  139. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_max_f32_storage.d.ts +12 -0
  140. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_max_f32_storage.d.ts.map +1 -0
  141. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_max_f32_storage.js +28 -0
  142. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_min_f32_storage.d.ts +12 -0
  143. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_min_f32_storage.d.ts.map +1 -0
  144. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_min_f32_storage.js +28 -0
  145. package/src/shade/renderer/shader/chunk/geometry/plane/{plane_ray_raycast.d.ts → chunk_plane_ray_raycast.d.ts} +2 -2
  146. package/src/shade/renderer/shader/chunk/geometry/plane/chunk_plane_ray_raycast.d.ts.map +1 -0
  147. package/src/shade/renderer/shader/chunk/geometry/plane/{plane_ray_raycast.js → chunk_plane_ray_raycast.js} +1 -1
  148. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_newton_ray.d.ts +4 -0
  149. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_newton_ray.d.ts.map +1 -0
  150. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_newton_ray.js +101 -0
  151. package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.js +3 -3
  152. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +62 -29
  153. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  154. package/src/shade/renderer/water/GPUWaterRenderer.js +235 -147
  155. package/src/shade/renderer/water/chunk_water_transport.d.ts +5 -0
  156. package/src/shade/renderer/water/chunk_water_transport.d.ts.map +1 -0
  157. package/src/shade/renderer/water/chunk_water_transport.js +94 -0
  158. package/src/shade/renderer/water/shader_water.d.ts +9 -26
  159. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  160. package/src/shade/renderer/water/shader_water.js +384 -246
  161. package/src/shade/renderer/water/shader_water_avboit.d.ts +12 -0
  162. package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -0
  163. package/src/shade/renderer/water/shader_water_avboit.js +258 -0
  164. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  165. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +13 -0
  166. package/src/shade/renderer/animation/skinning/bounds_refresh/chunk_aabb_atomic_f32.d.ts +0 -29
  167. package/src/shade/renderer/animation/skinning/bounds_refresh/chunk_aabb_atomic_f32.d.ts.map +0 -1
  168. package/src/shade/renderer/animation/skinning/bounds_refresh/chunk_aabb_atomic_f32.js +0 -59
  169. package/src/shade/renderer/shader/chunk/geometry/plane/plane_ray_raycast.d.ts.map +0 -1
@@ -1,46 +1,75 @@
1
+ import { assert } from "../../../../core/assert.js";
1
2
  import Signal from "../../../../core/events/signal/Signal.js";
2
3
  import { Vector1 } from "../../../../core/geom/Vector1.js";
3
4
  import { XRInputValueSource } from "./XRInputValueSource.js";
4
5
 
5
6
  /**
6
- * Observable amount of a select/squeeze action, in [0, 1]. Read x and subscribe to onChanged as with
7
- * any Vector1. The device owns mutation. Scalar changes never manufacture an action outcome.
7
+ * Native interaction lifecycle, independent of pressure.
8
+ * @readonly
9
+ * @enum {number}
10
+ */
11
+ const InteractionState = {
12
+ Idle: 0,
13
+ Started: 1,
14
+ Completed: 2
15
+ };
16
+
17
+ /**
18
+ * Observable amount of a select/squeeze action, in [0, 1]. Read {@link x} and subscribe to
19
+ * {@link onChanged} as with any Vector1. The device owns mutation.
8
20
  *
9
- * Completion/cancellation describe a browser interaction, independently of the amount. There is no
10
- * public up/down state or inferred threshold. The private lifecycle only pairs native events so an
11
- * interrupted interaction is cancelled once and an already completed one is never cancelled.
21
+ * {@link valueSource} identifies a native button amount or an event-derived 0/1 value.
22
+ * {@link completed} fires on native action completion; {@link cancelled} fires when an unfinished
23
+ * interaction ends or is interrupted. Each interaction has at most one outcome. Amount changes alone
24
+ * do not complete or cancel an action.
12
25
  */
13
26
  export class XRInputAction extends Vector1 {
14
- /** @readonly @type {Signal} No arguments. */
27
+ /**
28
+ * Successful native action completion. Dispatches no arguments.
29
+ * @readonly
30
+ * @type {Signal}
31
+ */
15
32
  completed = new Signal();
16
33
 
17
- /** @readonly @type {Signal} No arguments. */
34
+ /**
35
+ * An unfinished interaction ended or was interrupted. Dispatches no arguments.
36
+ * @readonly
37
+ * @type {Signal}
38
+ */
18
39
  cancelled = new Signal();
19
40
 
20
- /** @type {XRInputValueSource} Do not modify directly. */
41
+ /**
42
+ * Origin of the current amount. Do not modify directly.
43
+ * @type {XRInputValueSource}
44
+ */
21
45
  valueSource = XRInputValueSource.Unavailable;
22
46
 
23
- /** 0: idle, 1: awaiting outcome, 2: completed, awaiting native end. */
24
- #interaction = 0;
47
+ #interaction = InteractionState.Idle;
25
48
 
26
49
  #cancelled = false;
27
50
 
28
51
  /**
52
+ * Store the supplied amount and its source.
29
53
  * @internal
30
- * @param {XRInputValueSource} source
31
- * @param {number} value
54
+ * @param {number} [value=0]
55
+ * @param {XRInputValueSource} [source=XRInputValueSource.Unavailable]
32
56
  */
33
- update(source, value) {
57
+ update(value = 0, source = XRInputValueSource.Unavailable) {
58
+ assert.greaterThanOrEqual(value, 0, 'value >= 0');
59
+ assert.lessThanOrEqual(value, 1, 'value <= 1');
60
+ assert.enum(source, XRInputValueSource, 'source');
61
+
34
62
  this.valueSource = source;
35
- this.set(source === XRInputValueSource.Gamepad ? value : source === XRInputValueSource.Action && this.#interaction !== 0 ? 1 : 0);
63
+ this.set(value);
36
64
  }
37
65
 
38
66
  /** @internal Called only for a native start, never inferred from the amount. */
39
67
  begin() {
40
- if (this.#interaction !== 0) {
68
+ if (this.#interaction !== InteractionState.Idle) {
41
69
  return;
42
70
  }
43
- this.#interaction = 1;
71
+
72
+ this.#interaction = InteractionState.Started;
44
73
  if (this.valueSource === XRInputValueSource.Action) {
45
74
  this.set(1);
46
75
  }
@@ -48,20 +77,23 @@ export class XRInputAction extends Vector1 {
48
77
 
49
78
  /** @internal A completion without an observed start is not replayed after a rebind/interruption. */
50
79
  complete() {
51
- if (this.#interaction !== 1) {
80
+ if (this.#interaction !== InteractionState.Started) {
52
81
  return;
53
82
  }
54
- this.#interaction = 2;
83
+
84
+ this.#interaction = InteractionState.Completed;
55
85
  this.completed.send0();
56
86
  }
57
87
 
58
88
  /** @internal */
59
89
  end() {
60
- const cancelled = this.#interaction === 1;
61
- this.#interaction = 0;
90
+ const cancelled = this.#interaction === InteractionState.Started;
91
+ this.#interaction = InteractionState.Idle;
92
+
62
93
  if (this.valueSource === XRInputValueSource.Action) {
63
94
  this.set(0);
64
95
  }
96
+
65
97
  if (cancelled) {
66
98
  this.cancelled.send0();
67
99
  }
@@ -69,10 +101,9 @@ export class XRInputAction extends Vector1 {
69
101
 
70
102
  /** @internal Stage cancellation so the whole handle can become neutral before its notification. */
71
103
  reset() {
72
- this.#cancelled = this.#cancelled || this.#interaction === 1;
73
- this.#interaction = 0;
74
- this.valueSource = XRInputValueSource.Unavailable;
75
- this.set(0);
104
+ this.#cancelled = this.#cancelled || this.#interaction === InteractionState.Started;
105
+ this.#interaction = InteractionState.Idle;
106
+ this.update();
76
107
  }
77
108
 
78
109
  /** @internal */
@@ -1,7 +1,7 @@
1
1
  /**
2
- * One raw GamepadButton: its scalar amount and the two binary facts reported by the browser.
3
- * pressed is not inferred from x; a partially depressed trigger need not be pressed.
4
- * The device owns mutation. Switches and onChanged keep their identity across source replacement.
2
+ * One raw GamepadButton: its amount in [0, 1] and its native pressed/touched switches.
3
+ * {@link pressed} is independent of {@link x}; a partially depressed trigger need not be pressed.
4
+ * The device owns mutation. Switches and {@link onChanged} retain their identity across source replacement.
5
5
  */
6
6
  export class XRInputButton extends Vector1 {
7
7
  /** @readonly */
@@ -12,13 +12,11 @@ export class XRInputButton extends Vector1 {
12
12
  * Stage binary state while updating the scalar. All numeric channels and poses are sampled before
13
13
  * the device calls flushSwitches, so an input-map action can read the corresponding amount.
14
14
  * @internal
15
- * @param {{value: number, pressed: boolean, touched: boolean}|undefined} button
15
+ * @param {number} [value=0]
16
+ * @param {boolean} [pressed=false]
17
+ * @param {boolean} [touched=false]
16
18
  */
17
- update(button: {
18
- value: number;
19
- pressed: boolean;
20
- touched: boolean;
21
- } | undefined): void;
19
+ update(value?: number, pressed?: boolean, touched?: boolean): void;
22
20
  /** @internal */
23
21
  flushSwitches(): void;
24
22
  #private;
@@ -1 +1 @@
1
- {"version":3,"file":"XRInputButton.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/input/devices/xr/XRInputButton.js"],"names":[],"mappings":"AAGA;;;;GAIG;AACH;IACI,gBAAgB;IAChB,oCAAkC;IAClC,gBAAgB;IAChB,oCAAkC;IAKlC;;;;;OAKG;IACH,eAFW;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAC,GAAC,SAAS,QAMvE;IAED,gBAAgB;IAChB,sBAWC;;CACJ;wBA1CuB,kCAAkC;kCACxB,yBAAyB"}
1
+ {"version":3,"file":"XRInputButton.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/input/devices/xr/XRInputButton.js"],"names":[],"mappings":"AAIA;;;;GAIG;AACH;IACI,gBAAgB;IAChB,oCAAkC;IAElC,gBAAgB;IAChB,oCAAkC;IAKlC;;;;;;;OAOG;IACH,eAJW,MAAM,YACN,OAAO,YACP,OAAO,QAWjB;IAED,gBAAgB;IAChB,sBAYC;;CACJ;wBAnDuB,kCAAkC;kCACxB,yBAAyB"}
@@ -1,14 +1,16 @@
1
+ import { assert } from "../../../../core/assert.js";
1
2
  import { Vector1 } from "../../../../core/geom/Vector1.js";
2
3
  import { InputDeviceSwitch } from "../InputDeviceSwitch.js";
3
4
 
4
5
  /**
5
- * One raw GamepadButton: its scalar amount and the two binary facts reported by the browser.
6
- * pressed is not inferred from x; a partially depressed trigger need not be pressed.
7
- * The device owns mutation. Switches and onChanged keep their identity across source replacement.
6
+ * One raw GamepadButton: its amount in [0, 1] and its native pressed/touched switches.
7
+ * {@link pressed} is independent of {@link x}; a partially depressed trigger need not be pressed.
8
+ * The device owns mutation. Switches and {@link onChanged} retain their identity across source replacement.
8
9
  */
9
10
  export class XRInputButton extends Vector1 {
10
11
  /** @readonly */
11
12
  pressed = new InputDeviceSwitch();
13
+
12
14
  /** @readonly */
13
15
  touched = new InputDeviceSwitch();
14
16
 
@@ -19,12 +21,19 @@ export class XRInputButton extends Vector1 {
19
21
  * Stage binary state while updating the scalar. All numeric channels and poses are sampled before
20
22
  * the device calls flushSwitches, so an input-map action can read the corresponding amount.
21
23
  * @internal
22
- * @param {{value: number, pressed: boolean, touched: boolean}|undefined} button
24
+ * @param {number} [value=0]
25
+ * @param {boolean} [pressed=false]
26
+ * @param {boolean} [touched=false]
23
27
  */
24
- update(button) {
25
- this.#pressed = button !== undefined && button.pressed;
26
- this.#touched = button !== undefined && button.touched;
27
- this.set(button === undefined ? 0 : button.value);
28
+ update(value = 0, pressed = false, touched = false) {
29
+ assert.greaterThanOrEqual(value, 0, 'value >= 0');
30
+ assert.lessThanOrEqual(value, 1, 'value <= 1');
31
+ assert.isBoolean(pressed, 'pressed');
32
+ assert.isBoolean(touched, 'touched');
33
+
34
+ this.#pressed = pressed;
35
+ this.#touched = touched;
36
+ this.set(value);
28
37
  }
29
38
 
30
39
  /** @internal */
@@ -34,6 +43,7 @@ export class XRInputButton extends Vector1 {
34
43
  } else {
35
44
  this.pressed.release();
36
45
  }
46
+
37
47
  if (this.#touched) {
38
48
  this.touched.press();
39
49
  } else {
@@ -1,9 +1,17 @@
1
- /** @param {number} index Native button index. @returns {number} Scalar amount, not pressed/touched. */
1
+ /**
2
+ * Code for a raw button's scalar amount. Use InputTriggerXRButton for pressed/touched switches.
3
+ * @param {number} index Native button index.
4
+ * @returns {number}
5
+ */
2
6
  export function xr_input_button_coordinate(index: number): number;
3
- /** @param {number} index Native axis index. @returns {number} */
7
+ /**
8
+ * Code for a raw axis, without stick calibration.
9
+ * @param {number} index Native axis index.
10
+ * @returns {number}
11
+ */
4
12
  export function xr_input_axis_coordinate(index: number): number;
5
13
  /**
6
- * Stable codes used in serialized input maps.
14
+ * Stable coordinate codes used in serialized input maps.
7
15
  */
8
16
  export type XRInputCoordinates = number;
9
17
  export namespace XRInputCoordinates {
@@ -1 +1 @@
1
- {"version":3,"file":"XRInputCoordinates.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/input/devices/xr/XRInputCoordinates.js"],"names":[],"mappings":"AAUA,uGAAuG;AACvG,kDADY,MAAM,GAAuC,MAAM,CAI9D;AAED,iEAAiE;AACjE,gDADY,MAAM,GAAqC,MAAM,CAI5D;;;;iCAlBoB,MAAM"}
1
+ {"version":3,"file":"XRInputCoordinates.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/input/devices/xr/XRInputCoordinates.js"],"names":[],"mappings":"AAcA;;;;GAIG;AACH,kDAHW,MAAM,GACJ,MAAM,CAMlB;AAED;;;;GAIG;AACH,gDAHW,MAAM,GACJ,MAAM,CAMlB;;;;iCA7BS,MAAM"}
@@ -1,6 +1,10 @@
1
1
  import { assert } from "../../../../core/assert.js";
2
2
 
3
- /** @readonly @enum {number} Stable codes used in serialized input maps. */
3
+ /**
4
+ * Stable coordinate codes used in serialized input maps.
5
+ * @readonly
6
+ * @enum {number}
7
+ */
4
8
  export const XRInputCoordinates = {
5
9
  select: 0,
6
10
  squeeze: 1,
@@ -8,14 +12,24 @@ export const XRInputCoordinates = {
8
12
  touchpad: 3
9
13
  };
10
14
 
11
- /** @param {number} index Native button index. @returns {number} Scalar amount, not pressed/touched. */
15
+ /**
16
+ * Code for a raw button's scalar amount. Use InputTriggerXRButton for pressed/touched switches.
17
+ * @param {number} index Native button index.
18
+ * @returns {number}
19
+ */
12
20
  export function xr_input_button_coordinate(index) {
13
21
  assert.isNonNegativeInteger(index, 'index');
22
+
14
23
  return 4 + index * 2;
15
24
  }
16
25
 
17
- /** @param {number} index Native axis index. @returns {number} */
26
+ /**
27
+ * Code for a raw axis, without stick calibration.
28
+ * @param {number} index Native axis index.
29
+ * @returns {number}
30
+ */
18
31
  export function xr_input_axis_coordinate(index) {
19
32
  assert.isNonNegativeInteger(index, 'index');
33
+
20
34
  return 5 + index * 2;
21
35
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * One XR input source, or a persistent handedness slot owned by XRInputDevice.
3
3
  * All control objects survive disconnection. Consumers read them; the device owns mutation.
4
- * Only xr-standard supplies known button/axis semantics. Other mappings remain available through
5
- * buttons/axes, while select/squeeze use the explicitly identified binary action fallback.
4
+ * With xr-standard, select/squeeze read native button amounts. Other mappings remain available through
5
+ * buttons/axes, while select/squeeze report 0/1 from native events; check their valueSource.
6
6
  *
7
7
  * Button indices 0/1 are select/squeeze; axes 0/1 are touchpad and 2/3 thumbstick in xr-standard.
8
8
  * Placeholder entries are allowed by WebXR: a zero entry does not prove that a control exists.
@@ -12,33 +12,63 @@ export class XRInputHandle {
12
12
  readonly select: XRInputAction;
13
13
  /** @readonly */
14
14
  readonly squeeze: XRInputAction;
15
- /** @readonly @type {XRInputButton[]} Use getButton for an index that may not exist yet. */
15
+ /**
16
+ * Raw native buttons. Use {@link getButton} for an index that may not exist yet.
17
+ * @readonly
18
+ * @type {XRInputButton[]}
19
+ */
16
20
  readonly buttons: XRInputButton[];
17
- /** @readonly @type {Vector1[]} Use getAxis for an index that may not exist yet. */
21
+ /**
22
+ * Raw native axes in [-1, 1]. Use {@link getAxis} for an index that may not exist yet.
23
+ * @readonly
24
+ * @type {Vector1[]}
25
+ */
18
26
  readonly axes: Vector1[];
19
- /** @readonly */
27
+ /**
28
+ * Calibrated xr-standard thumbstick, +x right and +y down.
29
+ * @readonly
30
+ */
20
31
  readonly thumbstick: GamepadStick;
21
- /** @readonly Uncalibrated pad coordinates, +x right, +y down. */
32
+ /**
33
+ * Uncalibrated xr-standard touchpad coordinates, +x right and +y down.
34
+ * @readonly
35
+ */
22
36
  readonly touchpad: Vector2;
23
37
  /** @readonly */
24
38
  readonly targetRay: XRInputPose;
25
39
  /** @readonly */
26
40
  readonly grip: XRInputPose;
27
- /** @readonly @type {import('./XRInputTypes.js').XRInputSource|null} Set by the device. */
41
+ /**
42
+ * Native source, or null while disconnected. Set by the device.
43
+ * @readonly
44
+ * @type {import('./XRInputTypes.js').XRInputSource|null}
45
+ */
28
46
  readonly nativeSource: import('./XRInputTypes.js').XRInputSource | null;
29
- /** Number of native entries, excluding controls materialised for bindings. */
47
+ /** Number of native buttons, excluding controls materialised for bindings. Do not modify directly. */
30
48
  buttonCount: number;
49
+ /** Number of native axes, excluding controls materialised for bindings. Do not modify directly. */
31
50
  axisCount: number;
32
51
  get connected(): boolean;
33
52
  get handedness(): string;
34
53
  get targetRayMode(): string;
35
54
  /** @returns {ReadonlyArray<string>} */
36
55
  get profiles(): readonly string[];
37
- /** @param {number} index @returns {XRInputButton} */
56
+ /**
57
+ * Return a stable button, creating neutral entries up to this index if needed.
58
+ * @param {number} index
59
+ * @returns {XRInputButton}
60
+ */
38
61
  getButton(index: number): XRInputButton;
39
- /** @param {number} index @returns {Vector1} */
62
+ /**
63
+ * Return a stable axis, creating neutral entries up to this index if needed.
64
+ * @param {number} index
65
+ * @returns {Vector1}
66
+ */
40
67
  getAxis(index: number): Vector1;
41
- /** @internal @param {import('./XRInputTypes.js').XRInputSource|null} source */
68
+ /**
69
+ * @internal
70
+ * @param {import('./XRInputTypes.js').XRInputSource|null} source
71
+ */
42
72
  bind(source: import('./XRInputTypes.js').XRInputSource | null): void;
43
73
  /**
44
74
  * @internal Sample values and poses before publishing raw switch edges.
@@ -1 +1 @@
1
- {"version":3,"file":"XRInputHandle.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/input/devices/xr/XRInputHandle.js"],"names":[],"mappings":"AAYA;;;;;;;;GAQG;AACH;IACI,gBAAgB;IAChB,+BAA6B;IAC7B,gBAAgB;IAChB,gCAA8B;IAC9B,2FAA2F;IAC3F,kBADqB,aAAa,EAAE,CACvB;IACb,mFAAmF;IACnF,eADqB,OAAO,EAAE,CACpB;IACV,gBAAgB;IAChB,kCAAoC;IACpC,iEAAiE;IACjE,2BAAyB;IACzB,gBAAgB;IAChB,gCAA8B;IAC9B,gBAAgB;IAChB,2BAAyB;IAEzB,0FAA0F;IAC1F,uBADqB,OAAO,mBAAmB,EAAE,aAAa,GAAC,IAAI,CAC/C;IAIpB,8EAA8E;IAC9E,oBAAgB;IAChB,kBAAc;IAOd,yBAAsD;IACtD,yBAA+F;IAC/F,4BAAiG;IACjG,uCAAuC;IACvC,kCAAgG;IAEhG,qDAAqD;IACrD,iBADY,MAAM,GAAkB,aAAa,CAOhD;IAED,+CAA+C;IAC/C,eADY,MAAM,GAAkB,OAAO,CAO1C;IAED,+EAA+E;IAC/E,aADsB,OAAO,mBAAmB,EAAE,aAAa,GAAC,IAAI,QAUnE;IAED;;;;OAIG;IACH,cAHW,OAAO,mBAAmB,EAAE,OAAO,GAAC,IAAI,kBACxC,WAAW,GAAC,IAAI,QA4C1B;IAED,sEAAsE;IACtE,wBAGC;IAED,yFAAyF;IACzF,cAeC;IAED,gBAAgB;IAChB,cAMC;;CACJ;8BAtK6B,oBAAoB;8BACpB,oBAAoB;wBAJ1B,kCAAkC;6BAE7B,4BAA4B;oBADrC,kCAAkC;4BAI1B,kBAAkB"}
1
+ {"version":3,"file":"XRInputHandle.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/input/devices/xr/XRInputHandle.js"],"names":[],"mappings":"AA2BA;;;;;;;;GAQG;AACH;IACI,gBAAgB;IAChB,+BAA6B;IAE7B,gBAAgB;IAChB,gCAA8B;IAE9B;;;;OAIG;IACH,kBAFU,aAAa,EAAE,CAEZ;IAEb;;;;OAIG;IACH,eAFU,OAAO,EAAE,CAET;IAEV;;;OAGG;IACH,kCAAoC;IAEpC;;;OAGG;IACH,2BAAyB;IAEzB,gBAAgB;IAChB,gCAA8B;IAE9B,gBAAgB;IAChB,2BAAyB;IAEzB;;;;OAIG;IACH,uBAFU,OAAO,mBAAmB,EAAE,aAAa,GAAC,IAAI,CAEpC;IAQpB,sGAAsG;IACtG,oBAAgB;IAEhB,mGAAmG;IACnG,kBAAc;IAOd,yBAEC;IAED,yBAEC;IAED,4BAEC;IAED,uCAAuC;IACvC,kCAEC;IAED;;;;OAIG;IACH,iBAHW,MAAM,GACJ,aAAa,CAUzB;IAED;;;;OAIG;IACH,eAHW,MAAM,GACJ,OAAO,CAUnB;IAED;;;OAGG;IACH,aAFW,OAAO,mBAAmB,EAAE,aAAa,GAAC,IAAI,QAgBxD;IAED;;;;OAIG;IACH,cAHW,OAAO,mBAAmB,EAAE,OAAO,GAAC,IAAI,kBACxC,WAAW,GAAC,IAAI,QA+C1B;IAmBD,sEAAsE;IACtE,wBAGC;IAED,yFAAyF;IACzF,cAeC;IAED,gBAAgB;IAChB,cAMC;;CACJ;8BAvQ6B,oBAAoB;8BACpB,oBAAoB;wBAJ1B,kCAAkC;6BAE7B,4BAA4B;oBADrC,kCAAkC;4BAIN,kBAAkB"}
@@ -4,17 +4,32 @@ import Vector2 from "../../../../core/geom/Vector2.js";
4
4
  import { GamepadStick } from "../gamepad/GamepadStick.js";
5
5
  import { XRInputAction } from "./XRInputAction.js";
6
6
  import { XRInputButton } from "./XRInputButton.js";
7
- import { XRInputPose } from "./XRInputPose.js";
7
+ import { XRInputPose, xr_input_read_pose } from "./XRInputPose.js";
8
8
  import { XRInputValueSource } from "./XRInputValueSource.js";
9
+ import { XRHandedness } from "./XRHandedness.js";
10
+ import { XRTargetRayMode } from "./XRTargetRayMode.js";
11
+ import { XRGamepadMapping } from "./XRGamepadMapping.js";
9
12
 
10
- /** @type {ReadonlyArray<string>} */
11
- const NO_PROFILES = Object.freeze([]);
13
+ const DEFAULT_METADATA = Object.freeze({
14
+ handedness: XRHandedness.None,
15
+ targetRayMode: XRTargetRayMode.Unavailable,
16
+ profiles: Object.freeze([])
17
+ });
18
+
19
+ /** @type {import('./XRInputTypes.js').XRGamepad} */
20
+ const DEFAULT_GAMEPAD = Object.freeze({
21
+ mapping: XRGamepadMapping.Unmapped,
22
+ buttons: Object.freeze([]),
23
+ axes: Object.freeze([])
24
+ });
25
+
26
+ const DEFAULT_BUTTON = Object.freeze({ value: 0, pressed: false, touched: false });
12
27
 
13
28
  /**
14
29
  * One XR input source, or a persistent handedness slot owned by XRInputDevice.
15
30
  * All control objects survive disconnection. Consumers read them; the device owns mutation.
16
- * Only xr-standard supplies known button/axis semantics. Other mappings remain available through
17
- * buttons/axes, while select/squeeze use the explicitly identified binary action fallback.
31
+ * With xr-standard, select/squeeze read native button amounts. Other mappings remain available through
32
+ * buttons/axes, while select/squeeze report 0/1 from native events; check their valueSource.
18
33
  *
19
34
  * Button indices 0/1 are select/squeeze; axes 0/1 are touchpad and 2/3 thumbstick in xr-standard.
20
35
  * Placeholder entries are allowed by WebXR: a zero entry does not prove that a control exists.
@@ -22,28 +37,59 @@ const NO_PROFILES = Object.freeze([]);
22
37
  export class XRInputHandle {
23
38
  /** @readonly */
24
39
  select = new XRInputAction();
40
+
25
41
  /** @readonly */
26
42
  squeeze = new XRInputAction();
27
- /** @readonly @type {XRInputButton[]} Use getButton for an index that may not exist yet. */
43
+
44
+ /**
45
+ * Raw native buttons. Use {@link getButton} for an index that may not exist yet.
46
+ * @readonly
47
+ * @type {XRInputButton[]}
48
+ */
28
49
  buttons = [];
29
- /** @readonly @type {Vector1[]} Use getAxis for an index that may not exist yet. */
50
+
51
+ /**
52
+ * Raw native axes in [-1, 1]. Use {@link getAxis} for an index that may not exist yet.
53
+ * @readonly
54
+ * @type {Vector1[]}
55
+ */
30
56
  axes = [];
31
- /** @readonly */
57
+
58
+ /**
59
+ * Calibrated xr-standard thumbstick, +x right and +y down.
60
+ * @readonly
61
+ */
32
62
  thumbstick = new GamepadStick(2, 3);
33
- /** @readonly Uncalibrated pad coordinates, +x right, +y down. */
63
+
64
+ /**
65
+ * Uncalibrated xr-standard touchpad coordinates, +x right and +y down.
66
+ * @readonly
67
+ */
34
68
  touchpad = new Vector2();
69
+
35
70
  /** @readonly */
36
71
  targetRay = new XRInputPose();
72
+
37
73
  /** @readonly */
38
74
  grip = new XRInputPose();
39
75
 
40
- /** @readonly @type {import('./XRInputTypes.js').XRInputSource|null} Set by the device. */
76
+ /**
77
+ * Native source, or null while disconnected. Set by the device.
78
+ * @readonly
79
+ * @type {import('./XRInputTypes.js').XRInputSource|null}
80
+ */
41
81
  nativeSource = null;
82
+
83
+ /** @type {Pick<import('./XRInputTypes.js').XRInputSource, 'handedness'|'targetRayMode'|'profiles'>} */
84
+ #metadata = DEFAULT_METADATA;
85
+
42
86
  /** @type {number[]} */
43
87
  #axisValues = [0, 0, 0, 0];
44
88
 
45
- /** Number of native entries, excluding controls materialised for bindings. */
89
+ /** Number of native buttons, excluding controls materialised for bindings. Do not modify directly. */
46
90
  buttonCount = 0;
91
+
92
+ /** Number of native axes, excluding controls materialised for bindings. Do not modify directly. */
47
93
  axisCount = 0;
48
94
 
49
95
  constructor() {
@@ -51,40 +97,71 @@ export class XRInputHandle {
51
97
  this.getAxis(3);
52
98
  }
53
99
 
54
- get connected() { return this.nativeSource !== null; }
55
- get handedness() { return this.nativeSource === null ? 'none' : this.nativeSource.handedness; }
56
- get targetRayMode() { return this.nativeSource === null ? '' : this.nativeSource.targetRayMode; }
100
+ get connected() {
101
+ return this.nativeSource !== null;
102
+ }
103
+
104
+ get handedness() {
105
+ return this.#metadata.handedness;
106
+ }
107
+
108
+ get targetRayMode() {
109
+ return this.#metadata.targetRayMode;
110
+ }
111
+
57
112
  /** @returns {ReadonlyArray<string>} */
58
- get profiles() { return this.nativeSource === null ? NO_PROFILES : this.nativeSource.profiles; }
113
+ get profiles() {
114
+ return this.#metadata.profiles;
115
+ }
59
116
 
60
- /** @param {number} index @returns {XRInputButton} */
117
+ /**
118
+ * Return a stable button, creating neutral entries up to this index if needed.
119
+ * @param {number} index
120
+ * @returns {XRInputButton}
121
+ */
61
122
  getButton(index) {
62
123
  assert.isNonNegativeInteger(index, 'index');
124
+
63
125
  while (this.buttons.length <= index) {
64
126
  this.buttons.push(new XRInputButton());
65
127
  }
128
+
66
129
  return this.buttons[index];
67
130
  }
68
131
 
69
- /** @param {number} index @returns {Vector1} */
132
+ /**
133
+ * Return a stable axis, creating neutral entries up to this index if needed.
134
+ * @param {number} index
135
+ * @returns {Vector1}
136
+ */
70
137
  getAxis(index) {
71
138
  assert.isNonNegativeInteger(index, 'index');
139
+
72
140
  while (this.axes.length <= index) {
73
141
  this.axes.push(new Vector1());
74
142
  }
143
+
75
144
  return this.axes[index];
76
145
  }
77
146
 
78
- /** @internal @param {import('./XRInputTypes.js').XRInputSource|null} source */
147
+ /**
148
+ * @internal
149
+ * @param {import('./XRInputTypes.js').XRInputSource|null} source
150
+ */
79
151
  bind(source) {
152
+ assert.defined(source, 'source');
153
+ assert.isObject(source, 'source');
154
+
80
155
  if (this.nativeSource === source) {
81
156
  return;
82
157
  }
83
158
  this.nativeSource = null;
159
+ this.#metadata = DEFAULT_METADATA;
84
160
  this.clear();
85
161
  this.flush();
86
162
  this.thumbstick.forgetCalibration();
87
163
  this.nativeSource = source;
164
+ this.#metadata = source ?? DEFAULT_METADATA;
88
165
  }
89
166
 
90
167
  /**
@@ -93,6 +170,9 @@ export class XRInputHandle {
93
170
  * @param {EventTarget|null} referenceSpace
94
171
  */
95
172
  sample(frame, referenceSpace) {
173
+ assert.defined(frame, 'frame');
174
+ assert.defined(referenceSpace, 'referenceSpace');
175
+
96
176
  const source = this.nativeSource;
97
177
  if (source === null) {
98
178
  return;
@@ -100,17 +180,16 @@ export class XRInputHandle {
100
180
  // A source/visibility notification has no pose frame. Preserve the last sample in that case;
101
181
  // only an actual unavailable pose, a reference-space change or cleanup invalidates it.
102
182
  if (frame !== null) {
103
- const ray = referenceSpace !== null ? frame.getPose(source.targetRaySpace, referenceSpace) ?? null : null;
104
- const grip = referenceSpace !== null && source.gripSpace != null
105
- ? frame.getPose(source.gripSpace, referenceSpace) ?? null : null;
183
+ const ray = xr_input_read_pose(frame, source.targetRaySpace, referenceSpace);
184
+ const grip = xr_input_read_pose(frame, source.gripSpace ?? null, referenceSpace);
106
185
  this.targetRay.update(ray);
107
186
  this.grip.update(grip);
108
187
  }
109
188
 
110
- const gamepad = source.gamepad;
111
- const mapped = gamepad != null && gamepad.mapping === 'xr-standard';
112
- this.buttonCount = gamepad == null ? 0 : gamepad.buttons.length;
113
- this.axisCount = gamepad == null ? 0 : gamepad.axes.length;
189
+ const gamepad = source.gamepad ?? DEFAULT_GAMEPAD;
190
+ const mapped = gamepad.mapping === XRGamepadMapping.Standard;
191
+ this.buttonCount = gamepad.buttons.length;
192
+ this.axisCount = gamepad.axes.length;
114
193
  if (this.buttonCount > 0) {
115
194
  this.getButton(this.buttonCount - 1);
116
195
  }
@@ -118,10 +197,11 @@ export class XRInputHandle {
118
197
  this.getAxis(this.axisCount - 1);
119
198
  }
120
199
  for (let i = 0; i < this.buttons.length; i++) {
121
- this.buttons[i].update(i < this.buttonCount ? gamepad.buttons[i] : undefined);
200
+ const button = gamepad.buttons[i] ?? DEFAULT_BUTTON;
201
+ this.buttons[i].update(button.value, button.pressed, button.touched);
122
202
  }
123
203
  for (let i = 0; i < this.axes.length; i++) {
124
- const value = i < this.axisCount ? gamepad.axes[i] : 0;
204
+ const value = gamepad.axes[i] ?? 0;
125
205
  this.#axisValues[i] = value;
126
206
  this.axes[i].set(value);
127
207
  }
@@ -132,14 +212,31 @@ export class XRInputHandle {
132
212
  this.touchpad.set(0, 0);
133
213
  this.thumbstick.reset();
134
214
  }
135
- this.select.update(mapped && this.buttonCount > 0 ? XRInputValueSource.Gamepad : XRInputValueSource.Action, this.buttons[0].x);
136
- this.squeeze.update(mapped && this.buttonCount > 1 ? XRInputValueSource.Gamepad : XRInputValueSource.Action, this.buttons[1].x);
215
+ this.#sampleAction(this.select, 0, mapped);
216
+ this.#sampleAction(this.squeeze, 1, mapped);
217
+ }
218
+
219
+ /**
220
+ * @param {XRInputAction} action
221
+ * @param {number} buttonIndex
222
+ * @param {boolean} mapped
223
+ */
224
+ #sampleAction(action, buttonIndex, mapped) {
225
+ if (mapped && buttonIndex < this.buttonCount) {
226
+ action.update(this.buttons[buttonIndex].x, XRInputValueSource.Gamepad);
227
+ return;
228
+ }
229
+ // Preserve event-driven amounts between native begin/end events. Clear a previous hardware
230
+ // amount when entering event-only mode.
231
+ if (action.valueSource !== XRInputValueSource.Action) {
232
+ action.update(0, XRInputValueSource.Action);
233
+ }
137
234
  }
138
235
 
139
236
  /** @internal Tracking loss does not imply release or cancellation. */
140
237
  invalidatePoses() {
141
- this.targetRay.update(null);
142
- this.grip.update(null);
238
+ this.targetRay.update();
239
+ this.grip.update();
143
240
  }
144
241
 
145
242
  /** @internal Neutralise everything before switch releases and cancellation callbacks. */
@@ -148,7 +245,7 @@ export class XRInputHandle {
148
245
  this.buttonCount = 0;
149
246
  this.axisCount = 0;
150
247
  for (let i = 0; i < this.buttons.length; i++) {
151
- this.buttons[i].update(undefined);
248
+ this.buttons[i].update();
152
249
  }
153
250
  for (let i = 0; i < this.axes.length; i++) {
154
251
  this.axes[i].set(0);