@woosh/meep-engine 2.139.0 → 2.141.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 (199) hide show
  1. package/package.json +1 -1
  2. package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.d.ts +3 -3
  3. package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.d.ts.map +1 -1
  4. package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.js +4 -4
  5. package/src/{engine/physics/broadphase/aabb_transform_oriented.d.ts → core/geom/3d/aabb/aabb3_transform_oriented.d.ts} +2 -2
  6. package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts.map +1 -0
  7. package/src/{engine/physics/broadphase/aabb_transform_oriented.js → core/geom/3d/aabb/aabb3_transform_oriented.js} +1 -1
  8. package/src/core/geom/3d/quaternion/quat3_multiply.d.ts +21 -0
  9. package/src/core/geom/3d/quaternion/quat3_multiply.d.ts.map +1 -0
  10. package/src/core/geom/3d/quaternion/quat3_multiply.js +25 -0
  11. package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts +54 -0
  12. package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts.map +1 -0
  13. package/src/core/geom/3d/quaternion/quat3_to_matrix3.js +69 -0
  14. package/src/core/geom/3d/shape/AbstractShape3D.d.ts +24 -2
  15. package/src/core/geom/3d/shape/AbstractShape3D.d.ts.map +1 -1
  16. package/src/core/geom/3d/shape/AbstractShape3D.js +24 -1
  17. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +148 -0
  18. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -0
  19. package/src/core/geom/3d/shape/HeightMapShape3D.js +451 -0
  20. package/src/core/geom/3d/shape/MeshShape3D.d.ts +210 -0
  21. package/src/core/geom/3d/shape/MeshShape3D.d.ts.map +1 -0
  22. package/src/core/geom/3d/shape/MeshShape3D.js +593 -0
  23. package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
  24. package/src/core/geom/3d/shape/TransformedShape3D.js +46 -2
  25. package/src/core/geom/3d/shape/Triangle3D.d.ts +95 -0
  26. package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -0
  27. package/src/core/geom/3d/shape/Triangle3D.js +318 -0
  28. package/src/core/geom/3d/shape/UnionShape3D.js +13 -0
  29. package/src/core/geom/3d/shape/shape_mesh_from_geometry.d.ts +30 -0
  30. package/src/core/geom/3d/shape/shape_mesh_from_geometry.d.ts.map +1 -0
  31. package/src/core/geom/3d/shape/shape_mesh_from_geometry.js +64 -0
  32. package/src/core/geom/3d/tetrahedra/prototype_tetrahedrize_mesh.js +9 -11
  33. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.d.ts +28 -0
  34. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.d.ts.map +1 -0
  35. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.js +48 -0
  36. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.d.ts.map +1 -1
  37. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.js +40 -18
  38. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts +9 -5
  39. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts.map +1 -1
  40. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.js +38 -10
  41. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts +14 -5
  42. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts.map +1 -1
  43. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.js +47 -5
  44. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts +19 -0
  45. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts.map +1 -1
  46. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.js +75 -13
  47. package/src/core/geom/3d/triangle/v3_compute_triangle_normal.d.ts +2 -2
  48. package/src/core/geom/3d/triangle/v3_compute_triangle_normal.d.ts.map +1 -1
  49. package/src/core/geom/3d/triangle/v3_compute_triangle_normal.js +1 -1
  50. package/src/core/geom/vec3/v3_dot_array_array.d.ts +3 -3
  51. package/src/core/geom/vec3/v3_dot_array_array.d.ts.map +1 -1
  52. package/src/core/geom/vec3/v3_dot_array_array.js +2 -2
  53. package/src/core/geom/vec3/v3_negate_array.d.ts +3 -3
  54. package/src/core/geom/vec3/v3_negate_array.d.ts.map +1 -1
  55. package/src/core/geom/vec3/v3_negate_array.js +2 -2
  56. package/src/core/geom/vec3/v3_quat3_apply.d.ts +29 -0
  57. package/src/core/geom/vec3/v3_quat3_apply.d.ts.map +1 -0
  58. package/src/core/geom/vec3/v3_quat3_apply.js +39 -0
  59. package/src/core/geom/vec3/v3_quat3_apply_inverse.d.ts +30 -0
  60. package/src/core/geom/vec3/v3_quat3_apply_inverse.d.ts.map +1 -0
  61. package/src/core/geom/vec3/v3_quat3_apply_inverse.js +41 -0
  62. package/src/core/geom/vec3/v3_triple_cross_product.d.ts +32 -0
  63. package/src/core/geom/vec3/v3_triple_cross_product.d.ts.map +1 -0
  64. package/src/core/geom/vec3/v3_triple_cross_product.js +45 -0
  65. package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +16 -3
  66. package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
  67. package/src/engine/control/first-person/FirstPersonPlayerController.js +211 -211
  68. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +72 -8
  69. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
  70. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +37 -5
  71. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +101 -3
  72. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
  73. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +1789 -1416
  74. package/src/engine/control/first-person/TODO.md +173 -127
  75. package/src/engine/control/first-person/abilities/Slide.d.ts.map +1 -1
  76. package/src/engine/control/first-person/abilities/Slide.js +9 -1
  77. package/src/engine/control/first-person/prototype_first_person_controller.js +88 -2
  78. package/src/engine/control/first-person/test/buildTestPlayer.d.ts.map +1 -1
  79. package/src/engine/control/first-person/test/buildTestPlayer.js +9 -1
  80. package/src/engine/graphics/geometry/CapsuleGeometry.d.ts +42 -0
  81. package/src/engine/graphics/geometry/CapsuleGeometry.d.ts.map +1 -0
  82. package/src/engine/graphics/geometry/CapsuleGeometry.js +171 -0
  83. package/src/engine/physics/BULLET_REVIEW.md +945 -0
  84. package/src/engine/physics/CANNON_REVIEW.md +1300 -0
  85. package/src/engine/physics/JOLT_REVIEW.md +913 -0
  86. package/src/engine/physics/PLAN.md +578 -236
  87. package/src/engine/physics/RAPIER_REVIEW.md +934 -0
  88. package/src/engine/physics/REVIEW_001_ACTION_PLAN.md +642 -0
  89. package/src/engine/physics/REVIEW_002.md +151 -0
  90. package/src/engine/physics/broadphase/compute_fat_world_aabb.js +2 -2
  91. package/src/engine/physics/constraint/DofMode.d.ts +28 -0
  92. package/src/engine/physics/constraint/DofMode.d.ts.map +1 -0
  93. package/src/engine/physics/constraint/DofMode.js +35 -0
  94. package/src/engine/physics/constraint/solve_constraints.d.ts +16 -0
  95. package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -0
  96. package/src/engine/physics/constraint/solve_constraints.js +436 -0
  97. package/src/engine/physics/contact/ManifoldStore.d.ts +83 -10
  98. package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
  99. package/src/engine/physics/contact/ManifoldStore.js +608 -499
  100. package/src/engine/physics/ecs/ColliderObserverSystem.d.ts +2 -2
  101. package/src/engine/physics/ecs/ColliderObserverSystem.d.ts.map +1 -1
  102. package/src/engine/physics/ecs/Joint.d.ts +179 -0
  103. package/src/engine/physics/ecs/Joint.d.ts.map +1 -0
  104. package/src/engine/physics/ecs/Joint.js +234 -0
  105. package/src/engine/physics/ecs/PhysicsSystem.d.ts +180 -20
  106. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  107. package/src/engine/physics/ecs/PhysicsSystem.js +1423 -1159
  108. package/src/engine/physics/fluid/FluidField.d.ts +14 -10
  109. package/src/engine/physics/fluid/FluidField.d.ts.map +1 -1
  110. package/src/engine/physics/fluid/FluidField.js +14 -10
  111. package/src/engine/physics/fluid/FluidSimulator.js +1 -1
  112. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts +17 -10
  113. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts.map +1 -1
  114. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +18 -11
  115. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts +13 -10
  116. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts.map +1 -1
  117. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +18 -13
  118. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts +4 -3
  119. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts.map +1 -1
  120. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +15 -11
  121. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts +30 -6
  122. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts.map +1 -1
  123. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +44 -18
  124. package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts +6 -6
  125. package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts.map +1 -1
  126. package/src/engine/physics/gjk/expanding_polytope_algorithm.js +68 -22
  127. package/src/engine/physics/gjk/gjk.d.ts +28 -2
  128. package/src/engine/physics/gjk/gjk.d.ts.map +1 -1
  129. package/src/engine/physics/gjk/gjk.js +421 -378
  130. package/src/engine/physics/gjk/minkowski_support.d.ts +37 -0
  131. package/src/engine/physics/gjk/minkowski_support.d.ts.map +1 -0
  132. package/src/engine/physics/gjk/minkowski_support.js +75 -0
  133. package/src/engine/physics/gjk/mpr.d.ts +56 -0
  134. package/src/engine/physics/gjk/mpr.d.ts.map +1 -0
  135. package/src/engine/physics/gjk/mpr.js +344 -0
  136. package/src/engine/physics/inertia/world_inverse_inertia.d.ts +20 -5
  137. package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
  138. package/src/engine/physics/inertia/world_inverse_inertia.js +36 -38
  139. package/src/engine/physics/integration/integrate_position.d.ts +25 -7
  140. package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
  141. package/src/engine/physics/integration/integrate_position.js +43 -12
  142. package/src/engine/physics/integration/integrate_velocity.d.ts +30 -0
  143. package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
  144. package/src/engine/physics/integration/integrate_velocity.js +82 -1
  145. package/src/engine/physics/island/IslandBuilder.d.ts +4 -1
  146. package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -1
  147. package/src/engine/physics/island/IslandBuilder.js +33 -16
  148. package/src/engine/physics/narrowphase/PosedShape.d.ts +0 -8
  149. package/src/engine/physics/narrowphase/PosedShape.d.ts.map +1 -1
  150. package/src/engine/physics/narrowphase/PosedShape.js +28 -30
  151. package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
  152. package/src/engine/physics/narrowphase/box_box_manifold.js +140 -18
  153. package/src/engine/physics/narrowphase/box_triangle_contact.d.ts +30 -0
  154. package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -0
  155. package/src/engine/physics/narrowphase/box_triangle_contact.js +811 -0
  156. package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
  157. package/src/engine/physics/narrowphase/capsule_contacts.js +10 -56
  158. package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts +71 -0
  159. package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts.map +1 -0
  160. package/src/engine/physics/narrowphase/capsule_triangle_contact.js +375 -0
  161. package/src/engine/physics/narrowphase/compute_penetration.d.ts +91 -0
  162. package/src/engine/physics/narrowphase/compute_penetration.d.ts.map +1 -0
  163. package/src/engine/physics/narrowphase/compute_penetration.js +396 -0
  164. package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.d.ts +35 -0
  165. package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.d.ts.map +1 -0
  166. package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.js +80 -0
  167. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts +31 -0
  168. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts.map +1 -0
  169. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +55 -0
  170. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts +42 -0
  171. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts.map +1 -0
  172. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.js +204 -0
  173. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts +42 -0
  174. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts.map +1 -0
  175. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.js +94 -0
  176. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts +37 -0
  177. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts.map +1 -0
  178. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +37 -0
  179. package/src/engine/physics/narrowphase/narrowphase_step.d.ts +41 -2
  180. package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
  181. package/src/engine/physics/narrowphase/narrowphase_step.js +1497 -382
  182. package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -1
  183. package/src/engine/physics/narrowphase/sphere_box_contact.js +16 -23
  184. package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts +48 -0
  185. package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts.map +1 -0
  186. package/src/engine/physics/narrowphase/sphere_triangle_contact.js +143 -0
  187. package/src/engine/physics/queries/overlap_shape.d.ts +51 -0
  188. package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -0
  189. package/src/engine/physics/queries/overlap_shape.js +183 -0
  190. package/src/engine/physics/queries/shape_cast.d.ts +56 -0
  191. package/src/engine/physics/queries/shape_cast.d.ts.map +1 -0
  192. package/src/engine/physics/queries/shape_cast.js +387 -0
  193. package/src/engine/physics/solver/solve_contacts.d.ts +146 -32
  194. package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
  195. package/src/engine/physics/solver/solve_contacts.js +809 -223
  196. package/src/engine/physics/broadphase/aabb_transform_oriented.d.ts.map +0 -1
  197. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.d.ts +0 -20
  198. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.d.ts.map +0 -1
  199. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.js +0 -83
