@woosh/meep-engine 3.38.0 → 3.40.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 (1183) hide show
  1. package/build/bundle-worker-image-decoder.js +1 -1
  2. package/build/bundle-worker-terrain.js +1 -1
  3. package/package.json +1 -1
  4. package/src/core/binary/BinaryBuffer.d.ts +11 -0
  5. package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
  6. package/src/core/binary/BinaryBuffer.js +30 -0
  7. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_aabb.d.ts.map +1 -1
  8. package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_aabb.js +28 -7
  9. package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts +15 -0
  10. package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts.map +1 -1
  11. package/src/core/geom/3d/aabb/aabb3_transform_oriented.js +41 -0
  12. package/src/core/geom/3d/aabb/aabb3_transform_oriented_inverse.d.ts +12 -0
  13. package/src/core/geom/3d/aabb/aabb3_transform_oriented_inverse.d.ts.map +1 -1
  14. package/src/core/geom/3d/aabb/aabb3_transform_oriented_inverse.js +27 -0
  15. package/src/core/geom/3d/gjk/gjk.js +3 -3
  16. package/src/core/geom/3d/gjk/gjk_distance.d.ts +94 -0
  17. package/src/core/geom/3d/gjk/gjk_distance.d.ts.map +1 -0
  18. package/src/core/geom/3d/gjk/gjk_distance.js +782 -0
  19. package/src/core/geom/3d/gjk/gjk_epa_penetration.d.ts +5 -4
  20. package/src/core/geom/3d/gjk/gjk_epa_penetration.d.ts.map +1 -1
  21. package/src/core/geom/3d/gjk/gjk_epa_penetration.js +14 -9
  22. package/src/core/geom/3d/gjk/minkowski_support.d.ts +11 -7
  23. package/src/core/geom/3d/gjk/minkowski_support.d.ts.map +1 -1
  24. package/src/core/geom/3d/gjk/minkowski_support.js +33 -9
  25. package/src/core/geom/3d/gjk/mpr.d.ts.map +1 -1
  26. package/src/core/geom/3d/gjk/mpr.js +26 -5
  27. package/src/core/geom/3d/line/line3_compute_segment_closest_point_to_aabb3_t.d.ts +15 -0
  28. package/src/core/geom/3d/line/line3_compute_segment_closest_point_to_aabb3_t.d.ts.map +1 -1
  29. package/src/core/geom/3d/line/line3_compute_segment_closest_point_to_aabb3_t.js +106 -33
  30. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.d.ts +2 -1
  31. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.d.ts.map +1 -1
  32. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.js +4 -8
  33. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_array.d.ts +32 -0
  34. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_array.d.ts.map +1 -0
  35. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_array.js +85 -0
  36. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.d.ts +3 -3
  37. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.d.ts.map +1 -1
  38. package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.js +24 -34
  39. package/src/core/geom/3d/quaternion/quat3_integrate.d.ts +17 -0
  40. package/src/core/geom/3d/quaternion/quat3_integrate.d.ts.map +1 -1
  41. package/src/core/geom/3d/quaternion/quat3_integrate.js +45 -4
  42. package/src/core/geom/3d/quaternion/quat3_multiply.d.ts +16 -0
  43. package/src/core/geom/3d/quaternion/quat3_multiply.d.ts.map +1 -1
  44. package/src/core/geom/3d/quaternion/quat3_multiply.js +25 -0
  45. package/src/core/geom/3d/quaternion/quat3_nlerp.d.ts +18 -0
  46. package/src/core/geom/3d/quaternion/quat3_nlerp.d.ts.map +1 -1
  47. package/src/core/geom/3d/quaternion/quat3_nlerp.js +45 -0
  48. package/src/core/geom/3d/quaternion/quat3_shortest_arc.d.ts +17 -0
  49. package/src/core/geom/3d/quaternion/quat3_shortest_arc.d.ts.map +1 -1
  50. package/src/core/geom/3d/quaternion/quat3_shortest_arc.js +155 -94
  51. package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts +12 -0
  52. package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts.map +1 -1
  53. package/src/core/geom/3d/quaternion/quat3_to_matrix3.js +31 -0
  54. package/src/core/geom/3d/shape/AbstractShape3D.d.ts +20 -6
  55. package/src/core/geom/3d/shape/AbstractShape3D.d.ts.map +1 -1
  56. package/src/core/geom/3d/shape/AbstractShape3D.js +20 -6
  57. package/src/core/geom/3d/shape/BoxShape3D.d.ts +1 -1
  58. package/src/core/geom/3d/shape/BoxShape3D.d.ts.map +1 -1
  59. package/src/core/geom/3d/shape/BoxShape3D.js +5 -1
  60. package/src/core/geom/3d/shape/CapsuleShape3D.d.ts +1 -1
  61. package/src/core/geom/3d/shape/CapsuleShape3D.d.ts.map +1 -1
  62. package/src/core/geom/3d/shape/CapsuleShape3D.js +5 -1
  63. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts +25 -11
  64. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
  65. package/src/core/geom/3d/shape/ConvexHullShape3D.js +392 -22
  66. package/src/core/geom/3d/shape/CylinderShape3D.d.ts +8 -7
  67. package/src/core/geom/3d/shape/CylinderShape3D.d.ts.map +1 -1
  68. package/src/core/geom/3d/shape/CylinderShape3D.js +12 -7
  69. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +51 -8
  70. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -1
  71. package/src/core/geom/3d/shape/HeightMapShape3D.js +260 -14
  72. package/src/core/geom/3d/shape/MeshShape3D.d.ts +61 -7
  73. package/src/core/geom/3d/shape/MeshShape3D.d.ts.map +1 -1
  74. package/src/core/geom/3d/shape/MeshShape3D.js +111 -20
  75. package/src/core/geom/3d/shape/PointShape3D.d.ts +1 -1
  76. package/src/core/geom/3d/shape/PointShape3D.d.ts.map +1 -1
  77. package/src/core/geom/3d/shape/PointShape3D.js +1 -1
  78. package/src/core/geom/3d/shape/PosedShape3D.d.ts +9 -0
  79. package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
  80. package/src/core/geom/3d/shape/PosedShape3D.js +36 -6
  81. package/src/core/geom/3d/shape/SphereShape3D.d.ts +1 -1
  82. package/src/core/geom/3d/shape/SphereShape3D.d.ts.map +1 -1
  83. package/src/core/geom/3d/shape/SphereShape3D.js +5 -1
  84. package/src/core/geom/3d/shape/TransformedShape3D.d.ts +12 -10
  85. package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
  86. package/src/core/geom/3d/shape/TransformedShape3D.js +53 -19
  87. package/src/core/geom/3d/shape/Triangle3D.d.ts +1 -1
  88. package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -1
  89. package/src/core/geom/3d/shape/Triangle3D.js +5 -1
  90. package/src/core/geom/3d/shape/UnionShape3D.d.ts +1 -1
  91. package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
  92. package/src/core/geom/3d/shape/UnionShape3D.js +19 -2
  93. package/src/core/geom/3d/shape/sdf/sdf3_box.d.ts +14 -0
  94. package/src/core/geom/3d/shape/sdf/sdf3_box.d.ts.map +1 -1
  95. package/src/core/geom/3d/shape/sdf/sdf3_box.js +67 -5
  96. package/src/core/geom/3d/shape/sdf/sdf3_capsule.d.ts +14 -0
  97. package/src/core/geom/3d/shape/sdf/sdf3_capsule.d.ts.map +1 -1
  98. package/src/core/geom/3d/shape/sdf/sdf3_capsule.js +61 -3
  99. package/src/core/geom/3d/shape/sdf/sdf3_cylinder.d.ts +14 -0
  100. package/src/core/geom/3d/shape/sdf/sdf3_cylinder.d.ts.map +1 -1
  101. package/src/core/geom/3d/shape/sdf/sdf3_cylinder.js +63 -4
  102. package/src/core/geom/3d/shape/sdf/sdf3_plane.d.ts +15 -0
  103. package/src/core/geom/3d/shape/sdf/sdf3_plane.d.ts.map +1 -1
  104. package/src/core/geom/3d/shape/sdf/sdf3_plane.js +64 -2
  105. package/src/core/geom/3d/shape/sdf/sdf3_sphere.d.ts +21 -0
  106. package/src/core/geom/3d/shape/sdf/sdf3_sphere.d.ts.map +1 -1
  107. package/src/core/geom/3d/shape/sdf/sdf3_sphere.js +66 -3
  108. package/src/core/geom/3d/shape/shape3_mesh_from_geometry.d.ts +2 -0
  109. package/src/core/geom/3d/shape/shape3_mesh_from_geometry.d.ts.map +1 -1
  110. package/src/core/geom/3d/shape/shape3_mesh_from_geometry.js +7 -0
  111. package/src/core/geom/3d/shape/util/mesh_compute_triangle_edge_flags.d.ts +41 -0
  112. package/src/core/geom/3d/shape/util/mesh_compute_triangle_edge_flags.d.ts.map +1 -0
  113. package/src/core/geom/3d/shape/util/mesh_compute_triangle_edge_flags.js +538 -0
  114. package/src/core/geom/3d/sphere/harmonics/sh_basis_at.d.ts +31 -0
  115. package/src/core/geom/3d/sphere/harmonics/sh_basis_at.d.ts.map +1 -0
  116. package/src/core/geom/3d/sphere/harmonics/sh_basis_at.js +85 -0
  117. package/src/core/geom/3d/sphere/harmonics/sh_vmf_window.d.ts +18 -0
  118. package/src/core/geom/3d/sphere/harmonics/sh_vmf_window.d.ts.map +1 -0
  119. package/src/core/geom/3d/sphere/harmonics/sh_vmf_window.js +19 -0
  120. package/src/core/geom/3d/triangle/TriangleEdgeFlags.d.ts +49 -0
  121. package/src/core/geom/3d/triangle/TriangleEdgeFlags.d.ts.map +1 -0
  122. package/src/core/geom/3d/triangle/TriangleEdgeFlags.js +55 -0
  123. package/src/core/geom/3d/triangle/tri3_classify_shared_edge.d.ts +42 -0
  124. package/src/core/geom/3d/triangle/tri3_classify_shared_edge.d.ts.map +1 -0
  125. package/src/core/geom/3d/triangle/tri3_classify_shared_edge.js +85 -0
  126. package/src/core/geom/3d/triangle/tri3_closest_point.d.ts +13 -0
  127. package/src/core/geom/3d/triangle/tri3_closest_point.d.ts.map +1 -1
  128. package/src/core/geom/3d/triangle/tri3_closest_point.js +36 -1
  129. package/src/core/geom/3d/triangle/tri3_closest_point_barycentric.d.ts +14 -0
  130. package/src/core/geom/3d/triangle/tri3_closest_point_barycentric.d.ts.map +1 -1
  131. package/src/core/geom/3d/triangle/tri3_closest_point_barycentric.js +40 -0
  132. package/src/core/geom/vec3/v3_quaternion_apply.d.ts +19 -0
  133. package/src/core/geom/vec3/v3_quaternion_apply.d.ts.map +1 -1
  134. package/src/core/geom/vec3/v3_quaternion_apply.js +32 -0
  135. package/src/core/geom/vec3/v3_quaternion_apply_inverse.d.ts +19 -0
  136. package/src/core/geom/vec3/v3_quaternion_apply_inverse.d.ts.map +1 -1
  137. package/src/core/geom/vec3/v3_quaternion_apply_inverse.js +32 -0
  138. package/src/engine/EngineHarness.d.ts.map +1 -1
  139. package/src/engine/EngineHarness.js +17 -4
  140. package/src/engine/asset/loaders/image/avif/threaded_image_encoder.js +1 -1
  141. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +135 -10
  142. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
  143. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +338 -105
  144. package/src/engine/control/first-person/abilities/Ability.d.ts +14 -5
  145. package/src/engine/control/first-person/abilities/Ability.d.ts.map +1 -1
  146. package/src/engine/control/first-person/abilities/Ability.js +14 -5
  147. package/src/engine/control/first-person/abilities/LedgeGrab.d.ts.map +1 -1
  148. package/src/engine/control/first-person/abilities/LedgeGrab.js +16 -12
  149. package/src/engine/control/first-person/abilities/Mantle.d.ts.map +1 -1
  150. package/src/engine/control/first-person/abilities/Mantle.js +5 -1
  151. package/src/engine/control/first-person/abilities/Slide.js +1 -1
  152. package/src/engine/control/first-person/collision/KinematicMover.d.ts +92 -14
  153. package/src/engine/control/first-person/collision/KinematicMover.d.ts.map +1 -1
  154. package/src/engine/control/first-person/collision/KinematicMover.js +218 -37
  155. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts +5 -0
  156. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts.map +1 -1
  157. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js +17 -10
  158. package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
  159. package/src/engine/ecs/EntityComponentDataset.js +27 -13
  160. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  161. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +8 -0
  162. package/src/engine/ecs/transform/Transform64.d.ts +4 -2
  163. package/src/engine/ecs/transform/Transform64.d.ts.map +1 -1
  164. package/src/engine/ecs/transform/Transform64.js +10 -16
  165. package/src/engine/graphics/ecs/water/Water.d.ts +31 -72
  166. package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
  167. package/src/engine/graphics/ecs/water/Water.js +59 -84
  168. package/src/engine/graphics/ecs/water/WaterSerializationAdapter.d.ts.map +1 -1
  169. package/src/engine/graphics/ecs/water/WaterSerializationAdapter.js +11 -49
  170. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_1_2.d.ts +1 -1
  171. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_1_2.d.ts.map +1 -1
  172. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_1_2.js +27 -18
  173. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_2_3.d.ts +12 -0
  174. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_2_3.d.ts.map +1 -0
  175. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_2_3.js +45 -0
  176. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_3_4.d.ts +6 -0
  177. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_3_4.d.ts.map +1 -0
  178. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_3_4.js +10 -0
  179. package/src/engine/graphics/ecs/water/WaterWaves.d.ts +33 -0
  180. package/src/engine/graphics/ecs/water/WaterWaves.d.ts.map +1 -0
  181. package/src/engine/graphics/ecs/water/WaterWaves.js +46 -0
  182. package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.d.ts +17 -0
  183. package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.d.ts.map +1 -0
  184. package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.js +26 -0
  185. package/src/engine/graphics/texture/3d/scs3d_sample_linear.d.ts +22 -0
  186. package/src/engine/graphics/texture/3d/scs3d_sample_linear.d.ts.map +1 -1
  187. package/src/engine/graphics/texture/3d/scs3d_sample_linear.js +43 -1
  188. package/src/engine/graphics/texture/sampler/Sampler2D.d.ts +29 -3
  189. package/src/engine/graphics/texture/sampler/Sampler2D.d.ts.map +1 -1
  190. package/src/engine/graphics/texture/sampler/Sampler2D.js +102 -14
  191. package/src/engine/graphics/texture/sampler/sampler2d_to_avif.d.ts.map +1 -1
  192. package/src/engine/graphics/texture/sampler/sampler2d_to_avif.js +8 -5
  193. package/src/engine/graphics3/WaterSystem.d.ts +31 -48
  194. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  195. package/src/engine/graphics3/WaterSystem.js +177 -261
  196. package/src/engine/interpolation/BinaryInterpolationAdapter.d.ts +10 -4
  197. package/src/engine/interpolation/BinaryInterpolationAdapter.d.ts.map +1 -1
  198. package/src/engine/interpolation/BinaryInterpolationAdapter.js +11 -5
  199. package/src/engine/interpolation/InterpolationLog.d.ts +11 -5
  200. package/src/engine/interpolation/InterpolationLog.d.ts.map +1 -1
  201. package/src/engine/interpolation/InterpolationLog.js +12 -6
  202. package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
  203. package/src/engine/interpolation/InterpolationSystem.js +39 -8
  204. package/src/engine/interpolation/PoseInterpolationAdapter.d.ts +1 -1
  205. package/src/engine/interpolation/PoseInterpolationAdapter.d.ts.map +1 -1
  206. package/src/engine/interpolation/PoseInterpolationAdapter.js +51 -22
  207. package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -1
  208. package/src/engine/interpolation/TransformPoseSerializationAdapter.js +18 -23
  209. package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts +1 -1
  210. package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts.map +1 -1
  211. package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +3 -1
  212. package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts +1 -1
  213. package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts.map +1 -1
  214. package/src/engine/network/adapters/TransformInterpolationAdapter.js +3 -1
  215. package/src/engine/network/adapters/Vector3InterpolationAdapter.d.ts.map +1 -1
  216. package/src/engine/network/adapters/Vector3InterpolationAdapter.js +5 -2
  217. package/src/engine/physics/body/SolverBodyState.d.ts.map +1 -1
  218. package/src/engine/physics/body/SolverBodyState.js +4 -5
  219. package/src/engine/physics/broadphase/compute_fat_world_aabb.d.ts +12 -0
  220. package/src/engine/physics/broadphase/compute_fat_world_aabb.d.ts.map +1 -1
  221. package/src/engine/physics/broadphase/compute_fat_world_aabb.js +28 -3
  222. package/src/engine/physics/ccd/linear_sweep.d.ts +42 -12
  223. package/src/engine/physics/ccd/linear_sweep.d.ts.map +1 -1
  224. package/src/engine/physics/ccd/linear_sweep.js +1131 -97
  225. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts +13 -0
  226. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts.map +1 -1
  227. package/src/engine/physics/cloth/collider/ClothColliderIndex.js +90 -22
  228. package/src/engine/physics/cloth/collider/cloth_collider_bounds.js +84 -24
  229. package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts +46 -3
  230. package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts.map +1 -1
  231. package/src/engine/physics/cloth/collider/cloth_collider_sdf.js +116 -68
  232. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
  233. package/src/engine/physics/cloth/ecs/ClothWorld.js +59 -47
  234. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -1
  235. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +62 -55
  236. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.d.ts.map +1 -1
  237. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.js +46 -39
  238. package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts.map +1 -1
  239. package/src/engine/physics/cloth/ecs/cloth_write_back.js +41 -36
  240. package/src/engine/physics/cloth/playground/garment_build.js +6 -6
  241. package/src/engine/physics/cloth/playground/garment_main.js +18 -14
  242. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts.map +1 -1
  243. package/src/engine/physics/cloth/solver/cloth_contact_find.js +8 -5
  244. package/src/engine/physics/cloth/solver/cloth_solve_vertex.d.ts.map +1 -1
  245. package/src/engine/physics/cloth/solver/cloth_solve_vertex.js +8 -5
  246. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts.map +1 -1
  247. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.js +15 -15
  248. package/src/engine/physics/cloth/solver/find_non_finite_cloth_state.d.ts +5 -0
  249. package/src/engine/physics/cloth/solver/find_non_finite_cloth_state.d.ts.map +1 -1
  250. package/src/engine/physics/cloth/solver/find_non_finite_cloth_state.js +32 -9
  251. package/src/engine/physics/constraint/solve_constraints.d.ts +50 -8
  252. package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
  253. package/src/engine/physics/constraint/solve_constraints.js +703 -429
  254. package/src/engine/physics/contact/ManifoldStore.d.ts +12 -0
  255. package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
  256. package/src/engine/physics/contact/ManifoldStore.js +32 -0
  257. package/src/engine/physics/ecs/BodyKind.d.ts +16 -8
  258. package/src/engine/physics/ecs/BodyKind.d.ts.map +1 -1
  259. package/src/engine/physics/ecs/BodyKind.js +16 -8
  260. package/src/engine/physics/ecs/Collider.d.ts +7 -2
  261. package/src/engine/physics/ecs/Collider.d.ts.map +1 -1
  262. package/src/engine/physics/ecs/Collider.js +7 -2
  263. package/src/engine/physics/ecs/PhysicsEvents.d.ts +13 -0
  264. package/src/engine/physics/ecs/PhysicsEvents.d.ts.map +1 -1
  265. package/src/engine/physics/ecs/PhysicsEvents.js +13 -0
  266. package/src/engine/physics/ecs/PhysicsSystem.d.ts +320 -36
  267. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  268. package/src/engine/physics/ecs/PhysicsSystem.js +779 -81
  269. package/src/engine/physics/fluid/FluidField.d.ts.map +1 -1
  270. package/src/engine/physics/fluid/FluidField.js +5 -1
  271. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +11 -9
  272. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts.map +1 -1
  273. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +7 -5
  274. package/src/engine/physics/fluid/ecs/fluid_reanchor_field.d.ts.map +1 -1
  275. package/src/engine/physics/fluid/ecs/fluid_reanchor_field.js +9 -7
  276. package/src/engine/physics/fluid/effector/ImpulseFluidEffector.d.ts.map +1 -1
  277. package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +3 -4
  278. package/src/engine/physics/fluid/effector/WakeFluidEffector.d.ts.map +1 -1
  279. package/src/engine/physics/fluid/effector/WakeFluidEffector.js +9 -7
  280. package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.d.ts.map +1 -1
  281. package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.js +65 -43
  282. package/src/engine/physics/inertia/MASS_PROPERTIES_SIZE.d.ts +23 -0
  283. package/src/engine/physics/inertia/MASS_PROPERTIES_SIZE.d.ts.map +1 -0
  284. package/src/engine/physics/inertia/MASS_PROPERTIES_SIZE.js +22 -0
  285. package/src/engine/physics/inertia/convex_hull_mass_properties.d.ts +29 -0
  286. package/src/engine/physics/inertia/convex_hull_mass_properties.d.ts.map +1 -0
  287. package/src/engine/physics/inertia/convex_hull_mass_properties.js +117 -0
  288. package/src/engine/physics/inertia/mass_properties_accumulate.d.ts +24 -0
  289. package/src/engine/physics/inertia/mass_properties_accumulate.d.ts.map +1 -0
  290. package/src/engine/physics/inertia/mass_properties_accumulate.js +93 -0
  291. package/src/engine/physics/inertia/primitive_mass_properties.d.ts +46 -0
  292. package/src/engine/physics/inertia/primitive_mass_properties.d.ts.map +1 -0
  293. package/src/engine/physics/inertia/primitive_mass_properties.js +118 -0
  294. package/src/engine/physics/inertia/rigid_body_set_mass_properties.d.ts +27 -0
  295. package/src/engine/physics/inertia/rigid_body_set_mass_properties.d.ts.map +1 -0
  296. package/src/engine/physics/inertia/rigid_body_set_mass_properties.js +62 -0
  297. package/src/engine/physics/inertia/world_inverse_inertia.d.ts +67 -25
  298. package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
  299. package/src/engine/physics/inertia/world_inverse_inertia.js +149 -26
  300. package/src/engine/physics/integration/integrate_position.d.ts +18 -19
  301. package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
  302. package/src/engine/physics/integration/integrate_position.js +40 -37
  303. package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
  304. package/src/engine/physics/integration/integrate_velocity.js +13 -4
  305. package/src/engine/physics/island/IslandBuilder.d.ts +124 -6
  306. package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -1
  307. package/src/engine/physics/island/IslandBuilder.js +322 -19
  308. package/src/engine/physics/narrowphase/box_box_manifold.d.ts +13 -0
  309. package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
  310. package/src/engine/physics/narrowphase/box_box_manifold.js +95 -39
  311. package/src/engine/physics/narrowphase/box_triangle_contact.d.ts +40 -0
  312. package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -1
  313. package/src/engine/physics/narrowphase/box_triangle_contact.js +415 -120
  314. package/src/engine/physics/narrowphase/capsule_contacts.d.ts +63 -0
  315. package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
  316. package/src/engine/physics/narrowphase/capsule_contacts.js +234 -46
  317. package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts +13 -0
  318. package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts.map +1 -1
  319. package/src/engine/physics/narrowphase/capsule_triangle_contact.js +141 -93
  320. package/src/engine/physics/narrowphase/compute_penetration.d.ts +29 -11
  321. package/src/engine/physics/narrowphase/compute_penetration.d.ts.map +1 -1
  322. package/src/engine/physics/narrowphase/compute_penetration.js +157 -34
  323. package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts +76 -0
  324. package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts.map +1 -1
  325. package/src/engine/physics/narrowphase/convex_convex_manifold.js +243 -48
  326. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts +13 -0
  327. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts.map +1 -1
  328. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +28 -12
  329. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts +13 -0
  330. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts.map +1 -1
  331. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.js +27 -0
  332. package/src/engine/physics/narrowphase/decomposition/heightmap_triangle_edge_flags.d.ts +42 -0
  333. package/src/engine/physics/narrowphase/decomposition/heightmap_triangle_edge_flags.d.ts.map +1 -0
  334. package/src/engine/physics/narrowphase/decomposition/heightmap_triangle_edge_flags.js +250 -0
  335. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts +27 -11
  336. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts.map +1 -1
  337. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.js +291 -12
  338. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts +7 -1
  339. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts.map +1 -1
  340. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +7 -1
  341. package/src/engine/physics/narrowphase/decomposition/triangle_edge_flags.d.ts +35 -0
  342. package/src/engine/physics/narrowphase/decomposition/triangle_edge_flags.d.ts.map +1 -0
  343. package/src/engine/physics/narrowphase/decomposition/triangle_edge_flags.js +61 -0
  344. package/src/engine/physics/narrowphase/hull_sphere_contact.d.ts +54 -0
  345. package/src/engine/physics/narrowphase/hull_sphere_contact.d.ts.map +1 -0
  346. package/src/engine/physics/narrowphase/hull_sphere_contact.js +244 -0
  347. package/src/engine/physics/narrowphase/mesh_convex_hull.js +5 -1
  348. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.d.ts +2 -0
  349. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.d.ts.map +1 -1
  350. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +56 -31
  351. package/src/engine/physics/narrowphase/narrowphase_step.d.ts +6 -5
  352. package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
  353. package/src/engine/physics/narrowphase/narrowphase_step.js +2185 -483
  354. package/src/engine/physics/narrowphase/reduce_manifold_contacts.d.ts +56 -5
  355. package/src/engine/physics/narrowphase/reduce_manifold_contacts.d.ts.map +1 -1
  356. package/src/engine/physics/narrowphase/reduce_manifold_contacts.js +546 -5
  357. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts +29 -2
  358. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -1
  359. package/src/engine/physics/narrowphase/refine_ray_concave.js +628 -75
  360. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +3 -2
  361. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
  362. package/src/engine/physics/narrowphase/refine_ray_hit.js +3 -2
  363. package/src/engine/physics/narrowphase/rigid_subject_pose.d.ts +27 -0
  364. package/src/engine/physics/narrowphase/rigid_subject_pose.d.ts.map +1 -0
  365. package/src/engine/physics/narrowphase/rigid_subject_pose.js +96 -0
  366. package/src/engine/physics/narrowphase/sphere_box_contact.d.ts +14 -0
  367. package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -1
  368. package/src/engine/physics/narrowphase/sphere_box_contact.js +51 -5
  369. package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts +12 -0
  370. package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts.map +1 -1
  371. package/src/engine/physics/narrowphase/sphere_sphere_contact.js +30 -0
  372. package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts +12 -0
  373. package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts.map +1 -1
  374. package/src/engine/physics/narrowphase/sphere_triangle_contact.js +37 -8
  375. package/src/engine/physics/narrowphase/support_feature_manifold.d.ts +139 -0
  376. package/src/engine/physics/narrowphase/support_feature_manifold.d.ts.map +1 -0
  377. package/src/engine/physics/narrowphase/support_feature_manifold.js +949 -0
  378. package/src/engine/physics/queries/PhysicsSurfacePoint.d.ts +32 -13
  379. package/src/engine/physics/queries/PhysicsSurfacePoint.d.ts.map +1 -1
  380. package/src/engine/physics/queries/PhysicsSurfacePoint.js +36 -13
  381. package/src/engine/physics/queries/convex_overlaps_shape.d.ts +27 -0
  382. package/src/engine/physics/queries/convex_overlaps_shape.d.ts.map +1 -0
  383. package/src/engine/physics/queries/convex_overlaps_shape.js +169 -0
  384. package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -1
  385. package/src/engine/physics/queries/overlap_shape.js +57 -79
  386. package/src/engine/physics/queries/raycast.d.ts +10 -4
  387. package/src/engine/physics/queries/raycast.d.ts.map +1 -1
  388. package/src/engine/physics/queries/raycast.js +133 -9
  389. package/src/engine/physics/queries/shape_cast.d.ts +55 -24
  390. package/src/engine/physics/queries/shape_cast.d.ts.map +1 -1
  391. package/src/engine/physics/queries/shape_cast.js +2061 -245
  392. package/src/engine/physics/solver/apply_impulse_to_body.d.ts +32 -10
  393. package/src/engine/physics/solver/apply_impulse_to_body.d.ts.map +1 -1
  394. package/src/engine/physics/solver/apply_impulse_to_body.js +69 -28
  395. package/src/engine/physics/solver/solve_contacts.d.ts +65 -18
  396. package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
  397. package/src/engine/physics/solver/solve_contacts.js +458 -234
  398. package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -1
  399. package/src/engine/physics/vehicle/RaycastVehicle.js +5 -6
  400. package/src/format/image/avif/api/convert_to_rgba.d.ts +2 -2
  401. package/src/format/image/avif/api/convert_to_rgba.d.ts.map +1 -1
  402. package/src/format/image/avif/api/convert_to_rgba.js +10 -9
  403. package/src/format/image/avif/api/decode_avif.d.ts +2 -2
  404. package/src/format/image/avif/api/decode_avif.d.ts.map +1 -1
  405. package/src/format/image/avif/api/decode_avif.js +1 -1
  406. package/src/shade/assets/textures/split_sum.bin +0 -0
  407. package/src/shade/device/mock/SoftwareGPUQueue.d.ts.map +1 -1
  408. package/src/shade/device/mock/SoftwareGPUQueue.js +2 -1
  409. package/src/shade/playground/path_traced_media/README.md +78 -0
  410. package/src/shade/playground/path_traced_media/capture_radiance.d.ts +33 -0
  411. package/src/shade/playground/path_traced_media/capture_radiance.d.ts.map +1 -0
  412. package/src/shade/playground/path_traced_media/capture_radiance.js +174 -0
  413. package/src/shade/playground/path_traced_media/checks.d.ts +160 -0
  414. package/src/shade/playground/path_traced_media/checks.d.ts.map +1 -0
  415. package/src/shade/playground/path_traced_media/checks.js +921 -0
  416. package/src/shade/playground/path_traced_media/index.html +71 -0
  417. package/src/shade/playground/path_traced_media/main.d.ts +2 -0
  418. package/src/shade/playground/path_traced_media/main.d.ts.map +1 -0
  419. package/src/shade/playground/path_traced_media/main.js +440 -0
  420. package/src/shade/playground/path_traced_media/make_wave_pool_geometry.d.ts +26 -0
  421. package/src/shade/playground/path_traced_media/make_wave_pool_geometry.d.ts.map +1 -0
  422. package/src/shade/playground/path_traced_media/make_wave_pool_geometry.js +117 -0
  423. package/src/shade/playground/path_traced_media/views.d.ts +21 -0
  424. package/src/shade/playground/path_traced_media/views.d.ts.map +1 -0
  425. package/src/shade/playground/path_traced_media/views.js +161 -0
  426. package/src/shade/playground/sh_exponential/README.md +43 -0
  427. package/src/shade/playground/sh_exponential/index.html +59 -0
  428. package/src/shade/playground/sh_exponential/main.d.ts +2 -0
  429. package/src/shade/playground/sh_exponential/main.d.ts.map +1 -0
  430. package/src/shade/playground/sh_exponential/main.js +571 -0
  431. package/src/shade/playground/sh_exponential/sh_exp_playground_shaders.d.ts +46 -0
  432. package/src/shade/playground/sh_exponential/sh_exp_playground_shaders.d.ts.map +1 -0
  433. package/src/shade/playground/sh_exponential/sh_exp_playground_shaders.js +270 -0
  434. package/src/shade/playground/ssr_reprojection/README.md +111 -103
  435. package/src/shade/playground/ssr_reprojection/SpatialFilterProbe.js +68 -68
  436. package/src/shade/playground/ssr_reprojection/index.html +80 -79
  437. package/src/shade/playground/ssr_reprojection/main.js +256 -247
  438. package/src/shade/playground/ssr_variance/capture.js +96 -96
  439. package/src/shade/playground/ssr_variance/reference.js +78 -78
  440. package/src/shade/playground/vsm/README.md +96 -0
  441. package/src/shade/playground/vsm/VSMDebugOverlay.d.ts +49 -0
  442. package/src/shade/playground/vsm/VSMDebugOverlay.d.ts.map +1 -0
  443. package/src/shade/playground/vsm/VSMDebugOverlay.js +351 -0
  444. package/src/shade/playground/vsm/clipmap_panel.d.ts +8 -0
  445. package/src/shade/playground/vsm/clipmap_panel.d.ts.map +1 -0
  446. package/src/shade/playground/vsm/clipmap_panel.js +138 -0
  447. package/src/shade/playground/vsm/index.html +170 -0
  448. package/src/shade/playground/vsm/main.d.ts +2 -0
  449. package/src/shade/playground/vsm/main.d.ts.map +1 -0
  450. package/src/shade/playground/vsm/main.js +389 -0
  451. package/src/shade/playground/vsm/scenes.d.ts +52 -0
  452. package/src/shade/playground/vsm/scenes.d.ts.map +1 -0
  453. package/src/shade/playground/vsm/scenes.js +320 -0
  454. package/src/shade/playground/vsm/verify_vsm.d.ts +38 -0
  455. package/src/shade/playground/vsm/verify_vsm.d.ts.map +1 -0
  456. package/src/shade/playground/vsm/verify_vsm.js +305 -0
  457. package/src/shade/playground/water_refraction/README.md +76 -19
  458. package/src/shade/playground/water_refraction/index.html +6 -8
  459. package/src/shade/playground/water_refraction/main.js +22 -12
  460. package/src/shade/playground/water_refraction/world.d.ts +3 -0
  461. package/src/shade/playground/water_refraction/world.d.ts.map +1 -1
  462. package/src/shade/playground/water_refraction/world.js +19 -5
  463. package/src/shade/renderer/Renderer.d.ts +21 -0
  464. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  465. package/src/shade/renderer/Renderer.js +95 -1
  466. package/src/shade/renderer/animation/GPUAnimationManager.d.ts +8 -0
  467. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  468. package/src/shade/renderer/animation/GPUAnimationManager.js +18 -0
  469. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts +7 -0
  470. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
  471. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +11 -0
  472. package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.d.ts +3 -1
  473. package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.d.ts.map +1 -1
  474. package/src/shade/renderer/animation/skinning/SKINNING_BINDING_STRUCT.js +10 -1
  475. package/src/shade/renderer/buffer/table/serde/table_wgsl_gen_read_code.d.ts.map +1 -1
  476. package/src/shade/renderer/buffer/table/serde/table_wgsl_gen_read_code.js +28 -0
  477. package/src/shade/renderer/buffer/table/serde/table_wgsl_gen_write_code.d.ts.map +1 -1
  478. package/src/shade/renderer/buffer/table/serde/table_wgsl_gen_write_code.js +31 -1
  479. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.d.ts.map +1 -1
  480. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.js +9 -1
  481. package/src/shade/renderer/geometry/MeshletGeometry.d.ts +3 -0
  482. package/src/shade/renderer/geometry/MeshletGeometry.d.ts.map +1 -1
  483. package/src/shade/renderer/geometry/MeshletGeometry.js +1 -0
  484. package/src/shade/renderer/geometry/sdf/chunk_scene_point_query_volume_sign.d.ts.map +1 -1
  485. package/src/shade/renderer/geometry/sdf/chunk_scene_point_query_volume_sign.js +6 -4
  486. package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.d.ts.map +1 -1
  487. package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.js +1 -0
  488. package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_rand1.d.ts +7 -0
  489. package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_rand1.d.ts.map +1 -1
  490. package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_rand1.js +12 -12
  491. package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_spherical.d.ts +4 -0
  492. package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_spherical.d.ts.map +1 -1
  493. package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_spherical.js +13 -9
  494. package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.d.ts.map +1 -1
  495. package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.js +2 -0
  496. package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.d.ts.map +1 -1
  497. package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.js +3 -0
  498. package/src/shade/renderer/global_illumination/lpv/deprecated/probe_trace_compute_shader_code.d.ts.map +1 -1
  499. package/src/shade/renderer/global_illumination/lpv/deprecated/probe_trace_compute_shader_code.js +4 -9
  500. package/src/shade/renderer/light/LIGHT_DATABASE_SPEC.d.ts.map +1 -1
  501. package/src/shade/renderer/light/LIGHT_DATABASE_SPEC.js +79 -11
  502. package/src/shade/renderer/light/environment/rgbe/load_environment_map.d.ts +2 -1
  503. package/src/shade/renderer/light/environment/rgbe/load_environment_map.d.ts.map +1 -1
  504. package/src/shade/renderer/light/environment/rgbe/load_environment_map.js +2 -1
  505. package/src/shade/renderer/light/environment/rgbe/parse_rgbe.d.ts +1 -1
  506. package/src/shade/renderer/light/environment/rgbe/parse_rgbe.js +2 -2
  507. package/src/shade/renderer/light/environment/rgbe/rgbe_byte_to_rgb_half.d.ts +2 -2
  508. package/src/shade/renderer/light/environment/rgbe/rgbe_byte_to_rgb_half.d.ts.map +1 -1
  509. package/src/shade/renderer/light/environment/rgbe/rgbe_byte_to_rgb_half.js +6 -14
  510. package/src/shade/renderer/light/environment/sh_exponential/README.md +171 -0
  511. package/src/shade/renderer/light/environment/sh_exponential/SHExpEncoder.d.ts +170 -0
  512. package/src/shade/renderer/light/environment/sh_exponential/SHExpEncoder.d.ts.map +1 -0
  513. package/src/shade/renderer/light/environment/sh_exponential/SHExpEncoder.js +377 -0
  514. package/src/shade/renderer/light/environment/sh_exponential/SH_EXP_ENCODE_PARAMETERS_STRUCT.d.ts +16 -0
  515. package/src/shade/renderer/light/environment/sh_exponential/SH_EXP_ENCODE_PARAMETERS_STRUCT.d.ts.map +1 -0
  516. package/src/shade/renderer/light/environment/sh_exponential/SH_EXP_ENCODE_PARAMETERS_STRUCT.js +27 -0
  517. package/src/shade/renderer/light/environment/sh_exponential/chunk_sh_exp_workgroup_sum.d.ts +21 -0
  518. package/src/shade/renderer/light/environment/sh_exponential/chunk_sh_exp_workgroup_sum.d.ts.map +1 -0
  519. package/src/shade/renderer/light/environment/sh_exponential/chunk_sh_exp_workgroup_sum.js +57 -0
  520. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_design_row.d.ts +33 -0
  521. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_design_row.d.ts.map +1 -0
  522. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_design_row.js +90 -0
  523. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_fit_samples.d.ts +28 -0
  524. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_fit_samples.d.ts.map +1 -0
  525. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_fit_samples.js +92 -0
  526. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_layout.d.ts +39 -0
  527. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_layout.d.ts.map +1 -0
  528. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_layout.js +44 -0
  529. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_solve_matrix.d.ts +42 -0
  530. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_solve_matrix.d.ts.map +1 -0
  531. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_solve_matrix.js +123 -0
  532. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.d.ts +30 -0
  533. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.d.ts.map +1 -0
  534. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.js +147 -0
  535. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_log_floor.d.ts +24 -0
  536. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_log_floor.d.ts.map +1 -0
  537. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_log_floor.js +79 -0
  538. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_project.d.ts +17 -0
  539. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_project.d.ts.map +1 -0
  540. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_project.js +99 -0
  541. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_reference_reflectance.d.ts +24 -0
  542. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_reference_reflectance.d.ts.map +1 -0
  543. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_reference_reflectance.js +104 -0
  544. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_solve.d.ts +12 -0
  545. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_solve.d.ts.map +1 -0
  546. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_solve.js +93 -0
  547. package/src/shade/renderer/lightmap/GPULightMap.d.ts.map +1 -1
  548. package/src/shade/renderer/lightmap/GPULightMap.js +1 -0
  549. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_irradiance_flat.d.ts.map +1 -1
  550. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_irradiance_flat.js +4 -0
  551. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_sh.d.ts.map +1 -1
  552. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_sh.js +4 -0
  553. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  554. package/src/shade/renderer/material/PIXEL_TEXTURE_ORM.d.ts +1 -1
  555. package/src/shade/renderer/material/PIXEL_TEXTURE_ORM.d.ts.map +1 -1
  556. package/src/shade/renderer/material/StandardShadeMaterial.d.ts +15 -0
  557. package/src/shade/renderer/material/StandardShadeMaterial.d.ts.map +1 -1
  558. package/src/shade/renderer/material/StandardShadeMaterial.js +17 -0
  559. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +36 -2
  560. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  561. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +218 -18
  562. package/src/shade/renderer/material/resident/INDIRECT_MATERIAL_STRUCT.d.ts.map +1 -1
  563. package/src/shade/renderer/material/resident/INDIRECT_MATERIAL_STRUCT.js +10 -0
  564. package/src/shade/renderer/medium/MEDIA_TABLE_STRUCT.d.ts +17 -0
  565. package/src/shade/renderer/medium/MEDIA_TABLE_STRUCT.d.ts.map +1 -0
  566. package/src/shade/renderer/medium/MEDIA_TABLE_STRUCT.js +24 -0
  567. package/src/shade/renderer/medium/MEDIUM_PHASE_LOBE_KIND.d.ts +24 -0
  568. package/src/shade/renderer/medium/MEDIUM_PHASE_LOBE_KIND.d.ts.map +1 -0
  569. package/src/shade/renderer/medium/MEDIUM_PHASE_LOBE_KIND.js +18 -0
  570. package/src/shade/renderer/medium/MEDIUM_STRUCT.d.ts +20 -0
  571. package/src/shade/renderer/medium/MEDIUM_STRUCT.d.ts.map +1 -0
  572. package/src/shade/renderer/medium/MEDIUM_STRUCT.js +43 -0
  573. package/src/shade/renderer/medium/ParticipatingMedium.d.ts +111 -0
  574. package/src/shade/renderer/medium/ParticipatingMedium.d.ts.map +1 -0
  575. package/src/shade/renderer/medium/ParticipatingMedium.js +211 -0
  576. package/src/shade/renderer/medium/chunk_medium_lobe_phase.d.ts +13 -0
  577. package/src/shade/renderer/medium/chunk_medium_lobe_phase.d.ts.map +1 -0
  578. package/src/shade/renderer/medium/chunk_medium_lobe_phase.js +33 -0
  579. package/src/shade/renderer/medium/participating_medium_serialization.d.ts +32 -0
  580. package/src/shade/renderer/medium/participating_medium_serialization.d.ts.map +1 -0
  581. package/src/shade/renderer/medium/participating_medium_serialization.js +181 -0
  582. package/src/shade/renderer/medium/phase/MediumPhaseFunction.d.ts +48 -0
  583. package/src/shade/renderer/medium/phase/MediumPhaseFunction.d.ts.map +1 -0
  584. package/src/shade/renderer/medium/phase/MediumPhaseFunction.js +42 -0
  585. package/src/shade/renderer/medium/phase/PhaseCornetteShanks.d.ts +23 -0
  586. package/src/shade/renderer/medium/phase/PhaseCornetteShanks.d.ts.map +1 -0
  587. package/src/shade/renderer/medium/phase/PhaseCornetteShanks.js +45 -0
  588. package/src/shade/renderer/medium/phase/PhaseDraine.d.ts +30 -0
  589. package/src/shade/renderer/medium/phase/PhaseDraine.d.ts.map +1 -0
  590. package/src/shade/renderer/medium/phase/PhaseDraine.js +58 -0
  591. package/src/shade/renderer/medium/phase/PhaseFournierForand.d.ts +24 -0
  592. package/src/shade/renderer/medium/phase/PhaseFournierForand.d.ts.map +1 -0
  593. package/src/shade/renderer/medium/phase/PhaseFournierForand.js +53 -0
  594. package/src/shade/renderer/medium/phase/PhaseHenyeyGreenstein.d.ts +21 -0
  595. package/src/shade/renderer/medium/phase/PhaseHenyeyGreenstein.d.ts.map +1 -0
  596. package/src/shade/renderer/medium/phase/PhaseHenyeyGreenstein.js +43 -0
  597. package/src/shade/renderer/medium/phase/PhaseJendersieDeon.d.ts +25 -0
  598. package/src/shade/renderer/medium/phase/PhaseJendersieDeon.d.ts.map +1 -0
  599. package/src/shade/renderer/medium/phase/PhaseJendersieDeon.js +66 -0
  600. package/src/shade/renderer/medium/phase/PhaseRayleigh.d.ts +9 -0
  601. package/src/shade/renderer/medium/phase/PhaseRayleigh.d.ts.map +1 -0
  602. package/src/shade/renderer/medium/phase/PhaseRayleigh.js +21 -0
  603. package/src/shade/renderer/medium/phase/PhaseWaterMolecular.d.ts +28 -0
  604. package/src/shade/renderer/medium/phase/PhaseWaterMolecular.d.ts.map +1 -0
  605. package/src/shade/renderer/medium/phase/PhaseWaterMolecular.js +49 -0
  606. package/src/shade/renderer/medium/water/WATER_DEFAULT_PATH_LENGTHS.d.ts +10 -0
  607. package/src/shade/renderer/medium/water/WATER_DEFAULT_PATH_LENGTHS.d.ts.map +1 -0
  608. package/src/shade/renderer/medium/water/WATER_DEFAULT_PATH_LENGTHS.js +25 -0
  609. package/src/shade/renderer/medium/water/water_iop_from_appearance.d.ts +47 -0
  610. package/src/shade/renderer/medium/water/water_iop_from_appearance.d.ts.map +1 -0
  611. package/src/shade/renderer/medium/water/water_iop_from_appearance.js +174 -0
  612. package/src/shade/renderer/medium/water/water_iop_from_constituents.d.ts +57 -0
  613. package/src/shade/renderer/medium/water/water_iop_from_constituents.d.ts.map +1 -0
  614. package/src/shade/renderer/medium/water/water_iop_from_constituents.js +242 -0
  615. package/src/shade/renderer/medium/water/water_iop_jerlov.d.ts +41 -0
  616. package/src/shade/renderer/medium/water/water_iop_jerlov.d.ts.map +1 -0
  617. package/src/shade/renderer/medium/water/water_iop_jerlov.js +231 -0
  618. package/src/shade/renderer/medium/water/water_iop_spectral_reference.d.ts +66 -0
  619. package/src/shade/renderer/medium/water/water_iop_spectral_reference.d.ts.map +1 -0
  620. package/src/shade/renderer/medium/water/water_iop_spectral_reference.js +223 -0
  621. package/src/shade/renderer/medium/water/water_medium_from_iop.d.ts +96 -0
  622. package/src/shade/renderer/medium/water/water_medium_from_iop.d.ts.map +1 -0
  623. package/src/shade/renderer/medium/water/water_medium_from_iop.js +145 -0
  624. package/src/shade/renderer/medium/water/water_presets.d.ts +69 -0
  625. package/src/shade/renderer/medium/water/water_presets.d.ts.map +1 -0
  626. package/src/shade/renderer/medium/water/water_presets.js +119 -0
  627. package/src/shade/renderer/medium/water/water_rgb_from_spectral.d.ts +69 -0
  628. package/src/shade/renderer/medium/water/water_rgb_from_spectral.d.ts.map +1 -0
  629. package/src/shade/renderer/medium/water/water_rgb_from_spectral.js +341 -0
  630. package/src/shade/renderer/particles/DESIGN.md +4 -2
  631. package/src/shade/renderer/particles/shaders/chunk_particle_avboit_voxel_fade.d.ts +26 -0
  632. package/src/shade/renderer/particles/shaders/chunk_particle_avboit_voxel_fade.d.ts.map +1 -0
  633. package/src/shade/renderer/particles/shaders/chunk_particle_avboit_voxel_fade.js +71 -0
  634. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts.map +1 -1
  635. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +16 -1
  636. package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.d.ts.map +1 -1
  637. package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.js +6 -13
  638. package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +520 -0
  639. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.d.ts.map +1 -1
  640. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.js +1 -0
  641. package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.d.ts.map +1 -1
  642. package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.js +2 -0
  643. package/src/shade/renderer/path_tracer/graph_path_trace_scene.d.ts.map +1 -1
  644. package/src/shade/renderer/path_tracer/graph_path_trace_scene.js +188 -185
  645. package/src/shade/renderer/path_tracer/shader/STANDARD_MATERIAL_DATA_STRUCT.d.ts.map +1 -1
  646. package/src/shade/renderer/path_tracer/shader/STANDARD_MATERIAL_DATA_STRUCT.js +4 -1
  647. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_tangent_normal.d.ts +13 -0
  648. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_tangent_normal.d.ts.map +1 -0
  649. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_tangent_normal.js +29 -0
  650. package/src/shade/renderer/path_tracer/shader/chunk_render_trace_path.d.ts +79 -0
  651. package/src/shade/renderer/path_tracer/shader/chunk_render_trace_path.d.ts.map +1 -1
  652. package/src/shade/renderer/path_tracer/shader/chunk_render_trace_path.js +360 -70
  653. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts.map +1 -1
  654. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js +4 -3
  655. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_beam.d.ts +24 -0
  656. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_beam.d.ts.map +1 -0
  657. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_beam.js +99 -0
  658. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_connect.d.ts +68 -0
  659. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_connect.d.ts.map +1 -0
  660. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_connect.js +263 -0
  661. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_constraint.d.ts +55 -0
  662. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_constraint.d.ts.map +1 -0
  663. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_constraint.js +148 -0
  664. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light.d.ts +17 -0
  665. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light.d.ts.map +1 -0
  666. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light.js +41 -0
  667. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light_sample.d.ts +20 -0
  668. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light_sample.d.ts.map +1 -0
  669. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light_sample.js +105 -0
  670. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle.d.ts +28 -0
  671. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle.d.ts.map +1 -0
  672. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle.js +97 -0
  673. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle_of_hit.d.ts +12 -0
  674. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle_of_hit.d.ts.map +1 -0
  675. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle_of_hit.js +46 -0
  676. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_interface.d.ts +25 -0
  677. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_interface.d.ts.map +1 -0
  678. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_interface.js +75 -0
  679. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_path.d.ts +54 -0
  680. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_path.d.ts.map +1 -0
  681. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_path.js +245 -0
  682. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_phase.d.ts +42 -0
  683. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_phase.d.ts.map +1 -0
  684. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_phase.js +236 -0
  685. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_scatter_nee.d.ts +24 -0
  686. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_scatter_nee.d.ts.map +1 -0
  687. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_scatter_nee.js +97 -0
  688. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_stack.d.ts +57 -0
  689. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_stack.d.ts.map +1 -0
  690. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_stack.js +222 -0
  691. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_transport.d.ts +88 -0
  692. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_transport.d.ts.map +1 -0
  693. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_transport.js +304 -0
  694. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_volumes.d.ts +35 -0
  695. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_volumes.d.ts.map +1 -0
  696. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_volumes.js +137 -0
  697. package/src/shade/renderer/postprocess/eye/shader_autoexposure_build_histogram.d.ts.map +1 -1
  698. package/src/shade/renderer/postprocess/eye/shader_autoexposure_build_histogram.js +5 -35
  699. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  700. package/src/shade/renderer/postprocess/ssr/SSR.d.ts +12 -4
  701. package/src/shade/renderer/postprocess/ssr/SSR.d.ts.map +1 -1
  702. package/src/shade/renderer/postprocess/ssr/SSR.js +36 -8
  703. package/src/shade/renderer/postprocess/ssr/SSR_DENOISE_TILE_SIZE.d.ts +3 -0
  704. package/src/shade/renderer/postprocess/ssr/SSR_DENOISE_TILE_SIZE.d.ts.map +1 -0
  705. package/src/shade/renderer/postprocess/ssr/SSR_DENOISE_TILE_SIZE.js +2 -0
  706. package/src/shade/renderer/postprocess/ssr/chunk_ssr_denoise_tile_active.d.ts +8 -0
  707. package/src/shade/renderer/postprocess/ssr/chunk_ssr_denoise_tile_active.d.ts.map +1 -0
  708. package/src/shade/renderer/postprocess/ssr/chunk_ssr_denoise_tile_active.js +40 -0
  709. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.d.ts.map +1 -1
  710. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.js +85 -84
  711. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.d.ts.map +1 -1
  712. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.js +150 -137
  713. package/src/shade/renderer/postprocess/ssr/shader_ssr_denoise_tile_classify.d.ts +3 -0
  714. package/src/shade/renderer/postprocess/ssr/shader_ssr_denoise_tile_classify.d.ts.map +1 -0
  715. package/src/shade/renderer/postprocess/ssr/shader_ssr_denoise_tile_classify.js +44 -0
  716. package/src/shade/renderer/postprocess/ssr/shader_ssr_denoise_tile_dilate.d.ts +5 -0
  717. package/src/shade/renderer/postprocess/ssr/shader_ssr_denoise_tile_dilate.d.ts.map +1 -0
  718. package/src/shade/renderer/postprocess/ssr/shader_ssr_denoise_tile_dilate.js +35 -0
  719. package/src/shade/renderer/postprocess/ssr/ssr_graph_denoise_tiles.d.ts +26 -0
  720. package/src/shade/renderer/postprocess/ssr/ssr_graph_denoise_tiles.d.ts.map +1 -0
  721. package/src/shade/renderer/postprocess/ssr/ssr_graph_denoise_tiles.js +42 -0
  722. package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts +1 -0
  723. package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts.map +1 -1
  724. package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.js +113 -105
  725. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.d.ts +2 -0
  726. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.d.ts.map +1 -1
  727. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.js +177 -167
  728. package/src/shade/renderer/rasterize/DEPTH_STENCIL_STATE_SHADOW.d.ts.map +1 -1
  729. package/src/shade/renderer/rasterize/DEPTH_STENCIL_STATE_SHADOW.js +10 -0
  730. package/src/shade/renderer/rasterize/RasterizationJob.d.ts +1 -2
  731. package/src/shade/renderer/rasterize/RasterizationJob.d.ts.map +1 -1
  732. package/src/shade/renderer/rasterize/RasterizationJob.js +1 -2
  733. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts +5 -6
  734. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts.map +1 -1
  735. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +5 -6
  736. package/src/shade/renderer/rasterize/native/avboit/AVBOITSettings.d.ts +8 -0
  737. package/src/shade/renderer/rasterize/native/avboit/AVBOITSettings.d.ts.map +1 -1
  738. package/src/shade/renderer/rasterize/native/avboit/AVBOITSettings.js +9 -0
  739. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts +14 -4
  740. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts.map +1 -1
  741. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js +13 -4
  742. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.d.ts +13 -7
  743. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.d.ts.map +1 -1
  744. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.js +13 -7
  745. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +309 -0
  746. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +54 -9
  747. package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.d.ts.map +1 -1
  748. package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.js +9 -1
  749. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_footprint_bias.d.ts +33 -0
  750. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_footprint_bias.d.ts.map +1 -0
  751. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_footprint_bias.js +45 -0
  752. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_integral_depth_coordinate.d.ts +4 -0
  753. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_integral_depth_coordinate.d.ts.map +1 -1
  754. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_integral_depth_coordinate.js +6 -2
  755. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_sample_transmittance.d.ts +2 -1
  756. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_sample_transmittance.d.ts.map +1 -1
  757. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_sample_transmittance.js +4 -2
  758. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_level_word.d.ts +14 -0
  759. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_level_word.d.ts.map +1 -0
  760. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_level_word.js +28 -0
  761. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_lut_entry.d.ts +10 -0
  762. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_lut_entry.d.ts.map +1 -0
  763. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_lut_entry.js +18 -0
  764. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_marks.d.ts +19 -0
  765. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_marks.d.ts.map +1 -0
  766. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_marks.js +44 -0
  767. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_step_count.d.ts +10 -0
  768. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_step_count.d.ts.map +1 -0
  769. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_step_count.js +17 -0
  770. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  771. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +3 -1
  772. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_build_warp.d.ts.map +1 -1
  773. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_build_warp.js +163 -64
  774. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  775. package/src/shade/renderer/scene/GPUSceneContext.js +4 -1
  776. package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.d.ts.map +1 -1
  777. package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.js +16 -0
  778. package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.d.ts +12 -1
  779. package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.d.ts.map +1 -1
  780. package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.js +22 -3
  781. package/src/shade/renderer/shader/chunk/atmosphere/phase/chunk_sample_henyey_greenstein_cos.d.ts +18 -0
  782. package/src/shade/renderer/shader/chunk/atmosphere/phase/chunk_sample_henyey_greenstein_cos.d.ts.map +1 -0
  783. package/src/shade/renderer/shader/chunk/atmosphere/phase/chunk_sample_henyey_greenstein_cos.js +31 -0
  784. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_phase_fournier_forand.d.ts +43 -0
  785. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_phase_fournier_forand.d.ts.map +1 -0
  786. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_phase_fournier_forand.js +153 -0
  787. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_sample_fournier_forand.d.ts +13 -0
  788. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_sample_fournier_forand.d.ts.map +1 -0
  789. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_sample_fournier_forand.js +57 -0
  790. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/fournier_forand_parameters_from_backscatter.d.ts +45 -0
  791. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/fournier_forand_parameters_from_backscatter.d.ts.map +1 -0
  792. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/fournier_forand_parameters_from_backscatter.js +109 -0
  793. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/build_lut_fog_parameters_jendersie_deon.d.ts.map +1 -1
  794. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/build_lut_fog_parameters_jendersie_deon.js +4 -5
  795. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_phase_draine.d.ts.map +1 -1
  796. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_phase_draine.js +6 -3
  797. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine.d.ts +24 -0
  798. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine.d.ts.map +1 -0
  799. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine.js +49 -0
  800. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine_cos.d.ts +18 -0
  801. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine_cos.d.ts.map +1 -1
  802. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine_cos.js +18 -0
  803. package/src/shade/renderer/shader/chunk/brdf/dfg/chunk_integrate_brdf.d.ts +3 -0
  804. package/src/shade/renderer/shader/chunk/brdf/dfg/chunk_integrate_brdf.d.ts.map +1 -1
  805. package/src/shade/renderer/shader/chunk/brdf/dfg/chunk_integrate_brdf.js +3 -0
  806. package/src/shade/renderer/shader/chunk/brdf/dfg/generate_dfg_texture.d.ts.map +1 -1
  807. package/src/shade/renderer/shader/chunk/brdf/dfg/generate_dfg_texture.js +1 -5
  808. package/src/shade/renderer/shader/chunk/brdf/dfg/shader_split_sum_dgf_lut.d.ts.map +1 -1
  809. package/src/shade/renderer/shader/chunk/brdf/dfg/shader_split_sum_dgf_lut.js +3 -1
  810. package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_dielectric.d.ts +24 -0
  811. package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_dielectric.d.ts.map +1 -0
  812. package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_dielectric.js +53 -0
  813. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_scene_point_query_is_inside_geometry.d.ts +7 -1
  814. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_scene_point_query_is_inside_geometry.d.ts.map +1 -1
  815. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_scene_point_query_is_inside_geometry.js +12 -60
  816. package/src/shade/renderer/shader/chunk/geometry/sphere/chunk_compute_spherical_excess.d.ts +26 -6
  817. package/src/shade/renderer/shader/chunk/geometry/sphere/chunk_compute_spherical_excess.d.ts.map +1 -1
  818. package/src/shade/renderer/shader/chunk/geometry/sphere/chunk_compute_spherical_excess.js +30 -20
  819. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_directional.d.ts.map +1 -1
  820. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_directional.js +3 -1
  821. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_point.d.ts.map +1 -1
  822. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_point.js +3 -1
  823. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_spot.d.ts.map +1 -1
  824. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_spot.js +3 -1
  825. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_receiver_cosine.d.ts +14 -0
  826. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_receiver_cosine.d.ts.map +1 -0
  827. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_receiver_cosine.js +25 -0
  828. package/src/shade/renderer/shader/chunk/light/sample/chunk_select_light_importance.d.ts +9 -2
  829. package/src/shade/renderer/shader/chunk/light/sample/chunk_select_light_importance.d.ts.map +1 -1
  830. package/src/shade/renderer/shader/chunk/light/sample/chunk_select_light_importance.js +33 -10
  831. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_importance.d.ts +30 -0
  832. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_importance.d.ts.map +1 -0
  833. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_importance.js +54 -0
  834. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_newton_ray.d.ts.map +1 -1
  835. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_newton_ray.js +71 -37
  836. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_band_of_index.d.ts +14 -0
  837. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_band_of_index.d.ts.map +1 -0
  838. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_band_of_index.js +30 -0
  839. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_basis_function.d.ts +19 -0
  840. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_basis_function.d.ts.map +1 -0
  841. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_basis_function.js +73 -0
  842. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_vmf_window.d.ts +19 -0
  843. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_vmf_window.d.ts.map +1 -0
  844. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_vmf_window.js +25 -0
  845. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/SH_EXP_P4Q2_PROBE_STRUCT.d.ts +17 -0
  846. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/SH_EXP_P4Q2_PROBE_STRUCT.d.ts.map +1 -0
  847. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/SH_EXP_P4Q2_PROBE_STRUCT.js +23 -0
  848. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_p4q2_base_reflectance.d.ts +26 -0
  849. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_p4q2_base_reflectance.d.ts.map +1 -0
  850. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_p4q2_base_reflectance.js +55 -0
  851. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_split_sum.d.ts +52 -0
  852. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_split_sum.d.ts.map +1 -0
  853. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_split_sum.js +65 -0
  854. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_basis_at.d.ts +13 -0
  855. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_basis_at.d.ts.map +1 -0
  856. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_basis_at.js +229 -0
  857. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_color_evaluate_vmf.d.ts +23 -0
  858. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_color_evaluate_vmf.d.ts.map +1 -0
  859. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_color_evaluate_vmf.js +278 -0
  860. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh2_color_evaluate_vmf.d.ts +23 -0
  861. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh2_color_evaluate_vmf.d.ts.map +1 -0
  862. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh2_color_evaluate_vmf.js +58 -0
  863. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh3_color_evaluate_vmf.d.ts +23 -0
  864. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh3_color_evaluate_vmf.d.ts.map +1 -0
  865. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh3_color_evaluate_vmf.js +75 -0
  866. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_basis_at.d.ts +13 -0
  867. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_basis_at.d.ts.map +1 -0
  868. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_basis_at.js +70 -0
  869. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_color_evaluate_vmf.d.ts +23 -0
  870. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_color_evaluate_vmf.d.ts.map +1 -0
  871. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_color_evaluate_vmf.js +95 -0
  872. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_basis_at.d.ts +13 -0
  873. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_basis_at.d.ts.map +1 -0
  874. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_basis_at.js +89 -0
  875. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_color_evaluate_vmf.d.ts +23 -0
  876. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_color_evaluate_vmf.d.ts.map +1 -0
  877. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_color_evaluate_vmf.js +118 -0
  878. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_basis_at.d.ts +13 -0
  879. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_basis_at.d.ts.map +1 -0
  880. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_basis_at.js +111 -0
  881. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_color_evaluate_vmf.d.ts +23 -0
  882. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_color_evaluate_vmf.d.ts.map +1 -0
  883. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_color_evaluate_vmf.js +144 -0
  884. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_basis_at.d.ts +13 -0
  885. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_basis_at.d.ts.map +1 -0
  886. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_basis_at.js +136 -0
  887. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_color_evaluate_vmf.d.ts +23 -0
  888. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_color_evaluate_vmf.d.ts.map +1 -0
  889. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_color_evaluate_vmf.js +173 -0
  890. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_basis_at.d.ts +13 -0
  891. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_basis_at.d.ts.map +1 -0
  892. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_basis_at.js +164 -0
  893. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_color_evaluate_vmf.d.ts +23 -0
  894. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_color_evaluate_vmf.d.ts.map +1 -0
  895. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_color_evaluate_vmf.js +205 -0
  896. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_basis_at.d.ts +13 -0
  897. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_basis_at.d.ts.map +1 -0
  898. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_basis_at.js +195 -0
  899. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_color_evaluate_vmf.d.ts +23 -0
  900. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_color_evaluate_vmf.d.ts.map +1 -0
  901. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_color_evaluate_vmf.js +240 -0
  902. package/src/shade/renderer/shader/resource/ShaderResourceGroupDescriptor.d.ts +0 -6
  903. package/src/shade/renderer/shader/resource/ShaderResourceGroupDescriptor.d.ts.map +1 -1
  904. package/src/shade/renderer/shader/resource/ShaderResourceGroupDescriptor.js +46 -1
  905. package/src/shade/renderer/shader/type/serde/read_gpu_typed_buffer.d.ts.map +1 -1
  906. package/src/shade/renderer/shader/type/serde/read_gpu_typed_buffer.js +3 -2
  907. package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.d.ts +7 -7
  908. package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.js +7 -7
  909. package/src/shade/renderer/shadow/map/DIRECTIONAL_SHADOWMAP_STRUCT.d.ts +19 -0
  910. package/src/shade/renderer/shadow/map/DIRECTIONAL_SHADOWMAP_STRUCT.d.ts.map +1 -0
  911. package/src/shade/renderer/shadow/map/DIRECTIONAL_SHADOWMAP_STRUCT.js +27 -0
  912. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.d.ts +43 -10
  913. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.d.ts.map +1 -1
  914. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.js +108 -48
  915. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts +74 -4
  916. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  917. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +681 -77
  918. package/src/shade/renderer/shadow/map/Shadowmap.d.ts +3 -1
  919. package/src/shade/renderer/shadow/map/Shadowmap.d.ts.map +1 -1
  920. package/src/shade/renderer/shadow/map/Shadowmap.js +3 -1
  921. package/src/shade/renderer/shadow/map/csm/CSM_CASCADE_STRUCT.d.ts +30 -0
  922. package/src/shade/renderer/shadow/map/csm/CSM_CASCADE_STRUCT.d.ts.map +1 -0
  923. package/src/shade/renderer/shadow/map/csm/CSM_CASCADE_STRUCT.js +52 -0
  924. package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_DEFAULT_CASCADE_COUNT.d.ts +8 -0
  925. package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_DEFAULT_CASCADE_COUNT.d.ts.map +1 -0
  926. package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_DEFAULT_CASCADE_COUNT.js +7 -0
  927. package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.d.ts +8 -6
  928. package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.d.ts.map +1 -1
  929. package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.js +28 -34
  930. package/src/shade/renderer/shadow/map/shader/chunk_sample_shadowmap_atlas_for_light.d.ts +8 -0
  931. package/src/shade/renderer/shadow/map/shader/chunk_sample_shadowmap_atlas_for_light.d.ts.map +1 -1
  932. package/src/shade/renderer/shadow/map/shader/chunk_sample_shadowmap_atlas_for_light.js +34 -3
  933. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_csm_compute_cascade_blended.d.ts +3 -2
  934. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_csm_compute_cascade_blended.d.ts.map +1 -1
  935. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_csm_compute_cascade_blended.js +19 -14
  936. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.d.ts +5 -3
  937. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.d.ts.map +1 -1
  938. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.js +27 -7
  939. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.d.ts.map +1 -1
  940. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.js +137 -133
  941. package/src/shade/renderer/shadow/map/shadowmap_budget_carry_over.d.ts +4 -4
  942. package/src/shade/renderer/shadow/map/shadowmap_budget_carry_over.js +4 -4
  943. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_FIRST_TEXEL_SIZE.d.ts +15 -0
  944. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_FIRST_TEXEL_SIZE.d.ts.map +1 -0
  945. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_FIRST_TEXEL_SIZE.js +14 -0
  946. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_PAGE_BUDGET.d.ts +16 -0
  947. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_PAGE_BUDGET.d.ts.map +1 -0
  948. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_PAGE_BUDGET.js +15 -0
  949. package/src/shade/renderer/shadow/vsm/PLAN.md +440 -0
  950. package/src/shade/renderer/shadow/vsm/REVIEW_LEDGER.md +43 -0
  951. package/src/shade/renderer/shadow/vsm/VSMMoverTracker.d.ts +42 -0
  952. package/src/shade/renderer/shadow/vsm/VSMMoverTracker.d.ts.map +1 -0
  953. package/src/shade/renderer/shadow/vsm/VSMMoverTracker.js +153 -0
  954. package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts +327 -0
  955. package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts.map +1 -0
  956. package/src/shade/renderer/shadow/vsm/VSMPageManager.js +1408 -0
  957. package/src/shade/renderer/shadow/vsm/VSM_COVER_LEVEL_STEP.d.ts +14 -0
  958. package/src/shade/renderer/shadow/vsm/VSM_COVER_LEVEL_STEP.d.ts.map +1 -0
  959. package/src/shade/renderer/shadow/vsm/VSM_COVER_LEVEL_STEP.js +13 -0
  960. package/src/shade/renderer/shadow/vsm/VSM_FEEDBACK_LAYOUT.d.ts +63 -0
  961. package/src/shade/renderer/shadow/vsm/VSM_FEEDBACK_LAYOUT.d.ts.map +1 -0
  962. package/src/shade/renderer/shadow/vsm/VSM_FEEDBACK_LAYOUT.js +70 -0
  963. package/src/shade/renderer/shadow/vsm/VSM_LEVEL_LIMIT.d.ts +11 -0
  964. package/src/shade/renderer/shadow/vsm/VSM_LEVEL_LIMIT.d.ts.map +1 -0
  965. package/src/shade/renderer/shadow/vsm/VSM_LEVEL_LIMIT.js +10 -0
  966. package/src/shade/renderer/shadow/vsm/VSM_MOVER_LAYOUT.d.ts +54 -0
  967. package/src/shade/renderer/shadow/vsm/VSM_MOVER_LAYOUT.d.ts.map +1 -0
  968. package/src/shade/renderer/shadow/vsm/VSM_MOVER_LAYOUT.js +75 -0
  969. package/src/shade/renderer/shadow/vsm/VSM_RECORD_STRUCT.d.ts +23 -0
  970. package/src/shade/renderer/shadow/vsm/VSM_RECORD_STRUCT.d.ts.map +1 -0
  971. package/src/shade/renderer/shadow/vsm/VSM_RECORD_STRUCT.js +85 -0
  972. package/src/shade/renderer/shadow/vsm/VSM_SLOT_STRUCT.d.ts +16 -0
  973. package/src/shade/renderer/shadow/vsm/VSM_SLOT_STRUCT.d.ts.map +1 -0
  974. package/src/shade/renderer/shadow/vsm/VSM_SLOT_STRUCT.js +29 -0
  975. package/src/shade/renderer/shadow/vsm/VirtualShadowMap.d.ts +136 -25
  976. package/src/shade/renderer/shadow/vsm/VirtualShadowMap.d.ts.map +1 -1
  977. package/src/shade/renderer/shadow/vsm/VirtualShadowMap.js +280 -100
  978. package/src/shade/renderer/shadow/vsm/graph_vsm_feedback.d.ts +39 -0
  979. package/src/shade/renderer/shadow/vsm/graph_vsm_feedback.d.ts.map +1 -0
  980. package/src/shade/renderer/shadow/vsm/graph_vsm_feedback.js +289 -0
  981. package/src/shade/renderer/shadow/vsm/shader/chunk_shadowmap_sample_virtual.d.ts +18 -0
  982. package/src/shade/renderer/shadow/vsm/shader/chunk_shadowmap_sample_virtual.d.ts.map +1 -0
  983. package/src/shade/renderer/shadow/vsm/shader/chunk_shadowmap_sample_virtual.js +65 -0
  984. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_addressing.d.ts +17 -0
  985. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_addressing.d.ts.map +1 -0
  986. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_addressing.js +75 -0
  987. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_append_mover.d.ts +14 -0
  988. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_append_mover.d.ts.map +1 -0
  989. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_append_mover.js +45 -0
  990. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_find_page.d.ts +17 -0
  991. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_find_page.d.ts.map +1 -0
  992. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_find_page.js +100 -0
  993. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_page_view_projection.d.ts +15 -0
  994. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_page_view_projection.d.ts.map +1 -0
  995. package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_page_view_projection.js +47 -0
  996. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_demand.d.ts +26 -0
  997. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_demand.d.ts.map +1 -0
  998. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_demand.js +156 -0
  999. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_gather_counts.d.ts +13 -0
  1000. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_gather_counts.d.ts.map +1 -0
  1001. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_gather_counts.js +65 -0
  1002. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_movers.d.ts +30 -0
  1003. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_movers.d.ts.map +1 -0
  1004. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_movers.js +184 -0
  1005. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_skinned_movers.d.ts +23 -0
  1006. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_skinned_movers.d.ts.map +1 -0
  1007. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_skinned_movers.js +109 -0
  1008. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_stale.d.ts +56 -0
  1009. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_stale.d.ts.map +1 -0
  1010. package/src/shade/renderer/shadow/vsm/shader/shader_vsm_stale.js +211 -0
  1011. package/src/shade/renderer/texture/format/decode_typed_gpu_data.d.ts +1 -1
  1012. package/src/shade/renderer/texture/format/decode_typed_gpu_data.d.ts.map +1 -1
  1013. package/src/shade/renderer/texture/format/decode_typed_gpu_data.js +3 -13
  1014. package/src/shade/renderer/texture/format/infer_format_from_parameters.d.ts.map +1 -1
  1015. package/src/shade/renderer/texture/format/infer_format_from_parameters.js +2 -3
  1016. package/src/shade/renderer/texture/texture_write_to_gpu.d.ts.map +1 -1
  1017. package/src/shade/renderer/texture/texture_write_to_gpu.js +16 -16
  1018. package/src/shade/renderer/texture/util/BLACK_PIXEL_TEXTURE.d.ts +1 -1
  1019. package/src/shade/renderer/texture/util/BLACK_PIXEL_TEXTURE.d.ts.map +1 -1
  1020. package/src/shade/renderer/texture/util/PIXEL_TEXTURE_NORMAL.d.ts +18 -1
  1021. package/src/shade/renderer/texture/util/PIXEL_TEXTURE_NORMAL.d.ts.map +1 -1
  1022. package/src/shade/renderer/texture/util/PIXEL_TEXTURE_NORMAL.js +24 -3
  1023. package/src/shade/renderer/texture/util/WHITE_PIXEL_TEXTURE.d.ts +1 -1
  1024. package/src/shade/renderer/texture/util/WHITE_PIXEL_TEXTURE.d.ts.map +1 -1
  1025. package/src/shade/renderer/texture/util/get_single_pixel_texture.d.ts +0 -1
  1026. package/src/shade/renderer/texture/util/get_single_pixel_texture.d.ts.map +1 -1
  1027. package/src/shade/renderer/texture/util/get_single_pixel_texture.js +2 -23
  1028. package/src/shade/renderer/texture/util/single_pixel_texture_from_sampler.d.ts +11 -0
  1029. package/src/shade/renderer/texture/util/single_pixel_texture_from_sampler.d.ts.map +1 -0
  1030. package/src/shade/renderer/texture/util/single_pixel_texture_from_sampler.js +34 -0
  1031. package/src/shade/renderer/texture/virtual/VTPhysicalCache.d.ts +4 -90
  1032. package/src/shade/renderer/texture/virtual/VTPhysicalCache.d.ts.map +1 -1
  1033. package/src/shade/renderer/texture/virtual/VTPhysicalCache.js +6 -210
  1034. package/src/shade/renderer/texture/virtual/VTPhysicalSlots.d.ts +119 -0
  1035. package/src/shade/renderer/texture/virtual/VTPhysicalSlots.d.ts.map +1 -0
  1036. package/src/shade/renderer/texture/virtual/VTPhysicalSlots.js +245 -0
  1037. package/src/shade/renderer/texture/virtual/build_chunk_vt_lookup.d.ts +1 -1
  1038. package/src/shade/renderer/texture/virtual/build_chunk_vt_lookup.d.ts.map +1 -1
  1039. package/src/shade/renderer/texture/virtual/build_chunk_vt_lookup.js +3 -19
  1040. package/src/shade/renderer/texture/virtual/chunk_vt_page_table_entry.d.ts +13 -0
  1041. package/src/shade/renderer/texture/virtual/chunk_vt_page_table_entry.d.ts.map +1 -0
  1042. package/src/shade/renderer/texture/virtual/chunk_vt_page_table_entry.js +34 -0
  1043. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.d.ts +17 -8
  1044. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.d.ts.map +1 -1
  1045. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.js +35 -64
  1046. package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.d.ts +21 -0
  1047. package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.d.ts.map +1 -0
  1048. package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.js +70 -0
  1049. package/src/shade/renderer/volumetrics/chunk_participating_media_volumes_iterate.d.ts +31 -0
  1050. package/src/shade/renderer/volumetrics/chunk_participating_media_volumes_iterate.d.ts.map +1 -0
  1051. package/src/shade/renderer/volumetrics/chunk_participating_media_volumes_iterate.js +86 -0
  1052. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
  1053. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +2 -4
  1054. package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.d.ts.map +1 -1
  1055. package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.js +6 -7
  1056. package/src/shade/renderer/volumetrics/shader_volumetrics_build_scattering_lut.js +7 -7
  1057. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +20 -3
  1058. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  1059. package/src/shade/renderer/water/GPUWaterRenderer.js +84 -35
  1060. package/src/shade/renderer/water/chunk_water_gi.d.ts +14 -0
  1061. package/src/shade/renderer/water/chunk_water_gi.d.ts.map +1 -0
  1062. package/src/shade/renderer/water/chunk_water_gi.js +56 -0
  1063. package/src/shade/renderer/water/chunk_water_transport.d.ts +2 -0
  1064. package/src/shade/renderer/water/chunk_water_transport.d.ts.map +1 -1
  1065. package/src/shade/renderer/water/chunk_water_transport.js +52 -41
  1066. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +15 -0
  1067. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +1 -0
  1068. package/src/shade/renderer/water/medium/GPUWaterBodies.js +64 -0
  1069. package/src/shade/renderer/water/medium/WATER_BODIES_STRUCT.d.ts +4 -0
  1070. package/src/shade/renderer/water/medium/WATER_BODIES_STRUCT.d.ts.map +1 -0
  1071. package/src/shade/renderer/water/medium/WATER_BODIES_STRUCT.js +10 -0
  1072. package/src/shade/renderer/water/medium/chunk_water_bodies.d.ts +3 -0
  1073. package/src/shade/renderer/water/medium/chunk_water_bodies.d.ts.map +1 -0
  1074. package/src/shade/renderer/water/medium/chunk_water_bodies.js +3 -0
  1075. package/src/shade/renderer/water/medium/chunk_water_medium_derived.d.ts +9 -0
  1076. package/src/shade/renderer/water/medium/chunk_water_medium_derived.d.ts.map +1 -0
  1077. package/src/shade/renderer/water/medium/chunk_water_medium_derived.js +55 -0
  1078. package/src/shade/renderer/water/medium/chunk_water_medium_scattering.d.ts +8 -0
  1079. package/src/shade/renderer/water/medium/chunk_water_medium_scattering.d.ts.map +1 -0
  1080. package/src/shade/renderer/water/medium/chunk_water_medium_scattering.js +17 -0
  1081. package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts +4 -0
  1082. package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts.map +1 -0
  1083. package/src/shade/renderer/water/medium/shader_water_bodies_derive.js +32 -0
  1084. package/src/shade/renderer/water/medium/shader_water_meshlets.d.ts +4 -0
  1085. package/src/shade/renderer/water/medium/shader_water_meshlets.d.ts.map +1 -0
  1086. package/src/shade/renderer/water/medium/shader_water_meshlets.js +69 -0
  1087. package/src/shade/renderer/water/optics/chunk_water_inscatter.d.ts +28 -0
  1088. package/src/shade/renderer/water/optics/chunk_water_inscatter.d.ts.map +1 -0
  1089. package/src/shade/renderer/water/optics/chunk_water_inscatter.js +124 -0
  1090. package/src/shade/renderer/water/optics/chunk_water_optics.d.ts +12 -0
  1091. package/src/shade/renderer/water/optics/chunk_water_optics.d.ts.map +1 -0
  1092. package/src/shade/renderer/water/optics/chunk_water_optics.js +75 -0
  1093. package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.d.ts +27 -0
  1094. package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.d.ts.map +1 -0
  1095. package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.js +75 -0
  1096. package/src/shade/renderer/water/shader_water.d.ts +11 -5
  1097. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  1098. package/src/shade/renderer/water/shader_water.js +262 -156
  1099. package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
  1100. package/src/shade/renderer/water/shader_water_avboit.js +19 -9
  1101. package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts +25 -0
  1102. package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts.map +1 -0
  1103. package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.js +128 -0
  1104. package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.d.ts +6 -0
  1105. package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.d.ts.map +1 -0
  1106. package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.js +23 -0
  1107. package/src/shade/renderer/water/surface/chunk_water_tessellation.d.ts +9 -0
  1108. package/src/shade/renderer/water/surface/chunk_water_tessellation.d.ts.map +1 -0
  1109. package/src/shade/renderer/water/surface/chunk_water_tessellation.js +144 -0
  1110. package/src/shade/renderer/water/surface/chunk_water_wave.d.ts +13 -0
  1111. package/src/shade/renderer/water/surface/chunk_water_wave.d.ts.map +1 -0
  1112. package/src/shade/renderer/water/surface/chunk_water_wave.js +40 -0
  1113. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +14 -0
  1114. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts.map +1 -0
  1115. package/src/shade/renderer/water/surface/graph_water_tessellation.js +34 -0
  1116. package/src/shade/renderer/water/surface/shader_water_tiles.d.ts +4 -0
  1117. package/src/shade/renderer/water/surface/shader_water_tiles.d.ts.map +1 -0
  1118. package/src/shade/renderer/water/surface/shader_water_tiles.js +123 -0
  1119. package/src/shade/renderer/water/surface/shader_water_wave_batch.d.ts +6 -0
  1120. package/src/shade/renderer/water/surface/shader_water_wave_batch.d.ts.map +1 -0
  1121. package/src/shade/renderer/water/surface/shader_water_wave_batch.js +85 -0
  1122. package/src/shade/renderer/water/surface/shader_water_wave_domain.d.ts +6 -0
  1123. package/src/shade/renderer/water/surface/shader_water_wave_domain.d.ts.map +1 -0
  1124. package/src/shade/renderer/water/surface/shader_water_wave_domain.js +83 -0
  1125. package/src/shade/renderer/water/water_height_texture_from_sampler.d.ts.map +1 -1
  1126. package/src/shade/renderer/water/water_height_texture_from_sampler.js +4 -3
  1127. package/src/shade/renderer/water/water_missing_receiver_path.d.ts +17 -0
  1128. package/src/shade/renderer/water/water_missing_receiver_path.d.ts.map +1 -0
  1129. package/src/shade/renderer/water/water_missing_receiver_path.js +40 -0
  1130. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
  1131. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +247 -23
  1132. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts +18 -14
  1133. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  1134. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +48 -6
  1135. package/src/engine/graphics/ecs/water/WATER_SIZE.d.ts +0 -2
  1136. package/src/engine/graphics/ecs/water/WATER_SIZE.d.ts.map +0 -1
  1137. package/src/engine/graphics/ecs/water/WATER_SIZE.js +0 -1
  1138. package/src/shade/renderer/light/GPUViewLightContext.d.ts +0 -22
  1139. package/src/shade/renderer/light/GPUViewLightContext.d.ts.map +0 -1
  1140. package/src/shade/renderer/light/GPUViewLightContext.js +0 -62
  1141. package/src/shade/renderer/shadow/map/csm/CSM_CASTER_RANGE_STRUCT.d.ts +0 -29
  1142. package/src/shade/renderer/shadow/map/csm/CSM_CASTER_RANGE_STRUCT.d.ts.map +0 -1
  1143. package/src/shade/renderer/shadow/map/csm/CSM_CASTER_RANGE_STRUCT.js +0 -43
  1144. package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_CASCADE_COUNT.d.ts +0 -7
  1145. package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_CASCADE_COUNT.d.ts.map +0 -1
  1146. package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_CASCADE_COUNT.js +0 -6
  1147. package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.d.ts +0 -27
  1148. package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.d.ts.map +0 -1
  1149. package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.js +0 -125
  1150. package/src/shade/renderer/shadow/vsm/GPUVirtualShadowTextureResidence.d.ts +0 -7
  1151. package/src/shade/renderer/shadow/vsm/GPUVirtualShadowTextureResidence.d.ts.map +0 -1
  1152. package/src/shade/renderer/shadow/vsm/GPUVirtualShadowTextureResidence.js +0 -41
  1153. package/src/shade/renderer/shadow/vsm/VSM_RESIDENCE_WORKGROUP_TILE_RESOLUTION.d.ts +0 -2
  1154. package/src/shade/renderer/shadow/vsm/VSM_RESIDENCE_WORKGROUP_TILE_RESOLUTION.d.ts.map +0 -1
  1155. package/src/shade/renderer/shadow/vsm/VSM_RESIDENCE_WORKGROUP_TILE_RESOLUTION.js +0 -1
  1156. package/src/shade/renderer/shadow/vsm/VSM_UNIFORMS_STRUCT.d.ts +0 -3
  1157. package/src/shade/renderer/shadow/vsm/VSM_UNIFORMS_STRUCT.d.ts.map +0 -1
  1158. package/src/shade/renderer/shadow/vsm/VSM_UNIFORMS_STRUCT.js +0 -7
  1159. package/src/shade/renderer/shadow/vsm/VirtualShadowmapRenderer.d.ts +0 -24
  1160. package/src/shade/renderer/shadow/vsm/VirtualShadowmapRenderer.d.ts.map +0 -1
  1161. package/src/shade/renderer/shadow/vsm/VirtualShadowmapRenderer.js +0 -103
  1162. package/src/shade/renderer/shadow/vsm/debug_inspect_coarse_bounds_buffer.d.ts +0 -8
  1163. package/src/shade/renderer/shadow/vsm/debug_inspect_coarse_bounds_buffer.d.ts.map +0 -1
  1164. package/src/shade/renderer/shadow/vsm/debug_inspect_coarse_bounds_buffer.js +0 -34
  1165. package/src/shade/renderer/shadow/vsm/debug_inspect_residence_buffer.d.ts +0 -10
  1166. package/src/shade/renderer/shadow/vsm/debug_inspect_residence_buffer.d.ts.map +0 -1
  1167. package/src/shade/renderer/shadow/vsm/debug_inspect_residence_buffer.js +0 -64
  1168. package/src/shade/renderer/shadow/vsm/decode_tile_address.d.ts +0 -11
  1169. package/src/shade/renderer/shadow/vsm/decode_tile_address.d.ts.map +0 -1
  1170. package/src/shade/renderer/shadow/vsm/decode_tile_address.js +0 -34
  1171. package/src/shade/renderer/shadow/vsm/render/vms_render_tiles.d.ts +0 -2
  1172. package/src/shade/renderer/shadow/vsm/render/vms_render_tiles.d.ts.map +0 -1
  1173. package/src/shade/renderer/shadow/vsm/render/vms_render_tiles.js +0 -11
  1174. package/src/shade/renderer/shadow/vsm/vms_pipeline_compute_coarse_bounds.d.ts +0 -20
  1175. package/src/shade/renderer/shadow/vsm/vms_pipeline_compute_coarse_bounds.d.ts.map +0 -1
  1176. package/src/shade/renderer/shadow/vsm/vms_pipeline_compute_coarse_bounds.js +0 -187
  1177. package/src/shade/renderer/shadow/vsm/vsm_residence_pipeline.d.ts +0 -25
  1178. package/src/shade/renderer/shadow/vsm/vsm_residence_pipeline.d.ts.map +0 -1
  1179. package/src/shade/renderer/shadow/vsm/vsm_residence_pipeline.js +0 -220
  1180. package/src/shade/renderer/shadow/vsm/vsm_tile_allocation_pipeline.d.ts +0 -19
  1181. package/src/shade/renderer/shadow/vsm/vsm_tile_allocation_pipeline.d.ts.map +0 -1
  1182. package/src/shade/renderer/shadow/vsm/vsm_tile_allocation_pipeline.js +0 -115
  1183. package/src/shade/renderer/texture/encode/ENCODE_NOTES_2026_09_02.md +0 -462
