@woosh/meep-engine 2.139.0 → 2.140.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 (172) 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_to_matrix3.d.ts +54 -0
  9. package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts.map +1 -0
  10. package/src/core/geom/3d/quaternion/quat3_to_matrix3.js +69 -0
  11. package/src/core/geom/3d/shape/AbstractShape3D.d.ts +24 -2
  12. package/src/core/geom/3d/shape/AbstractShape3D.d.ts.map +1 -1
  13. package/src/core/geom/3d/shape/AbstractShape3D.js +24 -1
  14. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +148 -0
  15. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -0
  16. package/src/core/geom/3d/shape/HeightMapShape3D.js +451 -0
  17. package/src/core/geom/3d/shape/MeshShape3D.d.ts +210 -0
  18. package/src/core/geom/3d/shape/MeshShape3D.d.ts.map +1 -0
  19. package/src/core/geom/3d/shape/MeshShape3D.js +593 -0
  20. package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
  21. package/src/core/geom/3d/shape/TransformedShape3D.js +46 -2
  22. package/src/core/geom/3d/shape/Triangle3D.d.ts +95 -0
  23. package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -0
  24. package/src/core/geom/3d/shape/Triangle3D.js +318 -0
  25. package/src/core/geom/3d/shape/UnionShape3D.js +13 -0
  26. package/src/core/geom/3d/shape/shape_mesh_from_geometry.d.ts +30 -0
  27. package/src/core/geom/3d/shape/shape_mesh_from_geometry.d.ts.map +1 -0
  28. package/src/core/geom/3d/shape/shape_mesh_from_geometry.js +64 -0
  29. package/src/core/geom/3d/tetrahedra/prototype_tetrahedrize_mesh.js +9 -11
  30. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.d.ts +28 -0
  31. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.d.ts.map +1 -0
  32. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.js +48 -0
  33. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.d.ts.map +1 -1
  34. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.js +40 -18
  35. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts +9 -5
  36. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts.map +1 -1
  37. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.js +38 -10
  38. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts +14 -5
  39. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts.map +1 -1
  40. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.js +47 -5
  41. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts +19 -0
  42. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts.map +1 -1
  43. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.js +75 -13
  44. package/src/core/geom/3d/triangle/v3_compute_triangle_normal.d.ts +2 -2
  45. package/src/core/geom/3d/triangle/v3_compute_triangle_normal.d.ts.map +1 -1
  46. package/src/core/geom/3d/triangle/v3_compute_triangle_normal.js +1 -1
  47. package/src/core/geom/vec3/v3_dot_array_array.d.ts +3 -3
  48. package/src/core/geom/vec3/v3_dot_array_array.d.ts.map +1 -1
  49. package/src/core/geom/vec3/v3_dot_array_array.js +2 -2
  50. package/src/core/geom/vec3/v3_negate_array.d.ts +3 -3
  51. package/src/core/geom/vec3/v3_negate_array.d.ts.map +1 -1
  52. package/src/core/geom/vec3/v3_negate_array.js +2 -2
  53. package/src/core/geom/vec3/v3_quat3_apply.d.ts +29 -0
  54. package/src/core/geom/vec3/v3_quat3_apply.d.ts.map +1 -0
  55. package/src/core/geom/vec3/v3_quat3_apply.js +39 -0
  56. package/src/core/geom/vec3/v3_quat3_apply_inverse.d.ts +30 -0
  57. package/src/core/geom/vec3/v3_quat3_apply_inverse.d.ts.map +1 -0
  58. package/src/core/geom/vec3/v3_quat3_apply_inverse.js +41 -0
  59. package/src/core/geom/vec3/v3_triple_cross_product.d.ts +32 -0
  60. package/src/core/geom/vec3/v3_triple_cross_product.d.ts.map +1 -0
  61. package/src/core/geom/vec3/v3_triple_cross_product.js +45 -0
  62. package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +16 -3
  63. package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
  64. package/src/engine/control/first-person/FirstPersonPlayerController.js +211 -211
  65. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +72 -8
  66. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
  67. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +37 -5
  68. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +101 -3
  69. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
  70. package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +1789 -1416
  71. package/src/engine/control/first-person/TODO.md +173 -127
  72. package/src/engine/control/first-person/abilities/Slide.d.ts.map +1 -1
  73. package/src/engine/control/first-person/abilities/Slide.js +9 -1
  74. package/src/engine/control/first-person/prototype_first_person_controller.js +88 -2
  75. package/src/engine/control/first-person/test/buildTestPlayer.d.ts.map +1 -1
  76. package/src/engine/control/first-person/test/buildTestPlayer.js +9 -1
  77. package/src/engine/graphics/geometry/CapsuleGeometry.d.ts +42 -0
  78. package/src/engine/graphics/geometry/CapsuleGeometry.d.ts.map +1 -0
  79. package/src/engine/graphics/geometry/CapsuleGeometry.js +171 -0
  80. package/src/engine/physics/BULLET_REVIEW.md +945 -0
  81. package/src/engine/physics/CANNON_REVIEW.md +1300 -0
  82. package/src/engine/physics/JOLT_REVIEW.md +913 -0
  83. package/src/engine/physics/PLAN.md +461 -236
  84. package/src/engine/physics/RAPIER_REVIEW.md +934 -0
  85. package/src/engine/physics/REVIEW_001_ACTION_PLAN.md +642 -0
  86. package/src/engine/physics/broadphase/compute_fat_world_aabb.js +2 -2
  87. package/src/engine/physics/contact/ManifoldStore.d.ts +83 -10
  88. package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
  89. package/src/engine/physics/contact/ManifoldStore.js +608 -499
  90. package/src/engine/physics/ecs/ColliderObserverSystem.d.ts +2 -2
  91. package/src/engine/physics/ecs/ColliderObserverSystem.d.ts.map +1 -1
  92. package/src/engine/physics/ecs/PhysicsSystem.d.ts +128 -20
  93. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  94. package/src/engine/physics/ecs/PhysicsSystem.js +1301 -1159
  95. package/src/engine/physics/fluid/FluidSimulator.d.ts.map +1 -1
  96. package/src/engine/physics/fluid/FluidSimulator.js +2 -1
  97. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts +28 -6
  98. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts.map +1 -1
  99. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +39 -17
  100. package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts +6 -6
  101. package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts.map +1 -1
  102. package/src/engine/physics/gjk/expanding_polytope_algorithm.js +68 -22
  103. package/src/engine/physics/gjk/gjk.d.ts +28 -2
  104. package/src/engine/physics/gjk/gjk.d.ts.map +1 -1
  105. package/src/engine/physics/gjk/gjk.js +421 -378
  106. package/src/engine/physics/gjk/minkowski_support.d.ts +37 -0
  107. package/src/engine/physics/gjk/minkowski_support.d.ts.map +1 -0
  108. package/src/engine/physics/gjk/minkowski_support.js +75 -0
  109. package/src/engine/physics/gjk/mpr.d.ts +56 -0
  110. package/src/engine/physics/gjk/mpr.d.ts.map +1 -0
  111. package/src/engine/physics/gjk/mpr.js +344 -0
  112. package/src/engine/physics/inertia/world_inverse_inertia.d.ts +20 -5
  113. package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
  114. package/src/engine/physics/inertia/world_inverse_inertia.js +36 -38
  115. package/src/engine/physics/integration/integrate_position.d.ts +25 -7
  116. package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
  117. package/src/engine/physics/integration/integrate_position.js +43 -12
  118. package/src/engine/physics/integration/integrate_velocity.d.ts +30 -0
  119. package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
  120. package/src/engine/physics/integration/integrate_velocity.js +82 -1
  121. package/src/engine/physics/narrowphase/PosedShape.d.ts +0 -8
  122. package/src/engine/physics/narrowphase/PosedShape.d.ts.map +1 -1
  123. package/src/engine/physics/narrowphase/PosedShape.js +28 -30
  124. package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
  125. package/src/engine/physics/narrowphase/box_box_manifold.js +113 -17
  126. package/src/engine/physics/narrowphase/box_triangle_contact.d.ts +30 -0
  127. package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -0
  128. package/src/engine/physics/narrowphase/box_triangle_contact.js +811 -0
  129. package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
  130. package/src/engine/physics/narrowphase/capsule_contacts.js +10 -56
  131. package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts +71 -0
  132. package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts.map +1 -0
  133. package/src/engine/physics/narrowphase/capsule_triangle_contact.js +375 -0
  134. package/src/engine/physics/narrowphase/compute_penetration.d.ts +91 -0
  135. package/src/engine/physics/narrowphase/compute_penetration.d.ts.map +1 -0
  136. package/src/engine/physics/narrowphase/compute_penetration.js +396 -0
  137. package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.d.ts +35 -0
  138. package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.d.ts.map +1 -0
  139. package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.js +80 -0
  140. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts +31 -0
  141. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts.map +1 -0
  142. package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +55 -0
  143. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts +42 -0
  144. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts.map +1 -0
  145. package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.js +204 -0
  146. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts +42 -0
  147. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts.map +1 -0
  148. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.js +94 -0
  149. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts +37 -0
  150. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts.map +1 -0
  151. package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +37 -0
  152. package/src/engine/physics/narrowphase/narrowphase_step.d.ts +8 -2
  153. package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
  154. package/src/engine/physics/narrowphase/narrowphase_step.js +1422 -382
  155. package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -1
  156. package/src/engine/physics/narrowphase/sphere_box_contact.js +16 -23
  157. package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts +48 -0
  158. package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts.map +1 -0
  159. package/src/engine/physics/narrowphase/sphere_triangle_contact.js +143 -0
  160. package/src/engine/physics/queries/overlap_shape.d.ts +51 -0
  161. package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -0
  162. package/src/engine/physics/queries/overlap_shape.js +183 -0
  163. package/src/engine/physics/queries/shape_cast.d.ts +56 -0
  164. package/src/engine/physics/queries/shape_cast.d.ts.map +1 -0
  165. package/src/engine/physics/queries/shape_cast.js +387 -0
  166. package/src/engine/physics/solver/solve_contacts.d.ts +116 -30
  167. package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
  168. package/src/engine/physics/solver/solve_contacts.js +641 -223
  169. package/src/engine/physics/broadphase/aabb_transform_oriented.d.ts.map +0 -1
  170. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.d.ts +0 -20
  171. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.d.ts.map +0 -1
  172. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.js +0 -83