@@ -2,8 +2,8 @@
2
2
  * Apply the world-frame inverse inertia tensor of a rigid body to a world-space
3
3
  * vector and write the result.
4
4
  *
5
- * Body-frame inverse inertia is stored as a diagonal in principal axes
6
- * (`rb.inverseInertiaLocal`). World inverse inertia is
5
+ * Body-frame inverse inertia is supplied as a diagonal in principal axes
6
+ * (a Vector3-like with `.x .y .z`). World inverse inertia is
7
7
  * `I_w⁻¹ = R · diag · R^T` where `R` is the body's orientation. We compute
8
8
  * `result = R · diag · R^T · v` without materialising the full 3x3 matrix:
9
9
  *
@@ -17,21 +17,26 @@
17
17
  * Sphere short-circuit: if the inverse inertia is isotropic (ix === iy === iz),
18
18
  * the rotation cancels and `result = ix * v`. Skip the trig and save the work.
19
19
  *
20
+ * Decoupled from RigidBody / Transform on purpose — callers pass only what
21
+ * is actually read. Lets `compute_penetration` and other standalone
22
+ * geometry helpers reuse this without manufacturing a body.
23
+ *
20
24
  * @param {number[]|Float64Array} result length >= 3
21
25
  * @param {number} result_offset
22
- * @param {RigidBody} rb
23
- * @param {Transform} transform
26
+ * @param {{x: number, y: number, z: number}} inv_inertia_local
27
+ * Body-frame inverse inertia diagonal (typically `rb.inverseInertiaLocal`).
28
+ * @param {{x: number, y: number, z: number, w: number}} rotation
29
+ * Body orientation as a unit quaternion (typically `transform.rotation`).
24
30
  * @param {number} vx
