@woosh/meep-engine 3.37.0 → 3.39.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 (1016) 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/control/first-person/FirstPersonPlayerControllerSystem.d.ts +135 -10
  139. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
  140. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +338 -105
  141. package/src/engine/control/first-person/abilities/Ability.d.ts +14 -5
  142. package/src/engine/control/first-person/abilities/Ability.d.ts.map +1 -1
  143. package/src/engine/control/first-person/abilities/Ability.js +14 -5
  144. package/src/engine/control/first-person/abilities/LedgeGrab.d.ts.map +1 -1
  145. package/src/engine/control/first-person/abilities/LedgeGrab.js +16 -12
  146. package/src/engine/control/first-person/abilities/Mantle.d.ts.map +1 -1
  147. package/src/engine/control/first-person/abilities/Mantle.js +5 -1
  148. package/src/engine/control/first-person/abilities/Slide.js +1 -1
  149. package/src/engine/control/first-person/collision/KinematicMover.d.ts +92 -14
  150. package/src/engine/control/first-person/collision/KinematicMover.d.ts.map +1 -1
  151. package/src/engine/control/first-person/collision/KinematicMover.js +218 -37
  152. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts +5 -0
  153. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts.map +1 -1
  154. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js +17 -10
  155. package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
  156. package/src/engine/ecs/EntityComponentDataset.js +27 -13
  157. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  158. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +6 -0
  159. package/src/engine/ecs/transform/TRANSFORM64_EVENT_PLACED.d.ts +11 -0
  160. package/src/engine/ecs/transform/TRANSFORM64_EVENT_PLACED.d.ts.map +1 -0
  161. package/src/engine/ecs/transform/TRANSFORM64_EVENT_PLACED.js +10 -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/ecs/transform-attachment/TransformAttachmentSystem.d.ts.map +1 -1
  166. package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +16 -0
  167. package/src/engine/graphics/ecs/water/Water.d.ts +26 -34
  168. package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
  169. package/src/engine/graphics/ecs/water/Water.js +42 -36
  170. package/src/engine/graphics/ecs/water/WaterSerializationAdapter.d.ts.map +1 -1
  171. package/src/engine/graphics/ecs/water/WaterSerializationAdapter.js +38 -62
  172. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_1_2.d.ts +6 -0
  173. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_1_2.d.ts.map +1 -0
  174. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_1_2.js +27 -0
  175. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_2_3.d.ts +12 -0
  176. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_2_3.d.ts.map +1 -0
  177. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_2_3.js +45 -0
  178. package/src/engine/graphics/ecs/water/WaterWaves.d.ts +33 -0
  179. package/src/engine/graphics/ecs/water/WaterWaves.d.ts.map +1 -0
  180. package/src/engine/graphics/ecs/water/WaterWaves.js +46 -0
  181. package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.d.ts +17 -0
  182. package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.d.ts.map +1 -0
  183. package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.js +26 -0
  184. package/src/engine/graphics/texture/3d/scs3d_sample_linear.d.ts +22 -0
  185. package/src/engine/graphics/texture/3d/scs3d_sample_linear.d.ts.map +1 -1
  186. package/src/engine/graphics/texture/3d/scs3d_sample_linear.js +43 -1
  187. package/src/engine/graphics/texture/sampler/Sampler2D.d.ts +29 -3
  188. package/src/engine/graphics/texture/sampler/Sampler2D.d.ts.map +1 -1
  189. package/src/engine/graphics/texture/sampler/Sampler2D.js +102 -14
  190. package/src/engine/graphics3/WaterSystem.d.ts +58 -6
  191. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  192. package/src/engine/graphics3/WaterSystem.js +423 -293
  193. package/src/engine/interpolation/BinaryInterpolationAdapter.d.ts +10 -4
  194. package/src/engine/interpolation/BinaryInterpolationAdapter.d.ts.map +1 -1
  195. package/src/engine/interpolation/BinaryInterpolationAdapter.js +11 -5
  196. package/src/engine/interpolation/InterpolationLog.d.ts +11 -5
  197. package/src/engine/interpolation/InterpolationLog.d.ts.map +1 -1
  198. package/src/engine/interpolation/InterpolationLog.js +12 -6
  199. package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
  200. package/src/engine/interpolation/InterpolationSystem.js +39 -8
  201. package/src/engine/interpolation/PoseInterpolationAdapter.d.ts +1 -1
  202. package/src/engine/interpolation/PoseInterpolationAdapter.d.ts.map +1 -1
  203. package/src/engine/interpolation/PoseInterpolationAdapter.js +51 -22
  204. package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -1
  205. package/src/engine/interpolation/TransformPoseSerializationAdapter.js +18 -23
  206. package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts +1 -1
  207. package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts.map +1 -1
  208. package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +3 -1
  209. package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts +1 -1
  210. package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts.map +1 -1
  211. package/src/engine/network/adapters/TransformInterpolationAdapter.js +3 -1
  212. package/src/engine/network/adapters/Vector3InterpolationAdapter.d.ts.map +1 -1
  213. package/src/engine/network/adapters/Vector3InterpolationAdapter.js +5 -2
  214. package/src/engine/physics/body/SolverBodyState.d.ts.map +1 -1
  215. package/src/engine/physics/body/SolverBodyState.js +4 -5
  216. package/src/engine/physics/broadphase/compute_fat_world_aabb.d.ts +12 -0
  217. package/src/engine/physics/broadphase/compute_fat_world_aabb.d.ts.map +1 -1
  218. package/src/engine/physics/broadphase/compute_fat_world_aabb.js +28 -3
  219. package/src/engine/physics/ccd/linear_sweep.d.ts +42 -12
  220. package/src/engine/physics/ccd/linear_sweep.d.ts.map +1 -1
  221. package/src/engine/physics/ccd/linear_sweep.js +1131 -97
  222. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts +26 -14
  223. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts.map +1 -1
  224. package/src/engine/physics/cloth/collider/ClothColliderIndex.js +221 -52
  225. package/src/engine/physics/cloth/collider/cloth_collider_bounds.d.ts +30 -0
  226. package/src/engine/physics/cloth/collider/cloth_collider_bounds.d.ts.map +1 -1
  227. package/src/engine/physics/cloth/collider/cloth_collider_bounds.js +117 -24
  228. package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts +46 -3
  229. package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts.map +1 -1
  230. package/src/engine/physics/cloth/collider/cloth_collider_sdf.js +116 -68
  231. package/src/engine/physics/cloth/ecs/ClothColliderFlags.js +2 -1
  232. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts +10 -1
  233. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts.map +1 -1
  234. package/src/engine/physics/cloth/ecs/ClothColliderSystem.js +36 -14
  235. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +15 -2
  236. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
  237. package/src/engine/physics/cloth/ecs/ClothInstance.js +17 -2
  238. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
  239. package/src/engine/physics/cloth/ecs/ClothWorld.js +78 -53
  240. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +18 -20
  241. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -1
  242. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +185 -128
  243. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.d.ts.map +1 -1
  244. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.js +46 -39
  245. package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts.map +1 -1
  246. package/src/engine/physics/cloth/ecs/cloth_write_back.js +41 -36
  247. package/src/engine/physics/cloth/playground/garment_build.js +6 -6
  248. package/src/engine/physics/cloth/playground/garment_main.js +18 -14
  249. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts.map +1 -1
  250. package/src/engine/physics/cloth/solver/cloth_contact_find.js +8 -5
  251. package/src/engine/physics/cloth/solver/cloth_solve_vertex.d.ts.map +1 -1
  252. package/src/engine/physics/cloth/solver/cloth_solve_vertex.js +8 -5
  253. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts.map +1 -1
  254. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.js +15 -15
  255. package/src/engine/physics/cloth/solver/find_non_finite_cloth_state.d.ts +5 -0
  256. package/src/engine/physics/cloth/solver/find_non_finite_cloth_state.d.ts.map +1 -1
  257. package/src/engine/physics/cloth/solver/find_non_finite_cloth_state.js +32 -9
  258. package/src/engine/physics/constraint/solve_constraints.d.ts +50 -8
  259. package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
  260. package/src/engine/physics/constraint/solve_constraints.js +703 -429
  261. package/src/engine/physics/contact/ManifoldStore.d.ts +12 -0
  262. package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
  263. package/src/engine/physics/contact/ManifoldStore.js +32 -0
  264. package/src/engine/physics/ecs/BodyKind.d.ts +16 -8
  265. package/src/engine/physics/ecs/BodyKind.d.ts.map +1 -1
  266. package/src/engine/physics/ecs/BodyKind.js +16 -8
  267. package/src/engine/physics/ecs/Collider.d.ts +7 -2
  268. package/src/engine/physics/ecs/Collider.d.ts.map +1 -1
  269. package/src/engine/physics/ecs/Collider.js +7 -2
  270. package/src/engine/physics/ecs/PhysicsEvents.d.ts +13 -0
  271. package/src/engine/physics/ecs/PhysicsEvents.d.ts.map +1 -1
  272. package/src/engine/physics/ecs/PhysicsEvents.js +13 -0
  273. package/src/engine/physics/ecs/PhysicsSystem.d.ts +320 -36
  274. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  275. package/src/engine/physics/ecs/PhysicsSystem.js +810 -96
  276. package/src/engine/physics/fluid/FluidField.d.ts.map +1 -1
  277. package/src/engine/physics/fluid/FluidField.js +5 -1
  278. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +11 -9
  279. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts.map +1 -1
  280. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +7 -5
  281. package/src/engine/physics/fluid/ecs/fluid_reanchor_field.d.ts.map +1 -1
  282. package/src/engine/physics/fluid/ecs/fluid_reanchor_field.js +9 -7
  283. package/src/engine/physics/fluid/effector/ImpulseFluidEffector.d.ts.map +1 -1
  284. package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +3 -4
  285. package/src/engine/physics/fluid/effector/WakeFluidEffector.d.ts.map +1 -1
  286. package/src/engine/physics/fluid/effector/WakeFluidEffector.js +9 -7
  287. package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.d.ts.map +1 -1
  288. package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.js +65 -43
  289. package/src/engine/physics/inertia/MASS_PROPERTIES_SIZE.d.ts +23 -0
  290. package/src/engine/physics/inertia/MASS_PROPERTIES_SIZE.d.ts.map +1 -0
  291. package/src/engine/physics/inertia/MASS_PROPERTIES_SIZE.js +22 -0
  292. package/src/engine/physics/inertia/convex_hull_mass_properties.d.ts +29 -0
  293. package/src/engine/physics/inertia/convex_hull_mass_properties.d.ts.map +1 -0
  294. package/src/engine/physics/inertia/convex_hull_mass_properties.js +117 -0
  295. package/src/engine/physics/inertia/mass_properties_accumulate.d.ts +24 -0
  296. package/src/engine/physics/inertia/mass_properties_accumulate.d.ts.map +1 -0
  297. package/src/engine/physics/inertia/mass_properties_accumulate.js +93 -0
  298. package/src/engine/physics/inertia/primitive_mass_properties.d.ts +46 -0
  299. package/src/engine/physics/inertia/primitive_mass_properties.d.ts.map +1 -0
  300. package/src/engine/physics/inertia/primitive_mass_properties.js +118 -0
  301. package/src/engine/physics/inertia/rigid_body_set_mass_properties.d.ts +27 -0
  302. package/src/engine/physics/inertia/rigid_body_set_mass_properties.d.ts.map +1 -0
  303. package/src/engine/physics/inertia/rigid_body_set_mass_properties.js +62 -0
  304. package/src/engine/physics/inertia/world_inverse_inertia.d.ts +67 -25
  305. package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
  306. package/src/engine/physics/inertia/world_inverse_inertia.js +149 -26
  307. package/src/engine/physics/integration/integrate_position.d.ts +18 -19
  308. package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
  309. package/src/engine/physics/integration/integrate_position.js +40 -37
  310. package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
  311. package/src/engine/physics/integration/integrate_velocity.js +13 -4
  312. package/src/engine/physics/island/IslandBuilder.d.ts +124 -6
  313. package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -1
  314. package/src/engine/physics/island/IslandBuilder.js +322 -19
  315. package/src/engine/physics/narrowphase/box_box_manifold.d.ts +13 -0
  316. package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
  317. package/src/engine/physics/narrowphase/box_box_manifold.js +95 -39
  318. package/src/engine/physics/narrowphase/box_triangle_contact.d.ts +40 -0
  319. package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -1
  320. package/src/engine/physics/narrowphase/box_triangle_contact.js +415 -120
  321. package/src/engine/physics/narrowphase/capsule_contacts.d.ts +63 -0
  322. package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
  323. package/src/engine/physics/narrowphase/capsule_contacts.js +234 -46
  324. package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts +13 -0
  325. package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts.map +1 -1
  326. package/src/engine/physics/narrowphase/capsule_triangle_contact.js +141 -93
  327. package/src/engine/physics/narrowphase/compute_penetration.d.ts +29 -11
  328. package/src/engine/physics/narrowphase/compute_penetration.d.ts.map +1 -1
  329. package/src/engine/physics/narrowphase/compute_penetration.js +157 -34
  330. package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts +76 -0
  331. package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts.map +1 -1
  332. package/src/engine/physics/narrowphase/convex_convex_manifold.js +243 -48
  333. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts +13 -0
  334. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts.map +1 -1
  335. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +28 -12
  336. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts +13 -0
  337. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts.map +1 -1
  338. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.js +27 -0
  339. package/src/engine/physics/narrowphase/decomposition/heightmap_triangle_edge_flags.d.ts +42 -0
  340. package/src/engine/physics/narrowphase/decomposition/heightmap_triangle_edge_flags.d.ts.map +1 -0
  341. package/src/engine/physics/narrowphase/decomposition/heightmap_triangle_edge_flags.js +250 -0
  342. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts +27 -11
  343. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts.map +1 -1
  344. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.js +291 -12
  345. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts +7 -1
  346. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts.map +1 -1
  347. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +7 -1
  348. package/src/engine/physics/narrowphase/decomposition/triangle_edge_flags.d.ts +35 -0
  349. package/src/engine/physics/narrowphase/decomposition/triangle_edge_flags.d.ts.map +1 -0
  350. package/src/engine/physics/narrowphase/decomposition/triangle_edge_flags.js +61 -0
  351. package/src/engine/physics/narrowphase/hull_sphere_contact.d.ts +54 -0
  352. package/src/engine/physics/narrowphase/hull_sphere_contact.d.ts.map +1 -0
  353. package/src/engine/physics/narrowphase/hull_sphere_contact.js +244 -0
  354. package/src/engine/physics/narrowphase/mesh_convex_hull.js +5 -1
  355. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.d.ts +2 -0
  356. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.d.ts.map +1 -1
  357. package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +56 -31
  358. package/src/engine/physics/narrowphase/narrowphase_step.d.ts +6 -5
  359. package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
  360. package/src/engine/physics/narrowphase/narrowphase_step.js +2185 -483
  361. package/src/engine/physics/narrowphase/reduce_manifold_contacts.d.ts +56 -5
  362. package/src/engine/physics/narrowphase/reduce_manifold_contacts.d.ts.map +1 -1
  363. package/src/engine/physics/narrowphase/reduce_manifold_contacts.js +546 -5
  364. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts +29 -2
  365. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -1
  366. package/src/engine/physics/narrowphase/refine_ray_concave.js +628 -75
  367. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +3 -2
  368. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
  369. package/src/engine/physics/narrowphase/refine_ray_hit.js +3 -2
  370. package/src/engine/physics/narrowphase/rigid_subject_pose.d.ts +27 -0
  371. package/src/engine/physics/narrowphase/rigid_subject_pose.d.ts.map +1 -0
  372. package/src/engine/physics/narrowphase/rigid_subject_pose.js +96 -0
  373. package/src/engine/physics/narrowphase/sphere_box_contact.d.ts +14 -0
  374. package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -1
  375. package/src/engine/physics/narrowphase/sphere_box_contact.js +51 -5
  376. package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts +12 -0
  377. package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts.map +1 -1
  378. package/src/engine/physics/narrowphase/sphere_sphere_contact.js +30 -0
  379. package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts +12 -0
  380. package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts.map +1 -1
  381. package/src/engine/physics/narrowphase/sphere_triangle_contact.js +37 -8
  382. package/src/engine/physics/narrowphase/support_feature_manifold.d.ts +139 -0
  383. package/src/engine/physics/narrowphase/support_feature_manifold.d.ts.map +1 -0
  384. package/src/engine/physics/narrowphase/support_feature_manifold.js +949 -0
  385. package/src/engine/physics/queries/PhysicsSurfacePoint.d.ts +32 -13
  386. package/src/engine/physics/queries/PhysicsSurfacePoint.d.ts.map +1 -1
  387. package/src/engine/physics/queries/PhysicsSurfacePoint.js +36 -13
  388. package/src/engine/physics/queries/convex_overlaps_shape.d.ts +27 -0
  389. package/src/engine/physics/queries/convex_overlaps_shape.d.ts.map +1 -0
  390. package/src/engine/physics/queries/convex_overlaps_shape.js +169 -0
  391. package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -1
  392. package/src/engine/physics/queries/overlap_shape.js +57 -79
  393. package/src/engine/physics/queries/raycast.d.ts +10 -4
  394. package/src/engine/physics/queries/raycast.d.ts.map +1 -1
  395. package/src/engine/physics/queries/raycast.js +133 -9
  396. package/src/engine/physics/queries/shape_cast.d.ts +55 -24
  397. package/src/engine/physics/queries/shape_cast.d.ts.map +1 -1
  398. package/src/engine/physics/queries/shape_cast.js +2061 -245
  399. package/src/engine/physics/solver/apply_impulse_to_body.d.ts +32 -10
  400. package/src/engine/physics/solver/apply_impulse_to_body.d.ts.map +1 -1
  401. package/src/engine/physics/solver/apply_impulse_to_body.js +69 -28
  402. package/src/engine/physics/solver/solve_contacts.d.ts +65 -18
  403. package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
  404. package/src/engine/physics/solver/solve_contacts.js +458 -234
  405. package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -1
  406. package/src/engine/physics/vehicle/RaycastVehicle.js +5 -6
  407. package/src/shade/assets/textures/split_sum.bin +0 -0
  408. package/src/shade/device/mock/SoftwareGPUQueue.d.ts.map +1 -1
  409. package/src/shade/device/mock/SoftwareGPUQueue.js +2 -1
  410. package/src/shade/playground/path_traced_media/README.md +78 -0
  411. package/src/shade/playground/path_traced_media/capture_radiance.d.ts +33 -0
  412. package/src/shade/playground/path_traced_media/capture_radiance.d.ts.map +1 -0
  413. package/src/shade/playground/path_traced_media/capture_radiance.js +174 -0
  414. package/src/shade/playground/path_traced_media/checks.d.ts +160 -0
  415. package/src/shade/playground/path_traced_media/checks.d.ts.map +1 -0
  416. package/src/shade/playground/path_traced_media/checks.js +921 -0
  417. package/src/shade/playground/path_traced_media/index.html +71 -0
  418. package/src/shade/playground/path_traced_media/main.d.ts +2 -0
  419. package/src/shade/playground/path_traced_media/main.d.ts.map +1 -0
  420. package/src/shade/playground/path_traced_media/main.js +440 -0
  421. package/src/shade/playground/path_traced_media/make_wave_pool_geometry.d.ts +26 -0
  422. package/src/shade/playground/path_traced_media/make_wave_pool_geometry.d.ts.map +1 -0
  423. package/src/shade/playground/path_traced_media/make_wave_pool_geometry.js +117 -0
  424. package/src/shade/playground/path_traced_media/views.d.ts +21 -0
  425. package/src/shade/playground/path_traced_media/views.d.ts.map +1 -0
  426. package/src/shade/playground/path_traced_media/views.js +161 -0
  427. package/src/shade/playground/sh_exponential/README.md +43 -0
  428. package/src/shade/playground/sh_exponential/index.html +59 -0
  429. package/src/shade/playground/sh_exponential/main.d.ts +2 -0
  430. package/src/shade/playground/sh_exponential/main.d.ts.map +1 -0
  431. package/src/shade/playground/sh_exponential/main.js +571 -0
  432. package/src/shade/playground/sh_exponential/sh_exp_playground_shaders.d.ts +46 -0
  433. package/src/shade/playground/sh_exponential/sh_exp_playground_shaders.d.ts.map +1 -0
  434. package/src/shade/playground/sh_exponential/sh_exp_playground_shaders.js +270 -0
  435. package/src/shade/playground/water_refraction/README.md +158 -0
  436. package/src/shade/playground/water_refraction/index.html +40 -0
  437. package/src/shade/playground/water_refraction/main.d.ts +2 -0
  438. package/src/shade/playground/water_refraction/main.d.ts.map +1 -0
  439. package/src/shade/playground/water_refraction/main.js +202 -0
  440. package/src/shade/playground/water_refraction/world.d.ts +39 -0
  441. package/src/shade/playground/water_refraction/world.d.ts.map +1 -0
  442. package/src/shade/playground/water_refraction/world.js +141 -0
  443. package/src/shade/renderer/Renderer.d.ts +2 -0
  444. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  445. package/src/shade/renderer/Renderer.js +27 -0
  446. package/src/shade/renderer/animation/chunk_pose_accumulator.d.ts +2 -3
  447. package/src/shade/renderer/animation/chunk_pose_accumulator.d.ts.map +1 -1
  448. package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_reduce.d.ts.map +1 -1
  449. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.d.ts.map +1 -1
  450. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.js +9 -1
  451. package/src/shade/renderer/geometry/sdf/chunk_scene_point_query_volume_sign.d.ts.map +1 -1
  452. package/src/shade/renderer/geometry/sdf/chunk_scene_point_query_volume_sign.js +6 -4
  453. package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.d.ts.map +1 -1
  454. package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.js +1 -0
  455. package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_rand1.d.ts +7 -0
  456. package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_rand1.d.ts.map +1 -1
  457. package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_rand1.js +12 -12
  458. package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_spherical.d.ts +4 -0
  459. package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_spherical.d.ts.map +1 -1
  460. package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_spherical.js +13 -9
  461. package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.d.ts.map +1 -1
  462. package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.js +2 -0
  463. package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.d.ts.map +1 -1
  464. package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.js +3 -0
  465. package/src/shade/renderer/global_illumination/lpv/deprecated/probe_trace_compute_shader_code.d.ts.map +1 -1
  466. package/src/shade/renderer/global_illumination/lpv/deprecated/probe_trace_compute_shader_code.js +4 -9
  467. package/src/shade/renderer/light/LIGHT_DATABASE_SPEC.d.ts.map +1 -1
  468. package/src/shade/renderer/light/LIGHT_DATABASE_SPEC.js +20 -10
  469. package/src/shade/renderer/light/environment/chunk_roughness_to_mip_ratio.d.ts +8 -0
  470. package/src/shade/renderer/light/environment/chunk_roughness_to_mip_ratio.d.ts.map +1 -0
  471. package/src/shade/renderer/light/environment/chunk_sphere_probe_roughness_to_lod.d.ts +0 -6
  472. package/src/shade/renderer/light/environment/chunk_sphere_probe_roughness_to_lod.d.ts.map +1 -1
  473. package/src/shade/renderer/light/environment/rgbe/load_environment_map.d.ts +2 -1
  474. package/src/shade/renderer/light/environment/rgbe/load_environment_map.d.ts.map +1 -1
  475. package/src/shade/renderer/light/environment/rgbe/load_environment_map.js +2 -1
  476. package/src/shade/renderer/light/environment/sh_exponential/README.md +171 -0
  477. package/src/shade/renderer/light/environment/sh_exponential/SHExpEncoder.d.ts +170 -0
  478. package/src/shade/renderer/light/environment/sh_exponential/SHExpEncoder.d.ts.map +1 -0
  479. package/src/shade/renderer/light/environment/sh_exponential/SHExpEncoder.js +377 -0
  480. package/src/shade/renderer/light/environment/sh_exponential/SH_EXP_ENCODE_PARAMETERS_STRUCT.d.ts +16 -0
  481. package/src/shade/renderer/light/environment/sh_exponential/SH_EXP_ENCODE_PARAMETERS_STRUCT.d.ts.map +1 -0
  482. package/src/shade/renderer/light/environment/sh_exponential/SH_EXP_ENCODE_PARAMETERS_STRUCT.js +27 -0
  483. package/src/shade/renderer/light/environment/sh_exponential/chunk_sh_exp_workgroup_sum.d.ts +21 -0
  484. package/src/shade/renderer/light/environment/sh_exponential/chunk_sh_exp_workgroup_sum.d.ts.map +1 -0
  485. package/src/shade/renderer/light/environment/sh_exponential/chunk_sh_exp_workgroup_sum.js +53 -0
  486. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_design_row.d.ts +33 -0
  487. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_design_row.d.ts.map +1 -0
  488. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_design_row.js +90 -0
  489. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_fit_samples.d.ts +28 -0
  490. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_fit_samples.d.ts.map +1 -0
  491. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_fit_samples.js +92 -0
  492. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_layout.d.ts +39 -0
  493. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_layout.d.ts.map +1 -0
  494. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_layout.js +44 -0
  495. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_solve_matrix.d.ts +42 -0
  496. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_solve_matrix.d.ts.map +1 -0
  497. package/src/shade/renderer/light/environment/sh_exponential/sh_exp_solve_matrix.js +123 -0
  498. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.d.ts +30 -0
  499. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.d.ts.map +1 -0
  500. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.js +147 -0
  501. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_log_floor.d.ts +24 -0
  502. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_log_floor.d.ts.map +1 -0
  503. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_log_floor.js +79 -0
  504. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_project.d.ts +17 -0
  505. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_project.d.ts.map +1 -0
  506. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_project.js +99 -0
  507. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_reference_reflectance.d.ts +24 -0
  508. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_reference_reflectance.d.ts.map +1 -0
  509. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_reference_reflectance.js +104 -0
  510. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_solve.d.ts +12 -0
  511. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_solve.d.ts.map +1 -0
  512. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_solve.js +93 -0
  513. package/src/shade/renderer/lightmap/GPULightMap.d.ts.map +1 -1
  514. package/src/shade/renderer/lightmap/GPULightMap.js +1 -0
  515. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_irradiance_flat.d.ts.map +1 -1
  516. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_irradiance_flat.js +4 -0
  517. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_sh.d.ts.map +1 -1
  518. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_sh.js +4 -0
  519. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  520. package/src/shade/renderer/material/PIXEL_TEXTURE_ORM.d.ts +1 -1
  521. package/src/shade/renderer/material/PIXEL_TEXTURE_ORM.d.ts.map +1 -1
  522. package/src/shade/renderer/material/StandardShadeMaterial.d.ts +15 -0
  523. package/src/shade/renderer/material/StandardShadeMaterial.d.ts.map +1 -1
  524. package/src/shade/renderer/material/StandardShadeMaterial.js +17 -0
  525. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +36 -2
  526. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  527. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +218 -18
  528. package/src/shade/renderer/material/resident/INDIRECT_MATERIAL_STRUCT.d.ts.map +1 -1
  529. package/src/shade/renderer/material/resident/INDIRECT_MATERIAL_STRUCT.js +10 -0
  530. package/src/shade/renderer/medium/MEDIA_TABLE_STRUCT.d.ts +17 -0
  531. package/src/shade/renderer/medium/MEDIA_TABLE_STRUCT.d.ts.map +1 -0
  532. package/src/shade/renderer/medium/MEDIA_TABLE_STRUCT.js +24 -0
  533. package/src/shade/renderer/medium/MEDIUM_PHASE_LOBE_KIND.d.ts +24 -0
  534. package/src/shade/renderer/medium/MEDIUM_PHASE_LOBE_KIND.d.ts.map +1 -0
  535. package/src/shade/renderer/medium/MEDIUM_PHASE_LOBE_KIND.js +18 -0
  536. package/src/shade/renderer/medium/MEDIUM_STRUCT.d.ts +20 -0
  537. package/src/shade/renderer/medium/MEDIUM_STRUCT.d.ts.map +1 -0
  538. package/src/shade/renderer/medium/MEDIUM_STRUCT.js +43 -0
  539. package/src/shade/renderer/medium/ParticipatingMedium.d.ts +108 -0
  540. package/src/shade/renderer/medium/ParticipatingMedium.d.ts.map +1 -0
  541. package/src/shade/renderer/medium/ParticipatingMedium.js +204 -0
  542. package/src/shade/renderer/medium/chunk_medium_lobe_phase.d.ts +13 -0
  543. package/src/shade/renderer/medium/chunk_medium_lobe_phase.d.ts.map +1 -0
  544. package/src/shade/renderer/medium/chunk_medium_lobe_phase.js +33 -0
  545. package/src/shade/renderer/medium/participating_medium_serialization.d.ts +32 -0
  546. package/src/shade/renderer/medium/participating_medium_serialization.d.ts.map +1 -0
  547. package/src/shade/renderer/medium/participating_medium_serialization.js +181 -0
  548. package/src/shade/renderer/medium/phase/MediumPhaseFunction.d.ts +48 -0
  549. package/src/shade/renderer/medium/phase/MediumPhaseFunction.d.ts.map +1 -0
  550. package/src/shade/renderer/medium/phase/MediumPhaseFunction.js +42 -0
  551. package/src/shade/renderer/medium/phase/PhaseCornetteShanks.d.ts +23 -0
  552. package/src/shade/renderer/medium/phase/PhaseCornetteShanks.d.ts.map +1 -0
  553. package/src/shade/renderer/medium/phase/PhaseCornetteShanks.js +45 -0
  554. package/src/shade/renderer/medium/phase/PhaseDraine.d.ts +30 -0
  555. package/src/shade/renderer/medium/phase/PhaseDraine.d.ts.map +1 -0
  556. package/src/shade/renderer/medium/phase/PhaseDraine.js +58 -0
  557. package/src/shade/renderer/medium/phase/PhaseFournierForand.d.ts +24 -0
  558. package/src/shade/renderer/medium/phase/PhaseFournierForand.d.ts.map +1 -0
  559. package/src/shade/renderer/medium/phase/PhaseFournierForand.js +53 -0
  560. package/src/shade/renderer/medium/phase/PhaseHenyeyGreenstein.d.ts +21 -0
  561. package/src/shade/renderer/medium/phase/PhaseHenyeyGreenstein.d.ts.map +1 -0
  562. package/src/shade/renderer/medium/phase/PhaseHenyeyGreenstein.js +43 -0
  563. package/src/shade/renderer/medium/phase/PhaseJendersieDeon.d.ts +25 -0
  564. package/src/shade/renderer/medium/phase/PhaseJendersieDeon.d.ts.map +1 -0
  565. package/src/shade/renderer/medium/phase/PhaseJendersieDeon.js +66 -0
  566. package/src/shade/renderer/medium/phase/PhaseRayleigh.d.ts +9 -0
  567. package/src/shade/renderer/medium/phase/PhaseRayleigh.d.ts.map +1 -0
  568. package/src/shade/renderer/medium/phase/PhaseRayleigh.js +21 -0
  569. package/src/shade/renderer/medium/phase/PhaseWaterMolecular.d.ts +28 -0
  570. package/src/shade/renderer/medium/phase/PhaseWaterMolecular.d.ts.map +1 -0
  571. package/src/shade/renderer/medium/phase/PhaseWaterMolecular.js +49 -0
  572. package/src/shade/renderer/medium/water/WATER_DEFAULT_PATH_LENGTHS.d.ts +10 -0
  573. package/src/shade/renderer/medium/water/WATER_DEFAULT_PATH_LENGTHS.d.ts.map +1 -0
  574. package/src/shade/renderer/medium/water/WATER_DEFAULT_PATH_LENGTHS.js +25 -0
  575. package/src/shade/renderer/medium/water/water_iop_from_appearance.d.ts +47 -0
  576. package/src/shade/renderer/medium/water/water_iop_from_appearance.d.ts.map +1 -0
  577. package/src/shade/renderer/medium/water/water_iop_from_appearance.js +174 -0
  578. package/src/shade/renderer/medium/water/water_iop_from_constituents.d.ts +57 -0
  579. package/src/shade/renderer/medium/water/water_iop_from_constituents.d.ts.map +1 -0
  580. package/src/shade/renderer/medium/water/water_iop_from_constituents.js +242 -0
  581. package/src/shade/renderer/medium/water/water_iop_jerlov.d.ts +41 -0
  582. package/src/shade/renderer/medium/water/water_iop_jerlov.d.ts.map +1 -0
  583. package/src/shade/renderer/medium/water/water_iop_jerlov.js +231 -0
  584. package/src/shade/renderer/medium/water/water_iop_spectral_reference.d.ts +66 -0
  585. package/src/shade/renderer/medium/water/water_iop_spectral_reference.d.ts.map +1 -0
  586. package/src/shade/renderer/medium/water/water_iop_spectral_reference.js +223 -0
  587. package/src/shade/renderer/medium/water/water_medium_from_iop.d.ts +96 -0
  588. package/src/shade/renderer/medium/water/water_medium_from_iop.d.ts.map +1 -0
  589. package/src/shade/renderer/medium/water/water_medium_from_iop.js +145 -0
  590. package/src/shade/renderer/medium/water/water_presets.d.ts +69 -0
  591. package/src/shade/renderer/medium/water/water_presets.d.ts.map +1 -0
  592. package/src/shade/renderer/medium/water/water_presets.js +119 -0
  593. package/src/shade/renderer/medium/water/water_rgb_from_spectral.d.ts +69 -0
  594. package/src/shade/renderer/medium/water/water_rgb_from_spectral.d.ts.map +1 -0
  595. package/src/shade/renderer/medium/water/water_rgb_from_spectral.js +341 -0
  596. package/src/shade/renderer/particles/DESIGN.md +4 -2
  597. package/src/shade/renderer/particles/bounds/chunk_particle_bounds.d.ts +1 -1
  598. package/src/shade/renderer/particles/bounds/chunk_particle_bounds.d.ts.map +1 -1
  599. package/src/shade/renderer/particles/shaders/chunk_particle_avboit_voxel_fade.d.ts +26 -0
  600. package/src/shade/renderer/particles/shaders/chunk_particle_avboit_voxel_fade.d.ts.map +1 -0
  601. package/src/shade/renderer/particles/shaders/chunk_particle_avboit_voxel_fade.js +71 -0
  602. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts.map +1 -1
  603. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +16 -1
  604. package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.d.ts.map +1 -1
  605. package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.js +6 -13
  606. package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +520 -0
  607. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.d.ts.map +1 -1
  608. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.js +1 -0
  609. package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.d.ts.map +1 -1
  610. package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.js +2 -0
  611. package/src/shade/renderer/path_tracer/graph_path_trace_scene.d.ts.map +1 -1
  612. package/src/shade/renderer/path_tracer/graph_path_trace_scene.js +188 -185
  613. package/src/shade/renderer/path_tracer/shader/STANDARD_MATERIAL_DATA_STRUCT.d.ts.map +1 -1
  614. package/src/shade/renderer/path_tracer/shader/STANDARD_MATERIAL_DATA_STRUCT.js +4 -1
  615. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_tangent_normal.d.ts +13 -0
  616. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_tangent_normal.d.ts.map +1 -0
  617. package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_tangent_normal.js +29 -0
  618. package/src/shade/renderer/path_tracer/shader/chunk_render_trace_path.d.ts +79 -0
  619. package/src/shade/renderer/path_tracer/shader/chunk_render_trace_path.d.ts.map +1 -1
  620. package/src/shade/renderer/path_tracer/shader/chunk_render_trace_path.js +360 -70
  621. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts.map +1 -1
  622. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js +4 -3
  623. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_beam.d.ts +24 -0
  624. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_beam.d.ts.map +1 -0
  625. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_beam.js +99 -0
  626. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_connect.d.ts +68 -0
  627. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_connect.d.ts.map +1 -0
  628. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_connect.js +263 -0
  629. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_constraint.d.ts +55 -0
  630. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_constraint.d.ts.map +1 -0
  631. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_constraint.js +148 -0
  632. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light.d.ts +17 -0
  633. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light.d.ts.map +1 -0
  634. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light.js +41 -0
  635. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light_sample.d.ts +20 -0
  636. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light_sample.d.ts.map +1 -0
  637. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light_sample.js +105 -0
  638. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle.d.ts +28 -0
  639. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle.d.ts.map +1 -0
  640. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle.js +97 -0
  641. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle_of_hit.d.ts +12 -0
  642. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle_of_hit.d.ts.map +1 -0
  643. package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle_of_hit.js +46 -0
  644. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_interface.d.ts +25 -0
  645. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_interface.d.ts.map +1 -0
  646. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_interface.js +75 -0
  647. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_path.d.ts +54 -0
  648. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_path.d.ts.map +1 -0
  649. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_path.js +245 -0
  650. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_phase.d.ts +42 -0
  651. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_phase.d.ts.map +1 -0
  652. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_phase.js +236 -0
  653. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_scatter_nee.d.ts +24 -0
  654. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_scatter_nee.d.ts.map +1 -0
  655. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_scatter_nee.js +97 -0
  656. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_stack.d.ts +57 -0
  657. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_stack.d.ts.map +1 -0
  658. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_stack.js +222 -0
  659. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_transport.d.ts +88 -0
  660. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_transport.d.ts.map +1 -0
  661. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_transport.js +304 -0
  662. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_volumes.d.ts +35 -0
  663. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_volumes.d.ts.map +1 -0
  664. package/src/shade/renderer/path_tracer/shader/media/chunk_medium_volumes.js +137 -0
  665. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  666. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.d.ts.map +1 -1
  667. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.js +85 -84
  668. package/src/shade/renderer/rasterize/native/avboit/AVBOITSettings.d.ts +8 -0
  669. package/src/shade/renderer/rasterize/native/avboit/AVBOITSettings.d.ts.map +1 -1
  670. package/src/shade/renderer/rasterize/native/avboit/AVBOITSettings.js +9 -0
  671. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts +28 -1
  672. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts.map +1 -1
  673. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js +17 -1
  674. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.d.ts +13 -7
  675. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.d.ts.map +1 -1
  676. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.js +13 -7
  677. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +309 -0
  678. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +54 -9
  679. package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.d.ts.map +1 -1
  680. package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.js +9 -1
  681. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_footprint_bias.d.ts +33 -0
  682. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_footprint_bias.d.ts.map +1 -0
  683. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_footprint_bias.js +45 -0
  684. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_integral_depth_coordinate.d.ts +4 -0
  685. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_integral_depth_coordinate.d.ts.map +1 -1
  686. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_integral_depth_coordinate.js +6 -2
  687. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_renormalization.d.ts +4 -0
  688. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_renormalization.d.ts.map +1 -0
  689. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_renormalization.js +11 -0
  690. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_rgb_contribution.d.ts +4 -0
  691. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_rgb_contribution.d.ts.map +1 -0
  692. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_rgb_contribution.js +19 -0
  693. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_sample_transmittance.d.ts +2 -1
  694. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_sample_transmittance.d.ts.map +1 -1
  695. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_sample_transmittance.js +4 -2
  696. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_splat_slice.d.ts.map +1 -1
  697. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_splat_slice.js +5 -3
  698. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_split_counts.d.ts +4 -0
  699. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_split_counts.d.ts.map +1 -0
  700. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_split_counts.js +8 -0
  701. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_level_word.d.ts +14 -0
  702. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_level_word.d.ts.map +1 -0
  703. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_level_word.js +28 -0
  704. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_lut_entry.d.ts +10 -0
  705. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_lut_entry.d.ts.map +1 -0
  706. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_lut_entry.js +18 -0
  707. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_marks.d.ts +19 -0
  708. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_marks.d.ts.map +1 -0
  709. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_marks.js +44 -0
  710. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_step_count.d.ts +10 -0
  711. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_step_count.d.ts.map +1 -0
  712. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_step_count.js +17 -0
  713. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts +1 -1
  714. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  715. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +23 -2
  716. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_build_warp.d.ts.map +1 -1
  717. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_build_warp.js +163 -64
  718. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_resolve.d.ts.map +1 -1
  719. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_resolve.js +4 -8
  720. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  721. package/src/shade/renderer/scene/GPUSceneContext.js +4 -1
  722. package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.d.ts.map +1 -1
  723. package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.js +5 -0
  724. package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.d.ts.map +1 -1
  725. package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.d.ts +12 -1
  726. package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.d.ts.map +1 -1
  727. package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.js +22 -3
  728. package/src/shade/renderer/shader/chunk/atmosphere/phase/chunk_sample_henyey_greenstein_cos.d.ts +18 -0
  729. package/src/shade/renderer/shader/chunk/atmosphere/phase/chunk_sample_henyey_greenstein_cos.d.ts.map +1 -0
  730. package/src/shade/renderer/shader/chunk/atmosphere/phase/chunk_sample_henyey_greenstein_cos.js +31 -0
  731. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_phase_fournier_forand.d.ts +43 -0
  732. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_phase_fournier_forand.d.ts.map +1 -0
  733. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_phase_fournier_forand.js +153 -0
  734. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_sample_fournier_forand.d.ts +13 -0
  735. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_sample_fournier_forand.d.ts.map +1 -0
  736. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_sample_fournier_forand.js +57 -0
  737. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/fournier_forand_parameters_from_backscatter.d.ts +45 -0
  738. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/fournier_forand_parameters_from_backscatter.d.ts.map +1 -0
  739. package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/fournier_forand_parameters_from_backscatter.js +109 -0
  740. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_phase_draine.d.ts.map +1 -1
  741. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_phase_draine.js +6 -3
  742. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine.d.ts +24 -0
  743. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine.d.ts.map +1 -0
  744. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine.js +49 -0
  745. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine_cos.d.ts +18 -0
  746. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine_cos.d.ts.map +1 -1
  747. package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine_cos.js +18 -0
  748. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_add_f32_storage.d.ts +12 -0
  749. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_add_f32_storage.d.ts.map +1 -0
  750. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_max_f32_storage.d.ts +12 -0
  751. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_max_f32_storage.d.ts.map +1 -0
  752. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_min_f32_storage.d.ts +12 -0
  753. package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_min_f32_storage.d.ts.map +1 -0
  754. package/src/shade/renderer/shader/chunk/brdf/dfg/chunk_integrate_brdf.d.ts +3 -0
  755. package/src/shade/renderer/shader/chunk/brdf/dfg/chunk_integrate_brdf.d.ts.map +1 -1
  756. package/src/shade/renderer/shader/chunk/brdf/dfg/chunk_integrate_brdf.js +3 -0
  757. package/src/shade/renderer/shader/chunk/brdf/dfg/shader_split_sum_dgf_lut.d.ts.map +1 -1
  758. package/src/shade/renderer/shader/chunk/brdf/dfg/shader_split_sum_dgf_lut.js +3 -1
  759. package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_dielectric.d.ts +24 -0
  760. package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_dielectric.d.ts.map +1 -0
  761. package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_dielectric.js +53 -0
  762. package/src/shade/renderer/shader/chunk/geometry/plane/{plane_ray_raycast.d.ts → chunk_plane_ray_raycast.d.ts} +2 -2
  763. package/src/shade/renderer/shader/chunk/geometry/plane/chunk_plane_ray_raycast.d.ts.map +1 -0
  764. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_scene_point_query_is_inside_geometry.d.ts +7 -1
  765. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_scene_point_query_is_inside_geometry.d.ts.map +1 -1
  766. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_scene_point_query_is_inside_geometry.js +12 -60
  767. package/src/shade/renderer/shader/chunk/geometry/sphere/chunk_compute_spherical_excess.d.ts +26 -6
  768. package/src/shade/renderer/shader/chunk/geometry/sphere/chunk_compute_spherical_excess.d.ts.map +1 -1
  769. package/src/shade/renderer/shader/chunk/geometry/sphere/chunk_compute_spherical_excess.js +30 -20
  770. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_directional.d.ts.map +1 -1
  771. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_directional.js +3 -1
  772. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_point.d.ts.map +1 -1
  773. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_point.js +3 -1
  774. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_spot.d.ts.map +1 -1
  775. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_spot.js +3 -1
  776. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_receiver_cosine.d.ts +14 -0
  777. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_receiver_cosine.d.ts.map +1 -0
  778. package/src/shade/renderer/shader/chunk/light/sample/chunk_light_receiver_cosine.js +25 -0
  779. package/src/shade/renderer/shader/chunk/light/sample/chunk_select_light_importance.d.ts +9 -2
  780. package/src/shade/renderer/shader/chunk/light/sample/chunk_select_light_importance.d.ts.map +1 -1
  781. package/src/shade/renderer/shader/chunk/light/sample/chunk_select_light_importance.js +33 -10
  782. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_newton_ray.d.ts +4 -0
  783. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_newton_ray.d.ts.map +1 -0
  784. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_newton_ray.js +135 -0
  785. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_band_of_index.d.ts +14 -0
  786. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_band_of_index.d.ts.map +1 -0
  787. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_band_of_index.js +30 -0
  788. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_basis_function.d.ts +19 -0
  789. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_basis_function.d.ts.map +1 -0
  790. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_basis_function.js +73 -0
  791. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_vmf_window.d.ts +19 -0
  792. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_vmf_window.d.ts.map +1 -0
  793. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_vmf_window.js +25 -0
  794. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/SH_EXP_P4Q2_PROBE_STRUCT.d.ts +17 -0
  795. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/SH_EXP_P4Q2_PROBE_STRUCT.d.ts.map +1 -0
  796. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/SH_EXP_P4Q2_PROBE_STRUCT.js +23 -0
  797. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_p4q2_base_reflectance.d.ts +26 -0
  798. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_p4q2_base_reflectance.d.ts.map +1 -0
  799. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_p4q2_base_reflectance.js +55 -0
  800. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_split_sum.d.ts +52 -0
  801. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_split_sum.d.ts.map +1 -0
  802. package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_split_sum.js +65 -0
  803. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_basis_at.d.ts +13 -0
  804. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_basis_at.d.ts.map +1 -0
  805. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_basis_at.js +229 -0
  806. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_color_evaluate_vmf.d.ts +23 -0
  807. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_color_evaluate_vmf.d.ts.map +1 -0
  808. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_color_evaluate_vmf.js +278 -0
  809. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh2_color_evaluate_vmf.d.ts +23 -0
  810. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh2_color_evaluate_vmf.d.ts.map +1 -0
  811. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh2_color_evaluate_vmf.js +58 -0
  812. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh3_color_evaluate_vmf.d.ts +23 -0
  813. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh3_color_evaluate_vmf.d.ts.map +1 -0
  814. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh3_color_evaluate_vmf.js +75 -0
  815. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_basis_at.d.ts +13 -0
  816. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_basis_at.d.ts.map +1 -0
  817. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_basis_at.js +70 -0
  818. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_color_evaluate_vmf.d.ts +23 -0
  819. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_color_evaluate_vmf.d.ts.map +1 -0
  820. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_color_evaluate_vmf.js +95 -0
  821. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_basis_at.d.ts +13 -0
  822. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_basis_at.d.ts.map +1 -0
  823. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_basis_at.js +89 -0
  824. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_color_evaluate_vmf.d.ts +23 -0
  825. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_color_evaluate_vmf.d.ts.map +1 -0
  826. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_color_evaluate_vmf.js +118 -0
  827. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_basis_at.d.ts +13 -0
  828. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_basis_at.d.ts.map +1 -0
  829. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_basis_at.js +111 -0
  830. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_color_evaluate_vmf.d.ts +23 -0
  831. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_color_evaluate_vmf.d.ts.map +1 -0
  832. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_color_evaluate_vmf.js +144 -0
  833. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_basis_at.d.ts +13 -0
  834. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_basis_at.d.ts.map +1 -0
  835. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_basis_at.js +136 -0
  836. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_color_evaluate_vmf.d.ts +23 -0
  837. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_color_evaluate_vmf.d.ts.map +1 -0
  838. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_color_evaluate_vmf.js +173 -0
  839. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_basis_at.d.ts +13 -0
  840. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_basis_at.d.ts.map +1 -0
  841. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_basis_at.js +164 -0
  842. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_color_evaluate_vmf.d.ts +23 -0
  843. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_color_evaluate_vmf.d.ts.map +1 -0
  844. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_color_evaluate_vmf.js +205 -0
  845. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_basis_at.d.ts +13 -0
  846. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_basis_at.d.ts.map +1 -0
  847. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_basis_at.js +195 -0
  848. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_color_evaluate_vmf.d.ts +23 -0
  849. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_color_evaluate_vmf.d.ts.map +1 -0
  850. package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_color_evaluate_vmf.js +240 -0
  851. package/src/shade/renderer/shader/resource/ShaderResourceGroupDescriptor.d.ts +0 -6
  852. package/src/shade/renderer/shader/resource/ShaderResourceGroupDescriptor.d.ts.map +1 -1
  853. package/src/shade/renderer/shader/resource/ShaderResourceGroupDescriptor.js +46 -1
  854. package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.d.ts +7 -7
  855. package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.js +7 -7
  856. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.d.ts +42 -10
  857. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.d.ts.map +1 -1
  858. package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.js +104 -48
  859. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts +20 -4
  860. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  861. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +186 -74
  862. package/src/shade/renderer/shadow/map/Shadowmap.d.ts +2 -1
  863. package/src/shade/renderer/shadow/map/Shadowmap.d.ts.map +1 -1
  864. package/src/shade/renderer/shadow/map/Shadowmap.js +2 -1
  865. package/src/shade/renderer/shadow/map/csm/CSM_CASCADE_STRUCT.d.ts +30 -0
  866. package/src/shade/renderer/shadow/map/csm/CSM_CASCADE_STRUCT.d.ts.map +1 -0
  867. package/src/shade/renderer/shadow/map/csm/CSM_CASCADE_STRUCT.js +52 -0
  868. package/src/shade/renderer/shadow/map/csm/CSM_SHADOWMAP_STRUCT.d.ts +14 -0
  869. package/src/shade/renderer/shadow/map/csm/CSM_SHADOWMAP_STRUCT.d.ts.map +1 -0
  870. package/src/shade/renderer/shadow/map/csm/CSM_SHADOWMAP_STRUCT.js +20 -0
  871. package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_DEFAULT_CASCADE_COUNT.d.ts +8 -0
  872. package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_DEFAULT_CASCADE_COUNT.d.ts.map +1 -0
  873. package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_DEFAULT_CASCADE_COUNT.js +7 -0
  874. package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.d.ts +8 -6
  875. package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.d.ts.map +1 -1
  876. package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.js +28 -34
  877. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_csm_compute_cascade_blended.d.ts +3 -2
  878. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_csm_compute_cascade_blended.d.ts.map +1 -1
  879. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_csm_compute_cascade_blended.js +19 -14
  880. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.d.ts +4 -3
  881. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.d.ts.map +1 -1
  882. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.js +12 -7
  883. package/src/shade/renderer/shadow/map/shadowmap_budget_carry_over.d.ts +4 -4
  884. package/src/shade/renderer/shadow/map/shadowmap_budget_carry_over.js +4 -4
  885. package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.d.ts.map +1 -1
  886. package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.js +39 -17
  887. package/src/shade/renderer/shadow/vsm/GPUVirtualShadowTextureResidence.d.ts.map +1 -1
  888. package/src/shade/renderer/shadow/vsm/GPUVirtualShadowTextureResidence.js +5 -36
  889. package/src/shade/renderer/shadow/vsm/VSM_UNIFORMS_STRUCT.d.ts.map +1 -1
  890. package/src/shade/renderer/shadow/vsm/VSM_UNIFORMS_STRUCT.js +3 -2
  891. package/src/shade/renderer/shadow/vsm/VirtualShadowMap.d.ts +1 -1
  892. package/src/shade/renderer/shadow/vsm/VirtualShadowMap.d.ts.map +1 -1
  893. package/src/shade/renderer/shadow/vsm/VirtualShadowMap.js +49 -35
  894. package/src/shade/renderer/shadow/vsm/VirtualShadowmapRenderer.d.ts +4 -1
  895. package/src/shade/renderer/shadow/vsm/VirtualShadowmapRenderer.d.ts.map +1 -1
  896. package/src/shade/renderer/shadow/vsm/VirtualShadowmapRenderer.js +8 -9
  897. package/src/shade/renderer/shadow/vsm/debug_inspect_residence_buffer.js +2 -2
  898. package/src/shade/renderer/shadow/vsm/decode_tile_address.d.ts.map +1 -1
  899. package/src/shade/renderer/shadow/vsm/decode_tile_address.js +15 -23
  900. package/src/shade/renderer/shadow/vsm/vms_pipeline_compute_coarse_bounds.d.ts.map +1 -1
  901. package/src/shade/renderer/shadow/vsm/vms_pipeline_compute_coarse_bounds.js +6 -9
  902. package/src/shade/renderer/shadow/vsm/vsm_residence_pipeline.d.ts.map +1 -1
  903. package/src/shade/renderer/shadow/vsm/vsm_residence_pipeline.js +19 -9
  904. package/src/shade/renderer/shadow/vsm/vsm_tile_allocation_pipeline.d.ts.map +1 -1
  905. package/src/shade/renderer/shadow/vsm/vsm_tile_allocation_pipeline.js +18 -6
  906. package/src/shade/renderer/texture/util/BLACK_PIXEL_TEXTURE.d.ts +1 -1
  907. package/src/shade/renderer/texture/util/BLACK_PIXEL_TEXTURE.d.ts.map +1 -1
  908. package/src/shade/renderer/texture/util/PIXEL_TEXTURE_NORMAL.d.ts +18 -1
  909. package/src/shade/renderer/texture/util/PIXEL_TEXTURE_NORMAL.d.ts.map +1 -1
  910. package/src/shade/renderer/texture/util/PIXEL_TEXTURE_NORMAL.js +24 -3
  911. package/src/shade/renderer/texture/util/WHITE_PIXEL_TEXTURE.d.ts +1 -1
  912. package/src/shade/renderer/texture/util/WHITE_PIXEL_TEXTURE.d.ts.map +1 -1
  913. package/src/shade/renderer/texture/util/get_single_pixel_texture.d.ts +0 -1
  914. package/src/shade/renderer/texture/util/get_single_pixel_texture.d.ts.map +1 -1
  915. package/src/shade/renderer/texture/util/get_single_pixel_texture.js +2 -23
  916. package/src/shade/renderer/texture/util/single_pixel_texture_from_sampler.d.ts +11 -0
  917. package/src/shade/renderer/texture/util/single_pixel_texture_from_sampler.d.ts.map +1 -0
  918. package/src/shade/renderer/texture/util/single_pixel_texture_from_sampler.js +34 -0
  919. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.d.ts +17 -8
  920. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.d.ts.map +1 -1
  921. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.js +35 -64
  922. package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.d.ts +21 -0
  923. package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.d.ts.map +1 -0
  924. package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.js +70 -0
  925. package/src/shade/renderer/volumetrics/chunk_participating_media_volumes_iterate.d.ts +31 -0
  926. package/src/shade/renderer/volumetrics/chunk_participating_media_volumes_iterate.d.ts.map +1 -0
  927. package/src/shade/renderer/volumetrics/chunk_participating_media_volumes_iterate.js +86 -0
  928. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
  929. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +2 -4
  930. package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.d.ts.map +1 -1
  931. package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.js +6 -7
  932. package/src/shade/renderer/volumetrics/shader_volumetrics_build_scattering_lut.js +7 -7
  933. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +72 -27
  934. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  935. package/src/shade/renderer/water/GPUWaterRenderer.js +275 -138
  936. package/src/shade/renderer/water/chunk_water_gi.d.ts +14 -0
  937. package/src/shade/renderer/water/chunk_water_gi.d.ts.map +1 -0
  938. package/src/shade/renderer/water/chunk_water_gi.js +56 -0
  939. package/src/shade/renderer/water/chunk_water_transport.d.ts +7 -0
  940. package/src/shade/renderer/water/chunk_water_transport.d.ts.map +1 -0
  941. package/src/shade/renderer/water/chunk_water_transport.js +105 -0
  942. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +29 -0
  943. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +1 -0
  944. package/src/shade/renderer/water/medium/GPUWaterBodies.js +116 -0
  945. package/src/shade/renderer/water/medium/WATER_BODIES_STRUCT.d.ts +23 -0
  946. package/src/shade/renderer/water/medium/WATER_BODIES_STRUCT.d.ts.map +1 -0
  947. package/src/shade/renderer/water/medium/WATER_BODIES_STRUCT.js +42 -0
  948. package/src/shade/renderer/water/medium/chunk_water_medium_derived.d.ts +9 -0
  949. package/src/shade/renderer/water/medium/chunk_water_medium_derived.d.ts.map +1 -0
  950. package/src/shade/renderer/water/medium/chunk_water_medium_derived.js +55 -0
  951. package/src/shade/renderer/water/medium/chunk_water_medium_scattering.d.ts +8 -0
  952. package/src/shade/renderer/water/medium/chunk_water_medium_scattering.d.ts.map +1 -0
  953. package/src/shade/renderer/water/medium/chunk_water_medium_scattering.js +17 -0
  954. package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts +8 -0
  955. package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts.map +1 -0
  956. package/src/shade/renderer/water/medium/shader_water_bodies_derive.js +30 -0
  957. package/src/shade/renderer/water/optics/chunk_water_inscatter.d.ts +28 -0
  958. package/src/shade/renderer/water/optics/chunk_water_inscatter.d.ts.map +1 -0
  959. package/src/shade/renderer/water/optics/chunk_water_inscatter.js +123 -0
  960. package/src/shade/renderer/water/optics/chunk_water_light_transmittance.d.ts +23 -0
  961. package/src/shade/renderer/water/optics/chunk_water_light_transmittance.d.ts.map +1 -0
  962. package/src/shade/renderer/water/optics/chunk_water_light_transmittance_dry.d.ts +18 -0
  963. package/src/shade/renderer/water/optics/chunk_water_light_transmittance_dry.d.ts.map +1 -0
  964. package/src/shade/renderer/water/optics/chunk_water_optics.d.ts +12 -0
  965. package/src/shade/renderer/water/optics/chunk_water_optics.d.ts.map +1 -0
  966. package/src/shade/renderer/water/optics/chunk_water_optics.js +75 -0
  967. package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.d.ts +27 -0
  968. package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.d.ts.map +1 -0
  969. package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.js +74 -0
  970. package/src/shade/renderer/water/shader_water.d.ts +15 -26
  971. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  972. package/src/shade/renderer/water/shader_water.js +373 -213
  973. package/src/shade/renderer/water/shader_water_avboit.d.ts +12 -0
  974. package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -0
  975. package/src/shade/renderer/water/shader_water_avboit.js +268 -0
  976. package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts +98 -0
  977. package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts.map +1 -0
  978. package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.js +408 -0
  979. package/src/shade/renderer/water/surface/WATER_SURFACE_VERTEX_STRUCT.d.ts +12 -0
  980. package/src/shade/renderer/water/surface/WATER_SURFACE_VERTEX_STRUCT.d.ts.map +1 -0
  981. package/src/shade/renderer/water/surface/WATER_SURFACE_VERTEX_STRUCT.js +27 -0
  982. package/src/shade/renderer/water/surface/WATER_WAVE_PARAMETERS_STRUCT.d.ts +14 -0
  983. package/src/shade/renderer/water/surface/WATER_WAVE_PARAMETERS_STRUCT.d.ts.map +1 -0
  984. package/src/shade/renderer/water/surface/WATER_WAVE_PARAMETERS_STRUCT.js +49 -0
  985. package/src/shade/renderer/water/surface/chunk_water_wave.d.ts +15 -0
  986. package/src/shade/renderer/water/surface/chunk_water_wave.d.ts.map +1 -0
  987. package/src/shade/renderer/water/surface/chunk_water_wave.js +48 -0
  988. package/src/shade/renderer/water/surface/chunk_water_wave_grid.d.ts +9 -0
  989. package/src/shade/renderer/water/surface/chunk_water_wave_grid.d.ts.map +1 -0
  990. package/src/shade/renderer/water/surface/chunk_water_wave_grid.js +47 -0
  991. package/src/shade/renderer/water/surface/shader_water_surface_write.d.ts +10 -0
  992. package/src/shade/renderer/water/surface/shader_water_surface_write.d.ts.map +1 -0
  993. package/src/shade/renderer/water/surface/shader_water_surface_write.js +51 -0
  994. package/src/shade/renderer/water/surface/shader_water_wave_step.d.ts +12 -0
  995. package/src/shade/renderer/water/surface/shader_water_wave_step.d.ts.map +1 -0
  996. package/src/shade/renderer/water/surface/shader_water_wave_step.js +54 -0
  997. package/src/shade/renderer/water/surface/water_surface_grid_indices.d.ts +13 -0
  998. package/src/shade/renderer/water/surface/water_surface_grid_indices.d.ts.map +1 -0
  999. package/src/shade/renderer/water/surface/water_surface_grid_indices.js +39 -0
  1000. package/src/shade/renderer/water/water_missing_receiver_path.d.ts +17 -0
  1001. package/src/shade/renderer/water/water_missing_receiver_path.d.ts.map +1 -0
  1002. package/src/shade/renderer/water/water_missing_receiver_path.js +40 -0
  1003. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
  1004. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +247 -23
  1005. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts +18 -14
  1006. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  1007. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +61 -6
  1008. package/src/shade/renderer/animation/skinning/bounds_refresh/chunk_aabb_atomic_f32.d.ts +0 -29
  1009. package/src/shade/renderer/animation/skinning/bounds_refresh/chunk_aabb_atomic_f32.d.ts.map +0 -1
  1010. package/src/shade/renderer/shader/chunk/geometry/plane/plane_ray_raycast.d.ts.map +0 -1
  1011. package/src/shade/renderer/shadow/map/csm/CSM_CASTER_RANGE_STRUCT.d.ts +0 -29
  1012. package/src/shade/renderer/shadow/map/csm/CSM_CASTER_RANGE_STRUCT.d.ts.map +0 -1
  1013. package/src/shade/renderer/shadow/map/csm/CSM_CASTER_RANGE_STRUCT.js +0 -43
  1014. package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_CASCADE_COUNT.d.ts +0 -7
  1015. package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_CASCADE_COUNT.d.ts.map +0 -1
  1016. package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_CASCADE_COUNT.js +0 -6
