@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
@@ -104,25 +104,16 @@ export class ClothColliderIndex {
104
104
  /**
105
105
  * The widest {@link CCR_INFLATION} any live record carries, metres.
106
106
  *
107
- * A gather's margin has to cover what the narrowphase will ask for, and
108
- * `cloth_contact_find` reaches `(radius + inflation) · (1 + margin)` from
109
- * each particle. The gather cannot know which colliders it is about to find,
110
- * so it takes the widest in the index — which is why this is here rather
111
- * than derived per query.
112
- *
113
- * Exact after {@link advance}, and only ever raised between advances by
114
- * {@link add} and {@link setSurface}. Stale-high is a wider margin; the
115
- * other direction is a missed contact, so the maximum is never lowered
116
- * inside a step.
107
+ * Diagnostic upper bound, exact after {@link advance} and only raised
108
+ * between advances. Contact queries use each collider's own reach instead.
117
109
  * @type {number}
118
110
  */
119
111
  max_inflation: number;
120
112
  /**
121
113
  * The furthest any live collider has moved since {@link advance}, metres.
122
114
  *
123
- * The narrowphase adds each collider's own travel to its reach on top of
124
- * testing the swept bounds, so the gather's margin has to carry it too or
125
- * the set it hands over is smaller than the set the rows are drawn from.
115
+ * Diagnostic upper bound. Contact queries carry travel on each collider's
116
+ * leaf, so a remote outlier cannot widen every cloth's query.
126
117
  * @type {number}
127
118
  */
128
119
  max_collider_travel: number;
@@ -154,6 +145,11 @@ export class ClothColliderIndex {
154
145
  * @returns {boolean}
155
146
  */
156
147
  isLive(handle: number): boolean;
148
+ /**
149
+ * @param {number} handle
150
+ * @returns {number} revision of the live record
151
+ */
152
+ revisionOf(handle: number): number;
157
153
  /**
158
154
  * Roll every live record's current pose into its previous one. Called once
159
155
  * per fixed step, before any pose for this step is written.
@@ -235,9 +231,12 @@ export class ClothColliderIndex {
235
231
  * @param {number} max_z
236
232
  * @param {number} layer the querier's layer
237
233
  * @param {number} mask the querier's mask
234
+ * @param {boolean} [contact] include each collider's own travel and contact
235
+ * inflation and conversion allowance; the caller supplies particle radius,
236
+ * travel and its local-coordinate conversion allowance in the query box
238
237
  * @returns {number} how many handles were written
239
238
  */
240
- query(out: number[], min_x: number, min_y: number, min_z: number, max_x: number, max_y: number, max_z: number, layer: number, mask: number): number;
239
+ query(out: number[], min_x: number, min_y: number, min_z: number, max_x: number, max_y: number, max_z: number, layer: number, mask: number, contact?: boolean): number;
241
240
  #private;
242
241
  }
243
242
  //# sourceMappingURL=ClothColliderIndex.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ClothColliderIndex.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/collider/ClothColliderIndex.js"],"names":[],"mappings":"AAuCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgFG;AACH;IAEI;;;;;;OAMG;IACH,OAFU,YAAY,CAEM;IAE5B;;;;;OAKG;IACH,MAFU,UAAU,CAEK;IAEzB;;;;OAIG;IACH,OAFU,MAAM,CAEN;IAEV;;;;;;;;;;;;;;OAcG;IACH,eAFU,MAAM,CAEE;IAElB;;;;;;;OAOG;IACH,qBAFU,MAAM,CAEQ;IAmExB;;;;;;;;;;;OAWG;IACH,sCAPW,MAAM,aACN,MAAM,SACN,MAAM,QACN,MAAM,UACN,MAAM,GACJ,MAAM,CA6DlB;IAED;;;;;;;OAOG;IACH,eAHW,MAAM,GACJ,IAAI,CAoChB;IAED;;;;OAIG;IACH,eAHW,MAAM,GACJ,OAAO,CAInB;IAED;;;;OAIG;IACH,WAFa,IAAI,CA0ChB;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,gBAVW,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,GACJ,IAAI,CAkFhB;IAyCD;;;;;;;;;;;;;;;OAeG;IACH,kBAVW,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,GACJ,IAAI,CAiBhB;IAED;;;;;;;;OAQG;IACH,mBALW,MAAM,YACN,MAAM,aACN,MAAM,GACJ,IAAI,CAchB;IAED;;;;;OAKG;IACH,kBALW,MAAM,SACN,MAAM,QACN,MAAM,GACJ,IAAI,CAOhB;IAED;;;OAGG;IACH,iBAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,WAXW,MAAM,EAAE,SACR,MAAM,SACN,MAAM,SACN,MAAM,SACN,MAAM,SACN,MAAM,SACN,MAAM,SACN,MAAM,QACN,MAAM,GACJ,MAAM,CA6DlB;;CA+FJ"}
1
+ {"version":3,"file":"ClothColliderIndex.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/collider/ClothColliderIndex.js"],"names":[],"mappings":"AA2CA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgFG;AACH;IAEI;;;;;;OAMG;IACH,OAFU,YAAY,CAEM;IAE5B;;;;;OAKG;IACH,MAFU,UAAU,CAEK;IAEzB;;;;OAIG;IACH,OAFU,MAAM,CAEN;IAEV;;;;;;OAMG;IACH,eAFU,MAAM,CAEE;IAElB;;;;;;OAMG;IACH,qBAFU,MAAM,CAEQ;IAoFxB;;;;;;;;;;;OAWG;IACH,sCAPW,MAAM,aACN,MAAM,SACN,MAAM,QACN,MAAM,UACN,MAAM,GACJ,MAAM,CA8DlB;IAED;;;;;;;OAOG;IACH,eAHW,MAAM,GACJ,IAAI,CAoChB;IAED;;;;OAIG;IACH,eAHW,MAAM,GACJ,OAAO,CAInB;IAED;;;OAGG;IACH,mBAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;OAIG;IACH,WAFa,IAAI,CAuDhB;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,gBAVW,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,GACJ,IAAI,CA2BhB;IAwID;;;;;;;;;;;;;;;OAeG;IACH,kBAVW,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,GACJ,IAAI,CAiBhB;IAED;;;;;;;;OAQG;IACH,mBALW,MAAM,YACN,MAAM,aACN,MAAM,GACJ,IAAI,CAoBhB;IAED;;;;;OAKG;IACH,kBALW,MAAM,SACN,MAAM,QACN,MAAM,GACJ,IAAI,CAWhB;IAED;;;OAGG;IACH,iBAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,WAdW,MAAM,EAAE,SACR,MAAM,SACN,MAAM,SACN,MAAM,SACN,MAAM,SACN,MAAM,SACN,MAAM,SACN,MAAM,QACN,MAAM,YACN,OAAO,GAGL,MAAM,CA6DlB;;CA6GJ"}
@@ -17,6 +17,9 @@ import {
17
17
  CCR_META_MASK,
18
18
  CCR_META_STRIDE,
19
19
  CCR_KIND,
20
+ CCR_PARAM_0,
21
+ CCR_PARAM_1,
22
+ CCR_PARAM_2,
20
23
  CCR_PREV_QW,
21
24
  CCR_PREV_QX,
22
25
  CCR_PREV_QY,
@@ -33,9 +36,10 @@ import {
33
36
  CCR_TY,
34
37
  CCR_TZ
35
38
  } from "./ClothColliderRecord.js";
36
- import { cloth_collider_refit_bounds } from "./cloth_collider_bounds.js";
39
+ import { cloth_collider_refit_bounds, cloth_contact_rounding } from "./cloth_collider_bounds.js";
37
40
  import { cloth_collider_pack } from "./cloth_collider_pack.js";
38
41
  import { ClothColliderKind } from "./ClothColliderKind.js";
42
+ import { CLOTH_CONTACT_MARGIN } from "../solver/cloth_contact_find.js";
39
43
 
40
44
  /**
41
45
  * # The index of live cloth colliders
@@ -147,16 +151,8 @@ export class ClothColliderIndex {
147
151
  /**
148
152
  * The widest {@link CCR_INFLATION} any live record carries, metres.
149
153
  *
150
- * A gather's margin has to cover what the narrowphase will ask for, and
151
- * `cloth_contact_find` reaches `(radius + inflation) · (1 + margin)` from
152
- * each particle. The gather cannot know which colliders it is about to find,
153
- * so it takes the widest in the index — which is why this is here rather
154
- * than derived per query.
155
- *
156
- * Exact after {@link advance}, and only ever raised between advances by
157
- * {@link add} and {@link setSurface}. Stale-high is a wider margin; the
158
- * other direction is a missed contact, so the maximum is never lowered
159
- * inside a step.
154
+ * Diagnostic upper bound, exact after {@link advance} and only raised
155
+ * between advances. Contact queries use each collider's own reach instead.
160
156
  * @type {number}
161
157
  */
162
158
  max_inflation = 0;
@@ -164,9 +160,8 @@ export class ClothColliderIndex {
164
160
  /**
165
161
  * The furthest any live collider has moved since {@link advance}, metres.
166
162
  *
167
- * The narrowphase adds each collider's own travel to its reach on top of
168
- * testing the swept bounds, so the gather's margin has to carry it too or
169
- * the set it hands over is smaller than the set the rows are drawn from.
163
+ * Diagnostic upper bound. Contact queries carry travel on each collider's
164
+ * leaf, so a remote outlier cannot widen every cloth's query.
170
165
  * @type {number}
171
166
  */
172
167
  max_collider_travel = 0;
@@ -214,6 +209,23 @@ export class ClothColliderIndex {
214
209
  */
215
210
  #node = new Uint32Array(0);
216
211
 
212
+ /**
213
+ * Each collider's speculative contact reach: its own translation travel
214
+ * plus inflation. Stored on the leaf instead of widening every cloth's
215
+ * query by the largest motion anywhere in the world.
216
+ * @type {Float64Array}
217
+ * @private
218
+ */
219
+ #reach = new Float64Array(0);
220
+
221
+ /**
222
+ * Changes to a handle's identity, pose, surface or filter. A placement has
223
+ * equal previous/current poses but must still wake cloth resting against it.
224
+ * @type {Uint32Array}
225
+ * @private
226
+ */
227
+ #revision = new Uint32Array(0);
228
+
217
229
  /**
218
230
  * Handles of live half-spaces, which cannot go in the tree. Empty in every
219
231
  * scene this engine has; see the class docblock.
@@ -277,6 +289,7 @@ export class ClothColliderIndex {
277
289
  this.meta[m + CCR_META_ENTITY] = entity;
278
290
 
279
291
  this.#live[handle] = 1;
292
+ this.#revision[handle]++;
280
293
 
281
294
  // No pose yet, and a record with an uninitialised rotation is a
282
295
  // quaternion of all zeros — which normalises to nothing and poses
@@ -362,6 +375,14 @@ export class ClothColliderIndex {
362
375
  return handle >= 0 && handle < this.count && this.#live[handle] === 1;
363
376
  }
364
377
 
378
+ /**
379
+ * @param {number} handle
380
+ * @returns {number} revision of the live record
381
+ */
382
+ revisionOf(handle) {
383
+ return this.#revision[handle];
384
+ }
385
+
365
386
  /**
366
387
  * Roll every live record's current pose into its previous one. Called once
367
388
  * per fixed step, before any pose for this step is written.
@@ -398,6 +419,14 @@ export class ClothColliderIndex {
398
419
  this.max_inflation = table[r + CCR_INFLATION];
399
420
  }
400
421
 
422
+ const moved = table[r + CCR_PREV_TX] !== table[r + CCR_TX]
423
+ || table[r + CCR_PREV_TY] !== table[r + CCR_TY]
424
+ || table[r + CCR_PREV_TZ] !== table[r + CCR_TZ]
425
+ || table[r + CCR_PREV_QX] !== table[r + CCR_QX]
426
+ || table[r + CCR_PREV_QY] !== table[r + CCR_QY]
427
+ || table[r + CCR_PREV_QZ] !== table[r + CCR_QZ]
428
+ || table[r + CCR_PREV_QW] !== table[r + CCR_QW];
429
+
401
430
  table[r + CCR_PREV_TX] = table[r + CCR_TX];
402
431
  table[r + CCR_PREV_TY] = table[r + CCR_TY];
403
432
  table[r + CCR_PREV_TZ] = table[r + CCR_TZ];
@@ -406,6 +435,11 @@ export class ClothColliderIndex {
406
435
  table[r + CCR_PREV_QY] = table[r + CCR_QY];
407
436
  table[r + CCR_PREV_QZ] = table[r + CCR_QZ];
408
437
  table[r + CCR_PREV_QW] = table[r + CCR_QW];
438
+
439
+ if (moved) {
440
+ cloth_collider_refit_bounds(table, handle, CCR_STRIDE);
441
+ this.#refit(handle);
442
+ }
409
443
  }
410
444
  }
411
445
 
@@ -434,6 +468,12 @@ export class ClothColliderIndex {
434
468
 
435
469
  const r = handle * CCR_STRIDE;
436
470
 
471
+ if (table[r + CCR_TX] !== tx || table[r + CCR_TY] !== ty || table[r + CCR_TZ] !== tz
472
+ || table[r + CCR_QX] !== qx || table[r + CCR_QY] !== qy
473
+ || table[r + CCR_QZ] !== qz || table[r + CCR_QW] !== qw) {
474
+ this.#revision[handle]++;
475
+ }
476
+
437
477
  table[r + CCR_TX] = tx;
438
478
  table[r + CCR_TY] = ty;
439
479
  table[r + CCR_TZ] = tz;
@@ -445,12 +485,37 @@ export class ClothColliderIndex {
445
485
 
446
486
  cloth_collider_refit_bounds(this.table, handle, CCR_STRIDE);
447
487
 
488
+ this.#refit(handle);
489
+ }
490
+
491
+ /**
492
+ * @param {number} handle
493
+ * @private
494
+ */
495
+ #refit(handle) {
496
+ const table = this.table;
497
+ const r = handle * CCR_STRIDE;
498
+
448
499
  const travel = Math.hypot(
449
- tx - table[r + CCR_PREV_TX],
450
- ty - table[r + CCR_PREV_TY],
451
- tz - table[r + CCR_PREV_TZ]
500
+ table[r + CCR_TX] - table[r + CCR_PREV_TX],
501
+ table[r + CCR_TY] - table[r + CCR_PREV_TY],
502
+ table[r + CCR_TZ] - table[r + CCR_PREV_TZ]
452
503
  );
453
504
 
505
+ const contact_reach = travel + Math.max(0, table[r + CCR_INFLATION]) * (1 + CLOTH_CONTACT_MARGIN);
506
+ // The sum of the bounded primitive's dimensions bounds its radius.
507
+ // Include conversion error in BOTH the tree leaf and the final test;
508
+ // the BVH's storage slack alone is discarded by the latter.
509
+ const geometry = table[r + CCR_KIND] === ClothColliderKind.Plane ? 0
510
+ : Math.abs(table[r + CCR_PARAM_0]) + Math.abs(table[r + CCR_PARAM_1])
511
+ + Math.abs(table[r + CCR_PARAM_2]);
512
+ const local_scale = geometry + contact_reach;
513
+ const world_scale = local_scale
514
+ + Math.abs(table[r + CCR_TX]) + Math.abs(table[r + CCR_TY]) + Math.abs(table[r + CCR_TZ])
515
+ + Math.abs(table[r + CCR_PREV_TX]) + Math.abs(table[r + CCR_PREV_TY]) + Math.abs(table[r + CCR_PREV_TZ]);
516
+ const reach = contact_reach + cloth_contact_rounding(local_scale, world_scale);
517
+ this.#reach[handle] = reach;
518
+
454
519
  if (travel > this.max_collider_travel) {
455
520
  this.max_collider_travel = travel;
456
521
  }
@@ -485,12 +550,20 @@ export class ClothColliderIndex {
485
550
  // tree.
486
551
  this.#bvh.node_get_aabb(node, STORED_AABB);
487
552
 
488
- if (table[r + CCR_AABB_MIN_X] >= STORED_AABB[0]
489
- && table[r + CCR_AABB_MIN_Y] >= STORED_AABB[1]
490
- && table[r + CCR_AABB_MIN_Z] >= STORED_AABB[2]
491
- && table[r + CCR_AABB_MAX_X] <= STORED_AABB[3]
492
- && table[r + CCR_AABB_MAX_Y] <= STORED_AABB[4]
493
- && table[r + CCR_AABB_MAX_Z] <= STORED_AABB[5]) {
553
+ let contained = true;
554
+ let oversized = false;
555
+ for (let axis = 0; axis < 3; axis++) {
556
+ const lower = table[r + CCR_AABB_MIN_X + axis] - reach;
557
+ const upper = table[r + CCR_AABB_MAX_X + axis] + reach;
558
+ if (lower < STORED_AABB[axis] || upper > STORED_AABB[axis + 3]) contained = false;
559
+ // A one-step outlier must not leave a scene-sized leaf behind once
560
+ // motion stops. Allow ordinary jitter to keep spending the slack.
561
+ if (STORED_AABB[axis + 3] - STORED_AABB[axis]
562
+ > 2 * (upper - lower + 2 * BVH_LEAF_SLACK
563
+ + (Math.abs(lower) + Math.abs(upper)) * FLOAT32_ROUNDING)) oversized = true;
564
+ }
565
+
566
+ if (contained && !oversized) {
494
567
  return;
495
568
  }
496
569
 
@@ -510,8 +583,8 @@ export class ClothColliderIndex {
510
583
  }
511
584
 
512
585
  /**
513
- * Write a record's swept bounds into {@link FAT_AABB} with the slack the
514
- * refit skip spends, and store them on the leaf.
586
+ * Write a record's swept bounds plus its contact reach into {@link FAT_AABB}
587
+ * with the slack the refit skip spends, and store them on the leaf.
515
588
  *
516
589
  * The slack is also what makes the tree's `Float32` nodes safe to hold a
517
590
  * `Float64` world, and it has two terms because one is not enough. The
@@ -536,10 +609,11 @@ export class ClothColliderIndex {
536
609
  */
537
610
  #store_fat_aabb(node, r) {
538
611
  const table = this.table;
612
+ const reach = this.#reach[r / CCR_STRIDE];
539
613
 
540
614
  for (let axis = 0; axis < 3; axis++) {
541
- const lower = table[r + CCR_AABB_MIN_X + axis];
542
- const upper = table[r + CCR_AABB_MAX_X + axis];
615
+ const lower = table[r + CCR_AABB_MIN_X + axis] - reach;
616
+ const upper = table[r + CCR_AABB_MAX_X + axis] + reach;
543
617
 
544
618
  FAT_AABB[axis] = lower - BVH_LEAF_SLACK - Math.abs(lower) * FLOAT32_ROUNDING;
545
619
  FAT_AABB[axis + 3] = upper + BVH_LEAF_SLACK + Math.abs(upper) * FLOAT32_ROUNDING;
@@ -593,6 +667,10 @@ export class ClothColliderIndex {
593
667
  setSurface(handle, friction, inflation) {
594
668
  const r = handle * CCR_STRIDE;
595
669
 
670
+ if (this.table[r + CCR_FRICTION] !== friction || this.table[r + CCR_INFLATION] !== inflation) {
671
+ this.#revision[handle]++;
672
+ }
673
+
596
674
  this.table[r + CCR_FRICTION] = friction;
597
675
  this.table[r + CCR_INFLATION] = inflation;
598
676
 
@@ -602,6 +680,8 @@ export class ClothColliderIndex {
602
680
  if (inflation > this.max_inflation) {
603
681
  this.max_inflation = inflation;
604
682
  }
683
+
684
+ this.#refit(handle);
605
685
  }
606
686
 
607
687
  /**
@@ -613,6 +693,10 @@ export class ClothColliderIndex {
613
693
  setFilter(handle, layer, mask) {
614
694
  const m = handle * CCR_META_STRIDE;
615
695
 
696
+ if (this.meta[m + CCR_META_LAYER] !== (layer | 0) || this.meta[m + CCR_META_MASK] !== (mask | 0)) {
697
+ this.#revision[handle]++;
698
+ }
699
+
616
700
  this.meta[m + CCR_META_LAYER] = layer;
617
701
  this.meta[m + CCR_META_MASK] = mask;
618
702
  }
@@ -642,9 +726,12 @@ export class ClothColliderIndex {
642
726
  * @param {number} max_z
643
727
  * @param {number} layer the querier's layer
644
728
  * @param {number} mask the querier's mask
729
+ * @param {boolean} [contact] include each collider's own travel and contact
730
+ * inflation and conversion allowance; the caller supplies particle radius,
731
+ * travel and its local-coordinate conversion allowance in the query box
645
732
  * @returns {number} how many handles were written
646
733
  */
647
- query(out, min_x, min_y, min_z, max_x, max_y, max_z, layer, mask) {
734
+ query(out, min_x, min_y, min_z, max_x, max_y, max_z, layer, mask, contact = false) {
648
735
  QUERY_AABB[0] = min_x;
649
736
  QUERY_AABB[1] = min_y;
650
737
  QUERY_AABB[2] = min_z;
@@ -676,9 +763,9 @@ export class ClothColliderIndex {
676
763
  for (let i = 0; i < candidate_count; i++) {
677
764
  const handle = candidates[i];
678
765
 
679
- // The leaf carries slack and `Float32` rounding; the record is the
680
- // exact `Float64` answer, and it is what the narrowphase will read.
681
- if (this.#overlaps(handle, min_x, min_y, min_z, max_x, max_y, max_z, layer, mask)) {
766
+ // Raw queries use the exact record. Contact queries retain the
767
+ // conversion allowance needed by the solver's Float32 table.
768
+ if (this.#overlaps(handle, min_x, min_y, min_z, max_x, max_y, max_z, layer, mask, contact)) {
682
769
  answer.push(handle);
683
770
  }
684
771
  }
@@ -688,7 +775,7 @@ export class ClothColliderIndex {
688
775
  for (let i = 0; i < planes.length; i++) {
689
776
  const handle = planes[i];
690
777
 
691
- if (this.#overlaps(handle, min_x, min_y, min_z, max_x, max_y, max_z, layer, mask)) {
778
+ if (this.#overlaps(handle, min_x, min_y, min_z, max_x, max_y, max_z, layer, mask, contact)) {
692
779
  answer.push(handle);
693
780
  }
694
781
  }
@@ -718,10 +805,11 @@ export class ClothColliderIndex {
718
805
  * @param {number} max_z
719
806
  * @param {number} layer
720
807
  * @param {number} mask
808
+ * @param {boolean} contact
721
809
  * @returns {boolean}
722
810
  * @private
723
811
  */
724
- #overlaps(handle, min_x, min_y, min_z, max_x, max_y, max_z, layer, mask) {
812
+ #overlaps(handle, min_x, min_y, min_z, max_x, max_y, max_z, layer, mask, contact) {
725
813
  if (this.#live[handle] === 0) {
726
814
  return false;
727
815
  }
@@ -742,15 +830,22 @@ export class ClothColliderIndex {
742
830
 
743
831
  const r = handle * CCR_STRIDE;
744
832
 
745
- if (table[r + CCR_AABB_MIN_X] > max_x || table[r + CCR_AABB_MAX_X] < min_x) {
833
+ // A plane has no finite radius with which to bound a rounded normal's
834
+ // displacement. Keep every filtered half-space; the gather can reject
835
+ // it using the solver's actual local bounds before applying its cap.
836
+ if (contact && table[r + CCR_KIND] === ClothColliderKind.Plane) return true;
837
+
838
+ const reach = contact ? this.#reach[handle] : 0;
839
+
840
+ if (table[r + CCR_AABB_MIN_X] - reach > max_x || table[r + CCR_AABB_MAX_X] + reach < min_x) {
746
841
  return false;
747
842
  }
748
843
 
749
- if (table[r + CCR_AABB_MIN_Y] > max_y || table[r + CCR_AABB_MAX_Y] < min_y) {
844
+ if (table[r + CCR_AABB_MIN_Y] - reach > max_y || table[r + CCR_AABB_MAX_Y] + reach < min_y) {
750
845
  return false;
751
846
  }
752
847
 
753
- if (table[r + CCR_AABB_MIN_Z] > max_z || table[r + CCR_AABB_MAX_Z] < min_z) {
848
+ if (table[r + CCR_AABB_MIN_Z] - reach > max_z || table[r + CCR_AABB_MAX_Z] + reach < min_z) {
754
849
  return false;
755
850
  }
756
851
 
@@ -784,11 +879,15 @@ export class ClothColliderIndex {
784
879
  const meta = new Int32Array(capacity * CCR_META_STRIDE);
785
880
  const live = new Uint8Array(capacity);
786
881
  const node = new Uint32Array(capacity);
882
+ const reach = new Float64Array(capacity);
883
+ const revision = new Uint32Array(capacity);
787
884
 
788
885
  table.set(this.table);
789
886
  meta.set(this.meta);
790
887
  live.set(this.#live);
791
888
  node.set(this.#node);
889
+ reach.set(this.#reach);
890
+ revision.set(this.#revision);
792
891
 
793
892
  node.fill(NULL_NODE, this.#node.length);
794
893
 
@@ -796,6 +895,8 @@ export class ClothColliderIndex {
796
895
  this.meta = meta;
797
896
  this.#live = live;
798
897
  this.#node = node;
898
+ this.#reach = reach;
899
+ this.#revision = revision;
799
900
  }
800
901
 
801
902
  }
@@ -1,3 +1,33 @@
1
+ /**
2
+ * Outward error allowance when a world-space query precedes the solver's
3
+ * Float32 conversion. Shared by the cloth query and the collider leaf: their
4
+ * allowances add, just as their speculative contact reaches do.
5
+ *
6
+ * Let u = 2^-24, P bound the particle's local distance from the anchor, G the
7
+ * shape radius, and L the sum of the contact and motion reaches. Rounding a
8
+ * localized translation contributes at most u times its length; rounding both
9
+ * swept translations adds at most u(2|t| + travel) to the measured travel.
10
+ * The relative quaternion is rounded, normalized and rounded again. Its
11
+ * error is less than 4u, so its inverse rotation/scale displaces a point by
12
+ * less than 9u times its radius. Shape parameters and inflation round once.
13
+ * Together these are bounded by 4u|t| + 16u(G + L). For an accepted contact,
14
+ * |t| <= P + G + L + error; substituting bounds that error by 32u(P + G + L).
15
+ * We spend 64u, including outward arithmetic rounding. P and the particle
16
+ * reach are paid by the query, G and collider reach by the index. No world
17
+ * coordinate is multiplied by a Float32 epsilon.
18
+ *
19
+ * World transforms/bounds use doubles: the separate world term covers their
20
+ * subtraction and rotation arithmetic, including cancellation far from the
21
+ * origin. The absolute term covers Float32 subnormals. Assumes finite solver
22
+ * values and rigid unit-quaternion input poses, as the collider API does.
23
+ * Unbounded planes cannot use a shape-radius bound and are always candidates
24
+ * for a contact query (their layer/mask still applies).
25
+ *
26
+ * @param {number} local_scale nonnegative local length bound
27
+ * @param {number} world_scale nonnegative world arithmetic magnitude bound
28
+ * @returns {number} metres of outward padding
29
+ */
30
+ export function cloth_contact_rounding(local_scale: number, world_scale: number): number;
1
31
  /**
2
32
  * # The swept bounds of a packed collider
3
33
  *
@@ -1 +1 @@
1
- {"version":3,"file":"cloth_collider_bounds.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/collider/cloth_collider_bounds.js"],"names":[],"mappings":"AAqBA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;GAOG;AACH,mDALW,YAAY,GAAC,YAAY,UACzB,MAAM,UACN,MAAM,GACJ,IAAI,CAoBhB"}
1
+ {"version":3,"file":"cloth_collider_bounds.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/collider/cloth_collider_bounds.js"],"names":[],"mappings":"AAqBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,oDAJW,MAAM,eACN,MAAM,GACJ,MAAM,CAIlB;AAED;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;GAOG;AACH,mDALW,YAAY,GAAC,YAAY,UACzB,MAAM,UACN,MAAM,GACJ,IAAI,CAoBhB"}
@@ -19,6 +19,39 @@ import {
19
19
  } from "./ClothColliderRecord.js";
20
20
  import { cloth_collider_local_bounds } from "./cloth_collider_pack.js";
21
21
 
22
+ /**
23
+ * Outward error allowance when a world-space query precedes the solver's
24
+ * Float32 conversion. Shared by the cloth query and the collider leaf: their
25
+ * allowances add, just as their speculative contact reaches do.
26
+ *
27
+ * Let u = 2^-24, P bound the particle's local distance from the anchor, G the
28
+ * shape radius, and L the sum of the contact and motion reaches. Rounding a
29
+ * localized translation contributes at most u times its length; rounding both
30
+ * swept translations adds at most u(2|t| + travel) to the measured travel.
31
+ * The relative quaternion is rounded, normalized and rounded again. Its
32
+ * error is less than 4u, so its inverse rotation/scale displaces a point by
33
+ * less than 9u times its radius. Shape parameters and inflation round once.
34
+ * Together these are bounded by 4u|t| + 16u(G + L). For an accepted contact,
35
+ * |t| <= P + G + L + error; substituting bounds that error by 32u(P + G + L).
36
+ * We spend 64u, including outward arithmetic rounding. P and the particle
37
+ * reach are paid by the query, G and collider reach by the index. No world
38
+ * coordinate is multiplied by a Float32 epsilon.
39
+ *
40
+ * World transforms/bounds use doubles: the separate world term covers their
41
+ * subtraction and rotation arithmetic, including cancellation far from the
42
+ * origin. The absolute term covers Float32 subnormals. Assumes finite solver
43
+ * values and rigid unit-quaternion input poses, as the collider API does.
44
+ * Unbounded planes cannot use a shape-radius bound and are always candidates
45
+ * for a contact query (their layer/mask still applies).
46
+ *
47
+ * @param {number} local_scale nonnegative local length bound
48
+ * @param {number} world_scale nonnegative world arithmetic magnitude bound
49
+ * @returns {number} metres of outward padding
50
+ */
51
+ export function cloth_contact_rounding(local_scale, world_scale) {
52
+ return 64 * (2 ** -24 * local_scale + Number.EPSILON * world_scale + 2 ** -149);
53
+ }
54
+
22
55
  /**
23
56
  * # The swept bounds of a packed collider
24
57
  *
@@ -24,7 +24,8 @@ export const ClothColliderFlags = {
24
24
  * the sweep degenerates to the pose it already had. Move the entity anyway
25
25
  * and the cloth will not notice: that is the trade, and it is why this is
26
26
  * an author's assertion rather than something derived from a pose that
27
- * happens not to have changed yet.
27
+ * happens not to have changed yet. An explicit TRANSFORM64_EVENT_PLACED
28
+ * (including PhysicsSystem.setPose) does re-place a static proxy.
28
29
  */
29
30
  Static: 1
30
31
  };
@@ -63,7 +63,8 @@ export class ClothColliderSystem extends System<any> {
63
63
  * they were linked. Held rather than looked up per step for the same reason
64
64
  * `ClothInstance` holds its transforms: a refresh is a loop over live
65
65
  * colliders, not a query.
66
- * @type {{collider: ClothCollider, transform: Transform64, entity: number, handle: number}[]}
66
+ * @type {{collider: ClothCollider, transform: Transform64, entity: number, handle: number,
67
+ * linked: boolean, onPlaced: function(): void}[]}
67
68
  * @private
68
69
  */
69
70
  private __linked;
@@ -99,6 +100,14 @@ export class ClothColliderSystem extends System<any> {
99
100
  * @returns {void}
100
101
  */
101
102
  refreshSurface(entity: number): void;
103
+ /**
104
+ * Reset motion history at an explicitly published placement, including
105
+ * static proxies. The next refresh can still sweep any subsequent motion.
106
+ * @param {number} handle
107
+ * @param {Transform64} transform
108
+ * @private
109
+ */
110
+ private __place;
102
111
  /**
103
112
  * @param {number} entity
104
113
  * @returns {number} the index handle, or `undefined` if the entity is not indexed
@@ -1 +1 @@
1
- {"version":3,"file":"ClothColliderSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/ecs/ClothColliderSystem.js"],"names":[],"mappings":"AAaA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH;;IAEI,8EAAsD;IAEtD,wKAIE;IAEF;;OAEG;IACH,OAFU,kBAAkB,CAEK;IAEjC;;;;;;OAMG;IACH,kBAAsB;IAEtB;;;;;;;OAOG;IACH,iBAAc;IAEd;;;;;OAKG;IACH,qBALW,aAAa,YACb,QAAQ,aACR,WAAW,UACX,MAAM,QAsChB;IAED;;;;;OAKG;IACH,uBALW,aAAa,YACb,QAAQ,aACR,WAAW,UACX,MAAM,QAmBhB;IAED;;;;;;OAMG;IACH,WAFa,IAAI,CAkChB;IAED;;;;;;;;OAQG;IACH,uBAHW,MAAM,GACJ,IAAI,CAyBhB;IAED;;;OAGG;IACH,iBAHW,MAAM,GACJ,MAAM,CAIlB;CACJ;uBAhPsB,wBAAwB;4BAKxC,uCAAuC;yBACrB,uBAAuB;8BAElB,oBAAoB;4CATN,uDAAuD;mCAQhE,mCAAmC"}
1
+ {"version":3,"file":"ClothColliderSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/ecs/ClothColliderSystem.js"],"names":[],"mappings":"AAcA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH;;IAEI,8EAAsD;IAEtD,wKAIE;IAEF;;OAEG;IACH,OAFU,kBAAkB,CAEK;IAEjC;;;;;;OAMG;IACH,kBAAsB;IAEtB;;;;;;;;OAQG;IACH,iBAAc;IAEd;;;;;OAKG;IACH,qBALW,aAAa,YACb,QAAQ,aACR,WAAW,UACX,MAAM,QAoChB;IAED;;;;;OAKG;IACH,uBALW,aAAa,YACb,QAAQ,aACR,WAAW,UACX,MAAM,QAqBhB;IAED;;;;;;OAMG;IACH,WAFa,IAAI,CAkChB;IAED;;;;;;;;OAQG;IACH,uBAHW,MAAM,GACJ,IAAI,CAyBhB;IAED;;;;;;OAMG;IACH,gBAWC;IAED;;;OAGG;IACH,iBAHW,MAAM,GACJ,MAAM,CAIlB;CACJ;uBAtQsB,wBAAwB;4BAMxC,uCAAuC;yBACrB,uBAAuB;8BAElB,oBAAoB;4CAVN,uDAAuD;mCAShE,mCAAmC"}
@@ -1,6 +1,7 @@
1
1
  import { ResourceAccessKind } from "../../../../core/model/ResourceAccessKind.js";
2
2
  import { ResourceAccessSpecification } from "../../../../core/model/ResourceAccessSpecification.js";
3
3
  import { System } from "../../../ecs/System.js";
4
+ import { TRANSFORM64_EVENT_PLACED } from "../../../ecs/transform/TRANSFORM64_EVENT_PLACED.js";
4
5
  import {
5
6
  TRANSFORM64_ROTATION_OFFSET,
6
7
  TRANSFORM64_TRANSLATION_OFFSET,
@@ -84,7 +85,8 @@ export class ClothColliderSystem extends System {
84
85
  * they were linked. Held rather than looked up per step for the same reason
85
86
  * `ClothInstance` holds its transforms: a refresh is a loop over live
86
87
  * colliders, not a query.
87
- * @type {{collider: ClothCollider, transform: Transform64, entity: number, handle: number}[]}
88
+ * @type {{collider: ClothCollider, transform: Transform64, entity: number, handle: number,
89
+ * linked: boolean, onPlaced: function(): void}[]}
88
90
  * @private
89
91
  */
90
92
  __linked = [];
@@ -117,20 +119,18 @@ export class ClothColliderSystem extends System {
117
119
 
118
120
  this.__handles.set(entity, handle);
119
121
 
120
- this.__linked.push({ collider: cloth_collider, transform, entity, handle });
122
+ const entry = { collider: cloth_collider, transform, entity, handle, linked: true, onPlaced: () => {
123
+ // Dispatch snapshots can retain this listener after unlink, even
124
+ // after both its entity id and index slot have been reused.
125
+ if (entry.linked) this.__place(handle, transform);
126
+ } };
127
+ this.__linked.push(entry);
128
+
129
+ this.entityManager.dataset.addEntityEventListener(entity, TRANSFORM64_EVENT_PLACED, entry.onPlaced);
121
130
 
122
131
  // Placed rather than posed, so its first step sweeps nothing. A
123
132
  // collider that has just appeared did not move.
124
- this.index.placePose(
125
- handle,
126
- transform[TRANSFORM64_TRANSLATION_OFFSET],
127
- transform[TRANSFORM64_TRANSLATION_OFFSET + 1],
128
- transform[TRANSFORM64_TRANSLATION_OFFSET + 2],
129
- transform[TRANSFORM64_ROTATION_OFFSET],
130
- transform[TRANSFORM64_ROTATION_OFFSET + 1],
131
- transform[TRANSFORM64_ROTATION_OFFSET + 2],
132
- transform[TRANSFORM64_ROTATION_OFFSET + 3]
133
- );
133
+ this.__place(handle, transform);
134
134
  }
135
135
 
136
136
  /**
@@ -148,9 +148,11 @@ export class ClothColliderSystem extends System {
148
148
 
149
149
  this.__handles.delete(entity);
150
150
  this.index.remove(handle);
151
-
152
151
  for (let i = 0; i < this.__linked.length; i++) {
153
- if (this.__linked[i].entity === entity) {
152
+ const entry = this.__linked[i];
153
+ if (entry.entity === entity) {
154
+ entry.linked = false;
155
+ this.entityManager.dataset.removeEntityEventListener(entity, TRANSFORM64_EVENT_PLACED, entry.onPlaced);
154
156
  this.__linked.splice(i, 1);
155
157
 
156
158
  break;
@@ -233,6 +235,26 @@ export class ClothColliderSystem extends System {
233
235
  this.index.setFilter(handle, cloth_collider.layer, cloth_collider.mask);
234
236
  }
235
237
 
238
+ /**
239
+ * Reset motion history at an explicitly published placement, including
240
+ * static proxies. The next refresh can still sweep any subsequent motion.
241
+ * @param {number} handle
242
+ * @param {Transform64} transform
243
+ * @private
244
+ */
245
+ __place(handle, transform) {
246
+ this.index.placePose(
247
+ handle,
248
+ transform[TRANSFORM64_TRANSLATION_OFFSET],
249
+ transform[TRANSFORM64_TRANSLATION_OFFSET + 1],
250
+ transform[TRANSFORM64_TRANSLATION_OFFSET + 2],
251
+ transform[TRANSFORM64_ROTATION_OFFSET],
252
+ transform[TRANSFORM64_ROTATION_OFFSET + 1],
253
+ transform[TRANSFORM64_ROTATION_OFFSET + 2],
254
+ transform[TRANSFORM64_ROTATION_OFFSET + 3]
255
+ );
256
+ }
257
+
236
258
  /**
237
259
  * @param {number} entity
238
260
  * @returns {number} the index handle, or `undefined` if the entity is not indexed