25
31
  * @param {number} vy
26
32
  * @param {number} vz
27
33
  */
28
34
  export function world_inverse_inertia_apply(
29
35
  result, result_offset,
30
- rb, transform,
36
+ inv_inertia_local, rotation,
31
37
  vx, vy, vz
32
38
  ) {
33
- const ii = rb.inverseInertiaLocal;
34
- const ix = ii.x, iy = ii.y, iz = ii.z;
39
+ const ix = inv_inertia_local.x, iy = inv_inertia_local.y, iz = inv_inertia_local.z;
35
40
 
36
41
  if (ix === iy && iy === iz) {
37
42
  // Isotropic — rotation cancels.
@@ -41,39 +46,32 @@ export function world_inverse_inertia_apply(
41
46
  return;
42
47
  }
43
48
 
44
- const q = transform.rotation;
45
- const qx = q.x, qy = q.y, qz = q.z, qw = q.w;
49
+ const qx = rotation.x, qy = rotation.y, qz = rotation.z, qw = rotation.w;
46
50
 
47
51
  // Step 1: v_body = R^T · v. R^T corresponds to applying the conjugate
48
- // quaternion (-qx, -qy, -qz, qw).
49
- // Identity: v' = q · v · q*. For inverse rotation use q* on either side.
50
- // Equivalent expanded form:
51
- // t = (q* · v_quat) where v_quat = (vx, vy, vz, 0)
52
- // v_body = (t · q).xyz
53
- {
54
- const cx = -qx, cy = -qy, cz = -qz;
55
- // t = c · v (Hamilton product, treating v as pure quaternion w=0)
56
- const tx = qw * vx + cy * vz - cz * vy;
57
- const ty = qw * vy + cz * vx - cx * vz;
58
- const tz = qw * vz + cx * vy - cy * vx;
59
- const tw = -cx * vx - cy * vy - cz * vz;
60
- // v_body = t · (c*) where c* = (qx, qy, qz, qw)
61
- const vbx = tx * qw + tw * qx + ty * qz - tz * qy;
62
- const vby = ty * qw + tw * qy + tz * qx - tx * qz;
63
- const vbz = tz * qw + tw * qz + tx * qy - ty * qx;
52
+ // quaternion (-qx, -qy, -qz, qw). Inlined for V8 inliner — see
53
+ // PosedShape.support and core/geom/vec3/v3_quat3_apply_inverse.js
54
+ // for the canonical form.
55
+ const cx = -qx, cy = -qy, cz = -qz;
56
+ const tx = qw * vx + cy * vz - cz * vy;
57
+ const ty = qw * vy + cz * vx - cx * vz;
58
+ const tz = qw * vz + cx * vy - cy * vx;
59
+ const tw = -cx * vx - cy * vy - cz * vz;
60
+ const vbx = tx * qw + tw * qx + ty * qz - tz * qy;
61
+ const vby = ty * qw + tw * qy + tz * qx - tx * qz;
62
+ const vbz = tz * qw + tw * qz + tx * qy - ty * qx;
64
63
 
65
- // Step 2: scale by diagonal.
66
- const sx = vbx * ix;
67
- const sy = vby * iy;
68
- const sz = vbz * iz;
64
+ // Step 2: scale by the principal-frame diagonal.
65
+ const sx = vbx * ix;
66
+ const sy = vby * iy;
67
+ const sz = vbz * iz;
69
68
 
70
- // Step 3: result = R · scaled = q · scaled · q*
71
- const ux = qw * sx + qy * sz - qz * sy;
72
- const uy = qw * sy + qz * sx - qx * sz;
73
- const uz = qw * sz + qx * sy - qy * sx;
74
- const uw = -qx * sx - qy * sy - qz * sz;
75
- result[result_offset] = ux * qw + uw * (-qx) + uy * (-qz) - uz * (-qy);
76
- result[result_offset + 1] = uy * qw + uw * (-qy) + uz * (-qx) - ux * (-qz);
77
- result[result_offset + 2] = uz * qw + uw * (-qz) + ux * (-qy) - uy * (-qx);
78
- }
69
+ // Step 3: result = R · scaled = q · scaled · q*. Inlined likewise.
70
+ const ux = qw * sx + qy * sz - qz * sy;
71
+ const uy = qw * sy + qz * sx - qx * sz;
72
+ const uz = qw * sz + qx * sy - qy * sx;
73
+ const uw = -qx * sx - qy * sy - qz * sz;
74
+ result[result_offset] = ux * qw + uw * (-qx) + uy * (-qz) - uz * (-qy);
75
+ result[result_offset + 1] = uy * qw + uw * (-qy) + uz * (-qx) - ux * (-qz);
76
+ result[result_offset + 2] = uz * qw + uw * (-qz) + ux * (-qy) - uy * (-qx);
79
77
  }
@@ -1,16 +1,34 @@
1
1
  /**
2
2
  * Integrate `transform.position` and `transform.rotation` from the body's
3
- * current linear and angular velocity over a step `dt`. Static bodies are not
4
- * touched.
3
+ * current linear and angular velocity over a step `dt`. Static bodies are
4
+ * not touched.
5
5
  *
6
- * KinematicPosition bodies are treated like Dynamic for the position update
7
- * if the gameplay code is driving them, their velocity is whatever the user
8
- * wrote (typically zero); the step is effectively a no-op in that case.
9
- * KinematicVelocity bodies advance under their user-set velocity.
6
+ * The trailing six arguments are the body's *pseudo-velocity*: the
7
+ * position-pass output from the constraint solver (Catto split-impulse).
8
+ * Pseudo-velocity exists only inside the step it's folded into the
9
+ * integration so the position update reflects depth correction, then
10
+ * implicitly discarded. The body's persistent `linearVelocity` /
11
+ * `angularVelocity` (which carry restitution + warm-start across steps)
12
+ * are never contaminated by it.
13
+ *
14
+ * Callers with no position-pass output should pass zeros for the six
15
+ * pseudo arguments — folding zero is free (one add per axis).
16
+ *
17
+ * KinematicPosition bodies are treated like Dynamic for the position
18
+ * update — if the gameplay code is driving them, their velocity is
19
+ * whatever the user wrote (typically zero); the step is effectively a
20
+ * no-op in that case. KinematicVelocity bodies advance under their
21
+ * user-set velocity.
10
22
  *
11
23
  * @param {RigidBody} rb
12
24
  * @param {Transform} transform
13
25
  * @param {number} dt
26
+ * @param {number} ps_lin_x pseudo-linear-velocity x (0 when no contacts)
27
+ * @param {number} ps_lin_y
28
+ * @param {number} ps_lin_z
29
+ * @param {number} ps_ang_x pseudo-angular-velocity x (0 when no contacts)
30
+ * @param {number} ps_ang_y
31
+ * @param {number} ps_ang_z
14
32
  */
15
- export function integrate_position(rb: RigidBody, transform: Transform, dt: number): void;
33
+ export function integrate_position(rb: RigidBody, transform: Transform, dt: number, ps_lin_x: number, ps_lin_y: number, ps_lin_z: number, ps_ang_x: number, ps_ang_y: number, ps_ang_z: number): void;
16
34
  //# sourceMappingURL=integrate_position.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"integrate_position.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/integration/integrate_position.js"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;GAaG;AACH,4EAFW,MAAM,QA8BhB"}
1
+ {"version":3,"file":"integrate_position.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/integration/integrate_position.js"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,4EARW,MAAM,YACN,MAAM,YACN,MAAM,YACN,MAAM,YACN,MAAM,YACN,MAAM,YACN,MAAM,QA2ChB"}
@@ -5,19 +5,41 @@ const scratch_q = new Float64Array(4);
5
5
 
6
6
  /**
7
7
  * Integrate `transform.position` and `transform.rotation` from the body's
8
- * current linear and angular velocity over a step `dt`. Static bodies are not
9
- * touched.
8
+ * current linear and angular velocity over a step `dt`. Static bodies are
9
+ * not touched.
10
10
  *
11
- * KinematicPosition bodies are treated like Dynamic for the position update
12
- * if the gameplay code is driving them, their velocity is whatever the user
13
- * wrote (typically zero); the step is effectively a no-op in that case.
14
- * KinematicVelocity bodies advance under their user-set velocity.
11
+ * The trailing six arguments are the body's *pseudo-velocity*: the
12
+ * position-pass output from the constraint solver (Catto split-impulse).
13
+ * Pseudo-velocity exists only inside the step it's folded into the
14
+ * integration so the position update reflects depth correction, then
15
+ * implicitly discarded. The body's persistent `linearVelocity` /
16
+ * `angularVelocity` (which carry restitution + warm-start across steps)
17
+ * are never contaminated by it.
18
+ *
19
+ * Callers with no position-pass output should pass zeros for the six
20
+ * pseudo arguments — folding zero is free (one add per axis).
21
+ *
22
+ * KinematicPosition bodies are treated like Dynamic for the position
23
+ * update — if the gameplay code is driving them, their velocity is
24
+ * whatever the user wrote (typically zero); the step is effectively a
25
+ * no-op in that case. KinematicVelocity bodies advance under their
26
+ * user-set velocity.
15
27
  *
16
28
  * @param {RigidBody} rb
17
29
  * @param {Transform} transform
18
30
  * @param {number} dt
31
+ * @param {number} ps_lin_x pseudo-linear-velocity x (0 when no contacts)
32
+ * @param {number} ps_lin_y
33
+ * @param {number} ps_lin_z
34
+ * @param {number} ps_ang_x pseudo-angular-velocity x (0 when no contacts)
35
+ * @param {number} ps_ang_y
36
+ * @param {number} ps_ang_z
19
37
  */
20
- export function integrate_position(rb, transform, dt) {
38
+ export function integrate_position(
39
+ rb, transform, dt,
40
+ ps_lin_x, ps_lin_y, ps_lin_z,
41
+ ps_ang_x, ps_ang_y, ps_ang_z,
42
+ ) {
21
43
  if (rb.kind === BodyKind.Static) {
22
44
  return;
23
45
  }
@@ -25,22 +47,31 @@ export function integrate_position(rb, transform, dt) {
25
47
  const lv = rb.linearVelocity;
26
48
  const p = transform.position;
27
49
 
50
+ // Combined integration velocity: persistent + pseudo. The pseudo
51
+ // contribution exists only for this step and never lands in `lv`.
52
+ const vx = lv[0] + ps_lin_x;
53
+ const vy = lv[1] + ps_lin_y;
54
+ const vz = lv[2] + ps_lin_z;
55
+
28
56
  // Direct reads via typed-array index; the write goes through .set() so
29
57
  // Transform's onChanged subscribers (matrix recompose, parent/child sync,
30
58
  // viewport position, fog-of-war reveal) fire once per body per step.
31
59
  p.set(
32
- p[0] + lv[0] * dt,
33
- p[1] + lv[1] * dt,
34
- p[2] + lv[2] * dt
60
+ p[0] + vx * dt,
61
+ p[1] + vy * dt,
62
+ p[2] + vz * dt
35
63
  );
36
64
 
37
65
  const av = rb.angularVelocity;
38
- if (av[0] !== 0 || av[1] !== 0 || av[2] !== 0) {
66
+ const wx = av[0] + ps_ang_x;
67
+ const wy = av[1] + ps_ang_y;
68
+ const wz = av[2] + ps_ang_z;
69
+ if (wx !== 0 || wy !== 0 || wz !== 0) {
39
70
  const q = transform.rotation;
40
71
  quat_integrate(
41
72
  scratch_q,
42
73
  q[0], q[1], q[2], q[3],
43
- av[0], av[1], av[2],
74
+ wx, wy, wz,
44
75
  dt
45
76
  );
46
77
  q.set(scratch_q[0], scratch_q[1], scratch_q[2], scratch_q[3]);
@@ -22,4 +22,34 @@
22
22
  * @param {number} dt step size in seconds
23
23
  */
24
24
  export function integrate_velocity(rb: RigidBody, transform: Transform, gx: number, gy: number, gz: number, dt: number): void;
25
+ /**
26
+ * Apply the accumulated force / torque to velocity over a step `dt`, then
27
+ * zero the accumulators. Gravity and damping are NOT applied here.
28
+ *
29
+ * The "once per outer step" half of the TGS integrator: a user force is a
30
+ * per-frame budget that must land in full exactly once, independent of
31
+ * substep count. Gravity is the continuous field re-applied every substep
32
+ * by {@link integrate_velocity_gravity}.
33
+ *
34
+ * @param {RigidBody} rb
35
+ * @param {Transform} transform used to rotate body-frame inertia into world
36
+ * @param {number} dt full outer-step size in seconds
37
+ */
38
+ export function integrate_velocity_forces(rb: RigidBody, transform: Transform, dt: number): void;
39
+ /**
40
+ * Apply gravity and velocity damping over a (sub)step `dt`. Does NOT touch
41
+ * the force / torque accumulators (consumed once by
42
+ * {@link integrate_velocity_forces}).
43
+ *
44
+ * The "every substep" half of the TGS integrator. Per substep the body sees
45
+ * only `g·h` of gravity, which the per-substep contact warm-start + solve
46
+ * cancels exactly — keeping a resting stack at zero velocity. Damping is the
47
+ * stable implicit decay `v *= 1/(1 + d·dt)` applied at the substep `dt`.
48
+ *
49
+ * @param {RigidBody} rb
50
+ * @param {Transform} transform (unused; signature symmetry with the family)
51
+ * @param {number} gx @param {number} gy @param {number} gz world gravity
52
+ * @param {number} dt sub-step size in seconds
53
+ */
54
+ export function integrate_velocity_gravity(rb: RigidBody, transform: Transform, gx: number, gy: number, gz: number, dt: number): void;
25
55
  //# sourceMappingURL=integrate_velocity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"integrate_velocity.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/integration/integrate_velocity.js"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,4EALW,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,QAoDhB"}
1
+ {"version":3,"file":"integrate_velocity.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/integration/integrate_velocity.js"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,4EALW,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,QAoDhB;AAED;;;;;;;;;;;;GAYG;AACH,mFAFW,MAAM,QA6BhB;AAED;;;;;;;;;;;;;;GAcG;AACH,oFAHW,MAAM,MAAa,MAAM,MAAa,MAAM,MAC5C,MAAM,QAwBhB"}
@@ -60,7 +60,7 @@ export function integrate_velocity(rb, transform, gx, gy, gz, dt) {
60
60
  let avy = av[1];
61
61
  let avz = av[2];
62
62
  if (at[0] !== 0 || at[1] !== 0 || at[2] !== 0) {
63
- world_inverse_inertia_apply(scratch_angular, 0, rb, transform, at[0], at[1], at[2]);
63
+ world_inverse_inertia_apply(scratch_angular, 0, rb.inverseInertiaLocal, transform.rotation, at[0], at[1], at[2]);
64
64
  avx += scratch_angular[0] * dt;
65
65
  avy += scratch_angular[1] * dt;
66
66
  avz += scratch_angular[2] * dt;
@@ -77,3 +77,84 @@ export function integrate_velocity(rb, transform, gx, gy, gz, dt) {
77
77
  af[0] = 0; af[1] = 0; af[2] = 0;
78
78
  at[0] = 0; at[1] = 0; at[2] = 0;
79
79
  }
80
+
81
+ /**
82
+ * Apply the accumulated force / torque to velocity over a step `dt`, then
83
+ * zero the accumulators. Gravity and damping are NOT applied here.
84
+ *
85
+ * The "once per outer step" half of the TGS integrator: a user force is a
86
+ * per-frame budget that must land in full exactly once, independent of
87
+ * substep count. Gravity is the continuous field re-applied every substep
88
+ * by {@link integrate_velocity_gravity}.
89
+ *
90
+ * @param {RigidBody} rb
91
+ * @param {Transform} transform used to rotate body-frame inertia into world
92
+ * @param {number} dt full outer-step size in seconds
93
+ */
94
+ export function integrate_velocity_forces(rb, transform, dt) {
95
+ const af = rb.accumulatedForce;
96
+ const at = rb.accumulatedTorque;
97
+
98
+ if (rb.kind !== BodyKind.Dynamic) {
99
+ af[0] = 0; af[1] = 0; af[2] = 0;
100
+ at[0] = 0; at[1] = 0; at[2] = 0;
101
+ return;
102
+ }
103
+
104
+ const inv_m = rb.mass > 0 ? 1 / rb.mass : 0;
105
+
106
+ const lv = rb.linearVelocity;
107
+ lv[0] += af[0] * inv_m * dt;
108
+ lv[1] += af[1] * inv_m * dt;
109
+ lv[2] += af[2] * inv_m * dt;
110
+
111
+ if (at[0] !== 0 || at[1] !== 0 || at[2] !== 0) {
112
+ world_inverse_inertia_apply(scratch_angular, 0, rb.inverseInertiaLocal, transform.rotation, at[0], at[1], at[2]);
113
+ const av = rb.angularVelocity;
114
+ av[0] += scratch_angular[0] * dt;
115
+ av[1] += scratch_angular[1] * dt;
116
+ av[2] += scratch_angular[2] * dt;
117
+ }
118
+
119
+ af[0] = 0; af[1] = 0; af[2] = 0;
120
+ at[0] = 0; at[1] = 0; at[2] = 0;
121
+ }
122
+
123
+ /**
124
+ * Apply gravity and velocity damping over a (sub)step `dt`. Does NOT touch
125
+ * the force / torque accumulators (consumed once by
126
+ * {@link integrate_velocity_forces}).
127
+ *
128
+ * The "every substep" half of the TGS integrator. Per substep the body sees
129
+ * only `g·h` of gravity, which the per-substep contact warm-start + solve
130
+ * cancels exactly — keeping a resting stack at zero velocity. Damping is the
131
+ * stable implicit decay `v *= 1/(1 + d·dt)` applied at the substep `dt`.
132
+ *
133
+ * @param {RigidBody} rb
134
+ * @param {Transform} transform (unused; signature symmetry with the family)
135
+ * @param {number} gx @param {number} gy @param {number} gz world gravity
136
+ * @param {number} dt sub-step size in seconds
137
+ */
138
+ export function integrate_velocity_gravity(rb, transform, gx, gy, gz, dt) {
139
+ if (rb.kind !== BodyKind.Dynamic) {
140
+ return;
141
+ }
142
+
143
+ const gs = rb.gravityScale;
144
+ const lv = rb.linearVelocity;
145
+ let lvx = lv[0] + gx * gs * dt;
146
+ let lvy = lv[1] + gy * gs * dt;
147
+ let lvz = lv[2] + gz * gs * dt;
148
+
149
+ if (rb.linearDamping > 0) {
150
+ const k = 1 / (1 + rb.linearDamping * dt);
151
+ lvx *= k; lvy *= k; lvz *= k;
152
+ }
153
+ lv[0] = lvx; lv[1] = lvy; lv[2] = lvz;
154
+
155
+ if (rb.angularDamping > 0) {
156
+ const av = rb.angularVelocity;
157
+ const k = 1 / (1 + rb.angularDamping * dt);
158
+ av[0] *= k; av[1] *= k; av[2] *= k;
159
+ }
160
+ }
@@ -117,8 +117,11 @@ export class IslandBuilder {
117
117
  * @param {ManifoldStore} manifolds
118
118
  * @param {RigidBody[]} bodies sparse, indexed by body index
119
119
  * @param {Array[]} body_collider_lists sparse, indexed by body index
120
+ * @param {Joint[]} joints live joints (sparse). Jointed dynamic-dynamic
121
+ * bodies are unioned into the same island so a chain / ragdoll sleeps
122
+ * and wakes as a unit. Callers with no joints pass an empty array.
120
123
  */
121
- build(storage: BodyStorage, manifolds: ManifoldStore, bodies: RigidBody[], body_collider_lists: any[][]): void;
124
+ build(storage: BodyStorage, manifolds: ManifoldStore, bodies: RigidBody[], body_collider_lists: any[][], joints: Joint[]): void;
122
125
  /**
123
126
  * Fill `body_offsets` + `body_data` with awake dynamic bodies grouped
124
127
  * by island, sorted ascending within each island.
@@ -1 +1 @@
1
- {"version":3,"file":"IslandBuilder.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/island/IslandBuilder.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH;IAyFI;;;;;OAKG;IACH,gCAUC;IAtGG;;;;;OAKG;IACH,QAFU,WAAW,CAEY;IAEjC;;;;OAIG;IACH,gBAFU,UAAU,CAEoB;IAExC;;;OAGG;IACH,cAFU,MAAM,CAEK;IAErB;;;;OAIG;IACH,cAFU,WAAW,CAEiB;IAEtC;;;;OAIG;IACH,WAFU,WAAW,CAEe;IAEpC;;;OAGG;IACH,YAFU,MAAM,CAEG;IAEnB;;;;;OAKG;IACH,iBAFU,WAAW,CAEoB;IAEzC;;;;OAIG;IACH,cAFU,WAAW,CAEkB;IAEvC;;;OAGG;IACH,eAFU,MAAM,CAEM;IAEtB;;;;;OAKG;IACH,yBAA0C;IAE1C;;;;;OAKG;IACH,wBAA0C;IAE1C;;;;OAIG;IACH,kBAAmC;IAqBvC;;;;;;;;OAQG;IACH,8DAHW,WAAW,uBACX,OAAO,QAuEjB;IAED;;;;OAIG;IACH,yBA+CC;IAED;;;;;;OAMG;IACH,4BAwCC;IAED;;;;;;;;OAQG;IACH,yBAcC;IAID;;;OAGG;IACH,+BAQC;IAED;;;OAGG;IACH,uCAOC;IAED;;;OAGG;IACH,oCAIC;IAED;;;OAGG;IACH,uCAIC;CACJ"}
1
+ {"version":3,"file":"IslandBuilder.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/island/IslandBuilder.js"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH;IAyFI;;;;;OAKG;IACH,gCAUC;IAtGG;;;;;OAKG;IACH,QAFU,WAAW,CAEY;IAEjC;;;;OAIG;IACH,gBAFU,UAAU,CAEoB;IAExC;;;OAGG;IACH,cAFU,MAAM,CAEK;IAErB;;;;OAIG;IACH,cAFU,WAAW,CAEiB;IAEtC;;;;OAIG;IACH,WAFU,WAAW,CAEe;IAEpC;;;OAGG;IACH,YAFU,MAAM,CAEG;IAEnB;;;;;OAKG;IACH,iBAFU,WAAW,CAEoB;IAEzC;;;;OAIG;IACH,cAFU,WAAW,CAEkB;IAEvC;;;OAGG;IACH,eAFU,MAAM,CAEM;IAEtB;;;;;OAKG;IACH,yBAA0C;IAE1C;;;;;OAKG;IACH,wBAA0C;IAE1C;;;;OAIG;IACH,kBAAmC;IAqBvC;;;;;;;;;;;OAWG;IACH,8DANW,WAAW,uBACX,OAAO,UACP,OAAO,QAgGjB;IAED;;;;OAIG;IACH,yBA+CC;IAED;;;;;;OAMG;IACH,4BAwCC;IAED;;;;;;;;OAQG;IACH,yBAcC;IAID;;;OAGG;IACH,+BAQC;IAED;;;OAGG;IACH,uCAOC;IAED;;;OAGG;IACH,oCAIC;IAED;;;OAGG;IACH,uCAIC;CACJ"}
@@ -1,6 +1,8 @@
1
+ import { ceilPowerOfTwo } from "../../../core/binary/operations/ceilPowerOfTwo.js";
1
2
  import { body_id_index } from "../body/BodyStorage.js";
2
3
  import { BodyKind } from "../ecs/BodyKind.js";
3
4
  import { ColliderFlags } from "../ecs/ColliderFlags.js";
5
+ import { JOINT_WORLD } from "../ecs/Joint.js";
4
6
  import { RigidBodyFlags } from "../ecs/RigidBodyFlags.js";
5
7
  import { uf_find, uf_init, uf_union } from "./union_find.js";
6
8
 
@@ -149,8 +151,11 @@ export class IslandBuilder {
149
151
  * @param {ManifoldStore} manifolds
150
152
  * @param {RigidBody[]} bodies sparse, indexed by body index
151
153
  * @param {Array[]} body_collider_lists sparse, indexed by body index
154
+ * @param {Joint[]} joints live joints (sparse). Jointed dynamic-dynamic
155
+ * bodies are unioned into the same island so a chain / ragdoll sleeps
156
+ * and wakes as a unit. Callers with no joints pass an empty array.
152
157
  */
153
- build(storage, manifolds, bodies, body_collider_lists) {
158
+ build(storage, manifolds, bodies, body_collider_lists, joints) {
154
159
  const hwm = storage.high_water_mark;
155
160
  this.__ensure_body_capacity(hwm);
156
161
 
@@ -174,6 +179,29 @@ export class IslandBuilder {
174
179
  }
175
180
  }
176
181
 
182
+ // --- Pass 1b: union dynamic-dynamic bodies connected by a joint, so a
183
+ // chain / ragdoll forms one island (and so sleeps/wakes as a unit).
184
+ // Joint-to-world and joint-to-static/kinematic anchor the island
185
+ // without enlarging it — same rule as a static contact. Stale joint
186
+ // references (body unlinked / slot reused) are filtered by the
187
+ // generation-checked `is_valid`. (Empty when the caller has no
188
+ // joints — the loop is then a no-op.)
189
+ const jn = joints.length;
190
+ for (let i = 0; i < jn; i++) {
191
+ const joint = joints[i];
192
+ if (joint === undefined || joint === null) continue;
193
+ if (joint._bodyIdB === JOINT_WORLD) continue;
194
+ if (!storage.is_valid(joint._bodyIdA) || !storage.is_valid(joint._bodyIdB)) continue;
195
+ const idxA = body_id_index(joint._bodyIdA);
196
+ const idxB = body_id_index(joint._bodyIdB);
197
+ const rbA = bodies[idxA];
198
+ const rbB = bodies[idxB];
199
+ if (rbA === undefined || rbB === undefined) continue;
200
+ if (rbA.kind === BodyKind.Dynamic && rbB.kind === BodyKind.Dynamic) {
201
+ uf_union(parent, idxA, idxB);
202
+ }
203
+ }
204
+
177
205
  // --- Pass 2: collect distinct roots over awake dynamic bodies.
178
206
  const island_of_body = this.island_of_body;
179
207
  // Cheap reset: only the indices we may write are below hwm.
@@ -357,7 +385,7 @@ export class IslandBuilder {
357
385
  */
358
386
  __ensure_body_capacity(n) {
359
387
  if (this.parent.length < n) {
360
- const cap = next_pow2(n);
388
+ const cap = ceilPowerOfTwo(n);
361
389
  this.parent = new Uint32Array(cap);
362
390
  this.island_of_body = new Int32Array(cap);
363
391
  this.__root_to_island = new Int32Array(cap);
@@ -371,7 +399,7 @@ export class IslandBuilder {
371
399
  */
372
400
  __ensure_island_count_capacity(n) {
373
401
  if (this.body_offsets.length < n + 1) {
374
- const cap = next_pow2(n + 1);
402
+ const cap = ceilPowerOfTwo(n + 1);
375
403
  this.body_offsets = new Uint32Array(cap);
376
404
  this.contact_offsets = new Uint32Array(cap);
377
405
  this.__cursors = new Uint32Array(cap);
@@ -384,7 +412,7 @@ export class IslandBuilder {
384
412
  */
385
413
  __ensure_body_data_capacity(n) {
386
414
  if (this.body_data.length < n) {
387
- this.body_data = new Uint32Array(next_pow2(n));
415
+ this.body_data = new Uint32Array(ceilPowerOfTwo(n));
388
416
  }
389
417
  }
390
418
 
@@ -394,18 +422,7 @@ export class IslandBuilder {
394
422
  */
395
423
  __ensure_contact_data_capacity(n) {
396
424
  if (this.contact_data.length < n) {
397
- this.contact_data = new Uint32Array(next_pow2(n));
425
+ this.contact_data = new Uint32Array(ceilPowerOfTwo(n));
398
426
  }
399
427
  }
400
428
  }
401
-
402
- /**
403
- * @param {number} n
404
- * @returns {number}
405
- */
406
- function next_pow2(n) {
407
- if (n <= 1) return 1;
408
- let p = 1;
409
- while (p < n) p <<= 1;
410
- return p;
411
- }
@@ -45,14 +45,6 @@ export class PosedShape extends AbstractShape3D {
45
45
  z: number;
46
46
  w: number;
47
47
  }): void;
48
- /**
49
- * @param {number[]|Float32Array|Float64Array} result
50
- * @param {number} result_offset
51
- * @param {number} dx world-space query direction x (unit)
52
- * @param {number} dy world-space query direction y
53
- * @param {number} dz world-space query direction z
54
- */
55
- support(result: number[] | Float32Array | Float64Array, result_offset: number, dx: number, dy: number, dz: number): void;
56
48
  compute_bounding_box(result: any): void;
57
49
  }
58
50
  import { AbstractShape3D } from "../../../core/geom/3d/shape/AbstractShape3D.js";
@@ -1 +1 @@
1
- {"version":3,"file":"PosedShape.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/PosedShape.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;GAkBG;AACH;IAKQ,mCAAmC;IACnC,OADW,eAAe,GAAC,IAAI,CACd;IAEjB,WAAW;IAAE,WAAW;IAAE,WAAW;IACrC,WAAW;IAAE,WAAW;IAAE,WAAW;IAAE,WAAW;IAGtD;;;;;;;OAOG;IACH,aAJW,eAAe,YACf,UAAQ;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,YACpC,aAAW;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,QAM1D;IAED;;;;;;OAMG;IACH,gBANW,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,iBAClC,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,QAyChB;IAED,wCASC;CACJ;gCA7G+B,gDAAgD"}
1
+ {"version":3,"file":"PosedShape.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/PosedShape.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;GAkBG;AACH;IAKQ,mCAAmC;IACnC,OADW,eAAe,GAAC,IAAI,CACd;IAEjB,WAAW;IAAE,WAAW;IAAE,WAAW;IACrC,WAAW;IAAE,WAAW;IAAE,WAAW;IAAE,WAAW;IAGtD;;;;;;;OAOG;IACH,aAJW,eAAe,YACf,UAAQ;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,YACpC,aAAW;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,QAM1D;IAgDD,wCASC;CACJ;gCA1G+B,gDAAgD"}
@@ -1,5 +1,5 @@
1
+ import { aabb3_transform_oriented } from "../../../core/geom/3d/aabb/aabb3_transform_oriented.js";
1
2
  import { AbstractShape3D } from "../../../core/geom/3d/shape/AbstractShape3D.js";
2
- import { aabb_transform_oriented } from "../broadphase/aabb_transform_oriented.js";
3
3
 
4
4
  const scratch_local = new Float64Array(3);
5
5
  const scratch_local_aabb = new Float64Array(6);
@@ -57,49 +57,47 @@ export class PosedShape extends AbstractShape3D {
57
57
  * @param {number} dz world-space query direction z
58
58
  */
59
59
  support(result, result_offset, dx, dy, dz) {
60
- // Rotate world direction into body frame using the conjugate quaternion.
61
- // Conjugate: (-qx, -qy, -qz, qw). v_local = q* · v_world · q
60
+ // Inline q·v·q* math: PosedShape.support is the hottest GJK call
61
+ // (twice per iteration of the body-level fallback, once per
62
+ // triangle for concave dispatch). V8 declines to inline
63
+ // v3_quat3_apply at 9-arg call sites, so the math is repeated
64
+ // inline here at the cost of ~7 extra lines per direction.
65
+ // See core/geom/vec3/v3_quat3_apply.js for the canonical form
66
+ // — this is a literal expansion of it.
62
67
  const qx = this.qx, qy = this.qy, qz = this.qz, qw = this.qw;
63
68
 
64
- // Apply conjugate quaternion to dir: same formula as Vector3.applyQuaternion
65
- // but with (qx,qy,qz) negated.
66
- const cx = -qx, cy = -qy, cz = -qz;
69
+ // Rotate world direction into body frame via the conjugate
70
+ // quaternion: v_local = q* · v_world · q. Conjugate xyz are
71
+ // negated (cx, cy, cz below).
72
+ const tdx = qw * dx - qy * dz + qz * dy;
73
+ const tdy = qw * dy - qz * dx + qx * dz;
74
+ const tdz = qw * dz - qx * dy + qy * dx;
75
+ const tdw = qx * dx + qy * dy + qz * dz;
67
76
 
68
- // t = c · v
69
- const tx = qw * dx + cy * dz - cz * dy;
70
- const ty = qw * dy + cz * dx - cx * dz;
71
- const tz = qw * dz + cx * dy - cy * dx;
72
- const tw = -cx * dx - cy * dy - cz * dz;
73
-
74
- // result_local = t · c^-1; conjugate of c is (-cx,-cy,-cz,qw) = (qx,qy,qz,qw) = original q
75
- const ldx = tx * qw + tw * (-cx) + ty * (-cz) - tz * (-cy);
76
- const ldy = ty * qw + tw * (-cy) + tz * (-cx) - tx * (-cz);
77
- const ldz = tz * qw + tw * (-cz) + tx * (-cy) - ty * (-cx);
77
+ const ldx = tdx * qw + tdw * qx + tdy * qz - tdz * qy;
78
+ const ldy = tdy * qw + tdw * qy + tdz * qx - tdx * qz;
79
+ const ldz = tdz * qw + tdw * qz + tdx * qy - tdy * qx;
78
80
 
79
81
  // Ask the local shape for its support point in body-local space.
80
82
  this.shape.support(scratch_local, 0, ldx, ldy, ldz);
81
83
 
82
- // Rotate result back to world frame using original quaternion.
84
+ // Rotate the body-local support point back to world: v_world =
85
+ // q · v_local · q*.
83
86
  const lsx = scratch_local[0], lsy = scratch_local[1], lsz = scratch_local[2];
84
87
 
85
- // v_world = q · v_local · q*
86
- const ix = qw * lsx + qy * lsz - qz * lsy;
87
- const iy = qw * lsy + qz * lsx - qx * lsz;
88
- const iz = qw * lsz + qx * lsy - qy * lsx;
89
- const iw = -qx * lsx - qy * lsy - qz * lsz;
90
-
91
- const wx = ix * qw + iw * (-qx) + iy * (-qz) - iz * (-qy);
92
- const wy = iy * qw + iw * (-qy) + iz * (-qx) - ix * (-qz);
93
- const wz = iz * qw + iw * (-qz) + ix * (-qy) - iy * (-qx);
88
+ const tsx = qw * lsx + qy * lsz - qz * lsy;
89
+ const tsy = qw * lsy + qz * lsx - qx * lsz;
90
+ const tsz = qw * lsz + qx * lsy - qy * lsx;
91
+ const tsw = -qx * lsx - qy * lsy - qz * lsz;
94
92
 
95
- result[result_offset] = wx + this.px;
96
- result[result_offset + 1] = wy + this.py;
97
- result[result_offset + 2] = wz + this.pz;
93
+ result[result_offset] = tsx * qw - tsw * qx - tsy * qz + tsz * qy + this.px;
94
+ result[result_offset + 1] = tsy * qw - tsw * qy - tsz * qx + tsx * qz + this.py;
95
+ result[result_offset + 2] = tsz * qw - tsw * qz - tsx * qy + tsy * qx + this.pz;
98
96
  }
99
97
 
100
98
  compute_bounding_box(result) {
101
99
  this.shape.compute_bounding_box(scratch_local_aabb);
102
- aabb_transform_oriented(
100
+ aabb3_transform_oriented(
103
101
  result, 0,
104
102
  scratch_local_aabb[0], scratch_local_aabb[1], scratch_local_aabb[2],
105
103
  scratch_local_aabb[3], scratch_local_aabb[4], scratch_local_aabb[5],
@@ -1 +1 @@
1
- {"version":3,"file":"box_box_manifold.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/box_box_manifold.js"],"names":[],"mappings":"AAiIA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,sCAvBW,MAAM,EAAE,GAAC,YAAY,MACrB,MAAM,MACN,MAAM,MACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,GACJ,OAAO,CAwUnB;AA1bD;;;GAGG;AACH,iCAFU,MAAM,CAEoD"}
1
+ {"version":3,"file":"box_box_manifold.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/box_box_manifold.js"],"names":[],"mappings":"AAqJA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,sCAvBW,MAAM,EAAE,GAAC,YAAY,MACrB,MAAM,MACN,MAAM,MACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,GACJ,OAAO,CA8anB;AA3hBD;;;GAGG;AACH,iCAFU,MAAM,CAEoD"}