@@ -1,39 +1,65 @@
1
- import { aabb3_transform_oriented } from "../../../core/geom/3d/aabb/aabb3_transform_oriented.js";
1
+ import { aabb3_transform_oriented_array } from "../../../core/geom/3d/aabb/aabb3_transform_oriented.js";
2
2
  import { Triangle3D } from "../../../core/geom/3d/shape/Triangle3D.js";
3
3
  import { body_id_index } from "../body/BodyStorage.js";
4
4
  import { ColliderFlags } from "../ecs/ColliderFlags.js";
5
5
  import { combine_friction, combine_restitution } from "../contact/combine_material.js";
6
- import { CONTACT_FLAG_SENSOR, CONTACT_STRIDE, MAX_CONTACTS_PER_MANIFOLD } from "../contact/ManifoldStore.js";
6
+ import {
7
+ CONTACT_FLAG_SENSOR,
8
+ CONTACT_FLAGS_OFFSET,
9
+ CONTACT_STRIDE,
10
+ MAX_CONTACTS_PER_MANIFOLD
11
+ } from "../contact/ManifoldStore.js";
7
12
  import { GJK_SEPARATED, gjk_epa_penetration } from "../../../core/geom/3d/gjk/gjk_epa_penetration.js";
8
- import { reduce_manifold_contacts } from "./reduce_manifold_contacts.js";
13
+ import { reduce_manifold_contacts_by_plane } from "./reduce_manifold_contacts.js";
9
14
  import { mpr } from "../../../core/geom/3d/gjk/mpr.js";