@@ -1,52 +1,161 @@
1
1
  import { assert } from "../../../core/assert.js";
2
2
  import { bvh_query_user_data_intersects_aabb } from "../../../core/bvh2/bvh3/query/bvh_query_user_data_intersects_aabb.js";
3
3
  import { returnTrue } from "../../../core/function/returnTrue.js";
4
+ import { lowbias32 } from "../../../core/math/hash/lowbias32.js";
4
5
  import { aabb3_transform_oriented } from "../../../core/geom/3d/aabb/aabb3_transform_oriented.js";
6
+ import { aabb3_transform_oriented_inverse } from "../../../core/geom/3d/aabb/aabb3_transform_oriented_inverse.js";
5
7
  import { body_id_index } from "../body/BodyStorage.js";
6
8
  import { gjk } from "../../../core/geom/3d/gjk/gjk.js";
9
+ import {
10
+ GJK_DISTANCE_RELATIVE_GAP,
11
+ gjk_distance,
12
+ gjk_simplex_translate_b,
13
+ GJKSimplex
14
+ } from "../../../core/geom/3d/gjk/gjk_distance.js";
7
15
  import { mpr } from "../../../core/geom/3d/gjk/mpr.js";
8
16
  import { PosedShape3D } from "../../../core/geom/3d/shape/PosedShape3D.js";
