@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.
- package/package.json +1 -1
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.d.ts +3 -3
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_aabb.js +4 -4
- package/src/{engine/physics/broadphase/aabb_transform_oriented.d.ts → core/geom/3d/aabb/aabb3_transform_oriented.d.ts} +2 -2
- package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts.map +1 -0
- package/src/{engine/physics/broadphase/aabb_transform_oriented.js → core/geom/3d/aabb/aabb3_transform_oriented.js} +1 -1
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts +21 -0
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts.map +1 -0
- package/src/core/geom/3d/quaternion/quat3_multiply.js +25 -0
- package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts +54 -0
- package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts.map +1 -0
- package/src/core/geom/3d/quaternion/quat3_to_matrix3.js +69 -0
- package/src/core/geom/3d/shape/AbstractShape3D.d.ts +24 -2
- package/src/core/geom/3d/shape/AbstractShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/AbstractShape3D.js +24 -1
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +148 -0
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/HeightMapShape3D.js +451 -0
- package/src/core/geom/3d/shape/MeshShape3D.d.ts +210 -0
- package/src/core/geom/3d/shape/MeshShape3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/MeshShape3D.js +593 -0
- package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/TransformedShape3D.js +46 -2
- package/src/core/geom/3d/shape/Triangle3D.d.ts +95 -0
- package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/Triangle3D.js +318 -0
- package/src/core/geom/3d/shape/UnionShape3D.js +13 -0
- package/src/core/geom/3d/shape/shape_mesh_from_geometry.d.ts +30 -0
- package/src/core/geom/3d/shape/shape_mesh_from_geometry.d.ts.map +1 -0
- package/src/core/geom/3d/shape/shape_mesh_from_geometry.js +64 -0
- package/src/core/geom/3d/tetrahedra/prototype_tetrahedrize_mesh.js +9 -11
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.d.ts +28 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_build_vertex_to_tets_map.js +48 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.js +40 -18
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts +9 -5
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.js +38 -10
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts +14 -5
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts.map +1 -1
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.js +47 -5
- package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts +19 -0
- package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.js +75 -13
- package/src/core/geom/3d/triangle/v3_compute_triangle_normal.d.ts +2 -2
- package/src/core/geom/3d/triangle/v3_compute_triangle_normal.d.ts.map +1 -1
- package/src/core/geom/3d/triangle/v3_compute_triangle_normal.js +1 -1
- package/src/core/geom/vec3/v3_dot_array_array.d.ts +3 -3
- package/src/core/geom/vec3/v3_dot_array_array.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_dot_array_array.js +2 -2
- package/src/core/geom/vec3/v3_negate_array.d.ts +3 -3
- package/src/core/geom/vec3/v3_negate_array.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_negate_array.js +2 -2
- package/src/core/geom/vec3/v3_quat3_apply.d.ts +29 -0
- package/src/core/geom/vec3/v3_quat3_apply.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_quat3_apply.js +39 -0
- package/src/core/geom/vec3/v3_quat3_apply_inverse.d.ts +30 -0
- package/src/core/geom/vec3/v3_quat3_apply_inverse.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_quat3_apply_inverse.js +41 -0
- package/src/core/geom/vec3/v3_triple_cross_product.d.ts +32 -0
- package/src/core/geom/vec3/v3_triple_cross_product.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_triple_cross_product.js +45 -0
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +16 -3
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerController.js +211 -211
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +72 -8
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +37 -5
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +101 -3
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +1789 -1416
- package/src/engine/control/first-person/TODO.md +173 -127
- package/src/engine/control/first-person/abilities/Slide.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/Slide.js +9 -1
- package/src/engine/control/first-person/prototype_first_person_controller.js +88 -2
- package/src/engine/control/first-person/test/buildTestPlayer.d.ts.map +1 -1
- package/src/engine/control/first-person/test/buildTestPlayer.js +9 -1
- package/src/engine/graphics/geometry/CapsuleGeometry.d.ts +42 -0
- package/src/engine/graphics/geometry/CapsuleGeometry.d.ts.map +1 -0
- package/src/engine/graphics/geometry/CapsuleGeometry.js +171 -0
- package/src/engine/physics/BULLET_REVIEW.md +945 -0
- package/src/engine/physics/CANNON_REVIEW.md +1300 -0
- package/src/engine/physics/JOLT_REVIEW.md +913 -0
- package/src/engine/physics/PLAN.md +578 -236
- package/src/engine/physics/RAPIER_REVIEW.md +934 -0
- package/src/engine/physics/REVIEW_001_ACTION_PLAN.md +642 -0
- package/src/engine/physics/REVIEW_002.md +151 -0
- package/src/engine/physics/broadphase/compute_fat_world_aabb.js +2 -2
- package/src/engine/physics/constraint/DofMode.d.ts +28 -0
- package/src/engine/physics/constraint/DofMode.d.ts.map +1 -0
- package/src/engine/physics/constraint/DofMode.js +35 -0
- package/src/engine/physics/constraint/solve_constraints.d.ts +16 -0
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -0
- package/src/engine/physics/constraint/solve_constraints.js +436 -0
- package/src/engine/physics/contact/ManifoldStore.d.ts +83 -10
- package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
- package/src/engine/physics/contact/ManifoldStore.js +608 -499
- package/src/engine/physics/ecs/ColliderObserverSystem.d.ts +2 -2
- package/src/engine/physics/ecs/ColliderObserverSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/Joint.d.ts +179 -0
- package/src/engine/physics/ecs/Joint.d.ts.map +1 -0
- package/src/engine/physics/ecs/Joint.js +234 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +180 -20
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +1423 -1159
- package/src/engine/physics/fluid/FluidField.d.ts +14 -10
- package/src/engine/physics/fluid/FluidField.d.ts.map +1 -1
- package/src/engine/physics/fluid/FluidField.js +14 -10
- package/src/engine/physics/fluid/FluidSimulator.js +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts +17 -10
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +18 -11
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts +13 -10
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +18 -13
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts +4 -3
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +15 -11
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts +30 -6
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +44 -18
- package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts +6 -6
- package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts.map +1 -1
- package/src/engine/physics/gjk/expanding_polytope_algorithm.js +68 -22
- package/src/engine/physics/gjk/gjk.d.ts +28 -2
- package/src/engine/physics/gjk/gjk.d.ts.map +1 -1
- package/src/engine/physics/gjk/gjk.js +421 -378
- package/src/engine/physics/gjk/minkowski_support.d.ts +37 -0
- package/src/engine/physics/gjk/minkowski_support.d.ts.map +1 -0
- package/src/engine/physics/gjk/minkowski_support.js +75 -0
- package/src/engine/physics/gjk/mpr.d.ts +56 -0
- package/src/engine/physics/gjk/mpr.d.ts.map +1 -0
- package/src/engine/physics/gjk/mpr.js +344 -0
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts +20 -5
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
- package/src/engine/physics/inertia/world_inverse_inertia.js +36 -38
- package/src/engine/physics/integration/integrate_position.d.ts +25 -7
- package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_position.js +43 -12
- package/src/engine/physics/integration/integrate_velocity.d.ts +30 -0
- package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_velocity.js +82 -1
- package/src/engine/physics/island/IslandBuilder.d.ts +4 -1
- package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -1
- package/src/engine/physics/island/IslandBuilder.js +33 -16
- package/src/engine/physics/narrowphase/PosedShape.d.ts +0 -8
- package/src/engine/physics/narrowphase/PosedShape.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/PosedShape.js +28 -30
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_box_manifold.js +140 -18
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts +30 -0
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/box_triangle_contact.js +811 -0
- package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/capsule_contacts.js +10 -56
- package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts +71 -0
- package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/capsule_triangle_contact.js +375 -0
- package/src/engine/physics/narrowphase/compute_penetration.d.ts +91 -0
- package/src/engine/physics/narrowphase/compute_penetration.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/compute_penetration.js +396 -0
- package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.d.ts +35 -0
- package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/aabb_world_to_local.js +80 -0
- package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts +31 -0
- package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +55 -0
- package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts +42 -0
- package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.js +204 -0
- package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts +42 -0
- package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.js +94 -0
- package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts +37 -0
- package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +37 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts +41 -2
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/narrowphase_step.js +1497 -382
- package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/sphere_box_contact.js +16 -23
- package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts +48 -0
- package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/sphere_triangle_contact.js +143 -0
- package/src/engine/physics/queries/overlap_shape.d.ts +51 -0
- package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -0
- package/src/engine/physics/queries/overlap_shape.js +183 -0
- package/src/engine/physics/queries/shape_cast.d.ts +56 -0
- package/src/engine/physics/queries/shape_cast.d.ts.map +1 -0
- package/src/engine/physics/queries/shape_cast.js +387 -0
- package/src/engine/physics/solver/solve_contacts.d.ts +146 -32
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
- package/src/engine/physics/solver/solve_contacts.js +809 -223
- package/src/engine/physics/broadphase/aabb_transform_oriented.d.ts.map +0 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.d.ts +0 -20
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.d.ts.map +0 -1
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.js +0 -83
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# REVIEW_002 — TGS solver, contact robustness, and constraints
|
|
2
|
+
|
|
3
|
+
Retrospective on the "competent → great" push: promoting the solver from
|
|
4
|
+
single-step PGS to substepped TGS, fixing the contact-robustness issues that
|
|
5
|
+
surfaced, and standing up the 6-DOF constraint subsystem. Companion to the
|
|
6
|
+
engine-comparison docs (`CANNON_REVIEW`, `RAPIER_REVIEW`, `JOLT_REVIEW`,
|
|
7
|
+
`BULLET_REVIEW`); this one is a build retrospective, not a comparison.
|
|
8
|
+
|
|
9
|
+
Scope: the work tracked in `PLAN.md` under "Solver quality" + "Constraints /
|
|
10
|
+
joints". What landed, the hard-won lessons, where the implementation
|
|
11
|
+
deviated from the original plan and why, and what's deferred.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## What landed
|
|
16
|
+
|
|
17
|
+
**TGS (Temporal Gauss-Seidel), Phases 1–3.** The solver is now a staged
|
|
18
|
+
pipeline driven by a substep loop:
|
|
19
|
+
`prepare → per substep [redetect-concave / refresh-convex → warm-start →
|
|
20
|
+
solve-velocity → solve-position] → restitution`. Defaults: 4 substeps, 4
|
|
21
|
+
velocity + 1 position iteration per substep. Split-impulse position
|
|
22
|
+
correction (pseudo-velocity folded into the pose, never into real velocity);
|
|
23
|
+
one-shot restitution; SPOOK compliance as the soft-constraint dial.
|
|
24
|
+
|
|
25
|
+
**Contact robustness.**
|
|
26
|
+
- Box-box SAT reference tie-break deadband → aligned cube stacks 4–10 high
|
|
27
|
+
settle to zero velocity and sleep.
|
|
28
|
+
- Per-substep concave re-detection → a dynamic concave mesh body (torus knot)
|
|
29
|
+
settles instead of rocking.
|
|
30
|
+
|
|
31
|
+
**Constraints — 6-DOF configurable joint** (PhysX D6 / Jolt SixDOF model):
|
|
32
|
+
one constraint type, each of 3 linear + 3 angular DOFs independently
|
|
33
|
+
locked/free/limited/spring/motor. Landed modes: LOCKED linear (ball-socket)
|
|
34
|
+
and LOCKED angular (hinge, weld). Solved as a parallel row set inside the
|
|
35
|
+
same substep loop; island-integrated; generation-checked against stale body
|
|
36
|
+
references.
|
|
37
|
+
|
|
38
|
+
Coverage went ~698 → 720+ physics tests, all green. Falling-tower bench
|
|
39
|
+
unchanged (~48 ms / 1000 active bodies).
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Hard-won lessons
|
|
44
|
+
|
|
45
|
+
These cost real debugging and are the reason the code looks the way it does.
|
|
46
|
+
|
|
47
|
+
1. **Per-substep warm-start is mandatory under substepping.** The first
|
|
48
|
+
substep attempt warm-started once per outer step and applied gravity per
|
|
49
|
+
substep. That mismatch — replaying a *full-frame* impulse against a
|
|
50
|
+
*single substep* of gravity — over-pushes resting contacts and **explodes**
|
|
51
|
+
deep stacks (a 10-cube stack hit 9 m of drift / 13 m/s). The fix: gravity
|
|
52
|
+
per substep **and** warm-start per substep, so each substep's gravity and
|
|
53
|
+
each substep's replayed impulse cancel exactly at rest. A resting body
|
|
54
|
+
holds at `v = 0`, `j_n ≈ m·g·h`.
|
|
55
|
+
|
|
56
|
+
2. **Restitution must gate on the running-max normal impulse, not the
|
|
57
|
+
end-of-loop value.** With per-substep warm-start, a transient collision's
|
|
58
|
+
accumulated `j_n` relaxes back to ~0 by the end of the loop (no sustained
|
|
59
|
+
load to hold it), so gating restitution on the final `j_n` silently killed
|
|
60
|
+
every bounce. Tracking `maxNormalImpulse` over the whole step (Box2D-v3's
|
|
61
|
+
trick) is what makes restitution fire.
|
|
62
|
+
|
|
63
|
+
3. **Analytic separation re-derivation is great for convex, wrong for
|
|
64
|
+
dynamic concave.** Re-deriving each substep's penetration from frozen
|
|
65
|
+
witness anchors + current pose (no narrowphase re-run) is cheap and exact
|
|
66
|
+
*while the contact feature is stable* — true for a convex primitive under
|
|
67
|
+
the small per-step motion. For a concave body rocking on a mesh, the
|
|
68
|
+
supporting triangle (and its normal) genuinely changes mid-step, so
|
|
69
|
+
freezing it pumps energy in. Resolution: convex pairs use the cheap
|
|
70
|
+
analytic refresh; concave-involved pairs re-run narrowphase geometry each
|
|
71
|
+
substep (`redetect_concave_contacts`). Hybrid, gated by collider convexity.
|
|
72
|
+
|
|
73
|
+
4. **The aligned-stack instability was not the solver.** Cube stacks crept
|
|
74
|
+
and toppled; the instinct was "solver convergence". Dumping
|
|
75
|
+
`box_box_manifold` across a settling pair showed the normal and contact
|
|
76
|
+
*count* were stable, but the *reference face* flip-flopped between A and B
|
|
77
|
+
each frame (their SAT overlaps are exactly equal for aligned boxes, so
|
|
78
|
+
float noise from a sub-degree wobble decided the winner). That reordered
|
|
79
|
+
the contacts → flipped the Gauss-Seidel sweep order → alternating bias →
|
|
80
|
+
creep. A 6-line relative+absolute tie-break deadband (bias ties toward the
|
|
81
|
+
earlier-tested axis) fixed stacks 4–10. **Lesson: diagnose the manifold
|
|
82
|
+
before blaming the solver.**
|
|
83
|
+
|
|
84
|
+
5. **Granularity is everything for dynamic concave.** The first instinct
|
|
85
|
+
("decompose the mesh into the exact Delaunay tets we already have") is
|
|
86
|
+
wrong: a Suzanne is ~8000 tets → ~8000 colliders → ~8000 dynamic-BVH
|
|
87
|
+
leaves refit every frame. Tet meshing is a *volumetric* (FEM/soft-body)
|
|
88
|
+
tool. Rigid collision wants the *fewest* convex pieces — a single convex
|
|
89
|
+
hull for most dynamic objects, or a few-hull (V-HACD-style) decomposition.
|
|
90
|
+
Recorded as the long-term path; the per-substep re-detection is the
|
|
91
|
+
interim.
|
|
92
|
+
|
|
93
|
+
6. **One configurable 6-DOF constraint beats N joint types.** The row math is
|
|
94
|
+
contact-shaped (Jacobian + effective mass + bias + bounds), so it slots
|
|
95
|
+
into the existing solver. Concrete joints become *config*: ball-socket =
|
|
96
|
+
lock 3 linear; hinge = lock 3 linear + 2 angular; weld = lock 6. This
|
|
97
|
+
minimises distinct code paths — the whole point.
|
|
98
|
+
|
|
99
|
+
7. **Sign conventions: linear A−B, angular B−A.** The linear rows use
|
|
100
|
+
"A minus B" (impulse +to A); the angular rows use "B minus A" (impulse
|
|
101
|
+
+to B). Each is internally consistent — the body-order difference just
|
|
102
|
+
folds the sign in. Derived carefully against the SPOOK Baumgarte target
|
|
103
|
+
(`dC/dt = vrel`); both worked first try once written down.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Deviations from the original plan (and why)
|
|
108
|
+
|
|
109
|
+
- **Joints run as a parallel row set, not by porting contacts onto a shared
|
|
110
|
+
constraint base.** The plan floated either. Porting the working,
|
|
111
|
+
well-tuned contact path carried regression risk for no immediate benefit;
|
|
112
|
+
running joints as a second row family in the same substep loop gets the
|
|
113
|
+
coupling (shared warm-start / islands / substep cadence) at far lower risk.
|
|
114
|
+
Unifying remains optional.
|
|
115
|
+
|
|
116
|
+
- **Convex contacts use analytic refresh, not per-substep match-and-merge.**
|
|
117
|
+
The plan said "substeps refresh contacts via match-and-merge". Analytic
|
|
118
|
+
re-derivation is cheaper and avoids manifold-lifecycle churn for the convex
|
|
119
|
+
majority; match-and-merge-style re-detection is reserved for the concave
|
|
120
|
+
minority (lesson 3).
|
|
121
|
+
|
|
122
|
+
- **Dynamic concave: per-substep re-detection now, convex proxies later** —
|
|
123
|
+
not the tet-compound idea first sketched (lesson 5).
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Deferred (with rationale)
|
|
128
|
+
|
|
129
|
+
- **Constraints**: prismatic (needs frame-basis linear rows — today's linear
|
|
130
|
+
locks use world axes, exact only when all 3 are locked), limits, motors,
|
|
131
|
+
springs (SPOOK soft), swing-twist (ragdoll cone), the raycast-vehicle
|
|
132
|
+
layer, and extras (pulley/gear/conveyor/breakable). Sequenced in `PLAN.md`.
|
|
133
|
+
- **Trajectory accuracy**: gravity is substepped, so ballistic integration is
|
|
134
|
+
at the substep rate — good. No higher-order integrator pursued.
|
|
135
|
+
- **Per-island parallel solve / CCD shape-cast / closed-form
|
|
136
|
+
triangle-vs-primitive remainder**: unchanged backlog items.
|
|
137
|
+
- **Convex hull builder + few-hull decomposition**: the production answer for
|
|
138
|
+
dynamic concave (lesson 5), not yet built.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Bottom line
|
|
143
|
+
|
|
144
|
+
The engine moved from "competent PGS" to a substepped TGS solver with stable
|
|
145
|
+
stacks, accurate restitution, robust mass ratios, settling dynamic concave
|
|
146
|
+
bodies, and a working 6-DOF constraint (ball-socket / hinge / weld) — i.e.
|
|
147
|
+
chains, ropes, and the structural joints for ragdolls and mechanisms. The
|
|
148
|
+
costliest mistakes were all about *substepping invariants* (warm-start and
|
|
149
|
+
restitution-gating cadence) and *not trusting assumptions* (the stack bug was
|
|
150
|
+
narrowphase, not the solver). The remaining constraint work is incremental
|
|
151
|
+
on a validated framework.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { aabb3_transform_oriented } from "../../../core/geom/3d/aabb/aabb3_transform_oriented.js";
|
|
2
2
|
|
|
3
3
|
const scratch_local = new Float64Array(6);
|
|
4
4
|
|
|
@@ -41,7 +41,7 @@ export function compute_fat_world_aabb(
|
|
|
41
41
|
const p = transform.position;
|
|
42
42
|
const q = transform.rotation;
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
aabb3_transform_oriented(
|
|
45
45
|
result, result_offset,
|
|
46
46
|
scratch_local[0], scratch_local[1], scratch_local[2],
|
|
47
47
|
scratch_local[3], scratch_local[4], scratch_local[5],
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-degree-of-freedom mode for a 6-DOF constraint ({@link Joint }).
|
|
3
|
+
*
|
|
4
|
+
* A 6-DOF constraint has 3 linear DOFs (translation of body B's anchor frame
|
|
5
|
+
* relative to body A's, along A's frame axes) and 3 angular DOFs (relative
|
|
6
|
+
* rotation, swing-twist decomposed). Each DOF independently takes one of these
|
|
7
|
+
* modes — which is how a single configurable constraint expresses the whole
|
|
8
|
+
* joint taxonomy (PhysX D6 / Jolt SixDOF / Bullet Generic6DOF):
|
|
9
|
+
*
|
|
10
|
+
* - ball-socket = lock 3 linear
|
|
11
|
+
* - hinge = lock 3 linear + 2 angular
|
|
12
|
+
* - prismatic = lock 2 linear + 3 angular
|
|
13
|
+
* - weld = lock all 6
|
|
14
|
+
* - cone-twist = lock 3 linear + limit 3 angular
|
|
15
|
+
* - suspension = spring 1 linear + lock the rest
|
|
16
|
+
*
|
|
17
|
+
* Implementation lands mode-by-mode: LOCKED first (covers ball-socket / hinge
|
|
18
|
+
* / prismatic / weld), then LIMITED, SPRING, MOTOR.
|
|
19
|
+
*/
|
|
20
|
+
export type DofMode = number;
|
|
21
|
+
export namespace DofMode {
|
|
22
|
+
let LOCKED: number;
|
|
23
|
+
let FREE: number;
|
|
24
|
+
let LIMITED: number;
|
|
25
|
+
let SPRING: number;
|
|
26
|
+
let MOTOR: number;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=DofMode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DofMode.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/constraint/DofMode.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;sBAqBU,MAAM"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-degree-of-freedom mode for a 6-DOF constraint ({@link Joint}).
|
|
3
|
+
*
|
|
4
|
+
* A 6-DOF constraint has 3 linear DOFs (translation of body B's anchor frame
|
|
5
|
+
* relative to body A's, along A's frame axes) and 3 angular DOFs (relative
|
|
6
|
+
* rotation, swing-twist decomposed). Each DOF independently takes one of these
|
|
7
|
+
* modes — which is how a single configurable constraint expresses the whole
|
|
8
|
+
* joint taxonomy (PhysX D6 / Jolt SixDOF / Bullet Generic6DOF):
|
|
9
|
+
*
|
|
10
|
+
* - ball-socket = lock 3 linear
|
|
11
|
+
* - hinge = lock 3 linear + 2 angular
|
|
12
|
+
* - prismatic = lock 2 linear + 3 angular
|
|
13
|
+
* - weld = lock all 6
|
|
14
|
+
* - cone-twist = lock 3 linear + limit 3 angular
|
|
15
|
+
* - suspension = spring 1 linear + lock the rest
|
|
16
|
+
*
|
|
17
|
+
* Implementation lands mode-by-mode: LOCKED first (covers ball-socket / hinge
|
|
18
|
+
* / prismatic / weld), then LIMITED, SPRING, MOTOR.
|
|
19
|
+
*
|
|
20
|
+
* @author Alex Goldring
|
|
21
|
+
* @copyright Company Named Limited (c) 2026
|
|
22
|
+
* @enum {number}
|
|
23
|
+
*/
|
|
24
|
+
export const DofMode = {
|
|
25
|
+
/** Constrained to zero (relative position/angle held at the rest value). */
|
|
26
|
+
LOCKED: 0,
|
|
27
|
+
/** Unconstrained — the DOF moves freely. */
|
|
28
|
+
FREE: 1,
|
|
29
|
+
/** Free within `[lower, upper]`, constrained (one-sided) at the limits. */
|
|
30
|
+
LIMITED: 2,
|
|
31
|
+
/** A soft spring (stiffness + damping) pulling toward a rest target. */
|
|
32
|
+
SPRING: 3,
|
|
33
|
+
/** Driven toward a target velocity, bounded by a maximum force. */
|
|
34
|
+
MOTOR: 4,
|
|
35
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solve every joint for one substep: recompute geometry at the current poses,
|
|
3
|
+
* replay the per-substep warm-start, and run `iters` velocity iterations of
|
|
4
|
+
* the locked linear DOFs.
|
|
5
|
+
*
|
|
6
|
+
* Called once per substep from `PhysicsSystem.fixedUpdate`, after the contact
|
|
7
|
+
* solve so the two share the substep / warm-start cadence.
|
|
8
|
+
*
|
|
9
|
+
* @param {Joint[]} joints live joints (sparse array; holes skipped)
|
|
10
|
+
* @param {PhysicsSystem} system reads `__bodies` / `__transforms` / index map
|
|
11
|
+
* @param {number} dt_sub substep size in seconds (the SPOOK gain is derived
|
|
12
|
+
* from it, matching the contact solver's per-substep position stiffness)
|
|
13
|
+
* @param {number} iters velocity iterations
|
|
14
|
+
*/
|
|
15
|
+
export function solve_joints(joints: Joint[], system: PhysicsSystem, dt_sub: number, iters: number): void;
|
|
16
|
+
//# sourceMappingURL=solve_constraints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solve_constraints.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/constraint/solve_constraints.js"],"names":[],"mappings":"AAyJA;;;;;;;;;;;;;GAaG;AACH,qCANW,OAAO,iCAEP,MAAM,SAEN,MAAM,QA8QhB"}
|