10
- import { box_box_manifold, BOX_BOX_OUT_LENGTH } from "./box_box_manifold.js";
11
- import { convex_hull_clip, CONVEX_CONVEX_OUT_LENGTH } from "./convex_convex_manifold.js";
15
+ import { box_box_manifold_array, BOX_BOX_OUT_LENGTH } from "./box_box_manifold.js";
16
+ import { convex_hull_clip_array, CONVEX_CONVEX_OUT_LENGTH } from "./convex_convex_manifold.js";
12
17
  import { get_mesh_convex_hull } from "./mesh_convex_hull.js";
13
18
  import { mesh_mesh_tet_contacts } from "./mesh_mesh_tet_manifold.js";
14
- import { box_triangle_contact, BOX_TRIANGLE_OUT_LENGTH } from "./box_triangle_contact.js";
19
+ import {
20
+ BOX_TRIANGLE_AXIS_BOX_FACE,
21
+ BOX_TRIANGLE_AXIS_OFFSET,
22
+ BOX_TRIANGLE_AXIS_TRIANGLE_FACE,
23
+ box_triangle_contact_array,
24
+ BOX_TRIANGLE_OUT_LENGTH
25
+ } from "./box_triangle_contact.js";
15
26
  import {
16
27
  CAPSULE_BOX_CONTACT_STRIDE,
17
28
  CAPSULE_BOX_MAX_CONTACTS,
18
- capsule_box_multi_contacts,
19
- capsule_capsule_contact,
29
+ capsule_box_multi_contacts_array,
20
30
  CAPSULE_CAPSULE_CONTACT_STRIDE,
21
- capsule_capsule_multi_contacts,
22
- capsule_sphere_contact,
31
+ capsule_capsule_multi_contacts_array,
32
+ capsule_sphere_contact_array,
23
33
  } from "./capsule_contacts.js";
24
34
  import {
25
- capsule_triangle_contact,
35
+ capsule_triangle_contact_array,
26
36
  CAPSULE_TRIANGLE_CONTACT_STRIDE,
27
37
  CAPSULE_TRIANGLE_MAX_CONTACTS,
28
38
  } from "./capsule_triangle_contact.js";
29
- import { aabb3_transform_oriented_inverse } from "../../../core/geom/3d/aabb/aabb3_transform_oriented_inverse.js";
30
- import { decompose_to_triangles } from "./decomposition/decompose_to_triangles.js";
39
+ import { aabb3_transform_oriented_inverse_array } from "../../../core/geom/3d/aabb/aabb3_transform_oriented_inverse.js";
40
+ import { decompose_to_triangles_array } from "./decomposition/decompose_to_triangles.js";
31
41
  import { TRIANGLE_FLOAT_STRIDE } from "./decomposition/triangle_buffer_layout.js";
42
+ import { triangle_edge_flags, triangle_vertex_flat } from "./decomposition/triangle_edge_flags.js";
43
+ import { FLAT_EDGE_COS, TriangleEdgeFlags } from "../../../core/geom/3d/triangle/TriangleEdgeFlags.js";
32
44
  import { PosedShape3D } from "../../../core/geom/3d/shape/PosedShape3D.js";
33
- import { sphere_box_contact } from "./sphere_box_contact.js";
34
- import { sphere_sphere_contact } from "./sphere_sphere_contact.js";
35
- import { sphere_triangle_contact } from "./sphere_triangle_contact.js";
36
- import { Transform64 } from "../../ecs/transform/Transform64.js";
45
+ import { hull_sphere_contact_array } from "./hull_sphere_contact.js";
46
+ import {
47
+ feature_manifold,
48
+ FEATURE_MANIFOLD_OUT_LENGTH,
49
+ FEATURE_POLYGON,
50
+ support_feature,
51
+ SupportFeature,
52
+ triangle_feature
53
+ } from "./support_feature_manifold.js";
54
+ import { sphere_box_contact_array } from "./sphere_box_contact.js";
55
+ import { sphere_sphere_contact_array } from "./sphere_sphere_contact.js";
56
+ import { sphere_triangle_contact_array } from "./sphere_triangle_contact.js";
57
+ import {
58
+ Transform64,
59
+ TRANSFORM64_ROTATION_OFFSET,
60
+ TRANSFORM64_TRANSLATION_OFFSET
61
+ } from "../../ecs/transform/Transform64.js";
62
+ import { rigid_subject_pose } from "./rigid_subject_pose.js";
37
63
 
38
64
  const posed_a = new PosedShape3D();
39
65
  const posed_b = new PosedShape3D();
@@ -52,11 +78,26 @@ const box_triangle_result = new Float64Array(BOX_TRIANGLE_OUT_LENGTH);
52
78
  const capsule_triangle_result = new Float64Array(CAPSULE_TRIANGLE_MAX_CONTACTS * CAPSULE_TRIANGLE_CONTACT_STRIDE);
53
79
  const box_manifold_result = new Float64Array(BOX_BOX_OUT_LENGTH);
54
80
  const convex_manifold_result = new Float64Array(CONVEX_CONVEX_OUT_LENGTH);
81
+
82
+ /**
83
+ * The two poses {@link convex_hull_clip} takes as arrays, copied out of the transforms: reading a
84
+ * Transform64's `translation` or `rotation` builds a fresh view on every read.
85
+ */
86
+ const hull_position_a = new Float64Array(3);
87
+ const hull_rotation_a = new Float64Array(4);
88
+ const hull_position_b = new Float64Array(3);
89
+ const hull_rotation_b = new Float64Array(4);
90
+
55
91
  const capsule_box_multi_result = new Float64Array(CAPSULE_BOX_MAX_CONTACTS * CAPSULE_BOX_CONTACT_STRIDE);
56
92
 
93
+ /** Supporting features of the two sides, and their manifold, for the GJK + EPA paths. */
94
+ const feature_a = new SupportFeature();
95
+ const feature_b = new SupportFeature();
96
+ const feature_manifold_result = new Float64Array(FEATURE_MANIFOLD_OUT_LENGTH);
97
+
57
98
  /**
58
99
  * Candidate-contact stride: wax, way, waz, wbx, wby, wbz, nx, ny, nz, depth,
59
- * feature_id, friction, restitution.
100
+ * feature_id, friction, restitution, flags.
60
101
  *
61
102
  * The `feature_id` (offset 10) is a stable cross-frame identifier of the
62
103
  * geometric feature pair that produced this contact — used by the
@@ -71,6 +112,9 @@ const capsule_box_multi_result = new Float64Array(CAPSULE_BOX_MAX_CONTACTS * CAP
71
112
  * on each side) and carried into the manifold so a compound body's per-collider
72
113
  * materials are honoured per-contact.
73
114
  *
115
+ * `flags` (offset 13) are that same collider pair's per-contact bits — see
116
+ * {@link CONTACT_FLAG_SENSOR}.
117
+ *
74
118
  * @type {number}
75
119
  */
76
120
  const CANDIDATE_STRIDE = 14;
@@ -191,6 +235,9 @@ const CANDIDATE_CAPACITY_INITIAL = 64;
191
235
  */
192
236
  let redetect_claimed = new Uint8Array(CANDIDATE_CAPACITY_INITIAL);
193
237
 