17
+ import { Triangle3D } from "../../../core/geom/3d/shape/Triangle3D.js";
18
+ import { tri3_closest_point } from "../../../core/geom/3d/triangle/tri3_closest_point.js";
19
+ import { v3_quaternion_apply } from "../../../core/geom/vec3/v3_quaternion_apply.js";
20
+ import { v3_quaternion_apply_inverse } from "../../../core/geom/vec3/v3_quaternion_apply_inverse.js";
21
+ import { TriangleEdgeFlags } from "../../../core/geom/3d/triangle/TriangleEdgeFlags.js";
22
+ import { decompose_to_triangles } from "../narrowphase/decomposition/decompose_to_triangles.js";
23
+ import { TRIANGLE_FEATURE_ID_OFFSET, TRIANGLE_FLOAT_STRIDE } from "../narrowphase/decomposition/triangle_buffer_layout.js";
24
+ import { triangle_edge_flags } from "../narrowphase/decomposition/triangle_edge_flags.js";
25
+ import { heightmap_box_triangles } from "../narrowphase/refine_ray_concave.js";
26
+ import { rigid_subject_pose } from "../narrowphase/rigid_subject_pose.js";
27
+ import { PENETRATION_SLOP } from "../solver/solve_contacts.js";
28
+ import { Transform64 } from "../../ecs/transform/Transform64.js";
9
29
 
10
30
  /**
11
- * Bisection halts when the search interval is below this fraction of
12
- * the sweep length. With `MAX_BISECTION = 32` and a unit-length sweep
13
- * the achieved precision is ≈ 1 / 2³² (well below any practical
14
- * world-scale tolerance), but we cap on relative tolerance so a
15
- * 10 m sweep doesn't insist on sub-micron precision.
31
+ * The face shortcut's probe distance, and the bracket search's stopping width,
32
+ * as a fraction of the sweep length: precise enough for any practical sweep
33
+ * without a 10 m sweep insisting on sub-micron precision.
16
34
  * @type {number}
17
35
  */
18
36
  const TOI_TOLERANCE = 1e-4;
19
37
 
20
38
  /**
21
- * Maximum bisection iterations. `log2(1 / TOI_TOLERANCE)` is enough
22
- * for any practical sweep; this is the hard upper bound to prevent
39
+ * Maximum bisection iterations of the bracket search. `log2(1 / TOI_TOLERANCE)`
40
+ * is enough for any practical sweep; this is the hard upper bound to prevent
23
41
  * pathological inputs (NaN ray, etc.) from hanging the query.
24
42
  * @type {number}
25
43
  */
26
44
  const MAX_BISECTION = 32;
27
45
 
28
46
  /**
29
- * Coarse linear-search resolution for finding *any* overlap moment
30
- * along the per-candidate AABB-overlap interval (NOT the full sweep —
31
- * see `aabb_overlap_interval` for the analytical narrowing that runs
32
- * upstream). 32 substeps over the AABB-overlap window is dense enough
33
- * for any sweep where the shape's smallest extent is on the order of
34
- * its AABB's extent (true for spheres / boxes / capsules); the slab
35
- * intersection keeps that interval small even for kilometre-scale
36
- * sweeps, so this isn't the limit it would otherwise be.
47
+ * Samples of the bracket search's coarse scan for an overlapping moment. Only
48
+ * the fallback for an advancement that did not converge: 32 samples step clean
49
+ * over any feature thinner than 1/32 of the window they cover (a thin beam, a
50
+ * plank or a 1 mm box at an oblique pose — 186 of 300 crossings of a 2 cm beam
51
+ * at 45° were missed when this was the search).
37
52
  * @type {number}
38
53
  */
39
54
  const COARSE_STEPS = 32;
40
55
 
56
+ /**
57
+ * The separation at which conservative advancement parks the swept shape short
58
+ * of a candidate: the reported pose is apart from it by at least this, and by
59
+ * no more than this plus {@link ADVANCE_TOLERANCE}. Box3D parks at its linear
60
+ * slop; this sits a tenth of {@link CONTACT_EPSILON} out, far under any skin a
61
+ * caller keeps — and, being apart, it is a pose the narrowphase (which reports
62
+ * only positive penetration) does not call a contact.
63
+ * @type {number}
64
+ */
65
+ const ADVANCE_TARGET = 1e-5;
66
+
67
+ /**
68
+ * How far past {@link ADVANCE_TARGET} the advancement may stop. Box3D's ratio,
69
+ * a quarter of the target.
70
+ * @type {number}
71
+ */
72
+ const ADVANCE_TOLERANCE = 0.25 * ADVANCE_TARGET;
73
+
74
+ /**
75
+ * How exactly the advancement asks the distance kernel for its gap on the way
76
+ * in. It steps by the gap's lower bound along that bound's normal, which is
77
+ * safe for any plane that separates the two, and a sweep that does not close
78
+ * on such a plane can never touch: neither needs the closest one. A bound this
79
+ * far under the truth shortens a step by no more than this, so the step still
80
+ * lands within {@link ADVANCE_TOLERANCE} of the target. What the touch is
81
+ * judged and reported by — its gap, its normal, whether the sweep still closes
82
+ * on it — is asked again at full precision once the bound is inside the touch
83
+ * band: a 2.5e-6 gap leaves a curved pair's normal up to half a degree off, and
84
+ * a body the sweep stopped at a corner kept up to 2 m/s of slide along it.
85
+ *
86
+ * The kernel's own stop, a 1e-10 relative gap, ran a sphere's queries 23
87
+ * iterations on average, a quarter to a third of them to its 32-iteration
88
+ * cap; asked this loosely on the way in, a sphere's casts at a mesh or a
89
+ * heightmap run in 0.63 to 0.76 of the time, a capsule's in 0.78 to 0.89.
90
+ * @type {number}
91
+ */
92
+ const ADVANCE_DISTANCE_TOLERANCE = ADVANCE_TOLERANCE;
93
+
94
+ /**
95
+ * Contacts that begin within this of each other along the sweep are one
96
+ * instant for ranking candidates, and a triangle FACE struck in that instant
97
+ * wins over anything else — another triangle's edge, a convex collider. A
98
+ * shape landing flat on a mesh face meets the seam to the face's other half at
99
+ * the very instant it meets the face, and the edge's contact, read off the
100
+ * closest points, carries its own normal: a box landing on a cube face read
101
+ * one 21° off it.
102
+ * @type {number}
103
+ */
104
+ const SIMULTANEOUS = ADVANCE_TARGET;
105
+
106
+ /**
107
+ * Most distance queries one candidate's advancement runs before it is taken
108
+ * as not converging and handed to the bracket search. A flat approach closes
109
+ * in one step and a curved one in a few; a near-graze converges linearly, the
110
+ * gap quartering per step, and takes about ten from metres away.
111
+ * @type {number}
112
+ */
113
+ const ADVANCE_MAX_ITERATIONS = 32;
114
+
41
115
  const swept_aabb = new Float64Array(6);
42
116
  const local_aabb = new Float64Array(6);
43
117
  const initial_aabb = new Float64Array(6);
44
118
  const target_local_aabb = new Float64Array(6);
45
119
  const target_world_aabb = new Float64Array(6);
46
120
 
47
- // GJK simplex buffer (used to detect overlap during the bisection).
121
+ // GJK simplex buffer (used to detect overlap at a sample pose).
48
122
  const simplex_buf = new Float64Array(12);
49
123
 
124
+ /**
125
+ * The distance kernel's simplex and result, for conservative advancement. The
126
+ * simplex is carried from one advancement step to the next (the swept shape
127
+ * only translates between them), so each query starts where the last ended.
128
+ */
129
+ const distance_simplex = new GJKSimplex();
130
+ const distance_result = new Float64Array(10);
131
+
132
+ /**
133
+ * The contact normal of the last {@link cast_candidate} hit found by
134
+ * advancement — the separating direction from the candidate to the swept shape
135
+ * at the reported pose — and whether there is one ({@link cast_has_normal}).
136
+ */
137
+ const cast_normal = new Float64Array(3);
138
+ let cast_has_normal = false;
139
+
140
+ /**
141
+ * When the contact of the last {@link cast_candidate} hit actually begins, as
142
+ * near as it is known: a face's opening exactly, advancement's stop plus the
143
+ * gap it stopped short by over the closing speed, otherwise the reported time
144
+ * itself. Candidates are ranked by it rather than by the time they report: an
145
+ * advancement parks {@link ADVANCE_TARGET} short of its contact, so ranked by
146
+ * report it beat a face struck at the same instant — a sphere landing on a mesh
147
+ * face a millimetre from the seam took the neighbour's seam edge, 1.1° off.
148
+ * @type {number}
149
+ */
150
+ let cast_touch_t = 0;
151
+
152
+ /**
153
+ * Whether an advancement that ran out of iterations has been reported (once
154
+ * per process).
155
+ * @type {boolean}
156
+ */
157
+ let advance_exhaustion_reported = false;
158
+
50
159
  // Scratch for MPR's MTV output — same vec3 convention as EPA: direction
51
160
  // is "A's interior into B" with the magnitude being the depth. We use
52
161
  // MPR for the contact-normal recovery at the kiss point because it
@@ -57,7 +166,7 @@ const mpr_result = new Float64Array(3);
57
166
  /**
58
167
  * Long-lived PosedShape3D adapters for the swept shape and the
59
168
  * candidate's collider. Both are re-bound per query — the swept one
60
- * also gets its position field re-written every bisection step
169
+ * also gets its position field re-written at every sample pose
61
170
  * without re-running `setup()`, since shape and rotation are
62
171
  * constant through the sweep.
63
172
  */
@@ -71,6 +180,290 @@ const candidate_posed = new PosedShape3D();
71
180
  */
72
181
  let scratch_candidates = new Uint32Array(64);
73
182
 