@@ -1 +1 @@
1
- {"version":3,"file":"sphere_box_contact.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/sphere_box_contact.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wCAjBW,MAAM,EAAE,GAAC,YAAY,MACrB,MAAM,MACN,MAAM,MACN,MAAM,UACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,GACJ,OAAO,CA+FnB"}
1
+ {"version":3,"file":"sphere_box_contact.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/sphere_box_contact.js"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wCAjBW,MAAM,EAAE,GAAC,YAAY,MACrB,MAAM,MACN,MAAM,MACN,MAAM,UACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,GACJ,OAAO,CA8EnB"}
@@ -1,3 +1,13 @@
1
+ import { v3_quat3_apply } from "../../../core/geom/vec3/v3_quat3_apply.js";
2
+ import { v3_quat3_apply_inverse } from "../../../core/geom/vec3/v3_quat3_apply_inverse.js";
3
+
4
+ /**
5
+ * Scratch for the world→box-local rotation of the sphere centre at the
6
+ * top of {@link sphere_box_contact}. 3 floats; allocation-free.
7
+ * @type {Float64Array}
8
+ */
9
+ const scratch_local = new Float64Array(3);
10
+
1
11
  /**
2
12
  * Closed-form contact generation for a unit sphere vs. an oriented box.
3
13
  *
@@ -43,18 +53,10 @@ export function sphere_box_contact(
43
53
  ) {
44
54
  // Step 1: bring the sphere centre into box-local space via the conjugate
45
55
  // quaternion. v_local = q* · (s - b) · q
46
- const dx = sx - bx, dy = sy - by, dz = sz - bz;
47
-
48
- // Apply conjugate (-qx,-qy,-qz, qw) to (dx,dy,dz,0):
49
- const cx = -bqx, cy = -bqy, cz = -bqz;
50
- const tx0 = bqw * dx + cy * dz - cz * dy;
51
- const ty0 = bqw * dy + cz * dx - cx * dz;
52
- const tz0 = bqw * dz + cx * dy - cy * dx;
53
- const tw0 = -cx * dx - cy * dy - cz * dz;
54
- // Then multiply by c* = (qx,qy,qz,qw) (the original quaternion):
55
- const lx = tx0 * bqw + tw0 * bqx + ty0 * bqz - tz0 * bqy;
56
- const ly = ty0 * bqw + tw0 * bqy + tz0 * bqx - tx0 * bqz;
57
- const lz = tz0 * bqw + tw0 * bqz + tx0 * bqy - ty0 * bqx;
56
+ v3_quat3_apply_inverse(scratch_local, 0, sx - bx, sy - by, sz - bz, bqx, bqy, bqz, bqw);
57
+ const lx = scratch_local[0];
58
+ const ly = scratch_local[1];
59
+ const lz = scratch_local[2];
58
60
 
59
61
  // Step 2: closest point on the box to the sphere centre, in body frame.
60
62
  const clx = lx < -hx ? -hx : (lx > hx ? hx : lx);
@@ -100,18 +102,9 @@ export function sphere_box_contact(
100
102
  }
101
103
 
102
104
  // Step 3: rotate normal and surface point back to world. v_world = q · v · q*
103
- function rotate_to_world(out_arr, off, vx, vy, vz) {
104
- const ix = bqw * vx + bqy * vz - bqz * vy;
105
- const iy = bqw * vy + bqz * vx - bqx * vz;
106
- const iz = bqw * vz + bqx * vy - bqy * vx;
107
- const iw = -bqx * vx - bqy * vy - bqz * vz;
108
- out_arr[off] = ix * bqw + iw * (-bqx) + iy * (-bqz) - iz * (-bqy);
109
- out_arr[off + 1] = iy * bqw + iw * (-bqy) + iz * (-bqx) - ix * (-bqz);
110
- out_arr[off + 2] = iz * bqw + iw * (-bqz) + ix * (-bqy) - iy * (-bqx);
111
- }
112
105
 
113
106
  // World normal (box → sphere).
114
- rotate_to_world(out, 0, nlx, nly, nlz);
107
+ v3_quat3_apply(out, 0, nlx, nly, nlz, bqx, bqy, bqz, bqw);
115
108
  const nx = out[0], ny = out[1], nz = out[2];
116
109
  out[3] = radius - dist;
117
110
 
@@ -121,7 +114,7 @@ export function sphere_box_contact(
121
114
  out[6] = sz - nz * radius;
122
115
 
123
116
  // World contact on box: rotate the local-space clamped point to world, plus box centre.
124
- rotate_to_world(out, 7, clx, cly, clz);
117
+ v3_quat3_apply(out, 7, clx, cly, clz, bqx, bqy, bqz, bqw);
125
118
  out[7] += bx;
126
119
  out[8] += by;
127
120
  out[9] += bz;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Closed-form contact generation for a sphere vs. a triangle.
3
+ *
4
+ * Algorithm: find the closest point on the triangle to the sphere centre
5
+ * via Ericson barycentric voronoi-region projection, then compare the
6
+ * distance to the sphere radius. The result is closed-form, exact for
7
+ * the geometric primitives involved, and avoids the precision wall that
8
+ * GJK+EPA hits on {@link Triangle3D} — whose support function is
9
+ * degenerate along the face normal (all 3 vertices project to the same
10
+ * value) and produces noisy depths at small penetrations.
11
+ *
12
+ * Sphere-vs-triangle is the building block for sphere-vs-concave
13
+ * (mesh, heightmap) narrowphase: the concave dispatch in
14
+ * {@link narrowphase_step} decomposes the concave shape into world-space
15
+ * triangles and runs this function per triangle.
16
+ *
17
+ * Output convention (mirrors {@link sphere_box_contact} and
18
+ * {@link sphere_sphere_contact}): the normal in `out[0..2]` points
19
+ * from the triangle's surface toward the sphere centre — i.e. the
20
+ * direction the sphere should be pushed to separate; `out[3]` is the
21
+ * positive penetration depth; `out[4..6]` is the world contact on
22
+ * the sphere's surface; `out[7..9]` is the world contact on the
23
+ * triangle's surface (the closest point).
24
+ *
25
+ * Singular case: when the sphere centre lies exactly on the triangle's
26
+ * plane (distance == 0), the normal direction is geometrically undefined.
27
+ * We fall back to the triangle's face normal (CCW winding), which is the
28
+ * direction the surrounding solid is "outward" from for closed meshes
29
+ * and heightmaps.
30
+ *
31
+ * @param {number[]|Float64Array} out length >= 10
32
+ * @param {number} sx sphere centre x (world)
33
+ * @param {number} sy
34
+ * @param {number} sz
35
+ * @param {number} radius
36
+ * @param {number} ax triangle vertex A x (world)
37
+ * @param {number} ay
38
+ * @param {number} az
39
+ * @param {number} bx triangle vertex B x (world)
40
+ * @param {number} by
41
+ * @param {number} bz
42
+ * @param {number} cx triangle vertex C x (world)
43
+ * @param {number} cy
44
+ * @param {number} cz
45
+ * @returns {boolean} true if sphere overlaps the triangle
46
+ */
47
+ export function sphere_triangle_contact(out: number[] | Float64Array, sx: number, sy: number, sz: number, radius: number, ax: number, ay: number, az: number, bx: number, by: number, bz: number, cx: number, cy: number, cz: number): boolean;
48
+ //# sourceMappingURL=sphere_triangle_contact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sphere_triangle_contact.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/narrowphase/sphere_triangle_contact.js"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,6CAhBW,MAAM,EAAE,GAAC,YAAY,MACrB,MAAM,MACN,MAAM,MACN,MAAM,UACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,GACJ,OAAO,CAwFnB"}
@@ -0,0 +1,143 @@
1
+ import { computeTriangleClosestPointToPointBarycentric } from "../../../core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.js";
2
+
3
+ /**
4
+ * Closest-point on triangle, written into the first two floats of `out`
5
+ * as `(alpha_A, alpha_B)` — `alpha_C = 1 - alpha_A - alpha_B`. Reused
6
+ * across the {@link sphere_triangle_contact} body to avoid allocation.
7
+ * @type {Float64Array}
8
+ */
9
+ const scratch_bary = new Float64Array(2);
10
+
11
+ /**
12
+ * Closed-form contact generation for a sphere vs. a triangle.
13
+ *
14
+ * Algorithm: find the closest point on the triangle to the sphere centre
15
+ * via Ericson barycentric voronoi-region projection, then compare the
16
+ * distance to the sphere radius. The result is closed-form, exact for
17
+ * the geometric primitives involved, and avoids the precision wall that
18
+ * GJK+EPA hits on {@link Triangle3D} — whose support function is
19
+ * degenerate along the face normal (all 3 vertices project to the same
20
+ * value) and produces noisy depths at small penetrations.
21
+ *
22
+ * Sphere-vs-triangle is the building block for sphere-vs-concave
23
+ * (mesh, heightmap) narrowphase: the concave dispatch in
24
+ * {@link narrowphase_step} decomposes the concave shape into world-space
25
+ * triangles and runs this function per triangle.
26
+ *
27
+ * Output convention (mirrors {@link sphere_box_contact} and
28
+ * {@link sphere_sphere_contact}): the normal in `out[0..2]` points
29
+ * from the triangle's surface toward the sphere centre — i.e. the
30
+ * direction the sphere should be pushed to separate; `out[3]` is the
31
+ * positive penetration depth; `out[4..6]` is the world contact on
32
+ * the sphere's surface; `out[7..9]` is the world contact on the
33
+ * triangle's surface (the closest point).
34
+ *
35
+ * Singular case: when the sphere centre lies exactly on the triangle's
36
+ * plane (distance == 0), the normal direction is geometrically undefined.
37
+ * We fall back to the triangle's face normal (CCW winding), which is the
38
+ * direction the surrounding solid is "outward" from for closed meshes
39
+ * and heightmaps.
40
+ *
41
+ * @param {number[]|Float64Array} out length >= 10
42
+ * @param {number} sx sphere centre x (world)
43
+ * @param {number} sy
44
+ * @param {number} sz
45
+ * @param {number} radius
46
+ * @param {number} ax triangle vertex A x (world)
47
+ * @param {number} ay
48
+ * @param {number} az
49
+ * @param {number} bx triangle vertex B x (world)
50
+ * @param {number} by
51
+ * @param {number} bz
52
+ * @param {number} cx triangle vertex C x (world)
53
+ * @param {number} cy
54
+ * @param {number} cz
55
+ * @returns {boolean} true if sphere overlaps the triangle
56
+ */
57
+ export function sphere_triangle_contact(
58
+ out,
59
+ sx, sy, sz, radius,
60
+ ax, ay, az,
61
+ bx, by, bz,
62
+ cx, cy, cz
63
+ ) {
64
+ // Degenerate-triangle guard. For zero-area triangles (colinear or
65
+ // duplicate vertices) the closest-point algorithm still returns
66
+ // a point on the degenerate "line" but the surrounding geometry
67
+ // has no meaningful face — emitting a contact would mislead the
68
+ // solver. Catch this upfront via the face-normal cross product;
69
+ // the same magnitude is reused for the dist=0 fallback below.
70
+ //
71
+ // Cost: 9 flops + 1 sqrt. Negligible vs. the rest of the function
72
+ // and saves the caller from having to pre-filter its input.
73
+ const e1x = bx - ax, e1y = by - ay, e1z = bz - az;
74
+ const e2x = cx - ax, e2y = cy - ay, e2z = cz - az;
75
+ const fnx = e1y * e2z - e1z * e2y;
76
+ const fny = e1z * e2x - e1x * e2z;
77
+ const fnz = e1x * e2y - e1y * e2x;
78
+ const fn_mag_sqr = fnx * fnx + fny * fny + fnz * fnz;
79
+ if (fn_mag_sqr === 0) return false;
80
+
81
+ // Closest-point on triangle to sphere centre — barycentric output.
82
+ computeTriangleClosestPointToPointBarycentric(
83
+ scratch_bary, 0,
84
+ sx, sy, sz,
85
+ ax, ay, az,
86
+ bx, by, bz,
87
+ cx, cy, cz
88
+ );
89
+ const alpha_a = scratch_bary[0];
90
+ const alpha_b = scratch_bary[1];
91
+ const alpha_c = 1 - alpha_a - alpha_b;
92
+
93
+ // Reconstruct the closest world-space point.
94
+ const px = alpha_a * ax + alpha_b * bx + alpha_c * cx;
95
+ const py = alpha_a * ay + alpha_b * by + alpha_c * cy;
96
+ const pz = alpha_a * az + alpha_b * bz + alpha_c * cz;
97
+
98
+ // Vector from closest point to sphere centre.
99
+ const dx = sx - px;
100
+ const dy = sy - py;
101
+ const dz = sz - pz;
102
+ const dist_sqr = dx * dx + dy * dy + dz * dz;
103
+
104
+ if (dist_sqr >= radius * radius) {
105
+ return false;
106
+ }
107
+
108
+ const dist = Math.sqrt(dist_sqr);
109
+ let nx, ny, nz;
110
+ if (dist > 0) {
111
+ const inv = 1 / dist;
112
+ nx = dx * inv;
113
+ ny = dy * inv;
114
+ nz = dz * inv;
115
+ } else {
116
+ // Sphere centre lies on the triangle's surface — direction is
117
+ // undefined. Fall back to the triangle's face normal (CCW
118
+ // winding: (B - A) × (C - A)), which is the outward direction
119
+ // for closed meshes and heightmaps. The cross product was
120
+ // already computed for the degeneracy guard at the top of
121
+ // the function; reuse fn_mag_sqr.
122
+ const fn_mag = Math.sqrt(fn_mag_sqr);
123
+ const inv_fn = 1 / fn_mag;
124
+ nx = fnx * inv_fn;
125
+ ny = fny * inv_fn;
126
+ nz = fnz * inv_fn;
127
+ }
128
+
129
+ out[0] = nx;
130
+ out[1] = ny;
131
+ out[2] = nz;
132
+ out[3] = radius - dist;
133
+ // Sphere surface point: centre minus normal*radius (the point on
134
+ // the sphere nearest the triangle).
135
+ out[4] = sx - nx * radius;
136
+ out[5] = sy - ny * radius;
137
+ out[6] = sz - nz * radius;
138
+ // Triangle surface point: the closest point we already computed.
139
+ out[7] = px;
140
+ out[8] = py;
141
+ out[9] = pz;
142
+ return true;
143
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Test what bodies overlap a convex shape placed at a given pose. Each
3
+ * overlapping body's `body_id` is written to `output` starting at
4
+ * `output_offset`; the function returns the number of body ids written.
5
+ *
6
+ * Use case: speculative physics queries for kinematic / character
7
+ * controllers. An external system can ask "would my body collide with
8
+ * anything if I moved it here?" without committing a tick of
9
+ * simulation. The output is a flat list of body ids so the caller can
10
+ * decide what to do per hit (skip, push, slide, etc.).
11
+ *
12
+ * The pipeline mirrors the narrowphase pair test:
13
+ * 1. Build the query shape's world AABB.
14
+ * 2. Pull candidates from both broadphase trees that overlap that AABB.
15
+ * 3. For each candidate, run GJK in world frame. Convex candidates
16
+ * go through one GJK call; concave candidates (heightmap / mesh)
17
+ * go through the per-triangle decomposition path.
18
+ * 4. Apply the optional `filter` callback (same signature as in
19
+ * raycast / shapeCast) before the GJK test — early-out on bodies
20
+ * the caller already wants to skip (themselves, allies, etc.).
21
+ *
22
+ * The query shape must be convex (`is_convex === true`). Concave shapes
23
+ * are typically static terrain and not used as kinematic query
24
+ * probes; rejecting them avoids the M×N triangle-pair cost.
25
+ *
26
+ * @param {PhysicsSystem} system
27
+ * @param {AbstractShape3D} shape query shape, convex; expressed in
28
+ * its own local frame
29
+ * @param {{x:number,y:number,z:number}} position world position of the
30
+ * query shape
31
+ * @param {{x:number,y:number,z:number,w:number}} rotation world rotation
32
+ * of the query shape (unit quaternion)
33
+ * @param {Uint32Array|number[]} output buffer to write body_ids into.
34
+ * Caller is responsible for sizing it; ids past its end are dropped.
35
+ * @param {number} output_offset float-index in output to start writing at
36
+ * @param {(entity:number, collider:Collider)=>boolean} [filter]
37
+ * defaults to {@link returnTrue} (accept every candidate)
38
+ * @returns {number} number of overlapping bodies written
39
+ * @throws {Error} if `shape.is_convex === false`
40
+ */
41
+ export function overlap_shape(system: PhysicsSystem, shape: AbstractShape3D, position: {
42
+ x: number;
43
+ y: number;
44
+ z: number;
45
+ }, rotation: {
46
+ x: number;
47
+ y: number;
48
+ z: number;
49
+ w: number;
50
+ }, output: Uint32Array | number[], output_offset: number, filter?: (entity: number, collider: Collider) => boolean): number;
51
+ //# sourceMappingURL=overlap_shape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlap_shape.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/queries/overlap_shape.js"],"names":[],"mappings":"AA4CA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,uFAZW;IAAC,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAA;CAAC,YAE5B;IAAC,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAA;CAAC,UAErC,WAAW,GAAC,MAAM,EAAE,iBAEpB,MAAM,oBACE,MAAM,yBAAsB,OAAO,GAEzC,MAAM,CAqGlB"}
@@ -0,0 +1,183 @@
1
+ import { bvh_query_user_data_overlaps_aabb } from "../../../core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.js";
2
+ import { returnTrue } from "../../../core/function/returnTrue.js";
3
+ import { aabb3_transform_oriented } from "../../../core/geom/3d/aabb/aabb3_transform_oriented.js";
4
+ import { Triangle3D } from "../../../core/geom/3d/shape/Triangle3D.js";
5
+ import { body_id_index } from "../body/BodyStorage.js";
6
+ import { gjk } from "../gjk/gjk.js";
7
+ import { aabb_world_to_local } from "../narrowphase/decomposition/aabb_world_to_local.js";
8
+ import { decompose_to_triangles } from "../narrowphase/decomposition/decompose_to_triangles.js";
9
+ import { TRIANGLE_FLOAT_STRIDE } from "../narrowphase/decomposition/triangle_buffer_layout.js";
10
+ import { PosedShape } from "../narrowphase/PosedShape.js";
11
+
12
+ /**
13
+ * Scratch state — module-scoped to avoid per-query allocation. Safe
14
+ * because PhysicsSystem queries run on the main thread, sequentially.
15
+ */
16
+ const local_aabb = new Float64Array(6);
17
+ const world_aabb = new Float64Array(6);
18
+ const concave_query_aabb = new Float64Array(6);
19
+ const simplex_buf = new Float64Array(12);
20
+
21
+ const query_posed = new PosedShape();
22
+ const candidate_posed = new PosedShape();
23
+ const triangle_shape = new Triangle3D();
24
+
25
+ /**
26
+ * Maximum triangles a concave candidate can emit per overlap pair.
27
+ * Same rationale as the narrowphase's `MAX_TRIANGLES_PER_PAIR`: the
28
+ * broadphase has already bounded the query AABB to the query shape's
29
+ * envelope, so a single candidate typically yields tens of triangles.
30
+ * Excess triangles are dropped by the enumerator's bounds check —
31
+ * worst case is a missed overlap on a far edge of the candidate's
32
+ * geometry, recovered next query.
33
+ * @type {number}
34
+ */
35
+ const MAX_TRIANGLES_PER_PAIR = 1024;
36
+
37
+ const triangle_buffer = new Float64Array(MAX_TRIANGLES_PER_PAIR * TRIANGLE_FLOAT_STRIDE);
38
+
39
+ /**
40
+ * Broadphase candidate buffer. Grows by doubling on overflow.
41
+ * @type {Uint32Array}
42
+ */
43
+ let scratch_candidates = new Uint32Array(64);
44
+
45
+ /**
46
+ * Test what bodies overlap a convex shape placed at a given pose. Each
47
+ * overlapping body's `body_id` is written to `output` starting at
48
+ * `output_offset`; the function returns the number of body ids written.
49
+ *
50
+ * Use case: speculative physics queries for kinematic / character
51
+ * controllers. An external system can ask "would my body collide with
52
+ * anything if I moved it here?" without committing a tick of
53
+ * simulation. The output is a flat list of body ids so the caller can
54
+ * decide what to do per hit (skip, push, slide, etc.).
55
+ *
56
+ * The pipeline mirrors the narrowphase pair test:
57
+ * 1. Build the query shape's world AABB.
58
+ * 2. Pull candidates from both broadphase trees that overlap that AABB.
59
+ * 3. For each candidate, run GJK in world frame. Convex candidates
60
+ * go through one GJK call; concave candidates (heightmap / mesh)
61
+ * go through the per-triangle decomposition path.
62
+ * 4. Apply the optional `filter` callback (same signature as in
63
+ * raycast / shapeCast) before the GJK test — early-out on bodies
64
+ * the caller already wants to skip (themselves, allies, etc.).
65
+ *
66
+ * The query shape must be convex (`is_convex === true`). Concave shapes
67
+ * are typically static terrain and not used as kinematic query
68
+ * probes; rejecting them avoids the M×N triangle-pair cost.
69
+ *
70
+ * @param {PhysicsSystem} system
71
+ * @param {AbstractShape3D} shape query shape, convex; expressed in
72
+ * its own local frame
73
+ * @param {{x:number,y:number,z:number}} position world position of the
74
+ * query shape
75
+ * @param {{x:number,y:number,z:number,w:number}} rotation world rotation
76
+ * of the query shape (unit quaternion)
77
+ * @param {Uint32Array|number[]} output buffer to write body_ids into.
78
+ * Caller is responsible for sizing it; ids past its end are dropped.
79
+ * @param {number} output_offset float-index in output to start writing at
80
+ * @param {(entity:number, collider:Collider)=>boolean} [filter]
81
+ * defaults to {@link returnTrue} (accept every candidate)
82
+ * @returns {number} number of overlapping bodies written
83
+ * @throws {Error} if `shape.is_convex === false`
84
+ */
85
+ export function overlap_shape(system, shape, position, rotation, output, output_offset, filter = returnTrue) {
86
+ if (shape.is_convex === false) {
87
+ throw new Error(`overlap_shape: query shape must be convex; received \`${shape.constructor.name}\` (is_convex=false)`);
88
+ }
89
+
90
+ // ── 1. Query shape's world AABB ─────────────────────────────────
91
+ shape.compute_bounding_box(local_aabb);
92
+ aabb3_transform_oriented(
93
+ world_aabb, 0,
94
+ local_aabb[0], local_aabb[1], local_aabb[2],
95
+ local_aabb[3], local_aabb[4], local_aabb[5],
96
+ position.x, position.y, position.z,
97
+ rotation.x, rotation.y, rotation.z, rotation.w
98
+ );
99
+
100
+ // ── 2. Gather broadphase candidates ─────────────────────────────
101
+ const n_static = bvh_query_user_data_overlaps_aabb(
102
+ scratch_candidates, 0, system.staticBvh, world_aabb
103
+ );
104
+ const n_dynamic = bvh_query_user_data_overlaps_aabb(
105
+ scratch_candidates, n_static, system.dynamicBvh, world_aabb
106
+ );
107
+ const n_total = n_static + n_dynamic;
108
+ if (n_total === 0) return 0;
109
+
110
+ // ── 3. Set up query PosedShape (constant across candidates) ─────
111
+ query_posed.setup(shape, position, rotation);
112
+
113
+ // ── 4. Per-candidate narrowphase ────────────────────────────────
114
+ const output_capacity = output.length - output_offset;
115
+ let count = 0;
116
+ let cursor = output_offset;
117
+
118
+ for (let i = 0; i < n_total; i++) {
119
+ if (count >= output_capacity) break;
120
+
121
+ const body_id = scratch_candidates[i];
122
+ const body_idx = body_id_index(body_id);
123
+
124
+ const entity = system.entityOf(body_id);
125
+ if (entity < 0) continue;
126
+
127
+ const collider = system.__primary_collider(body_idx);
128
+ if (collider === null) continue;
129
+ if (!filter(entity, collider)) continue;
130
+
131
+ const candidate_tr = system.__transforms[body_idx];
132
+
133
+ let overlaps = false;
134
+
135
+ if (collider.shape.is_convex !== false) {
136
+ candidate_posed.setup(collider.shape, candidate_tr.position, candidate_tr.rotation);
137
+ overlaps = gjk(simplex_buf, query_posed, candidate_posed);
138
+ } else {
139
+ // Concave candidate: project the query's world AABB into
140
+ // the candidate's body-local frame, decompose to triangles,
141
+ // run per-triangle GJK until one overlap is found.
142
+ aabb_world_to_local(
143
+ concave_query_aabb, 0,
144
+ world_aabb,
145
+ candidate_tr.position.x, candidate_tr.position.y, candidate_tr.position.z,
146
+ candidate_tr.rotation.x, candidate_tr.rotation.y, candidate_tr.rotation.z, candidate_tr.rotation.w
147
+ );
148
+
149
+ const tri_count = decompose_to_triangles(
150
+ triangle_buffer, 0, collider.shape,
151
+ concave_query_aabb[0], concave_query_aabb[1], concave_query_aabb[2],
152
+ concave_query_aabb[3], concave_query_aabb[4], concave_query_aabb[5]
153
+ );
154
+
155
+ // Re-pose candidate as the concave body, rebinding the
156
+ // flyweight triangle per iteration.
157
+ candidate_posed.shape = triangle_shape;
158
+ candidate_posed.px = candidate_tr.position.x;
159
+ candidate_posed.py = candidate_tr.position.y;
160
+ candidate_posed.pz = candidate_tr.position.z;
161
+ candidate_posed.qx = candidate_tr.rotation.x;
162
+ candidate_posed.qy = candidate_tr.rotation.y;
163
+ candidate_posed.qz = candidate_tr.rotation.z;
164
+ candidate_posed.qw = candidate_tr.rotation.w;
165
+
166
+ for (let t = 0; t < tri_count; t++) {
167
+ triangle_shape.bind(triangle_buffer, t * TRIANGLE_FLOAT_STRIDE);
168
+ if (gjk(simplex_buf, query_posed, candidate_posed)) {
169
+ overlaps = true;
170
+ break;
171
+ }
172
+ }
173
+ }
174
+
175
+ if (overlaps) {
176
+ output[cursor] = body_id;
177
+ cursor++;
178
+ count++;
179
+ }
180
+ }
181
+
182
+ return count;
183
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Sweep a convex shape along a ray and find the first body it would
3
+ * hit. Used by character controllers (sweep a capsule along intended
4
+ * movement to find blockers), high-speed projectile movement (avoid
5
+ * tunnelling without paying for full per-body CCD), and any kinematic
6
+ * "where would this body end up if I tried to move it?" query.
7
+ *
8
+ * Pipeline:
9
+ * 1. Build the swept AABB: the shape's local-frame AABB rotated
10
+ * into world by `rotation`, translated to `ray.origin`, then
11
+ * stretched along `ray.direction * ray.tMax`.
12
+ * 2. Query both broadphase BVHs (static + dynamic) for leaves whose
13
+ * AABB overlaps the swept volume.
14
+ * 3. For each candidate, bisect the [0, best_t] interval for the
15
+ * smallest `t` at which the swept shape overlaps the candidate's
16
+ * collider. GJK on each midpoint.
17
+ *
18
+ * Picking `best_t` (rather than always running the full [0, tMax]
19
+ * bisection) is the early-termination optimisation: once we have a
20
+ * hit at some t, no later candidate can produce a smaller TOI by
21
+ * being checked at its full [0, tMax] interval.
22
+ *
23
+ * Output on hit:
24
+ * - `result.t` — sweep distance to impact (in the same units as
25
+ * `ray.direction`'s magnitude; for a unit-direction ray, this is
26
+ * metres).
27
+ * - `result.position` — `ray.origin + t * ray.direction`, i.e. the
28
+ * centre of the swept shape at the moment of first contact. NOT
29
+ * a point on the target's surface; that would need narrowphase
30
+ * refinement which the broadphase-only architecture doesn't have
31
+ * wired up yet.
32
+ * - `result.normal` — target surface's outward normal at the kiss
33
+ * point (unit length), computed by running GJK + EPA against the
34
+ * just-overlapping configuration at the TOI. Falls back to
35
+ * `-ray.direction` on the rare case EPA degenerates (NaN / zero
36
+ * depth) — see comment near the EPA call.
37
+ * - `result.entity` / `result.body_id` — the hit body.
38
+ *
39
+ * Output on miss: untouched; same convention as `raycast`.
40
+ *
41
+ * @param {PhysicsSystem} system
42
+ * @param {Ray3} ray origin + unit direction + `tMax`
43
+ * @param {AbstractShape3D} shape shape being swept, in its local frame
44
+ * @param {{x:number,y:number,z:number,w:number}} rotation fixed orientation
45
+ * @param {PhysicsSurfacePoint} result populated on hit; untouched on miss
46
+ * @param {(entity:number, collider:Collider)=>boolean} [filter] mandatory in
47
+ * contract; defaults to {@link returnTrue}
48
+ * @returns {boolean}
49
+ */
50
+ export function shape_cast(system: PhysicsSystem, ray: Ray3, shape: AbstractShape3D, rotation: {
51
+ x: number;
52
+ y: number;
53
+ z: number;
54
+ w: number;
55
+ }, result: PhysicsSurfacePoint, filter?: (entity: number, collider: Collider) => boolean): boolean;
56
+ //# sourceMappingURL=shape_cast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape_cast.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/queries/shape_cast.js"],"names":[],"mappings":"AAwEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,+FANW;IAAC,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAC;IAAA,CAAC,EAAC,MAAM,CAAA;CAAC,iDAE7B,MAAM,yBAAsB,OAAO,GAEzC,OAAO,CA2QnB"}