238
+ /** Data offsets of the contacts a re-detection found no candidate for (see {@link redetect_pair_geometry}). */
239
+ const redetect_stale = new Int32Array(64);
240
+
194
241
  /**
195
242
  * Per body-pair scratch buffer for candidate contacts produced by the
196
243
  * cross-product of A's colliders × B's colliders.
@@ -230,36 +277,1110 @@ function grow_candidates() {
230
277
  redetect_claimed = new Uint8Array(capacity);
231
278
  }
232
279
 
280
+ /*
281
+ * ## Concave-dispatch contact dedup index
282
+ *
283
+ * Adjacent triangles report the same contact where they share an edge or a
284
+ * vertex, so each per-triangle path drops a new contact when an earlier one of
285
+ * the same dispatch matches it: the sphere path when a normal lies within
286
+ * 0.001 (squared) of the new one, the box / capsule / feature paths when a
287
+ * normal does AND the stored A-side witness lies within 1e-6 (squared) of the
288
+ * new one. That was a scan over every earlier contact of the dispatch, per
289
+ * contact: quadratic in contacts, and with the triangle cap gone a 4 m crate
290
+ * on a floor meshed at 10 cm paid ~100 ms a step for it.
291
+ *
292
+ * The test is unchanged; what changes is which earlier contacts it is run
293
+ * against. Each kept contact is filed under a grid cell of its key — the
294
+ * witness for the point paths, the normal for the sphere path — and a new
295
+ * contact is tested, with the very same arithmetic, against the contacts filed
296
+ * in the cells its tolerance can reach. Whether a match exists does not depend
297
+ * on the order the earlier contacts are tested in, so the contacts kept, and
298
+ * their order, are exactly what the scan kept.
299
+ *
300
+ * Reach: a squared distance that rounds below 1e-6 has every component under
301
+ * 1e-3 (1 + 3 ulp); cells of 1/256 put that at 0.256 of a cell, so only the
302
+ * neighbour across a face nearer than {@link DEDUP_REACH} can hold a match.
303
+ * Normals: under sqrt(0.001) = 0.0316 per component, cells of 1/8, 0.253 of a
304
+ * cell. The scales are powers of two, so `key * scale` and its fraction are
305
+ * exact. A component that scales to a non-finite value files and looks up in
306
+ * cell 0 on that axis alike, so equal keys still meet (a non-finite key never
307
+ * passes the test anyway).
308
+ *
309
+ * Buckets are reset by stamp, entries by count, so a dispatch costs nothing
310
+ * per bucket; both grow with the high-water mark and are kept.
311
+ */
312
+
313
+ /** Cell size 1/256 m for witness points (tolerance 1 mm). */
314
+ const DEDUP_POINT_SCALE = 256;
315
+
316
+ /** Cell size 1/8 for unit normals (tolerance 0.0316). */
317
+ const DEDUP_NORMAL_SCALE = 8;
318
+
319
+ /** Either tolerance in cell units (0.256, 0.253), rounded up. */
320
+ const DEDUP_REACH = 0.26;
321
+
322
+ /** @type {Int32Array} first entry of each bucket, valid when its stamp is current */
323
+ let dedup_bucket_head = new Int32Array(1024);
324
+ /** @type {Int32Array} */
325
+ let dedup_bucket_stamp = new Int32Array(1024);
326
+ /** @type {Int32Array} candidate index of each entry */
327
+ let dedup_entry_candidate = new Int32Array(1024);
328
+ /** @type {Int32Array} next entry in the same bucket, -1 ends */
329
+ let dedup_entry_next = new Int32Array(1024);
330
+ let dedup_entry_count = 0;
331
+ let dedup_stamp = 0;
332
+
333
+ /**
334
+ * Key of the dispatch in progress: the normal (sphere path) or the A-side
335
+ * witness (every other path). One dispatch takes one path.
336
+ * @type {boolean}
337
+ */
338
+ let dedup_by_normal = false;
339
+
340
+ /**
341
+ * Start an empty index for a concave dispatch.
342
+ * @param {boolean} by_normal
343
+ */
344
+ function dedup_begin(by_normal) {
345
+ dedup_by_normal = by_normal;
346
+ dedup_entry_count = 0;
347
+ dedup_stamp++;
348
+
349
+ if (dedup_stamp === 0x7FFFFFFF) {
350
+ dedup_bucket_stamp.fill(0);
351
+ dedup_stamp = 1;
352
+ }
353
+ }
354
+
355
+ /**
356
+ * @param {number} cx integer-valued cell coordinate (any double; non-finite folds to 0)
357
+ * @param {number} cy
358
+ * @param {number} cz
359
+ * @returns {number} bucket index
360
+ */
361
+ function dedup_bucket(cx, cy, cz) {
362
+ return (Math.imul(cx | 0, 73856093) ^ Math.imul(cy | 0, 19349663) ^ Math.imul(cz | 0, 83492791))
363
+ & (dedup_bucket_head.length - 1);
364
+ }
365
+
366
+ /**
367
+ * File entry `e` (already holding its candidate index) under its key's cell.
368
+ * @param {number} e
369
+ */
370
+ function dedup_file(e) {
371
+ const scale = dedup_by_normal ? DEDUP_NORMAL_SCALE : DEDUP_POINT_SCALE;
372
+ const o = dedup_entry_candidate[e] * CANDIDATE_STRIDE + (dedup_by_normal ? 6 : 0);
373
+
374
+ const b = dedup_bucket(
375
+ Math.floor(candidates[o] * scale),
376
+ Math.floor(candidates[o + 1] * scale),
377
+ Math.floor(candidates[o + 2] * scale)
378
+ );
379
+
380
+ if (dedup_bucket_stamp[b] !== dedup_stamp) {
381
+ dedup_bucket_stamp[b] = dedup_stamp;
382
+ dedup_bucket_head[b] = -1;
383
+ }
384
+
385
+ dedup_entry_next[e] = dedup_bucket_head[b];
386
+ dedup_bucket_head[b] = e;
387
+ }
388
+
389
+ /**
390
+ * Index candidate `index`, just appended by the dispatch in progress.
391
+ * @param {number} index
392
+ */
393
+ function dedup_insert(index) {
394
+ const e = dedup_entry_count;
395
+
396
+ if (e === dedup_entry_candidate.length) {
397
+ const candidate = new Int32Array(e * 2);
398
+ candidate.set(dedup_entry_candidate);
399
+ dedup_entry_candidate = candidate;
400
+ dedup_entry_next = new Int32Array(e * 2);
401
+
402
+ // re-chain below, with the buckets
403
+ dedup_entry_count = e;
404
+ dedup_rebuild(dedup_bucket_head.length);
405
+ }
406
+
407
+ dedup_entry_candidate[e] = index;
408
+ dedup_entry_count = e + 1;
409
+
410
+ if (dedup_entry_count * 2 > dedup_bucket_head.length) {
411
+ dedup_rebuild(dedup_bucket_head.length * 2);
412
+ } else {
413
+ dedup_file(e);
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Re-file every entry into `bucket_count` buckets.
419
+ * @param {number} bucket_count power of two
420
+ */
421
+ function dedup_rebuild(bucket_count) {
422
+ if (bucket_count !== dedup_bucket_head.length) {
423
+ dedup_bucket_head = new Int32Array(bucket_count);
424
+ dedup_bucket_stamp = new Int32Array(bucket_count);
425
+ }
426
+
427
+ // a fresh stamp empties every bucket
428
+ dedup_stamp++;
429
+
430
+ if (dedup_stamp === 0x7FFFFFFF) {
431
+ dedup_bucket_stamp.fill(0);
432
+ dedup_stamp = 1;
433
+ }
434
+
435
+ for (let e = 0; e < dedup_entry_count; e++) {
436
+ dedup_file(e);
437
+ }
438
+ }
439
+
233
440
  /**
234
- * Maximum triangles a concave-side enumerator can produce per pair.
235
- * The query AABB is bounded by the broadphase's fattened envelope of
236
- * the convex-side body, so a single concave-vs-convex pair typically
237
- * yields tens of triangles, not thousands. 1024 is the safety cap.
441
+ * Whether an earlier contact of the dispatch in progress matches a new one —
442
+ * the per-path test, run against the contacts filed where it can pass.
443
+ *
444
+ * The new contact is read from a record laid out as {@link append_contact}
445
+ * takes its numbers: A-side witness at `offset` (ignored when keyed by
446
+ * normal), stored normal at `offset + 6`. See {@link staged_contact}.
447
+ *
448
+ * @param {Float64Array} contact
449
+ * @param {number} offset
450
+ * @returns {boolean}
451
+ */
452
+ function dedup_contains(contact, offset) {
453
+ const px = contact[offset], py = contact[offset + 1], pz = contact[offset + 2];
454
+ const nx = contact[offset + 6], ny = contact[offset + 7], nz = contact[offset + 8];
455
+
456
+ const by_normal = dedup_by_normal;
457
+ const scale = by_normal ? DEDUP_NORMAL_SCALE : DEDUP_POINT_SCALE;
458
+
459
+ const sx = (by_normal ? nx : px) * scale;
460
+ const sy = (by_normal ? ny : py) * scale;
461
+ const sz = (by_normal ? nz : pz) * scale;
462
+
463
+ const cx = Math.floor(sx), cy = Math.floor(sy), cz = Math.floor(sz);
464
+ const fx = sx - cx, fy = sy - cy, fz = sz - cz;
465
+
466
+ // cells per axis: the key's own, plus a neighbour whose face is in reach
467
+ const x0 = fx < DEDUP_REACH ? cx - 1 : cx;
468
+ const y0 = fy < DEDUP_REACH ? cy - 1 : cy;
469
+ const z0 = fz < DEDUP_REACH ? cz - 1 : cz;
470
+ const nx_cells = 1 + (fx < DEDUP_REACH ? 1 : 0) + (fx > 1 - DEDUP_REACH ? 1 : 0);
471
+ const ny_cells = 1 + (fy < DEDUP_REACH ? 1 : 0) + (fy > 1 - DEDUP_REACH ? 1 : 0);
472
+ const nz_cells = 1 + (fz < DEDUP_REACH ? 1 : 0) + (fz > 1 - DEDUP_REACH ? 1 : 0);
473
+
474
+ for (let ix = 0; ix < nx_cells; ix++) {
475
+ for (let iy = 0; iy < ny_cells; iy++) {
476
+ for (let iz = 0; iz < nz_cells; iz++) {
477
+ const b = dedup_bucket(x0 + ix, y0 + iy, z0 + iz);
478
+
479
+ if (dedup_bucket_stamp[b] !== dedup_stamp) continue;
480
+
481
+ for (let e = dedup_bucket_head[b]; e !== -1; e = dedup_entry_next[e]) {
482
+ const qo = dedup_entry_candidate[e] * CANDIDATE_STRIDE;
483
+
484
+ const dnx = candidates[qo + 6] - nx;
485
+ const dny = candidates[qo + 7] - ny;
486
+ const dnz = candidates[qo + 8] - nz;
487
+
488
+ if (dnx * dnx + dny * dny + dnz * dnz < 0.001) {
489
+ if (by_normal) return true;
490
+
491
+ const dpx = candidates[qo] - px;
492
+ const dpy = candidates[qo + 1] - py;
493
+ const dpz = candidates[qo + 2] - pz;
494
+
495
+ if (dpx * dpx + dpy * dpy + dpz * dpz < 1e-6) return true;
496
+ }
497
+ }
498
+ }
499
+ }
500
+ }
501
+
502
+ return false;
503
+ }
504
+
505
+ /*
506
+ * ## Internal edges ("ghost" contacts)
238
507
  *
239
- * For a heightmap the per-cell triangle count scales O(N²) with the
240
- * shape's {@link HeightMapShape3D#tessellation} (a sub-cell quad is 2
241
- * triangles, and there are N×N sub-cells per sampler cell). The bounded
242
- * query AABB keeps the cell count small, so a typical pair stays well
243
- * inside 1024 at moderate tessellation; the silent-drop backstop below
244
- * covers any overflow at extreme values.
508
+ * A flat floor cut into triangles is collided one triangle at a time, and each triangle on its own has
509
+ * edges the floor does not. A body sliding from one triangle onto the next first touches the next
510
+ * one's near edge — a sliver of overlap the per-triangle routine resolves along whatever axis is
511
+ * shortest for that triangle alone: the box's leading face (pushing it back), or an edge-edge axis
512
+ * tilted off the floor's normal (kicking it up and spinning it). A sphere crossing the same edge gets
513
+ * a second contact on it, beside the face contact of the triangle it is over, tilted and as deep. A
514
+ * frictionless box crossing a flat triangulated floor at 6 m/s hopped 0.1 m and lost a third of its
515
+ * speed; a sphere hopped by centimetres; both crossed a box floor without either.
245
516
  *
246
- * Enumerators return the TRUE overlap count and have no bounds check of
247
- * their own: writes past the scratch buffer are dropped (typed-array OOB
248
- * semantics), so on overflow the consume loop MUST clamp the returned
249
- * count to this cap or it reads `undefined` past the end and poisons the
250
- * manifold with NaN. The worst case after clamping is a missed contact
251
- * on a far edge of the filtered region, which the next-step
252
- * rebroadphase corrects.
517
+ * Box3D's answer (mesh_contact.c, `b3ComputeMeshManifolds`, MIT, Erin Catto), ported here: each
518
+ * triangle's contacts are FACE contacts — on the triangle's face, or a face of the convex body lying
519
+ * within 60° of the triangle's plane — or TENTATIVE: on an edge or a vertex of the triangle. Face
520
+ * contacts are kept as they are. (Box3D counts a deep contact on a body face as a face contact at any
521
+ * angle, so that it is never dropped; here no contact is dropped for lying on a flat feature, and the
522
+ * rule only kept a body face's normal where the plane's belonged: a box landing tilted on a small
523
+ * triangle, its back face 12 mm into it, was pushed sideways by its own face and stopped dead on a flat
524
+ * floor.) Tentative ones:
253
525
  *
526
+ * - a sphere's are kept, closest first, when the feature they touch (an edge or a vertex) is not yet
527
+ * claimed — by a face contact's triangle, or by a nearer tentative one's (each claims its edges and
528
+ * vertices whether or not it was kept) — and dropped otherwise, as Box3D does. A sphere's face
529
+ * contact is the nearest point of the surface, so a contact on an edge or vertex of that face's
530
+ * triangle can only repeat it, further off and tilted. Claims go by coordinates, though, and a
531
+ * neighbour can have the edge under other ones — its copy of a vertex a hair away, or the edge
532
+ * split at a T-junction — so a kept contact on a FLAT edge or corner is taken as the plane's,
533
+ * like everything else's below;
534
+ * - anything else's are judged by where on its triangle the contact lies ({@link
535
+ * triangle_witness_feature}). Inside the triangle, or on an edge or a corner that is {@link
536
+ * TriangleEdgeFlags FLAT} — no feature of the surface, an artefact of its triangulation — the
537
+ * contact is taken as the plane's: its normal becomes the triangle's, its depth the body's depth
538
+ * below that plane (dropped if not below it). On a real edge or corner — convex, concave, the rim,
539
+ * a vertex any real edge meets — it is kept as it is: the surface has that feature. A round body
540
+ * (sphere, capsule) touches a plane under its core. Where that point lies off the triangle and the
541
+ * contact meets the feature from more than a flat edge's bend off the plane's normal, the body is
542
+ * not on this face — a capsule riding over a 1 cm step touched the riser's internal diagonal from
543
+ * above, and taken as the riser's plane that contact pushed it back by its whole 8 cm intrusion into
544
+ * that plane, turning it round — and the contact goes. Otherwise it is the plane's: on the top of a
545
+ * convex crease flatter than that the point lies off both triangles, and neither holds the body.
546
+ *
547
+ * Box3D judges the triangle instead: a tentative contact goes when every edge of its triangle is
548
+ * flat, or one of them is flat and shared with a triangle that made a face contact — wherever on
549
+ * the triangle the contact lies. The triangle beside a ridge whose one corner is on the crest has
550
+ * every edge flat, so its contact at that corner, on the crest, went too: a box resting on two
551
+ * knife-edged rails got the flanks' normals there, 72° off vertical, and slid off sideways, and a
552
+ * lying cylinder driven into a 0.3 m riser spun up to 3.2 rad/s. And Box3D drops these contacts,
553
+ * where this takes them as the plane's: its triangle-hull SAT prefers a face axis, falling back to
554
+ * an edge pair only when face clipping fails, so its tentative contacts are the odd few; this
555
+ * box-triangle SAT takes whichever axis overlaps least, so a box corner pressed onto a vertex of a
556
+ * finely cut convex surface gets all its deep contacts on edge axes — and dropping them sank a cube
557
+ * balanced on a corner of a dome 166 mm where it used to sink 2 mm.
558
+ *
559
+ * Features are recognised by their coordinates in the shape's frame, which a vertex shared by two
560
+ * triangles has bit for bit in both (see triangle_buffer_layout.js). Edge and corner classes are
561
+ * looked up ({@link triangle_edge_flags}, {@link triangle_vertex_flat}) for the triangles of tentative
562
+ * contacts only: a mesh keeps them, a heightmap samples them. Classifying every enumerated heightmap
563
+ * triangle instead — a ring of extra samples around each query — cost a heightmap terrain scene a
564
+ * fifth of its step.
565
+ */
566
+
567
+ /**
568
+ * A contact whose normal is within this cosine of its triangle's face normal is on the face. The
569
+ * closest-point paths (sphere, capsule) leave the normal exactly the face's when the nearest point is
570
+ * inside the triangle, so this only has to absorb rounding.
571
+ * @type {number}
572
+ */
573
+ const FACE_CONTACT_COS = 1 - 1e-6;
574
+
575
+ /**
576
+ * A face of the convex body clipping a triangle makes a face contact when within this cosine of the
577
+ * triangle's normal: Box3D's 0.5 (60°) for a hull face.
254
578
  * @type {number}
255
579
  */
256
- const MAX_TRIANGLES_PER_PAIR = 1024;
580
+ const HULL_FACE_COS = 0.5;
581
+
582
+
583
+ /**
584
+ * Feature of a triangle a tentative contact touches: edges A→B, B→C, C→A, then vertices A, B, C, then
585
+ * the face; NONE for a triangle with no area to place a point in.
586
+ */
587
+ const FEATURE_EDGE_AB = 0;
588
+ const FEATURE_VERTEX_A = 3;
589
+ const FEATURE_FACE = 6;
590
+ const FEATURE_NONE = 7;
591
+
592
+ /**
593
+ * A point is on an edge of a triangle when its barycentric weight of the vertex across from that edge
594
+ * is at most this: a micrometre from the edge of a triangle a metre across. The contact routines
595
+ * place their points on an edge by interpolating along it, which is exact to rounding, many orders
596
+ * below this; a point really inside the triangle, as near the edge as this, is on the plane either way.
597
+ * @type {number}
598
+ */
599
+ const ON_EDGE_WEIGHT = 1e-6;
600
+
601
+ /**
602
+ * Tentative contacts of the concave dispatch in progress. Stride: the eleven values
603
+ * {@link append_contact} takes (A witness, B witness, stored normal, depth, fid), the index of the
604
+ * triangle in {@link triangle_buffer}, its feature on that triangle, and the sort key (squared distance
605
+ * to the sphere's centre). Grown by doubling, high-water mark kept.
606
+ */
607
+ const TENTATIVE_STRIDE = 14;
608
+ let tentatives = new Float64Array(64 * TENTATIVE_STRIDE);
609
+ let tentative_count = 0;
610
+
611
+ /** Processing order of the tentatives (closest first, for the sphere rule). */
612
+ let tentative_order = new Int32Array(64);
613
+
614
+ /**
615
+ * Indices in {@link triangle_buffer} of the triangles with a face contact in the sphere dispatch in
616
+ * progress (the sphere rule's claims).
617
+ */
618
+ let face_triangles = new Int32Array(64);
619
+ let face_triangle_count = 0;
620
+
621
+ /**
622
+ * A contact the concave dispatch is about to keep, stash or test, in the order {@link append_contact}
623
+ * takes its numbers — A witness (0..2), B witness (3..5), stored normal (6..8), depth (9), fid (10) —
624
+ * then a tentative contact's sort key (11). The per-contact helpers below take it as an array: none of
625
+ * them is inlined into dispatch_pair, and a call that is not inlined boxes each number passed to it.
626
+ */
627
+ const staged_contact = new Float64Array(12);
257
628
 
258
629
  /**
259
- * Per-pair scratch for the concave-side triangle decomposition.
630
+ * Stash a tentative contact, already oriented as {@link append_contact} wants it: the eleven values
631
+ * and the sort key of `contact` (see {@link staged_contact}).
632
+ *
633
+ * @param {Float64Array} contact
634
+ * @param {number} triangle
635
+ * @param {number} feature
636
+ */
637
+ function tentative_add(contact, triangle, feature) {
638
+ if ((tentative_count + 1) * TENTATIVE_STRIDE > tentatives.length) {
639
+ const grown = new Float64Array(tentatives.length * 2);
640
+ grown.set(tentatives);
641
+ tentatives = grown;
642
+ tentative_order = new Int32Array(grown.length / TENTATIVE_STRIDE);
643
+ }
644
+
645
+ const o = tentative_count * TENTATIVE_STRIDE;
646
+
647
+ tentatives[o] = contact[0]; tentatives[o + 1] = contact[1]; tentatives[o + 2] = contact[2];
648
+ tentatives[o + 3] = contact[3]; tentatives[o + 4] = contact[4]; tentatives[o + 5] = contact[5];
649
+ tentatives[o + 6] = contact[6]; tentatives[o + 7] = contact[7]; tentatives[o + 8] = contact[8];
650
+ tentatives[o + 9] = contact[9];
651
+ tentatives[o + 10] = contact[10];
652
+ tentatives[o + 11] = triangle;
653
+ tentatives[o + 12] = feature;
654
+ tentatives[o + 13] = contact[11];
655
+
656
+ tentative_count++;
657
+ }
658
+
659
+ /**
660
+ * Note that triangle `i` (index in {@link triangle_buffer}) made a face contact. Its contacts arrive
661
+ * together, so a repeat is the last entry.
662
+ * @param {number} i
663
+ */
664
+ function face_triangle_add(i) {
665
+ if (face_triangle_count > 0 && face_triangles[face_triangle_count - 1] === i) {
666
+ return;
667
+ }
668
+
669
+ if (face_triangle_count === face_triangles.length) {
670
+ const grown = new Int32Array(face_triangle_count * 2);
671
+ grown.set(face_triangles);
672
+ face_triangles = grown;
673
+ }
674
+
675
+ face_triangles[face_triangle_count++] = i;
676
+ }
677
+
678
+ /**
679
+ * Which feature of a triangle faces a direction: the vertices tied at its extreme along it — an edge
680
+ * for two, a vertex for one, the face for three. For a closest-point contact, the direction from the
681
+ * triangle to the body, this is the region the nearest point lies in.
682
+ *
683
+ * The triangle (world) and the direction are read from arrays, as the concave dispatch has them.
684
+ *
685
+ * @param {Float64Array} triangle vertices A, B, C
686
+ * @param {Float64Array} direction
687
+ * @param {number} direction_offset
688
+ * @returns {number} {@link FEATURE_EDGE_AB} + k for edge k, {@link FEATURE_VERTEX_A} + k for vertex k,
689
+ * {@link FEATURE_FACE}
690
+ */
691
+ function triangle_facing_feature(triangle, direction, direction_offset) {
692
+ const ax = triangle[0], ay = triangle[1], az = triangle[2];
693
+ const bx = triangle[3], by = triangle[4], bz = triangle[5];
694
+ const cx = triangle[6], cy = triangle[7], cz = triangle[8];
695
+ const dx = direction[direction_offset], dy = direction[direction_offset + 1], dz = direction[direction_offset + 2];
696
+
697
+ const da = ax * dx + ay * dy + az * dz;
698
+ const db = bx * dx + by * dy + bz * dz;
699
+ const dc = cx * dx + cy * dy + cz * dz;
700
+
701
+ const hi = Math.max(da, db, dc);
702
+ const floor = hi - (hi - Math.min(da, db, dc)) * 1e-6;
703
+
704
+ const ta = da >= floor, tb = db >= floor, tc = dc >= floor;
705
+
706
+ if (ta && tb && tc) return FEATURE_FACE;
707
+ if (ta && tb) return FEATURE_EDGE_AB;
708
+ if (tb && tc) return FEATURE_EDGE_AB + 1;
709
+ if (tc && ta) return FEATURE_EDGE_AB + 2;
710
+ if (ta) return FEATURE_VERTEX_A;
711
+ if (tb) return FEATURE_VERTEX_A + 1;
712
+ return FEATURE_VERTEX_A + 2;
713
+ }
714
+
715
+ /**
716
+ * Which feature of a triangle a point on it lies on: an edge when within {@link ON_EDGE_WEIGHT} of it
717
+ * (or past it), the vertex two such edges meet, else the face. The point is taken where it projects
718
+ * onto the triangle's plane.
719
+ *
720
+ * The triangle (world) and the point are read from arrays, as the concave dispatch has them.
721
+ *
722
+ * @param {Float64Array} triangle vertices A, B, C
723
+ * @param {Float64Array} point
724
+ * @param {number} point_offset
725
+ * @returns {number} {@link FEATURE_EDGE_AB} + k for edge k, {@link FEATURE_VERTEX_A} + k for vertex k,
726
+ * {@link FEATURE_FACE}, or {@link FEATURE_NONE} for a triangle without area
727
+ */
728
+ function triangle_witness_feature(triangle, point, point_offset) {
729
+ const ax = triangle[0], ay = triangle[1], az = triangle[2];
730
+ const bx = triangle[3], by = triangle[4], bz = triangle[5];
731
+ const cx = triangle[6], cy = triangle[7], cz = triangle[8];
732
+ const px = point[point_offset], py = point[point_offset + 1], pz = point[point_offset + 2];
733
+
734
+ const e1x = bx - ax, e1y = by - ay, e1z = bz - az;
735
+ const e2x = cx - ax, e2y = cy - ay, e2z = cz - az;
736
+
737
+ const nx = e1y * e2z - e1z * e2y;
738
+ const ny = e1z * e2x - e1x * e2z;
739
+ const nz = e1x * e2y - e1y * e2x;
740
+
741
+ const nn = nx * nx + ny * ny + nz * nz;
742
+
743
+ if (!(nn > 0)) {
744
+ return FEATURE_NONE;
745
+ }
746
+
747
+ // weights of A and B: the signed areas of (P, B, C) and (P, C, A) over the triangle's
748
+ const pax = ax - px, pay = ay - py, paz = az - pz;
749
+ const pbx = bx - px, pby = by - py, pbz = bz - pz;
750
+ const pcx = cx - px, pcy = cy - py, pcz = cz - pz;
751
+
752
+ const wa = ((pby * pcz - pbz * pcy) * nx + (pbz * pcx - pbx * pcz) * ny + (pbx * pcy - pby * pcx) * nz) / nn;
753
+ const wb = ((pcy * paz - pcz * pay) * nx + (pcz * pax - pcx * paz) * ny + (pcx * pay - pcy * pax) * nz) / nn;
754
+ const wc = 1 - wa - wb;
755
+
756
+ // on edge k: the weight of the vertex across from it, k + 2, vanishes
757
+ const on_ab = wc <= ON_EDGE_WEIGHT;
758
+ const on_bc = wa <= ON_EDGE_WEIGHT;
759
+ const on_ca = wb <= ON_EDGE_WEIGHT;
760
+
761
+ if (on_ab) {
762
+ if (on_bc) return on_ca ? FEATURE_NONE : FEATURE_VERTEX_A + 1;
763
+ return on_ca ? FEATURE_VERTEX_A : FEATURE_EDGE_AB;
764
+ }
765
+
766
+ if (on_bc) {
767
+ return on_ca ? FEATURE_VERTEX_A + 2 : FEATURE_EDGE_AB + 1;
768
+ }
769
+
770
+ return on_ca ? FEATURE_EDGE_AB + 2 : FEATURE_FACE;
771
+ }
772
+
773
+ /** Bits of a float, for hashing coordinates. */
774
+ const key_float = new Float64Array(1);
775
+ const key_words = new Uint32Array(key_float.buffer);
776
+
777
+ /**
778
+ * A set of points (width 3) or point pairs (width 6) keyed by their exact coordinates — the vertices
779
+ * and edges a concave dispatch has claimed. Open addressing; emptied by stamp, grown by doubling, the
780
+ * high-water mark kept.
781
+ */
782
+ class CoordinateKeySet {
783
+ /**
784
+ * @param {number} width floats per key
785
+ */
786
+ constructor(width) {
787
+ this.width = width;
788
+ this.keys = new Float64Array(64 * width);
789
+ this.stamps = new Int32Array(64);
790
+ this.stamp = 1;
791
+ this.size = 0;
792
+
793
+ /** Rehash scratch: a caller's key must survive the growth its add triggers. */
794
+ this.rehash_key = new Float64Array(width);
795
+ }
796
+
797
+ clear() {
798
+ this.size = 0;
799
+ this.stamp++;
800
+
801
+ if (this.stamp === 0x7FFFFFFF) {
802
+ this.stamps.fill(0);
803
+ this.stamp = 1;
804
+ }
805
+ }
806
+
807
+ /**
808
+ * @param {Float64Array} key `width` floats
809
+ * @returns {number}
810
+ */
811
+ hash(key) {
812
+ let h = 0;
813
+
814
+ for (let i = 0; i < this.width; i++) {
815
+ // + 0 folds -0 into 0: equal coordinates must hash equal
816
+ key_float[0] = key[i] + 0;
817
+ h = Math.imul(h ^ key_words[0], 0x85EBCA6B) ^ Math.imul(key_words[1], 0xC2B2AE35);
818
+ h ^= h >>> 15;
819
+ }
820
+
821
+ return h;
822
+ }
823
+
824
+ /**
825
+ * Add a key.
826
+ * @param {Float64Array} key `width` floats
827
+ * @returns {boolean} whether it was not there before
828
+ */
829
+ add(key) {
830
+ if ((this.size + 1) * 2 > this.stamps.length) {
831
+ this.grow();
832
+ }
833
+
834
+ const width = this.width;
835
+ const mask = this.stamps.length - 1;
836
+
837
+ for (let slot = this.hash(key) & mask; ; slot = (slot + 1) & mask) {
838
+ if (this.stamps[slot] !== this.stamp) {
839
+ this.stamps[slot] = this.stamp;
840
+ this.keys.set(key, slot * width);
841
+ this.size++;
842
+ return true;
843
+ }
844
+
845
+ if (this.equals(slot, key)) {
846
+ return false;
847
+ }
848
+ }
849
+ }
850
+
851
+ /**
852
+ * @param {Float64Array} key `width` floats
853
+ * @returns {boolean}
854
+ */
855
+ has(key) {
856
+ const mask = this.stamps.length - 1;
857
+
858
+ for (let slot = this.hash(key) & mask; ; slot = (slot + 1) & mask) {
859
+ if (this.stamps[slot] !== this.stamp) {
860
+ return false;
861
+ }
862
+
863
+ if (this.equals(slot, key)) {
864
+ return true;
865
+ }
866
+ }
867
+ }
868
+
869
+ equals(slot, key) {
870
+ const width = this.width;
871
+ const o = slot * width;
872
+
873
+ for (let i = 0; i < width; i++) {
874
+ if (this.keys[o + i] !== key[i]) {
875
+ return false;
876
+ }
877
+ }
878
+
879
+ return true;
880
+ }
881
+
882
+ grow() {
883
+ const width = this.width;
884
+ const old_keys = this.keys;
885
+ const old_stamps = this.stamps;
886
+ const old_stamp = this.stamp;
887
+ const capacity = old_stamps.length * 2;
888
+
889
+ this.keys = new Float64Array(capacity * width);
890
+ this.stamps = new Int32Array(capacity);
891
+ this.stamp = 1;
892
+ this.size = 0;
893
+
894
+ const key = this.rehash_key;
895
+
896
+ for (let slot = 0; slot < old_stamps.length; slot++) {
897
+ if (old_stamps[slot] !== old_stamp) continue;
898
+
899
+ for (let i = 0; i < width; i++) key[i] = old_keys[slot * width + i];
900
+ this.add(key);
901
+ }
902
+ }
903
+ }
904
+
905
+ /** Key scratch: a vertex, and an edge with its end points in lexicographic order. */
906
+ const key_vertex = new Float64Array(3);
907
+ const key_edge = new Float64Array(6);
908
+
909
+ /** Vertices and edges claimed in the sphere dispatch in progress (the sphere rule). */
910
+ const claimed_vertices = new CoordinateKeySet(3);
911
+ const claimed_edges = new CoordinateKeySet(6);
912
+
913
+ /**
914
+ * Write vertex `k` of the triangle at `o` in {@link triangle_buffer} into {@link key_vertex}.
915
+ */
916
+ function key_set_vertex(o, k) {
917
+ key_vertex[0] = triangle_buffer[o + k * 3];
918
+ key_vertex[1] = triangle_buffer[o + k * 3 + 1];
919
+ key_vertex[2] = triangle_buffer[o + k * 3 + 2];
920
+ }
921
+
922
+ /**
923
+ * Write edge `k` (vertex k to vertex k + 1) of the triangle at `o` in {@link triangle_buffer} into
924
+ * {@link key_edge}, its end points in lexicographic order so both triangles sharing it write one key.
925
+ */
926
+ function key_set_edge(o, k) {
927
+ const p = o + k * 3;
928
+ const q = o + ((k + 1) % 3) * 3;
929
+
930
+ const px = triangle_buffer[p], py = triangle_buffer[p + 1], pz = triangle_buffer[p + 2];
931
+ const qx = triangle_buffer[q], qy = triangle_buffer[q + 1], qz = triangle_buffer[q + 2];
932
+
933
+ const p_first = px < qx || (px === qx && (py < qy || (py === qy && pz <= qz)));
934
+
935
+ if (p_first) {
936
+ key_edge[0] = px; key_edge[1] = py; key_edge[2] = pz;
937
+ key_edge[3] = qx; key_edge[4] = qy; key_edge[5] = qz;
938
+ } else {
939
+ key_edge[0] = qx; key_edge[1] = qy; key_edge[2] = qz;
940
+ key_edge[3] = px; key_edge[4] = py; key_edge[5] = pz;
941
+ }
942
+ }
943
+
944
+ /**
945
+ * Append tentative contact `t` unless an earlier contact of the dispatch repeats it.
946
+ * @param {number} count
947
+ * @param {number} t
948
+ * @returns {number}
949
+ */
950
+ function tentative_append(count, t) {
951
+ const o = t * TENTATIVE_STRIDE;
952
+
953
+ if (dedup_contains(tentatives, o)) {
954
+ return count;
955
+ }
956
+
957
+ count = append_contact_record(count, tentatives, o);
958
+
959
+ dedup_insert(count - 1);
960
+
961
+ return count;
962
+ }
963
+
964
+ /**
965
+ * Box3D's sphere rule: tentative contacts closest first, each kept when the feature it touches is
966
+ * claimed for the first time; face contacts' triangles claim theirs before any. A kept one whose
967
+ * feature is FLAT is taken as the plane's, as everything else's is ({@link
968
+ * resolve_tentatives_by_features}): claims go by coordinates, and a neighbour whose copy of the edge
969
+ * is apart from this one's by float rounding, or that meets it at a T-junction, claims a different
970
+ * key.
971
+ *
972
+ * @param {number} count
973
+ * @param {AbstractShape3D} shape the concave shape the triangles are of
974
+ * @param {boolean} concave_is_a whether the concave shape is the contacts' A side
975
+ * @param {Transform64} concave_tr its pose
976
+ * @returns {number}
977
+ */
978
+ function resolve_tentatives_by_claims(count, shape, concave_is_a, concave_tr) {
979
+ claimed_vertices.clear();
980
+ claimed_edges.clear();
981
+
982
+ for (let f = 0; f < face_triangle_count; f++) {
983
+ const o = face_triangles[f] * TRIANGLE_FLOAT_STRIDE;
984
+
985
+ for (let k = 0; k < 3; k++) {
986
+ key_set_vertex(o, k);
987
+ claimed_vertices.add(key_vertex);
988
+ key_set_edge(o, k);
989
+ claimed_edges.add(key_edge);
990
+ }
991
+ }
992
+
993
+ // closest first; a shell sort, as the tentatives are few and this allocates nothing
994
+ const n = tentative_count;
995
+ const order = tentative_order;
996
+
997
+ for (let t = 0; t < n; t++) order[t] = t;
998
+
999
+ for (let gap = n >> 1; gap > 0; gap >>= 1) {
1000
+ for (let t = gap; t < n; t++) {
1001
+ const v = order[t];
1002
+ const key = tentatives[v * TENTATIVE_STRIDE + 13];
1003
+
1004
+ let s = t;
1005
+
1006
+ while (s >= gap && tentatives[order[s - gap] * TENTATIVE_STRIDE + 13] > key) {
1007
+ order[s] = order[s - gap];
1008
+ s -= gap;
1009
+ }
1010
+
1011
+ order[s] = v;
1012
+ }
1013
+ }
1014
+
1015
+ for (let r = 0; r < n; r++) {
1016
+ const t = order[r];
1017
+ const o = t * TENTATIVE_STRIDE;
1018
+ const tri = tentatives[o + 11] * TRIANGLE_FLOAT_STRIDE;
1019
+ const feature = tentatives[o + 12];
1020
+
1021
+ // claim every vertex and edge of the triangle, noting whether the touched one is new
1022
+ let fresh = feature === FEATURE_FACE;
1023
+
1024
+ for (let k = 0; k < 3; k++) {
1025
+ key_set_vertex(tri, k);
1026
+ if (claimed_vertices.add(key_vertex) && feature === FEATURE_VERTEX_A + k) fresh = true;
1027
+
1028
+ key_set_edge(tri, k);
1029
+ if (claimed_edges.add(key_edge) && feature === FEATURE_EDGE_AB + k) fresh = true;
1030
+ }
1031
+
1032
+ if (!fresh) {
1033
+ continue;
1034
+ }
1035
+
1036
+ if (feature_is_flat(shape, tri, feature)) {
1037
+ triangle_plane_world(tri, concave_tr);
1038
+ count = tentative_append_on_plane(count, t, concave_is_a);
1039
+ } else {
1040
+ count = tentative_append(count, t);
1041
+ }
1042
+ }
1043
+
1044
+ return count;
1045
+ }
1046
+
1047
+ /**
1048
+ * Whether `feature` of the triangle at float index `tri` of {@link triangle_buffer} is no feature of
1049
+ * the surface: the face, a FLAT edge, a FLAT corner.
1050
+ * @param {AbstractShape3D} shape
1051
+ * @param {number} tri
1052
+ * @param {number} feature
1053
+ * @returns {boolean}
1054
+ */
1055
+ function feature_is_flat(shape, tri, feature) {
1056
+ if (feature === FEATURE_FACE) {
1057
+ return true;
1058
+ }
1059
+
1060
+ if (feature < FEATURE_VERTEX_A) {
1061
+ return (triangle_edge_flags(shape, triangle_buffer, tri) & (TriangleEdgeFlags.FlatAB << feature)) !== 0;
1062
+ }
1063
+
1064
+ if (feature < FEATURE_FACE) {
1065
+ return triangle_vertex_flat(shape, triangle_buffer, tri, feature - FEATURE_VERTEX_A);
1066
+ }
1067
+
1068
+ return false;
1069
+ }
1070
+
1071
+ /**
1072
+ * A triangle's plane, from {@link triangle_plane_world}: its unit outward normal (world) at 0..2, the
1073
+ * plane's offset along it at 3, and its corners (world) at 4..12.
1074
+ */
1075
+ const tri_plane = new Float64Array(13);
1076
+
1077
+ /**
1078
+ * The plane of the triangle at float index `tri` of {@link triangle_buffer}, posed by `concave_tr`,
1079
+ * into {@link tri_plane}.
1080
+ * @param {number} tri
1081
+ * @param {Transform64} concave_tr
1082
+ */
1083
+ function triangle_plane_world(tri, concave_tr) {
1084
+ pose_pack(plane_pose, concave_tr);
1085
+ triangle_vertices_to_world(scratch_tri_world, triangle_buffer, tri, plane_pose);
1086
+
1087
+ const w = scratch_tri_world;
1088
+ const e1x = w[3] - w[0], e1y = w[4] - w[1], e1z = w[5] - w[2];
1089
+ const e2x = w[6] - w[0], e2y = w[7] - w[1], e2z = w[8] - w[2];
1090
+ const nx = e1y * e2z - e1z * e2y, ny = e1z * e2x - e1x * e2z, nz = e1x * e2y - e1y * e2x;
1091
+ const inv = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz);
1092
+
1093
+ tri_plane[0] = nx * inv;
1094
+ tri_plane[1] = ny * inv;
1095
+ tri_plane[2] = nz * inv;
1096
+ tri_plane[3] = tri_plane[0] * w[0] + tri_plane[1] * w[1] + tri_plane[2] * w[2];
1097
+
1098
+ for (let i = 0; i < 9; i++) {
1099
+ tri_plane[4 + i] = w[i];
1100
+ }
1101
+ }
1102
+
1103
+ /**
1104
+ * The round core of the dispatch's convex body, for {@link plane_witness_of}: every point of a
1105
+ * sphere's or a capsule's surface lies `round_core[ROUND_RADIUS]` from its core segment (world, from
1106
+ * `ROUND_P0` to `ROUND_P1`; a sphere's has no length). A radius of 0: the body has corners, or no core
1107
+ * known here. In an array, not module `let`s: a number written to a module `let` is boxed.
1108
+ *
1109
+ * `round_core[ROUND_HALF]` is the core's half-length, staged with the radius for
1110
+ * {@link round_core_place}.
1111
+ */
1112
+ const ROUND_RADIUS = 0;
1113
+ const ROUND_P0 = 1;
1114
+ const ROUND_P1 = 4;
1115
+ const ROUND_HALF = 7;
1116
+ const round_core = new Float64Array(8);
1117
+
1118
+ /**
1119
+ * Place the round core of the dispatch's convex body, whose radius and half-length the caller has
1120
+ * staged at ROUND_RADIUS and ROUND_HALF: `half` along its local y either side of its centre,
1121
+ * `radius` out from that. They are staged rather than passed because the call is not inlined into
1122
+ * dispatch_pair, and it would box the two numbers.
1123
+ * @param {Transform64} tr the body's pose
1124
+ */
1125
+ function round_core_place(tr) {
1126
+ const half = round_core[ROUND_HALF];
1127
+
1128
+ // q · (0, half, 0) · q*
1129
+ const qx = tr[TRANSFORM64_ROTATION_OFFSET], qy = tr[TRANSFORM64_ROTATION_OFFSET + 1];
1130
+ const qz = tr[TRANSFORM64_ROTATION_OFFSET + 2], qw = tr[TRANSFORM64_ROTATION_OFFSET + 3];
1131
+ const ax = 2 * (qx * qy - qw * qz) * half;
1132
+ const ay = (1 - 2 * (qx * qx + qz * qz)) * half;
1133
+ const az = 2 * (qy * qz + qw * qx) * half;
1134
+
1135
+ const px = tr[TRANSFORM64_TRANSLATION_OFFSET];
1136
+ const py = tr[TRANSFORM64_TRANSLATION_OFFSET + 1];
1137
+ const pz = tr[TRANSFORM64_TRANSLATION_OFFSET + 2];
1138
+
1139
+ round_core[ROUND_P0] = px - ax;
1140
+ round_core[ROUND_P0 + 1] = py - ay;
1141
+ round_core[ROUND_P0 + 2] = pz - az;
1142
+ round_core[ROUND_P1] = px + ax;
1143
+ round_core[ROUND_P1 + 1] = py + ay;
1144
+ round_core[ROUND_P1 + 2] = pz + az;
1145
+ }
1146
+
1147
+ /** The convex body's point touching the plane, of the contact {@link tentative_append_on_plane} is making. */
1148
+ const plane_witness = new Float64Array(3);
1149
+
1150
+ /**
1151
+ * Where the convex body touches a plane of outward normal f, near its witness `(bx, by, bz)` of a
1152
+ * contact along some other normal — into {@link plane_witness}.
1153
+ *
1154
+ * A body with corners touches with the same corner or edge point whichever way the normal turns, so
1155
+ * the witness stays. A round body does not: its witness slides around its surface with the normal,
1156
+ * and its point touching the plane is the one under its core, `radius` along -f from the core point
1157
+ * nearest the witness. Left where it was, the witness of a capsule touching a flat edge a few
1158
+ * millimetres off the line under its axis put the plane's push there, off the axis, and a
1159
+ * frictionless capsule sliding across a triangulated floor picked up spin at every edge — 0.1 to
1160
+ * 0.5 rad/s, where a box floor leaves it none.
1161
+ */
1162
+ function plane_witness_of(bx, by, bz, fx, fy, fz) {
1163
+ const round_radius = round_core[ROUND_RADIUS];
1164
+
1165
+ if (round_radius === 0) {
1166
+ plane_witness[0] = bx;
1167
+ plane_witness[1] = by;
1168
+ plane_witness[2] = bz;
1169
+ return;
1170
+ }
1171
+
1172
+ const round_x0 = round_core[ROUND_P0], round_y0 = round_core[ROUND_P0 + 1], round_z0 = round_core[ROUND_P0 + 2];
1173
+ const round_x1 = round_core[ROUND_P1], round_y1 = round_core[ROUND_P1 + 1], round_z1 = round_core[ROUND_P1 + 2];
1174
+
1175
+ const sx = round_x1 - round_x0, sy = round_y1 - round_y0, sz = round_z1 - round_z0;
1176
+ const ss = sx * sx + sy * sy + sz * sz;
1177
+
1178
+ let u = ss > 0 ? ((bx - round_x0) * sx + (by - round_y0) * sy + (bz - round_z0) * sz) / ss : 0;
1179
+ u = u < 0 ? 0 : u > 1 ? 1 : u;
1180
+
1181
+ plane_witness[0] = round_x0 + sx * u - fx * round_radius;
1182
+ plane_witness[1] = round_y0 + sy * u - fy * round_radius;
1183
+ plane_witness[2] = round_z0 + sz * u - fz * round_radius;
1184
+ }
1185
+
1186
+ /**
1187
+ * Whether a point of the plane in {@link tri_plane} lies on its triangle, to {@link ON_EDGE_WEIGHT}
1188
+ * of its barycentric weights.
1189
+ * @param {Float64Array} point
1190
+ * @param {number} point_offset
1191
+ * @returns {boolean}
1192
+ */
1193
+ function tri_plane_contains(point, point_offset) {
1194
+ const px = point[point_offset], py = point[point_offset + 1], pz = point[point_offset + 2];
1195
+
1196
+ const ax = tri_plane[4], ay = tri_plane[5], az = tri_plane[6];
1197
+ const bx = tri_plane[7], by = tri_plane[8], bz = tri_plane[9];
1198
+ const cx = tri_plane[10], cy = tri_plane[11], cz = tri_plane[12];
1199
+
1200
+ const e1x = bx - ax, e1y = by - ay, e1z = bz - az;
1201
+ const e2x = cx - ax, e2y = cy - ay, e2z = cz - az;
1202
+ const nx = e1y * e2z - e1z * e2y, ny = e1z * e2x - e1x * e2z, nz = e1x * e2y - e1y * e2x;
1203
+ const nn = nx * nx + ny * ny + nz * nz;
1204
+
1205
+ if (!(nn > 0)) {
1206
+ return false;
1207
+ }
1208
+
1209
+ // the weights of A and B: the signed areas of (P, B, C) and (P, C, A) over the triangle's
1210
+ const pax = ax - px, pay = ay - py, paz = az - pz;
1211
+ const pbx = bx - px, pby = by - py, pbz = bz - pz;
1212
+ const pcx = cx - px, pcy = cy - py, pcz = cz - pz;
1213
+
1214
+ const wa = ((pby * pcz - pbz * pcy) * nx + (pbz * pcx - pbx * pcz) * ny + (pbx * pcy - pby * pcx) * nz) / nn;
1215
+ const wb = ((pcy * paz - pcz * pay) * nx + (pcz * pax - pcx * paz) * ny + (pcx * pay - pcy * pax) * nz) / nn;
1216
+
1217
+ return wa >= -ON_EDGE_WEIGHT && wb >= -ON_EDGE_WEIGHT && 1 - wa - wb >= -ON_EDGE_WEIGHT;
1218
+ }
1219
+
1220
+ /**
1221
+ * Append tentative contact `t` as a contact with the plane of its triangle, the one in {@link
1222
+ * tri_plane}: the normal the triangle's, the depth below that plane of the convex body's point touching
1223
+ * it ({@link plane_witness_of}), the triangle's witness that point's projection onto it. Not appended
1224
+ * when the point is not below the plane, nor for a round body whose point projects off the triangle and
1225
+ * whose contact's own normal is more than a flat edge's bend off the plane's.
1226
+ *
1227
+ * The plane is the one in {@link tri_plane}: its unit outward normal f (world) and its offset along
1228
+ * it, `f · p` for any point p of it.
1229
+ *
1230
+ * @param {number} count
1231
+ * @param {number} t
1232
+ * @param {boolean} concave_is_a whether the triangle is the contact's A side
1233
+ * @returns {number}
1234
+ */
1235
+ function tentative_append_on_plane(count, t, concave_is_a) {
1236
+ const fx = tri_plane[0], fy = tri_plane[1], fz = tri_plane[2];
1237
+ const plane = tri_plane[3];
1238
+
1239
+ const o = t * TENTATIVE_STRIDE;
1240
+
1241
+ // the convex body's witness, moved to where it touches the plane
1242
+ const bo = concave_is_a ? o + 3 : o;
1243
+ plane_witness_of(tentatives[bo], tentatives[bo + 1], tentatives[bo + 2], fx, fy, fz);
1244
+ const bx = plane_witness[0], by = plane_witness[1], bz = plane_witness[2];
1245
+
1246
+ const depth = plane - (fx * bx + fy * by + fz * bz);
1247
+
1248
+ if (!(depth > 0)) {
1249
+ return count;
1250
+ }
1251
+
1252
+ const px = bx + fx * depth, py = by + fy * depth, pz = bz + fz * depth;
1253
+
1254
+ // the contact, staged: the triangle's point is A's witness when the triangle is A
1255
+ const c = staged_contact;
1256
+ const p_offset = concave_is_a ? 0 : 3;
1257
+ const b_offset = concave_is_a ? 3 : 0;
1258
+
1259
+ c[p_offset] = px; c[p_offset + 1] = py; c[p_offset + 2] = pz;
1260
+ c[b_offset] = bx; c[b_offset + 1] = by; c[b_offset + 2] = bz;
1261
+
1262
+ // A round body's point under its core, off this triangle, and the contact meeting the feature from
1263
+ // further off this plane's normal than a flat edge bends: the body is not on this face. A capsule over
1264
+ // a 1 cm step touched the riser's internal diagonal from above, and taken as the riser's plane that
1265
+ // contact pushed it back by its whole 8 cm intrusion into the plane, which there is not the surface.
1266
+ // Nearer the plane's normal it is the plane's, off the triangle or not: on a convex crease flatter
1267
+ // than a flat edge's bend, the point lies past the crease on either side, off both triangles, and
1268
+ // neither holds the body. Dropped on both, a sphere set on a 1° roof's ridge fell through it.
1269
+ if (round_core[ROUND_RADIUS] !== 0 && !tri_plane_contains(c, p_offset)
1270
+ && Math.abs(tentatives[o + 6] * fx + tentatives[o + 7] * fy + tentatives[o + 8] * fz) < FLAT_EDGE_COS) {
1271
+ return count;
1272
+ }
1273
+
1274
+ // stored B → A: the triangle is A, so toward it
1275
+ c[6] = concave_is_a ? -fx : fx;
1276
+ c[7] = concave_is_a ? -fy : fy;
1277
+ c[8] = concave_is_a ? -fz : fz;
1278
+ c[9] = depth;
1279
+ c[10] = tentatives[o + 10];
1280
+
1281
+ if (dedup_contains(c, 0)) {
1282
+ return count;
1283
+ }
1284
+
1285
+ count = append_contact_record(count, c, 0);
1286
+
1287
+ dedup_insert(count - 1);
1288
+
1289
+ return count;
1290
+ }
1291
+
1292
+ /**
1293
+ * The rule for everything but a sphere: a tentative contact lying inside its triangle, or on an edge or
1294
+ * a corner of it that is FLAT, touches no feature of the surface — the surface there is the plane — and
1295
+ * is taken as a contact with the triangle's plane ({@link tentative_append_on_plane}). One on a real
1296
+ * edge or a real corner is kept as it is. See "Internal edges".
1297
+ *
1298
+ * @param {number} count
1299
+ * @param {AbstractShape3D} shape the concave shape the triangles are of
1300
+ * @param {boolean} concave_is_a whether the concave shape is the contacts' A side
1301
+ * @param {Transform64} concave_tr its pose
1302
+ * @returns {number}
1303
+ */
1304
+ function resolve_tentatives_by_features(count, shape, concave_is_a, concave_tr) {
1305
+ // a triangle's tentative contacts arrive together: classify its edges, and find its plane, once
1306
+ let edges_tri = -1, edges = 0;
1307
+ let plane_tri = -1;
1308
+
1309
+ for (let t = 0; t < tentative_count; t++) {
1310
+ const o = t * TENTATIVE_STRIDE;
1311
+ const tri = tentatives[o + 11] * TRIANGLE_FLOAT_STRIDE;
1312
+ const feature = tentatives[o + 12];
1313
+
1314
+ let flat;
1315
+
1316
+ if (feature < FEATURE_VERTEX_A) {
1317
+ if (edges_tri !== tri) {
1318
+ edges = triangle_edge_flags(shape, triangle_buffer, tri);
1319
+ edges_tri = tri;
1320
+ }
1321
+
1322
+ flat = (edges & (TriangleEdgeFlags.FlatAB << feature)) !== 0;
1323
+ } else {
1324
+ flat = feature_is_flat(shape, tri, feature);
1325
+ }
1326
+
1327
+ if (!flat) {
1328
+ count = tentative_append(count, t);
1329
+ continue;
1330
+ }
1331
+
1332
+ if (plane_tri !== tri) {
1333
+ triangle_plane_world(tri, concave_tr);
1334
+ plane_tri = tri;
1335
+ }
1336
+
1337
+ count = tentative_append_on_plane(count, t, concave_is_a);
1338
+ }
1339
+
1340
+ return count;
1341
+ }
1342
+ /**
1343
+ * Most triangles one concave-vs-convex pair is collided against: the bound
1344
+ * on {@link triangle_buffer}'s growth (65,536 triangles, 5 MiB of scratch).
1345
+ *
1346
+ * The count is the convex side's footprint over the concave side's
1347
+ * triangle density, not a property of the pair kind: a 4 m crate on a
1348
+ * floor meshed at 10 cm covers some 3,500 triangles, an 8 m box on a
1349
+ * tessellated heightmap 32,768. The buffer used to be a fixed 1,024 and
1350
+ * the overflow was dropped — a drop that is not "a far edge", because the
1351
+ * enumerators emit in index (mesh) or row (heightmap) order, so what was
1352
+ * lost was every triangle past a line through the footprint. The crate
1353
+ * kept the triangles under one side, tipped about that line, and fell
1354
+ * through the floor, the same way every step.
1355
+ *
1356
+ * Past this bound the rest of the pair's triangles are not collided
1357
+ * against, and the first time it happens the process is told so.
1358
+ *
1359
+ * @type {number}
1360
+ */
1361
+ const MAX_TRIANGLES_PER_PAIR = 1 << 16;
1362
+
1363
+ /**
1364
+ * Per-pair scratch for the concave-side triangle decomposition. Grows (by
1365
+ * doubling, re-enumerating once) when a pair overlaps more triangles than
1366
+ * it holds, up to {@link MAX_TRIANGLES_PER_PAIR}; the high-water mark is
1367
+ * kept for the process.
1368
+ *
1369
+ * Enumerators return the TRUE overlap count and drop writes past the
1370
+ * buffer's end (typed-array OOB semantics), so the consume loop must never
1371
+ * run past the capacity — it would read `undefined` and poison the
1372
+ * manifold with NaN.
260
1373
  * @type {Float64Array}
261
1374
  */
262
- const triangle_buffer = new Float64Array(MAX_TRIANGLES_PER_PAIR * TRIANGLE_FLOAT_STRIDE);
1375
+ let triangle_buffer = new Float64Array(1024 * TRIANGLE_FLOAT_STRIDE);
1376
+
1377
+ /**
1378
+ * Whether a pair overlapping more than {@link MAX_TRIANGLES_PER_PAIR}
1379
+ * triangles has been reported. Once per process: the condition belongs to
1380
+ * the content, and it would recur every substep.
1381
+ * @type {boolean}
1382
+ */
1383
+ let triangle_overflow_reported = false;
263
1384
 
264
1385
  /**
265
1386
  * Flyweight triangle shape — rebound to each successive triangle slice
@@ -290,8 +1411,25 @@ const concave_query_aabb = new Float64Array(6);
290
1411
  *
291
1412
  * Shared by the sphere / box / capsule closed-form triangle fast-paths in
292
1413
  * the concave dispatch, which each carried an identical inlined copy.
1414
+ *
1415
+ * The vertices and the pose are read from arrays: the dispatch calls this
1416
+ * per triangle from more sites than it can inline, and a call that is not
1417
+ * inlined boxes each number passed to it — sixteen per triangle.
1418
+ *
1419
+ * @param {Float64Array} out
1420
+ * @param {Float64Array} triangle local vertices A, B, C
1421
+ * @param {number} triangle_offset
1422
+ * @param {Float64Array} pose the concave body's position then orientation:
1423
+ * `[px, py, pz, qx, qy, qz, qw]`
293
1424
  */
294
- function triangle_vertices_to_world(out, ax, ay, az, bx, by, bz, cx, cy, cz, qx, qy, qz, qw, px, py, pz) {
1425
+ function triangle_vertices_to_world(out, triangle, triangle_offset, pose) {
1426
+ const ax = triangle[triangle_offset], ay = triangle[triangle_offset + 1], az = triangle[triangle_offset + 2];
1427
+ const bx = triangle[triangle_offset + 3], by = triangle[triangle_offset + 4], bz = triangle[triangle_offset + 5];
1428
+ const cx = triangle[triangle_offset + 6], cy = triangle[triangle_offset + 7], cz = triangle[triangle_offset + 8];
1429
+
1430
+ const px = pose[0], py = pose[1], pz = pose[2];
1431
+ const qx = pose[3], qy = pose[4], qz = pose[5], qw = pose[6];
1432
+
295
1433
  const axi = qw * ax + qy * az - qz * ay;
296
1434
  const ayi = qw * ay + qz * ax - qx * az;
297
1435
  const azi = qw * az + qx * ay - qy * ax;
@@ -323,6 +1461,195 @@ function triangle_vertices_to_world(out, ax, ay, az, bx, by, bz, cx, cy, cz, qx,
323
1461
  /** Destination for {@link triangle_vertices_to_world}. */
324
1462
  const scratch_tri_world = new Float64Array(9);
325
1463
 
1464
+ /**
1465
+ * Poses, `[px, py, pz, qx, qy, qz, qw]`: the concave and the convex body's in the concave dispatch,
1466
+ * packed once per dispatch, and {@link triangle_plane_world}'s.
1467
+ */
1468
+ const concave_pose = new Float64Array(7);
1469
+ const convex_pose = new Float64Array(7);
1470
+ const plane_pose = new Float64Array(7);
1471
+
1472
+ /**
1473
+ * The concave dispatch's convex body, packed once per dispatch for the closed-form triangle kernels'
1474
+ * array forms: a box's centre, orientation and half-extents (10); a sphere's centre and radius (4); a
1475
+ * capsule's centre, orientation, radius and half-height (9).
1476
+ */
1477
+ const convex_record = new Float64Array(10);
1478
+
1479
+ /**
1480
+ * Pack a pose, `[px, py, pz, qx, qy, qz, qw]`, from a transform.
1481
+ *
1482
+ * Read off the transform's lanes (a Transform64 is a Float64Array), not through `translation_x` and
1483
+ * the rest: inlined into dispatch_pair, those accessor calls land on a branch too rarely taken for
1484
+ * TurboFan to inline them, and each then returns its number boxed.
1485
+ *
1486
+ * @param {Float64Array} out
1487
+ * @param {Transform64} tr
1488
+ */
1489
+ function pose_pack(out, tr) {
1490
+ out[0] = tr[TRANSFORM64_TRANSLATION_OFFSET];
1491
+ out[1] = tr[TRANSFORM64_TRANSLATION_OFFSET + 1];
1492
+ out[2] = tr[TRANSFORM64_TRANSLATION_OFFSET + 2];
1493
+ out[3] = tr[TRANSFORM64_ROTATION_OFFSET];
1494
+ out[4] = tr[TRANSFORM64_ROTATION_OFFSET + 1];
1495
+ out[5] = tr[TRANSFORM64_ROTATION_OFFSET + 2];
1496
+ out[6] = tr[TRANSFORM64_ROTATION_OFFSET + 3];
1497
+ }
1498
+
1499
+ // dispatch_pair branches on the shape pair, so each branch runs on a fraction of its calls: too few
1500
+ // for TurboFan to inline a call made there, small or not, and a call that is not inlined boxes each
1501
+ // number handed to it. The closed-form pair kernels therefore get their bodies packed into these
1502
+ // records, and their contacts are appended from arrays.
1503
+
1504
+ /** dispatch_pair's boxes, `[px, py, pz, qx, qy, qz, qw, hx, hy, hz]`. */
1505
+ const pair_box_a = new Float64Array(10);
1506
+ const pair_box_b = new Float64Array(10);
1507
+
1508
+ /** dispatch_pair's spheres, `[px, py, pz, radius]`. */
1509
+ const pair_sphere_a = new Float64Array(4);
1510
+ const pair_sphere_b = new Float64Array(4);
1511
+
1512
+ /** dispatch_pair's capsules, `[px, py, pz, qx, qy, qz, qw, radius, half_height]`. */
1513
+ const pair_capsule_a = new Float64Array(9);
1514
+ const pair_capsule_b = new Float64Array(9);
1515
+
1516
+ /** A hull's pose, `[px, py, pz, qx, qy, qz, qw]`, for the hull-sphere kernel. */
1517
+ const pair_pose = new Float64Array(7);
1518
+
1519
+ /** The two poses of a hull pair, for {@link hull_pair_contacts}. */
1520
+ const pair_pose_a = new Float64Array(7);
1521
+ const pair_pose_b = new Float64Array(7);
1522
+
1523
+ /** One contact for {@link append_contact_record}, staged by dispatch_pair's closed-form branches. */
1524
+ const pair_contact = new Float64Array(11);
1525
+
1526
+ /**
1527
+ * Pack a sphere record, `[px, py, pz, radius]`.
1528
+ * @param {Float64Array} out
1529
+ * @param {Transform64} tr
1530
+ * @param {SphereShape3D} shape
1531
+ */
1532
+ function sphere_record_pack(out, tr, shape) {
1533
+ out[0] = tr[TRANSFORM64_TRANSLATION_OFFSET];
1534
+ out[1] = tr[TRANSFORM64_TRANSLATION_OFFSET + 1];
1535
+ out[2] = tr[TRANSFORM64_TRANSLATION_OFFSET + 2];
1536
+ out[3] = shape.radius;
1537
+ }
1538
+
1539
+ /**
1540
+ * Pack a capsule record, `[px, py, pz, qx, qy, qz, qw, radius, half_height]`.
1541
+ * @param {Float64Array} out
1542
+ * @param {Transform64} tr
1543
+ * @param {CapsuleShape3D} shape
1544
+ */
1545
+ function capsule_record_pack(out, tr, shape) {
1546
+ pose_pack(out, tr);
1547
+ out[7] = shape.radius;
1548
+ out[8] = shape.height * 0.5;
1549
+ }
1550
+
1551
+ /**
1552
+ * Append a closed-form contact from a result laid out `[normal, depth, witness 1, witness 2]` — the
1553
+ * sphere-box, capsule-sphere and hull-sphere kernels' layout, whose normal points from witness 2's
1554
+ * body toward witness 1's.
1555
+ *
1556
+ * @param {number} count
1557
+ * @param {Float64Array} result
1558
+ * @param {boolean} first_is_a witness 1 is on A; else the witnesses swap and the normal turns round
1559
+ * @param {number} feature_id
1560
+ * @returns {number}
1561
+ */
1562
+ function append_closed_form_contact(count, result, first_is_a, feature_id) {
1563
+ const c = pair_contact;
1564
+
1565
+ if (first_is_a) {
1566
+ c[0] = result[4]; c[1] = result[5]; c[2] = result[6];
1567
+ c[3] = result[7]; c[4] = result[8]; c[5] = result[9];
1568
+ c[6] = result[0]; c[7] = result[1]; c[8] = result[2];
1569
+ } else {
1570
+ c[0] = result[7]; c[1] = result[8]; c[2] = result[9];
1571
+ c[3] = result[4]; c[4] = result[5]; c[5] = result[6];
1572
+ c[6] = -result[0]; c[7] = -result[1]; c[8] = -result[2];
1573
+ }
1574
+
1575
+ c[9] = result[3];
1576
+ c[10] = feature_id;
1577
+
1578
+ return append_contact_record(count, c, 0);
1579
+ }
1580
+
1581
+ /**
1582
+ * Append contact `offset` of a multi-contact result laid out `[witness 1, witness 2, normal, depth]`
1583
+ * per contact — the capsule-capsule and capsule-box kernels' layout, whose normal points from
1584
+ * witness 2's body toward witness 1's.
1585
+ *
1586
+ * @param {number} count
1587
+ * @param {Float64Array} result
1588
+ * @param {number} offset
1589
+ * @param {boolean} first_is_a witness 1 is on A; else the witnesses swap and the normal turns round
1590
+ * @param {number} feature_id
1591
+ * @returns {number}
1592
+ */
1593
+ function append_manifold_contact(count, result, offset, first_is_a, feature_id) {
1594
+ const c = pair_contact;
1595
+
1596
+ if (first_is_a) {
1597
+ c[0] = result[offset]; c[1] = result[offset + 1]; c[2] = result[offset + 2];
1598
+ c[3] = result[offset + 3]; c[4] = result[offset + 4]; c[5] = result[offset + 5];
1599
+ c[6] = result[offset + 6]; c[7] = result[offset + 7]; c[8] = result[offset + 8];
1600
+ } else {
1601
+ c[0] = result[offset + 3]; c[1] = result[offset + 4]; c[2] = result[offset + 5];
1602
+ c[3] = result[offset]; c[4] = result[offset + 1]; c[5] = result[offset + 2];
1603
+ c[6] = -result[offset + 6]; c[7] = -result[offset + 7]; c[8] = -result[offset + 8];
1604
+ }
1605
+
1606
+ c[9] = result[offset + 9];
1607
+ c[10] = feature_id;
1608
+
1609
+ return append_contact_record(count, c, 0);
1610
+ }
1611
+
1612
+ /**
1613
+ * Pack a box record, `[px, py, pz, qx, qy, qz, qw, hx, hy, hz]`: its centre and orientation from the
1614
+ * transform, then its half-extents, read off their lanes for {@link pose_pack}'s reason (a Vector3 is
1615
+ * a Float64Array).
1616
+ * @param {Float64Array} out
1617
+ * @param {Transform64} tr
1618
+ * @param {Vector3} half_extents
1619
+ */
1620
+ function box_record_pack(out, tr, half_extents) {
1621
+ pose_pack(out, tr);
1622
+ out[7] = half_extents[0];
1623
+ out[8] = half_extents[1];
1624
+ out[9] = half_extents[2];
1625
+ }
1626
+
1627
+ /**
1628
+ * Append contact `offset` of a box-box or convex-convex manifold (the layout is shared) — A witness,
1629
+ * B witness, depth, after the manifold's shared normal at [0..2] — with no feature id.
1630
+ * @param {number} count
1631
+ * @param {Float64Array} manifold a {@link box_box_manifold_array} or {@link convex_hull_clip_array} result
1632
+ * @param {number} offset
1633
+ * @returns {number}
1634
+ */
1635
+ function append_box_manifold_contact(count, manifold, offset) {
1636
+ const c = pair_contact;
1637
+
1638
+ c[0] = manifold[offset];
1639
+ c[1] = manifold[offset + 1];
1640
+ c[2] = manifold[offset + 2];
1641
+ c[3] = manifold[offset + 3];
1642
+ c[4] = manifold[offset + 4];
1643
+ c[5] = manifold[offset + 5];
1644
+ c[6] = manifold[0];
1645
+ c[7] = manifold[1];
1646
+ c[8] = manifold[2];
1647
+ c[9] = manifold[offset + 6];
1648
+ c[10] = 0;
1649
+
1650
+ return append_contact_record(count, c, 0);
1651
+ }
1652
+
326
1653
  /**
327
1654
  * Append one contact to the candidate buffer. Returns the new count.
328
1655
  *
@@ -360,6 +1687,37 @@ function append_contact(count, wax, way, waz, wbx, wby, wbz, nx, ny, nz, depth,
360
1687
  return count + 1;
361
1688
  }
362
1689
 
1690
+ /**
1691
+ * {@link append_contact} with the contact read from a record laid out as append_contact takes it —
1692
+ * A witness, B witness, stored normal, depth, feature id — at `offset`: for the concave dispatch's
1693
+ * per-triangle contacts, where a call that is not inlined would box all eleven. (append_contact keeps
1694
+ * its own copy of the stores rather than packing for this: as it is, the contact producers that take
1695
+ * it as their callback inline it.)
1696
+ *
1697
+ * @param {number} count
1698
+ * @param {Float64Array} contact
1699
+ * @param {number} offset
1700
+ * @returns {number}
1701
+ */
1702
+ function append_contact_record(count, contact, offset) {
1703
+ if (count * CANDIDATE_STRIDE >= candidates.length){
1704
+ grow_candidates();
1705
+ }
1706
+
1707
+ const off = count * CANDIDATE_STRIDE;
1708
+
1709
+ candidates[off] = contact[offset]; candidates[off + 1] = contact[offset + 1]; candidates[off + 2] = contact[offset + 2];
1710
+ candidates[off + 3] = contact[offset + 3]; candidates[off + 4] = contact[offset + 4]; candidates[off + 5] = contact[offset + 5];
1711
+ candidates[off + 6] = contact[offset + 6]; candidates[off + 7] = contact[offset + 7]; candidates[off + 8] = contact[offset + 8];
1712
+ candidates[off + 9] = contact[offset + 9];
1713
+ candidates[off + 10] = contact[offset + 10];
1714
+ candidates[off + 11] = g_pair_friction;
1715
+ candidates[off + 12] = g_pair_restitution;
1716
+ candidates[off + 13] = g_pair_flags;
1717
+
1718
+ return count + 1;
1719
+ }
1720
+
363
1721
  /**
364
1722
  * Compute a stable voronoi-region feature id for a sphere-vs-box contact.
365
1723
  * Returns one of 27 values in `[1, 27]` based on which of the 27 voronoi
@@ -375,22 +1733,22 @@ function append_contact(count, wax, way, waz, wbx, wby, wbz, nx, ny, nz, depth,
375
1733
  * The "+1" offset ensures the result is never 0, since 0 is reserved
376
1734
  * for "no feature info, use position-fallback".
377
1735
  *
378
- * @param {number} sx sphere centre x
379
- * @param {number} sy
380
- * @param {number} sz
381
- * @param {number} bx box centre x
382
- * @param {number} by
383
- * @param {number} bz
384
- * @param {number} bqx box quaternion x
385
- * @param {number} bqy
386
- * @param {number} bqz
387
- * @param {number} bqw
388
- * @param {number} hx box half-extent x
389
- * @param {number} hy
390
- * @param {number} hz
1736
+ * The sphere and the box are read from their records (see {@link sphere_record_pack} and
1737
+ * {@link box_record_pack}): this is called from a dispatch_pair branch, where the thirteen numbers
1738
+ * would each be boxed into a new heap object.
1739
+ *
1740
+ * @param {Float64Array} sphere `[centre, radius]`
1741
+ * @param {number} sphere_offset
1742
+ * @param {Float64Array} box `[centre, orientation, half-extents]`
1743
+ * @param {number} box_offset
391
1744
  * @returns {number}
392
1745
  */
393
- function sphere_box_voronoi_fid(sx, sy, sz, bx, by, bz, bqx, bqy, bqz, bqw, hx, hy, hz) {
1746
+ function sphere_box_voronoi_fid_array(sphere, sphere_offset, box, box_offset) {
1747
+ const sx = sphere[sphere_offset], sy = sphere[sphere_offset + 1], sz = sphere[sphere_offset + 2];
1748
+ const bx = box[box_offset], by = box[box_offset + 1], bz = box[box_offset + 2];
1749
+ const bqx = box[box_offset + 3], bqy = box[box_offset + 4], bqz = box[box_offset + 5], bqw = box[box_offset + 6];
1750
+ const hx = box[box_offset + 7], hy = box[box_offset + 8], hz = box[box_offset + 9];
1751
+
394
1752
  // Inverse-rotate (sx - bx, sy - by, sz - bz) by the box's quaternion
395
1753
  // to get the sphere centre in box-local frame. Inlined for the same
396
1754
  // V8-inliner reason described in PosedShape3D.support — see
@@ -430,45 +1788,182 @@ function sphere_box_voronoi_fid(sx, sy, sz, bx, by, bz, bqx, bqy, bqz, bqw, hx,
430
1788
  * @param {Transform64} trA
431
1789
  * @param {{vertices,face_offsets,face_loops,support}} hullB in B's local frame
432
1790
  * @param {Transform64} trB
433
- * @param {Function} append_contact
434
1791
  * @returns {number}
435
1792
  */
436
- function hull_pair_contacts(count, hullA, trA, hullB, trB, append_contact) {
437
- posed_a.setup_components(
438
- hullA,
439
- trA.translation_x, trA.translation_y, trA.translation_z,
440
- trA.rotation_x, trA.rotation_y, trA.rotation_z, trA.rotation_w
441
- );
442
- posed_b.setup_components(
443
- hullB,
444
- trB.translation_x, trB.translation_y, trB.translation_z,
445
- trB.rotation_x, trB.rotation_y, trB.rotation_z, trB.rotation_w
446
- );
1793
+ function hull_pair_contacts(count, hullA, trA, hullB, trB) {
1794
+ // Poses go to the posed shapes and the clip in arrays, read off the transforms' lanes: this is
1795
+ // called from a dispatch_pair branch, and numbers handed across a call there are boxed.
1796
+ pose_pack(pair_pose_a, trA);
1797
+ pose_pack(pair_pose_b, trB);
1798
+ posed_a.setup_pose(hullA, pair_pose_a, 0);
1799
+ posed_b.setup_pose(hullB, pair_pose_b, 0);
447
1800
  const depth = gjk_epa_penetration(epa_result, posed_a, posed_b);
448
1801
  if (!(depth > 0) || !Number.isFinite(depth)) return count;
449
1802
 
1803
+ hull_position_a[0] = pair_pose_a[0];
1804
+ hull_position_a[1] = pair_pose_a[1];
1805
+ hull_position_a[2] = pair_pose_a[2];
1806
+ hull_rotation_a[0] = pair_pose_a[3];
1807
+ hull_rotation_a[1] = pair_pose_a[4];
1808
+ hull_rotation_a[2] = pair_pose_a[5];
1809
+ hull_rotation_a[3] = pair_pose_a[6];
1810
+
1811
+ hull_position_b[0] = pair_pose_b[0];
1812
+ hull_position_b[1] = pair_pose_b[1];
1813
+ hull_position_b[2] = pair_pose_b[2];
1814
+ hull_rotation_b[0] = pair_pose_b[3];
1815
+ hull_rotation_b[1] = pair_pose_b[4];
1816
+ hull_rotation_b[2] = pair_pose_b[5];
1817
+ hull_rotation_b[3] = pair_pose_b[6];
1818
+
450
1819
  // convex_hull_clip orients the axis B→A robustly (via vertex centroids) and
451
1820
  // writes the final normal into convex_manifold_result[0..2].
452
- convex_hull_clip(convex_manifold_result,
453
- hullA, trA.translation, trA.rotation,
454
- hullB, trB.translation, trB.rotation,
455
- epa_result[0], epa_result[1], epa_result[2]);
456
- const nx = convex_manifold_result[0], ny = convex_manifold_result[1], nz = convex_manifold_result[2];
1821
+ convex_hull_clip_array(convex_manifold_result,
1822
+ hullA, hull_position_a, hull_rotation_a,
1823
+ hullB, hull_position_b, hull_rotation_b,
1824
+ epa_result, 0);
457
1825
  const cc = convex_manifold_result[3] | 0;
458
1826
  // Clipped points migrate across features as the hulls rotate, so leave
459
1827
  // fid = 0 and let the match-and-merge pass position-match (as box-box does).
460
1828
  for (let k = 0; k < cc; k++) {
461
- const base = 4 + k * 7;
462
- count = append_contact(count,
463
- convex_manifold_result[base], convex_manifold_result[base + 1], convex_manifold_result[base + 2],
464
- convex_manifold_result[base + 3], convex_manifold_result[base + 4], convex_manifold_result[base + 5],
465
- nx, ny, nz,
466
- convex_manifold_result[base + 6],
467
- 0);
1829
+ count = append_box_manifold_contact(count, convex_manifold_result, 4 + k * 7);
468
1830
  }
469
1831
  return count;
470
1832
  }
471
1833
 
1834
+ /**
1835
+ * A box as a polytope, for {@link hull_pair_contacts}: corners `(±hx, ±hy, ±hz)` numbered
1836
+ * 0 −−−, 1 +−−, 2 ++−, 3 −+−, 4 −−+, 5 +−+, 6 +++, 7 −++ (the −z face's square, then the +z face's),
1837
+ * six quad faces wound CCW from outside, and the exact box support. {@link box_polytope_set} sizes it
1838
+ * before each use.
1839
+ */
1840
+ class BoxPolytope {
1841
+ vertices = new Float64Array(24);
1842
+ face_offsets = new Uint32Array([0, 4, 8, 12, 16, 20, 24]);
1843
+ face_loops = new Uint32Array([
1844
+ 1, 2, 6, 5, // +X
1845
+ 0, 4, 7, 3, // -X
1846
+ 3, 7, 6, 2, // +Y
1847
+ 0, 1, 5, 4, // -Y
1848
+ 4, 5, 6, 7, // +Z
1849
+ 0, 3, 2, 1, // -Z
1850
+ ]);
1851
+
1852
+ // a typed array, not three number fields: a double field of a plain object is a heap box, and
1853
+ // reading one in the GJK support loop allocated ~320 B per dispatch
1854
+ half_extents = new Float64Array(3);
1855
+
1856
+ support(result, result_offset, direction, direction_offset) {
1857
+ const dx = direction[direction_offset];
1858
+ const dy = direction[direction_offset + 1];
1859
+ const dz = direction[direction_offset + 2];
1860
+
1861
+ const h = this.half_extents;
1862
+ result[result_offset] = dx >= 0 ? h[0] : -h[0];
1863
+ result[result_offset + 1] = dy >= 0 ? h[1] : -h[1];
1864
+ result[result_offset + 2] = dz >= 0 ? h[2] : -h[2];
1865
+ }
1866
+ }
1867
+
1868
+ const box_polytope = new BoxPolytope();
1869
+
1870
+ /**
1871
+ * Size {@link box_polytope} to a box's half-extents.
1872
+ * @param {{x: number, y: number, z: number}} half_extents
1873
+ */
1874
+ function box_polytope_set(half_extents) {
1875
+ const hx = half_extents.x, hy = half_extents.y, hz = half_extents.z;
1876
+ const v = box_polytope.vertices;
1877
+ const h = box_polytope.half_extents;
1878
+
1879
+ h[0] = hx;
1880
+ h[1] = hy;
1881
+ h[2] = hz;
1882
+
1883
+ for (let i = 0; i < 8; i++) {
1884
+ // +x at corners 1, 2, 5, 6; +y at 2, 3, 6, 7; +z at 4..7
1885
+ const px = (i === 1 || i === 2 || i === 5 || i === 6) ? hx : -hx;
1886
+ const py = (i === 2 || i === 3 || i === 6 || i === 7) ? hy : -hy;
1887
+ const pz = i >= 4 ? hz : -hz;
1888
+
1889
+ v[i * 3] = px;
1890
+ v[i * 3 + 1] = py;
1891
+ v[i * 3 + 2] = pz;
1892
+ }
1893
+ }
1894
+
1895
+ /**
1896
+ * Stand-in colliders and world poses for the subjects of rigid TransformedShape3D wrappers, one per
1897
+ * level of nesting (a wrapper may wrap a wrapper, and both sides of a pair may be wrapped).
1898
+ * @type {Array<{shape: AbstractShape3D|null, friction: number|undefined, restitution: number|undefined, flags: number|undefined}>}
1899
+ */
1900
+ const subject_colliders = [];
1901
+
1902
+ /** @type {Transform64[]} */
1903
+ const subject_poses = [];
1904
+
1905
+ let subject_depth = 0;
1906
+
1907
+ /**
1908
+ * The subject pose of the current level of nesting, made on first use.
1909
+ *
1910
+ * @returns {Transform64}
1911
+ */
1912
+ function subject_pose_here() {
1913
+ const level = subject_depth;
1914
+
1915
+ if (level === subject_colliders.length) {
1916
+ subject_colliders.push({ shape: null, friction: undefined, restitution: undefined, flags: undefined });
1917
+ subject_poses.push(new Transform64());
1918
+ }
1919
+
1920
+ return subject_poses[level];
1921
+ }
1922
+
1923
+ /**
1924
+ * Dispatch a pair one of whose shapes is a rigid {@link TransformedShape3D}, as its SUBJECT at the
1925
+ * composed world pose, which {@link rigid_subject_pose} must just have written into this level's
1926
+ * {@link subject_pose_here}.
1927
+ *
1928
+ * A rigid wrapper moves its subject without changing it, so the subject collides through whatever
1929
+ * routine its own type has — a wrapped box is a box, with the box's closed forms and its four-point
1930
+ * manifold. Left wrapped it had no routine: it fell to the GJK + EPA fallback, which put the contact at
1931
+ * the bodies' origins. That is how the first-person player's capsule, lifted off the feet by a
1932
+ * wrapper, met everything it touched.
1933
+ *
1934
+ * @param {number} count
1935
+ * @param {Collider} colA
1936
+ * @param {Transform64} trA
1937
+ * @param {Collider} colB
1938
+ * @param {Transform64} trB
1939
+ * @param {boolean} a_side the wrapper is A's shape (else B's)
1940
+ * @returns {number}
1941
+ */
1942
+ function dispatch_subject(count, colA, trA, colB, trB, a_side) {
1943
+ const level = subject_depth;
1944
+
1945
+ const wrapper_col = a_side ? colA : colB;
1946
+
1947
+ // the materials and the sensor flag belong to the collider, not the shape
1948
+ const col = subject_colliders[level];
1949
+ col.shape = wrapper_col.shape.subject;
1950
+ col.friction = wrapper_col.friction;
1951
+ col.restitution = wrapper_col.restitution;
1952
+ col.flags = wrapper_col.flags;
1953
+
1954
+ const pose = subject_poses[level];
1955
+
1956
+ subject_depth++;
1957
+
1958
+ const result = a_side
1959
+ ? dispatch_pair(count, col, pose, colB, trB)
1960
+ : dispatch_pair(count, colA, trA, col, pose);
1961
+
1962
+ subject_depth--;
1963
+
1964
+ return result;
1965
+ }
1966
+
472
1967
  /**
473
1968
  * Run pairwise narrowphase for one (colliderA, colliderB) tuple — dispatches
474
1969
  * by shape type and appends 0..K contacts to the candidate buffer. Returns
@@ -485,6 +1980,23 @@ function dispatch_pair(count, colA, trA, colB, trB) {
485
1980
  const shapeA = colA.shape;
486
1981
  const shapeB = colB.shape;
487
1982
 
1983
+ // A rigid wrapper is its subject, moved: see dispatch_subject. (A scaling one is not, and stays.)
1984
+ if (shapeA.isTransformedShape3D === true && rigid_subject_pose(
1985
+ subject_pose_here(), shapeA,
1986
+ trA.translation_x, trA.translation_y, trA.translation_z,
1987
+ trA.rotation_x, trA.rotation_y, trA.rotation_z, trA.rotation_w
1988
+ )) {
1989
+ return dispatch_subject(count, colA, trA, colB, trB, true);
1990
+ }
1991
+
1992
+ if (shapeB.isTransformedShape3D === true && rigid_subject_pose(
1993
+ subject_pose_here(), shapeB,
1994
+ trB.translation_x, trB.translation_y, trB.translation_z,
1995
+ trB.rotation_x, trB.rotation_y, trB.rotation_z, trB.rotation_w
1996
+ )) {
1997
+ return dispatch_subject(count, colA, trA, colB, trB, false);
1998
+ }
1999
+
488
2000
  // Per-contact materials: combine the two source colliders' coefficients
489
2001
  // once here (this is the only place that knows the exact collider on each
490
2002
  // side) and stamp them onto every contact this dispatch appends. The
@@ -528,85 +2040,54 @@ function dispatch_pair(count, colA, trA, colB, trB) {
528
2040
 
529
2041
  // sphere-sphere
530
2042
  if (isSphereA && isSphereB) {
531
- const ra = shapeA.radius, rb = shapeB.radius;
2043
+ const a = pair_sphere_a, b = pair_sphere_b;
2044
+ sphere_record_pack(a, trA, shapeA);
2045
+ sphere_record_pack(b, trB, shapeB);
532
2046
 
533
- const ok = sphere_sphere_contact(
534
- sphere_result,
535
- trA.translation_x, trA.translation_y, trA.translation_z,
536
- trB.translation_x, trB.translation_y, trB.translation_z,
537
- ra, rb
538
- );
2047
+ const ok = sphere_sphere_contact_array(sphere_result, a, 0, b, 0);
539
2048
 
540
2049
  if (!ok) return count;
541
2050
 
542
2051
  const nx = sphere_result[0], ny = sphere_result[1], nz = sphere_result[2];
2052
+ const ra = a[3], rb = b[3];
543
2053
 
544
2054
  // Sphere-sphere produces exactly one contact per pair; fid = 1
545
2055
  // identifies it as a real feature (distinguishes from "no info" = 0)
546
2056
  // and is trivially stable across frames. Witnesses are the surface
547
2057
  // points along the (unit) normal, scaled by each sphere's radius.
548
- return append_contact(count,
549
- trA.translation_x - nx * ra, trA.translation_y - ny * ra, trA.translation_z - nz * ra,
550
- trB.translation_x + nx * rb, trB.translation_y + ny * rb, trB.translation_z + nz * rb,
551
- nx, ny, nz, sphere_result[3], 1);
2058
+ const c = pair_contact;
2059
+ c[0] = a[0] - nx * ra; c[1] = a[1] - ny * ra; c[2] = a[2] - nz * ra;
2060
+ c[3] = b[0] + nx * rb; c[4] = b[1] + ny * rb; c[5] = b[2] + nz * rb;
2061
+ c[6] = nx; c[7] = ny; c[8] = nz;
2062
+ c[9] = sphere_result[3];
2063
+ c[10] = 1;
2064
+ return append_contact_record(count, c, 0);
552
2065
  }
553
2066
 
554
2067
  // sphere ↔ box
555
2068
  if ((isSphereA && isBoxB) || (isBoxA && isSphereB)) {
556
- const sphereTr = isSphereA ? trA : trB;
557
- const sphereShape = isSphereA ? shapeA : shapeB;
558
- const boxTr = isSphereA ? trB : trA;
559
- const boxShape = isSphereA ? shapeB : shapeA;
560
- const bh = boxShape.half_extents;
561
-
562
- const ok = sphere_box_contact(
563
- closed_form_result,
564
- sphereTr.translation_x, sphereTr.translation_y, sphereTr.translation_z, sphereShape.radius,
565
- boxTr.translation_x, boxTr.translation_y, boxTr.translation_z,
566
- boxTr.rotation_x, boxTr.rotation_y, boxTr.rotation_z, boxTr.rotation_w,
567
- bh.x, bh.y, bh.z
568
- );
2069
+ sphere_record_pack(pair_sphere_a, isSphereA ? trA : trB, isSphereA ? shapeA : shapeB);
2070
+ box_record_pack(pair_box_b, isSphereA ? trB : trA, (isSphereA ? shapeB : shapeA).half_extents);
569
2071
 
570
- if (!ok) return count;
2072
+ const ok = sphere_box_contact_array(closed_form_result, pair_sphere_a, 0, pair_box_b, 0);
571
2073
 
572
- let nx = closed_form_result[0], ny = closed_form_result[1], nz = closed_form_result[2];
2074
+ if (!ok) return count;
573
2075
 
574
- const depth = closed_form_result[3];
575
- const wsx = closed_form_result[4], wsy = closed_form_result[5], wsz = closed_form_result[6];
576
- const wbx = closed_form_result[7], wby = closed_form_result[8], wbz = closed_form_result[9];
577
2076
  // Feature id from the box-local voronoi region (1..27) — stable
578
2077
  // while the sphere stays on the same face / edge / vertex of the
579
2078
  // box, which is the steady-state case for resting / sliding contact.
2079
+ const fid = sphere_box_voronoi_fid_array(pair_sphere_a, 0, pair_box_b, 0);
580
2080
 
581
- const fid = sphere_box_voronoi_fid(
582
- sphereTr.translation_x, sphereTr.translation_y, sphereTr.translation_z,
583
- boxTr.translation_x, boxTr.translation_y, boxTr.translation_z,
584
- boxTr.rotation_x, boxTr.rotation_y, boxTr.rotation_z, boxTr.rotation_w,
585
- bh.x, bh.y, bh.z
586
- );
587
-
588
- if (isSphereA) {
589
- return append_contact(count, wsx, wsy, wsz, wbx, wby, wbz, nx, ny, nz, depth, fid);
590
- } else {
591
- return append_contact(count, wbx, wby, wbz, wsx, wsy, wsz, -nx, -ny, -nz, depth, fid);
592
- }
2081
+ // normal box → sphere, [4..6] on the sphere, [7..9] on the box
2082
+ return append_closed_form_contact(count, closed_form_result, isSphereA, fid);
593
2083
  }
594
2084
 
595
2085
  // box-box multi-point
596
2086
  if (isBoxA && isBoxB) {
597
- const ah = shapeA.half_extents;
598
- const bh = shapeB.half_extents;
599
- const ok = box_box_manifold(
600
- box_manifold_result,
601
- trA.translation_x, trA.translation_y, trA.translation_z,
602
- trA.rotation_x, trA.rotation_y, trA.rotation_z, trA.rotation_w,
603
- ah.x, ah.y, ah.z,
604
- trB.translation_x, trB.translation_y, trB.translation_z,
605
- trB.rotation_x, trB.rotation_y, trB.rotation_z, trB.rotation_w,
606
- bh.x, bh.y, bh.z
607
- );
2087
+ box_record_pack(pair_box_a, trA, shapeA.half_extents);
2088
+ box_record_pack(pair_box_b, trB, shapeB.half_extents);
2089
+ const ok = box_box_manifold_array(box_manifold_result, pair_box_a, 0, pair_box_b, 0);
608
2090
  if (!ok) return count;
609
- const nx = box_manifold_result[0], ny = box_manifold_result[1], nz = box_manifold_result[2];
610
2091
  const cc = box_manifold_result[3] | 0;
611
2092
  // Box-box manifolds: closed-form clipping doesn't expose stable
612
2093
  // per-contact feature ids (a contact migrates from "incident-vertex k"
@@ -616,13 +2097,7 @@ function dispatch_pair(count, colA, trA, colB, trB) {
616
2097
  // typically stay >>MATCH_TOL apart, so position matching is
617
2098
  // unambiguous frame-to-frame.
618
2099
  for (let k = 0; k < cc; k++) {
619
- const base = 4 + k * 7;
620
- count = append_contact(count,
621
- box_manifold_result[base], box_manifold_result[base + 1], box_manifold_result[base + 2],
622
- box_manifold_result[base + 3], box_manifold_result[base + 4], box_manifold_result[base + 5],
623
- nx, ny, nz,
624
- box_manifold_result[base + 6],
625
- 0);
2100
+ count = append_box_manifold_contact(count, box_manifold_result, 4 + k * 7);
626
2101
  }
627
2102
  return count;
628
2103
  }
@@ -630,100 +2105,74 @@ function dispatch_pair(count, colA, trA, colB, trB) {
630
2105
  // capsule-capsule (multi-point: near-parallel overlap emits both ends of
631
2106
  // the overlap interval; everything else the single closest-point contact)
632
2107
  if (isCapsuleA && isCapsuleB) {
633
- const a_shape = colA.shape, b_shape = colB.shape;
634
- const n_cc = capsule_capsule_multi_contacts(
635
- capsule_capsule_result,
636
- trA.translation_x, trA.translation_y, trA.translation_z,
637
- trA.rotation_x, trA.rotation_y, trA.rotation_z, trA.rotation_w,
638
- a_shape.radius, a_shape.height * 0.5,
639
- trB.translation_x, trB.translation_y, trB.translation_z,
640
- trB.rotation_x, trB.rotation_y, trB.rotation_z, trB.rotation_w,
641
- b_shape.radius, b_shape.height * 0.5
642
- );
2108
+ capsule_record_pack(pair_capsule_a, trA, shapeA);
2109
+ capsule_record_pack(pair_capsule_b, trB, shapeB);
2110
+ const n_cc = capsule_capsule_multi_contacts_array(capsule_capsule_result, pair_capsule_a, 0, pair_capsule_b, 0);
643
2111
  for (let k = 0; k < n_cc; k++) {
644
- const off = k * CAPSULE_CAPSULE_CONTACT_STRIDE;
645
2112
  // fid = k + 1: stable per interval end (1-based; 0 = no info).
646
- count = append_contact(count,
647
- capsule_capsule_result[off], capsule_capsule_result[off + 1], capsule_capsule_result[off + 2],
648
- capsule_capsule_result[off + 3], capsule_capsule_result[off + 4], capsule_capsule_result[off + 5],
649
- capsule_capsule_result[off + 6], capsule_capsule_result[off + 7], capsule_capsule_result[off + 8],
650
- capsule_capsule_result[off + 9], k + 1);
2113
+ count = append_manifold_contact(count, capsule_capsule_result, k * CAPSULE_CAPSULE_CONTACT_STRIDE, true, k + 1);
651
2114
  }
652
2115
  return count;
653
2116
  }
654
2117
 
655
2118
  // capsule ↔ sphere
656
2119
  if ((isCapsuleA && isSphereB) || (isSphereA && isCapsuleB)) {
657
- const capsuleTr = isCapsuleA ? trA : trB;
658
- const capsuleShape = isCapsuleA ? colA.shape : colB.shape;
659
- const sphereTr = isCapsuleA ? trB : trA;
660
- const sphereShape = isCapsuleA ? colB.shape : colA.shape;
661
- const ok = capsule_sphere_contact(
662
- closed_form_result,
663
- capsuleTr.translation_x, capsuleTr.translation_y, capsuleTr.translation_z,
664
- capsuleTr.rotation_x, capsuleTr.rotation_y, capsuleTr.rotation_z, capsuleTr.rotation_w,
665
- capsuleShape.radius, capsuleShape.height * 0.5,
666
- sphereTr.translation_x, sphereTr.translation_y, sphereTr.translation_z, sphereShape.radius
667
- );
2120
+ capsule_record_pack(pair_capsule_a, isCapsuleA ? trA : trB, isCapsuleA ? shapeA : shapeB);
2121
+ sphere_record_pack(pair_sphere_b, isCapsuleA ? trB : trA, isCapsuleA ? shapeB : shapeA);
2122
+ const ok = capsule_sphere_contact_array(closed_form_result, pair_capsule_a, 0, pair_sphere_b, 0);
668
2123
  if (!ok) return count;
669
- let nx = closed_form_result[0], ny = closed_form_result[1], nz = closed_form_result[2];
670
- const depth = closed_form_result[3];
671
- const cap_x = closed_form_result[4], cap_y = closed_form_result[5], cap_z = closed_form_result[6];
672
- const sph_x = closed_form_result[7], sph_y = closed_form_result[8], sph_z = closed_form_result[9];
673
- // Single contact per capsule-sphere pair; fid = 1.
674
- if (isCapsuleA) {
675
- return append_contact(count, cap_x, cap_y, cap_z, sph_x, sph_y, sph_z, nx, ny, nz, depth, 1);
676
- } else {
677
- return append_contact(count, sph_x, sph_y, sph_z, cap_x, cap_y, cap_z, -nx, -ny, -nz, depth, 1);
678
- }
2124
+ // normal sphere → capsule, [4..6] on the capsule, [7..9] on the sphere. Single contact per
2125
+ // capsule-sphere pair; fid = 1.
2126
+ return append_closed_form_contact(count, closed_form_result, isCapsuleA, 1);
679
2127
  }
680
2128
 
681
2129
  // capsule ↔ box (multi-point — 1 closest-segment + up to 2 cap-centres)
682
2130
  if ((isCapsuleA && isBoxB) || (isBoxA && isCapsuleB)) {
683
- const capsuleTr = isCapsuleA ? trA : trB;
684
- const capsuleShape = isCapsuleA ? shapeA : shapeB;
685
- const boxTr = isCapsuleA ? trB : trA;
686
- const boxShape = isCapsuleA ? shapeB : shapeA;
687
- const bh = boxShape.half_extents;
688
- const cc = capsule_box_multi_contacts(
689
- capsule_box_multi_result,
690
- capsuleTr.translation_x, capsuleTr.translation_y, capsuleTr.translation_z,
691
- capsuleTr.rotation_x, capsuleTr.rotation_y, capsuleTr.rotation_z, capsuleTr.rotation_w,
692
- capsuleShape.radius, capsuleShape.height * 0.5,
693
- boxTr.translation_x, boxTr.translation_y, boxTr.translation_z,
694
- boxTr.rotation_x, boxTr.rotation_y, boxTr.rotation_z, boxTr.rotation_w,
695
- bh.x, bh.y, bh.z
696
- );
2131
+ capsule_record_pack(pair_capsule_a, isCapsuleA ? trA : trB, isCapsuleA ? shapeA : shapeB);
2132
+ box_record_pack(pair_box_b, isCapsuleA ? trB : trA, (isCapsuleA ? shapeB : shapeA).half_extents);
2133
+ const cc = capsule_box_multi_contacts_array(capsule_box_multi_result, pair_capsule_a, 0, pair_box_b, 0);
697
2134
  if (cc === 0) return count;
698
2135
  // multi_result layout per contact: cap_x/y/z (A side), box_x/y/z (B side), nx/ny/nz, depth.
699
2136
  // Feature id per sub-contact: 1 = closest-segment, 2/3 = caps. The
700
2137
  // emission order from capsule_box_multi_contacts is stable across
701
2138
  // frames for the same geometric configuration.
702
2139
  for (let k = 0; k < cc; k++) {
703
- const o = k * CAPSULE_BOX_CONTACT_STRIDE;
704
- const cap_x = capsule_box_multi_result[o];
705
- const cap_y = capsule_box_multi_result[o + 1];
706
- const cap_z = capsule_box_multi_result[o + 2];
707
- const box_x = capsule_box_multi_result[o + 3];
708
- const box_y = capsule_box_multi_result[o + 4];
709
- const box_z = capsule_box_multi_result[o + 5];
710
- const nx = capsule_box_multi_result[o + 6];
711
- const ny = capsule_box_multi_result[o + 7];
712
- const nz = capsule_box_multi_result[o + 8];
713
- const depth = capsule_box_multi_result[o + 9];
714
- const sub_fid = k + 1;
715
- if (isCapsuleA) {
716
- count = append_contact(count, cap_x, cap_y, cap_z, box_x, box_y, box_z, nx, ny, nz, depth, sub_fid);
717
- } else {
718
- count = append_contact(count, box_x, box_y, box_z, cap_x, cap_y, cap_z, -nx, -ny, -nz, depth, sub_fid);
719
- }
2140
+ count = append_manifold_contact(count, capsule_box_multi_result, k * CAPSULE_BOX_CONTACT_STRIDE, isCapsuleA, k + 1);
720
2141
  }
721
2142
  return count;
722
2143
  }
723
2144
 
724
2145
  // convex hull ↔ convex hull (multi-point clipping via GJK + EPA + face-clip).
725
2146
  if (shapeA.isConvexHullShape3D === true && shapeB.isConvexHullShape3D === true) {
726
- return hull_pair_contacts(count, shapeA, trA, shapeB, trB, append_contact);
2147
+ return hull_pair_contacts(count, shapeA, trA, shapeB, trB);
2148
+ }
2149
+
2150
+ // box ↔ convex hull: a box is a polytope too, so it takes the same axis and face clip — a
2151
+ // face-on-face patch, not the generic fallback's single contact.
2152
+ if (isBoxA && shapeB.isConvexHullShape3D === true) {
2153
+ box_polytope_set(shapeA.half_extents);
2154
+ return hull_pair_contacts(count, box_polytope, trA, shapeB, trB);
2155
+ }
2156
+
2157
+ if (shapeA.isConvexHullShape3D === true && isBoxB) {
2158
+ box_polytope_set(shapeB.half_extents);
2159
+ return hull_pair_contacts(count, shapeA, trA, box_polytope, trB);
2160
+ }
2161
+
2162
+ // convex hull ↔ sphere: closed form, the contact on the line through the sphere's centre
2163
+ if ((shapeA.isConvexHullShape3D === true && isSphereB) || (isSphereA && shapeB.isConvexHullShape3D === true)) {
2164
+ const hull_is_a = isSphereB;
2165
+
2166
+ pose_pack(pair_pose, hull_is_a ? trA : trB);
2167
+ sphere_record_pack(pair_sphere_b, hull_is_a ? trB : trA, hull_is_a ? shapeB : shapeA);
2168
+
2169
+ const ok = hull_sphere_contact_array(closed_form_result, hull_is_a ? shapeA : shapeB, pair_pose, 0, pair_sphere_b, 0);
2170
+
2171
+ if (!ok) return count;
2172
+
2173
+ // normal from the hull toward the sphere; [4..6] on the sphere, [7..9] on the hull; one
2174
+ // contact per pair, fid = 1
2175
+ return append_closed_form_contact(count, closed_form_result, !hull_is_a, 1);
727
2176
  }
728
2177
 
729
2178
  // mesh ↔ mesh. A globally-convex mesh collides as a single cached hull
@@ -735,7 +2184,7 @@ function dispatch_pair(count, colA, trA, colB, trB) {
735
2184
  const hullA = get_mesh_convex_hull(shapeA);
736
2185
  const hullB = get_mesh_convex_hull(shapeB);
737
2186
  if (hullA !== null && hullB !== null) {
738
- return hull_pair_contacts(count, hullA, trA, hullB, trB, append_contact);
2187
+ return hull_pair_contacts(count, hullA, trA, hullB, trB);
739
2188
  }
740
2189
  return mesh_mesh_tet_contacts(count, shapeA, trA, shapeB, trB, append_contact);
741
2190
  }
@@ -762,34 +2211,41 @@ function dispatch_pair(count, colA, trA, colB, trB) {
762
2211
  const convex_col = isConcaveA ? colB : colA;
763
2212
  const convex_tr = isConcaveA ? trB : trA;
764
2213
 
2214
+ // The two bodies' poses, packed once for the calls below and the per-triangle kernels: passed
2215
+ // as numbers to a call that is not inlined, every one would be boxed (see
2216
+ // triangle_vertices_to_world).
2217
+ pose_pack(convex_pose, convex_tr);
2218
+ pose_pack(concave_pose, concave_tr);
2219
+
765
2220
  // 1. Convex shape's world AABB.
766
2221
  convex_col.shape.compute_bounding_box(concave_local_aabb);
767
- aabb3_transform_oriented(
768
- concave_world_aabb, 0,
769
- concave_local_aabb[0], concave_local_aabb[1], concave_local_aabb[2],
770
- concave_local_aabb[3], concave_local_aabb[4], concave_local_aabb[5],
771
- convex_tr.translation_x, convex_tr.translation_y, convex_tr.translation_z,
772
- convex_tr.rotation_x, convex_tr.rotation_y, convex_tr.rotation_z, convex_tr.rotation_w
773
- );
2222
+ aabb3_transform_oriented_array(concave_world_aabb, 0, concave_local_aabb, 0, convex_pose, 0);
774
2223
 
775
2224
  // 2. Project into concave's body-local frame.
776
- aabb3_transform_oriented_inverse(
777
- concave_query_aabb, 0,
778
- concave_world_aabb,
779
- concave_tr.translation_x, concave_tr.translation_y, concave_tr.translation_z,
780
- concave_tr.rotation_x, concave_tr.rotation_y, concave_tr.rotation_z, concave_tr.rotation_w
781
- );
2225
+ aabb3_transform_oriented_inverse_array(concave_query_aabb, 0, concave_world_aabb, concave_pose, 0);
782
2226
 
783
2227
  // 3. Decompose concave shape into triangles overlapping the query.
784
- // The enumerator returns the TRUE overlap count — clamp to the
785
- // scratch capacity (overflow writes were dropped; reading past the
786
- // buffer would poison the manifold with NaN).
787
- let tri_count = decompose_to_triangles(
788
- triangle_buffer, 0, concave_col.shape,
789
- concave_query_aabb[0], concave_query_aabb[1], concave_query_aabb[2],
790
- concave_query_aabb[3], concave_query_aabb[4], concave_query_aabb[5]
791
- );
792
- if (tri_count > MAX_TRIANGLES_PER_PAIR) tri_count = MAX_TRIANGLES_PER_PAIR;
2228
+ // The enumerator returns the TRUE overlap count; when that did not
2229
+ // fit, grow and enumerate again (deterministic, so once suffices).
2230
+ let tri_count = decompose_to_triangles_array(triangle_buffer, 0, concave_col.shape, concave_query_aabb, 0);
2231
+ let tri_capacity = triangle_buffer.length / TRIANGLE_FLOAT_STRIDE;
2232
+ if (tri_count > tri_capacity && tri_capacity < MAX_TRIANGLES_PER_PAIR) {
2233
+ tri_capacity = Math.min(MAX_TRIANGLES_PER_PAIR, Math.max(tri_count, tri_capacity * 2));
2234
+ triangle_buffer = new Float64Array(tri_capacity * TRIANGLE_FLOAT_STRIDE);
2235
+ tri_count = decompose_to_triangles_array(triangle_buffer, 0, concave_col.shape, concave_query_aabb, 0);
2236
+ }
2237
+ if (tri_count > tri_capacity) {
2238
+ // Past the bound: overflow writes were dropped, so never read them.
2239
+ if (!triangle_overflow_reported) {
2240
+ triangle_overflow_reported = true;
2241
+ console.warn(
2242
+ `narrowphase: a ${convex_col.shape.constructor.name} overlaps ${tri_count} triangles of a`
2243
+ + ` ${concave_col.shape.constructor.name}; ${tri_capacity} are collided against and the`
2244
+ + ` rest are not. Coarsen the collision surface under it, or split the body.`
2245
+ );
2246
+ }
2247
+ tri_count = tri_capacity;
2248
+ }
793
2249
  if (tri_count === 0) return count;
794
2250
 
795
2251
  // 4. Set up the convex side once; the concave side gets a
@@ -797,11 +2253,7 @@ function dispatch_pair(count, colA, trA, colB, trB) {
797
2253
  // the concave side as our internal "A" so EPA's sign-check
798
2254
  // convention matches the convex fallback below; the final
799
2255
  // append_contact() swaps if the original A was the convex one.
800
- posed_b.setup_components(
801
- convex_col.shape,
802
- convex_tr.translation_x, convex_tr.translation_y, convex_tr.translation_z,
803
- convex_tr.rotation_x, convex_tr.rotation_y, convex_tr.rotation_z, convex_tr.rotation_w
804
- );
2256
+ posed_b.setup_pose(convex_col.shape, convex_pose, 0);
805
2257
  posed_a.shape = triangle_shape;
806
2258
  posed_a.px = concave_tr.translation_x;
807
2259
  posed_a.py = concave_tr.translation_y;
@@ -817,12 +2269,6 @@ function dispatch_pair(count, colA, trA, colB, trB) {
817
2269
  const convex_wy = convex_tr.translation_y;
818
2270
  const convex_wz = convex_tr.translation_z;
819
2271
 
820
- // Track the candidate-buffer index at the start of this
821
- // concave dispatch — the per-triangle dedup pass scans from
822
- // here to the current count, ignoring contacts from earlier
823
- // collider pairs in the same body pair.
824
- const pair_start_count = count;
825
-
826
2272
  // Pre-compute concave's rotation components for the q · v · q⁻¹
827
2273
  // rotations done per-triangle below (face normal + centroid).
828
2274
  const cqx = concave_tr.rotation_x;
@@ -858,6 +2304,49 @@ function dispatch_pair(count, colA, trA, colB, trB) {
858
2304
  const isCapsuleConvex = convex_col.shape.isCapsuleShape3D === true;
859
2305
  const capsule_shape = isCapsuleConvex ? convex_col.shape : null;
860
2306
 
2307
+ if (isSphereConvex) {
2308
+ convex_record[0] = convex_wx;
2309
+ convex_record[1] = convex_wy;
2310
+ convex_record[2] = convex_wz;
2311
+ convex_record[3] = sphere_radius;
2312
+ } else if (isBoxConvex || isCapsuleConvex) {
2313
+ pose_pack(convex_record, convex_tr);
2314
+
2315
+ if (isBoxConvex) {
2316
+ convex_record[7] = box_half_extents.x;
2317
+ convex_record[8] = box_half_extents.y;
2318
+ convex_record[9] = box_half_extents.z;
2319
+ } else {
2320
+ convex_record[7] = capsule_shape.radius;
2321
+ convex_record[8] = capsule_shape.height * 0.5;
2322
+ }
2323
+ }
2324
+
2325
+ // The per-triangle dedup compares each new contact with the earlier
2326
+ // contacts of THIS concave dispatch only — not those of earlier
2327
+ // collider pairs in the same body pair — through an index that starts
2328
+ // empty here (see dedup_contains). The sphere path matches on the
2329
+ // normal alone, every other path on the normal and the A-side point.
2330
+ dedup_begin(isSphereConvex);
2331
+
2332
+ // Contacts on an edge or a vertex of a triangle wait here until every triangle has been
2333
+ // collided, then go through Box3D's internal-edge rules (see "Internal edges" above).
2334
+ tentative_count = 0;
2335
+ face_triangle_count = 0;
2336
+
2337
+ // where a round body touches a plane, for the contacts taken as a plane's
2338
+ if (isSphereConvex) {
2339
+ round_core[ROUND_RADIUS] = sphere_radius;
2340
+ round_core[ROUND_HALF] = 0;
2341
+ round_core_place(convex_tr);
2342
+ } else if (isCapsuleConvex) {
2343
+ round_core[ROUND_RADIUS] = capsule_shape.radius;
2344
+ round_core[ROUND_HALF] = capsule_shape.height * 0.5;
2345
+ round_core_place(convex_tr);
2346
+ } else {
2347
+ round_core[ROUND_RADIUS] = 0;
2348
+ }
2349
+
861
2350
  for (let i = 0; i < tri_count; i++) {
862
2351
  const tri_offset = i * TRIANGLE_FLOAT_STRIDE;
863
2352
  triangle_shape.bind(triangle_buffer, tri_offset);
@@ -906,23 +2395,9 @@ function dispatch_pair(count, colA, trA, colB, trB) {
906
2395
 
907
2396
  // Rotate each triangle vertex from concave-local to world
908
2397
  // via the shared helper.
909
- triangle_vertices_to_world(
910
- scratch_tri_world,
911
- ax, ay, az, bx, by, bz, cx_v, cy_v, cz_v,
912
- cqx, cqy, cqz, cqw,
913
- c_pos_x, c_pos_y, c_pos_z
914
- );
915
- const ax_w = scratch_tri_world[0], ay_w = scratch_tri_world[1], az_w = scratch_tri_world[2];
916
- const bx_w = scratch_tri_world[3], by_w = scratch_tri_world[4], bz_w = scratch_tri_world[5];
917
- const cx_w = scratch_tri_world[6], cy_w = scratch_tri_world[7], cz_w = scratch_tri_world[8];
918
-
919
- const ok = sphere_triangle_contact(
920
- sphere_triangle_result,
921
- convex_wx, convex_wy, convex_wz, sphere_radius,
922
- ax_w, ay_w, az_w,
923
- bx_w, by_w, bz_w,
924
- cx_w, cy_w, cz_w
925
- );
2398
+ triangle_vertices_to_world(scratch_tri_world, triangle_buffer, tri_offset, concave_pose);
2399
+
2400
+ const ok = sphere_triangle_contact_array(sphere_triangle_result, convex_record, 0, scratch_tri_world, 0);
926
2401
 
927
2402
  if (!ok) continue;
928
2403
 
@@ -939,7 +2414,8 @@ function dispatch_pair(count, colA, trA, colB, trB) {
939
2414
  // outward side of the triangle. If the contact normal
940
2415
  // opposes the face normal, the sphere is behind /
941
2416
  // inside the solid — skip rather than push it deeper.
942
- if (n_t2s_x * fnx_w + n_t2s_y * fny_w + n_t2s_z * fnz_w <= 0) continue;
2417
+ const n_dot_f = n_t2s_x * fnx_w + n_t2s_y * fny_w + n_t2s_z * fnz_w;
2418
+ if (n_dot_f <= 0) continue;
943
2419
 
944
2420
  // Stored normal convention is "B → A". `nx, ny, nz` here
945
2421
  // points convex → concave (which the existing EPA branch
@@ -953,44 +2429,48 @@ function dispatch_pair(count, colA, trA, colB, trB) {
953
2429
  const stored_nx_s = isConcaveA ? nx_s : -nx_s;
954
2430
  const stored_ny_s = isConcaveA ? ny_s : -ny_s;
955
2431
  const stored_nz_s = isConcaveA ? nz_s : -nz_s;
956
- let is_duplicate_s = false;
957
- for (let k = pair_start_count; k < count; k++) {
958
- const ko = k * CANDIDATE_STRIDE;
959
- const dnx = candidates[ko + 6] - stored_nx_s;
960
- const dny = candidates[ko + 7] - stored_ny_s;
961
- const dnz = candidates[ko + 8] - stored_nz_s;
962
- if (dnx * dnx + dny * dny + dnz * dnz < 0.001) {
963
- is_duplicate_s = true;
964
- break;
965
- }
966
- }
967
- if (is_duplicate_s) continue;
968
2432
 
969
2433
  // Surface witnesses from the closed-form solver:
970
2434
  // result[4..6] = sphere surface point (convex side)
971
2435
  // result[7..9] = triangle closest point (concave side)
972
- // Use the actual witnesses rather than body centres —
973
- // unlike the EPA fallback (which uses body centres
974
- // because flat-faced support witnesses can be arbitrarily
975
- // far from the contact patch), closest-point-on-triangle
976
- // is exact and lies on the contact patch.
2436
+ // Closest-point-on-triangle is exact and lies on the
2437
+ // contact patch.
977
2438
  const sphere_wx = sphere_triangle_result[4];
978
2439
  const sphere_wy = sphere_triangle_result[5];
979
2440
  const sphere_wz = sphere_triangle_result[6];
980
2441
  const tri_wx = sphere_triangle_result[7];
981
2442
  const tri_wy = sphere_triangle_result[8];
982
2443
  const tri_wz = sphere_triangle_result[9];
983
- if (isConcaveA) {
984
- count = append_contact(count,
985
- tri_wx, tri_wy, tri_wz,
986
- sphere_wx, sphere_wy, sphere_wz,
987
- nx_s, ny_s, nz_s, sd, tri_fid);
988
- } else {
989
- count = append_contact(count,
990
- sphere_wx, sphere_wy, sphere_wz,
991
- tri_wx, tri_wy, tri_wz,
992
- -nx_s, -ny_s, -nz_s, sd, tri_fid);
2444
+
2445
+ const c = staged_contact;
2446
+ c[0] = isConcaveA ? tri_wx : sphere_wx;
2447
+ c[1] = isConcaveA ? tri_wy : sphere_wy;
2448
+ c[2] = isConcaveA ? tri_wz : sphere_wz;
2449
+ c[3] = isConcaveA ? sphere_wx : tri_wx;
2450
+ c[4] = isConcaveA ? sphere_wy : tri_wy;
2451
+ c[5] = isConcaveA ? sphere_wz : tri_wz;
2452
+ c[6] = stored_nx_s; c[7] = stored_ny_s; c[8] = stored_nz_s;
2453
+ c[9] = sd;
2454
+ c[10] = tri_fid;
2455
+
2456
+ // The nearest point on an edge or a vertex: it waits for the others, nearest first.
2457
+ // (cos(n, face) < FACE_CONTACT_COS, squared: n is unit, n · face > 0.)
2458
+ if (n_dot_f * n_dot_f < FACE_CONTACT_COS * FACE_CONTACT_COS * (fnx_w * fnx_w + fny_w * fny_w + fnz_w * fnz_w)) {
2459
+ const distance = sphere_radius - sd;
2460
+
2461
+ c[11] = distance * distance;
2462
+ tentative_add(c, i, triangle_facing_feature(scratch_tri_world, sphere_triangle_result, 0));
2463
+ continue;
993
2464
  }
2465
+
2466
+ face_triangle_add(i);
2467
+
2468
+ // a duplicate is any earlier contact of this dispatch with the same normal (keyed by
2469
+ // normal: the witness is not looked at)
2470
+ if (dedup_contains(c, 0)) continue;
2471
+
2472
+ count = append_contact_record(count, c, 0);
2473
+ dedup_insert(count - 1);
994
2474
  continue;
995
2475
  }
996
2476
 
@@ -998,25 +2478,9 @@ function dispatch_pair(count, colA, trA, colB, trB) {
998
2478
  if (isBoxConvex) {
999
2479
  // Rotate each triangle vertex from concave-local to world
1000
2480
  // via the shared helper.
1001
- triangle_vertices_to_world(
1002
- scratch_tri_world,
1003
- ax, ay, az, bx, by, bz, cx_v, cy_v, cz_v,
1004
- cqx, cqy, cqz, cqw,
1005
- c_pos_x, c_pos_y, c_pos_z
1006
- );
1007
- const ax_w = scratch_tri_world[0], ay_w = scratch_tri_world[1], az_w = scratch_tri_world[2];
1008
- const bx_w = scratch_tri_world[3], by_w = scratch_tri_world[4], bz_w = scratch_tri_world[5];
1009
- const cx_w = scratch_tri_world[6], cy_w = scratch_tri_world[7], cz_w = scratch_tri_world[8];
1010
-
1011
- const ok = box_triangle_contact(
1012
- box_triangle_result,
1013
- convex_tr.translation_x, convex_tr.translation_y, convex_tr.translation_z,
1014
- convex_tr.rotation_x, convex_tr.rotation_y, convex_tr.rotation_z, convex_tr.rotation_w,
1015
- box_half_extents.x, box_half_extents.y, box_half_extents.z,
1016
- ax_w, ay_w, az_w,
1017
- bx_w, by_w, bz_w,
1018
- cx_w, cy_w, cz_w
1019
- );
2481
+ triangle_vertices_to_world(scratch_tri_world, triangle_buffer, tri_offset, concave_pose);
2482
+
2483
+ const ok = box_triangle_contact_array(box_triangle_result, convex_record, 0, scratch_tri_world, 0);
1020
2484
  if (!ok) continue;
1021
2485
 
1022
2486
  // Same convention as sphere_triangle: result normal points
@@ -1030,7 +2494,8 @@ function dispatch_pair(count, colA, trA, colB, trB) {
1030
2494
  // One-sided rejection: the box must lie on the outward
1031
2495
  // side of the triangle. If the contact normal opposes
1032
2496
  // the face normal, the box is inside the solid → skip.
1033
- if (n_t2box_x * fnx_w + n_t2box_y * fny_w + n_t2box_z * fnz_w <= 0) continue;
2497
+ const n_dot_f = n_t2box_x * fnx_w + n_t2box_y * fny_w + n_t2box_z * fnz_w;
2498
+ if (n_dot_f <= 0) continue;
1034
2499
 
1035
2500
  const nx_b = -n_t2box_x;
1036
2501
  const ny_b = -n_t2box_y;
@@ -1039,11 +2504,23 @@ function dispatch_pair(count, colA, trA, colB, trB) {
1039
2504
  const stored_ny_b = isConcaveA ? ny_b : -ny_b;
1040
2505
  const stored_nz_b = isConcaveA ? nz_b : -nz_b;
1041
2506
 
2507
+ const cc = box_triangle_result[3] | 0;
2508
+
2509
+ // A face contact: on the triangle's face, or on a face of the box lying within 60° of
2510
+ // it. An edge-edge axis, or a box face standing up off the triangle, is tentative, and
2511
+ // waits for the internal-edge rules.
2512
+ const axis = box_triangle_result[BOX_TRIANGLE_AXIS_OFFSET];
2513
+ let face = axis === BOX_TRIANGLE_AXIS_TRIANGLE_FACE;
2514
+
2515
+ if (axis === BOX_TRIANGLE_AXIS_BOX_FACE) {
2516
+ // cos(n, face) > HULL_FACE_COS, squared: n is unit, n · face > 0
2517
+ face = n_dot_f * n_dot_f > HULL_FACE_COS * HULL_FACE_COS * (fnx_w * fnx_w + fny_w * fny_w + fnz_w * fnz_w);
2518
+ }
2519
+
1042
2520
  // Emit each clipped contact. The contact count from
1043
2521
  // box_triangle_contact is at most 4; combined with the
1044
2522
  // existing per-pair candidate cap (64 entries), tens of
1045
2523
  // overlapping triangles per pair are safely accommodated.
1046
- const cc = box_triangle_result[3] | 0;
1047
2524
  for (let k = 0; k < cc; k++) {
1048
2525
  const base = 4 + k * 7;
1049
2526
  const t_wx = box_triangle_result[base];
@@ -1056,45 +2533,32 @@ function dispatch_pair(count, colA, trA, colB, trB) {
1056
2533
 
1057
2534
  // Dedup against earlier candidates in THIS dispatch
1058
2535
  // (adjacent triangles can produce coincident contacts
1059
- // — same as the sphere and EPA paths).
1060
- let is_duplicate_k = false;
1061
- for (let q = pair_start_count; q < count; q++) {
1062
- const qo = q * CANDIDATE_STRIDE;
1063
- const dnx = candidates[qo + 6] - stored_nx_b;
1064
- const dny = candidates[qo + 7] - stored_ny_b;
1065
- const dnz = candidates[qo + 8] - stored_nz_b;
1066
- if (dnx * dnx + dny * dny + dnz * dnz < 0.001) {
1067
- // Same normal; check positions too (a
1068
- // multi-point manifold has distinct points
1069
- // with the same normal — those should NOT
1070
- // be deduped). Compare the world-A side
1071
- // (triangle surface or box surface depending
1072
- // on dispatcher swap).
1073
- const pa_x = isConcaveA ? t_wx : b_wx;
1074
- const pa_y = isConcaveA ? t_wy : b_wy;
1075
- const pa_z = isConcaveA ? t_wz : b_wz;
1076
- const dpx = candidates[qo] - pa_x;
1077
- const dpy = candidates[qo + 1] - pa_y;
1078
- const dpz = candidates[qo + 2] - pa_z;
1079
- if (dpx * dpx + dpy * dpy + dpz * dpz < 1e-6) {
1080
- is_duplicate_k = true;
1081
- break;
1082
- }
1083
- }
1084
- }
1085
- if (is_duplicate_k) continue;
1086
-
1087
- if (isConcaveA) {
1088
- count = append_contact(count,
1089
- t_wx, t_wy, t_wz,
1090
- b_wx, b_wy, b_wz,
1091
- nx_b, ny_b, nz_b, d_k, tri_fid);
1092
- } else {
1093
- count = append_contact(count,
1094
- b_wx, b_wy, b_wz,
1095
- t_wx, t_wy, t_wz,
1096
- -nx_b, -ny_b, -nz_b, d_k, tri_fid);
2536
+ // — same as the sphere and EPA paths): same normal AND
2537
+ // same world-A point (triangle surface or box surface
2538
+ // depending on dispatcher swap) — a multi-point manifold
2539
+ // has distinct points with the same normal, and those
2540
+ // should NOT be deduped.
2541
+ const c = staged_contact;
2542
+ c[0] = isConcaveA ? t_wx : b_wx;
2543
+ c[1] = isConcaveA ? t_wy : b_wy;
2544
+ c[2] = isConcaveA ? t_wz : b_wz;
2545
+ c[3] = isConcaveA ? b_wx : t_wx;
2546
+ c[4] = isConcaveA ? b_wy : t_wy;
2547
+ c[5] = isConcaveA ? b_wz : t_wz;
2548
+ c[6] = stored_nx_b; c[7] = stored_ny_b; c[8] = stored_nz_b;
2549
+ c[9] = d_k;
2550
+ c[10] = tri_fid;
2551
+
2552
+ if (!face) {
2553
+ c[11] = 0;
2554
+ tentative_add(c, i, triangle_witness_feature(scratch_tri_world, box_triangle_result, base));
2555
+ continue;
1097
2556
  }
2557
+
2558
+ if (dedup_contains(c, 0)) continue;
2559
+
2560
+ count = append_contact_record(count, c, 0);
2561
+ dedup_insert(count - 1);
1098
2562
  }
1099
2563
  continue;
1100
2564
  }
@@ -1103,25 +2567,9 @@ function dispatch_pair(count, colA, trA, colB, trB) {
1103
2567
  if (isCapsuleConvex) {
1104
2568
  // Rotate each triangle vertex from concave-local to world
1105
2569
  // via the shared helper.
1106
- triangle_vertices_to_world(
1107
- scratch_tri_world,
1108
- ax, ay, az, bx, by, bz, cx_v, cy_v, cz_v,
1109
- cqx, cqy, cqz, cqw,
1110
- c_pos_x, c_pos_y, c_pos_z
1111
- );
1112
- const ax_w = scratch_tri_world[0], ay_w = scratch_tri_world[1], az_w = scratch_tri_world[2];
1113
- const bx_w = scratch_tri_world[3], by_w = scratch_tri_world[4], bz_w = scratch_tri_world[5];
1114
- const cx_w = scratch_tri_world[6], cy_w = scratch_tri_world[7], cz_w = scratch_tri_world[8];
1115
-
1116
- const cap_cc = capsule_triangle_contact(
1117
- capsule_triangle_result,
1118
- convex_tr.translation_x, convex_tr.translation_y, convex_tr.translation_z,
1119
- convex_tr.rotation_x, convex_tr.rotation_y, convex_tr.rotation_z, convex_tr.rotation_w,
1120
- capsule_shape.radius, capsule_shape.height * 0.5,
1121
- ax_w, ay_w, az_w,
1122
- bx_w, by_w, bz_w,
1123
- cx_w, cy_w, cz_w
1124
- );
2570
+ triangle_vertices_to_world(scratch_tri_world, triangle_buffer, tri_offset, concave_pose);
2571
+
2572
+ const cap_cc = capsule_triangle_contact_array(capsule_triangle_result, convex_record, 0, scratch_tri_world, 0);
1125
2573
  if (cap_cc === 0) continue;
1126
2574
 
1127
2575
  // Each sub-contact's normal can be distinct (a flat
@@ -1144,7 +2592,8 @@ function dispatch_pair(count, colA, trA, colB, trB) {
1144
2592
 
1145
2593
  // One-sided face-normal rejection: capsule must be
1146
2594
  // on the outward side of the triangle.
1147
- if (n_t2cap_x * fnx_w + n_t2cap_y * fny_w + n_t2cap_z * fnz_w <= 0) continue;
2595
+ const n_dot_f = n_t2cap_x * fnx_w + n_t2cap_y * fny_w + n_t2cap_z * fnz_w;
2596
+ if (n_dot_f <= 0) continue;
1148
2597
 
1149
2598
  // Stored normal "convex → concave" = "capsule →
1150
2599
  // triangle" = negated t2cap.
@@ -1155,39 +2604,31 @@ function dispatch_pair(count, colA, trA, colB, trB) {
1155
2604
  const stored_ny_c = isConcaveA ? ny_c : -ny_c;
1156
2605
  const stored_nz_c = isConcaveA ? nz_c : -nz_c;
1157
2606
 
1158
- // Dedup against earlier candidates in THIS dispatch.
1159
- let is_duplicate_c = false;
1160
- for (let q = pair_start_count; q < count; q++) {
1161
- const qo = q * CANDIDATE_STRIDE;
1162
- const dnx = candidates[qo + 6] - stored_nx_c;
1163
- const dny = candidates[qo + 7] - stored_ny_c;
1164
- const dnz = candidates[qo + 8] - stored_nz_c;
1165
- if (dnx * dnx + dny * dny + dnz * dnz < 0.001) {
1166
- const pa_x = isConcaveA ? ct_x : cap_x;
1167
- const pa_y = isConcaveA ? ct_y : cap_y;
1168
- const pa_z = isConcaveA ? ct_z : cap_z;
1169
- const dpx = candidates[qo] - pa_x;
1170
- const dpy = candidates[qo + 1] - pa_y;
1171
- const dpz = candidates[qo + 2] - pa_z;
1172
- if (dpx * dpx + dpy * dpy + dpz * dpz < 1e-6) {
1173
- is_duplicate_c = true;
1174
- break;
1175
- }
1176
- }
1177
- }
1178
- if (is_duplicate_c) continue;
1179
-
1180
- if (isConcaveA) {
1181
- count = append_contact(count,
1182
- ct_x, ct_y, ct_z,
1183
- cap_x, cap_y, cap_z,
1184
- nx_c, ny_c, nz_c, d_c, tri_fid);
1185
- } else {
1186
- count = append_contact(count,
1187
- cap_x, cap_y, cap_z,
1188
- ct_x, ct_y, ct_z,
1189
- -nx_c, -ny_c, -nz_c, d_c, tri_fid);
2607
+ const c = staged_contact;
2608
+ c[0] = isConcaveA ? ct_x : cap_x;
2609
+ c[1] = isConcaveA ? ct_y : cap_y;
2610
+ c[2] = isConcaveA ? ct_z : cap_z;
2611
+ c[3] = isConcaveA ? cap_x : ct_x;
2612
+ c[4] = isConcaveA ? cap_y : ct_y;
2613
+ c[5] = isConcaveA ? cap_z : ct_z;
2614
+ c[6] = stored_nx_c; c[7] = stored_ny_c; c[8] = stored_nz_c;
2615
+ c[9] = d_c;
2616
+ c[10] = tri_fid;
2617
+
2618
+ // Each sub-contact is a closest-point contact: on the face when its normal is the
2619
+ // face's; otherwise on an edge or a vertex, and it waits for the internal-edge rules.
2620
+ if (n_dot_f * n_dot_f < FACE_CONTACT_COS * FACE_CONTACT_COS * (fnx_w * fnx_w + fny_w * fny_w + fnz_w * fnz_w)) {
2621
+ c[11] = 0;
2622
+ tentative_add(c, i, triangle_witness_feature(scratch_tri_world, capsule_triangle_result, base + 3));
2623
+ continue;
1190
2624
  }
2625
+
2626
+ // Dedup against earlier candidates in THIS dispatch:
2627
+ // same normal AND same world-A point.
2628
+ if (dedup_contains(c, 0)) continue;
2629
+
2630
+ count = append_contact_record(count, c, 0);
2631
+ dedup_insert(count - 1);
1191
2632
  }
1192
2633
  continue;
1193
2634
  }
@@ -1260,58 +2701,105 @@ function dispatch_pair(count, colA, trA, colB, trB) {
1260
2701
  const ny = -ey * inv;
1261
2702
  const nz = -ez * inv;
1262
2703
 
1263
- // Dedup against earlier contacts emitted in THIS dispatch.
1264
- // Adjacent triangles (heightmap cells sharing a diagonal;
1265
- // mesh triangles sharing an edge or vertex) often report
1266
- // identical contacts (same normal, same body-centre
1267
- // application points), and feeding duplicates to the
1268
- // sequential-impulse solver makes it escalate the impulse
1269
- // across iterations without bound. We deduplicate by
1270
- // contact normal within a small angular threshold: same
1271
- // body pair + same normal + same body-centre positions =
1272
- // the same physical contact, only one copy belongs in the
1273
- // manifold.
1274
- const stored_nx = isConcaveA ? nx : -nx;
1275
- const stored_ny = isConcaveA ? ny : -ny;
1276
- const stored_nz = isConcaveA ? nz : -nz;
1277
- let is_duplicate = false;
1278
- for (let k = pair_start_count; k < count; k++) {
1279
- const ko = k * CANDIDATE_STRIDE;
1280
- const dnx = candidates[ko + 6] - stored_nx;
1281
- const dny = candidates[ko + 7] - stored_ny;
1282
- const dnz = candidates[ko + 8] - stored_nz;
1283
- if (dnx * dnx + dny * dny + dnz * dnz < 0.001) {
1284
- is_duplicate = true;
1285
- break;
1286
- }
2704
+ // Contact points: the triangle's supporting feature toward the convex body (the triangle
2705
+ // itself, for a body lying on it) and the body's toward the triangle, clipped against each
2706
+ // other — see support_feature_manifold. These used to be the two bodies' ORIGINS: under the
2707
+ // contact only for a body directly above the concave body's origin, and a lever arm
2708
+ // reaching across to it everywhere else. Internally the triangle is A and the convex body
2709
+ // B, so (nx, ny, nz) — convex → triangle — is B → A.
2710
+ triangle_vertices_to_world(scratch_tri_world, triangle_buffer, tri_offset, concave_pose);
2711
+
2712
+ const fn_inv = 1 / Math.sqrt(fnx_w * fnx_w + fny_w * fny_w + fnz_w * fnz_w);
2713
+
2714
+ triangle_feature(feature_a,
2715
+ scratch_tri_world[0], scratch_tri_world[1], scratch_tri_world[2],
2716
+ scratch_tri_world[3], scratch_tri_world[4], scratch_tri_world[5],
2717
+ scratch_tri_world[6], scratch_tri_world[7], scratch_tri_world[8],
2718
+ fnx_w * fn_inv, fny_w * fn_inv, fnz_w * fn_inv,
2719
+ -nx, -ny, -nz
2720
+ );
2721
+ support_feature(feature_b, convex_col.shape,
2722
+ convex_tr.translation_x, convex_tr.translation_y, convex_tr.translation_z,
2723
+ convex_tr.rotation_x, convex_tr.rotation_y, convex_tr.rotation_z, convex_tr.rotation_w,
2724
+ nx, ny, nz
2725
+ );
2726
+ feature_manifold(feature_manifold_result, feature_a, feature_b, nx, ny, nz, depth);
2727
+
2728
+ const mnx = feature_manifold_result[0];
2729
+ const mny = feature_manifold_result[1];
2730
+ const mnz = feature_manifold_result[2];
2731
+ const stored_nx = isConcaveA ? mnx : -mnx;
2732
+ const stored_ny = isConcaveA ? mny : -mny;
2733
+ const stored_nz = isConcaveA ? mnz : -mnz;
2734
+
2735
+ const fc = feature_manifold_result[3];
2736
+
2737
+ // A face contact: the triangle's face facing the body — its polygon clipped, on its own
2738
+ // normal — or a face of the body within 60° of the triangle's plane. Anything else is
2739
+ // tentative, and waits for the internal-edge rules. The triangle's polygon on some other
2740
+ // normal is not its face: feature_manifold falls back to the EPA axis when the clip of the
2741
+ // two features finds nothing, and that axis off an edge of the triangle is a ghost.
2742
+ // (mn is convex → triangle.)
2743
+ let face = feature_a.kind === FEATURE_POLYGON
2744
+ && -(mnx * fnx_w + mny * fny_w + mnz * fnz_w) * fn_inv >= FACE_CONTACT_COS;
2745
+
2746
+ if (!face && feature_b.kind === FEATURE_POLYGON) {
2747
+ face = -(mnx * fnx_w + mny * fny_w + mnz * fnz_w) * fn_inv > HULL_FACE_COS;
1287
2748
  }
1288
- if (is_duplicate) continue;
1289
-
1290
- // Contact application points: body centres rather than
1291
- // the triangle's centroid. The existing GJK+EPA fallback
1292
- // (below) uses the same convention — for vertical contacts
1293
- // (sphere/box on a flat surface, the common case), the
1294
- // lever arm r × n vanishes and the impulse resolves
1295
- // cleanly.
1296
- if (isConcaveA) {
1297
- count = append_contact(count,
1298
- c_pos_x, c_pos_y, c_pos_z,
1299
- convex_wx, convex_wy, convex_wz,
1300
- nx, ny, nz, depth, tri_fid);
1301
- } else {
1302
- count = append_contact(count,
1303
- convex_wx, convex_wy, convex_wz,
1304
- c_pos_x, c_pos_y, c_pos_z,
1305
- -nx, -ny, -nz, depth, tri_fid);
2749
+
2750
+ for (let k = 0; k < fc; k++) {
2751
+ const base = 4 + k * 7;
2752
+ // [base..+2] on the triangle, [base+3..+5] on the convex body
2753
+ const t_wx = feature_manifold_result[base];
2754
+ const t_wy = feature_manifold_result[base + 1];
2755
+ const t_wz = feature_manifold_result[base + 2];
2756
+ const c_wx = feature_manifold_result[base + 3];
2757
+ const c_wy = feature_manifold_result[base + 4];
2758
+ const c_wz = feature_manifold_result[base + 5];
2759
+ const d_k = feature_manifold_result[base + 6];
2760
+
2761
+ // Dedup against earlier contacts in THIS dispatch: adjacent triangles report the
2762
+ // same contact where they share an edge or a vertex, and duplicates make the
2763
+ // sequential-impulse solver escalate. Same normal AND same point — a body resting
2764
+ // across several coplanar triangles has distinct contacts sharing one normal.
2765
+ const c = staged_contact;
2766
+ c[0] = isConcaveA ? t_wx : c_wx;
2767
+ c[1] = isConcaveA ? t_wy : c_wy;
2768
+ c[2] = isConcaveA ? t_wz : c_wz;
2769
+ c[3] = isConcaveA ? c_wx : t_wx;
2770
+ c[4] = isConcaveA ? c_wy : t_wy;
2771
+ c[5] = isConcaveA ? c_wz : t_wz;
2772
+ c[6] = stored_nx; c[7] = stored_ny; c[8] = stored_nz;
2773
+ c[9] = d_k;
2774
+ c[10] = tri_fid;
2775
+
2776
+ if (!face) {
2777
+ c[11] = 0;
2778
+ tentative_add(c, i, triangle_witness_feature(scratch_tri_world, feature_manifold_result, base));
2779
+ continue;
2780
+ }
2781
+
2782
+ if (dedup_contains(c, 0)) continue;
2783
+
2784
+ count = append_contact_record(count, c, 0);
2785
+ dedup_insert(count - 1);
1306
2786
  }
1307
2787
  }
1308
2788
 
2789
+ // The contacts on edges and vertices: kept where the surface has the edge; where it has none,
2790
+ // dropped (a sphere's) or taken as contacts with the plane (anything else's).
2791
+ if (tentative_count > 0) {
2792
+ count = isSphereConvex
2793
+ ? resolve_tentatives_by_claims(count, concave_col.shape, isConcaveA, concave_tr)
2794
+ : resolve_tentatives_by_features(count, concave_col.shape, isConcaveA, concave_tr);
2795
+ }
2796
+
1309
2797
  return count;
1310
2798
  }
1311
2799
 
1312
2800
  // Robust GJK + EPA fallback for any convex pair without a closed form or a
1313
- // dedicated branch above (e.g. a primitive vs an authored ConvexHullShape3D,
1314
- // a cylinder/cone). gjk_epa_penetration returns the depth and writes a UNIT
2801
+ // dedicated branch above (a cylinder against anything, a capsule against a
2802
+ // hull, a scaling TransformedShape3D). gjk_epa_penetration returns the depth and writes a UNIT
1315
2803
  // axis into epa_result; we scale to the MTV vector (ex,ey,ez) the body-centre
1316
2804
  // sign-guard below consumes. Replaces the old gjk_with_axis +
1317
2805
  // expanding_polytope_algorithm pair, which returned a non-minimal axis on the
@@ -1383,31 +2871,38 @@ function dispatch_pair(count, colA, trA, colB, trB) {
1383
2871
  const inv = 1 / depth;
1384
2872
  const nx = -ex * inv, ny = -ey * inv, nz = -ez * inv; // stored normal, B → A
1385
2873
 
1386
- // Contact application points: body centres rather than support-function
1387
- // witnesses. The natural-sounding alternative — `posed_a.support(+EPA_dir)`
1388
- // and `posed_b.support(-EPA_dir)` — interacts badly with flat-faced
1389
- // supports: a large floor's support in `+Y` returns a *corner* (the box
1390
- // support is multi-valued along an axis-aligned direction and picks the
1391
- // sign-tied corner), which can be tens of metres from the actual
1392
- // contact patch. That blows up the solver's lever arm `r × n` and
1393
- // collapses `m_eff` to ~0, leaving the impulse vanishingly small.
1394
- //
1395
- // Body centres give `r ‖ n` for vertical contacts (sphere/knot landing
1396
- // on a floor — the common case), so `r × n = 0` and `m_eff = invM`
1397
- // resolves the impact cleanly. For oblique contacts the application
1398
- // point lies on the line between centres, which is the right-ish lever
1399
- // arm for translational impulse and degrades gracefully on angular
1400
- // response. A future closed-form mesh-vs-box path will compute a
1401
- // proper face-projected witness; until then, body centres are the
1402
- // robust fallback for the GJK+EPA route.
1403
- // GJK+EPA fallback has no stable per-frame feature info; emit fid = 0
1404
- // so match-and-merge uses position-fallback. Single contact per pair.
1405
- return append_contact(count,
2874
+ // Contact points from the two supporting features along the axis (see support_feature_manifold):
2875
+ // where the bodies actually touch. Raw support points will not do — a large flat side's support
2876
+ // along its own normal is a tie between its corners, tens of metres from the patch — and the body
2877
+ // origins this used to report are only under the contact directly above the other's origin;
2878
+ // anywhere else the lever arm reached across to it and bodies with rotation enabled spun and fell
2879
+ // through. No stable per-frame feature info: fid = 0, so match-and-merge matches by position.
2880
+ support_feature(feature_a, colA.shape,
1406
2881
  trA.translation_x, trA.translation_y, trA.translation_z,
2882
+ trA.rotation_x, trA.rotation_y, trA.rotation_z, trA.rotation_w,
2883
+ -nx, -ny, -nz
2884
+ );
2885
+ support_feature(feature_b, colB.shape,
1407
2886
  trB.translation_x, trB.translation_y, trB.translation_z,
1408
- nx, ny, nz,
1409
- depth, 0
2887
+ trB.rotation_x, trB.rotation_y, trB.rotation_z, trB.rotation_w,
2888
+ nx, ny, nz
1410
2889
  );
2890
+ feature_manifold(feature_manifold_result, feature_a, feature_b, nx, ny, nz, depth);
2891
+
2892
+ const mnx = feature_manifold_result[0], mny = feature_manifold_result[1], mnz = feature_manifold_result[2];
2893
+ const cc = feature_manifold_result[3];
2894
+
2895
+ for (let k = 0; k < cc; k++) {
2896
+ const base = 4 + k * 7;
2897
+ count = append_contact(count,
2898
+ feature_manifold_result[base], feature_manifold_result[base + 1], feature_manifold_result[base + 2],
2899
+ feature_manifold_result[base + 3], feature_manifold_result[base + 4], feature_manifold_result[base + 5],
2900
+ mnx, mny, mnz,
2901
+ feature_manifold_result[base + 6], 0
2902
+ );
2903
+ }
2904
+
2905
+ return count;
1411
2906
  }
1412
2907
 
1413
2908
  // Reusable single-pair adapters for the penetration query below — no per-call
@@ -1446,8 +2941,9 @@ const _pp_trB = new Transform64();
1446
2941
  * step (depenetration, overlap depth, tooling) — never from inside one.
1447
2942
  *
1448
2943
  * Both poses are read BY INDEX on the way into the stand-in `Transform64`s —
1449
- * a `Transform64`'s own `translation` / `rotation` (`Float64Array` views), a
1450
- * `Vector3` / `Quaternion` (both `Float64Array` subclasses) and plain
2944
+ * a retained `Float64Array` (not a Transform64's `translation` / `rotation`,
2945
+ * which allocate a view per read), a `Vector3` / `Quaternion` (both
2946
+ * `Float64Array` subclasses) and plain
1451
2947
  * `[x, y, z]` / `[x, y, z, w]` arrays all work; an `{x, y, z, w}` object does
1452
2948
  * not. Its components read as `undefined`, which trips `setTranslation`'s
1453
2949
  * `assert.isNumber` in development and, with `assert.*` stripped from a
@@ -1509,6 +3005,159 @@ export function deepest_pair_penetration(out_normal, shapeA, posA, rotA, shapeB,
1509
3005
  return best_depth;
1510
3006
  }
1511
3007
 
3008
+ /**
3009
+ * Drop the sensor candidates among the first `count` when any solid candidate is there too,
3010
+ * compacting the solids to the front in their enumeration order. Returns the surviving count.
3011
+ *
3012
+ * A body pair's collider cross-product feeds ONE candidate list, which the reduction cuts to
3013
+ * {@link MAX_KEPT} by depth and spread, blind to the per-contact flags. A sensor overlaps the whole
3014
+ * volume it covers, so its candidates are the deepest in the list and used to take every slot; the
3015
+ * solver then skipped them for being sensors, and the solid contacts that should have held the body
3016
+ * were never in the manifold. A cube on a thin pedestal wrapped by a trigger fell straight through it,
3017
+ * and a crate carrying a 4 m pick-up zone fell through the floor.
3018
+ *
3019
+ * Sensor candidates are kept when the pair has nothing else, so `contact_count > 0` — the only thing
3020
+ * the contact events key on (diff_manifolds) — is unchanged: the pair is in contact whenever any of
3021
+ * its collider pairs is. Nothing that reaches gameplay says which collider pair a contact came from
3022
+ * (the event is per body pair and its payload carries no flags), so a sensor overlap that coincides
3023
+ * with a solid contact was never separately observable; the payload's representative contact is now
3024
+ * the deepest SOLID one rather than the sensor's whole-volume depth.
3025
+ *
3026
+ * @param {number} count
3027
+ * @returns {number}
3028
+ */
3029
+ function drop_sensor_candidates_under_solids(count) {
3030
+ let solids = 0;
3031
+
3032
+ for (let i = 0; i < count; i++) {
3033
+ if ((candidates[i * CANDIDATE_STRIDE + 13] & CONTACT_FLAG_SENSOR) === 0) {
3034
+ solids++;
3035
+ }
3036
+ }
3037
+
3038
+ if (solids === 0 || solids === count) {
3039
+ return count;
3040
+ }
3041
+
3042
+ let kept = 0;
3043
+
3044
+ for (let i = 0; i < count; i++) {
3045
+ const src = i * CANDIDATE_STRIDE;
3046
+
3047
+ if ((candidates[src + 13] & CONTACT_FLAG_SENSOR) !== 0) {
3048
+ continue;
3049
+ }
3050
+
3051
+ if (kept !== i) {
3052
+ candidates.copyWithin(kept * CANDIDATE_STRIDE, src, src + CANDIDATE_STRIDE);
3053
+ }
3054
+
3055
+ kept++;
3056
+ }
3057
+
3058
+ return kept;
3059
+ }
3060
+
3061
+ /**
3062
+ * World boxes of the colliders of the body pair in progress, six floats each: A's colliders, then B's.
3063
+ *
3064
+ * The broadphase pairs BODIES, so a body pair's every collider pair used to be dispatched: a static
3065
+ * compound of 256 hulls under one ball dispatched 256 hull-sphere tests a step for the one child the
3066
+ * ball sat on. A compound pair now tests each collider pair's boxes first, and dispatches only the pairs
3067
+ * whose boxes touch.
3068
+ *
3069
+ * Exactly what the dispatch would have found: no routine makes a contact between separated shapes —
3070
+ * every one needs them touching or overlapping (depth ≥ 0, no speculative margin) — and these are the
3071
+ * boxes the broadphase builds its leaves from (compute_world_aabb, broadphase/compute_fat_world_aabb.js),
3072
+ * at the very poses the dispatch reads. A collider whose box does not hold its surface (a height field
3073
+ * whose sampler was written without bumping its version) is already missed by the broadphase alone; a
3074
+ * compound inherits that, nothing more.
3075
+ *
3076
+ * Grown by doubling, high-water mark kept.
3077
+ * @type {Float64Array}
3078
+ */
3079
+ let collider_boxes = new Float64Array(16 * 6);
3080
+
3081
+ /** A collider's box in its own frame, for {@link collider_box}. */
3082
+ const collider_local_box = new Float64Array(6);
3083
+
3084
+ /**
3085
+ * Slack on the box test, m: the boxes and the contact routines round differently, and a contact at
3086
+ * depth 0 (touching) is still a contact. Neither needs more than this anywhere a body can be.
3087
+ * @type {number}
3088
+ */
3089
+ const COLLIDER_BOX_SLACK = 1e-6;
3090
+
3091
+ /**
3092
+ * Fill {@link collider_boxes} for a body pair's two collider lists.
3093
+ *
3094
+ * @param {Array<{collider: Collider, transform: Transform64}>} list_a
3095
+ * @param {Array<{collider: Collider, transform: Transform64}>} list_b
3096
+ */
3097
+ function compute_collider_boxes(list_a, list_b) {
3098
+ const la_len = list_a.length;
3099
+ const lb_len = list_b.length;
3100
+
3101
+ if ((la_len + lb_len) * 6 > collider_boxes.length) {
3102
+ let length = collider_boxes.length * 2;
3103
+
3104
+ while (length < (la_len + lb_len) * 6) {
3105
+ length *= 2;
3106
+ }
3107
+
3108
+ collider_boxes = new Float64Array(length);
3109
+ }
3110
+
3111
+ for (let a = 0; a < la_len; a++) {
3112
+ collider_box(a, list_a[a]);
3113
+ }
3114
+
3115
+ for (let b = 0; b < lb_len; b++) {
3116
+ collider_box(la_len + b, list_b[b]);
3117
+ }
3118
+ }
3119
+
3120
+ /**
3121
+ * Box `i` of {@link collider_boxes}: the collider's local box carried to its pose, compute_world_aabb's
3122
+ * arithmetic read from the pose's components — that function takes the pose's translation and rotation
3123
+ * views first, two allocations a call, which here would double the box's cost (76 ns against 35).
3124
+ *
3125
+ * @param {number} i
3126
+ * @param {{collider: Collider, transform: Transform64}} entry
3127
+ */
3128
+ function collider_box(i, entry) {
3129
+ const t = entry.transform;
3130
+
3131
+ entry.collider.shape.compute_bounding_box(collider_local_box);
3132
+
3133
+ pose_pack(collider_pose, t);
3134
+ aabb3_transform_oriented_array(collider_boxes, i * 6, collider_local_box, 0, collider_pose, 0);
3135
+ }
3136
+
3137
+ /** The pose {@link collider_box} hands {@link aabb3_transform_oriented_array}. */
3138
+ const collider_pose = new Float64Array(7);
3139
+
3140
+ /**
3141
+ * Whether boxes `i` and `j` of {@link collider_boxes} are apart on some axis. A box with NaN in it is
3142
+ * never apart: the dispatch decides what such a pair yields, as it did before the boxes were tested.
3143
+ *
3144
+ * @param {number} i
3145
+ * @param {number} j
3146
+ * @returns {boolean}
3147
+ */
3148
+ function collider_boxes_apart(i, j) {
3149
+ const b = collider_boxes;
3150
+ const io = i * 6;
3151
+ const jo = j * 6;
3152
+
3153
+ return b[io] > b[jo + 3] + COLLIDER_BOX_SLACK
3154
+ || b[jo] > b[io + 3] + COLLIDER_BOX_SLACK
3155
+ || b[io + 1] > b[jo + 4] + COLLIDER_BOX_SLACK
3156
+ || b[jo + 1] > b[io + 4] + COLLIDER_BOX_SLACK
3157
+ || b[io + 2] > b[jo + 5] + COLLIDER_BOX_SLACK
3158
+ || b[jo + 2] > b[io + 5] + COLLIDER_BOX_SLACK;
3159
+ }
3160
+
1512
3161
  /**
1513
3162
  * For every pair in `pair_list`, do a cross-product over A's collider list ×
1514
3163
  * B's collider list, accumulate candidate contacts, reduce to ≤4, and write
@@ -1550,11 +3199,23 @@ export function narrowphase_step(pair_list, manifolds, lists) {
1550
3199
  const la_len = list_a.length;
1551
3200
  const lb_len = list_b.length;
1552
3201
 
3202
+ // A compound pair: the collider pairs whose boxes do not touch are not dispatched.
3203
+ const pretest = la_len + lb_len > 2;
3204
+
3205
+ if (pretest) {
3206
+ compute_collider_boxes(list_a, list_b);
3207
+ }
3208
+
1553
3209
  let sub_pair = 0;
1554
3210
  for (let a = 0; a < la_len; a++) {
1555
3211
  const ea = list_a[a];
1556
3212
 
1557
- for (let b = 0; b < lb_len; b++) {
3213
+ for (let b = 0; b < lb_len; b++, sub_pair++) {
3214
+ // Skipped pairs keep their number: it salts the fids below.
3215
+ if (pretest && collider_boxes_apart(a, la_len + b)) {
3216
+ continue;
3217
+ }
3218
+
1558
3219
  const eb = list_b[b];
1559
3220
 
1560
3221
  const before = cand_count;
@@ -1576,7 +3237,6 @@ export function narrowphase_step(pair_list, manifolds, lists) {
1576
3237
  if (candidates[co + 10] !== 0) candidates[co + 10] += salt;
1577
3238
  }
1578
3239
  }
1579
- sub_pair++;
1580
3240
  }
1581
3241
  }
1582
3242
 
@@ -1591,7 +3251,11 @@ export function narrowphase_step(pair_list, manifolds, lists) {
1591
3251
  continue;
1592
3252
  }
1593
3253
 
1594
- const kept = reduce_manifold_contacts(candidates, cand_count, CANDIDATE_STRIDE, MAX_KEPT, 9);
3254
+ // Solids are reduced on their own; sensors only fill a manifold that has nothing solid.
3255
+ cand_count = drop_sensor_candidates_under_solids(cand_count);
3256
+
3257
+ // Shared out between the planes the contacts lie on: see reduce_manifold_contacts_by_plane.
3258
+ const kept = reduce_manifold_contacts_by_plane(candidates, cand_count, CANDIDATE_STRIDE, MAX_KEPT, 9, 6);
1595
3259
 
1596
3260
  // ── Match-and-merge: feature-id (with position fallback) ──────
1597
3261
  //
@@ -1679,16 +3343,7 @@ export function narrowphase_step(pair_list, manifolds, lists) {
1679
3343
  // copies the matched prev contact's impulses into THIS k,
1680
3344
  // so the geometric and warm-start state stay correlated
1681
3345
  // even if the matching mapping permuted the order.
1682
- manifolds.set_contact(
1683
- slot, k,
1684
- candidates[off], candidates[off + 1], candidates[off + 2],
1685
- candidates[off + 3], candidates[off + 4], candidates[off + 5],
1686
- candidates[off + 6], candidates[off + 7], candidates[off + 8],
1687
- candidates[off + 9],
1688
- candidates[off + 10],
1689
- candidates[off + 11], candidates[off + 12],
1690
- candidates[off + 13]
1691
- );
3346
+ manifolds.set_contact_from(slot, k, candidates, off);
1692
3347
  const prev_j = cand_to_prev[k];
1693
3348
  if (prev_j !== -1) {
1694
3349
  // Copy prev_j's impulse to this slot index.
@@ -1723,9 +3378,9 @@ export function narrowphase_step(pair_list, manifolds, lists) {
1723
3378
  *
1724
3379
  * Matching is by feature id (stable per-triangle for the decomposition path),
1725
3380
  * so a contact's geometry tracks the same triangle across substeps. A contact
1726
- * whose triangle isn't found this substep keeps its previous geometry (a rare
1727
- * transient; the once-per-frame {@link narrowphase_step} re-establishes the
1728
- * contact set next outer step). Count never changes here, so the solver's
3381
+ * whose triangle isn't found this substep keeps its previous geometry, moved by
3382
+ * how far the found contacts moved (a transient; the once-per-frame
3383
+ * {@link narrowphase_step} re-establishes the contact set next outer step). Count never changes here, so the solver's
1729
3384
  * per-contact scratch (sized once at prepare) stays aligned.
1730
3385
  *
1731
3386
  * @param {ManifoldStore} manifolds
@@ -1742,11 +3397,22 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
1742
3397
  const count = manifolds.contact_count(slot);
1743
3398
  if (count === 0) return;
1744
3399
 
3400
+ // As in narrowphase_step: a compound pair dispatches only the collider pairs whose boxes touch.
3401
+ const pretest = la_len + lb_len > 2;
3402
+
3403
+ if (pretest) {
3404
+ compute_collider_boxes(list_a, list_b);
3405
+ }
3406
+
1745
3407
  let cc = 0;
1746
3408
  let sub_pair = 0;
1747
3409
  for (let a = 0; a < la_len; a++) {
1748
3410
  const ea = list_a[a];
1749
- for (let b = 0; b < lb_len; b++) {
3411
+ for (let b = 0; b < lb_len; b++, sub_pair++) {
3412
+ if (pretest && collider_boxes_apart(a, la_len + b)) {
3413
+ continue;
3414
+ }
3415
+
1750
3416
  const eb = list_b[b];
1751
3417
  const before = cc;
1752
3418
  cc = dispatch_pair(cc, ea.collider, ea.transform, eb.collider, eb.transform);
@@ -1760,7 +3426,6 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
1760
3426
  if (candidates[co + 10] !== 0) candidates[co + 10] += salt;
1761
3427
  }
1762
3428
  }
1763
- sub_pair++;
1764
3429
  }
1765
3430
  }
1766
3431
  if (cc === 0) return; // nothing re-detected this substep — keep frozen geometry
@@ -1782,6 +3447,10 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
1782
3447
  // single candidate exactly once — identical to the old behaviour.
1783
3448
  for (let k = 0; k < cc; k++) redetect_claimed[k] = 0;
1784
3449
 
3450
+ // how far the matched contacts' points moved, summed, and the contacts left without a candidate
3451
+ let moved_x = 0, moved_y = 0, moved_z = 0, moved = 0;
3452
+ let stale = 0;
3453
+
1785
3454
  for (let j = 0; j < count; j++) {
1786
3455
  const off = slot_off + j * CONTACT_STRIDE;
1787
3456
  const fid = data[off + 13];
@@ -1793,11 +3462,17 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
1793
3462
  const pay = data[off + 1];
1794
3463
  const paz = data[off + 2];
1795
3464
 
3465
+ // A contact only takes geometry from a candidate of its own kind. The fresh candidates cover
3466
+ // every collider pair, sensors included; a solid contact refreshed from a sensor's
3467
+ // whole-volume overlap would keep its solid flag and push with the sensor's depth and normal.
3468
+ const sensor = data[off + CONTACT_FLAGS_OFFSET] & CONTACT_FLAG_SENSOR;
3469
+
1796
3470
  let best_k = -1;
1797
3471
  let best_d2 = Infinity;
1798
3472
  for (let k = 0; k < cc; k++) {
1799
3473
  if (redetect_claimed[k] === 1) continue;
1800
3474
  const co = k * CANDIDATE_STRIDE;
3475
+ if ((candidates[co + 13] & CONTACT_FLAG_SENSOR) !== sensor) continue;
1801
3476
  if (candidates[co + 10] !== fid) continue;
1802
3477
  const dx = candidates[co] - pax;
1803
3478
  const dy = candidates[co + 1] - pay;
@@ -1825,6 +3500,7 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
1825
3500
  for (let k = 0; k < cc; k++) {
1826
3501
  if (redetect_claimed[k] === 1) continue;
1827
3502
  const co = k * CANDIDATE_STRIDE;
3503
+ if ((candidates[co + 13] & CONTACT_FLAG_SENSOR) !== sensor) continue;
1828
3504
  const dx = candidates[co] - pax;
1829
3505
  const dy = candidates[co + 1] - pay;
1830
3506
  const dz = candidates[co + 2] - paz;
@@ -1836,10 +3512,20 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
1836
3512
  }
1837
3513
  }
1838
3514
 
1839
- if (best_k === -1) continue; // no unclaimed candidate at all this substep — keep frozen geometry
3515
+ if (best_k === -1) {
3516
+ // no unclaimed candidate at all this substep: its geometry is kept, moved with the others
3517
+ // (below)
3518
+ if (stale < redetect_stale.length) redetect_stale[stale++] = off;
3519
+ continue;
3520
+ }
1840
3521
 
1841
3522
  redetect_claimed[best_k] = 1;
1842
3523
  const co = best_k * CANDIDATE_STRIDE;
3524
+
3525
+ moved_x += (candidates[co] + candidates[co + 3] - data[off] - data[off + 3]) * 0.5;
3526
+ moved_y += (candidates[co + 1] + candidates[co + 4] - data[off + 1] - data[off + 4]) * 0.5;
3527
+ moved_z += (candidates[co + 2] + candidates[co + 5] - data[off + 2] - data[off + 5]) * 0.5;
3528
+ moved++;
1843
3529
  // Overwrite geometry only: witnesses, normal, depth. (Count, feature
1844
3530
  // ids and accumulated impulses are intentionally left untouched — see
1845
3531
  // the function contract.)
@@ -1854,4 +3540,20 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
1854
3540
  data[off + 8] = candidates[co + 8];
1855
3541
  data[off + 9] = candidates[co + 9];
1856
3542
  }
3543
+
3544
+ // A contact left without a candidate keeps its geometry from before this substep, and its points
3545
+ // stay where they were while the body moves on: at 6 m/s, 25 mm behind it each substep. A capsule
3546
+ // crossing a T-junction's rows lost one end's contact this way for a substep, and the frozen point,
3547
+ // up to 100 mm behind its axis, spun it at 0.13 rad/s. Moved with the contacts that were found —
3548
+ // by their mean displacement, the body's motion for one substep — it stays under the body.
3549
+ if (stale > 0 && moved > 0) {
3550
+ const dx = moved_x / moved, dy = moved_y / moved, dz = moved_z / moved;
3551
+
3552
+ for (let i = 0; i < stale; i++) {
3553
+ const off = redetect_stale[i];
3554
+
3555
+ data[off] += dx; data[off + 1] += dy; data[off + 2] += dz;
3556
+ data[off + 3] += dx; data[off + 4] += dy; data[off + 5] += dz;
3557
+ }
3558
+ }
1857
3559
  }