183
+ /**
184
+ * "Already tested in this query" stamps, indexed by body index. Every collider
185
+ * of a body owns a separate broadphase leaf carrying that body's id, so a
186
+ * compound body arrives in {@link scratch_candidates} once per overlapping
187
+ * collider, and each arrival tests all of its colliders: a sweep along a row
188
+ * of K children ran K² collider casts (64 children: 4,096 casts, 0.54 ms).
189
+ * Stamped rather than cleared so the cost stays O(1) per query; the same
190
+ * scheme as `overlap_shape`'s.
191
+ * @type {Int32Array}
192
+ */
193
+ let scratch_seen = new Int32Array(64);
194
+
195
+ /**
196
+ * Stamp written into {@link scratch_seen} by the query in progress. Starts
197
+ * at 1 so a freshly zeroed (or freshly grown) slot never reads as seen.
198
+ * @type {number}
199
+ */
200
+ let seen_stamp = 0;
201
+
202
+ /**
203
+ * Whether a {@link shape_cast} is in progress. Its candidates, the triangles
204
+ * under it and every pose it tests are module state, so a cast started from
205
+ * inside one — from its filter — would overwrite the outer's half-finished
206
+ * scan (it reported no hit at all).
207
+ *
208
+ * The refusal is an assert, and a consumer's production build strips asserts:
209
+ * there nothing refuses it, and the outer cast misses without a word. A contact
210
+ * filter that runs a shapeCast is the case that bites: the CCD sweep asks it
211
+ * from inside its own cast, and with asserts stripped 200 of 200 fast spheres
212
+ * fired at a 2 cm wall went through it.
213
+ * @type {boolean}
214
+ */
215
+ let shape_cast_running = false;
216
+
217
+ const NESTED_SHAPE_CAST = 'shape_cast: called from inside a shape_cast, from its filter. A cast keeps its working'
218
+ + ' state in module scratch, so it cannot run inside another; a raycast or an overlap can. A contact filter'
219
+ + ' (PhysicsSystem.setContactFilter) is one such filter: the CCD sweep asks it from inside its cast.';
220
+
221
+ /**
222
+ * Most triangles of one concave collider a sweep is tested against at once: the
223
+ * bound on {@link triangle_buffer}'s growth, the same 65,536 as the narrowphase,
224
+ * `compute_penetration`, `overlap_shape` and the ray's `refine_ray_concave`.
225
+ * A sweep whose box takes in more is walked a piece at a time, as the ray's is,
226
+ * each piece short enough for its box to fit; only a piece too short to split
227
+ * further — a pile of triangles in one spot — is tested against the first
228
+ * 65,536, and that is reported once. Unbounded, the scratch grew with the
229
+ * sweep's box, 80 bytes a triangle: a 140 m cast across a 100 m floor of 320,000
230
+ * triangles kept 25.6 MB of them.
231
+ * @type {number}
232
+ */
233
+ const MAX_TRIANGLES_PER_PAIR = 1 << 16;
234
+
235
+ /**
236
+ * Shortest piece of a sweep worth splitting further, as a fraction of it — the
237
+ * ray's `MIN_PIECE_FRACTION`.
238
+ * @type {number}
239
+ */
240
+ const MIN_PIECE_FRACTION = 1 / (1 << 20);
241
+
242
+ /**
243
+ * Whether a piece too short to split has overflowed (once per process).
244
+ * @type {boolean}
245
+ */
246
+ let triangle_overflow_reported = false;
247
+
248
+ /** The swept box of one piece of the sweep. */
249
+ const piece_aabb = new Float64Array(6);
250
+
251
+ /**
252
+ * A concave target's triangles that may overlap the swept AABB, in the target's
253
+ * local frame, laid out as {@link decompose_to_triangles} writes them. Grows by
254
+ * doubling when a sweep covers more triangles than it holds — a dropped triangle
255
+ * is a hole in the surface, and the sweep would pass straight through it — up
256
+ * to {@link MAX_TRIANGLES_PER_PAIR}; the high-water mark is kept.
257
+ * @type {Float64Array}
258
+ */
259
+ let triangle_buffer = new Float64Array(256 * TRIANGLE_FLOAT_STRIDE);
260
+
261
+ /** The swept AABB re-expressed in a concave target's local frame. */
262
+ const concave_query_aabb = new Float64Array(6);
263
+
264
+ /**
265
+ * The world pose of the mesh or heightmap a rigid TransformedShape3D collider wraps: the collider's
266
+ * pose composed with the wrapper's ({@link rigid_subject_pose}). And its copy for the winner, which
267
+ * the next wrapped collider would otherwise move.
268
+ */
269
+ const subject_pose = new Transform64();
270
+ const best_subject_pose = new Transform64();
271
+
272
+ /**
273
+ * The triangle under test, in its collider's local frame, and that collider's
274
+ * transform. `candidate_is_triangle` says whether `candidate_posed` is posing
275
+ * it (rather than a convex collider).
276
+ */
277
+ const triangle_local = new Float64Array(9);
278
+ /** @type {Transform64|null} */
279
+ let triangle_tr = null;
280
+ let candidate_is_triangle = false;
281
+
282
+ /** The nearest triangle hit so far, kept for the normal probe after the scan. */
283
+ const best_triangle_local = new Float64Array(9);
284
+
285
+ /**
286
+ * {@link triangle_local} relative to an ANCHOR point on the triangle, and the
287
+ * flyweight shape bound to it; `candidate_posed` poses it at the anchor.
288
+ *
289
+ * Anchoring is what lets the convex machinery below run on a triangle. GJK
290
+ * does not care where a shape's origin is, but MPR takes the posed shape's
291
+ * position as a point INSIDE it (its V0) and reports the depth along the ray
292
+ * from V0 through the origin — and the normal probes close on that position.
293
+ * A mesh's own origin can be metres off any one triangle, so the anchor must
294
+ * be ON the triangle: its centroid while sweeping, and for a normal probe the
295
+ * point of the triangle nearest the probed shape, which aims MPR's ray down
296
+ * the contact normal. The centroid would not do there: from a contact near the
297
+ * triangle's edge — the seam between two triangles of one flat face — the ray
298
+ * to it leaves through the edge, and MPR reports a normal tilted ~16° off the
299
+ * face it struck.
300
+ */
301
+ const triangle_vertices = new Float64Array(9);
302
+ const triangle_shape = new Triangle3D().bind(triangle_vertices, 0);
303
+
304
+ /** Scratch: a point in a collider's frame, and the same point in world space. */
305
+ const anchor_local = new Float64Array(3);
306
+ const anchor_world = new Float64Array(3);
307
+
308
+ /**
309
+ * The part of the sweep that can touch the triangle under test, written by
310
+ * {@link load_triangle}; empty (`enter > exit`) when the sweep does not close
311
+ * on its plane.
312
+ */
313
+ let triangle_window_enter = Infinity;
314
+ let triangle_window_exit = -Infinity;
315
+
316
+ /**
317
+ * How far the swept shape reaches across the plane of the triangle under test
318
+ * as the sweep starts, when the sweep closes on that plane; written by
319
+ * {@link load_triangle}.
320
+ * @type {number}
321
+ */
322
+ let triangle_start_depth = Infinity;
323
+
324
+ /**
325
+ * When the swept shape's centre passes through the triangle under test — its
326
+ * plane, within its edges — or `Infinity` when it does not; written by
327
+ * {@link load_triangle}.
328
+ * @type {number}
329
+ */
330
+ let triangle_centre_crossing = Infinity;
331
+
332
+ /**
333
+ * Whether the triangle under test is one the sweep does NOT close on, so that
334
+ * its edges may still be met ({@link triangle_edges_to_sweep}); written by
335
+ * {@link load_triangle}, with what that needs: the concave shape it came from,
336
+ * the plane's unit normal in that shape's frame facing the sweep's start, and
337
+ * the nearest the sweep's origin comes to the plane.
338
+ * @type {boolean}
339
+ */
340
+ let triangle_edges_pending = false;
341
+ /** @type {AbstractShape3D|null} */
342
+ let triangle_edge_shape = null;
343
+ const triangle_plane_local = new Float64Array(3);
344
+ let triangle_edge_nearest = 0;
345
+
346
+ /**
347
+ * One edge of the triangle under test, as a segment — a triangle whose last two
348
+ * vertices coincide — relative to the edge's midpoint, where `candidate_posed`
349
+ * poses it.
350
+ */
351
+ const edge_vertices = new Float64Array(9);
352
+ const edge_shape = new Triangle3D().bind(edge_vertices, 0);
353
+
354
+ /**
355
+ * Where the triangle under test sits in {@link triangle_buffer}, and how many
356
+ * triangles of its collider the buffer holds — for the search for its
357
+ * neighbours in {@link triangle_edges_to_sweep}.
358
+ */
359
+ let triangle_offset = 0;
360
+ let triangle_count = 0;
361
+
362
+ /** Scratch: the contact normal of the best edge hit so far. */
363
+ const edge_normal = new Float64Array(3);
364
+
365
+ /**
366
+ * The point of the swept shape that reaches the plane of the triangle under
367
+ * test first — its support toward the plane — at the sweep's origin; written by
368
+ * {@link load_triangle} when the sweep closes on the plane.
369
+ */
370
+ const triangle_touch_origin = new Float64Array(3);
371
+
372
+ /**
373
+ * Whether the swept shape, reaching the plane of the triangle under test at
374
+ * `t` (its window's opening), reaches it on the triangle: whether the point
375
+ * that touches the plane first ({@link triangle_touch_origin}, carried along
376
+ * the sweep to `t`) lies within the triangle's edges. A point on an edge
377
+ * counts — the seam of a face split in two belongs to both halves.
378
+ *
379
+ * @param {number} dx unit sweep direction
380
+ * @param {number} dy
381
+ * @param {number} dz
382
+ * @param {number} t
383
+ * @returns {boolean}
384
+ */
385
+ function triangle_touch_inside(dx, dy, dz, t) {
386
+ const tr = triangle_tr;
387
+ v3_quaternion_apply_inverse(
388
+ anchor_local, 0,
389
+ triangle_touch_origin[0] + dx * t - tr.translation_x,
390
+ triangle_touch_origin[1] + dy * t - tr.translation_y,
391
+ triangle_touch_origin[2] + dz * t - tr.translation_z,
392
+ tr.rotation_x, tr.rotation_y, tr.rotation_z, tr.rotation_w
393
+ );
394
+ const px = anchor_local[0], py = anchor_local[1], pz = anchor_local[2];
395
+ const v = triangle_local;
396
+
397
+ const e1x = v[3] - v[0], e1y = v[4] - v[1], e1z = v[5] - v[2];
398
+ const e2x = v[6] - v[0], e2y = v[7] - v[1], e2z = v[8] - v[2];
399
+ const nx = e1y * e2z - e1z * e2y;
400
+ const ny = e1z * e2x - e1x * e2z;
401
+ const nz = e1x * e2y - e1y * e2x;
402
+
403
+ // the point is on the inner side of every edge: (edge × to-point) · normal ≥ 0
404
+ for (let k = 0; k < 3; k++) {
405
+ const a = 3 * k, b = 3 * ((k + 1) % 3);
406
+ const ex = v[b] - v[a], ey = v[b + 1] - v[a + 1], ez = v[b + 2] - v[a + 2];
407
+ const qx = px - v[a], qy = py - v[a + 1], qz = pz - v[a + 2];
408
+ const cx = ey * qz - ez * qy;
409
+ const cy = ez * qx - ex * qz;
410
+ const cz = ex * qy - ey * qx;
411
+ if (cx * nx + cy * ny + cz * nz < 0) return false;
412
+ }
413
+ return true;
414
+ }
415
+
416
+ /**
417
+ * The world normal of the triangle under test, facing the sweep's start —
418
+ * written by {@link load_triangle} when the sweep closes on it — and a support
419
+ * point scratch.
420
+ */
421
+ const plane_normal = new Float64Array(3);
422
+ const support_point = new Float64Array(3);
423
+
424
+ /** The reversed {@link plane_normal}, the direction the swept shape's near support is asked along. */
425
+ const plane_normal_reversed = new Float64Array(3);
426
+
427
+ /** The sweep's origin [0..2] and unit direction [3..5] in a concave collider's frame. */
428
+ const local_sweep = new Float64Array(6);
429
+
430
+ /**
431
+ * Whether the last {@link cast_candidate} hit was a triangle FACE contact —
432
+ * the shape met the triangle where it first reached its plane — whose normal
433
+ * is the face's.
434
+ * @type {boolean}
435
+ */
436
+ let cast_face = false;
437
+
438
+ /**
439
+ * The contact normal the nearest hit so far came with, when it came with one:
440
+ * a triangle face's, or the separating direction advancement stopped on.
441
+ */
442
+ const best_normal = new Float64Array(3);
443
+
444
+ /**
445
+ * What the last successful {@link shape_cast} struck, in world space: the
446
+ * convex collider, or the triangle of a concave one (its vertices kept here,
447
+ * the collider's frame). Read through {@link shape_cast_struck_support}.
448
+ */
449
+ const struck_posed = new PosedShape3D();
450
+ const struck_triangle_vertices = new Float64Array(9);
451
+ const struck_triangle_shape = new Triangle3D().bind(struck_triangle_vertices, 0);
452
+
453
+ /**
454
+ * {@link cast_candidate}: the sweep does not reach this candidate before the
455
+ * limit, or steps over it.
456
+ * @type {number}
457
+ */
458
+ const CAST_MISS = -1;
459
+
460
+ /**
461
+ * {@link cast_candidate}: the shape starts inside this candidate and cannot
462
+ * move at all.
463
+ * @type {number}
464
+ */
465
+ const CAST_BLOCKED = -2;
466
+
74
467
  /**
75
468
  * Depths at or below this are not a contact. A kissing pair is not always
76
469
  * numerically exact — MPR returns a few microns of "overlap" for a sphere
@@ -107,10 +500,65 @@ const NORMAL_PROBE_OFFSET = 0.01;
107
500
  */
108
501
  const probe_normal = new Float64Array(3);
109
502
 
503
+ /**
504
+ * Pose {@link triangle_local} as `candidate_posed`, anchored at the point
505
+ * `(lx, ly, lz)` of the triangle (in its collider's frame) — see
506
+ * {@link triangle_vertices}.
507
+ *
508
+ * @param {number} lx
509
+ * @param {number} ly
510
+ * @param {number} lz
511
+ */
512
+ function anchor_triangle(lx, ly, lz) {
513
+ for (let i = 0; i < 9; i += 3) {
514
+ triangle_vertices[i] = triangle_local[i] - lx;
515
+ triangle_vertices[i + 1] = triangle_local[i + 1] - ly;
516
+ triangle_vertices[i + 2] = triangle_local[i + 2] - lz;
517
+ }
518
+
519
+ const tr = triangle_tr;
520
+ const qx = tr.rotation_x, qy = tr.rotation_y, qz = tr.rotation_z, qw = tr.rotation_w;
521
+ v3_quaternion_apply(anchor_world, 0, lx, ly, lz, qx, qy, qz, qw);
522
+ candidate_posed.setup_components(
523
+ triangle_shape,
524
+ tr.translation_x + anchor_world[0],
525
+ tr.translation_y + anchor_world[1],
526
+ tr.translation_z + anchor_world[2],
527
+ qx, qy, qz, qw
528
+ );
529
+ }
530
+
531
+ /**
532
+ * Anchor the triangle under test at its point nearest the world point
533
+ * `(px, py, pz)`.
534
+ *
535
+ * @param {number} px
536
+ * @param {number} py
537
+ * @param {number} pz
538
+ */
539
+ function anchor_triangle_nearest(px, py, pz) {
540
+ const tr = triangle_tr;
541
+ v3_quaternion_apply_inverse(
542
+ anchor_local, 0,
543
+ px - tr.translation_x, py - tr.translation_y, pz - tr.translation_z,
544
+ tr.rotation_x, tr.rotation_y, tr.rotation_z, tr.rotation_w
545
+ );
546
+ const v = triangle_local;
547
+ tri3_closest_point(
548
+ anchor_local, 0,
549
+ anchor_local[0], anchor_local[1], anchor_local[2],
550
+ v[0], v[1], v[2],
551
+ v[3], v[4], v[5],
552
+ v[6], v[7], v[8]
553
+ );
554
+ anchor_triangle(anchor_local[0], anchor_local[1], anchor_local[2]);
555
+ }
556
+
110
557
  /**
111
558
  * Recover the target's outward surface normal by running MPR on
112
559
  * `swept_posed` displaced to `(px, py, pz)` against `candidate_posed`
113
- * (both must already be bound by the caller).
560
+ * (both must already be bound by the caller). A triangle target is first
561
+ * re-anchored at its point nearest the probe pose.
114
562
  *
115
563
  * Returns `false` — leaving `out` untouched — when MPR reports no depth worth
116
564
  * the name (at or below {@link CONTACT_EPSILON}). That is not an MPR failure
@@ -120,33 +568,34 @@ const probe_normal = new Float64Array(3);
120
568
  * not an overlap".
121
569
  *
122
570
  * Sign-validation: MPR's polytope can settle on either side of the origin
123
- * for highly symmetric configurations, so we dot against the body-centre
124
- * axis and flip if needed (same pattern as `narrowphase_step`'s EPA path).
125
- * The stored normal is the target's outward surface normal — `B → A`
126
- * direction — i.e. the negated (validated) MTV.
571
+ * for highly symmetric configurations, so we dot against the axis to the
572
+ * target's centre (`candidate_posed`'s position) and flip if needed (same
573
+ * pattern as `narrowphase_step`'s EPA path). The stored normal is the
574
+ * target's outward surface normal — `B → A` direction — i.e. the negated
575
+ * (validated) MTV.
127
576
  *
128
577
  * @param {Float64Array} out receives the unit normal on success
129
578
  * @param {number} px swept shape's probe centre
130
579
  * @param {number} py
131
580
  * @param {number} pz
132
- * @param {Transform64} target_tr the target body's transform (centre for the
133
- * sign check)
134
581
  * @returns {boolean}
135
582
  */
