@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
@@ -1,3 +1,6 @@
1
+ import { line3_closest_points_segment_segment } from "../../../core/geom/3d/line/line3_closest_points_segment_segment.js";
2
+ import { quat3_to_matrix3 } from "../../../core/geom/3d/quaternion/quat3_to_matrix3.js";
3
+
1
4
  /**
2
5
  * Multi-point manifold construction for two oriented bounding boxes.
3
6
  *
@@ -36,6 +39,28 @@ const MAX_CONTACTS = 4;
36
39
  const CONTACT_STRIDE = 7;
37
40
  const PARALLEL_EPS_SQR = 1e-8;
38
41
 
42
+ /**
43
+ * Reference-axis tie-break deadband. SAT picks the minimum-overlap axis, but
44
+ * for aligned boxes two axes (A's face normal and B's face normal along the
45
+ * contact direction) have *equal* overlap, so floating-point noise from a
46
+ * sub-degree wobble flips which one wins frame to frame. That flips the
47
+ * reference/incident assignment, which reorders the contact points, which
48
+ * flips the solver's Gauss-Seidel sweep order — injecting an alternating
49
+ * bias that makes symmetric stacks creep and never sleep.
50
+ *
51
+ * The deadband makes a later axis replace the current best only if it
52
+ * reduces the overlap by more than `best · TIE_REL + TIE_ABS`. Axes are
53
+ * tested A-faces, then B-faces, then edge-crosses, so ties resolve to the
54
+ * earlier axis (A's face, then a face over an edge) — a stable, consistent
55
+ * choice. Genuine minima (overlap smaller by more than the band) still win,
56
+ * so SAT correctness for non-aligned boxes is unchanged; only the
57
+ * noise-driven flip on near-perfect alignment is suppressed. Box2D's
58
+ * `b2CollidePolygons` uses the same relative+absolute hysteresis.
59
+ * @type {number}
60
+ */
61
+ const TIE_REL = 0.02;
62
+ const TIE_ABS = 1e-5;
63
+
39
64
  /**
40
65
  * Length of `out` required by {@link box_box_manifold}.
41
66
  * @type {number}
@@ -61,16 +86,11 @@ const clipped_uv_out = new Float64Array(8 * 2);
61
86
  // Stride 4 per candidate: x, y, z, depth.
62
87
  const candidates = new Float64Array(8 * 4);
63
88
 
64
- // --- helpers ---------------------------------------------------------------
89
+ // Output of line3_closest_points_segment_segment for the edge-cross
90
+ // SAT-winner path: (s, t) parameters along the two edges.
91
+ const scratch_edge_st = new Float64Array(2);
65
92
 
66
- function quat_to_axes(out, off, qx, qy, qz, qw) {
67
- const xx = qx * qx, yy = qy * qy, zz = qz * qz;
68
- const xy = qx * qy, xz = qx * qz, yz = qy * qz;
69
- const wx = qw * qx, wy = qw * qy, wz = qw * qz;
70
- out[off] = 1 - 2 * (yy + zz); out[off + 1] = 2 * (xy + wz); out[off + 2] = 2 * (xz - wy);
71
- out[off + 3] = 2 * (xy - wz); out[off + 4] = 1 - 2 * (xx + zz); out[off + 5] = 2 * (yz + wx);
72
- out[off + 6] = 2 * (xz + wy); out[off + 7] = 2 * (yz - wx); out[off + 8] = 1 - 2 * (xx + yy);
73
- }
93
+ // --- helpers ---------------------------------------------------------------
74
94
 
75
95
  function projected_half_extent(axes, off, hx, hy, hz, lx, ly, lz) {
76
96
  const px = lx * axes[off] + ly * axes[off + 1] + lz * axes[off + 2];
@@ -157,8 +177,8 @@ export function box_box_manifold(
157
177
  ax, ay, az, aqx, aqy, aqz, aqw, ahx, ahy, ahz,
158
178
  bx, by, bz, bqx, bqy, bqz, bqw, bhx, bhy, bhz
159
179
  ) {
160
- quat_to_axes(scratch_axes_a, 0, aqx, aqy, aqz, aqw);
161
- quat_to_axes(scratch_axes_b, 0, bqx, bqy, bqz, bqw);
180
+ quat3_to_matrix3(scratch_axes_a, 0, aqx, aqy, aqz, aqw);
181
+ quat3_to_matrix3(scratch_axes_b, 0, bqx, bqy, bqz, bqw);
162
182
 
163
183
  const ta = scratch_axes_a;
164
184
  const tb = scratch_axes_b;
@@ -184,7 +204,11 @@ export function box_box_manifold(
184
204
  const dist = proj < 0 ? -proj : proj;
185
205
  const overlap = (rA + rB) - dist;
186
206
  if (overlap < 0) return true;
187
- if (overlap < best_overlap) {
207
+ // Deadband: the first axis always wins; a later axis replaces it only
208
+ // if it reduces the overlap past the hysteresis band. This biases ties
209
+ // toward earlier-tested axes (A faces > B faces > edges) so aligned
210
+ // boxes pick a stable reference instead of flip-flopping on noise.
211
+ if (best_source === -1 || overlap < best_overlap - (best_overlap * TIE_REL + TIE_ABS)) {
188
212
  best_overlap = overlap;
189
213
  const sign = proj > 0 ? -1 : 1; // normal points B→A
190
214
  best_nx = ux * sign;
@@ -217,14 +241,112 @@ export function box_box_manifold(
217
241
  const nx = best_nx, ny = best_ny, nz = best_nz;
218
242
  out[0] = nx; out[1] = ny; out[2] = nz;
219
243
 
220
- // ---- Edge-cross axis: single-point fallback ----
244
+ // ---- Edge-cross axis: closest-pair on the two involved edges ----
245
+ //
246
+ // When SAT identifies an edge-edge separating axis (source 6..14 =
247
+ // 6 + i*3 + j, where `i` is box A's local edge-axis index and
248
+ // `j` is box B's), the contact happens where one specific edge of
249
+ // A crosses (or is closest to) one specific edge of B. The earlier
250
+ // body-centre-midpoint fallback was sufficient for "we know they
251
+ // collide" but produced lever arms that confused the solver — for
252
+ // skewed-box stacks this led to slow drift.
253
+ //
254
+ // Procedure:
255
+ // 1. Decode (i, j) from `best_source`.
256
+ // 2. Among box A's 4 parallel edges along axis i, pick the one
257
+ // whose corner is most in the −n direction (closest to B).
258
+ // 3. Same for box B with +n direction.
259
+ // 4. `line3_closest_points_segment_segment` returns (s, t)
260
+ // parameters; reconstruct the world contact on each edge.
221
261
  if (best_source >= 6) {
222
- const mx = (ax + bx) * 0.5;
223
- const my = (ay + by) * 0.5;
224
- const mz = (az + bz) * 0.5;
262
+ const ax_src = best_source - 6;
263
+ const i_a = (ax_src / 3) | 0;
264
+ const j_b = ax_src - i_a * 3;
265
+
266
+ // Edge directions in world.
267
+ const edge_a_dx = ta[i_a * 3];
268
+ const edge_a_dy = ta[i_a * 3 + 1];
269
+ const edge_a_dz = ta[i_a * 3 + 2];
270
+ const edge_b_dx = tb[j_b * 3];
271
+ const edge_b_dy = tb[j_b * 3 + 1];
272
+ const edge_b_dz = tb[j_b * 3 + 2];
273
+
274
+ // Edge half-extents (along the chosen axis).
275
+ const edge_a_half = i_a === 0 ? ahx : (i_a === 1 ? ahy : ahz);
276
+ const edge_b_half = j_b === 0 ? bhx : (j_b === 1 ? bhy : bhz);
277
+
278
+ // Perpendicular axes (the other two) and their half-extents.
279
+ let u_a_idx, v_a_idx;
280
+ if (i_a === 0) { u_a_idx = 1; v_a_idx = 2; }
281
+ else if (i_a === 1) { u_a_idx = 2; v_a_idx = 0; }
282
+ else { u_a_idx = 0; v_a_idx = 1; }
283
+ const u_a_x = ta[u_a_idx * 3], u_a_y = ta[u_a_idx * 3 + 1], u_a_z = ta[u_a_idx * 3 + 2];
284
+ const v_a_x = ta[v_a_idx * 3], v_a_y = ta[v_a_idx * 3 + 1], v_a_z = ta[v_a_idx * 3 + 2];
285
+ const half_u_a = u_a_idx === 0 ? ahx : (u_a_idx === 1 ? ahy : ahz);
286
+ const half_v_a = v_a_idx === 0 ? ahx : (v_a_idx === 1 ? ahy : ahz);
287
+
288
+ let u_b_idx, v_b_idx;
289
+ if (j_b === 0) { u_b_idx = 1; v_b_idx = 2; }
290
+ else if (j_b === 1) { u_b_idx = 2; v_b_idx = 0; }
291
+ else { u_b_idx = 0; v_b_idx = 1; }
292
+ const u_b_x = tb[u_b_idx * 3], u_b_y = tb[u_b_idx * 3 + 1], u_b_z = tb[u_b_idx * 3 + 2];
293
+ const v_b_x = tb[v_b_idx * 3], v_b_y = tb[v_b_idx * 3 + 1], v_b_z = tb[v_b_idx * 3 + 2];
294
+ const half_u_b = u_b_idx === 0 ? bhx : (u_b_idx === 1 ? bhy : bhz);
295
+ const half_v_b = v_b_idx === 0 ? bhx : (v_b_idx === 1 ? bhy : bhz);
296
+
297
+ // The contact normal `n` (= best_n) points B → A. From A's
298
+ // perspective B is in the −n direction; pick the A-corner most
299
+ // in that direction. From B's perspective A is in +n; pick the
300
+ // B-corner most in +n.
301
+ const minus_n_dot_u_a = -(nx * u_a_x + ny * u_a_y + nz * u_a_z);
302
+ const minus_n_dot_v_a = -(nx * v_a_x + ny * v_a_y + nz * v_a_z);
303
+ const u_sign_a = minus_n_dot_u_a >= 0 ? 1 : -1;
304
+ const v_sign_a = minus_n_dot_v_a >= 0 ? 1 : -1;
305
+
306
+ const plus_n_dot_u_b = nx * u_b_x + ny * u_b_y + nz * u_b_z;
307
+ const plus_n_dot_v_b = nx * v_b_x + ny * v_b_y + nz * v_b_z;
308
+ const u_sign_b = plus_n_dot_u_b >= 0 ? 1 : -1;
309
+ const v_sign_b = plus_n_dot_v_b >= 0 ? 1 : -1;
310
+
311
+ // Box-A edge centre = A_centre + u_sign_a * half_u_a * u_a
312
+ // + v_sign_a * half_v_a * v_a.
313
+ const corner_a_cx = ax + u_a_x * u_sign_a * half_u_a + v_a_x * v_sign_a * half_v_a;
314
+ const corner_a_cy = ay + u_a_y * u_sign_a * half_u_a + v_a_y * v_sign_a * half_v_a;
315
+ const corner_a_cz = az + u_a_z * u_sign_a * half_u_a + v_a_z * v_sign_a * half_v_a;
316
+ const edge_a_p1_x = corner_a_cx - edge_a_dx * edge_a_half;
317
+ const edge_a_p1_y = corner_a_cy - edge_a_dy * edge_a_half;
318
+ const edge_a_p1_z = corner_a_cz - edge_a_dz * edge_a_half;
319
+ const edge_a_p2_x = corner_a_cx + edge_a_dx * edge_a_half;
320
+ const edge_a_p2_y = corner_a_cy + edge_a_dy * edge_a_half;
321
+ const edge_a_p2_z = corner_a_cz + edge_a_dz * edge_a_half;
322
+
323
+ const corner_b_cx = bx + u_b_x * u_sign_b * half_u_b + v_b_x * v_sign_b * half_v_b;
324
+ const corner_b_cy = by + u_b_y * u_sign_b * half_u_b + v_b_y * v_sign_b * half_v_b;
325
+ const corner_b_cz = bz + u_b_z * u_sign_b * half_u_b + v_b_z * v_sign_b * half_v_b;
326
+ const edge_b_p1_x = corner_b_cx - edge_b_dx * edge_b_half;
327
+ const edge_b_p1_y = corner_b_cy - edge_b_dy * edge_b_half;
328
+ const edge_b_p1_z = corner_b_cz - edge_b_dz * edge_b_half;
329
+ const edge_b_p2_x = corner_b_cx + edge_b_dx * edge_b_half;
330
+ const edge_b_p2_y = corner_b_cy + edge_b_dy * edge_b_half;
331
+ const edge_b_p2_z = corner_b_cz + edge_b_dz * edge_b_half;
332
+
333
+ line3_closest_points_segment_segment(
334
+ scratch_edge_st,
335
+ edge_a_p1_x, edge_a_p1_y, edge_a_p1_z, edge_a_p2_x, edge_a_p2_y, edge_a_p2_z,
336
+ edge_b_p1_x, edge_b_p1_y, edge_b_p1_z, edge_b_p2_x, edge_b_p2_y, edge_b_p2_z
337
+ );
338
+ const s = scratch_edge_st[0];
339
+ const t = scratch_edge_st[1];
340
+ const contact_a_x = edge_a_p1_x + s * (edge_a_p2_x - edge_a_p1_x);
341
+ const contact_a_y = edge_a_p1_y + s * (edge_a_p2_y - edge_a_p1_y);
342
+ const contact_a_z = edge_a_p1_z + s * (edge_a_p2_z - edge_a_p1_z);
343
+ const contact_b_x = edge_b_p1_x + t * (edge_b_p2_x - edge_b_p1_x);
344
+ const contact_b_y = edge_b_p1_y + t * (edge_b_p2_y - edge_b_p1_y);
345
+ const contact_b_z = edge_b_p1_z + t * (edge_b_p2_z - edge_b_p1_z);
346
+
225
347
  out[3] = 1;
226
- out[4] = mx; out[5] = my; out[6] = mz;
227
- out[7] = mx; out[8] = my; out[9] = mz;
348
+ out[4] = contact_a_x; out[5] = contact_a_y; out[6] = contact_a_z;
349
+ out[7] = contact_b_x; out[8] = contact_b_y; out[9] = contact_b_z;
228
350
  out[10] = best_overlap;
229
351
  return true;
230
352
  }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @param {number[]|Float64Array} out length >= {@link BOX_TRIANGLE_OUT_LENGTH}
3
+ * @param {number} bcx box centre x (world)
4
+ * @param {number} bcy
5
+ * @param {number} bcz
6
+ * @param {number} bqx box quaternion x
7
+ * @param {number} bqy
8
+ * @param {number} bqz
9
+ * @param {number} bqw
10
+ * @param {number} bhx box half-extent x (body frame)
11
+ * @param {number} bhy
12
+ * @param {number} bhz
13
+ * @param {number} ax triangle vertex A x (world)
14
+ * @param {number} ay
15
+ * @param {number} az
16
+ * @param {number} bx triangle vertex B x (world)
17
+ * @param {number} by
18
+ * @param {number} bz
19
+ * @param {number} cx triangle vertex C x (world)
20
+ * @param {number} cy
21
+ * @param {number} cz
22
+ * @returns {boolean} true if box and triangle overlap
23
+ */
24
+ export function box_triangle_contact(out: number[] | Float64Array, bcx: number, bcy: number, bcz: number, bqx: number, bqy: number, bqz: number, bqw: number, bhx: number, bhy: number, bhz: number, ax: number, ay: number, az: number, bx: number, by: number, bz: number, cx: number, cy: number, cz: number): boolean;
25
+ /**
26
+ * Length of `out` required by {@link box_triangle_contact}.
27
+ * @type {number}
28
+ */
29
+ export const BOX_TRIANGLE_OUT_LENGTH: number;
30
+ //# sourceMappingURL=box_triangle_contact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"box_triangle_contact.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/box_triangle_contact.js"],"names":[],"mappings":"AAuNA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,0CAtBW,MAAM,EAAE,GAAC,YAAY,OACrB,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,GACJ,OAAO,CAuJnB;AAxUD;;;GAGG;AACH,sCAFU,MAAM,CAEyD"}