@woosh/meep-engine 2.138.20 → 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.
- 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/core/collection/PairUint32Map.d.ts +100 -0
- package/src/core/collection/PairUint32Map.d.ts.map +1 -0
- package/src/core/collection/PairUint32Map.js +321 -0
- package/src/core/collection/Uint32Map.d.ts +119 -0
- package/src/core/collection/Uint32Map.d.ts.map +1 -0
- package/src/core/collection/Uint32Map.js +345 -0
- package/src/core/collection/array/array_shuffle.d.ts +10 -3
- package/src/core/collection/array/array_shuffle.d.ts.map +1 -1
- package/src/core/collection/array/array_shuffle.js +27 -22
- package/src/core/collection/heap/FibonacciHeap.d.ts +195 -0
- package/src/core/collection/heap/FibonacciHeap.d.ts.map +1 -0
- package/src/core/collection/heap/FibonacciHeap.js +586 -0
- package/src/core/collection/heap/Uint32Heap.js +1 -1
- package/src/core/collection/heap/Uint32Heap4.d.ts +169 -0
- package/src/core/collection/heap/Uint32Heap4.d.ts.map +1 -0
- package/src/core/collection/heap/Uint32Heap4.js +490 -0
- package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts +30 -0
- package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts.map +1 -0
- package/src/core/geom/3d/aabb/aabb3_transform_oriented.js +93 -0
- package/src/core/geom/3d/line/line3_closest_points_segment_segment.d.ts +27 -0
- package/src/core/geom/3d/line/line3_closest_points_segment_segment.d.ts.map +1 -0
- package/src/core/geom/3d/line/line3_closest_points_segment_segment.js +88 -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/BoxShape3D.d.ts +61 -0
- package/src/core/geom/3d/shape/BoxShape3D.d.ts.map +1 -0
- package/src/core/geom/3d/shape/BoxShape3D.js +158 -0
- package/src/core/geom/3d/shape/CapsuleShape3D.d.ts +11 -0
- package/src/core/geom/3d/shape/CapsuleShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/CapsuleShape3D.js +12 -0
- 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/UnitCubeShape3D.d.ts +37 -9
- package/src/core/geom/3d/shape/UnitCubeShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/UnitCubeShape3D.js +45 -98
- package/src/core/geom/3d/shape/UnitSphereShape3D.d.ts +10 -0
- package/src/core/geom/3d/shape/UnitSphereShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/UnitSphereShape3D.js +11 -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/shape/util/shape3d_voxelize_to_grid.d.ts +61 -0
- package/src/core/geom/3d/shape/util/shape3d_voxelize_to_grid.d.ts.map +1 -0
- package/src/core/geom/3d/shape/util/shape3d_voxelize_to_grid.js +148 -0
- package/src/core/geom/3d/tetrahedra/compute_tetrahedral_mesh_from_surface.d.ts +39 -0
- package/src/core/geom/3d/tetrahedra/compute_tetrahedral_mesh_from_surface.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/compute_tetrahedral_mesh_from_surface.js +147 -0
- package/src/core/geom/3d/tetrahedra/compute_tetrahedron_quality.d.ts +15 -0
- package/src/core/geom/3d/tetrahedra/compute_tetrahedron_quality.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/compute_tetrahedron_quality.js +22 -0
- package/src/core/geom/3d/tetrahedra/prototype_tetrahedrize_mesh.d.ts +2 -0
- package/src/core/geom/3d/tetrahedra/prototype_tetrahedrize_mesh.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/prototype_tetrahedrize_mesh.js +671 -0
- 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_carve_outside_surface.d.ts +26 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.js +222 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_find_tets_around_edge.d.ts +34 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_find_tets_around_edge.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_find_tets_around_edge.js +146 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_23.d.ts +36 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_23.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_23.js +232 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_32.d.ts +33 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_32.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_flip_32.js +255 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.d.ts +68 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_improve_quality.js +387 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts +35 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.js +140 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts +31 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.js +97 -0
- package/src/core/geom/3d/tetrahedra/tetrahedron_compute_quality.d.ts +32 -0
- package/src/core/geom/3d/tetrahedra/tetrahedron_compute_quality.d.ts.map +1 -0
- package/src/core/geom/3d/tetrahedra/tetrahedron_compute_quality.js +66 -0
- package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts +41 -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 +124 -13
- package/src/core/geom/3d/topology/struct/binary/BinaryTopology.d.ts +134 -0
- package/src/core/geom/3d/topology/struct/binary/BinaryTopology.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/BinaryTopology.js +276 -3
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.d.ts +17 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_close_boundary_holes.js +135 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compact.d.ts +14 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compact.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compact.js +177 -0
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_decouple.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_face_decouple.js +20 -4
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +5 -3
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.js +9 -0
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_get_or_create.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_get_or_create.js +21 -45
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill.js +7 -1
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.d.ts +8 -6
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_kill_parallels.js +8 -6
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_kill_short_edges.d.ts +22 -0
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_kill_short_edges.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_kill_short_edges.js +73 -0
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_replace.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_replace.js +51 -1
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_get.d.ts +10 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_get.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_edge_get.js +42 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.d.ts +28 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.js +227 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_walk_boundary_loops.d.ts +13 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_walk_boundary_loops.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_walk_boundary_loops.js +108 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_query_edge_is_boundary.d.ts +11 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_query_edge_is_boundary.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_query_edge_is_boundary.js +20 -0
- package/src/core/geom/3d/triangle/triangle_mesh_compute_signed_volume.d.ts +20 -0
- package/src/core/geom/3d/triangle/triangle_mesh_compute_signed_volume.d.ts.map +1 -0
- package/src/core/geom/3d/triangle/triangle_mesh_compute_signed_volume.js +38 -0
- 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/core/graph/csr/CSRGraph.d.ts +168 -0
- package/src/core/graph/csr/CSRGraph.d.ts.map +1 -0
- package/src/core/graph/csr/CSRGraph.js +319 -0
- package/src/core/graph/metis/cluster_mesh_metis.d.ts +12 -0
- package/src/core/graph/metis/cluster_mesh_metis.d.ts.map +1 -1
- package/src/core/graph/metis/cluster_mesh_metis.js +12 -0
- package/src/core/graph/metis/metis.d.ts +19 -0
- package/src/core/graph/metis/metis.d.ts.map +1 -1
- package/src/core/graph/metis/metis.js +20 -0
- package/src/core/graph/metis/metis_cluster_bs.d.ts +11 -0
- package/src/core/graph/metis/metis_cluster_bs.d.ts.map +1 -1
- package/src/core/graph/metis/metis_cluster_bs.js +11 -0
- package/src/core/graph/metis/metis_options.d.ts +17 -2
- package/src/core/graph/metis/metis_options.d.ts.map +1 -1
- package/src/core/graph/metis/metis_options.js +17 -2
- package/src/core/graph/metis/native/MetisGraph.d.ts +144 -0
- package/src/core/graph/metis/native/MetisGraph.d.ts.map +1 -0
- package/src/core/graph/metis/native/MetisGraph.js +212 -0
- package/src/core/graph/metis/native/bisection/BisectionScratch.d.ts +72 -0
- package/src/core/graph/metis/native/bisection/BisectionScratch.d.ts.map +1 -0
- package/src/core/graph/metis/native/bisection/BisectionScratch.js +101 -0
- package/src/core/graph/metis/native/bisection/bisect_graph.d.ts +37 -0
- package/src/core/graph/metis/native/bisection/bisect_graph.d.ts.map +1 -0
- package/src/core/graph/metis/native/bisection/bisect_graph.js +100 -0
- package/src/core/graph/metis/native/bisection/compute_2way_params.d.ts +15 -0
- package/src/core/graph/metis/native/bisection/compute_2way_params.d.ts.map +1 -0
- package/src/core/graph/metis/native/bisection/compute_2way_params.js +84 -0
- package/src/core/graph/metis/native/bisection/fm_2way.d.ts +30 -0
- package/src/core/graph/metis/native/bisection/fm_2way.d.ts.map +1 -0
- package/src/core/graph/metis/native/bisection/fm_2way.js +290 -0
- package/src/core/graph/metis/native/bisection/grow_bisection.d.ts +23 -0
- package/src/core/graph/metis/native/bisection/grow_bisection.d.ts.map +1 -0
- package/src/core/graph/metis/native/bisection/grow_bisection.js +137 -0
- package/src/core/graph/metis/native/bisection/split_graph_two_way.d.ts +28 -0
- package/src/core/graph/metis/native/bisection/split_graph_two_way.d.ts.map +1 -0
- package/src/core/graph/metis/native/bisection/split_graph_two_way.js +119 -0
- package/src/core/graph/metis/native/coarsen/coarsen_graph.d.ts +20 -0
- package/src/core/graph/metis/native/coarsen/coarsen_graph.d.ts.map +1 -0
- package/src/core/graph/metis/native/coarsen/coarsen_graph.js +94 -0
- package/src/core/graph/metis/native/coarsen/create_coarse_graph.d.ts +24 -0
- package/src/core/graph/metis/native/coarsen/create_coarse_graph.d.ts.map +1 -0
- package/src/core/graph/metis/native/coarsen/create_coarse_graph.js +158 -0
- package/src/core/graph/metis/native/coarsen/match_shem.d.ts +41 -0
- package/src/core/graph/metis/native/coarsen/match_shem.d.ts.map +1 -0
- package/src/core/graph/metis/native/coarsen/match_shem.js +175 -0
- package/src/core/graph/metis/native/initial/initial_kway_bfs.d.ts +24 -0
- package/src/core/graph/metis/native/initial/initial_kway_bfs.d.ts.map +1 -0
- package/src/core/graph/metis/native/initial/initial_kway_bfs.js +122 -0
- package/src/core/graph/metis/native/initial/initial_kway_recursive_bisection.d.ts +29 -0
- package/src/core/graph/metis/native/initial/initial_kway_recursive_bisection.d.ts.map +1 -0
- package/src/core/graph/metis/native/initial/initial_kway_recursive_bisection.js +170 -0
- package/src/core/graph/metis/native/metis_partition_kway.d.ts +41 -0
- package/src/core/graph/metis/native/metis_partition_kway.d.ts.map +1 -0
- package/src/core/graph/metis/native/metis_partition_kway.js +126 -0
- package/src/core/graph/metis/native/refine/IndexedFloatMaxHeap.d.ts +62 -0
- package/src/core/graph/metis/native/refine/IndexedFloatMaxHeap.d.ts.map +1 -0
- package/src/core/graph/metis/native/refine/IndexedFloatMaxHeap.js +261 -0
- package/src/core/graph/metis/native/refine/RefinementScratch.d.ts +45 -0
- package/src/core/graph/metis/native/refine/RefinementScratch.d.ts.map +1 -0
- package/src/core/graph/metis/native/refine/RefinementScratch.js +53 -0
- package/src/core/graph/metis/native/refine/compute_kway_params.d.ts +18 -0
- package/src/core/graph/metis/native/refine/compute_kway_params.d.ts.map +1 -0
- package/src/core/graph/metis/native/refine/compute_kway_params.js +138 -0
- package/src/core/graph/metis/native/refine/fm_kway.d.ts +63 -0
- package/src/core/graph/metis/native/refine/fm_kway.d.ts.map +1 -0
- package/src/core/graph/metis/native/refine/fm_kway.js +462 -0
- package/src/core/graph/metis/native/refine/project_kway.d.ts +22 -0
- package/src/core/graph/metis/native/refine/project_kway.d.ts.map +1 -0
- package/src/core/graph/metis/native/refine/project_kway.js +43 -0
- package/src/core/graph/metis/native/refine/refine_kway.d.ts +34 -0
- package/src/core/graph/metis/native/refine/refine_kway.d.ts.map +1 -0
- package/src/core/graph/metis/native/refine/refine_kway.js +43 -0
- package/src/core/math/linalg/eigen/matrix_householder_in_place.d.ts +2 -2
- package/src/core/math/linalg/eigen/matrix_householder_in_place.js +2 -2
- package/src/core/math/linalg/eigen/matrix_qr_in_place.d.ts +6 -4
- package/src/core/math/linalg/eigen/matrix_qr_in_place.d.ts.map +1 -1
- package/src/core/math/linalg/eigen/matrix_qr_in_place.js +69 -23
- package/src/engine/EngineHarness.d.ts +3 -1
- package/src/engine/EngineHarness.d.ts.map +1 -1
- package/src/engine/EngineHarness.js +3 -0
- package/src/engine/control/first-person/DESIGN.md +30 -6
- package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +563 -0
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +115 -9
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerController.js +211 -176
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +601 -8
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +349 -8
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +319 -23
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +1789 -799
- package/src/engine/control/first-person/TODO.md +173 -0
- package/src/engine/control/first-person/abilities/Ability.d.ts +101 -0
- package/src/engine/control/first-person/abilities/Ability.d.ts.map +1 -0
- package/src/engine/control/first-person/abilities/Ability.js +119 -0
- package/src/engine/control/first-person/abilities/AbilitySet.d.ts +86 -0
- package/src/engine/control/first-person/abilities/AbilitySet.d.ts.map +1 -0
- package/src/engine/control/first-person/abilities/AbilitySet.js +185 -0
- package/src/engine/control/first-person/abilities/LedgeGrab.d.ts +62 -0
- package/src/engine/control/first-person/abilities/LedgeGrab.d.ts.map +1 -0
- package/src/engine/control/first-person/abilities/LedgeGrab.js +199 -0
- package/src/engine/control/first-person/abilities/Mantle.d.ts +45 -0
- package/src/engine/control/first-person/abilities/Mantle.d.ts.map +1 -0
- package/src/engine/control/first-person/abilities/Mantle.js +188 -0
- package/src/engine/control/first-person/abilities/Slide.d.ts +33 -0
- package/src/engine/control/first-person/abilities/Slide.d.ts.map +1 -0
- package/src/engine/control/first-person/abilities/Slide.js +166 -0
- package/src/engine/control/first-person/abilities/WallJump.d.ts +45 -0
- package/src/engine/control/first-person/abilities/WallJump.d.ts.map +1 -0
- package/src/engine/control/first-person/abilities/WallJump.js +131 -0
- package/src/engine/control/first-person/abilities/WallRun.d.ts +44 -0
- package/src/engine/control/first-person/abilities/WallRun.d.ts.map +1 -0
- package/src/engine/control/first-person/abilities/WallRun.js +180 -0
- package/src/engine/control/first-person/composer/EyeOffsetStack.d.ts +49 -0
- package/src/engine/control/first-person/composer/EyeOffsetStack.d.ts.map +1 -0
- package/src/engine/control/first-person/composer/EyeOffsetStack.js +60 -0
- package/src/engine/control/first-person/mastery/BreathRhythmEvaluator.d.ts +100 -0
- package/src/engine/control/first-person/mastery/BreathRhythmEvaluator.d.ts.map +1 -0
- package/src/engine/control/first-person/mastery/BreathRhythmEvaluator.js +133 -0
- package/src/engine/control/first-person/mastery/DecisionPoint.d.ts +10 -0
- package/src/engine/control/first-person/mastery/DecisionPoint.d.ts.map +1 -0
- package/src/engine/control/first-person/mastery/DecisionPoint.js +30 -0
- package/src/engine/control/first-person/mastery/FootAsymmetryTurnEvaluator.d.ts +61 -0
- package/src/engine/control/first-person/mastery/FootAsymmetryTurnEvaluator.d.ts.map +1 -0
- package/src/engine/control/first-person/mastery/FootAsymmetryTurnEvaluator.js +109 -0
- package/src/engine/control/first-person/mastery/MasteryEvaluator.d.ts +40 -0
- package/src/engine/control/first-person/mastery/MasteryEvaluator.d.ts.map +1 -0
- package/src/engine/control/first-person/mastery/MasteryEvaluator.js +45 -0
- package/src/engine/control/first-person/mastery/MasteryScore.d.ts +68 -0
- package/src/engine/control/first-person/mastery/MasteryScore.d.ts.map +1 -0
- package/src/engine/control/first-person/mastery/MasteryScore.js +100 -0
- package/src/engine/control/first-person/mastery/MasterySet.d.ts +60 -0
- package/src/engine/control/first-person/mastery/MasterySet.d.ts.map +1 -0
- package/src/engine/control/first-person/mastery/MasterySet.js +86 -0
- package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.d.ts +58 -0
- package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.d.ts.map +1 -0
- package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.js +83 -0
- package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.d.ts +69 -0
- package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.d.ts.map +1 -0
- package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.js +109 -0
- package/src/engine/control/first-person/math/Spring.d.ts +56 -0
- package/src/engine/control/first-person/math/Spring.d.ts.map +1 -0
- package/src/engine/control/first-person/math/Spring.js +71 -0
- package/src/engine/control/first-person/math/computeLRCBreathRate.d.ts +26 -0
- package/src/engine/control/first-person/math/computeLRCBreathRate.d.ts.map +1 -0
- package/src/engine/control/first-person/math/computeLRCBreathRate.js +41 -0
- package/src/engine/control/first-person/math/computeMassRatios.d.ts +35 -0
- package/src/engine/control/first-person/math/computeMassRatios.d.ts.map +1 -0
- package/src/engine/control/first-person/math/computeMassRatios.js +44 -0
- package/src/engine/control/first-person/pose/FirstPersonPose.d.ts +31 -1
- package/src/engine/control/first-person/pose/FirstPersonPose.d.ts.map +1 -1
- package/src/engine/control/first-person/pose/FirstPersonPose.js +49 -3
- package/src/engine/control/first-person/pose/FirstPersonPosture.d.ts +7 -0
- package/src/engine/control/first-person/pose/FirstPersonPosture.d.ts.map +1 -0
- package/src/engine/control/first-person/pose/FirstPersonPosture.js +27 -0
- package/src/engine/control/first-person/prototype_first_person_controller.js +637 -120
- package/src/engine/control/first-person/sensors/FirstPersonSensors.d.ts +58 -0
- package/src/engine/control/first-person/sensors/FirstPersonSensors.d.ts.map +1 -0
- package/src/engine/control/first-person/sensors/FirstPersonSensors.js +77 -0
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts +80 -0
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts.map +1 -0
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js +196 -0
- package/src/engine/control/first-person/test/buildTestPlayer.d.ts +20 -0
- package/src/engine/control/first-person/test/buildTestPlayer.d.ts.map +1 -0
- package/src/engine/control/first-person/test/buildTestPlayer.js +36 -0
- package/src/engine/graphics/camera/testClippingPlaneComputation.js +0 -2
- package/src/engine/graphics/ecs/light/Light.d.ts.map +1 -1
- package/src/engine/graphics/ecs/light/Light.js +27 -0
- package/src/engine/graphics/ecs/light/LightSystem.js +1 -1
- package/src/engine/graphics/ecs/path/PathDisplaySystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/path/testPathDisplaySystem.js +0 -2
- package/src/engine/graphics/ecs/path/tube/prototypeAnimatedPathMask.js +0 -2
- 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/graphics/render/buffer/buffers/prototypeNormalFrameBuffer.js +0 -2
- package/src/engine/graphics/render/forward_plus/plugin/ptototypeFPPlugin.js +0 -2
- package/src/engine/graphics/render/visibility/hiz/prototypeHiZ.js +0 -2
- package/src/engine/navigation/grid/find_path_on_grid_astar.d.ts.map +1 -1
- package/src/engine/navigation/grid/find_path_on_grid_astar.js +11 -2
- package/src/engine/navigation/mesh/bt_mesh_face_find_path.d.ts.map +1 -1
- package/src/engine/navigation/mesh/bt_mesh_face_find_path.js +11 -1
- 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 +461 -0
- package/src/engine/physics/RAPIER_REVIEW.md +934 -0
- package/src/engine/physics/REVIEW_001_ACTION_PLAN.md +642 -0
- package/src/engine/physics/body/BodyStorage.d.ts +187 -0
- package/src/engine/physics/body/BodyStorage.d.ts.map +1 -0
- package/src/engine/physics/body/BodyStorage.js +427 -0
- package/src/engine/physics/broadphase/PairList.d.ts +62 -0
- package/src/engine/physics/broadphase/PairList.d.ts.map +1 -0
- package/src/engine/physics/broadphase/PairList.js +97 -0
- package/src/engine/physics/broadphase/compute_fat_world_aabb.d.ts +16 -0
- package/src/engine/physics/broadphase/compute_fat_world_aabb.d.ts.map +1 -0
- package/src/engine/physics/broadphase/compute_fat_world_aabb.js +61 -0
- package/src/engine/physics/broadphase/generate_pairs.d.ts +38 -0
- package/src/engine/physics/broadphase/generate_pairs.d.ts.map +1 -0
- package/src/engine/physics/broadphase/generate_pairs.js +101 -0
- package/src/engine/physics/contact/ManifoldStore.d.ts +299 -0
- package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -0
- package/src/engine/physics/contact/ManifoldStore.js +608 -0
- package/src/engine/physics/ecs/BodyKind.d.ts +23 -0
- package/src/engine/physics/ecs/BodyKind.d.ts.map +1 -0
- package/src/engine/physics/ecs/BodyKind.js +24 -0
- package/src/engine/physics/ecs/Collider.d.ts +98 -0
- package/src/engine/physics/ecs/Collider.d.ts.map +1 -0
- package/src/engine/physics/ecs/Collider.js +136 -0
- package/src/engine/physics/ecs/ColliderFlags.d.ts +14 -0
- package/src/engine/physics/ecs/ColliderFlags.d.ts.map +1 -0
- package/src/engine/physics/ecs/ColliderFlags.js +15 -0
- package/src/engine/physics/ecs/ColliderObserverSystem.d.ts +58 -0
- package/src/engine/physics/ecs/ColliderObserverSystem.d.ts.map +1 -0
- package/src/engine/physics/ecs/ColliderObserverSystem.js +103 -0
- package/src/engine/physics/ecs/ColliderSerializationAdapter.d.ts +25 -0
- package/src/engine/physics/ecs/ColliderSerializationAdapter.d.ts.map +1 -0
- package/src/engine/physics/ecs/ColliderSerializationAdapter.js +37 -0
- package/src/engine/physics/ecs/PhysicsEvents.d.ts +15 -0
- package/src/engine/physics/ecs/PhysicsEvents.d.ts.map +1 -0
- package/src/engine/physics/ecs/PhysicsEvents.js +16 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +628 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -0
- package/src/engine/physics/ecs/PhysicsSystem.js +1301 -0
- package/src/engine/physics/ecs/RigidBody.d.ts +197 -0
- package/src/engine/physics/ecs/RigidBody.d.ts.map +1 -0
- package/src/engine/physics/ecs/RigidBody.js +240 -0
- package/src/engine/physics/ecs/RigidBodyFlags.d.ts +21 -0
- package/src/engine/physics/ecs/RigidBodyFlags.d.ts.map +1 -0
- package/src/engine/physics/ecs/RigidBodyFlags.js +22 -0
- package/src/engine/physics/ecs/RigidBodySerializationAdapter.d.ts +28 -0
- package/src/engine/physics/ecs/RigidBodySerializationAdapter.d.ts.map +1 -0
- package/src/engine/physics/ecs/RigidBodySerializationAdapter.js +81 -0
- package/src/engine/physics/ecs/SleepState.d.ts +11 -0
- package/src/engine/physics/ecs/SleepState.d.ts.map +1 -0
- package/src/engine/physics/ecs/SleepState.js +12 -0
- package/src/engine/physics/events/ContactEventBuffer.d.ts +46 -0
- package/src/engine/physics/events/ContactEventBuffer.d.ts.map +1 -0
- package/src/engine/physics/events/ContactEventBuffer.js +83 -0
- package/src/engine/physics/events/diff_manifolds.d.ts +25 -0
- package/src/engine/physics/events/diff_manifolds.d.ts.map +1 -0
- package/src/engine/physics/events/diff_manifolds.js +50 -0
- package/src/engine/physics/fluid/FluidField.d.ts +294 -16
- package/src/engine/physics/fluid/FluidField.d.ts.map +1 -1
- package/src/engine/physics/fluid/FluidField.js +510 -66
- package/src/engine/physics/fluid/FluidSimulator.d.ts +188 -5
- package/src/engine/physics/fluid/FluidSimulator.d.ts.map +1 -1
- package/src/engine/physics/fluid/FluidSimulator.js +456 -95
- package/src/engine/physics/fluid/SliceVisualiser.d.ts +29 -6
- package/src/engine/physics/fluid/SliceVisualiser.d.ts.map +1 -1
- package/src/engine/physics/fluid/SliceVisualiser.js +190 -165
- package/src/engine/physics/fluid/ecs/FluidComponent.d.ts +154 -0
- package/src/engine/physics/fluid/ecs/FluidComponent.d.ts.map +1 -0
- package/src/engine/physics/fluid/ecs/FluidComponent.js +238 -0
- package/src/engine/physics/fluid/ecs/FluidEffectorsComponent.d.ts +45 -0
- package/src/engine/physics/fluid/ecs/FluidEffectorsComponent.d.ts.map +1 -0
- package/src/engine/physics/fluid/ecs/FluidEffectorsComponent.js +89 -0
- package/src/engine/physics/fluid/ecs/FluidSystem.d.ts +107 -0
- package/src/engine/physics/fluid/ecs/FluidSystem.d.ts.map +1 -0
- package/src/engine/physics/fluid/ecs/FluidSystem.js +278 -0
- package/src/engine/physics/fluid/effector/AbstractFluidEffector.d.ts +62 -1
- package/src/engine/physics/fluid/effector/AbstractFluidEffector.d.ts.map +1 -1
- package/src/engine/physics/fluid/effector/AbstractFluidEffector.js +81 -6
- package/src/engine/physics/fluid/effector/GlobalFluidEffector.d.ts +17 -4
- package/src/engine/physics/fluid/effector/GlobalFluidEffector.d.ts.map +1 -1
- package/src/engine/physics/fluid/effector/GlobalFluidEffector.js +105 -12
- package/src/engine/physics/fluid/effector/ImpulseFluidEffector.d.ts +43 -0
- package/src/engine/physics/fluid/effector/ImpulseFluidEffector.d.ts.map +1 -0
- package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +210 -0
- package/src/engine/physics/fluid/effector/WakeFluidEffector.d.ts +62 -1
- package/src/engine/physics/fluid/effector/WakeFluidEffector.d.ts.map +1 -1
- package/src/engine/physics/fluid/effector/WakeFluidEffector.js +302 -8
- package/src/engine/physics/fluid/prototype.js +102 -91
- package/src/engine/physics/fluid/solver/optimal_sor_omega.d.ts +33 -0
- package/src/engine/physics/fluid/solver/optimal_sor_omega.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/optimal_sor_omega.js +41 -0
- package/src/engine/physics/fluid/solver/v3_grid_apply_advection_forward.d.ts +20 -5
- package/src/engine/physics/fluid/solver/v3_grid_apply_advection_forward.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_apply_advection_forward.js +60 -38
- package/src/engine/physics/fluid/solver/v3_grid_apply_diffusion.d.ts +25 -4
- package/src/engine/physics/fluid/solver/v3_grid_apply_diffusion.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_apply_diffusion.js +93 -73
- package/src/engine/physics/fluid/solver/v3_grid_apply_scalar_advection.d.ts +23 -0
- package/src/engine/physics/fluid/solver/v3_grid_apply_scalar_advection.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_grid_apply_scalar_advection.js +60 -0
- package/src/engine/physics/fluid/solver/v3_grid_compute_divergence.d.ts +23 -0
- package/src/engine/physics/fluid/solver/v3_grid_compute_divergence.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_grid_compute_divergence.js +68 -0
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts +30 -0
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +66 -0
- package/src/engine/physics/fluid/solver/v3_grid_patch_edges_uniform.d.ts +26 -0
- package/src/engine/physics/fluid/solver/v3_grid_patch_edges_uniform.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_grid_patch_edges_uniform.js +113 -0
- package/src/engine/physics/fluid/solver/v3_grid_shift_in_place.d.ts +30 -0
- package/src/engine/physics/fluid/solver/v3_grid_shift_in_place.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_grid_shift_in_place.js +107 -0
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts +49 -0
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +126 -0
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts +93 -0
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +424 -0
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts +48 -0
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +92 -0
- 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 +76 -32
- 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 +44 -0
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -0
- package/src/engine/physics/inertia/world_inverse_inertia.js +77 -0
- package/src/engine/physics/integration/integrate_position.d.ts +34 -0
- package/src/engine/physics/integration/integrate_position.d.ts.map +1 -0
- package/src/engine/physics/integration/integrate_position.js +79 -0
- package/src/engine/physics/integration/integrate_velocity.d.ts +55 -0
- package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -0
- package/src/engine/physics/integration/integrate_velocity.js +160 -0
- package/src/engine/physics/integration/quat_integrate.d.ts +27 -0
- package/src/engine/physics/integration/quat_integrate.d.ts.map +1 -0
- package/src/engine/physics/integration/quat_integrate.js +62 -0
- package/src/engine/physics/island/IslandBuilder.d.ts +167 -0
- package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -0
- package/src/engine/physics/island/IslandBuilder.js +411 -0
- package/src/engine/physics/island/union_find.d.ts +51 -0
- package/src/engine/physics/island/union_find.d.ts.map +1 -0
- package/src/engine/physics/island/union_find.js +76 -0
- package/src/engine/physics/narrowphase/PosedShape.d.ts +51 -0
- package/src/engine/physics/narrowphase/PosedShape.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/PosedShape.js +108 -0
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts +32 -0
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/box_box_manifold.js +639 -0
- 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 +122 -0
- package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/capsule_contacts.js +462 -0
- 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 +17 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/narrowphase_step.js +1422 -0
- package/src/engine/physics/narrowphase/sphere_box_contact.d.ts +38 -0
- package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/sphere_box_contact.js +123 -0
- package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts +26 -0
- package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/sphere_sphere_contact.js +51 -0
- 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/PhysicsSurfacePoint.d.ts +83 -0
- package/src/engine/physics/queries/PhysicsSurfacePoint.d.ts.map +1 -0
- package/src/engine/physics/queries/PhysicsSurfacePoint.js +100 -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/raycast.d.ts +20 -0
- package/src/engine/physics/queries/raycast.d.ts.map +1 -0
- package/src/engine/physics/queries/raycast.js +249 -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/friction_cone.d.ts +16 -0
- package/src/engine/physics/solver/friction_cone.d.ts.map +1 -0
- package/src/engine/physics/solver/friction_cone.js +37 -0
- package/src/engine/physics/solver/solve_contacts.d.ts +122 -0
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -0
- package/src/engine/physics/solver/solve_contacts.js +1016 -0
- package/src/core/geom/3d/topology/struct/binary/io/edge/OrderedEdge.d.ts +0 -34
- package/src/core/geom/3d/topology/struct/binary/io/edge/OrderedEdge.d.ts.map +0 -1
- package/src/core/geom/3d/topology/struct/binary/io/edge/OrderedEdge.js +0 -66
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_calc_edges.d.ts +0 -2
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_calc_edges.d.ts.map +0 -1
- package/src/core/geom/3d/topology/struct/binary/io/edge/bt_mesh_calc_edges.js +0 -54
- package/src/core/geom/3d/topology/struct/binary/io/edge/get_or_create_edge_map.d.ts +0 -2
- package/src/core/geom/3d/topology/struct/binary/io/edge/get_or_create_edge_map.d.ts.map +0 -1
- package/src/core/geom/3d/topology/struct/binary/io/edge/get_or_create_edge_map.js +0 -26
- package/src/engine/ecs/components/Motion.d.ts +0 -21
- package/src/engine/ecs/components/Motion.d.ts.map +0 -1
- package/src/engine/ecs/components/Motion.js +0 -27
- package/src/engine/ecs/components/MotionSerializationAdapter.d.ts +0 -20
- package/src/engine/ecs/components/MotionSerializationAdapter.d.ts.map +0 -1
- package/src/engine/ecs/components/MotionSerializationAdapter.js +0 -26
- package/src/engine/ecs/systems/MotionSystem.d.ts +0 -9
- package/src/engine/ecs/systems/MotionSystem.d.ts.map +0 -1
- package/src/engine/ecs/systems/MotionSystem.js +0 -29
- package/src/engine/physics/fluid/Fluid.d.ts +0 -26
- package/src/engine/physics/fluid/Fluid.d.ts.map +0 -1
- package/src/engine/physics/fluid/Fluid.js +0 -221
- package/src/engine/physics/fluid/solver/v3_grid_apply_advection_reverse.d.ts +0 -7
- package/src/engine/physics/fluid/solver/v3_grid_apply_advection_reverse.d.ts.map +0 -1
- package/src/engine/physics/fluid/solver/v3_grid_apply_advection_reverse.js +0 -8
|
@@ -0,0 +1,934 @@
|
|
|
1
|
+
# Deep Technical Review: meep physics vs. Rapier / Parry
|
|
2
|
+
|
|
3
|
+
A focused, side-by-side comparison of the in-house deterministic JS rigid-body
|
|
4
|
+
engine in `engine/physics/` against [Rapier](https://github.com/dimforge/rapier)
|
|
5
|
+
(Rust dynamics) and its narrowphase library
|
|
6
|
+
[Parry](https://github.com/dimforge/parry). The aim is to surface correctness
|
|
7
|
+
gaps, numerical issues, and structural divergences worth investing in — not to
|
|
8
|
+
recommend a rewrite. Pure-JS, single-threaded and no-SAB are deliberate
|
|
9
|
+
constraints (see `PLAN.md`) and are not listed as gaps.
|
|
10
|
+
|
|
11
|
+
Repo paths in this document are relative to
|
|
12
|
+
`engine/physics/` for the meep side, and relative to the upstream repo
|
|
13
|
+
(`rapier/src/...`, `parry/src/...`) for the Rapier side.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 1. Overall architecture
|
|
18
|
+
|
|
19
|
+
### 1.1 Pipeline shape
|
|
20
|
+
|
|
21
|
+
Both engines run the same canonical fixed-step pipeline. The difference is
|
|
22
|
+
mostly granularity and where the boundaries fall:
|
|
23
|
+
|
|
24
|
+
| Stage | meep `PhysicsSystem.fixedUpdate` | Rapier (per substep inside `VelocitySolver::solve_constraints`) |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| Integrate velocities (forces + gravity) | semi-implicit Euler, `integrate_velocity.js` | per-substep velocity increment (`solver_vels_increment`) |
|
|
27
|
+
| Refit broadphase (fat AABB) | `compute_fat_world_aabb.js` per awake-body leaf | `BroadPhaseBvh::update`, change-tracked subtree refit |
|
|
28
|
+
| Broadphase pair generation | per-leaf query against static + dynamic BVH, dedup via touched flag | single-tree `BroadPhaseBvh` with pair hashmap |
|
|
29
|
+
| Wake propagation | `__wake_pairs` walks pair list, wakes sleepers | island-manager sleep root traversal |
|
|
30
|
+
| Narrowphase | type-switch dispatcher in `narrowphase_step.js` | dispatch into specialised `contact_manifolds_*` per pair type |
|
|
31
|
+
| Island build | union-find, deterministic CSR layout, separates static/dynamic | DFS active-list propagation, per-island sleep |
|
|
32
|
+
| Solver | per-island PGS+Baumgarte, 10 velocity iters | per-island PGS-S (TGS substepping) + bias/no-bias passes; warm-start |
|
|
33
|
+
| Integrate position | semi-implicit, post-solve | between substeps |
|
|
34
|
+
| Sleep test | per-island atomic, threshold on `\|v\|² + \|ω\|²` | per-island energy threshold, sleep-root traversal |
|
|
35
|
+
| Event diff | `diff_manifolds.js` → Begin/Stay/End | identical model via `ContactEventBuffer` |
|
|
36
|
+
|
|
37
|
+
The high-level shape is essentially identical. Where meep is closest to
|
|
38
|
+
Rapier is **broadphase refit + pair generation + island building +
|
|
39
|
+
sleep + event dispatch**. Where meep is structurally distinct is **solver
|
|
40
|
+
architecture** (no TGS / split-impulse), **CCD** (speculative margin only),
|
|
41
|
+
and **closed-form narrowphase coverage** (a fraction of Parry's pair
|
|
42
|
+
generators).
|
|
43
|
+
|
|
44
|
+
### 1.2 Data layout
|
|
45
|
+
|
|
46
|
+
Both engines use SoA arena storage with generation-tagged stable IDs:
|
|
47
|
+
|
|
48
|
+
- meep: `BodyStorage.js` — `Int32Array __entities`, `Uint8Array __generations`,
|
|
49
|
+
packed `(index << 8) | gen`, min-heap free-list (deterministic reuse), dense
|
|
50
|
+
awake list + reverse map.
|
|
51
|
+
- Rapier: `RigidBodySet` is an `Arena<RigidBody>` (the `Arena` is generic and
|
|
52
|
+
carries `(index, generation)` handles); `ColliderSet` likewise. The arena's
|
|
53
|
+
free-slot picking is also LIFO/heap-style for deterministic replay.
|
|
54
|
+
|
|
55
|
+
**These are structurally the same design.** The differences are:
|
|
56
|
+
|
|
57
|
+
- meep packs `(index, gen)` into a single u32 with an 8-bit generation; Rapier
|
|
58
|
+
uses a 64-bit `(u32, u32)` pair. Our wrap-mod-256 generation is fine for
|
|
59
|
+
games (256 alloc/free cycles before ABA risk on a slot) but is much tighter
|
|
60
|
+
than Rapier's.
|
|
61
|
+
- meep maintains a **separate dense awake list with reverse map** so the hot
|
|
62
|
+
loop iterates the active subset directly. Rapier achieves the same via the
|
|
63
|
+
`IslandManager`'s `awake_islands` list of island ids — different layer of
|
|
64
|
+
abstraction, same end result.
|
|
65
|
+
- meep stores per-body component pointers in a sparse `__bodies` array
|
|
66
|
+
(`PhysicsSystem.js:201`) by body index. Rapier returns `RigidBody` refs
|
|
67
|
+
directly out of the arena. Equivalent.
|
|
68
|
+
|
|
69
|
+
### 1.3 Broadphase
|
|
70
|
+
|
|
71
|
+
meep currently runs **two BVHs (static + dynamic)** with fat-AABB refit,
|
|
72
|
+
mirroring Jolt's separation. Rapier was previously `BroadPhaseMultiSap` but
|
|
73
|
+
is now (current master) `BroadPhaseBvh` — a **single unified BVH** with
|
|
74
|
+
change-tracked subtree refits:
|
|
75
|
+
|
|
76
|
+
```rust
|
|
77
|
+
pub struct BroadPhaseBvh {
|
|
78
|
+
pub(crate) tree: Bvh,
|
|
79
|
+
workspace: BvhWorkspace,
|
|
80
|
+
pairs: HashMap<(ColliderHandle, ColliderHandle), u32>,
|
|
81
|
+
frame_index: u32,
|
|
82
|
+
optimization_strategy: BvhOptimizationStrategy,
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The directional shift is interesting: **our two-tree split is what Rapier
|
|
87
|
+
moved away from** when it converged on BVH. Rapier's single-tree approach
|
|
88
|
+
relies on `collider.needs_broad_phase_update()` to skip static leaves at
|
|
89
|
+
refit time, which is roughly equivalent to our static-tree never being
|
|
90
|
+
refitted at all. The two trees, however, give us tighter static-side
|
|
91
|
+
queries (the dynamic tree is small even on huge scenes, so the
|
|
92
|
+
dynamic↔static query side of `generate_pairs.js:82-99` walks a balanced
|
|
93
|
+
shallow tree). The trade-off vs. Rapier:
|
|
94
|
+
|
|
95
|
+
- **Memory:** marginally higher for us — two BVH roots, two free-lists.
|
|
96
|
+
- **Refit cost:** lower for us — we never touch static leaves.
|
|
97
|
+
- **Query cost:** ~same; we pay one extra root-test per query, Rapier pays
|
|
98
|
+
for static leaf early-outs.
|
|
99
|
+
- **Hot-collider streaming (LOD-load / world streaming):** they're equivalent
|
|
100
|
+
— both must insert/remove leaves on the active tree.
|
|
101
|
+
|
|
102
|
+
Our pair de-dup goes through `ManifoldStore.is_touched(slot)` in
|
|
103
|
+
`generate_pairs.js:76,94`. Rapier uses the `HashMap<(handle, handle), u32>`
|
|
104
|
+
directly. Functionally equivalent; ours is allocation-free in steady state
|
|
105
|
+
(open-addressed `PairUint32Map`).
|
|
106
|
+
|
|
107
|
+
### 1.4 Island structure
|
|
108
|
+
|
|
109
|
+
Two genuinely different designs:
|
|
110
|
+
|
|
111
|
+
- **meep** (`IslandBuilder.js`) does **union-find with path halving + union
|
|
112
|
+
by min-index**, then writes a CSR-laid-out `(body_offsets, body_data,
|
|
113
|
+
contact_offsets, contact_data)` per frame. Deterministic and re-buildable
|
|
114
|
+
from scratch each step.
|
|
115
|
+
- **Rapier** (`island_manager/manager.rs`) uses **incremental DFS with
|
|
116
|
+
sleep-root traversal**. Islands persist across frames; only sleep-state
|
|
117
|
+
transitions trigger re-traversal, with a `MAX_PER_FRAME_COST` budget to
|
|
118
|
+
avoid frame spikes.
|
|
119
|
+
|
|
120
|
+
The Rapier approach is fundamentally laziness-driven: it pays cost only
|
|
121
|
+
when a body changes sleep state. The meep approach pays a small union-find
|
|
122
|
+
cost every frame.
|
|
123
|
+
|
|
124
|
+
For our workload (many mostly-sleeping bodies + a small active set), our
|
|
125
|
+
rebuild-from-scratch approach is fine because we only iterate awake
|
|
126
|
+
bodies + touched contacts. The union-find pass is `O((|awake_bodies| +
|
|
127
|
+
|touched_contacts|) · α(n))`, dominated by the awake count which is small
|
|
128
|
+
by design. **The deterministic CSR output is a meep-side win** — Rapier
|
|
129
|
+
sacrifices output ordering predictability for incrementality, which makes
|
|
130
|
+
it harder to do bit-exact replays of solver behaviour across two
|
|
131
|
+
instances.
|
|
132
|
+
|
|
133
|
+
### 1.5 Solver architecture
|
|
134
|
+
|
|
135
|
+
This is the largest structural divergence.
|
|
136
|
+
|
|
137
|
+
- **meep**: PGS in `solver/solve_contacts.js`, single velocity-iteration loop,
|
|
138
|
+
warm-start, Baumgarte position-correction folded into the velocity solve
|
|
139
|
+
via `bias_n` (`solve_contacts.js:444-449`), Coulomb friction with disk
|
|
140
|
+
clamp.
|
|
141
|
+
- **Rapier**: PGS-S (TGS substepping) in `velocity_solver.rs`. Two-phase
|
|
142
|
+
per substep:
|
|
143
|
+
1. `num_internal_pgs_iterations` of `contact_constraints.solve(...)` —
|
|
144
|
+
**with bias** (`rhs` includes restitution + position-correction).
|
|
145
|
+
2. `num_internal_stabilization_iterations` of
|
|
146
|
+
`contact_constraints.solve_wo_bias(...)` — pure non-penetration,
|
|
147
|
+
stabilising the bias-pumped velocity.
|
|
148
|
+
Between substeps Rapier calls `integrate_positions(...)`. The constraint
|
|
149
|
+
element (`contact_constraint_element.rs`) carries **two impulse fields:
|
|
150
|
+
`impulse` (current substep) and `impulse_accumulator` (sum across substeps)**
|
|
151
|
+
and **two RHS fields: `rhs` and `rhs_wo_bias`**. That's the split-impulse
|
|
152
|
+
+ TGS architecture that PLAN.md notes we tried and rolled back.
|
|
153
|
+
|
|
154
|
+
The relevant Rapier snippet (paraphrased from `contact_constraint_element.rs`):
|
|
155
|
+
|
|
156
|
+
```rust
|
|
157
|
+
// solve() at iteration k:
|
|
158
|
+
let dvel = dir1·v1 + ... + self.rhs;
|
|
159
|
+
let new_impulse = cfm * (self.impulse - r * dvel).max(0);
|
|
160
|
+
self.impulse = new_impulse;
|
|
161
|
+
// apply lambda = new_impulse - prev_impulse
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
```rust
|
|
165
|
+
// solve_wo_bias() — same arithmetic but with self.rhs replaced by self.rhs_wo_bias.
|
|
166
|
+
// Position-bias contribution is excluded so the iterates only enforce vn ≥ 0.
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The crucial property is that **restitution is encoded once into `rhs` at
|
|
170
|
+
constraint build time and `rhs_wo_bias` does not include it**, so the
|
|
171
|
+
post-bias stabilisation pass cannot subtract a previously-applied
|
|
172
|
+
restitution impulse. That's the fix for PLAN.md's "restitution × warm-start
|
|
173
|
+
clamp" failure mode.
|
|
174
|
+
|
|
175
|
+
### 1.6 Sleep system
|
|
176
|
+
|
|
177
|
+
Both have **atomic per-island sleep**. The mechanics differ:
|
|
178
|
+
|
|
179
|
+
- **meep** (`PhysicsSystem.__sleep_test` at `PhysicsSystem.js:942`): walks
|
|
180
|
+
islands once per step; if max `|v|² + |ω|²` is below threshold for the
|
|
181
|
+
whole island and the lowest-stabilising member's sleep_timer crosses the
|
|
182
|
+
time threshold, threads members into a circular doubly-linked list
|
|
183
|
+
(`sleep_group_next/prev`), zeroes velocities, and removes from the awake
|
|
184
|
+
set. Atomic wake walks the chain.
|
|
185
|
+
- **Rapier**: per-body kinetic energy (`activation.update_energy()`) drives
|
|
186
|
+
a sleep_root candidate flag; the IslandManager extracts sleeping islands
|
|
187
|
+
by graph traversal from those roots, time-sliced via `MAX_PER_FRAME_COST`.
|
|
188
|
+
Joint and contact connections re-awaken bodies.
|
|
189
|
+
|
|
190
|
+
Functionally similar — both wake all members in one frame when any member
|
|
191
|
+
is disturbed. The meep approach is simpler and runs every frame; Rapier's
|
|
192
|
+
is lazier and budget-aware.
|
|
193
|
+
|
|
194
|
+
### 1.7 Threading
|
|
195
|
+
|
|
196
|
+
Rapier offers per-island parallel solve via Rayon when the `parallel`
|
|
197
|
+
feature is enabled. meep is deliberately single-threaded (no SAB, no
|
|
198
|
+
workers). The island CSR layout in meep would support a worker-based
|
|
199
|
+
solver but isn't wired up. **Not a gap** — explicit out-of-scope decision.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 2. Specific algorithms and trade-offs
|
|
204
|
+
|
|
205
|
+
### 2.1 Broadphase
|
|
206
|
+
|
|
207
|
+
| Aspect | meep | Rapier |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| Structure | Two BVHs (static + dynamic), Jolt-style | One BVH (`BroadPhaseBvh`), change-tracked |
|
|
210
|
+
| Refit | Per awake-body fat AABB (`compute_fat_world_aabb.js:34`) | `collider.needs_broad_phase_update()` then subtree refit |
|
|
211
|
+
| Pair output | `PairList` canonical `(min,max)` | `HashMap<(handle,handle), u32>` |
|
|
212
|
+
| Dedup | `ManifoldStore.is_touched` (one slot per pair) | hashmap key uniqueness + per-frame frame_index |
|
|
213
|
+
| Velocity-pad | `FAT_LINEAR + FAT_VELOCITY_MULTIPLIER * speed * dt` (2 s look-ahead) | configurable; default similar (multiple of step) |
|
|
214
|
+
|
|
215
|
+
Rapier's optimisation strategy (`SubtreeOptimizer`) does incremental subtree
|
|
216
|
+
re-balancing each frame to amortise the cost of trees becoming
|
|
217
|
+
unbalanced as bodies move. We don't have an equivalent — our BVH's quality
|
|
218
|
+
degrades slowly under sustained motion patterns. Worth investigating for
|
|
219
|
+
the awake set, less so for the static tree which we never touch.
|
|
220
|
+
|
|
221
|
+
### 2.2 Narrowphase dispatch and contact generators
|
|
222
|
+
|
|
223
|
+
Parry's narrowphase is a **shape-type-pair dispatch table** with about
|
|
224
|
+
**18 contact-manifold generators**, each a closed-form algorithm for a
|
|
225
|
+
specific shape pair. meep's dispatcher in `narrowphase_step.js:192-353`
|
|
226
|
+
covers **five closed-form pairs** plus GJK+EPA fallback for everything else.
|
|
227
|
+
|
|
228
|
+
Side-by-side coverage:
|
|
229
|
+
|
|
230
|
+
| Pair | meep | Parry equivalent |
|
|
231
|
+
|---|---|---|
|
|
232
|
+
| sphere-sphere | ✅ `sphere_sphere_contact.js` | `contact_manifolds_ball_ball.rs` |
|
|
233
|
+
| sphere-box (cuboid) | ✅ `sphere_box_contact.js` | `contact_manifolds_convex_ball.rs` (general) |
|
|
234
|
+
| capsule-sphere | ✅ in `capsule_contacts.js` | `contact_manifolds_convex_ball.rs` |
|
|
235
|
+
| capsule-capsule | ✅ in `capsule_contacts.js` | `contact_manifolds_capsule_capsule.rs` |
|
|
236
|
+
| capsule-box | ✅ multi-contact in `capsule_contacts.js` | `contact_manifolds_cuboid_capsule.rs` |
|
|
237
|
+
| box-box | ✅ SAT + clipping `box_box_manifold.js` | `contact_manifolds_cuboid_cuboid.rs` |
|
|
238
|
+
| sphere-triangle (heightmap/mesh) | ❌ GJK+EPA per triangle | `contact_manifolds_trimesh_shape.rs` dispatches to closed-form |
|
|
239
|
+
| box-triangle | ❌ GJK+EPA per triangle | ✅ `contact_manifolds_cuboid_triangle.rs` |
|
|
240
|
+
| capsule-triangle | ❌ GJK+EPA per triangle | dispatched via convex-vs-trimesh; closed-form exists |
|
|
241
|
+
| half-space (plane) vs convex | ❌ (no plane shape) | ✅ `contact_manifolds_halfspace_pfm.rs` |
|
|
242
|
+
| heightfield | per-triangle GJK+EPA via `heightmap_enumerate_triangles` | `contact_manifolds_heightfield_shape.rs` |
|
|
243
|
+
| convex-convex generic | GJK+EPA | `contact_manifolds_pfm_pfm.rs` (polyhedral-feature-mapping) |
|
|
244
|
+
|
|
245
|
+
**The big gap is closed-form primitive-vs-triangle.** `PLAN.md` is correct
|
|
246
|
+
that this is the biggest accuracy gap, and Parry's `contact_manifolds_cuboid_triangle.rs`
|
|
247
|
+
is the direct port target. Its algorithm is:
|
|
248
|
+
|
|
249
|
+
1. SAT over cuboid faces (3 axes), triangle plane (1 axis), and cuboid×edge
|
|
250
|
+
edge crosses (9 axes in 3D).
|
|
251
|
+
2. Build `PolygonalFeature` from the cuboid's support face and the triangle.
|
|
252
|
+
3. Clip features via `PolygonalFeature::contacts(...)` to produce up to 4
|
|
253
|
+
contacts.
|
|
254
|
+
|
|
255
|
+
Crucially Parry's algorithm assigns **feature IDs to each clip output** so
|
|
256
|
+
contact persistence across frames is robust. Our per-triangle GJK+EPA has
|
|
257
|
+
no feature-id story at all (see §2.5).
|
|
258
|
+
|
|
259
|
+
### 2.3 GJK and EPA
|
|
260
|
+
|
|
261
|
+
**meep GJK** (`gjk/gjk.js`): Kevin Moran reference implementation, no
|
|
262
|
+
Voronoi simplex — direct case-analysis of triangle / tetrahedron
|
|
263
|
+
sub-regions; 64-iter cap; **no frame-coherent simplex caching**. The
|
|
264
|
+
recently-fixed simplex-shift bugs around `gjk.js:214-264` (clear comments
|
|
265
|
+
in source) suggest this code was carrying subtle correctness issues until
|
|
266
|
+
recently.
|
|
267
|
+
|
|
268
|
+
**Parry GJK** (`parry/src/query/gjk/gjk.rs`): **Voronoi simplex**
|
|
269
|
+
(`VoronoiSimplex`) — explicit projected-origin-and-reduce step. Frame
|
|
270
|
+
coherence via simplex passed in by caller (so callers can persist it
|
|
271
|
+
between frames; Parry persists in `ContactManifoldsWorkspace`).
|
|
272
|
+
|
|
273
|
+
The Voronoi simplex is the technically-correct GJK formulation — every
|
|
274
|
+
simplex update projects the origin onto the simplex once and reduces
|
|
275
|
+
based on Voronoi regions, rather than testing each potential reduction
|
|
276
|
+
edge/face manually. The trade-off in our case is small for static-shape
|
|
277
|
+
pairs (boxes don't have many support directions), but meaningful for
|
|
278
|
+
mesh / smooth shapes where the reduction path matters.
|
|
279
|
+
|
|
280
|
+
**meep EPA** (`gjk/expanding_polytope_algorithm.js`): up to 64 faces, 32
|
|
281
|
+
loose edges, 64 iterations; degeneration is silently swallowed and the
|
|
282
|
+
best-so-far face is returned. The `debugger;` statement at line 136 is a
|
|
283
|
+
leftover that should be removed.
|
|
284
|
+
|
|
285
|
+
**Parry EPA** (`parry/src/query/epa/epa3.rs`): adaptive `eps_tol` scaled
|
|
286
|
+
by `max(scale_of_coordinates, 1.0)` to prevent spurious failures on
|
|
287
|
+
large-coordinate geometry (fixes parry issue #415). Hard 100-iter cap.
|
|
288
|
+
Both stagnation- and progress-based termination criteria.
|
|
289
|
+
|
|
290
|
+
The **scale-adaptive tolerance** is a concrete, copyable improvement. Our
|
|
291
|
+
`EPA_TOLERANCE = 0.0001` (line 10) is an absolute threshold — on a
|
|
292
|
+
character-scale shape it's fine but on a kilometre-scale heightmap
|
|
293
|
+
collision it's well below the practical noise floor and may cause early
|
|
294
|
+
"convergence" on a degenerate face.
|
|
295
|
+
|
|
296
|
+
### 2.4 MPR
|
|
297
|
+
|
|
298
|
+
Both engines have MPR (`gjk/mpr.js` vs `parry/src/query/contact/`/various).
|
|
299
|
+
Parry uses MPR primarily for **support-map vs support-map proximity tests**
|
|
300
|
+
where GJK is overkill. Our `mpr.js` is used only by `shape_cast.js:364` for
|
|
301
|
+
the normal-recovery probe — it's **not wired into `narrowphase_step.js`** as
|
|
302
|
+
a fallback for non-convergent EPA on smooth shapes (PLAN.md notes this as
|
|
303
|
+
its intended role). That's a low-risk swap: per-pair config to prefer MPR
|
|
304
|
+
for shapes whose support function is smooth (sphere, capsule, possibly
|
|
305
|
+
the unwired cylinder), or as a fallback when EPA returns a non-positive
|
|
306
|
+
depth.
|
|
307
|
+
|
|
308
|
+
### 2.5 Manifold caching and contact persistence
|
|
309
|
+
|
|
310
|
+
This is where meep and Parry diverge most subtly.
|
|
311
|
+
|
|
312
|
+
- **meep** (`contact/ManifoldStore.js`):
|
|
313
|
+
- Slot keyed by `(idA, idB)` via `PairUint32Map`.
|
|
314
|
+
- Per-slot meta: count, touched bit, prev_touched bit, grace counter.
|
|
315
|
+
- Each step `acquire(idA, idB)` flips touched=1. `advance_frame` rolls
|
|
316
|
+
touched → prev_touched.
|
|
317
|
+
- Contacts overwritten each frame (`clear_contacts(slot)` at
|
|
318
|
+
`narrowphase_step.js:687`); only `j_n`, `j_t1`, `j_t2` (warm-start
|
|
319
|
+
impulses) survive into the next frame.
|
|
320
|
+
- **There is no per-contact feature-id matching.** Warm-start impulses
|
|
321
|
+
are written by index `(slot, k)` — if the narrowphase emits contacts
|
|
322
|
+
in a different order this frame vs. last frame (which can absolutely
|
|
323
|
+
happen for a rotating box-on-box), the warm-start impulses are applied
|
|
324
|
+
to the **wrong contact points**.
|
|
325
|
+
|
|
326
|
+
- **Parry** (`contact_manifold.rs`):
|
|
327
|
+
```rust
|
|
328
|
+
pub struct TrackedContact<Data> {
|
|
329
|
+
pub local_p1: Vector,
|
|
330
|
+
pub local_p2: Vector,
|
|
331
|
+
pub dist: Real,
|
|
332
|
+
pub fid1: PackedFeatureId, // feature id on shape 1
|
|
333
|
+
pub fid2: PackedFeatureId, // feature id on shape 2
|
|
334
|
+
pub data: Data, // user data (warm-start impulses)
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
Each per-pair contact generator (cuboid-cuboid, cuboid-triangle, etc.)
|
|
338
|
+
produces contacts with **stable feature IDs**: e.g. `face(2) × face(5)`,
|
|
339
|
+
`edge(0,1) × vertex(3)`, etc. `match_contacts()` then transfers the
|
|
340
|
+
user data (impulses) onto the corresponding contact in the new
|
|
341
|
+
manifold by feature-ID equality.
|
|
342
|
+
|
|
343
|
+
**This is a real correctness bug in our solver.** The warm-start cache
|
|
344
|
+
arithmetic is fine in isolation, but in the rotating-box-on-box case the
|
|
345
|
+
reduction pass in `box_box_manifold.js:498-530` and the candidate
|
|
346
|
+
reduction in `narrowphase_step.js:149-178` produce contact orderings that
|
|
347
|
+
can re-shuffle between frames. The solver then warm-starts contact[0]
|
|
348
|
+
with impulse from a different physical contact, smearing across the
|
|
349
|
+
manifold and producing the kind of low-amplitude jitter that's commonly
|
|
350
|
+
attributed to "PGS being lossy" but is actually warm-start mis-aim.
|
|
351
|
+
|
|
352
|
+
There are two ways to fix:
|
|
353
|
+
|
|
354
|
+
1. **Add feature-id columns to the manifold stride**, populate from
|
|
355
|
+
`box_box_manifold` (the reference + incident face indices + Sutherland-
|
|
356
|
+
Hodgman edge index already define a feature ID), and match by
|
|
357
|
+
feature ID when assigning impulses. Parry's `match_contacts` is the
|
|
358
|
+
reference. Stride goes from 13 → 15 floats. Needs cooperation from
|
|
359
|
+
each closed-form pair generator.
|
|
360
|
+
2. **Fallback: match by spatial proximity** like Parry's
|
|
361
|
+
`match_contacts_using_positions`. Cheaper to implement (just a 4×4
|
|
362
|
+
distance matrix at solver pre-step) but loses precision when contacts
|
|
363
|
+
are within the matching threshold of each other.
|
|
364
|
+
|
|
365
|
+
This is the single most impactful **correctness** fix in this review.
|
|
366
|
+
|
|
367
|
+
### 2.6 Solver: PGS vs PGS-S, Baumgarte vs split-impulse
|
|
368
|
+
|
|
369
|
+
Rapier carries **two impulses and two RHS values** per constraint element:
|
|
370
|
+
|
|
371
|
+
```rust
|
|
372
|
+
pub struct ContactConstraintNormalPart<N> {
|
|
373
|
+
torque_dir1, torque_dir2, ii_torque_dir1, ii_torque_dir2,
|
|
374
|
+
rhs, // restitution + position bias
|
|
375
|
+
rhs_wo_bias, // pure non-penetration
|
|
376
|
+
impulse, // this substep's accumulator
|
|
377
|
+
impulse_accumulator, // cumulative across substeps
|
|
378
|
+
r, r_mat_elts,
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Inside a substep:
|
|
383
|
+
|
|
384
|
+
```
|
|
385
|
+
for _ in 0..num_internal_pgs_iterations:
|
|
386
|
+
contact_constraints.solve(...) // uses rhs
|
|
387
|
+
for _ in 0..num_internal_stabilization_iterations:
|
|
388
|
+
contact_constraints.solve_wo_bias(...) // uses rhs_wo_bias
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
The two-phase scheme is exactly what defuses the failure modes PLAN.md
|
|
392
|
+
documents (restitution × warm-start, Baumgarte K× amplification):
|
|
393
|
+
|
|
394
|
+
- Restitution is part of `rhs`, applied once during the bias phase. The
|
|
395
|
+
no-bias stabilisation pass operates on `rhs_wo_bias` so the
|
|
396
|
+
restitution impulse cannot be subtracted away by a later iteration
|
|
397
|
+
seeing "separating velocity".
|
|
398
|
+
- Position correction is part of `rhs`, applied during the bias phase, and
|
|
399
|
+
the no-bias pass cleans up the post-bias velocity contamination.
|
|
400
|
+
- Forces are integrated once per substep into `solver_vels_increment`
|
|
401
|
+
outside the iteration loop, so the accumulator-timing issue doesn't
|
|
402
|
+
arise (each substep gets the right share of the force).
|
|
403
|
+
|
|
404
|
+
Our `solver/solve_contacts.js:531` does:
|
|
405
|
+
|
|
406
|
+
```js
|
|
407
|
+
const lambda_n = -m_eff_n * (vn + bias_n);
|
|
408
|
+
const sum_n = j_n_accum + lambda_n;
|
|
409
|
+
const new_j_n = sum_n > 0 ? sum_n : 0; // CLAMP
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
with `bias_n` blending Baumgarte position correction and restitution
|
|
413
|
+
(`solve_contacts.js:442-457`). On the very next iteration the same
|
|
414
|
+
contact sees a different `vn`, recomputes the full `(vn + bias_n)` target,
|
|
415
|
+
and can subtract whatever the previous iteration added. Restitution is
|
|
416
|
+
particularly fragile because it's a one-shot impulse that should be
|
|
417
|
+
applied during the contact's *first* solver pass and never adjusted
|
|
418
|
+
again, but our scheme keeps re-targeting it.
|
|
419
|
+
|
|
420
|
+
**This is the architectural lock that prevents TGS**, as `PLAN.md` documents.
|
|
421
|
+
The minimum fix is to **split `rhs` into `rhs_pos` and `rhs_restitution` +
|
|
422
|
+
`rhs_velocity_only`**, apply the position+restitution bias only on a
|
|
423
|
+
single dedicated iteration (or set of iterations), and then run the rest
|
|
424
|
+
of the solver on the velocity-only target. Even without TGS substepping,
|
|
425
|
+
this would defuse the warm-start × restitution interaction documented in
|
|
426
|
+
`solve_contacts.js:452-456`.
|
|
427
|
+
|
|
428
|
+
### 2.7 CCD
|
|
429
|
+
|
|
430
|
+
Rapier has full CCD (`ccd/ccd_solver.rs`):
|
|
431
|
+
|
|
432
|
+
```rust
|
|
433
|
+
pub struct CCDSolver; // stateless
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
with three phases:
|
|
437
|
+
1. `update_ccd_active_flags()` based on velocity threshold.
|
|
438
|
+
2. `predict_impacts_at_next_positions()` — sweeps a body's collider
|
|
439
|
+
against the broadphase, finds first TOI.
|
|
440
|
+
3. `clamp_motions()` — clamps motion to stop just before impact.
|
|
441
|
+
|
|
442
|
+
We have speculative-margin-via-fat-AABB only. PLAN.md flags this as a
|
|
443
|
+
backlog item; the falling-tower bench is the concrete failure case (180
|
|
444
|
+
of 1000 bodies tunnel through a 1 cm floor on a 1 km drop).
|
|
445
|
+
|
|
446
|
+
The shape-cast machinery already in `queries/shape_cast.js` provides the
|
|
447
|
+
TOI primitive. The missing piece is the per-body opt-in CCD path that
|
|
448
|
+
runs after broadphase but before solver: identify high-speed bodies
|
|
449
|
+
(`|v| · dt > min_extent`), sweep their colliders, clamp dt accordingly.
|
|
450
|
+
This is conceptually parallel to `__wake_pairs` — a stage between
|
|
451
|
+
broadphase and narrowphase.
|
|
452
|
+
|
|
453
|
+
### 2.8 Concave / triangle mesh
|
|
454
|
+
|
|
455
|
+
Both engines decompose a non-convex shape into triangles overlapping the
|
|
456
|
+
convex's AABB, then run a per-triangle algorithm:
|
|
457
|
+
|
|
458
|
+
- **meep** (`narrowphase_step.js:368-577`): generic per-triangle GJK+EPA
|
|
459
|
+
with a one-sided face-normal rejection. The `Triangle3D` support
|
|
460
|
+
function is degenerate along the face-normal axis (all 3 verts project
|
|
461
|
+
to the same value), so EPA can't tighten near vertical contacts — the
|
|
462
|
+
exact failure documented in PLAN.md's known limitations.
|
|
463
|
+
|
|
464
|
+
- **Parry** (`contact_manifolds_trimesh_shape.rs`): dispatches the
|
|
465
|
+
per-triangle contact to the appropriate **closed-form** generator
|
|
466
|
+
(`contact_manifolds_cuboid_triangle.rs`, `contact_manifolds_convex_ball.rs`,
|
|
467
|
+
etc.). Triangle is treated as a polygonal feature with explicit face,
|
|
468
|
+
3 edges, 3 vertices and **stable feature IDs** per sub-feature.
|
|
469
|
+
|
|
470
|
+
The actionable port is Parry's `contact_manifolds_cuboid_triangle.rs` —
|
|
471
|
+
SAT + face clipping — adapted to our `Triangle3D` flyweight and
|
|
472
|
+
emitting into our `(world_a, world_b, normal, depth)` stride. A
|
|
473
|
+
ball-vs-triangle closed form (Voronoi-region-based) is even simpler:
|
|
474
|
+
clamp the sphere centre to the triangle and dispatch on which feature
|
|
475
|
+
(face / edge / vertex) the clamp landed on.
|
|
476
|
+
|
|
477
|
+
### 2.9 Queries (raycast, shape-cast, overlap)
|
|
478
|
+
|
|
479
|
+
Parry's `QueryPipeline` mirrors what we have in `queries/`:
|
|
480
|
+
|
|
481
|
+
| meep | Parry equivalent | Notes |
|
|
482
|
+
|---|---|---|
|
|
483
|
+
| `raycast.js` | `query/ray/...` | meep is broadphase AABB-hit only; PLAN.md flags the narrowphase refinement as TODO |
|
|
484
|
+
| `shape_cast.js` | `shape_cast_support_map_support_map.rs` | meep uses **bisection on GJK overlap**; Parry uses **GJK directional_distance / minkowski_ray_cast** — conservative advancement |
|
|
485
|
+
| `overlap_shape.js` | `query/intersection_test/...` | both use broadphase + GJK |
|
|
486
|
+
|
|
487
|
+
**The shape_cast approach is genuinely different.** Our bisection is
|
|
488
|
+
robust and works, but is O(`log₂(tMax / tol)`) GJK calls per candidate.
|
|
489
|
+
Parry's GJK directional_distance is **continuous refinement** — it
|
|
490
|
+
advances the ray origin in the Minkowski difference each iteration and
|
|
491
|
+
tightens both bounds simultaneously. Typically converges in 5-15
|
|
492
|
+
iterations vs. our 32-step coarse linear search + up to 32 bisection
|
|
493
|
+
steps. This is a clean port target since we already have all the
|
|
494
|
+
support-function machinery.
|
|
495
|
+
|
|
496
|
+
### 2.10 Determinism
|
|
497
|
+
|
|
498
|
+
- **Rapier** advertises cross-platform determinism with f32 by avoiding
|
|
499
|
+
transcendentals and relying on IEEE-754 round-to-nearest behaviour on
|
|
500
|
+
the supported platforms. Their integration uses `mul_add`-free math
|
|
501
|
+
where possible.
|
|
502
|
+
- **meep** is **same-runtime bit-exact** because `Math.sin/cos/exp/log` are
|
|
503
|
+
ULP-correct but not bit-exact across V8 / SpiderMonkey / JSC. PLAN.md
|
|
504
|
+
is explicit about this. The codebase's typed-array discipline (direct
|
|
505
|
+
index writes, no `Vector3.set` on hot paths, min-heap free list, sorted
|
|
506
|
+
awake-list iteration, canonical pair ordering) is the right
|
|
507
|
+
infrastructure.
|
|
508
|
+
|
|
509
|
+
The action item to close the cross-runtime gap is documented in PLAN.md
|
|
510
|
+
(soft-float library for transcendentals). Not a gap in scope of this
|
|
511
|
+
review.
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
## 3. In-depth correctness comparison on six key touchpoints
|
|
516
|
+
|
|
517
|
+
### 3.1 Warm-start impulse correctness across reordered contacts
|
|
518
|
+
|
|
519
|
+
**meep** `solver/solve_contacts.js:467-476`:
|
|
520
|
+
|
|
521
|
+
```js
|
|
522
|
+
const j_n = data[off + 10];
|
|
523
|
+
const j_t1 = data[off + 11];
|
|
524
|
+
const j_t2 = data[off + 12];
|
|
525
|
+
const Px = nx * j_n + t1x * j_t1 + t2x * j_t2;
|
|
526
|
+
// ... apply ...
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
The warm-start impulses live at fixed slot offsets `(slot, k)` in the
|
|
530
|
+
manifold cache. The new frame's narrowphase calls `manifolds.clear_contacts(slot)`
|
|
531
|
+
(at `narrowphase_step.js:687`), which **zeroes the data array including
|
|
532
|
+
`j_n`, `j_t1`, `j_t2`** (see `ManifoldStore.js:233-239`):
|
|
533
|
+
|
|
534
|
+
```js
|
|
535
|
+
clear_contacts(slot) {
|
|
536
|
+
const meta_off = slot * SLOT_META_STRIDE;
|
|
537
|
+
this.__meta[meta_off + 2] = this.__meta[meta_off + 2] & ~COUNT_MASK;
|
|
538
|
+
const data_off = slot * SLOT_DATA_STRIDE;
|
|
539
|
+
this.__data.fill(0, data_off, data_off + SLOT_DATA_STRIDE);
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**Bug:** the docstring at `ManifoldStore.js:230` claims "narrowphase
|
|
544
|
+
determines the pair is separated this frame" but `narrowphase_step.js:687`
|
|
545
|
+
calls it **unconditionally** at the start of every narrowphase pass for
|
|
546
|
+
every pair, before the new contacts are computed. So warm-start
|
|
547
|
+
impulses are actively **destroyed on every frame**, every contact, every
|
|
548
|
+
pair. That's exactly the opposite of what warm-starting requires.
|
|
549
|
+
|
|
550
|
+
Walk-through:
|
|
551
|
+
- Frame N: pair has contact[0] with `j_n = 5.2`. End of frame: solver has
|
|
552
|
+
applied impulses, data[10] = 5.2.
|
|
553
|
+
- Frame N+1: `acquire()` is a no-op (slot exists, just sets touched).
|
|
554
|
+
- Frame N+1 narrowphase: `clear_contacts(slot)` runs — **5.2 is wiped to 0**.
|
|
555
|
+
- Frame N+1 contacts written via `set_contact`, which **does not touch
|
|
556
|
+
data[10..12]** (per its own docstring at `ManifoldStore.js:270` "warm-start
|
|
557
|
+
impulses are preserved") — but they were already zeroed by `clear_contacts`.
|
|
558
|
+
- Solver pre-step at `solve_contacts.js:468-470` reads `j_n=0, j_t1=0, j_t2=0` →
|
|
559
|
+
no warm-start impulse applied.
|
|
560
|
+
|
|
561
|
+
**The warm-start cache is effectively cold every frame.** This is a real
|
|
562
|
+
bug — the comment at `ManifoldStore.js:269` "warm-start impulses are
|
|
563
|
+
preserved across calls" describes intended behaviour that the
|
|
564
|
+
`clear_contacts → set_contact` sequence in narrowphase actively breaks.
|
|
565
|
+
|
|
566
|
+
Even if `clear_contacts` were fixed to preserve `j_n/j_t1/j_t2`, the
|
|
567
|
+
**ordering instability** from §2.5 is the deeper issue: contacts[0]
|
|
568
|
+
this frame is not contact[0] last frame for any non-trivial pair. Need
|
|
569
|
+
feature-ID or position matching.
|
|
570
|
+
|
|
571
|
+
**Improvement opportunity:**
|
|
572
|
+
1. Make `clear_contacts` either skip the j_n/j_t1/j_t2 stride elements, or
|
|
573
|
+
accept a flag for "narrowphase contact replacement" vs. "pair separated".
|
|
574
|
+
2. Implement feature-id or position matching at the contact-write site
|
|
575
|
+
(`narrowphase_step.js:691-700`) — when writing the new contact, find
|
|
576
|
+
the most-similar previous contact in this slot and **inherit its
|
|
577
|
+
warm-start impulses**.
|
|
578
|
+
|
|
579
|
+
### 3.2 Box-box manifold reduction non-determinism vs. Parry's clipping
|
|
580
|
+
|
|
581
|
+
**meep** `box_box_manifold.js:486-531`: reduce candidates to 4 by
|
|
582
|
+
"deepest, then max-distance-to-kept-set" — a perimeter-maximisation
|
|
583
|
+
heuristic. The ordering depends on numerical tiebreaks in
|
|
584
|
+
`d2 < min_d2` comparisons. For symmetric inputs (two axis-aligned
|
|
585
|
+
unit cubes at integer positions), this can produce different orderings
|
|
586
|
+
in two otherwise-identical PhysicsSystem instances if floating-point
|
|
587
|
+
arithmetic happens to differ by 1 ULP somewhere upstream.
|
|
588
|
+
|
|
589
|
+
**Parry** `contact_manifolds_cuboid_cuboid.rs`: clipping produces
|
|
590
|
+
**at most 4** candidates by construction (face × face Sutherland-Hodgman
|
|
591
|
+
on a rectangle gives ≤ 8 corners; the four "deepest" are kept by a
|
|
592
|
+
**feature-ID-based selection**, not a metric). Stable orderings.
|
|
593
|
+
|
|
594
|
+
Our reduction also doesn't guarantee that the **deepest 4** are kept —
|
|
595
|
+
it keeps deepest #1, then maximises spread. A cluster of three deep
|
|
596
|
+
contacts plus one shallow but spread-out contact will lose one of the
|
|
597
|
+
deep ones. For a flat box-on-floor with one corner slightly higher,
|
|
598
|
+
that's the right behaviour. For a tilted box where the corner contacts
|
|
599
|
+
matter, it can drop important contacts.
|
|
600
|
+
|
|
601
|
+
**Improvement opportunity:** mirror Parry's feature-ID-based 4-of-8 pick
|
|
602
|
+
(each corner of the clipped polygon carries the source edge and incident
|
|
603
|
+
corner id; pick the four corners that span the largest convex hull).
|
|
604
|
+
|
|
605
|
+
### 3.3 EPA on smooth supports — confirmed degeneration
|
|
606
|
+
|
|
607
|
+
**meep** `expanding_polytope_algorithm.js:255-263`:
|
|
608
|
+
|
|
609
|
+
```js
|
|
610
|
+
if (num_loose_edges >= EPA_MAX_NUM_LOOSE_EDGES) {
|
|
611
|
+
// Polytope degenerated ...
|
|
612
|
+
// Bail out and return the closest-face result accumulated so far ...
|
|
613
|
+
break;
|
|
614
|
+
}
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
The fallback path returns the **last closest face's normal × `dot_p_search_dir`**
|
|
618
|
+
as the depth — but `dot_p_search_dir` was computed from a new support point
|
|
619
|
+
that the polytope-update loop just **discarded** (because it can't fit it
|
|
620
|
+
into the polytope without exceeding the cap). That's a mismatch: the depth
|
|
621
|
+
is the projection of a vertex that isn't on the polytope, while the
|
|
622
|
+
direction is the polytope's closest-face normal. The two should be consistent.
|
|
623
|
+
|
|
624
|
+
**Parry** `epa3.rs`: scale-adaptive tolerance + 100-iteration cap; when
|
|
625
|
+
it bails, it returns the closest face's barycentric-projected point, which
|
|
626
|
+
is by construction a Minkowski-difference point. Output is a true MTV.
|
|
627
|
+
|
|
628
|
+
**Improvement opportunity:**
|
|
629
|
+
1. On the EPA bail path, return `closest_face_normal * min_dist` (the
|
|
630
|
+
stored `min_dist` for the chosen closest face), not
|
|
631
|
+
`normal * dot_p_search_dir`. `min_dist` is the actual signed distance
|
|
632
|
+
from origin to the closest face plane, which is the right MTV magnitude.
|
|
633
|
+
2. Scale `EPA_TOLERANCE` by `max(scale_of_coordinates, 1.0)` — same fix
|
|
634
|
+
Parry applied for issue #415. The coordinate scale is the max of the
|
|
635
|
+
incoming simplex's coordinate magnitudes.
|
|
636
|
+
3. Remove the leftover `debugger;` at `expanding_polytope_algorithm.js:136`.
|
|
637
|
+
|
|
638
|
+
### 3.4 Concave dispatch: face-normal rejection + concave-vs-concave
|
|
639
|
+
|
|
640
|
+
**meep** `narrowphase_step.js:519`:
|
|
641
|
+
|
|
642
|
+
```js
|
|
643
|
+
if (ex * fnx_w + ey * fny_w + ez * fnz_w <= 0) continue;
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
This is the "convex on outward side of triangle" test. **Correctness
|
|
647
|
+
concern:** for a sphere centred slightly above a flat heightmap, the
|
|
648
|
+
sphere-vs-triangle GJK+EPA may report an MTV that's near-perpendicular to
|
|
649
|
+
the face (good — `fnx_w · ex > 0`), but for cases where the sphere is
|
|
650
|
+
overlapping two adjacent triangles (one slanted, one horizontal), the
|
|
651
|
+
sphere-vs-slanted-triangle's EPA can return an MTV that points *into*
|
|
652
|
+
the slanted face. The rejection then skips that triangle, leaving only
|
|
653
|
+
the horizontal-face contact — which is correct here. But:
|
|
654
|
+
|
|
655
|
+
The face-normal rejection runs **after** sign-canonicalisation against the
|
|
656
|
+
triangle centroid → convex centre vector. For a thin shape (capsule
|
|
657
|
+
oriented along the slope), the centroid-to-centre vector might be near-
|
|
658
|
+
parallel to the face, and the sign canonicalisation flips inconsistently
|
|
659
|
+
between adjacent triangles, producing alternating contact directions
|
|
660
|
+
across the heightmap. The `compute_penetration.js` half-space pre-test
|
|
661
|
+
sidesteps this; the narrowphase doesn't.
|
|
662
|
+
|
|
663
|
+
**Improvement opportunity:** for primitive-vs-triangle, use the
|
|
664
|
+
half-space approach (project sphere centre / box corner / capsule
|
|
665
|
+
endpoints onto the triangle plane) like `compute_penetration.js` does.
|
|
666
|
+
This is roughly half the work of a closed-form ball-vs-triangle and
|
|
667
|
+
already validated in our codebase. The full closed-form (§2.8) is
|
|
668
|
+
strictly better — see backlog.
|
|
669
|
+
|
|
670
|
+
### 3.5 GJK iteration tolerance and convergence detection
|
|
671
|
+
|
|
672
|
+
**meep** `gjk.js:96-128`: 64-iter cap, **no progress check between
|
|
673
|
+
iterations**. The only termination is "origin enclosed in tetrahedron"
|
|
674
|
+
(return true) or "new support doesn't pass the origin" (return false).
|
|
675
|
+
Non-progressing inputs (degenerate Minkowski difference at exactly
|
|
676
|
+
touching, smooth-on-smooth tangent contact) run to 64 iterations and
|
|
677
|
+
return false.
|
|
678
|
+
|
|
679
|
+
**Parry** `gjk.rs`: progress check — if the new support's signed distance
|
|
680
|
+
to the previous closest-feature plane improves by less than `eps`, declare
|
|
681
|
+
convergence. Faster termination on near-touching pairs (sphere tangent to
|
|
682
|
+
sphere); same false return on actual non-overlap but with fewer
|
|
683
|
+
iterations.
|
|
684
|
+
|
|
685
|
+
**Improvement opportunity:** add a progress check on the new support
|
|
686
|
+
point's projection along the search direction. If `dot(new_support, dir)`
|
|
687
|
+
is within tolerance of the previous iteration's value, declare convergence.
|
|
688
|
+
On the practical level this affects performance (smooth-on-smooth) more
|
|
689
|
+
than correctness (we already return false on the iteration cap), but
|
|
690
|
+
falling out earlier on tangent contact is a real perf+stability win for
|
|
691
|
+
the broadphase-edge case.
|
|
692
|
+
|
|
693
|
+
### 3.6 Shape-cast bisection vs. GJK conservative advancement
|
|
694
|
+
|
|
695
|
+
**meep** `queries/shape_cast.js:267-297`: AABB slab interval narrowing +
|
|
696
|
+
32-step coarse linear search + up to 32 bisection steps + MPR normal
|
|
697
|
+
recovery.
|
|
698
|
+
|
|
699
|
+
**Parry** `shape_cast_support_map_support_map.rs` → `gjk::directional_distance`:
|
|
700
|
+
**continuous refinement** in Minkowski space, simultaneously tightening
|
|
701
|
+
both bounds. Converges in ~5-15 iterations on typical inputs because each
|
|
702
|
+
iteration uses geometric structure (the support hyperplane at the current
|
|
703
|
+
ray position) rather than blind bisection.
|
|
704
|
+
|
|
705
|
+
Trade-offs:
|
|
706
|
+
- Bisection is **simpler and dead-reliable** — no degenerate cases.
|
|
707
|
+
- Conservative advancement is **substantially fewer GJK calls** —
|
|
708
|
+
Parry's typical TOI query is in the 10s of microseconds; ours is
|
|
709
|
+
64-step bisection per candidate.
|
|
710
|
+
|
|
711
|
+
For our workload (kinematic character controllers — a few queries per
|
|
712
|
+
frame), the perf delta is small. **Not a high-priority change** but
|
|
713
|
+
worth knowing about. The normal recovery via MPR probe-step is fine
|
|
714
|
+
(`shape_cast.js:350-381`); Parry's TOI returns the contact normal as
|
|
715
|
+
part of the same conservative-advancement loop, so there's no separate
|
|
716
|
+
probe needed.
|
|
717
|
+
|
|
718
|
+
### 3.7 Friction: tangent disk vs. Coulomb pyramid
|
|
719
|
+
|
|
720
|
+
**meep** `solver/friction_cone.js` (called from `solve_contacts.js:569`):
|
|
721
|
+
two-tangent disk clamp — limits `sqrt(t1² + t2²) ≤ μ·j_n`. Coulomb cone
|
|
722
|
+
in the velocity-impulse plane.
|
|
723
|
+
|
|
724
|
+
**Parry**: looks similar in the `ContactConstraintTangentPart::solve` —
|
|
725
|
+
Vector2 impulse clamped against `limit = mu * normal_part.impulse`.
|
|
726
|
+
Functionally the same.
|
|
727
|
+
|
|
728
|
+
No actionable difference; mark as parity.
|
|
729
|
+
|
|
730
|
+
### 3.8 Body-centre application point fallback in GJK+EPA path
|
|
731
|
+
|
|
732
|
+
**meep** `narrowphase_step.js:640-644`:
|
|
733
|
+
|
|
734
|
+
```js
|
|
735
|
+
return append_contact(count,
|
|
736
|
+
trA.position.x, trA.position.y, trA.position.z,
|
|
737
|
+
trB.position.x, trB.position.y, trB.position.z,
|
|
738
|
+
nx, ny, nz,
|
|
739
|
+
depth);
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
The fallback contact application points are **body centres**, with the
|
|
743
|
+
detailed explanation in the inline comment (`narrowphase_step.js:623-639`)
|
|
744
|
+
about flat-face support being multi-valued. This is correct in spirit but
|
|
745
|
+
not in detail: for a tilted box on a flat floor with the GJK+EPA path
|
|
746
|
+
(e.g. a non-cube convex hull on a heightmap triangle), the contact
|
|
747
|
+
"point" sitting at the floor's centre rather than under the box produces
|
|
748
|
+
the wrong torque arm. Specifically `r × n` is large and in the wrong
|
|
749
|
+
direction. The Baumgarte position correction gets dampened by the large
|
|
750
|
+
effective mass denominator and the box separates slowly.
|
|
751
|
+
|
|
752
|
+
**Parry** computes contact points as **support-function witnesses**: for
|
|
753
|
+
each shape, the support point in the contact normal direction is the
|
|
754
|
+
contact application point. For flat-face shapes the witness is one of
|
|
755
|
+
the (possibly-multi-valued) corners, and Parry deals with the
|
|
756
|
+
multi-valued case by **picking a face when the support is multi-valued
|
|
757
|
+
in the support direction**. Their `support_face` returns a
|
|
758
|
+
`PolygonalFeature` rather than a point.
|
|
759
|
+
|
|
760
|
+
**Improvement opportunity:** in the GJK+EPA fallback path, compute the
|
|
761
|
+
contact point as `(posed_a.support(epa_dir) + posed_b.support(-epa_dir)) / 2`
|
|
762
|
+
**modulo a check for flat-face support**: if `|support_a - centre_a|` or
|
|
763
|
+
`|support_b - centre_b|` is suspiciously large relative to the shape's
|
|
764
|
+
characteristic length, fall back to body centres. This narrows the body-
|
|
765
|
+
centre fallback to its actually-correct domain (flat-faced shapes where
|
|
766
|
+
the support is multi-valued) instead of using it for everything.
|
|
767
|
+
|
|
768
|
+
---
|
|
769
|
+
|
|
770
|
+
## 4. Simplicity and uniformity
|
|
771
|
+
|
|
772
|
+
### 4.1 What's clean
|
|
773
|
+
|
|
774
|
+
- **`BodyStorage`** (`body/BodyStorage.js`): SoA, generation packing,
|
|
775
|
+
deterministic free-list. Every primitive operation is one block of
|
|
776
|
+
index arithmetic. Hard to make simpler without losing functionality.
|
|
777
|
+
- **`PairUint32Map`**: Robin Hood hashing in a single typed-array. No
|
|
778
|
+
allocation in steady state.
|
|
779
|
+
- **`union_find.js`**: path halving in 8 lines. Minimal code.
|
|
780
|
+
- **`integrate_velocity.js`**: 80 lines, no surprises, well-commented on
|
|
781
|
+
the implicit-decay damping recipe.
|
|
782
|
+
- **`compute_fat_world_aabb.js`**: 60 lines, single-purpose, all constants
|
|
783
|
+
named.
|
|
784
|
+
|
|
785
|
+
### 4.2 What's specialised in ways that earn their keep
|
|
786
|
+
|
|
787
|
+
- **The two-BVH split** (`PhysicsSystem.staticBvh` + `dynamicBvh`): cheap
|
|
788
|
+
cognitively, real perf win for the "mostly-static world" target. Static
|
|
789
|
+
tree is built once and never touched.
|
|
790
|
+
- **Atomic-island sleep with circular doubly-linked sleep groups**: the
|
|
791
|
+
alternative ("rebuild from scratch") would be much simpler but loses
|
|
792
|
+
the property that a hit at the base of a 100-stack wakes the whole
|
|
793
|
+
stack in one frame.
|
|
794
|
+
- **Per-island CSR layout from `IslandBuilder`**: enables solver,
|
|
795
|
+
sleep-test, and event-dispatch to all iterate the same data structure.
|
|
796
|
+
Genuine reuse.
|
|
797
|
+
- **`Triangle3D` flyweight + `triangle_buffer` reuse in the concave
|
|
798
|
+
dispatch**: zero allocation per triangle. The alternative (allocating
|
|
799
|
+
triangle instances) would be a hotspot.
|
|
800
|
+
|
|
801
|
+
### 4.3 What's specialised in ways that don't earn their keep
|
|
802
|
+
|
|
803
|
+
- **The narrowphase dispatcher** in `narrowphase_step.js:192-353` is a big
|
|
804
|
+
if-else chain on `isUnitSphereShape3D`, `isBoxShape3D`, etc., with
|
|
805
|
+
per-pair flipping of A/B identity (`isSphereA ? trA : trB` etc.). It's
|
|
806
|
+
legible but doesn't scale gracefully — adding cylinder support is
|
|
807
|
+
another ~50 lines of switch arms. Parry's approach is a **shape-type
|
|
808
|
+
pair → dispatch function** lookup, which is one indirection but
|
|
809
|
+
trivially extensible. A small registry table indexed by
|
|
810
|
+
`(shape_a_type << 8) | shape_b_type` would replace the entire chain
|
|
811
|
+
with one indirection. (NB: the type-tag identification via
|
|
812
|
+
`isUnitSphereShape3D === true` works in our JS world but the registry
|
|
813
|
+
generalises trivially.)
|
|
814
|
+
- **Quaternion conjugate-rotate-conjugate boilerplate** in 5+ files
|
|
815
|
+
(`sphere_box_contact.js:48-57`, `capsule_contacts.js:39-71`,
|
|
816
|
+
`narrowphase_step.js:467-475`, `PosedShape.js:67-93`). Each
|
|
817
|
+
reimplements the same `v_world = q · v_local · q*` arithmetic inline.
|
|
818
|
+
Extracting a single `quaternion_apply` and trusting V8 to inline it
|
|
819
|
+
would clean the code at no cost. Parry has `Isometry` for exactly this.
|
|
820
|
+
- **EPA's `loose_edges` array** (`expanding_polytope_algorithm.js:41`):
|
|
821
|
+
the `copyWithin` based edge removal scales poorly past ~32 edges and
|
|
822
|
+
the wrapping logic is fragile. Parry's adjacency-graph EPA is harder
|
|
823
|
+
to write but more robust. Worth keeping the current implementation
|
|
824
|
+
until it bites.
|
|
825
|
+
|
|
826
|
+
### 4.4 Adding a new shape pair / constraint / query
|
|
827
|
+
|
|
828
|
+
- **New shape pair (e.g. cylinder-box):** add a closed-form handler in
|
|
829
|
+
`narrowphase/cylinder_box_contact.js`, add an `isCylinderShape3D` type
|
|
830
|
+
tag, add a branch in `narrowphase_step.js` dispatch. ~3 files,
|
|
831
|
+
~100-200 lines, mechanical. Parry's equivalent is also mechanical but
|
|
832
|
+
more strongly typed.
|
|
833
|
+
- **New constraint (e.g. distance joint):** would need to thread `joints`
|
|
834
|
+
through the island builder + solver. PLAN.md notes "the solver loop is
|
|
835
|
+
already set up to iterate contacts ∪ joints; only constraint pre-step +
|
|
836
|
+
warm-start hook is missing." This is structurally easier than Parry
|
|
837
|
+
because we already iterate per-island.
|
|
838
|
+
- **New query (e.g. sphere-cast):** `queries/sphere_cast.js` next to
|
|
839
|
+
`shape_cast.js` — but `shape_cast` is already generic, so this would
|
|
840
|
+
be a thin wrapper. Parry has both `cast_ray` and `cast_shape` for the
|
|
841
|
+
same reason.
|
|
842
|
+
|
|
843
|
+
### 4.5 Recommended structural simplifications
|
|
844
|
+
|
|
845
|
+
1. **Extract a `quaternion_apply(out, off, vx, vy, vz, qx, qy, qz, qw)`
|
|
846
|
+
helper** in `core/geom/3d/quaternion/`. Replace every inline copy in
|
|
847
|
+
the narrowphase.
|
|
848
|
+
2. **Replace the narrowphase dispatcher's if-else chain with a small
|
|
849
|
+
typed dispatch table.** Adding shapes becomes a one-line entry.
|
|
850
|
+
3. **Move warm-start impulse persistence out of the
|
|
851
|
+
`clear_contacts` path** (§3.1) — narrowphase calls a different method
|
|
852
|
+
`replace_contacts(slot, new_count)` that leaves j_n/j_t1/j_t2 alone,
|
|
853
|
+
and `clear_contacts` continues to be the "this pair has separated"
|
|
854
|
+
reset.
|
|
855
|
+
4. **Centralise contact application-point selection** (§3.8) — a single
|
|
856
|
+
`contact_point_for_pair(shapeA, shapeB, epa_dir)` that knows when to
|
|
857
|
+
use witnesses vs. body centres, rather than scattering the heuristic
|
|
858
|
+
across narrowphase paths.
|
|
859
|
+
|
|
860
|
+
---
|
|
861
|
+
|
|
862
|
+
## 5. Headline findings, ranked
|
|
863
|
+
|
|
864
|
+
1. **Warm-start cache is effectively cold every frame** (§3.1) due to
|
|
865
|
+
`clear_contacts(slot)` running unconditionally at the start of every
|
|
866
|
+
narrowphase pass. Real bug. Cheap fix.
|
|
867
|
+
2. **Contact persistence lacks feature-id matching** (§2.5). When the
|
|
868
|
+
narrowphase emits manifold contacts in a different order this frame
|
|
869
|
+
vs. last, warm-start impulses apply to wrong contact points. Parry's
|
|
870
|
+
`TrackedContact { fid1, fid2, data }` is the model.
|
|
871
|
+
3. **Solver is structurally locked out of TGS by combined Baumgarte+
|
|
872
|
+
restitution bias in `rhs`** (§2.6). The Rapier `rhs` vs `rhs_wo_bias`
|
|
873
|
+
split + impulse_accumulator architecture defuses the three failure
|
|
874
|
+
modes documented in PLAN.md. Significant work but unblocks the
|
|
875
|
+
biggest performance roadmap item.
|
|
876
|
+
4. **Closed-form primitive-vs-triangle missing** (§2.8). PLAN.md flags as
|
|
877
|
+
the single biggest accuracy gap. Parry's `contact_manifolds_cuboid_triangle.rs`
|
|
878
|
+
+ a Voronoi-region ball-vs-triangle are the port targets.
|
|
879
|
+
5. **EPA scale-adaptive tolerance and consistent fallback** (§3.3). The
|
|
880
|
+
`EPA_TOLERANCE = 0.0001` is an absolute constant; should scale by
|
|
881
|
+
coordinate magnitude (Parry issue #415's fix). On the EPA bail path,
|
|
882
|
+
return `min_dist * face_normal`, not the inconsistent
|
|
883
|
+
`dot_p_search_dir * face_normal`. Also drop the leftover `debugger;`.
|
|
884
|
+
6. **Concave-side per-triangle GJK+EPA cannot converge on degenerate
|
|
885
|
+
`Triangle3D` support** (§3.4). Already documented in PLAN.md. The
|
|
886
|
+
sphere-vs-triangle closed-form is the smallest first step (Voronoi
|
|
887
|
+
region clamp) and removes a class of skipped tests.
|
|
888
|
+
7. **MPR is not wired into the narrowphase as an EPA fallback**.
|
|
889
|
+
PLAN.md correctly identifies this — the swap is in `narrowphase_step.js:580-644`
|
|
890
|
+
(replace the EPA call with `if (mpr(...)) ... else if (epa(...))` or
|
|
891
|
+
prefer MPR on smooth-shape pairs). The code already exists.
|
|
892
|
+
8. **Box-box reduction can drop deep contacts in favour of spread**
|
|
893
|
+
(§3.2). Mirror Parry's feature-ID-based 4-of-8 corner pick on the
|
|
894
|
+
clipped polygon.
|
|
895
|
+
9. **GJK lacks progress-based convergence detection** (§3.5). Adds a
|
|
896
|
+
single dot-product check per iteration; speeds up tangent contacts
|
|
897
|
+
and provides earlier-out on degenerate Minkowski differences.
|
|
898
|
+
10. **CCD is speculative-margin-only**. Already in backlog. Rapier's
|
|
899
|
+
`predict_impacts_at_next_positions → clamp_motions` two-phase TOI
|
|
900
|
+
sweep is the architecture; our `shape_cast.js` already has the TOI
|
|
901
|
+
primitive.
|
|
902
|
+
|
|
903
|
+
---
|
|
904
|
+
|
|
905
|
+
## Appendix A: file-by-file Rapier/Parry references
|
|
906
|
+
|
|
907
|
+
| meep file | Rapier/Parry counterpart |
|
|
908
|
+
|---|---|
|
|
909
|
+
| `ecs/PhysicsSystem.js` | `rapier/src/pipeline/physics_pipeline.rs` |
|
|
910
|
+
| `body/BodyStorage.js` | `rapier/src/dynamics/rigid_body_set.rs` |
|
|
911
|
+
| `broadphase/generate_pairs.js` | `rapier/src/geometry/broad_phase_bvh.rs` |
|
|
912
|
+
| `broadphase/compute_fat_world_aabb.js` | (inline in `broad_phase_bvh.rs` refit logic) |
|
|
913
|
+
| `contact/ManifoldStore.js` | `parry/src/query/contact_manifolds/contact_manifold.rs` |
|
|
914
|
+
| `events/diff_manifolds.js` | `rapier/src/pipeline/event_handler.rs` |
|
|
915
|
+
| `island/IslandBuilder.js` | `rapier/src/dynamics/island_manager/manager.rs` |
|
|
916
|
+
| `island/union_find.js` | (Rapier uses DFS, not union-find) |
|
|
917
|
+
| `gjk/gjk.js` | `parry/src/query/gjk/gjk.rs` (`VoronoiSimplex` based) |
|
|
918
|
+
| `gjk/expanding_polytope_algorithm.js` | `parry/src/query/epa/epa3.rs` |
|
|
919
|
+
| `gjk/mpr.js` | `parry/src/query/mpr/...` |
|
|
920
|
+
| `narrowphase/sphere_sphere_contact.js` | `parry/src/query/contact/contact_ball_ball.rs` |
|
|
921
|
+
| `narrowphase/sphere_box_contact.js` | `parry/src/query/contact_manifolds/contact_manifolds_convex_ball.rs` |
|
|
922
|
+
| `narrowphase/capsule_contacts.js` | `parry/src/query/contact_manifolds/contact_manifolds_capsule_capsule.rs`, `..._cuboid_capsule.rs` |
|
|
923
|
+
| `narrowphase/box_box_manifold.js` | `parry/src/query/contact_manifolds/contact_manifolds_cuboid_cuboid.rs` |
|
|
924
|
+
| `narrowphase/narrowphase_step.js` (concave path) | `parry/src/query/contact_manifolds/contact_manifolds_trimesh_shape.rs` |
|
|
925
|
+
| `narrowphase/decomposition/heightmap_enumerate_triangles.js` | `parry/src/query/contact_manifolds/contact_manifolds_heightfield_shape.rs` |
|
|
926
|
+
| `narrowphase/compute_penetration.js` | `parry/src/query/contact/contact_shape_shape.rs` |
|
|
927
|
+
| `solver/solve_contacts.js` | `rapier/src/dynamics/solver/velocity_solver.rs` + `rapier/src/dynamics/solver/contact_constraint/contact_constraint_element.rs` |
|
|
928
|
+
| `solver/friction_cone.js` | (inline in `ContactConstraintTangentPart::solve`) |
|
|
929
|
+
| `integration/integrate_velocity.js` | `rapier/src/dynamics/solver/velocity_solver.rs` (between-substep integration) |
|
|
930
|
+
| `integration/integrate_position.js` | `rapier/src/dynamics/solver/velocity_solver.rs` `integrate_positions` |
|
|
931
|
+
| `inertia/world_inverse_inertia.js` | `rapier/src/dynamics/solver/...` (inline in constraint setup) |
|
|
932
|
+
| `queries/raycast.js` | `parry/src/query/ray/...` |
|
|
933
|
+
| `queries/shape_cast.js` | `parry/src/query/shape_cast/shape_cast_support_map_support_map.rs` (uses GJK conservative advancement) |
|
|
934
|
+
| `queries/overlap_shape.js` | `parry/src/query/intersection_test/...` |
|