136
- function probe_contact_normal(out, px, py, pz, target_tr) {
583
+ function probe_contact_normal(out, px, py, pz) {
137
584
  swept_posed.px = px;
138
585
  swept_posed.py = py;
139
586
  swept_posed.pz = pz;
140
587
 
588
+ if (candidate_is_triangle) anchor_triangle_nearest(px, py, pz);
589
+
141
590
  if (!mpr(mpr_result, 0, swept_posed, candidate_posed)) return false;
142
591
 
143
592
  let ex = mpr_result[0], ey = mpr_result[1], ez = mpr_result[2];
144
593
  const depth = Math.sqrt(ex * ex + ey * ey + ez * ez);
145
594
  if (!(depth > CONTACT_EPSILON) || !Number.isFinite(depth)) return false;
146
595
 
147
- const ab_x = target_tr.translation_x - px;
148
- const ab_y = target_tr.translation_y - py;
149
- const ab_z = target_tr.translation_z - pz;
596
+ const ab_x = candidate_posed.px - px;
597
+ const ab_y = candidate_posed.py - py;
598
+ const ab_z = candidate_posed.pz - pz;
150
599
  if (ex * ab_x + ey * ab_y + ez * ab_z < 0) {
151
600
  ex = -ex; ey = -ey; ez = -ez;
152
601
  }
@@ -180,8 +629,9 @@ function probe_contact_normal(out, px, py, pz, target_tr) {
180
629
  * probe 1 separates them further. Either way MPR reports zero depth, and zero
181
630
  * depth carries no direction.
182
631
  *
183
- * So probe 2 displaces toward the target's CENTRE, which is interior to it
184
- * (MPR's own precondition) and so closes on it from any contact orientation.
632
+ * So probe 2 displaces toward the target's CENTRE — a convex collider's
633
+ * origin, or a triangle's point nearest the contact pose — which is interior to
634
+ * it (MPR's own precondition) and so closes on it from any contact orientation.
185
635
  * It starts from the CONTACT pose rather than probe 1's — anchoring it at
186
636
  * probe 1's pose lets a sweep directly away from the surface cancel the two
187
637
  * displacements against each other and land back on the tangency it was trying
@@ -211,21 +661,24 @@ function probe_contact_normal(out, px, py, pz, target_tr) {
211
661
  * @param {number} dx unit sweep direction
212
662
  * @param {number} dy
213
663
  * @param {number} dz
214
- * @param {Transform64} target_tr the target body's transform
215
664
  * @returns {boolean} false when every probe degenerates — `out` is untouched
216
665
  */
217
- function recover_contact_normal(out, cx, cy, cz, dx, dy, dz, target_tr) {
666
+ function recover_contact_normal(out, cx, cy, cz, dx, dy, dz) {
218
667
  if (probe_contact_normal(
219
668
  out,
220
669
  cx + dx * NORMAL_PROBE_OFFSET,
221
670
  cy + dy * NORMAL_PROBE_OFFSET,
222
671
  cz + dz * NORMAL_PROBE_OFFSET,
223
- target_tr,
224
672
  )) return true;
225
673
 
226
- const ab_x = target_tr.translation_x - cx;
227
- const ab_y = target_tr.translation_y - cy;
228
- const ab_z = target_tr.translation_z - cz;
674
+ // A triangle's centre for this purpose is its point nearest the contact
675
+ // pose: straight down the contact normal for a face, which is what the
676
+ // probe should close along.
677
+ if (candidate_is_triangle) anchor_triangle_nearest(cx, cy, cz);
678
+
679
+ const ab_x = candidate_posed.px - cx;
680
+ const ab_y = candidate_posed.py - cy;
681
+ const ab_z = candidate_posed.pz - cz;
229
682
  const ab_len = Math.sqrt(ab_x * ab_x + ab_y * ab_y + ab_z * ab_z);
230
683
  // Coincident centres leave no direction to close along, and the shapes are
231
684
  // then as overlapped as they get — probe 1 already had its answer. The
@@ -237,11 +690,1228 @@ function recover_contact_normal(out, cx, cy, cz, dx, dy, dz, target_tr) {
237
690
  // in log2(ab_len / NORMAL_PROBE_OFFSET) steps. Stopping there keeps the
238
691
  // probe on the near side of the target's centre.
239
692
  for (let d = NORMAL_PROBE_OFFSET; d < ab_len; d *= 2) {
240
- if (probe_contact_normal(out, cx + ux * d, cy + uy * d, cz + uz * d, target_tr)) return true;
693
+ if (probe_contact_normal(out, cx + ux * d, cy + uy * d, cz + uz * d)) return true;
694
+ }
695
+ return false;
696
+ }
697
+
698
+ /**
699
+ * Time of impact of `swept_posed` — swept from `(ox, oy, oz)` along unit
700
+ * `(dx, dy, dz)` — against the one convex `candidate_posed`, searched on
701
+ * `[0, t_limit)`. Both must already be bound by the caller (shape and
702
+ * rotation for the swept one; shape and full pose for the candidate), and
703
+ * `swept_aabb` / `initial_aabb` must hold this sweep's bounds.
704
+ *
705
+ * A convex collider is one candidate; a concave one is as many candidates as
706
+ * it has triangles under the sweep, each run through here on its own, so
707
+ * every rule below — the initial-overlap semantics included — holds per
708
+ * triangle exactly as it holds per collider.
709
+ *
710
+ * @param {number} ox
711
+ * @param {number} oy
712
+ * @param {number} oz
713
+ * @param {number} dx
714
+ * @param {number} dy
715
+ * @param {number} dz
716
+ * @param {number} t_limit only an impact strictly before this is wanted
717
+ * @param {number} stop_width the face shortcut's probe distance, and the
718
+ * bracket search's resolution
719
+ * @param {boolean} skip_initial_overlaps see {@link shape_cast}
720
+ * @param {number} window_enter the sweep can first touch the candidate no
721
+ * earlier than this (`-Infinity` when nothing narrower than its AABB is
722
+ * known)…
723
+ * @param {number} window_exit …and no later than this; an empty window
724
+ * (`enter > exit`) means the candidate is only tested for overlap at the
725
+ * start, and never swept. A finite window is a triangle's PLANE window (see
726
+ * {@link load_triangle}), and opens the face shortcut below.
727
+ * @returns {number} the time of impact (a pose still apart from the
728
+ * candidate), {@link CAST_MISS}, or {@link CAST_BLOCKED}; {@link cast_face}
729
+ * says whether it was a face contact, and {@link cast_has_normal} whether
730
+ * {@link cast_normal} holds its contact normal
731
+ */
732
+ function cast_candidate(ox, oy, oz, dx, dy, dz, t_limit, stop_width, skip_initial_overlaps, window_enter, window_exit) {
733
+ cast_face = false;
734
+ cast_has_normal = false;
735
+
736
+ const empty_window = !(window_enter <= window_exit);
737
+ // a triangle whose face is not swept may still have edges that are
738
+ const edges_pending = empty_window && candidate_is_triangle && triangle_edges_pending;
739
+
740
+ // Under CCD semantics a candidate that is never swept is a miss whether it
741
+ // overlaps the start (skipped) or not — all there is to ask of a triangle
742
+ // not closed on is whether the shape meets one of its edges, the cheapest
743
+ // tests first (most triangles have none to sweep: a floor's are all flat).
744
+ // Whether the shape starts in the triangle is asked only of an edge it
745
+ // would otherwise hit (see `cast_triangle_edges`).
746
+ if (empty_window && skip_initial_overlaps) {
747
+ const edges = edges_pending ? triangle_edges_to_sweep(ox, oy, oz, skip_initial_overlaps) : 0;
748
+ return edges !== 0
749
+ ? cast_triangle_edges(ox, oy, oz, dx, dy, dz, t_limit, stop_width, skip_initial_overlaps, edges)
750
+ : CAST_MISS;
751
+ }
752
+
753
+ // The candidate's tight world AABB. A candidate whose AABB misses the
754
+ // whole swept volume can neither overlap the shape at the start nor be
755
+ // reached along the sweep — the cheap answer for most of a concave
756
+ // target's triangles, which are enumerated without a height filter.
757
+ candidate_posed.shape.compute_bounding_box(target_local_aabb);
758
+ aabb3_transform_oriented(
759
+ target_world_aabb, 0,
760
+ target_local_aabb[0], target_local_aabb[1], target_local_aabb[2],
761
+ target_local_aabb[3], target_local_aabb[4], target_local_aabb[5],
762
+ candidate_posed.px, candidate_posed.py, candidate_posed.pz,
763
+ candidate_posed.qx, candidate_posed.qy, candidate_posed.qz, candidate_posed.qw
764
+ );
765
+ if (target_world_aabb[0] > swept_aabb[3] || target_world_aabb[3] < swept_aabb[0]
766
+ || target_world_aabb[1] > swept_aabb[4] || target_world_aabb[4] < swept_aabb[1]
767
+ || target_world_aabb[2] > swept_aabb[5] || target_world_aabb[5] < swept_aabb[2]) {
768
+ return CAST_MISS;
769
+ }
770
+
771
+ // Already overlapping at t = 0? There is no time of impact to search
772
+ // for: the shapes never come into contact, they start in it. Under CCD
773
+ // semantics it is a resting contact to step over — skip THIS candidate
774
+ // only, and keep scanning for real blockers.
775
+ //
776
+ // Under kinematic semantics targets are SOLID: a shape that starts
777
+ // genuinely inside one is blocked where it stands, `t = 0`, whatever
778
+ // direction it is trying to sweep. Nothing can beat that, so stop
779
+ // scanning. Getting out is depenetration's job, not the sweep's.
780
+ //
781
+ // Exact TANGENCY is the exception, and it needs one. A zero-depth
782
+ // touch is not "inside" anything — the shape is resting against a
783
+ // face, or flush with a brush seam, and is free to slide along it or
784
+ // leave. It also survives depenetration, which has no depth to push
785
+ // out of, so it persists frame after frame.
786
+ //
787
+ // Reporting it as a `t = 0` hit ends the scan, and that is the whole
788
+ // problem: the caller is handed a contact it correctly judges harmless
789
+ // for this direction and concludes the sweep is clear, while the body
790
+ // it was actually about to hit was never tested. A character flush
791
+ // against a wall falls through the floor that way. So a tangency
792
+ // blocks only when the sweep drives INTO it, and otherwise steps aside
793
+ // and lets the scan continue.
794
+ //
795
+ // MPR's depth at the START pose is what separates the two: positive
796
+ // means genuine overlap, none means tangency. Tangency is also exactly
797
+ // where the normal is hardest to recover, hence the second probe; if
798
+ // even that degenerates there is no normal to judge with and the
799
+ // contact blocks, stopping short being the safe failure.
800
+ //
801
+ // A triangle whose plane the shape has not reached yet (its window opens
802
+ // after t = 0) cannot overlap it at the start, and needs no test.
803
+ //
804
+ // Under CCD semantics two triangles a body starts in are not skipped. One
805
+ // its centre goes through — the plane, within the edges — is a tunnel
806
+ // however deep the body starts, and blocks it where it stands, with the
807
+ // face's normal. One it rests on, in by no more than the solver's slop,
808
+ // that the sweep closes on by more than the slop (its window is open) is
809
+ // judged as a kinematic tangency is: only a sweep driving into it is
810
+ // stopped. Skipped, a 0.4 m box left on a mesh plate by a clamp and pushed
811
+ // down at 100 m/s went through it in one step.
812
+ swept_posed.px = ox; swept_posed.py = oy; swept_posed.pz = oz;
813
+ if (!(window_enter > 0 && !empty_window) && gjk(simplex_buf, swept_posed, candidate_posed)) {
814
+ if (skip_initial_overlaps) {
815
+ if (candidate_is_triangle && !empty_window && triangle_centre_crossing < t_limit) {
816
+ cast_face = true;
817
+ cast_touch_t = 0;
818
+ return CAST_BLOCKED;
819
+ }
820
+ if (!(candidate_is_triangle && !empty_window && triangle_start_depth <= PENETRATION_SLOP)) {
821
+ return CAST_MISS;
822
+ }
823
+ }
824
+
825
+ if (!probe_contact_normal(probe_normal, ox, oy, oz)) {
826
+ const known = recover_contact_normal(probe_normal, ox, oy, oz, dx, dy, dz);
827
+ if (known) {
828
+ // Both vectors are unit, so this is the sine of the angle
829
+ // between the sweep and the contact plane. Zero means
830
+ // exactly parallel — sliding along the face — and positive
831
+ // means leaving it; neither blocks. The tolerance matters
832
+ // because the parallel case sits right on the sign change,
833
+ // and an MPR-recovered normal carries ~1e-6 of angular
834
+ // noise: without it a sweep ALONG a face flips to
835
+ // "head-on" on solver noise alone.
836
+ const into = dx * probe_normal[0] + dy * probe_normal[1] + dz * probe_normal[2];
837
+ if (into > -GRAZING_SINE_EPSILON) return CAST_MISS;
838
+ }
839
+ }
840
+
841
+ cast_touch_t = 0;
842
+ return CAST_BLOCKED;
843
+ }
844
+
845
+ if (empty_window) {
846
+ // Not closed on: only an edge is left to meet.
847
+ const edges = edges_pending ? triangle_edges_to_sweep(ox, oy, oz, skip_initial_overlaps) : 0;
848
+ return edges !== 0
849
+ ? cast_triangle_edges(ox, oy, oz, dx, dy, dz, t_limit, stop_width, skip_initial_overlaps, edges)
850
+ : CAST_MISS;
851
+ }
852
+
853
+ // ── Slab intersection: narrow the search interval analytically ─
854
+ //
855
+ // Intersect the candidate's tight world AABB with the swept shape's
856
+ // AABB sliding along the sweep direction. The result is the t-interval
857
+ // `[t_aabb_enter, t_aabb_exit]` during which the two AABBs overlap.
858
+ // Outside this interval the actual shapes can't overlap either (AABB is
859
+ // a superset of the shape), so we get to skip all of [0, t_aabb_enter)
860
+ // and (t_aabb_exit, t_limit] without testing.
861
+ //
862
+ // This is the key to making `shapeCast` scale to long sweeps:
863
+ // a 1 km cast of a 1 m sphere against another 1 m sphere has an
864
+ // AABB-overlap window of ≈ 2 m (the sum of diameters along the
865
+ // sweep axis), reducing the coarse-step grid from a 30 m
866
+ // resolution (1000/32) to 6 cm (2/32).
867
+ let t_aabb_enter = -Infinity;
868
+ let t_aabb_exit = Infinity;
869
+ for (let ax = 0; ax < 3; ax++) {
870
+ const init_min = initial_aabb[ax];
871
+ const init_max = initial_aabb[ax + 3];
872
+ const body_min = target_world_aabb[ax];
873
+ const body_max = target_world_aabb[ax + 3];
874
+ const d_ax = ax === 0 ? dx : (ax === 1 ? dy : dz);
875
+ if (d_ax === 0) {
876
+ // Static along this axis: AABBs must overlap in their
877
+ // initial position or this candidate is unreachable.
878
+ if (init_max < body_min || init_min > body_max) return CAST_MISS;
879
+ continue;
880
+ }
881
+ // swept_min(t) = init_min + d·t, swept_max(t) = init_max + d·t.
882
+ // Slab-enter: swept_max(t) = body_min → t = (body_min − init_max) / d
883
+ // Slab-exit: swept_min(t) = body_max → t = (body_max − init_min) / d
884
+ const t1 = (body_min - init_max) / d_ax;
885
+ const t2 = (body_max - init_min) / d_ax;
886
+ const lo = t1 < t2 ? t1 : t2;
887
+ const hi = t1 < t2 ? t2 : t1;
888
+ if (lo > t_aabb_enter) t_aabb_enter = lo;
889
+ if (hi < t_aabb_exit) t_aabb_exit = hi;
890
+ }
891
+ if (window_enter > t_aabb_enter) t_aabb_enter = window_enter;
892
+ if (window_exit < t_aabb_exit) t_aabb_exit = window_exit;
893
+ if (t_aabb_enter > t_aabb_exit) return CAST_MISS;
894
+
895
+ // Clamp to the relevant t-window: [0, t_limit]. Anything beyond it
896
+ // can't tighten the answer.
897
+ let t_start = t_aabb_enter > 0 ? t_aabb_enter : 0;
898
+ const t_end = t_aabb_exit < t_limit ? t_aabb_exit : t_limit;
899
+ if (t_start >= t_end) return CAST_MISS;
900
+
901
+ // ── Face contact: answered in closed form ──────────────────────
902
+ //
903
+ // A triangle's window opens the moment the shape reaches its plane, so
904
+ // nothing can touch the triangle sooner — and the point of the shape that
905
+ // reaches the plane first is known: its support toward the plane. If that
906
+ // point lands on the triangle, the shape met the triangle THERE, on its
907
+ // face: the time of impact is the window's opening, exact, and the contact
908
+ // normal is the face's (see `shape_cast`). A support and three cross
909
+ // products instead of an advancement, for every triangle under a body
910
+ // landing on a mesh floor. Otherwise the contact, if any, is with an edge
911
+ // or a corner, later. (The AABB slab can open a rounding error after the
912
+ // plane window does — that is the same instant.)
913
+ //
914
+ // This used to ask instead whether the shape overlapped the triangle a
915
+ // bisection tolerance, 1e-4 of the sweep, past the opening. A sphere's
916
+ // section through the plane by then reaches sideways by the square root
917
+ // of that depth times its diameter, so one reaching the plane just beyond
918
+ // the triangle's edge was taken to have struck the face, and reported the
919
+ // face's normal for the edge's: a 0.5 m sphere 0.15 m past a mesh cube's
920
+ // edge read 17.5° off on a 500 m cast.
921
+ if (window_enter >= 0 && t_start - window_enter <= stop_width) {
922
+ if (triangle_touch_inside(dx, dy, dz, window_enter)) {
923
+ cast_face = true;
924
+ cast_touch_t = t_start;
925
+ return t_start;
926
+ }
927
+ }
928
+
929
+ // Advance from a little short of the window: where the window opens on the
930
+ // AABB slab of an axis-aligned face, the shapes touch EXACTLY there, and an
931
+ // exact touch has no separating normal to advance along.
932
+ const t_from = t_start - 2 * ADVANCE_TARGET;
933
+ return advance_to_contact(ox, oy, oz, dx, dy, dz, t_from > 0 ? t_from : 0, t_end, stop_width);
934
+ }
935
+
936
+ /**
937
+ * The edges of the triangle under test — bit `k` for edge `k`, vertex `k` to
938
+ * vertex `k + 1` — that a sweep NOT closing on its plane (see
939
+ * {@link triangle_edges_pending}) is run against: none of the face, which it
940
+ * cannot enter, and of the edges, only those it can meet edge-on.
941
+ *
942
+ * It meets an edge from beyond it, so its motion has to carry it back across
943
+ * the edge's line, against the edge's outward direction in the plane; the
944
+ * edges it runs along or away from are behind it.
945
+ *
946
+ * It meets one at all only by already reaching across the plane — by more than
947
+ * {@link CONTACT_EPSILON} at the nearest the sweep comes to it. Not closing,
948
+ * it never reaches further, so a shape that does not straddle the plane cannot
949
+ * meet the triangle, and one merely touching it can only graze it. Under CCD
950
+ * semantics it has to reach across by more than {@link PENETRATION_SLOP} as
951
+ * well, as a plane closed on by no more than the slop is not swept either (see
952
+ * {@link load_triangle}): a body resting in a floor reaches across it by just
953
+ * that much, and an edge it meets only with that much of itself is the
954
+ * solver's to resolve, not something to tunnel through. Swept, every edge the
955
+ * flags and the neighbour search cannot see as interior — a T-junction, tiles
956
+ * a micrometre out of level — stopped a ball rolling across it, every step it
957
+ * crossed.
958
+ *
959
+ * And only at the surface's own boundary: the rim of an open surface, or an
960
+ * edge three faces share. Every other edge lies between exactly two faces, and
961
+ * is met on one of them first. A FLAT edge lies inside the flat stretch the two
962
+ * make, whose own boundary the shape crosses first. A CONCAVE one has its
963
+ * neighbour rising toward the shape, a plane the sweep closes on, and that
964
+ * face's sweep covers the edge. A shape coming at a convex ridge from outside
965
+ * approaches along a normal between the two faces', so it closes on at least
966
+ * one of their planes too, and that face finds the ridge. What is left of a
967
+ * ridge for a sweep closing on neither face is a shape already lying across
968
+ * one of them — a CCD body resting the solver's slop deep in it, a mover flush
969
+ * on it — brushing the ridge with the part of it already in, or on, the
970
+ * surface: the touch it started with, carried along, not something in its way.
971
+ * Swept anyway, where a face meets the ridge a hair after the edge, the edge
972
+ * wins and hands back its slanted normal in place of the face's: a capsule
973
+ * walking edge-on into a 2 mm mesh slab read a normal 0.16° off the end face's,
974
+ * slid along it, and walked off round the slab.
975
+ *
976
+ * The edge flags cannot tell a rim from a ridge, so the boundary is found by
977
+ * looking for the neighbour: the other triangles under the sweep sharing both
978
+ * of an edge's vertices, bit for bit (both enumerators write a shared vertex
979
+ * identically). A mesh's flags are kept and cost a lookup, so they go first and
980
+ * spare a floor — all flat edges — the search, which walks an index of the
981
+ * buffer's edges. A heightmap's neighbours are its grid's, so it needs no search
982
+ * ({@link heightmap_unshared_edges}): its edges all have neighbours but at its
983
+ * border.
984
+ *
985
+ * @param {number} ox sweep origin
986
+ * @param {number} oy
987
+ * @param {number} oz
988
+ * @param {boolean} skip_initial_overlaps see {@link shape_cast}
989
+ * @returns {number}
990
+ */
991
+ function triangle_edges_to_sweep(ox, oy, oz, skip_initial_overlaps) {
992
+ const v = triangle_local;
993
+ const ldx = local_sweep[3], ldy = local_sweep[4], ldz = local_sweep[5];
994
+
995
+ // the triangle's normal, either way up: the edges' outward directions follow its winding
996
+ const e1x = v[3] - v[0], e1y = v[4] - v[1], e1z = v[5] - v[2];
997
+ const e2x = v[6] - v[0], e2y = v[7] - v[1], e2z = v[8] - v[2];
998
+ const nx = e1y * e2z - e1z * e2y;
999
+ const ny = e1z * e2x - e1x * e2z;
1000
+ const nz = e1x * e2y - e1y * e2x;
1001
+
1002
+ let edges = 0;
1003
+ for (let k = 0; k < 3; k++) {
1004
+ const a = 3 * k, b = 3 * ((k + 1) % 3);
1005
+ const ex = v[b] - v[a], ey = v[b + 1] - v[a + 1], ez = v[b + 2] - v[a + 2];
1006
+ // edge × normal points out of the triangle across the edge
1007
+ const ox_ = ey * nz - ez * ny;
1008
+ const oy_ = ez * nx - ex * nz;
1009
+ const oz_ = ex * ny - ey * nx;
1010
+ if (ldx * ox_ + ldy * oy_ + ldz * oz_ < 0) edges |= 1 << k;
1011
+ }
1012
+ if (edges === 0) return 0;
1013
+
1014
+ // A heightmap's neighbours are its grid's, found without a search.
1015
+ const heightmap = triangle_edge_shape.isHeightMapShape3D === true;
1016
+ if (heightmap) {
1017
+ edges &= heightmap_unshared_edges(triangle_edge_shape);
1018
+ if (edges === 0) return 0;
1019
+ }
1020
+
1021
+ if (triangle_edge_shape.isMeshShape3D === true) {
1022
+ const flags = triangle_edge_flags(triangle_edge_shape, triangle_buffer, triangle_offset);
1023
+ for (let k = 0; k < 3; k++) {
1024
+ if ((flags & ((TriangleEdgeFlags.FlatAB | TriangleEdgeFlags.ConcaveAB) << k)) !== 0) edges &= ~(1 << k);
1025
+ }
1026
+ if (edges === 0) return 0;
1027
+ }
1028
+
1029
+ // straddling the plane?
1030
+ const tr = triangle_tr;
1031
+ v3_quaternion_apply(
1032
+ plane_normal, 0,
1033
+ triangle_plane_local[0], triangle_plane_local[1], triangle_plane_local[2],
1034
+ tr.rotation_x, tr.rotation_y, tr.rotation_z, tr.rotation_w
1035
+ );
1036
+ const wnx = plane_normal[0], wny = plane_normal[1], wnz = plane_normal[2];
1037
+ swept_posed.px = ox; swept_posed.py = oy; swept_posed.pz = oz;
1038
+ plane_normal_reversed[0] = -wnx;
1039
+ plane_normal_reversed[1] = -wny;
1040
+ plane_normal_reversed[2] = -wnz;
1041
+ swept_posed.support(support_point, 0, plane_normal_reversed, 0);
1042
+ const reach_near = (ox - support_point[0]) * wnx + (oy - support_point[1]) * wny + (oz - support_point[2]) * wnz;
1043
+ if (!(reach_near - triangle_edge_nearest > (skip_initial_overlaps ? PENETRATION_SLOP + CONTACT_EPSILON : CONTACT_EPSILON))) return 0;
1044
+
1045
+ if (heightmap) return edges;
1046
+
1047
+ // The faces across each edge: keep the edges with none, or more than one. A
1048
+ // face that only doubles this one — its back (the same three vertices, or
1049
+ // folded flat back over it across the edge), or a second copy of a face
1050
+ // already counted (the same far vertex) — is no neighbour: a double-sided
1051
+ // quad's rim is still a rim, and its diagonal is still between two faces.
1052
+ //
1053
+ // The candidates for an edge are the triangles with an edge of its hash
1054
+ // ({@link build_edge_index}), walked in buffer order: every triangle with
1055
+ // both of its vertices is among them.
1056
+ if (edge_index_generation !== buffer_generation) build_edge_index();
1057
+
1058
+ const buffer = triangle_buffer;
1059
+ const mask = edge_mask;
1060
+ const hv0 = vertex_hash(v[0], v[1], v[2]);
1061
+ const hv1 = vertex_hash(v[3], v[4], v[5]);
1062
+ const hv2 = vertex_hash(v[6], v[7], v[8]);
1063
+ const self = triangle_offset / TRIANGLE_FLOAT_STRIDE;
1064
+
1065
+ for (let k = 0; k < 3; k++) {
1066
+ if ((edges & (1 << k)) === 0) continue;
1067
+
1068
+ const key = edge_key(k === 0 ? hv0 : k === 1 ? hv1 : hv2, k === 0 ? hv1 : k === 1 ? hv2 : hv0);
1069
+ if (edge_seen_stamp === 0x7FFFFFFF) {
1070
+ edge_seen.fill(0);
1071
+ edge_seen_stamp = 0;
1072
+ }
1073
+ const stamp = ++edge_seen_stamp;
1074
+ let across_k = 0;
1075
+ const c = 3 * k;
1076
+
1077
+ for (let e = edge_heads[key & mask]; e !== -1; e = edge_next[e]) {
1078
+ if (edge_hash[e] !== key) continue;
1079
+ const t = edge_triangle[e];
1080
+ if (t === self || edge_seen[t] === stamp) continue;
1081
+ edge_seen[t] = stamp;
1082
+
1083
+ const o = t * TRIANGLE_FLOAT_STRIDE;
1084
+
1085
+ // which of this triangle's vertices that one has, and its vertex that is none of them
1086
+ let has = 0;
1087
+ let far = -1;
1088
+ for (let j = 0; j < 9; j += 3) {
1089
+ const x = buffer[o + j], y = buffer[o + j + 1], z = buffer[o + j + 2];
1090
+ if (x === v[0] && y === v[1] && z === v[2]) has |= 1;
1091
+ else if (x === v[3] && y === v[4] && z === v[5]) has |= 2;
1092
+ else if (x === v[6] && y === v[7] && z === v[8]) has |= 4;
1093
+ else far = o + j;
1094
+ }
1095
+ // across this edge, and not this very triangle again
1096
+ if ((has === 3 ? 0 : has === 6 ? 1 : has === 5 ? 2 : -1) !== k || far < 0) continue;
1097
+
1098
+ const fx = buffer[far], fy = buffer[far + 1], fz = buffer[far + 2];
1099
+ if (folds_back(k, fx, fy, fz)) continue;
1100
+
1101
+ if (across_k > 0 && across_far[c] === fx && across_far[c + 1] === fy && across_far[c + 2] === fz) continue;
1102
+ if (across_k === 0) {
1103
+ across_far[c] = fx; across_far[c + 1] = fy; across_far[c + 2] = fz;
1104
+ }
1105
+ across_k++;
1106
+ }
1107
+
1108
+ if (across_k === 1) edges &= ~(1 << k);
1109
+ }
1110
+
1111
+ return edges;
1112
+ }
1113
+
1114
+ /** The far vertex of the first face found across each edge of the triangle under test. */
1115
+ const across_far = new Float64Array(9);
1116
+
1117
+ /**
1118
+ * Which edges of a heightmap's triangle under test have no neighbour in {@link triangle_buffer}: bit
1119
+ * `k` for edge `k`, as {@link triangle_edges_to_sweep} numbers them.
1120
+ *
1121
+ * A heightmap's adjacency is its grid's. The enumerator emits sub-cell `(i, j)` as triangle 0 `(A, D,
1122
+ * B)` and triangle 1 `(D, C, B)` (A, B, C, D its corners `(i, j)`, `(i + 1, j)`, `(i + 1, j + 1)`,
1123
+ * `(i, j + 1)`), every shared corner written identically, in feature-id order. So the face across each
1124
+ * edge is known from the triangle's own feature id: across the diagonal, the cell's other triangle;
1125
+ * across a side, the next cell's triangle on that side, or none at the grid's border. And whether the
1126
+ * buffer holds it is a binary search of the feature ids — what a search of the buffer for a triangle
1127
+ * with both of the edge's vertices finds, without the search. No face of a heightmap doubles another
1128
+ * or folds back over it.
1129
+ *
1130
+ * @param {import("../../../core/geom/3d/shape/HeightMapShape3D.js").HeightMapShape3D} shape
1131
+ * @returns {number}
1132
+ */
1133
+ function heightmap_unshared_edges(shape) {
1134
+ const fid = triangle_buffer[triangle_offset + TRIANGLE_FEATURE_ID_OFFSET] - 1;
1135
+ const sampler = shape.sampler;
1136
+ const seg_u = (sampler.width - 1) * shape.tessellation;
1137
+ const seg_v = (sampler.height - 1) * shape.tessellation;
1138
+
1139
+ const cell = fid >> 1;
1140
+ const i = cell % seg_u;
1141
+ const j = (cell - i) / seg_u;
1142
+
1143
+ let unshared = 0;
1144
+
1145
+ if ((fid & 1) === 0) {
1146
+ // (A, D, B): A→D the side at u = i, D→B the diagonal, B→A the side at v = j
1147
+ if (!(i > 0 && buffer_holds_feature(cell - 1, 1))) unshared |= 1;
1148
+ if (!buffer_holds_feature(cell, 1)) unshared |= 2;
1149
+ if (!(j > 0 && buffer_holds_feature(cell - seg_u, 1))) unshared |= 4;
1150
+ } else {
1151
+ // (D, C, B): D→C the side at v = j + 1, C→B the side at u = i + 1, B→D the diagonal
1152
+ if (!(j < seg_v - 1 && buffer_holds_feature(cell + seg_u, 0))) unshared |= 1;
1153
+ if (!(i < seg_u - 1 && buffer_holds_feature(cell + 1, 0))) unshared |= 2;
1154
+ if (!buffer_holds_feature(cell, 0)) unshared |= 4;
1155
+ }
1156
+
1157
+ return unshared;
1158
+ }
1159
+
1160
+ /**
1161
+ * Whether the first {@link triangle_count} triangles of {@link triangle_buffer}, a heightmap's in
1162
+ * feature-id order, include triangle `t` of sub-cell `cell`.
1163
+ *
1164
+ * @param {number} cell
1165
+ * @param {number} t 0 or 1
1166
+ * @returns {boolean}
1167
+ */
1168
+ function buffer_holds_feature(cell, t) {
1169
+ const fid = cell * 2 + t + 1;
1170
+ let lo = 0, hi = triangle_count - 1;
1171
+ while (lo <= hi) {
1172
+ const mid = (lo + hi) >>> 1;
1173
+ const f = triangle_buffer[mid * TRIANGLE_FLOAT_STRIDE + TRIANGLE_FEATURE_ID_OFFSET];
1174
+ if (f < fid) lo = mid + 1;
1175
+ else if (f > fid) hi = mid - 1;
1176
+ else return true;
241
1177
  }
242
1178
  return false;
243
1179
  }
244
1180
 
1181
+ /**
1182
+ * The edges of the triangles in {@link triangle_buffer}, hashed by their two vertices, for the
1183
+ * neighbour search in {@link triangle_edges_to_sweep}: a chain of the triangles having an edge of each
1184
+ * hash, in buffer order, so that a search walks only the triangles that may share the edge rather
1185
+ * than every triangle under the sweep — a search per triangle over all of them was quadratic, 10 s of
1186
+ * a 2.4 m slide over a 720,000-triangle heightmap. Built on the first search after the buffer is
1187
+ * refilled ({@link buffer_generation}).
1188
+ */
1189
+ let edge_heads = new Int32Array(0);
1190
+ /** The buckets of {@link edge_heads} the last build used, less one: a power of two less one. */
1191
+ let edge_mask = 0;
1192
+ let edge_next = new Int32Array(0);
1193
+ let edge_hash = new Int32Array(0);
1194
+ let edge_triangle = new Int32Array(0);
1195
+ /** Per triangle, the search that last walked it, so a chain listing it twice is walked once. */
1196
+ let edge_seen = new Int32Array(0);
1197
+ let edge_seen_stamp = 0;
1198
+ /** The {@link buffer_generation} the index was built for. */
1199
+ let edge_index_generation = -1;
1200
+ /** Bumped by every enumeration into {@link triangle_buffer}. */
1201
+ let buffer_generation = 0;
1202
+
1203
+ const hash_f64 = new Float64Array(1);
1204
+ const hash_u32 = new Uint32Array(hash_f64.buffer);
1205
+
1206
+ /**
1207
+ * A hash of a vertex that equal coordinates share, as `===` has them equal: `-0` with `0`.
1208
+ *
1209
+ * @param {number} x
1210
+ * @param {number} y
1211
+ * @param {number} z
1212
+ * @returns {number}
1213
+ */
1214
+ function vertex_hash(x, y, z) {
1215
+ hash_f64[0] = x + 0;
1216
+ let h = Math.imul(hash_u32[0] ^ 0x811c9dc5, 0x01000193) ^ hash_u32[1];
1217
+ hash_f64[0] = y + 0;
1218
+ h = Math.imul(h ^ hash_u32[0], 0x01000193) ^ hash_u32[1];
1219
+ hash_f64[0] = z + 0;
1220
+ h = Math.imul(h ^ hash_u32[0], 0x01000193) ^ hash_u32[1];
1221
+ return lowbias32(h);
1222
+ }
1223
+
1224
+ /**
1225
+ * Index the first {@link triangle_count} triangles of {@link triangle_buffer} by their edges.
1226
+ */
1227
+ function build_edge_index() {
1228
+ const count = triangle_count;
1229
+ const entries = count * 3;
1230
+
1231
+ let size = 16;
1232
+ while (size < 2 * entries) size *= 2;
1233
+ if (edge_heads.length < size) edge_heads = new Int32Array(size);
1234
+ if (edge_next.length < entries) {
1235
+ edge_next = new Int32Array(entries);
1236
+ edge_hash = new Int32Array(entries);
1237
+ edge_triangle = new Int32Array(entries);
1238
+ }
1239
+ if (edge_seen.length < count) {
1240
+ edge_seen = new Int32Array(Math.max(count, edge_seen.length * 2));
1241
+ edge_seen_stamp = 0;
1242
+ }
1243
+
1244
+ const mask = size - 1;
1245
+ edge_mask = mask;
1246
+ edge_heads.fill(-1, 0, size);
1247
+
1248
+ const buffer = triangle_buffer;
1249
+ let e = 0;
1250
+ // last to first, each pushed onto the front of its chain: the chains run in buffer order
1251
+ for (let t = count - 1; t >= 0; t--) {
1252
+ const o = t * TRIANGLE_FLOAT_STRIDE;
1253
+ const ha = vertex_hash(buffer[o], buffer[o + 1], buffer[o + 2]);
1254
+ const hb = vertex_hash(buffer[o + 3], buffer[o + 4], buffer[o + 5]);
1255
+ const hc = vertex_hash(buffer[o + 6], buffer[o + 7], buffer[o + 8]);
1256
+
1257
+ for (let k = 0; k < 3; k++) {
1258
+ const h = edge_key(k === 0 ? ha : k === 1 ? hb : hc, k === 0 ? hb : k === 1 ? hc : ha);
1259
+ const bucket = h & mask;
1260
+ edge_hash[e] = h;
1261
+ edge_triangle[e] = t;
1262
+ edge_next[e] = edge_heads[bucket];
1263
+ edge_heads[bucket] = e;
1264
+ e++;
1265
+ }
1266
+ }
1267
+
1268
+ edge_index_generation = buffer_generation;
1269
+ }
1270
+
1271
+ /**
1272
+ * The hash of an edge, whichever way round its vertices come.
1273
+ *
1274
+ * @param {number} ha
1275
+ * @param {number} hb
1276
+ * @returns {number}
1277
+ */
1278
+ function edge_key(ha, hb) {
1279
+ return lowbias32((ha + hb) | 0) | 0;
1280
+ }
1281
+
1282
+ /**
1283
+ * Whether a face across edge `k` of the triangle under test, whose vertex off
1284
+ * the edge is `(fx, fy, fz)`, is folded flat back over the triangle: in its
1285
+ * plane, on its side of the edge — a back face, however its quad was split.
1286
+ *
1287
+ * @param {number} k
1288
+ * @param {number} fx
1289
+ * @param {number} fy
1290
+ * @param {number} fz
1291
+ * @returns {boolean}
1292
+ */
1293
+ function folds_back(k, fx, fy, fz) {
1294
+ const v = triangle_local;
1295
+ const a = 3 * k, b = 3 * ((k + 1) % 3), c = 3 * ((k + 2) % 3);
1296
+ const ex = v[b] - v[a], ey = v[b + 1] - v[a + 1], ez = v[b + 2] - v[a + 2];
1297
+ const cx = v[c] - v[a], cy = v[c + 1] - v[a + 1], cz = v[c + 2] - v[a + 2];
1298
+ const qx = fx - v[a], qy = fy - v[a + 1], qz = fz - v[a + 2];
1299
+
1300
+ // the triangle's normal, and the far vertex's height above its plane
1301
+ const nx = ey * cz - ez * cy, ny = ez * cx - ex * cz, nz = ex * cy - ey * cx;
1302
+ const height = nx * qx + ny * qy + nz * qz;
1303
+ const n_sqr = nx * nx + ny * ny + nz * nz;
1304
+ if (height * height > 1e-12 * n_sqr * (qx * qx + qy * qy + qz * qz)) return false;
1305
+
1306
+ // in the plane: on the triangle's side of the edge?
1307
+ const sx = ey * qz - ez * qy, sy = ez * qx - ex * qz, sz = ex * qy - ey * qx;
1308
+ return nx * sx + ny * sy + nz * sz > 0;
1309
+ }
1310
+
1311
+ /**
1312
+ * Sweep against edges `edges` (bits, as {@link triangle_edges_to_sweep}) of the
1313
+ * triangle under test, each one a segment cast as a convex candidate of its
1314
+ * own.
1315
+ *
1316
+ * @param {number} ox
1317
+ * @param {number} oy
1318
+ * @param {number} oz
1319
+ * @param {number} dx
1320
+ * @param {number} dy
1321
+ * @param {number} dz
1322
+ * @param {number} t_limit
1323
+ * @param {number} stop_width
1324
+ * @param {boolean} skip_initial_overlaps
1325
+ * @param {number} edges
1326
+ * @returns {number} the nearest edge's time of impact, or {@link CAST_MISS};
1327
+ * {@link cast_has_normal} / {@link cast_normal} as {@link cast_candidate}
1328
+ */
1329
+ function cast_triangle_edges(ox, oy, oz, dx, dy, dz, t_limit, stop_width, skip_initial_overlaps, edges) {
1330
+ const tr = triangle_tr;
1331
+ const qx = tr.rotation_x, qy = tr.rotation_y, qz = tr.rotation_z, qw = tr.rotation_w;
1332
+ const v = triangle_local;
1333
+
1334
+ let best = CAST_MISS;
1335
+ let best_contact = t_limit;
1336
+ let best_has_normal = false;
1337
+ /** Under CCD, whether the shape was found clear of the whole triangle at the start. */
1338
+ let clear_at_start = !skip_initial_overlaps;
1339
+
1340
+ for (let k = 0; k < 3; k++) {
1341
+ if ((edges & (1 << k)) === 0) continue;
1342
+
1343
+ const a = 3 * k;
1344
+ const b = 3 * ((k + 1) % 3);
1345
+ const mx = (v[a] + v[b]) * 0.5;
1346
+ const my = (v[a + 1] + v[b + 1]) * 0.5;
1347
+ const mz = (v[a + 2] + v[b + 2]) * 0.5;
1348
+ edge_vertices[0] = v[a] - mx; edge_vertices[1] = v[a + 1] - my; edge_vertices[2] = v[a + 2] - mz;
1349
+ edge_vertices[3] = v[b] - mx; edge_vertices[4] = v[b + 1] - my; edge_vertices[5] = v[b + 2] - mz;
1350
+ edge_vertices[6] = edge_vertices[3]; edge_vertices[7] = edge_vertices[4]; edge_vertices[8] = edge_vertices[5];
1351
+
1352
+ v3_quaternion_apply(anchor_world, 0, mx, my, mz, qx, qy, qz, qw);
1353
+ candidate_posed.setup_components(
1354
+ edge_shape,
1355
+ tr.translation_x + anchor_world[0], tr.translation_y + anchor_world[1], tr.translation_z + anchor_world[2],
1356
+ qx, qy, qz, qw
1357
+ );
1358
+
1359
+ // A segment is convex: cast as one, its normal probes read it, not the triangle.
1360
+ candidate_is_triangle = false;
1361
+ const t = cast_candidate(
1362
+ ox, oy, oz, dx, dy, dz, best_contact, stop_width, skip_initial_overlaps,
1363
+ -Infinity, Infinity
1364
+ );
1365
+ candidate_is_triangle = true;
1366
+ if (t === CAST_MISS || !(cast_touch_t < best_contact)) continue;
1367
+
1368
+ if (!clear_at_start) {
1369
+ // Under CCD a triangle the shape starts in is a resting contact to
1370
+ // step over, edges and all — a ball resting in an open floor by an
1371
+ // obtuse corner reaches past the line of the corner's edge beside
1372
+ // it, and can come onto the edge from outside.
1373
+ anchor_triangle(
1374
+ (v[0] + v[3] + v[6]) / 3, (v[1] + v[4] + v[7]) / 3, (v[2] + v[5] + v[8]) / 3
1375
+ );
1376
+ swept_posed.px = ox; swept_posed.py = oy; swept_posed.pz = oz;
1377
+ if (gjk(simplex_buf, swept_posed, candidate_posed)) {
1378
+ best = CAST_MISS;
1379
+ best_has_normal = false;
1380
+ break;
1381
+ }
1382
+ clear_at_start = true;
1383
+ }
1384
+
1385
+ best = t === CAST_BLOCKED ? 0 : t;
1386
+ best_contact = cast_touch_t;
1387
+ best_has_normal = cast_has_normal;
1388
+ if (cast_has_normal) edge_normal.set(cast_normal);
1389
+ if (best === 0) break;
1390
+ }
1391
+
1392
+ cast_face = false;
1393
+ cast_has_normal = best_has_normal;
1394
+ if (best_has_normal) cast_normal.set(edge_normal);
1395
+ cast_touch_t = best_contact;
1396
+ return best;
1397
+ }
1398
+
1399
+ /**
1400
+ * Conservative advancement of `swept_posed` toward `candidate_posed` along the
1401
+ * sweep from `t_start`, after Box3D's `b3ShapeCast` (distance.c, MIT, Erin
1402
+ * Catto): the distance kernel gives a plane separating the two, and nothing can
1403
+ * touch before the sweep closes that plane's gap, so the shape steps across it
1404
+ * in one move, to {@link ADVANCE_TARGET} short of it, and asks again. A flat
1405
+ * approach lands in one step; no feature, however thin or oblique, can be
1406
+ * stepped over, because no step is ever longer than the gap to it.
1407
+ *
1408
+ * Arriving within the target is a TOUCH, not yet a hit — a sphere sliding
1409
+ * along a wall touches it everywhere and never enters it. So a touch is
1410
+ * followed one step further, to where it would be {@link ADVANCE_TARGET} deep
1411
+ * at its present rate of approach: if the shapes overlap there, the sweep
1412
+ * enters the candidate and the touch is the hit. If not, the advancement goes
1413
+ * on from there. It ends as soon as the sweep stops closing: the distance
1414
+ * along a translation is convex, so once it stops falling it never falls
1415
+ * again — a graze is a miss, as it was when only overlap counted.
1416
+ *
1417
+ * Box3D answers an advancement that runs out of iterations with a silent
1418
+ * miss. Here it falls back on the bracket search over what is left of the
1419
+ * window, and says so once per process.
1420
+ *
1421
+ * On a hit, {@link cast_normal} holds the separating direction at the touch.
1422
+ *
1423
+ * @param {number} ox
1424
+ * @param {number} oy
1425
+ * @param {number} oz
1426
+ * @param {number} dx unit sweep direction
1427
+ * @param {number} dy
1428
+ * @param {number} dz
1429
+ * @param {number} t_start nothing touches before this
1430
+ * @param {number} t_end nothing can start touching after this
1431
+ * @param {number} stop_width the bracket search's resolution
1432
+ * @returns {number} the time of impact, or {@link CAST_MISS}
1433
+ */
1434
+ function advance_to_contact(ox, oy, oz, dx, dy, dz, t_start, t_end, stop_width) {
1435
+ distance_simplex.count = 0;
1436
+
1437
+ let t = t_start;
1438
+ swept_posed.px = ox + dx * t;
1439
+ swept_posed.py = oy + dy * t;
1440
+ swept_posed.pz = oz + dz * t;
1441
+
1442
+ /** A touch being followed: its time, or -1. */
1443
+ let t_touch = -1;
1444
+ /** When the contact behind that touch begins: the touch plus its gap over the closing speed. */
1445
+ let touch_contact = 0;
1446
+ /** The last time the shapes were measured apart. */
1447
+ let t_apart = t_start;
1448
+
1449
+ for (let iteration = 0; iteration < ADVANCE_MAX_ITERATIONS; iteration++) {
1450
+ let distance = gjk_distance(distance_result, distance_simplex, candidate_posed, swept_posed, ADVANCE_DISTANCE_TOLERANCE);
1451
+ if (distance > 0 && distance_result[9] <= ADVANCE_TARGET + ADVANCE_TOLERANCE
1452
+ && distance - distance_result[9] > GJK_DISTANCE_RELATIVE_GAP * distance) {
1453
+ // A touch, or nearly, that the tolerance stopped short of exact:
1454
+ // its gap and normal are what is judged and reported, so the search
1455
+ // goes on from where it stopped to the kernel's own precision. (A
1456
+ // polytope pair ends exact on a repeated support either way.)
1457
+ distance = gjk_distance(distance_result, distance_simplex, candidate_posed, swept_posed, 0);
1458
+ }
1459
+ if (distance > 0 && !(distance_result[9] > 0)) {
1460
+ // The search ran out of iterations before any plane separated the
1461
+ // two, so `distance` is only how close it came. Ask again from
1462
+ // nothing: a simplex carried across steps can stall where a fresh
1463
+ // one does not (a 1,000 m sphere 1e-5 from a small one, 106 times
1464
+ // in 400 carried, 10 fresh).
1465
+ distance_simplex.count = 0;
1466
+ distance = gjk_distance(distance_result, distance_simplex, candidate_posed, swept_posed, 0);
1467
+ if (distance > 0 && !(distance_result[9] > 0)) {
1468
+ // Still nothing to step by, and no reason to call it a touch:
1469
+ // the bracket search settles it from the last pose known apart.
1470
+ return bracket_search(ox, oy, oz, dx, dy, dz, t_apart, t_end, stop_width);
1471
+ }
1472
+ }
1473
+
1474
+ // In contact: the search ended on its own terms with no plane between
1475
+ // the two.
1476
+ if (distance === 0) {
1477
+ if (gjk(simplex_buf, swept_posed, candidate_posed)) {
1478
+ // Overlap. Behind a touch, that is the hit: the shapes were
1479
+ // apart at the touch and are in contact one step on.
1480
+ if (t_touch >= 0) {
1481
+ cast_has_normal = true;
1482
+ cast_touch_t = touch_contact;
1483
+ return t_touch;
1484
+ }
1485
+
1486
+ // Anywhere else the step that led here was supposed to be
1487
+ // safe, and only numerics put it inside. The bracket search
1488
+ // settles it from the last pose known apart.
1489
+ return bracket_search(ox, oy, oz, dx, dy, dz, t_apart, t_end, stop_width);
1490
+ }
1491
+
1492
+ // Exactly touching — a shape flush with a face it slides along, or
1493
+ // a step that landed on the contact to the last bit. The distance
1494
+ // has no normal to give, so it is read by penetration, as a touch
1495
+ // at the start of the sweep is, and the rule is the same: only a
1496
+ // sweep driving into the touch enters it (see `cast_candidate`).
1497
+ const px = swept_posed.px, py = swept_posed.py, pz = swept_posed.pz;
1498
+ if (!recover_contact_normal(probe_normal, px, py, pz, dx, dy, dz)) {
1499
+ // No normal to judge by: stopping short is the safe failure.
1500
+ cast_touch_t = t_touch >= 0 ? touch_contact : t;
1501
+ return t_touch >= 0 ? t_touch : t;
1502
+ }
1503
+ if (dx * probe_normal[0] + dy * probe_normal[1] + dz * probe_normal[2] > -GRAZING_SINE_EPSILON) {
1504
+ return CAST_MISS;
1505
+ }
1506
+ if (t_touch < 0) {
1507
+ cast_normal[0] = probe_normal[0]; cast_normal[1] = probe_normal[1]; cast_normal[2] = probe_normal[2];
1508
+ }
1509
+ cast_has_normal = true;
1510
+ cast_touch_t = t_touch >= 0 ? touch_contact : t;
1511
+ return t_touch >= 0 ? t_touch : t;
1512
+ }
1513
+ t_apart = t;
1514
+
1515
+ const nx = distance_result[6], ny = distance_result[7], nz = distance_result[8];
1516
+ const gap = distance_result[9];
1517
+
1518
+ // Closing speed along the separating normal, per unit of sweep.
1519
+ const closing = -(dx * nx + dy * ny + dz * nz);
1520
+ if (!(closing > 0)) return CAST_MISS;
1521
+
1522
+ let t_next;
1523
+ if (gap <= ADVANCE_TARGET + ADVANCE_TOLERANCE) {
1524
+ t_touch = t;
1525
+ touch_contact = t + gap / closing;
1526
+ cast_normal[0] = nx; cast_normal[1] = ny; cast_normal[2] = nz;
1527
+ t_next = t + (gap + ADVANCE_TARGET) / closing;
1528
+ // Only a contact strictly before `t_end` counts, so that is as far
1529
+ // as it needs to be followed.
1530
+ if (t_next > t_end) t_next = t_end;
1531
+ if (!(t_next > t)) return CAST_MISS;
1532
+ } else {
1533
+ t_touch = -1;
1534
+ t_next = t + (gap - ADVANCE_TARGET) / closing;
1535
+ if (!(t_next < t_end)) return CAST_MISS;
1536
+ }
1537
+
1538
+ const step = t_next - t;
1539
+ swept_posed.px = ox + dx * t_next;
1540
+ swept_posed.py = oy + dy * t_next;
1541
+ swept_posed.pz = oz + dz * t_next;
1542
+ gjk_simplex_translate_b(distance_simplex, dx * step, dy * step, dz * step);
1543
+ t = t_next;
1544
+ }
1545
+
1546
+ if (!advance_exhaustion_reported) {
1547
+ advance_exhaustion_reported = true;
1548
+ console.warn(
1549
+ `shape_cast: conservative advancement did not converge in ${ADVANCE_MAX_ITERATIONS} steps`
1550
+ + ` (at t = ${t} of a window ending at ${t_end}); falling back on the bracket search.`
1551
+ );
1552
+ }
1553
+ return bracket_search(ox, oy, oz, dx, dy, dz, t_apart, t_end, stop_width);
1554
+ }
1555
+
1556
+ /**
1557
+ * The time of impact by sampling: a coarse scan of `[t_start, t_end]` for an
1558
+ * overlapping moment, then bisection down to `stop_width`. What every sweep
1559
+ * used before {@link advance_to_contact}, and what it falls back on; a feature
1560
+ * thinner than a sample's spacing is stepped over (see {@link COARSE_STEPS}).
1561
+ *
1562
+ * @param {number} ox
1563
+ * @param {number} oy
1564
+ * @param {number} oz
1565
+ * @param {number} dx
1566
+ * @param {number} dy
1567
+ * @param {number} dz
1568
+ * @param {number} t_start not overlapping here
1569
+ * @param {number} t_end
1570
+ * @param {number} stop_width
1571
+ * @returns {number} the just-SEPARATING side of the bisection, or {@link CAST_MISS}
1572
+ */
1573
+ function bracket_search(ox, oy, oz, dx, dy, dz, t_start, t_end, stop_width) {
1574
+ cast_has_normal = false;
1575
+
1576
+ if (!(t_start < t_end)) return CAST_MISS;
1577
+
1578
+ // Coarse-search within the narrowed interval to find any t for
1579
+ // which the shapes overlap (their AABBs do by construction; the
1580
+ // shapes themselves may not, e.g. two spheres passing alongside).
1581
+ const step_dt = (t_end - t_start) / COARSE_STEPS;
1582
+ let t_prev = t_start;
1583
+ let t_hi = -1;
1584
+ for (let s = 1; s <= COARSE_STEPS; s++) {
1585
+ const t = t_start + s * step_dt;
1586
+ swept_posed.px = ox + dx * t;
1587
+ swept_posed.py = oy + dy * t;
1588
+ swept_posed.pz = oz + dz * t;
1589
+ if (gjk(simplex_buf, swept_posed, candidate_posed)) {
1590
+ t_hi = t;
1591
+ break;
1592
+ }
1593
+ t_prev = t;
1594
+ }
1595
+ if (t_hi < 0) return CAST_MISS;
1596
+
1597
+ // Bisect [t_prev, t_hi]: invariant is `gjk(t_lo) === false` and
1598
+ // `gjk(t_hi) === true`.
1599
+ let t_lo = t_prev;
1600
+ for (let bi = 0; bi < MAX_BISECTION && (t_hi - t_lo) > stop_width; bi++) {
1601
+ const t_mid = (t_lo + t_hi) * 0.5;
1602
+ swept_posed.px = ox + dx * t_mid;
1603
+ swept_posed.py = oy + dy * t_mid;
1604
+ swept_posed.pz = oz + dz * t_mid;
1605
+ if (gjk(simplex_buf, swept_posed, candidate_posed)) {
1606
+ t_hi = t_mid;
1607
+ } else {
1608
+ t_lo = t_mid;
1609
+ }
1610
+ }
1611
+
1612
+ // Use t_lo (the just-SEPARATING side of the bisection) rather
1613
+ // than t_hi (just-overlapping). With the fixed GJK that no
1614
+ // longer flags 5 mm-clear gaps as overlap, the bisection is
1615
+ // tight; reporting t_lo gives kinematic callers a conservative
1616
+ // TOI — the swept shape at the reported moment is provably NOT
1617
+ // overlapping the target. The normal-probe in `shape_cast` steps a
1618
+ // few mm past it to get a valid simplex for MPR.
1619
+ cast_touch_t = t_lo;
1620
+ return t_lo;
1621
+ }
1622
+
1623
+ /**
1624
+ * The world box the swept shape covers over `[t0, t1]` of the sweep along unit
1625
+ * `(dx, dy, dz)`, from the shape's box at the origin, {@link initial_aabb};
1626
+ * written into {@link piece_aabb}.
1627
+ *
1628
+ * @param {number} dx
1629
+ * @param {number} dy
1630
+ * @param {number} dz
1631
+ * @param {number} t0
1632
+ * @param {number} t1
1633
+ * @returns {Float64Array} {@link piece_aabb}
1634
+ */
1635
+ function sweep_piece_aabb(dx, dy, dz, t0, t1) {
1636
+ const sx0 = dx * t0, sx1 = dx * t1;
1637
+ const sy0 = dy * t0, sy1 = dy * t1;
1638
+ const sz0 = dz * t0, sz1 = dz * t1;
1639
+ piece_aabb[0] = initial_aabb[0] + (sx0 < sx1 ? sx0 : sx1);
1640
+ piece_aabb[1] = initial_aabb[1] + (sy0 < sy1 ? sy0 : sy1);
1641
+ piece_aabb[2] = initial_aabb[2] + (sz0 < sz1 ? sz0 : sz1);
1642
+ piece_aabb[3] = initial_aabb[3] + (sx0 > sx1 ? sx0 : sx1);
1643
+ piece_aabb[4] = initial_aabb[4] + (sy0 > sy1 ? sy0 : sy1);
1644
+ piece_aabb[5] = initial_aabb[5] + (sz0 > sz1 ? sz0 : sz1);
1645
+ return piece_aabb;
1646
+ }
1647
+
1648
+ /**
1649
+ * Enumerate the triangles of concave `shape`, posed at `target_tr`, that may
1650
+ * overlap the world AABB `world_aabb` into {@link triangle_buffer}, in the
1651
+ * shape's own frame — the same decomposition the narrowphase and
1652
+ * `overlap_shape` use.
1653
+ *
1654
+ * The enumerator returns the TRUE count and drops what does not fit; a
1655
+ * dropped triangle is a hole the sweep would pass straight through, so the
1656
+ * buffer grows and the enumeration re-runs (deterministic → one resize) — up to
1657
+ * {@link MAX_TRIANGLES_PER_PAIR}, and no further. Past that, the count returned
1658
+ * is still the true one and only the first that many are in the buffer: the
1659
+ * caller walks the sweep in shorter pieces (see `shape_cast`).
1660
+ *
1661
+ * @param {AbstractShape3D} shape
1662
+ * @param {Transform64} target_tr
1663
+ * @param {Float64Array} world_aabb
1664
+ * @returns {number} the triangles overlapping the box, which may be more than
1665
+ * {@link MAX_TRIANGLES_PER_PAIR}
1666
+ */
1667
+ function enumerate_triangles(shape, target_tr, world_aabb) {
1668
+ aabb3_transform_oriented_inverse(
1669
+ concave_query_aabb, 0,
1670
+ world_aabb,
1671
+ target_tr.translation_x, target_tr.translation_y, target_tr.translation_z,
1672
+ target_tr.rotation_x, target_tr.rotation_y, target_tr.rotation_z, target_tr.rotation_w
1673
+ );
1674
+
1675
+ buffer_generation++;
1676
+
1677
+ for (;;) {
1678
+ const count = decompose_to_triangles(
1679
+ triangle_buffer, 0, shape,
1680
+ concave_query_aabb[0], concave_query_aabb[1], concave_query_aabb[2],
1681
+ concave_query_aabb[3], concave_query_aabb[4], concave_query_aabb[5]
1682
+ );
1683
+ const capacity = triangle_buffer.length / TRIANGLE_FLOAT_STRIDE;
1684
+ if (count <= capacity || capacity >= MAX_TRIANGLES_PER_PAIR) return count;
1685
+ triangle_buffer = new Float64Array(
1686
+ Math.min(MAX_TRIANGLES_PER_PAIR, Math.max(count, capacity * 2)) * TRIANGLE_FLOAT_STRIDE
1687
+ );
1688
+ }
1689
+ }
1690
+
1691
+ /**
1692
+ * The count {@link enumerate_triangles} would return for the same box, for
1693
+ * sizing a piece of the walk. A heightmap's is counted in O(1)
1694
+ * ({@link heightmap_box_triangles}) and nothing is enumerated — the caller
1695
+ * enumerates the piece it settles on; a mesh's is enumerated, as its count can
1696
+ * only be had that way.
1697
+ *
1698
+ * @param {AbstractShape3D} shape
1699
+ * @param {Transform64} target_tr
1700
+ * @param {Float64Array} world_aabb
1701
+ * @returns {number}
1702
+ */
1703
+ function count_triangles(shape, target_tr, world_aabb) {
1704
+ if (shape.isHeightMapShape3D !== true) {
1705
+ return enumerate_triangles(shape, target_tr, world_aabb);
1706
+ }
1707
+
1708
+ aabb3_transform_oriented_inverse(
1709
+ concave_query_aabb, 0,
1710
+ world_aabb,
1711
+ target_tr.translation_x, target_tr.translation_y, target_tr.translation_z,
1712
+ target_tr.rotation_x, target_tr.rotation_y, target_tr.rotation_z, target_tr.rotation_w
1713
+ );
1714
+
1715
+ return heightmap_box_triangles(
1716
+ shape,
1717
+ concave_query_aabb[0], concave_query_aabb[1], concave_query_aabb[2],
1718
+ concave_query_aabb[3], concave_query_aabb[4], concave_query_aabb[5]
1719
+ );
1720
+ }
1721
+
1722
+ /**
1723
+ * Make the triangle at `offset` of `buffer` (in the frame of a collider posed
1724
+ * at `target_tr`) the one under test, posed as `candidate_posed` anchored at
1725
+ * its centroid.
1726
+ *
1727
+ * @param {Float64Array} buffer
1728
+ * @param {number} offset
1729
+ * @param {Transform64} target_tr
1730
+ */
1731
+ function set_triangle(buffer, offset, target_tr) {
1732
+ for (let i = 0; i < 9; i++) triangle_local[i] = buffer[offset + i];
1733
+ triangle_tr = target_tr;
1734
+ candidate_is_triangle = true;
1735
+
1736
+ anchor_triangle(
1737
+ (triangle_local[0] + triangle_local[3] + triangle_local[6]) / 3,
1738
+ (triangle_local[1] + triangle_local[4] + triangle_local[7]) / 3,
1739
+ (triangle_local[2] + triangle_local[5] + triangle_local[8]) / 3
1740
+ );
1741
+ }
1742
+
1743
+ /**
1744
+ * Make the triangle at `offset` of `buffer` — vertices in the frame of a
1745
+ * concave collider posed at `target_tr` — the candidate under test, posed as
1746
+ * `candidate_posed` anchored at its centroid (see {@link triangle_vertices}),
1747
+ * and write the part of the sweep that can touch it into
1748
+ * {@link triangle_window_enter} / {@link triangle_window_exit}.
1749
+ *
1750
+ * A triangle is flat, and that is what the window is read from: the sweep can
1751
+ * touch the triangle only while the swept shape straddles its PLANE, an
1752
+ * interval known in closed form from the shape's reach along the normal. It
1753
+ * matters more than the saving. The coarse search samples 32 points across
1754
+ * whatever interval it is given, and the AABB slab interval of a large
1755
+ * oblique triangle is metres long while the shape crosses its plane in
1756
+ * centimetres — measured on a 40 m ramp at 30° pitch, 225 of 300 casts of a
1757
+ * 5 cm sphere stepped clean over it (as they do over a 1 mm box at the same
1758
+ * pose; a triangle is simply always that thin).
1759
+ *
1760
+ * A sweep that does not CLOSE on the plane gets an empty window: it is tested
1761
+ * for overlap at the start, and the face is never swept. Running parallel to
1762
+ * the plane, or leaving it, the shape cannot enter the face; at most it meets
1763
+ * the triangle's boundary, and only if it already reaches across the plane.
1764
+ * Most of that boundary is no boundary of the surface: a coplanar neighbour
1765
+ * (the next triangle of a floor) is the very surface the shape is sliding on,
1766
+ * and reporting the touch at its seam stops a sliding shape at every seam it
1767
+ * crosses and hands a kinematic caller a contact it rightly judges harmless
1768
+ * while the scan behind it is cut short. So only the edges a sweep can meet
1769
+ * edge-on are swept ({@link triangle_edges_to_sweep}): the surface's own
1770
+ * boundary. Skipping all of them, a quad approached exactly edge-on, or 1e-5
1771
+ * off it, was invisible to every cast.
1772
+ *
1773
+ * What "closing" means follows `skip_initial_overlaps`. Kinematic semantics
1774
+ * are exact: any approach beyond {@link GRAZING_SINE_EPSILON}. Under CCD
1775
+ * semantics the body is usually RESTING in the surface it slides on, already
1776
+ * {@link PENETRATION_SLOP} deep, and its net motion over a step drifts into
1777
+ * the plane by solver noise; a sweep that closes on a plane by no more than
1778
+ * that slop over its whole length, its centre staying on the side it started,
1779
+ * cannot carry the body through anything, and the discrete solver owns that
1780
+ * depth. Measured without it: a CCD sphere sliding at 5 m/s across a flat
1781
+ * heightmap was stopped at a seam often enough to lose 4.2 m of travel in one
1782
+ * second.
1783
+ *
1784
+ * @param {AbstractShape3D} shape the concave shape `buffer` was enumerated from
1785
+ * @param {Float64Array} buffer
1786
+ * @param {number} offset float-index of the triangle in `buffer`
1787
+ * @param {Transform64} target_tr
1788
+ * @param {number} lx sweep origin in the target's frame
1789
+ * @param {number} ly
1790
+ * @param {number} lz
1791
+ * @param {number} ldx unit sweep direction in the target's frame
1792
+ * @param {number} ldy
1793
+ * @param {number} ldz
1794
+ * @param {number} ox sweep origin in world space
1795
+ * @param {number} oy
1796
+ * @param {number} oz
1797
+ * @param {number} tMax sweep length
1798
+ * @param {boolean} skip_initial_overlaps see {@link shape_cast}
1799
+ */
1800
+ function load_triangle(
1801
+ shape, buffer, offset, target_tr,
1802
+ lx, ly, lz, ldx, ldy, ldz,
1803
+ ox, oy, oz, tMax, skip_initial_overlaps
1804
+ ) {
1805
+ set_triangle(buffer, offset, target_tr);
1806
+
1807
+ const ax = triangle_local[0], ay = triangle_local[1], az = triangle_local[2];
1808
+ const bx = triangle_local[3], by = triangle_local[4], bz = triangle_local[5];
1809
+ const cx = triangle_local[6], cy = triangle_local[7], cz = triangle_local[8];
1810
+ const mx = (ax + bx + cx) / 3;
1811
+ const my = (ay + by + cy) / 3;
1812
+ const mz = (az + bz + cz) / 3;
1813
+
1814
+ // Empty until shown otherwise: tested at the start only.
1815
+ triangle_window_enter = Infinity;
1816
+ triangle_window_exit = -Infinity;
1817
+ triangle_start_depth = Infinity;
1818
+ triangle_centre_crossing = Infinity;
1819
+ triangle_edges_pending = false;
1820
+
1821
+ const e1x = bx - ax, e1y = by - ay, e1z = bz - az;
1822
+ const e2x = cx - ax, e2y = cy - ay, e2z = cz - az;
1823
+ const nx = e1y * e2z - e1z * e2y;
1824
+ const ny = e1z * e2x - e1x * e2z;
1825
+ const nz = e1x * e2y - e1y * e2x;
1826
+ const n_len = Math.sqrt(nx * nx + ny * ny + nz * nz);
1827
+ // A degenerate (zero-area) triangle has no plane to enter and no
1828
+ // surface of its own; its neighbours carry the geometry.
1829
+ if (!(n_len > 0)) return;
1830
+ const n_inv = 1 / n_len;
1831
+
1832
+ // Orient the unit normal toward the side the sweep starts on; a start IN
1833
+ // the plane has no side, so the normal faces the sweep.
1834
+ const side = ((lx - mx) * nx + (ly - my) * ny + (lz - mz) * nz) * n_inv;
1835
+ const rate = (ldx * nx + ldy * ny + ldz * nz) * n_inv;
1836
+ const sign = side > 0 ? 1 : (side < 0 ? -1 : (rate > 0 ? -1 : 1));
1837
+ const closing = -rate * sign;
1838
+ const distance = side * sign;
1839
+
1840
+ // The CCD slop: a sweep closing on the plane by no more than the solver's
1841
+ // penetration slop is the solver's — unless its centre ends on the far
1842
+ // side, which a body thinner than the slop does while staying inside it.
1843
+ // The discrete solver pushes such a body on through: a 2 mm plate or a
1844
+ // 4 mm sphere approaching a mesh floor at under 5 mm a step passed it in
1845
+ // 23 and 15 of 30 cases.
1846
+ const sweeps_plane = closing > GRAZING_SINE_EPSILON
1847
+ && !(skip_initial_overlaps && !(closing * tMax > PENETRATION_SLOP) && distance - closing * tMax > 0);
1848
+
1849
+ const s = sign * n_inv;
1850
+
1851
+ if (!sweeps_plane) {
1852
+ // Not closing on the plane: only the edges are left to meet, and only
1853
+ // by a shape already reaching across the plane. Which of them, and
1854
+ // whether it does, is worked out only when asked
1855
+ // ({@link triangle_edges_to_sweep}).
1856
+ triangle_edges_pending = true;
1857
+ triangle_edge_shape = shape;
1858
+ triangle_edge_nearest = distance - (closing > 0 ? closing * tMax : 0);
1859
+ triangle_plane_local[0] = nx * s;
1860
+ triangle_plane_local[1] = ny * s;
1861
+ triangle_plane_local[2] = nz * s;
1862
+ return;
1863
+ }
1864
+
1865
+ // The swept shape's reach toward the plane and away from it, from its
1866
+ // supports along the (world) normal at the sweep origin.
1867
+ v3_quaternion_apply(
1868
+ plane_normal, 0,
1869
+ nx * s, ny * s, nz * s,
1870
+ target_tr.rotation_x, target_tr.rotation_y, target_tr.rotation_z, target_tr.rotation_w
1871
+ );
1872
+ const wnx = plane_normal[0], wny = plane_normal[1], wnz = plane_normal[2];
1873
+
1874
+ swept_posed.px = ox; swept_posed.py = oy; swept_posed.pz = oz;
1875
+ plane_normal_reversed[0] = -wnx;
1876
+ plane_normal_reversed[1] = -wny;
1877
+ plane_normal_reversed[2] = -wnz;
1878
+ swept_posed.support(support_point, 0, plane_normal_reversed, 0);
1879
+ const reach_near = (ox - support_point[0]) * wnx + (oy - support_point[1]) * wny + (oz - support_point[2]) * wnz;
1880
+ // the point that reaches the plane first, as the sweep starts
1881
+ triangle_touch_origin[0] = support_point[0];
1882
+ triangle_touch_origin[1] = support_point[1];
1883
+ triangle_touch_origin[2] = support_point[2];
1884
+ swept_posed.support(support_point, 0, plane_normal, 0);
1885
+ const reach_far = (support_point[0] - ox) * wnx + (support_point[1] - oy) * wny + (support_point[2] - oz) * wnz;
1886
+
1887
+ triangle_window_enter = (distance - reach_near) / closing;
1888
+ triangle_window_exit = (distance + reach_far) / closing;
1889
+ triangle_start_depth = reach_near - distance;
1890
+
1891
+ // Where the centre reaches the plane, and whether that is within the
1892
+ // triangle's edges (each edge's cross product agrees with the normal).
1893
+ if (distance >= 0) {
1894
+ const t_cross = distance / closing;
1895
+ const px = lx + ldx * t_cross, py = ly + ldy * t_cross, pz = lz + ldz * t_cross;
1896
+ if (edge_holds(ax, ay, az, bx, by, bz, px, py, pz, nx, ny, nz)
1897
+ && edge_holds(bx, by, bz, cx, cy, cz, px, py, pz, nx, ny, nz)
1898
+ && edge_holds(cx, cy, cz, ax, ay, az, px, py, pz, nx, ny, nz)) {
1899
+ triangle_centre_crossing = t_cross;
1900
+ }
1901
+ }
1902
+ }
1903
+
1904
+ /**
1905
+ * Whether point `p`, in a triangle's plane, lies on the inner side of (or on)
1906
+ * the triangle's edge from `a` to `b`, the triangle's normal being `n` (from
1907
+ * its winding, either way up).
1908
+ */
1909
+ function edge_holds(ax, ay, az, bx, by, bz, px, py, pz, nx, ny, nz) {
1910
+ const ex = bx - ax, ey = by - ay, ez = bz - az;
1911
+ const qx = px - ax, qy = py - ay, qz = pz - az;
1912
+ return nx * (ey * qz - ez * qy) + ny * (ez * qx - ex * qz) + nz * (ex * qy - ey * qx) >= 0;
1913
+ }
1914
+
245
1915
  /**
246
1916
  * Sweep a convex shape along a ray and find the first body it would
247
1917
  * hit. Used by character controllers (sweep a capsule along intended
@@ -255,35 +1925,46 @@ function recover_contact_normal(out, cx, cy, cz, dx, dy, dz, target_tr) {
255
1925
  * stretched along `ray.direction * ray.tMax`.
256
1926
  * 2. Query both broadphase BVHs (static + dynamic) for leaves whose
257
1927
  * AABB overlaps the swept volume.
258
- * 3. For each candidate, bisect the [0, best_t] interval for the
259
- * smallest `t` at which the swept shape overlaps the candidate's
260
- * collider. GJK on each midpoint.
1928
+ * 3. For each candidate, advance the swept shape toward the candidate's
1929
+ * collider by conservative advancement on the GJK distance (see
1930
+ * `advance_to_contact`) for the smallest `t` at which it touches and
1931
+ * goes on into it, within [0, best_t). A concave collider (mesh,
1932
+ * heightmap) is a surface, not the solid its hull encloses: it is
1933
+ * swept as the set of its triangles under the swept AABB, each one a
1934
+ * convex candidate under the same rules.
261
1935
  *
262
- * Picking `best_t` (rather than always running the full [0, tMax]
263
- * bisection) is the early-termination optimisation: once we have a
264
- * hit at some t, no later candidate can produce a smaller TOI by
265
- * being checked at its full [0, tMax] interval.
1936
+ * Searching only [0, best_t) (rather than always the full [0, tMax]) is the
1937
+ * early-termination optimisation: once we have a hit at some t, no later
1938
+ * candidate can produce a smaller TOI by being checked over the rest.
266
1939
  *
267
1940
  * Output on hit:
268
1941
  * - `result.t` — sweep distance to impact (in the same units as
269
1942
  * `ray.direction`'s magnitude; for a unit-direction ray, this is
270
- * metres).
1943
+ * metres). The swept shape there is still apart from what it struck
1944
+ * — by about {@link ADVANCE_TARGET}, or within the bracket search's
1945
+ * resolution when that found it — so a caller can place a body there.
271
1946
  * - `result.position` — `ray.origin + t * ray.direction`, i.e. the
272
1947
  * centre of the swept shape at the moment of first contact. NOT
273
- * a point on the target's surface; that would need narrowphase
274
- * refinement which the broadphase-only architecture doesn't have
275
- * wired up yet.
1948
+ * a point on the target's surface.
276
1949
  * - `result.normal` — target surface's outward normal at the kiss
277
- * point (unit length), recovered by running MPR against the
278
- * just-overlapping configuration at the TOI. A pair that is exactly
279
- * tangent there carries no MTV to read, so a second probe nudged
280
- * toward the target's centre supplies one; `-ray.direction` remains
281
- * the last-resort fallback if both degenerate. See the
282
- * contact-normal recovery block near the end of the function.
1950
+ * point (unit length): the separating direction advancement stopped
1951
+ * on, or a triangle face's own normal. A cast blocked where it starts
1952
+ * reads it off by MPR, and a pair exactly tangent there carries no MTV
1953
+ * to read, so a second probe nudged toward the target's centre
1954
+ * supplies one; `-ray.direction` remains the last-resort fallback if
1955
+ * both degenerate. See the contact-normal block near the end of the
1956
+ * function.
283
1957
  * - `result.entity` / `result.body_id` — the hit body.
1958
+ * - `result.collider_entity` — the entity owning the struck collider (a
1959
+ * compound body's child's own); `result.triangle` — the struck triangle of
1960
+ * a mesh or heightmap collider, `-1` for any other.
284
1961
  *
285
1962
  * Output on miss: untouched; same convention as `raycast`.
286
1963
  *
1964
+ * The filter may run a raycast or an overlap of its own, but not another cast:
1965
+ * a cast keeps its working state in module scratch, and one started from its
1966
+ * filter is refused by an assert.
1967
+ *
287
1968
  * @param {PhysicsSystem} system
288
1969
  * @param {Ray3|{origin_x:number,origin_y:number,origin_z:number,direction_x:number,direction_y:number,direction_z:number,tMax:number}} ray
289
1970
  * origin + unit direction + `tMax`. Only the named accessors are read, so a
@@ -293,30 +1974,61 @@ function recover_contact_normal(out, cx, cy, cz, dx, dy, dz, target_tr) {
293
1974
  * @param {ArrayLike<number>} rotation the orientation the shape is held at for
294
1975
  * the whole sweep, read BY INDEX as `[x, y, z, w]` and taken to be a unit
295
1976
  * quaternion (it is neither normalised nor validated here — a non-unit one
296
- * silently returns wrong answers). A `Transform64`'s `rotation` (a
297
- * `Float64Array` view — what the CCD pass hands in), a `Quaternion`
1977
+ * silently returns wrong answers). A retained `Float64Array` (what the CCD
1978
+ * pass hands in: the body's orientation copied out of its Transform64, whose
1979
+ * `rotation` accessor allocates a view per read), a `Quaternion`
298
1980
  * (`Float64Array`-backed) and a plain `[x, y, z, w]` array all satisfy that;
299
1981
  * an `{x, y, z, w}` object does NOT — its components read as `undefined`,
300
1982
  * the swept AABB comes out NaN, and every cast then misses silently
301
1983
  * @param {PhysicsSurfacePoint} result populated on hit; untouched on miss
302
1984
  * @param {(entity:number, collider:Collider, body_id:number)=>boolean} [filter]
303
- * mandatory in contract; defaults to {@link returnTrue}. Receives the
304
- * candidate body's entity, primary collider, and packed body id (the id
305
- * distinguishes bodies when colliders live on child entities)
1985
+ * mandatory in contract; defaults to {@link returnTrue}. Called once per
1986
+ * collider of each candidate body, with the body's entity, that collider,
1987
+ * and the packed body id (the id distinguishes bodies when colliders live on
1988
+ * child entities)
306
1989
  * @param {boolean} [skip_initial_overlaps=false] how a candidate ALREADY
307
1990
  * overlapping the swept shape at `t = 0` is treated. `false` (kinematic
308
1991
  * "can I move?" semantics): targets are solid, so the cast is blocked where
309
- * it stands — report a hit at `t = 0`. The one exception is a convex target
1992
+ * it stands — report a hit at `t = 0`. The one exception is a target
310
1993
  * the shape merely TOUCHES (no depth beyond {@link CONTACT_EPSILON}): a
311
1994
  * tangency blocks only a sweep driving into it, and otherwise steps aside so
312
1995
  * the scan can reach real blockers. `true` (CCD semantics): such a candidate
313
1996
  * is a resting / sliding contact the discrete solver already owns, NOT a
314
1997
  * tunnelling threat — skip it and keep scanning the remaining candidates for
315
1998
  * real blockers along the sweep. Either way, one contact at the start of the
316
- * sweep must never blind the cast to every other obstacle.
1999
+ * sweep must never blind the cast to every other obstacle. For a concave
2000
+ * target all of this is decided per TRIANGLE: a shape touching or resting
2001
+ * in one triangle of a mesh is still stopped by another one ahead of it.
317
2002
  * @returns {boolean}
318
2003
  */
319
2004
  export function shape_cast(system, ray, shape, rotation, result, filter = returnTrue, skip_initial_overlaps = false) {
2005
+ assert.ok(!shape_cast_running, NESTED_SHAPE_CAST);
2006
+
2007
+ shape_cast_running = true;
2008
+ try {
2009
+ return shape_cast_nearest(system, ray, shape, rotation, result, filter, skip_initial_overlaps);
2010
+ } finally {
2011
+ // also on the way out of a throw — a filter's own error, or the assert
2012
+ // above in a cast the filter started — so the next cast is not refused
2013
+ shape_cast_running = false;
2014
+ }
2015
+ }
2016
+
2017
+ const NONFINITE_DIRECTION = 'shape_cast: ray direction must be a unit vector, and this one has a component that is not finite';
2018
+
2019
+ /**
2020
+ * {@link shape_cast}, unguarded.
2021
+ *
2022
+ * @param {PhysicsSystem} system
2023
+ * @param {{origin_x:number,origin_y:number,origin_z:number,direction_x:number,direction_y:number,direction_z:number,tMax:number}} ray
2024
+ * @param {AbstractShape3D} shape
2025
+ * @param {ArrayLike<number>} rotation
2026
+ * @param {PhysicsSurfacePoint} result
2027
+ * @param {(entity:number, collider:Collider, body_id:number)=>boolean} filter
2028
+ * @param {boolean} skip_initial_overlaps
2029
+ * @returns {boolean}
2030
+ */
2031
+ function shape_cast_nearest(system, ray, shape, rotation, result, filter, skip_initial_overlaps) {
320
2032
  const ox = ray.origin_x;
321
2033
  const oy = ray.origin_y;
322
2034
  const oz = ray.origin_z;
@@ -331,6 +2043,13 @@ export function shape_cast(system, ray, shape, rotation, result, filter = return
331
2043
  assert.ok(Number.isFinite(tMax) && tMax > 0,
332
2044
  `shape_cast: ray.tMax must be a positive finite sweep length, got ${tMax}`);
333
2045
  if (!(tMax > 0) || !Number.isFinite(tMax)) return false;
2046
+ // Nor a direction with a component that is not finite: there is no unit
2047
+ // length to sweep by, and the swept AABB is unbounded — over a heightmap
2048
+ // too big for one enumeration, the piece walk shortened an infinite piece
2049
+ // forever. The same as the reach: loud in dev, a miss in prod.
2050
+ const direction_finite = Number.isFinite(dx) && Number.isFinite(dy) && Number.isFinite(dz);
2051
+ assert.ok(direction_finite, NONFINITE_DIRECTION);
2052
+ if (!direction_finite) return false;
334
2053
 
335
2054
  const rx = rotation[0], ry = rotation[1], rz = rotation[2], rw = rotation[3];
336
2055
 
@@ -373,24 +2092,58 @@ export function shape_cast(system, ray, shape, rotation, result, filter = return
373
2092
  }
374
2093
  if (n_total === 0) return false;
375
2094
 
376
- // ── 3. Narrowphase: per-candidate bisection ────────────────────
2095
+ // ── 3. Narrowphase: per-candidate advancement ──────────────────
377
2096
  //
378
2097
  // Re-bind the swept shape's PosedShape3D once for shape + rotation;
379
- // we update `px/py/pz` per bisection midpoint without rerunning
380
- // setup (px/py/pz are public fields by contract).
2098
+ // we update `px/py/pz` per sample pose without rerunning setup
2099
+ // (px/py/pz are public fields by contract).
381
2100
  swept_posed.shape = shape;
382
2101
  swept_posed.qx = rx; swept_posed.qy = ry; swept_posed.qz = rz; swept_posed.qw = rw;
383
2102
 
384
2103
  let best_t = tMax;
2104
+ /** When the winner's contact begins, which candidates are ranked by ({@link cast_touch_t}). */
2105
+ let best_contact = tMax;
2106
+ /** Whether the winner is a triangle face contact, which wins a tie ({@link SIMULTANEOUS}). */
2107
+ let best_is_face = false;
385
2108
  let best_body_id = -1;
386
2109
  let best_entity = -1;
387
- /** Winning collider and the transform it was posed at, for the normal probe. */
2110
+ /** The struck collider's own entity, and the struck triangle of a concave one (else -1). */
2111
+ let best_collider_entity = -1;
2112
+ let best_triangle = -1;
2113
+ /**
2114
+ * Winning convex collider, for the normal probe — null when the winner is
2115
+ * a triangle of a concave collider, kept in {@link best_triangle_local}.
2116
+ */
388
2117
  let best_collider = null;
2118
+ /** The transform the winner was posed at (a triangle's: its collider's). */
389
2119
  let best_tr = null;
2120
+ /** Whether the winner came with its contact normal, in {@link best_normal}. */
2121
+ let best_has_normal = false;
2122
+
2123
+ const stop_width = TOI_TOLERANCE * tMax;
2124
+
2125
+ // Fresh stamp for this query; wrap back to a cleared table before the
2126
+ // counter could collide with a stamp left behind by an older query.
2127
+ seen_stamp++;
2128
+ if (seen_stamp >= 0x7FFFFFFF) {
2129
+ scratch_seen.fill(0);
2130
+ seen_stamp = 1;
2131
+ }
390
2132
 
391
2133
  candidates: for (let i = 0; i < n_total; i++) {
392
2134
  const body_id = scratch_candidates[i];
393
2135
  const body_idx = body_id_index(body_id);
2136
+
2137
+ if (body_idx >= scratch_seen.length) {
2138
+ // Preserve the stamps already written this query.
2139
+ const grown = new Int32Array(Math.max(body_idx + 1, scratch_seen.length * 2));
2140
+ grown.set(scratch_seen);
2141
+ scratch_seen = grown;
2142
+ }
2143
+ // A second leaf of a body already tested: every collider of it was.
2144
+ if (scratch_seen[body_idx] === seen_stamp) continue;
2145
+ scratch_seen[body_idx] = seen_stamp;
2146
+
394
2147
  const entity = system.entityOf(body_id);
395
2148
  if (entity < 0) continue;
396
2149
 
@@ -405,193 +2158,216 @@ export function shape_cast(system, ray, shape, rotation, result, filter = return
405
2158
  if (!filter(entity, collider, body_id)) continue;
406
2159
 
407
2160
  const target_tr = colliders[ci].transform;
408
- candidate_posed.setup_components(
2161
+
2162
+ if (collider.shape.is_convex !== false) {
2163
+ candidate_is_triangle = false;
2164
+ candidate_posed.setup_components(
409
2165
  collider.shape,
410
2166
  target_tr.translation_x, target_tr.translation_y, target_tr.translation_z,
411
2167
  target_tr.rotation_x, target_tr.rotation_y, target_tr.rotation_z, target_tr.rotation_w
412
2168
  );
413
2169
 
414
- // Already overlapping at t = 0? The bisection's "no overlap at lo,
415
- // overlap at hi" invariant doesn't hold for this candidate. Under CCD
416
- // semantics it is a resting contact to step over — skip THIS candidate
417
- // only, and keep scanning for real blockers.
418
- //
419
- // Under kinematic semantics targets are SOLID: a shape that starts
420
- // genuinely inside one is blocked where it stands, `t = 0`, whatever
421
- // direction it is trying to sweep. Nothing can beat that, so stop
422
- // scanning. Getting out is depenetration's job, not the sweep's.
423
- //
424
- // Exact TANGENCY is the exception, and it needs one. A zero-depth
425
- // touch is not "inside" anything — the shape is resting against a
426
- // face, or flush with a brush seam, and is free to slide along it or
427
- // leave. It also survives depenetration, which has no depth to push
428
- // out of, so it persists frame after frame.
429
- //
430
- // Reporting it as a `t = 0` hit ends the scan, and that is the whole
431
- // problem: the caller is handed a contact it correctly judges harmless
432
- // for this direction and concludes the sweep is clear, while the body
433
- // it was actually about to hit was never tested. A character flush
434
- // against a wall falls through the floor that way. So a tangency
435
- // blocks only when the sweep drives INTO it, and otherwise steps aside
436
- // and lets the scan continue.
437
- //
438
- // MPR's depth at the START pose is what separates the two: positive
439
- // means genuine overlap, none means tangency. Tangency is also exactly
440
- // where the normal is hardest to recover, hence the second probe; if
441
- // even that degenerates there is no normal to judge with and the
442
- // contact blocks, stopping short being the safe failure.
443
- swept_posed.px = ox; swept_posed.py = oy; swept_posed.pz = oz;
444
- if (gjk(simplex_buf, swept_posed, candidate_posed)) {
445
- if (skip_initial_overlaps) continue;
446
-
447
- // The tangency exception reads MPR's depth and normal, both of
448
- // which assume a convex target. This query has never supported
449
- // concave ones (`overlap_shape` decomposes them per-triangle;
450
- // nothing here does), so rather than extend a convex-only
451
- // inference to geometry the module cannot see properly, leave
452
- // those on the unconditional "solid, blocked at t = 0" path.
453
- const convex_target = collider.shape.is_convex !== false;
454
- if (convex_target && !probe_contact_normal(probe_normal, ox, oy, oz, target_tr)) {
455
- const known = recover_contact_normal(
456
- probe_normal, ox, oy, oz, dx, dy, dz, target_tr,
457
- );
458
- if (known) {
459
- // Both vectors are unit, so this is the sine of the angle
460
- // between the sweep and the contact plane. Zero means
461
- // exactly parallel — sliding along the face — and positive
462
- // means leaving it; neither blocks. The tolerance matters
463
- // because the parallel case sits right on the sign change,
464
- // and an MPR-recovered normal carries ~1e-6 of angular
465
- // noise: without it a sweep ALONG a face flips to
466
- // "head-on" on solver noise alone.
467
- const into = dx * probe_normal[0] + dy * probe_normal[1] + dz * probe_normal[2];
468
- if (into > -GRAZING_SINE_EPSILON) continue;
469
- }
470
- }
2170
+ const t = cast_candidate(
2171
+ ox, oy, oz, dx, dy, dz, best_contact, stop_width, skip_initial_overlaps,
2172
+ -Infinity, Infinity
2173
+ );
2174
+ if (t === CAST_MISS) continue;
2175
+ if (t !== CAST_BLOCKED && !(cast_touch_t < (best_is_face ? best_contact - SIMULTANEOUS : best_contact))) continue;
471
2176
 
472
- best_t = 0;
2177
+ best_contact = cast_touch_t;
2178
+ best_is_face = false;
473
2179
  best_body_id = body_id;
474
2180
  best_entity = entity;
2181
+ best_collider_entity = colliders[ci].entity;
2182
+ best_triangle = -1;
475
2183
  best_collider = collider;
476
2184
  best_tr = target_tr;
477
- break candidates;
2185
+ best_has_normal = cast_has_normal;
2186
+ if (cast_has_normal) best_normal.set(cast_normal);
2187
+ if (t === CAST_BLOCKED) {
2188
+ best_t = 0;
2189
+ break candidates;
2190
+ }
2191
+ best_t = t;
2192
+ continue;
478
2193
  }
479
2194
 
480
- // ── Slab intersection: narrow the search interval analytically ─
2195
+ // ── Concave collider: sweep its triangles ──────────────────
481
2196
  //
482
- // Compute the body's tight world AABB and intersect it with the
483
- // swept shape's AABB sliding along the sweep direction. The
484
- // result is the t-interval `[t_aabb_enter, t_aabb_exit]` during
485
- // which the two AABBs overlap. Outside this interval the actual
486
- // shapes can't overlap either (AABB is a superset of the
487
- // shape), so we get to skip all of [0, t_aabb_enter) and
488
- // (t_aabb_exit, best_t] without testing.
2197
+ // A mesh or a heightmap is a SURFACE. Its convex hull — what the
2198
+ // whole-shape GJK above would test — reports hits in the empty
2199
+ // space between parts of one mesh and blocks a shape standing
2200
+ // anywhere inside it, and a heightmap has no support function to
2201
+ // build a hull from at all. So it is swept as the triangles under
2202
+ // the swept AABB, enumerated in its own frame by the same
2203
+ // decomposition the narrowphase and `overlap_shape` use, each one a
2204
+ // candidate in its own right.
489
2205
  //
490
- // This is the key to making `shapeCast` scale to long sweeps:
491
- // a 1 km cast of a 1 m sphere against another 1 m sphere has an
492
- // AABB-overlap window of ≈ 2 m (the sum of diameters along the
493
- // sweep axis), reducing the coarse-step grid from a 30 m
494
- // resolution (1000/32) to 6 cm (2/32).
495
- collider.shape.compute_bounding_box(target_local_aabb);
496
- aabb3_transform_oriented(
497
- target_world_aabb, 0,
498
- target_local_aabb[0], target_local_aabb[1], target_local_aabb[2],
499
- target_local_aabb[3], target_local_aabb[4], target_local_aabb[5],
500
- target_tr.translation_x, target_tr.translation_y, target_tr.translation_z,
501
- target_tr.rotation_x, target_tr.rotation_y, target_tr.rotation_z, target_tr.rotation_w
2206
+ // At most MAX_TRIANGLES_PER_PAIR of them at a time: a sweep whose box
2207
+ // takes in more is walked in pieces, in sweep order, each tested
2208
+ // only for contacts that begin within it — a later contact on one
2209
+ // of its triangles is the next piece's to find, with the triangles
2210
+ // around it — until one is hit.
2211
+ //
2212
+ // A mesh or heightmap a rigid TransformedShape3D wraps is that
2213
+ // surface at the composed pose, as the narrowphase collides it. A
2214
+ // wrapper that scales is not unwrapped, and has no enumerator, here
2215
+ // as there.
2216
+ let concave = collider.shape;
2217
+ let concave_tr = target_tr;
2218
+ while (concave.isTransformedShape3D === true && rigid_subject_pose(
2219
+ subject_pose, concave,
2220
+ concave_tr.translation_x, concave_tr.translation_y, concave_tr.translation_z,
2221
+ concave_tr.rotation_x, concave_tr.rotation_y, concave_tr.rotation_z, concave_tr.rotation_w
2222
+ )) {
2223
+ concave = concave.subject;
2224
+ concave_tr = subject_pose;
2225
+ }
2226
+
2227
+ // The sweep in the surface's frame, for the per-triangle plane
2228
+ // test in `load_triangle`.
2229
+ const qx = concave_tr.rotation_x, qy = concave_tr.rotation_y, qz = concave_tr.rotation_z, qw = concave_tr.rotation_w;
2230
+ v3_quaternion_apply_inverse(
2231
+ local_sweep, 0,
2232
+ ox - concave_tr.translation_x, oy - concave_tr.translation_y, oz - concave_tr.translation_z,
2233
+ qx, qy, qz, qw
502
2234
  );
2235
+ v3_quaternion_apply_inverse(local_sweep, 3, dx, dy, dz, qx, qy, qz, qw);
2236
+ const lx = local_sweep[0], ly = local_sweep[1], lz = local_sweep[2];
2237
+ const ldx = local_sweep[3], ldy = local_sweep[4], ldz = local_sweep[5];
503
2238
 
504
- let t_aabb_enter = -Infinity;
505
- let t_aabb_exit = Infinity;
506
- let skip_candidate = false;
507
- for (let ax = 0; ax < 3; ax++) {
508
- const init_min = initial_aabb[ax];
509
- const init_max = initial_aabb[ax + 3];
510
- const body_min = target_world_aabb[ax];
511
- const body_max = target_world_aabb[ax + 3];
512
- const d_ax = ax === 0 ? dx : (ax === 1 ? dy : dz);
513
- if (d_ax === 0) {
514
- // Static along this axis: AABBs must overlap in their
515
- // initial position or this candidate is unreachable.
516
- if (init_max < body_min || init_min > body_max) {
517
- skip_candidate = true;
518
- break;
2239
+ // The walk ends. Every piece is at least `min_piece` long, but the
2240
+ // last: one that reaches the end of the sweep, or one that cannot
2241
+ // be made to fit even that short — the shape's own box holds more
2242
+ // than the budget — which takes the rest of the sweep with it,
2243
+ // tested as far as the budget goes. So there are at most
2244
+ // 1 / MIN_PIECE_FRACTION + 1 pieces, and every shortening of one
2245
+ // takes at least a tenth off it until it is `min_piece` long.
2246
+ // Offered ever-shorter pieces instead, a sphere whose own box held
2247
+ // 0.81 of the budget walked a Zeno walk that never reached the end
2248
+ // of a 2 m slide.
2249
+ const min_piece = MIN_PIECE_FRACTION * tMax;
2250
+ let piece_start = 0;
2251
+ let piece_end = best_contact;
2252
+ let last_piece = false;
2253
+ while (piece_start < best_contact) {
2254
+ let tri_count = count_triangles(concave, concave_tr, sweep_piece_aabb(dx, dy, dz, piece_start, piece_end));
2255
+ while (tri_count > MAX_TRIANGLES_PER_PAIR && !last_piece) {
2256
+ const length = piece_end - piece_start;
2257
+ if (length > min_piece) {
2258
+ // the box's triangles grow with its area, about the square of its length
2259
+ const shorter = length * Math.sqrt(MAX_TRIANGLES_PER_PAIR / tri_count) * 0.9;
2260
+ piece_end = piece_start + (shorter > min_piece ? shorter : min_piece);
2261
+ } else {
2262
+ last_piece = true;
2263
+ piece_end = best_contact;
519
2264
  }
520
- continue;
2265
+ tri_count = count_triangles(concave, concave_tr, sweep_piece_aabb(dx, dy, dz, piece_start, piece_end));
521
2266
  }
522
- // swept_min(t) = init_min + d·t, swept_max(t) = init_max + d·t.
523
- // Slab-enter: swept_max(t) = body_min → t = (body_min − init_max) / d
524
- // Slab-exit: swept_min(t) = body_max → t = (body_max − init_min) / d
525
- const t1 = (body_min - init_max) / d_ax;
526
- const t2 = (body_max - init_min) / d_ax;
527
- const lo = t1 < t2 ? t1 : t2;
528
- const hi = t1 < t2 ? t2 : t1;
529
- if (lo > t_aabb_enter) t_aabb_enter = lo;
530
- if (hi < t_aabb_exit) t_aabb_exit = hi;
531
- }
532
- if (skip_candidate) continue;
533
- if (t_aabb_enter > t_aabb_exit) continue;
534
-
535
- // Clamp to the relevant t-window: [0, best_t]. Anything beyond
536
- // best_t can't tighten the answer.
537
- const t_start = t_aabb_enter > 0 ? t_aabb_enter : 0;
538
- const t_end = t_aabb_exit < best_t ? t_aabb_exit : best_t;
539
- if (t_start >= t_end) continue;
540
-
541
- // Coarse-search within the narrowed interval to find any t for
542
- // which the shapes overlap (their AABBs do by construction; the
543
- // shapes themselves may not, e.g. two spheres passing alongside).
544
- const step_dt = (t_end - t_start) / COARSE_STEPS;
545
- let t_prev = t_start;
546
- let t_hi = -1;
547
- for (let s = 1; s <= COARSE_STEPS; s++) {
548
- const t = t_start + s * step_dt;
549
- swept_posed.px = ox + dx * t;
550
- swept_posed.py = oy + dy * t;
551
- swept_posed.pz = oz + dz * t;
552
- if (gjk(simplex_buf, swept_posed, candidate_posed)) {
553
- t_hi = t;
554
- break;
2267
+ // A heightmap's piece was only counted: enumerate the one settled on, once.
2268
+ if (concave.isHeightMapShape3D === true) {
2269
+ tri_count = enumerate_triangles(concave, concave_tr, piece_aabb);
555
2270
  }
556
- t_prev = t;
557
- }
558
- if (t_hi < 0) continue;
559
-
560
- // Bisect [t_prev, t_hi]: invariant is `gjk(t_lo) === false` and
561
- // `gjk(t_hi) === true`.
562
- let t_lo = t_prev;
563
- const stop_width = TOI_TOLERANCE * tMax;
564
- for (let bi = 0; bi < MAX_BISECTION && (t_hi - t_lo) > stop_width; bi++) {
565
- const t_mid = (t_lo + t_hi) * 0.5;
566
- swept_posed.px = ox + dx * t_mid;
567
- swept_posed.py = oy + dy * t_mid;
568
- swept_posed.pz = oz + dz * t_mid;
569
- if (gjk(simplex_buf, swept_posed, candidate_posed)) {
570
- t_hi = t_mid;
571
- } else {
572
- t_lo = t_mid;
2271
+ if (tri_count > MAX_TRIANGLES_PER_PAIR) {
2272
+ if (!triangle_overflow_reported) {
2273
+ triangle_overflow_reported = true;
2274
+ console.warn(
2275
+ `shape_cast: the sweep's box overlaps ${tri_count} triangles of a`
2276
+ + ` ${concave.constructor.name} over a stretch too short to split;`
2277
+ + ` it is tested against ${MAX_TRIANGLES_PER_PAIR} and not the rest.`
2278
+ );
2279
+ }
2280
+ tri_count = MAX_TRIANGLES_PER_PAIR;
573
2281
  }
574
- }
575
2282
 
576
- // Use t_lo (the just-SEPARATING side of the bisection) rather
577
- // than t_hi (just-overlapping). With the fixed GJK that no
578
- // longer flags 5 mm-clear gaps as overlap, the bisection is
579
- // tight; reporting t_lo gives kinematic callers a conservative
580
- // TOI — the swept shape at the reported moment is provably NOT
581
- // overlapping the target. The normal-probe below steps a few
582
- // mm past best_t to get a valid simplex for MPR.
583
- if (t_lo < best_t) {
584
- best_t = t_lo;
585
- best_body_id = body_id;
586
- best_entity = entity;
587
- best_collider = collider;
588
- best_tr = target_tr;
2283
+ triangle_count = tri_count;
2284
+ for (let k = 0; k < tri_count; k++) {
2285
+ triangle_offset = k * TRIANGLE_FLOAT_STRIDE;
2286
+ load_triangle(
2287
+ concave, triangle_buffer, triangle_offset, concave_tr,
2288
+ lx, ly, lz, ldx, ldy, ldz,
2289
+ ox, oy, oz, tMax, skip_initial_overlaps
2290
+ );
2291
+
2292
+ // a face may still win a tie with a contact of another kind
2293
+ let reach = best_is_face ? best_contact : best_contact + SIMULTANEOUS;
2294
+ if (reach > tMax) reach = tMax;
2295
+ const limit = reach < piece_end ? reach : piece_end;
2296
+ const t = cast_candidate(
2297
+ ox, oy, oz, dx, dy, dz, limit, stop_width, skip_initial_overlaps,
2298
+ triangle_window_enter, triangle_window_exit
2299
+ );
2300
+ if (t === CAST_MISS) continue;
2301
+ const beats = t === CAST_BLOCKED || (cast_face
2302
+ ? cast_touch_t < limit
2303
+ : cast_touch_t < (best_is_face ? best_contact - SIMULTANEOUS : best_contact) && cast_touch_t < piece_end);
2304
+ if (!beats) continue;
2305
+
2306
+ best_contact = cast_touch_t;
2307
+ best_is_face = cast_face;
2308
+ best_body_id = body_id;
2309
+ best_entity = entity;
2310
+ best_collider_entity = colliders[ci].entity;
2311
+ // the enumerators' feature id is the triangle's index plus one
2312
+ best_triangle = triangle_buffer[triangle_offset + TRIANGLE_FEATURE_ID_OFFSET] - 1;
2313
+ best_collider = null;
2314
+ if (concave_tr === subject_pose) {
2315
+ best_subject_pose.copy(subject_pose);
2316
+ best_tr = best_subject_pose;
2317
+ } else {
2318
+ best_tr = concave_tr;
2319
+ }
2320
+ best_triangle_local.set(triangle_local);
2321
+ if (cast_face) {
2322
+ best_has_normal = true;
2323
+ best_normal.set(plane_normal);
2324
+ } else {
2325
+ best_has_normal = cast_has_normal;
2326
+ if (cast_has_normal) best_normal.set(cast_normal);
2327
+ }
2328
+ if (t === CAST_BLOCKED) {
2329
+ best_t = 0;
2330
+ break candidates;
2331
+ }
2332
+ best_t = t;
2333
+ }
2334
+
2335
+ // A hit in this piece is nearer than anything a later one holds.
2336
+ if (best_contact < piece_end || last_piece) break;
2337
+
2338
+ // Offer the next piece the length this one's triangles say fills
2339
+ // the budget — never less than this one's — not the rest of the
2340
+ // sweep: enumerating the rest, over-full, only to learn that it
2341
+ // was, took a third of a 2.6 m sweep's time over a
2342
+ // 720,000-triangle floor.
2343
+ const piece_length = piece_end - piece_start;
2344
+ const fill = Math.sqrt(MAX_TRIANGLES_PER_PAIR / (tri_count > 0 ? tri_count : 1)) * 0.9;
2345
+ piece_start = piece_end;
2346
+ piece_end = piece_start + (fill > 1 ? piece_length * fill : piece_length);
2347
+ if (!(piece_end < best_contact)) piece_end = best_contact;
589
2348
  }
590
2349
  }
591
2350
  }
592
2351
 
593
2352
  if (best_body_id === -1) return false;
594
2353
 
2354
+ // What was struck — a convex collider, or the one triangle of a concave
2355
+ // one — posed for {@link shape_cast_struck_support}.
2356
+ if (best_collider !== null) {
2357
+ struck_posed.setup_components(
2358
+ best_collider.shape,
2359
+ best_tr.translation_x, best_tr.translation_y, best_tr.translation_z,
2360
+ best_tr.rotation_x, best_tr.rotation_y, best_tr.rotation_z, best_tr.rotation_w
2361
+ );
2362
+ } else {
2363
+ struck_triangle_vertices.set(best_triangle_local);
2364
+ struck_posed.setup_components(
2365
+ struck_triangle_shape,
2366
+ best_tr.translation_x, best_tr.translation_y, best_tr.translation_z,
2367
+ best_tr.rotation_x, best_tr.rotation_y, best_tr.rotation_z, best_tr.rotation_w
2368
+ );
2369
+ }
2370
+
595
2371
  const rp = result.position;
596
2372
  const rn = result.normal;
597
2373
 
@@ -603,20 +2379,26 @@ export function shape_cast(system, ray, shape, rotation, result, filter = return
603
2379
  rp[1] = swept_y;
604
2380
  rp[2] = swept_z;
605
2381
 
606
- // ── Contact-normal recovery via MPR ────────────────────────────
2382
+ // ── Contact normal ─────────────────────────────────────────────
607
2383
  //
608
- // Bisection converges with `swept_posed` somewhere within
609
- // `stop_width = TOI_TOLERANCE * tMax` of the true contact. At
610
- // exactly `best_t` the overlap depth is therefore sub-millimetre
611
- // on practical sweeps. EPA on smooth-vs-smooth supports at this
612
- // overlap can't tighten before its iteration cap and returns a
613
- // direction off by ~30° from truth. MPR is the better tool here:
614
- // it converges in 5-15 iterations on smooth surfaces, same MTV
615
- // output convention as EPA (direction = A's interior into B,
616
- // magnitude = depth).
2384
+ // A hit found by advancement comes with its normal: the direction
2385
+ // separating the two at the reported pose, from the closest points the
2386
+ // distance kernel stopped on — exact for any pair of shapes, edge and
2387
+ // corner contacts included. A triangle FACE contact comes with the face's.
2388
+ // Read by penetration instead, off the one triangle that won, it leans
2389
+ // toward that triangle's edge when the contact sits near the seam of a
2390
+ // face split into triangles — a capsule striking a cube face across its
2391
+ // diagonal read 5.7°, where a convex box reads its face exactly.
2392
+ //
2393
+ // The rest — a hit blocked at the start, or one the bracket search found
2394
+ // — is read off by MPR. EPA on smooth-vs-smooth supports at this overlap
2395
+ // can't tighten before its iteration cap and returns a direction off by
2396
+ // ~30° from truth. MPR is the better tool here: it converges in 5-15
2397
+ // iterations on smooth surfaces, same MTV output convention as EPA
2398
+ // (direction = A's interior into B, magnitude = depth).
617
2399
  //
618
2400
  // The probe pose is `NORMAL_PROBE_OFFSET` past `best_t` along the sweep
619
- // (a few mm into the target) — bisection stops on the just-SEPARATING
2401
+ // (a few mm into the target) — the search stops on the just-SEPARATING
620
2402
  // side, so there is no MTV to read exactly at `best_t`. The reported
621
2403
  // `result.t` stays at `best_t`; only the normal probe moves.
622
2404
  // `recover_contact_normal` handles the grazing case, where advancing
@@ -625,17 +2407,32 @@ export function shape_cast(system, ray, shape, rotation, result, filter = return
625
2407
  const contact_y = oy + dy * best_t;
626
2408
  const contact_z = oz + dz * best_t;
627
2409
  let normal_x = -dx, normal_y = -dy, normal_z = -dz;
2410
+ result.collider_entity = best_collider_entity;
2411
+ result.triangle = best_triangle;
2412
+ if (best_has_normal) {
2413
+ rn[0] = best_normal[0]; rn[1] = best_normal[1]; rn[2] = best_normal[2];
2414
+ result.t = best_t;
2415
+ result.entity = best_entity;
2416
+ result.body_id = best_body_id;
2417
+ return true;
2418
+ }
628
2419
  if (best_collider !== null) {
2420
+ candidate_is_triangle = false;
629
2421
  candidate_posed.setup_components(
630
- best_collider.shape,
631
- best_tr.translation_x, best_tr.translation_y, best_tr.translation_z,
632
- best_tr.rotation_x, best_tr.rotation_y, best_tr.rotation_z, best_tr.rotation_w
633
- );
634
- if (recover_contact_normal(probe_normal, contact_x, contact_y, contact_z, dx, dy, dz, best_tr)) {
635
- normal_x = probe_normal[0];
636
- normal_y = probe_normal[1];
637
- normal_z = probe_normal[2];
638
- }
2422
+ best_collider.shape,
2423
+ best_tr.translation_x, best_tr.translation_y, best_tr.translation_z,
2424
+ best_tr.rotation_x, best_tr.rotation_y, best_tr.rotation_z, best_tr.rotation_w
2425
+ );
2426
+ } else {
2427
+ // The probes re-anchor the triangle themselves.
2428
+ triangle_local.set(best_triangle_local);
2429
+ triangle_tr = best_tr;
2430
+ candidate_is_triangle = true;
2431
+ }
2432
+ if (recover_contact_normal(probe_normal, contact_x, contact_y, contact_z, dx, dy, dz)) {
2433
+ normal_x = probe_normal[0];
2434
+ normal_y = probe_normal[1];
2435
+ normal_z = probe_normal[2];
639
2436
  }
640
2437
 
641
2438
  rn[0] = normal_x; rn[1] = normal_y; rn[2] = normal_z;
@@ -644,3 +2441,22 @@ export function shape_cast(system, ray, shape, rotation, result, filter = return
644
2441
  result.body_id = best_body_id;
645
2442
  return true;
646
2443
  }
2444
+
2445
+ /**
2446
+ * Support point, in world space, of what the last successful {@link shape_cast}
2447
+ * struck — the convex collider, or for a concave collider the one triangle the
2448
+ * hit was on — along the world direction in `direction[direction_offset .. +2]`, under
2449
+ * {@link AbstractShape3D#support}'s contract.
2450
+ *
2451
+ * For a caller that needs the struck surface's own geometry at the contact
2452
+ * (the CCD pass places its impulse where the two facing features overlap).
2453
+ * Valid until the next cast; meaningless before any cast has hit.
2454
+ *
2455
+ * @param {Float64Array} out receives the point
2456
+ * @param {number} out_offset
2457
+ * @param {Float64Array|number[]} direction
2458
+ * @param {number} direction_offset
2459
+ */
2460
+ export function shape_cast_struck_support(out, out_offset, direction, direction_offset) {
2461
+ struck_posed.support(out, out_offset, direction, direction_offset);
2462
+ }