@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,1422 @@
|
|
|
1
|
+
import { aabb3_transform_oriented } from "../../../core/geom/3d/aabb/aabb3_transform_oriented.js";
|
|
2
|
+
import { Triangle3D } from "../../../core/geom/3d/shape/Triangle3D.js";
|
|
3
|
+
import { body_id_index } from "../body/BodyStorage.js";
|
|
4
|
+
import { CONTACT_STRIDE, MAX_CONTACTS_PER_MANIFOLD } from "../contact/ManifoldStore.js";
|
|
5
|
+
import { expanding_polytope_algorithm } from "../gjk/expanding_polytope_algorithm.js";
|
|
6
|
+
import { gjk_with_axis } from "../gjk/gjk.js";
|
|
7
|
+
import { mpr } from "../gjk/mpr.js";
|
|
8
|
+
import { box_box_manifold, BOX_BOX_OUT_LENGTH } from "./box_box_manifold.js";
|
|
9
|
+
import { box_triangle_contact, BOX_TRIANGLE_OUT_LENGTH } from "./box_triangle_contact.js";
|
|
10
|
+
import {
|
|
11
|
+
CAPSULE_BOX_CONTACT_STRIDE,
|
|
12
|
+
CAPSULE_BOX_MAX_CONTACTS,
|
|
13
|
+
capsule_box_multi_contacts,
|
|
14
|
+
capsule_capsule_contact,
|
|
15
|
+
capsule_sphere_contact,
|
|
16
|
+
} from "./capsule_contacts.js";
|
|
17
|
+
import {
|
|
18
|
+
capsule_triangle_contact,
|
|
19
|
+
CAPSULE_TRIANGLE_CONTACT_STRIDE,
|
|
20
|
+
CAPSULE_TRIANGLE_MAX_CONTACTS,
|
|
21
|
+
} from "./capsule_triangle_contact.js";
|
|
22
|
+
import { aabb_world_to_local } from "./decomposition/aabb_world_to_local.js";
|
|
23
|
+
import { decompose_to_triangles } from "./decomposition/decompose_to_triangles.js";
|
|
24
|
+
import { TRIANGLE_FLOAT_STRIDE } from "./decomposition/triangle_buffer_layout.js";
|
|
25
|
+
import { PosedShape } from "./PosedShape.js";
|
|
26
|
+
import { sphere_box_contact } from "./sphere_box_contact.js";
|
|
27
|
+
import { sphere_sphere_contact } from "./sphere_sphere_contact.js";
|
|
28
|
+
import { sphere_triangle_contact } from "./sphere_triangle_contact.js";
|
|
29
|
+
|
|
30
|
+
const posed_a = new PosedShape();
|
|
31
|
+
const posed_b = new PosedShape();
|
|
32
|
+
|
|
33
|
+
// Float64 simplex buffer. The simplex stores Minkowski-difference
|
|
34
|
+
// support points that GJK accumulates across up to 64 iterations and
|
|
35
|
+
// then hands to EPA for further refinement; precision loss here
|
|
36
|
+
// compounds through every subsequent dot / cross / normal calculation
|
|
37
|
+
// in the polytope expansion, so keep the full 53-bit mantissa.
|
|
38
|
+
const simplex_buf = new Float64Array(12);
|
|
39
|
+
const simplex_a = simplex_buf.subarray(0, 3);
|
|
40
|
+
const simplex_b = simplex_buf.subarray(3, 6);
|
|
41
|
+
const simplex_c = simplex_buf.subarray(6, 9);
|
|
42
|
+
const simplex_d = simplex_buf.subarray(9, 12);
|
|
43
|
+
|
|
44
|
+
const epa_result = new Float64Array(3);
|
|
45
|
+
const sphere_result = new Float64Array(4);
|
|
46
|
+
const closed_form_result = new Float64Array(10);
|
|
47
|
+
const sphere_triangle_result = new Float64Array(10);
|
|
48
|
+
const box_triangle_result = new Float64Array(BOX_TRIANGLE_OUT_LENGTH);
|
|
49
|
+
const capsule_triangle_result = new Float64Array(CAPSULE_TRIANGLE_MAX_CONTACTS * CAPSULE_TRIANGLE_CONTACT_STRIDE);
|
|
50
|
+
const box_manifold_result = new Float64Array(BOX_BOX_OUT_LENGTH);
|
|
51
|
+
const capsule_box_multi_result = new Float64Array(CAPSULE_BOX_MAX_CONTACTS * CAPSULE_BOX_CONTACT_STRIDE);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Candidate-contact stride: wax, way, waz, wbx, wby, wbz, nx, ny, nz, depth,
|
|
55
|
+
* feature_id.
|
|
56
|
+
*
|
|
57
|
+
* The `feature_id` (offset 10) is a stable cross-frame identifier of the
|
|
58
|
+
* geometric feature pair that produced this contact — used by the
|
|
59
|
+
* match-and-merge pass in {@link narrowphase_step} to carry warm-start
|
|
60
|
+
* impulses from the previous frame's manifold to the slot index that
|
|
61
|
+
* corresponds to the same physical contact. A value of 0 means
|
|
62
|
+
* "no feature info, fall back to position matching".
|
|
63
|
+
*
|
|
64
|
+
* @type {number}
|
|
65
|
+
*/
|
|
66
|
+
const CANDIDATE_STRIDE = 11;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Maximum number of contacts emitted into the per-pair manifold after the
|
|
70
|
+
* reduction step. Mirrors {@link MAX_CONTACTS_PER_MANIFOLD} in ManifoldStore.
|
|
71
|
+
* @type {number}
|
|
72
|
+
*/
|
|
73
|
+
const MAX_KEPT = MAX_CONTACTS_PER_MANIFOLD;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Position-fallback tolerance for warm-start matching: when a candidate
|
|
77
|
+
* contact has no feature id (or none of the previous-frame contacts shares
|
|
78
|
+
* its id), match by closest world_a within this 3-D distance.
|
|
79
|
+
*
|
|
80
|
+
* 2 cm matches the original PLAN.md spec for "Persistent manifold cache"
|
|
81
|
+
* — generous enough to follow small inter-frame contact migration on
|
|
82
|
+
* curved surfaces, tight enough that distinct contacts on a single
|
|
83
|
+
* manifold (typically >5 cm apart) don't get confused with each other.
|
|
84
|
+
*
|
|
85
|
+
* @type {number}
|
|
86
|
+
*/
|
|
87
|
+
const MATCH_TOL_SQR = 0.02 * 0.02;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Scratch for the previous-frame contact snapshot taken at the top of the
|
|
91
|
+
* match-and-merge pass. Sized for {@link MAX_CONTACTS_PER_MANIFOLD}
|
|
92
|
+
* contacts, 7 floats per contact:
|
|
93
|
+
* 0 : feature_id
|
|
94
|
+
* 1, 2, 3 : world_a x, y, z (for position-fallback matching)
|
|
95
|
+
* 4, 5, 6 : j_n, j_t1, j_t2 (carried forward to the matched candidate)
|
|
96
|
+
*
|
|
97
|
+
* Snapshotting upfront decouples the read (from the slot's previous state)
|
|
98
|
+
* from the write (the new contact data + impulse copy), avoiding the
|
|
99
|
+
* read-after-write hazard when the matching mapping shuffles indices.
|
|
100
|
+
*
|
|
101
|
+
* @type {Float64Array}
|
|
102
|
+
*/
|
|
103
|
+
const prev_snapshot = new Float64Array(MAX_CONTACTS_PER_MANIFOLD * 7);
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Per-prev-contact "already claimed by a candidate" flag.
|
|
107
|
+
* @type {Uint8Array}
|
|
108
|
+
*/
|
|
109
|
+
const prev_claimed = new Uint8Array(MAX_CONTACTS_PER_MANIFOLD);
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* For each kept candidate, the matched prev-contact index in
|
|
113
|
+
* `[0, prev_count)` or `-1` if no match.
|
|
114
|
+
* @type {Int32Array}
|
|
115
|
+
*/
|
|
116
|
+
const cand_to_prev = new Int32Array(MAX_KEPT);
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Per body-pair scratch buffer for candidate contacts produced by the
|
|
120
|
+
* cross-product of A's colliders × B's colliders. Sized generously for
|
|
121
|
+
* typical compound bodies (each collider-pair contributes 1..4 contacts;
|
|
122
|
+
* 64 covers up to 4 colliders per body × 4 colliders × 4 contacts = 64).
|
|
123
|
+
* @type {Float64Array}
|
|
124
|
+
*/
|
|
125
|
+
const candidates = new Float64Array(64 * CANDIDATE_STRIDE);
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Maximum triangles a concave-side enumerator can produce per pair.
|
|
129
|
+
* The query AABB is bounded by the broadphase's fattened envelope of
|
|
130
|
+
* the convex-side body, so a single concave-vs-convex pair typically
|
|
131
|
+
* yields tens of triangles, not thousands. 1024 is the safety cap.
|
|
132
|
+
*
|
|
133
|
+
* If an enumerator's output would exceed this, the extra triangles are
|
|
134
|
+
* silently dropped by the enumerator's bounds-check on the output
|
|
135
|
+
* array — the worst case is a missed contact on a far edge of the
|
|
136
|
+
* filtered region, which the next-step rebroadphase corrects.
|
|
137
|
+
*
|
|
138
|
+
* @type {number}
|
|
139
|
+
*/
|
|
140
|
+
const MAX_TRIANGLES_PER_PAIR = 1024;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Per-pair scratch for the concave-side triangle decomposition.
|
|
144
|
+
* @type {Float64Array}
|
|
145
|
+
*/
|
|
146
|
+
const triangle_buffer = new Float64Array(MAX_TRIANGLES_PER_PAIR * TRIANGLE_FLOAT_STRIDE);
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Flyweight triangle shape — rebound to each successive triangle slice
|
|
150
|
+
* of `triangle_buffer` during the concave-side dispatch loop. Zero
|
|
151
|
+
* allocation per triangle.
|
|
152
|
+
* @type {Triangle3D}
|
|
153
|
+
*/
|
|
154
|
+
const triangle_shape = new Triangle3D();
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Scratch AABB buffers used only by the concave-side dispatch:
|
|
158
|
+
* - `concave_local_aabb` : convex shape's local AABB (input to oriented transform)
|
|
159
|
+
* - `concave_world_aabb` : convex shape's world AABB
|
|
160
|
+
* - `concave_query_aabb` : convex shape's AABB projected into concave's body-local frame
|
|
161
|
+
* (what the triangle enumerator filters against)
|
|
162
|
+
*/
|
|
163
|
+
const concave_local_aabb = new Float64Array(6);
|
|
164
|
+
const concave_world_aabb = new Float64Array(6);
|
|
165
|
+
const concave_query_aabb = new Float64Array(6);
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Append one contact to the candidate buffer. Returns the new count.
|
|
169
|
+
*
|
|
170
|
+
* @param {number} count
|
|
171
|
+
* @param {number} wax
|
|
172
|
+
* @param {number} way
|
|
173
|
+
* @param {number} waz
|
|
174
|
+
* @param {number} wbx
|
|
175
|
+
* @param {number} wby
|
|
176
|
+
* @param {number} wbz
|
|
177
|
+
* @param {number} nx
|
|
178
|
+
* @param {number} ny
|
|
179
|
+
* @param {number} nz
|
|
180
|
+
* @param {number} depth
|
|
181
|
+
* @param {number} feature_id stable cross-frame ID for warm-start matching;
|
|
182
|
+
* `0` means no info — match-and-merge will fall back to position.
|
|
183
|
+
* @returns {number}
|
|
184
|
+
*/
|
|
185
|
+
function append_contact(count, wax, way, waz, wbx, wby, wbz, nx, ny, nz, depth, feature_id) {
|
|
186
|
+
if (count * CANDIDATE_STRIDE >= candidates.length){
|
|
187
|
+
return count;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const off = count * CANDIDATE_STRIDE;
|
|
191
|
+
|
|
192
|
+
candidates[off] = wax; candidates[off + 1] = way; candidates[off + 2] = waz;
|
|
193
|
+
candidates[off + 3] = wbx; candidates[off + 4] = wby; candidates[off + 5] = wbz;
|
|
194
|
+
candidates[off + 6] = nx; candidates[off + 7] = ny; candidates[off + 8] = nz;
|
|
195
|
+
candidates[off + 9] = depth;
|
|
196
|
+
candidates[off + 10] = feature_id;
|
|
197
|
+
|
|
198
|
+
return count + 1;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function swap_candidate(i, j) {
|
|
202
|
+
if (i === j){
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const oi = i * CANDIDATE_STRIDE;
|
|
207
|
+
const oj = j * CANDIDATE_STRIDE;
|
|
208
|
+
|
|
209
|
+
for (let k = 0; k < CANDIDATE_STRIDE; k++) {
|
|
210
|
+
const t = candidates[oi + k];
|
|
211
|
+
candidates[oi + k] = candidates[oj + k];
|
|
212
|
+
candidates[oj + k] = t;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Compute a stable voronoi-region feature id for a sphere-vs-box contact.
|
|
218
|
+
* Returns one of 27 values in `[1, 27]` based on which of the 27 voronoi
|
|
219
|
+
* regions of the box the sphere centre lies in (8 corners + 12 edges + 6
|
|
220
|
+
* faces + 1 interior). Stable across frames as long as the sphere stays
|
|
221
|
+
* in the same region — the typical case in steady-state contact.
|
|
222
|
+
*
|
|
223
|
+
* Encodes:
|
|
224
|
+
* bucket_x ∈ {0, 1, 2} = sign(lx) bucket in box-local frame
|
|
225
|
+
* bucket_y, bucket_z similarly
|
|
226
|
+
* fid = 1 + bucket_x + 3·bucket_y + 9·bucket_z → 1..27
|
|
227
|
+
*
|
|
228
|
+
* The "+1" offset ensures the result is never 0, since 0 is reserved
|
|
229
|
+
* for "no feature info, use position-fallback".
|
|
230
|
+
*
|
|
231
|
+
* @param {number} sx sphere centre x
|
|
232
|
+
* @param {number} sy
|
|
233
|
+
* @param {number} sz
|
|
234
|
+
* @param {number} bx box centre x
|
|
235
|
+
* @param {number} by
|
|
236
|
+
* @param {number} bz
|
|
237
|
+
* @param {number} bqx box quaternion x
|
|
238
|
+
* @param {number} bqy
|
|
239
|
+
* @param {number} bqz
|
|
240
|
+
* @param {number} bqw
|
|
241
|
+
* @param {number} hx box half-extent x
|
|
242
|
+
* @param {number} hy
|
|
243
|
+
* @param {number} hz
|
|
244
|
+
* @returns {number}
|
|
245
|
+
*/
|
|
246
|
+
function sphere_box_voronoi_fid(sx, sy, sz, bx, by, bz, bqx, bqy, bqz, bqw, hx, hy, hz) {
|
|
247
|
+
// Inverse-rotate (sx - bx, sy - by, sz - bz) by the box's quaternion
|
|
248
|
+
// to get the sphere centre in box-local frame. Inlined for the same
|
|
249
|
+
// V8-inliner reason described in PosedShape.support — see
|
|
250
|
+
// core/geom/vec3/v3_quat3_apply_inverse.js for the canonical form.
|
|
251
|
+
const dx = sx - bx;
|
|
252
|
+
const dy = sy - by;
|
|
253
|
+
const dz = sz - bz;
|
|
254
|
+
|
|
255
|
+
const tx0 = bqw * dx - bqy * dz + bqz * dy;
|
|
256
|
+
const ty0 = bqw * dy - bqz * dx + bqx * dz;
|
|
257
|
+
const tz0 = bqw * dz - bqx * dy + bqy * dx;
|
|
258
|
+
const tw0 = bqx * dx + bqy * dy + bqz * dz;
|
|
259
|
+
|
|
260
|
+
const lx = tx0 * bqw + tw0 * bqx + ty0 * bqz - tz0 * bqy;
|
|
261
|
+
const ly = ty0 * bqw + tw0 * bqy + tz0 * bqx - tx0 * bqz;
|
|
262
|
+
const lz = tz0 * bqw + tw0 * bqz + tx0 * bqy - ty0 * bqx;
|
|
263
|
+
|
|
264
|
+
const bx_b = lx <= -hx ? 0 : (lx >= hx ? 2 : 1);
|
|
265
|
+
const by_b = ly <= -hy ? 0 : (ly >= hy ? 2 : 1);
|
|
266
|
+
const bz_b = lz <= -hz ? 0 : (lz >= hz ? 2 : 1);
|
|
267
|
+
|
|
268
|
+
return 1 + bx_b + 3 * by_b + 9 * bz_b;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Reduce an in-place candidate list to at most {@link MAX_KEPT} entries:
|
|
273
|
+
* 1. Move the deepest to slot 0.
|
|
274
|
+
* 2. For each subsequent slot, pick the remaining candidate whose minimum
|
|
275
|
+
* distance to the already-kept set is largest (approximates max-area).
|
|
276
|
+
*
|
|
277
|
+
* @param {number} n
|
|
278
|
+
* @returns {number} kept count, in [0, min(n, MAX_KEPT)]
|
|
279
|
+
*/
|
|
280
|
+
function reduce_candidates(n) {
|
|
281
|
+
if (n <= MAX_KEPT){
|
|
282
|
+
return n;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Move deepest to slot 0.
|
|
286
|
+
let deepest_idx = 0;
|
|
287
|
+
let deepest_val = candidates[9];
|
|
288
|
+
|
|
289
|
+
for (let i = 1; i < n; i++) {
|
|
290
|
+
|
|
291
|
+
const d = candidates[i * CANDIDATE_STRIDE + 9];
|
|
292
|
+
|
|
293
|
+
if (d > deepest_val) {
|
|
294
|
+
deepest_val = d; deepest_idx = i;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
swap_candidate(0, deepest_idx);
|
|
299
|
+
|
|
300
|
+
for (let k = 1; k < MAX_KEPT; k++) {
|
|
301
|
+
|
|
302
|
+
let best_score = -1;
|
|
303
|
+
let best_i = -1;
|
|
304
|
+
|
|
305
|
+
for (let i = k; i < n; i++) {
|
|
306
|
+
let min_d2 = Infinity;
|
|
307
|
+
|
|
308
|
+
for (let j = 0; j < k; j++) {
|
|
309
|
+
|
|
310
|
+
const dx = candidates[i * CANDIDATE_STRIDE] - candidates[j * CANDIDATE_STRIDE];
|
|
311
|
+
const dy = candidates[i * CANDIDATE_STRIDE + 1] - candidates[j * CANDIDATE_STRIDE + 1];
|
|
312
|
+
const dz = candidates[i * CANDIDATE_STRIDE + 2] - candidates[j * CANDIDATE_STRIDE + 2];
|
|
313
|
+
const d2 = dx * dx + dy * dy + dz * dz;
|
|
314
|
+
|
|
315
|
+
if (d2 < min_d2){
|
|
316
|
+
min_d2 = d2;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (min_d2 > best_score) {
|
|
321
|
+
best_score = min_d2;
|
|
322
|
+
best_i = i;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
swap_candidate(k, best_i);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return MAX_KEPT;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Run pairwise narrowphase for one (colliderA, colliderB) tuple — dispatches
|
|
335
|
+
* by shape type and appends 0..K contacts to the candidate buffer. Returns
|
|
336
|
+
* the new candidate count.
|
|
337
|
+
*
|
|
338
|
+
* The optional `gjk_axis_buf` + `gjk_axis_off` arguments thread the
|
|
339
|
+
* manifold's cached separating axis through to the GJK + EPA fallback
|
|
340
|
+
* paths. Closed-form paths (sphere/box/capsule × sphere/box/capsule
|
|
341
|
+
* and the three concave fast-paths) don't use GJK and ignore them. As
|
|
342
|
+
* of P2.1 step 4 these are plumbed but not yet activated — the GJK
|
|
343
|
+
* call sites still use the cache-less `gjk(simplex, A, B)`; step 5
|
|
344
|
+
* flips one of them.
|
|
345
|
+
*
|
|
346
|
+
* @param {number} count
|
|
347
|
+
* @param {Collider} colA
|
|
348
|
+
* @param {Transform} trA
|
|
349
|
+
* @param {Collider} colB
|
|
350
|
+
* @param {Transform} trB
|
|
351
|
+
* @param {Float64Array|null} [gjk_axis_buf] per-slot cached-axis buffer
|
|
352
|
+
* (`manifolds.slot_axis_buffer`) or `null` for cold-start every call.
|
|
353
|
+
* @param {number} [gjk_axis_off] offset into the buffer; pass
|
|
354
|
+
* `manifolds.slot_axis_offset(slot)`.
|
|
355
|
+
* @returns {number}
|
|
356
|
+
*/
|
|
357
|
+
function dispatch_pair(count, colA, trA, colB, trB, gjk_axis_buf = null, gjk_axis_off = 0) {
|
|
358
|
+
const shapeA = colA.shape;
|
|
359
|
+
const shapeB = colB.shape;
|
|
360
|
+
|
|
361
|
+
const isSphereA = shapeA.isUnitSphereShape3D === true;
|
|
362
|
+
const isSphereB = shapeB.isUnitSphereShape3D === true;
|
|
363
|
+
// isBoxShape3D covers both UnitCubeShape3D (fixed 0.5) and BoxShape3D
|
|
364
|
+
// (arbitrary half-extents). Both expose `half_extents` as a Vector3.
|
|
365
|
+
const isBoxA = shapeA.isBoxShape3D === true;
|
|
366
|
+
const isBoxB = shapeB.isBoxShape3D === true;
|
|
367
|
+
const isCapsuleA = shapeA.isCapsuleShape3D === true;
|
|
368
|
+
const isCapsuleB = shapeB.isCapsuleShape3D === true;
|
|
369
|
+
|
|
370
|
+
// sphere-sphere
|
|
371
|
+
if (isSphereA && isSphereB) {
|
|
372
|
+
|
|
373
|
+
const ok = sphere_sphere_contact(
|
|
374
|
+
sphere_result,
|
|
375
|
+
trA.position.x, trA.position.y, trA.position.z,
|
|
376
|
+
trB.position.x, trB.position.y, trB.position.z,
|
|
377
|
+
1, 1
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
if (!ok) return count;
|
|
381
|
+
|
|
382
|
+
const nx = sphere_result[0], ny = sphere_result[1], nz = sphere_result[2];
|
|
383
|
+
|
|
384
|
+
// Sphere-sphere produces exactly one contact per pair; fid = 1
|
|
385
|
+
// identifies it as a real feature (distinguishes from "no info" = 0)
|
|
386
|
+
// and is trivially stable across frames.
|
|
387
|
+
return append_contact(count,
|
|
388
|
+
trA.position.x - nx, trA.position.y - ny, trA.position.z - nz,
|
|
389
|
+
trB.position.x + nx, trB.position.y + ny, trB.position.z + nz,
|
|
390
|
+
nx, ny, nz, sphere_result[3], 1);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// sphere ↔ box
|
|
394
|
+
if ((isSphereA && isBoxB) || (isBoxA && isSphereB)) {
|
|
395
|
+
const sphereTr = isSphereA ? trA : trB;
|
|
396
|
+
const boxTr = isSphereA ? trB : trA;
|
|
397
|
+
const boxShape = isSphereA ? shapeB : shapeA;
|
|
398
|
+
const bh = boxShape.half_extents;
|
|
399
|
+
|
|
400
|
+
const ok = sphere_box_contact(
|
|
401
|
+
closed_form_result,
|
|
402
|
+
sphereTr.position.x, sphereTr.position.y, sphereTr.position.z, 1,
|
|
403
|
+
boxTr.position.x, boxTr.position.y, boxTr.position.z,
|
|
404
|
+
boxTr.rotation.x, boxTr.rotation.y, boxTr.rotation.z, boxTr.rotation.w,
|
|
405
|
+
bh.x, bh.y, bh.z
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
if (!ok) return count;
|
|
409
|
+
|
|
410
|
+
let nx = closed_form_result[0], ny = closed_form_result[1], nz = closed_form_result[2];
|
|
411
|
+
|
|
412
|
+
const depth = closed_form_result[3];
|
|
413
|
+
const wsx = closed_form_result[4], wsy = closed_form_result[5], wsz = closed_form_result[6];
|
|
414
|
+
const wbx = closed_form_result[7], wby = closed_form_result[8], wbz = closed_form_result[9];
|
|
415
|
+
// Feature id from the box-local voronoi region (1..27) — stable
|
|
416
|
+
// while the sphere stays on the same face / edge / vertex of the
|
|
417
|
+
// box, which is the steady-state case for resting / sliding contact.
|
|
418
|
+
|
|
419
|
+
const fid = sphere_box_voronoi_fid(
|
|
420
|
+
sphereTr.position.x, sphereTr.position.y, sphereTr.position.z,
|
|
421
|
+
boxTr.position.x, boxTr.position.y, boxTr.position.z,
|
|
422
|
+
boxTr.rotation.x, boxTr.rotation.y, boxTr.rotation.z, boxTr.rotation.w,
|
|
423
|
+
bh.x, bh.y, bh.z
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
if (isSphereA) {
|
|
427
|
+
return append_contact(count, wsx, wsy, wsz, wbx, wby, wbz, nx, ny, nz, depth, fid);
|
|
428
|
+
} else {
|
|
429
|
+
return append_contact(count, wbx, wby, wbz, wsx, wsy, wsz, -nx, -ny, -nz, depth, fid);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// box-box multi-point
|
|
434
|
+
if (isBoxA && isBoxB) {
|
|
435
|
+
const ah = shapeA.half_extents;
|
|
436
|
+
const bh = shapeB.half_extents;
|
|
437
|
+
const ok = box_box_manifold(
|
|
438
|
+
box_manifold_result,
|
|
439
|
+
trA.position.x, trA.position.y, trA.position.z,
|
|
440
|
+
trA.rotation.x, trA.rotation.y, trA.rotation.z, trA.rotation.w,
|
|
441
|
+
ah.x, ah.y, ah.z,
|
|
442
|
+
trB.position.x, trB.position.y, trB.position.z,
|
|
443
|
+
trB.rotation.x, trB.rotation.y, trB.rotation.z, trB.rotation.w,
|
|
444
|
+
bh.x, bh.y, bh.z
|
|
445
|
+
);
|
|
446
|
+
if (!ok) return count;
|
|
447
|
+
const nx = box_manifold_result[0], ny = box_manifold_result[1], nz = box_manifold_result[2];
|
|
448
|
+
const cc = box_manifold_result[3] | 0;
|
|
449
|
+
// Box-box manifolds: closed-form clipping doesn't expose stable
|
|
450
|
+
// per-contact feature ids (a contact migrates from "incident-vertex k"
|
|
451
|
+
// to "clip-intersection on edge j" as the boxes rotate). Leave
|
|
452
|
+
// fid = 0 so the match-and-merge pass uses position-fallback —
|
|
453
|
+
// the per-contact clipped points are spread by face geometry and
|
|
454
|
+
// typically stay >>MATCH_TOL apart, so position matching is
|
|
455
|
+
// unambiguous frame-to-frame.
|
|
456
|
+
for (let k = 0; k < cc; k++) {
|
|
457
|
+
const base = 4 + k * 7;
|
|
458
|
+
count = append_contact(count,
|
|
459
|
+
box_manifold_result[base], box_manifold_result[base + 1], box_manifold_result[base + 2],
|
|
460
|
+
box_manifold_result[base + 3], box_manifold_result[base + 4], box_manifold_result[base + 5],
|
|
461
|
+
nx, ny, nz,
|
|
462
|
+
box_manifold_result[base + 6],
|
|
463
|
+
0);
|
|
464
|
+
}
|
|
465
|
+
return count;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// capsule-capsule
|
|
469
|
+
if (isCapsuleA && isCapsuleB) {
|
|
470
|
+
const a_shape = colA.shape, b_shape = colB.shape;
|
|
471
|
+
const ok = capsule_capsule_contact(
|
|
472
|
+
closed_form_result,
|
|
473
|
+
trA.position.x, trA.position.y, trA.position.z,
|
|
474
|
+
trA.rotation.x, trA.rotation.y, trA.rotation.z, trA.rotation.w,
|
|
475
|
+
a_shape.radius, a_shape.height * 0.5,
|
|
476
|
+
trB.position.x, trB.position.y, trB.position.z,
|
|
477
|
+
trB.rotation.x, trB.rotation.y, trB.rotation.z, trB.rotation.w,
|
|
478
|
+
b_shape.radius, b_shape.height * 0.5
|
|
479
|
+
);
|
|
480
|
+
if (!ok) return count;
|
|
481
|
+
// Single contact per capsule-capsule pair; fid = 1 (stable, real feature).
|
|
482
|
+
return append_contact(count,
|
|
483
|
+
closed_form_result[4], closed_form_result[5], closed_form_result[6],
|
|
484
|
+
closed_form_result[7], closed_form_result[8], closed_form_result[9],
|
|
485
|
+
closed_form_result[0], closed_form_result[1], closed_form_result[2],
|
|
486
|
+
closed_form_result[3], 1);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// capsule ↔ sphere
|
|
490
|
+
if ((isCapsuleA && isSphereB) || (isSphereA && isCapsuleB)) {
|
|
491
|
+
const capsuleTr = isCapsuleA ? trA : trB;
|
|
492
|
+
const capsuleShape = isCapsuleA ? colA.shape : colB.shape;
|
|
493
|
+
const sphereTr = isCapsuleA ? trB : trA;
|
|
494
|
+
const ok = capsule_sphere_contact(
|
|
495
|
+
closed_form_result,
|
|
496
|
+
capsuleTr.position.x, capsuleTr.position.y, capsuleTr.position.z,
|
|
497
|
+
capsuleTr.rotation.x, capsuleTr.rotation.y, capsuleTr.rotation.z, capsuleTr.rotation.w,
|
|
498
|
+
capsuleShape.radius, capsuleShape.height * 0.5,
|
|
499
|
+
sphereTr.position.x, sphereTr.position.y, sphereTr.position.z, 1
|
|
500
|
+
);
|
|
501
|
+
if (!ok) return count;
|
|
502
|
+
let nx = closed_form_result[0], ny = closed_form_result[1], nz = closed_form_result[2];
|
|
503
|
+
const depth = closed_form_result[3];
|
|
504
|
+
const cap_x = closed_form_result[4], cap_y = closed_form_result[5], cap_z = closed_form_result[6];
|
|
505
|
+
const sph_x = closed_form_result[7], sph_y = closed_form_result[8], sph_z = closed_form_result[9];
|
|
506
|
+
// Single contact per capsule-sphere pair; fid = 1.
|
|
507
|
+
if (isCapsuleA) {
|
|
508
|
+
return append_contact(count, cap_x, cap_y, cap_z, sph_x, sph_y, sph_z, nx, ny, nz, depth, 1);
|
|
509
|
+
} else {
|
|
510
|
+
return append_contact(count, sph_x, sph_y, sph_z, cap_x, cap_y, cap_z, -nx, -ny, -nz, depth, 1);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// capsule ↔ box (multi-point — 1 closest-segment + up to 2 cap-centres)
|
|
515
|
+
if ((isCapsuleA && isBoxB) || (isBoxA && isCapsuleB)) {
|
|
516
|
+
const capsuleTr = isCapsuleA ? trA : trB;
|
|
517
|
+
const capsuleShape = isCapsuleA ? shapeA : shapeB;
|
|
518
|
+
const boxTr = isCapsuleA ? trB : trA;
|
|
519
|
+
const boxShape = isCapsuleA ? shapeB : shapeA;
|
|
520
|
+
const bh = boxShape.half_extents;
|
|
521
|
+
const cc = capsule_box_multi_contacts(
|
|
522
|
+
capsule_box_multi_result,
|
|
523
|
+
capsuleTr.position.x, capsuleTr.position.y, capsuleTr.position.z,
|
|
524
|
+
capsuleTr.rotation.x, capsuleTr.rotation.y, capsuleTr.rotation.z, capsuleTr.rotation.w,
|
|
525
|
+
capsuleShape.radius, capsuleShape.height * 0.5,
|
|
526
|
+
boxTr.position.x, boxTr.position.y, boxTr.position.z,
|
|
527
|
+
boxTr.rotation.x, boxTr.rotation.y, boxTr.rotation.z, boxTr.rotation.w,
|
|
528
|
+
bh.x, bh.y, bh.z
|
|
529
|
+
);
|
|
530
|
+
if (cc === 0) return count;
|
|
531
|
+
// multi_result layout per contact: cap_x/y/z (A side), box_x/y/z (B side), nx/ny/nz, depth.
|
|
532
|
+
// Feature id per sub-contact: 1 = closest-segment, 2/3 = caps. The
|
|
533
|
+
// emission order from capsule_box_multi_contacts is stable across
|
|
534
|
+
// frames for the same geometric configuration.
|
|
535
|
+
for (let k = 0; k < cc; k++) {
|
|
536
|
+
const o = k * CAPSULE_BOX_CONTACT_STRIDE;
|
|
537
|
+
const cap_x = capsule_box_multi_result[o];
|
|
538
|
+
const cap_y = capsule_box_multi_result[o + 1];
|
|
539
|
+
const cap_z = capsule_box_multi_result[o + 2];
|
|
540
|
+
const box_x = capsule_box_multi_result[o + 3];
|
|
541
|
+
const box_y = capsule_box_multi_result[o + 4];
|
|
542
|
+
const box_z = capsule_box_multi_result[o + 5];
|
|
543
|
+
const nx = capsule_box_multi_result[o + 6];
|
|
544
|
+
const ny = capsule_box_multi_result[o + 7];
|
|
545
|
+
const nz = capsule_box_multi_result[o + 8];
|
|
546
|
+
const depth = capsule_box_multi_result[o + 9];
|
|
547
|
+
const sub_fid = k + 1;
|
|
548
|
+
if (isCapsuleA) {
|
|
549
|
+
count = append_contact(count, cap_x, cap_y, cap_z, box_x, box_y, box_z, nx, ny, nz, depth, sub_fid);
|
|
550
|
+
} else {
|
|
551
|
+
count = append_contact(count, box_x, box_y, box_z, cap_x, cap_y, cap_z, -nx, -ny, -nz, depth, sub_fid);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return count;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// ── Concave (non-convex) path ───────────────────────────────────────
|
|
558
|
+
//
|
|
559
|
+
// If either shape has `is_convex === false`, GJK on the whole shape
|
|
560
|
+
// produces incorrect results (Minkowski difference is not convex).
|
|
561
|
+
// We decompose the concave side into triangles overlapping the
|
|
562
|
+
// convex side's AABB, then run convex per-triangle GJK + EPA.
|
|
563
|
+
//
|
|
564
|
+
// Concave-vs-concave is intentionally NOT supported in v1: the
|
|
565
|
+
// M×N triangle pairs would dominate runtime, and the physics
|
|
566
|
+
// engine's design contract requires at least one side to be
|
|
567
|
+
// static / kinematic for concave shapes anyway (the broadphase +
|
|
568
|
+
// filter should keep such pairs out of the narrowphase entirely).
|
|
569
|
+
// If one slips through, we skip rather than burn cycles.
|
|
570
|
+
const isConcaveA = shapeA.is_convex === false;
|
|
571
|
+
const isConcaveB = shapeB.is_convex === false;
|
|
572
|
+
if (isConcaveA && isConcaveB) return count;
|
|
573
|
+
if (isConcaveA || isConcaveB) {
|
|
574
|
+
const concave_col = isConcaveA ? colA : colB;
|
|
575
|
+
const concave_tr = isConcaveA ? trA : trB;
|
|
576
|
+
const convex_col = isConcaveA ? colB : colA;
|
|
577
|
+
const convex_tr = isConcaveA ? trB : trA;
|
|
578
|
+
|
|
579
|
+
// 1. Convex shape's world AABB.
|
|
580
|
+
convex_col.shape.compute_bounding_box(concave_local_aabb);
|
|
581
|
+
aabb3_transform_oriented(
|
|
582
|
+
concave_world_aabb, 0,
|
|
583
|
+
concave_local_aabb[0], concave_local_aabb[1], concave_local_aabb[2],
|
|
584
|
+
concave_local_aabb[3], concave_local_aabb[4], concave_local_aabb[5],
|
|
585
|
+
convex_tr.position.x, convex_tr.position.y, convex_tr.position.z,
|
|
586
|
+
convex_tr.rotation.x, convex_tr.rotation.y, convex_tr.rotation.z, convex_tr.rotation.w
|
|
587
|
+
);
|
|
588
|
+
|
|
589
|
+
// 2. Project into concave's body-local frame.
|
|
590
|
+
aabb_world_to_local(
|
|
591
|
+
concave_query_aabb, 0,
|
|
592
|
+
concave_world_aabb,
|
|
593
|
+
concave_tr.position.x, concave_tr.position.y, concave_tr.position.z,
|
|
594
|
+
concave_tr.rotation.x, concave_tr.rotation.y, concave_tr.rotation.z, concave_tr.rotation.w
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
// 3. Decompose concave shape into triangles overlapping the query.
|
|
598
|
+
const tri_count = decompose_to_triangles(
|
|
599
|
+
triangle_buffer, 0, concave_col.shape,
|
|
600
|
+
concave_query_aabb[0], concave_query_aabb[1], concave_query_aabb[2],
|
|
601
|
+
concave_query_aabb[3], concave_query_aabb[4], concave_query_aabb[5]
|
|
602
|
+
);
|
|
603
|
+
if (tri_count === 0) return count;
|
|
604
|
+
|
|
605
|
+
// 4. Set up the convex side once; the concave side gets a
|
|
606
|
+
// Triangle3D rebound to each triangle in the loop. We keep
|
|
607
|
+
// the concave side as our internal "A" so EPA's sign-check
|
|
608
|
+
// convention matches the convex fallback below; the final
|
|
609
|
+
// append_contact() swaps if the original A was the convex one.
|
|
610
|
+
posed_b.setup(convex_col.shape, convex_tr.position, convex_tr.rotation);
|
|
611
|
+
posed_a.shape = triangle_shape;
|
|
612
|
+
posed_a.px = concave_tr.position.x;
|
|
613
|
+
posed_a.py = concave_tr.position.y;
|
|
614
|
+
posed_a.pz = concave_tr.position.z;
|
|
615
|
+
posed_a.qx = concave_tr.rotation.x;
|
|
616
|
+
posed_a.qy = concave_tr.rotation.y;
|
|
617
|
+
posed_a.qz = concave_tr.rotation.z;
|
|
618
|
+
posed_a.qw = concave_tr.rotation.w;
|
|
619
|
+
|
|
620
|
+
// Pre-compute the centre-axis used by EPA's sign-check loop.
|
|
621
|
+
// For convex-vs-convex this is `(B - A) = convex_centre − concave_centre`.
|
|
622
|
+
const convex_wx = convex_tr.position.x;
|
|
623
|
+
const convex_wy = convex_tr.position.y;
|
|
624
|
+
const convex_wz = convex_tr.position.z;
|
|
625
|
+
|
|
626
|
+
// Track the candidate-buffer index at the start of this
|
|
627
|
+
// concave dispatch — the per-triangle dedup pass scans from
|
|
628
|
+
// here to the current count, ignoring contacts from earlier
|
|
629
|
+
// collider pairs in the same body pair.
|
|
630
|
+
const pair_start_count = count;
|
|
631
|
+
|
|
632
|
+
// Pre-compute concave's rotation components for the q · v · q⁻¹
|
|
633
|
+
// rotations done per-triangle below (face normal + centroid).
|
|
634
|
+
const cqx = concave_tr.rotation.x;
|
|
635
|
+
const cqy = concave_tr.rotation.y;
|
|
636
|
+
const cqz = concave_tr.rotation.z;
|
|
637
|
+
const cqw = concave_tr.rotation.w;
|
|
638
|
+
const c_pos_x = concave_tr.position.x;
|
|
639
|
+
const c_pos_y = concave_tr.position.y;
|
|
640
|
+
const c_pos_z = concave_tr.position.z;
|
|
641
|
+
|
|
642
|
+
// Sphere fast-path: when the convex side is a UnitSphereShape3D we
|
|
643
|
+
// bypass GJK+EPA entirely per triangle and use the closed-form
|
|
644
|
+
// {@link sphere_triangle_contact}. This avoids the EPA precision
|
|
645
|
+
// wall on Triangle3D (whose support function is degenerate along
|
|
646
|
+
// the face normal — all 3 vertices project to the same value),
|
|
647
|
+
// which was producing noisy depths at small penetrations and
|
|
648
|
+
// letting dropped spheres tunnel through heightmaps / meshes.
|
|
649
|
+
const isSphereConvex = convex_col.shape.isUnitSphereShape3D === true;
|
|
650
|
+
|
|
651
|
+
// Box fast-path: closed-form {@link box_triangle_contact} via SAT
|
|
652
|
+
// over 13 axes + polygon clipping for face-vs-face contacts.
|
|
653
|
+
// Same motivation as the sphere path — Triangle3D's degenerate
|
|
654
|
+
// face-normal support kills EPA precision and produces noisy
|
|
655
|
+
// depths. The box path uses world-space triangle vertices.
|
|
656
|
+
const isBoxConvex = convex_col.shape.isBoxShape3D === true;
|
|
657
|
+
const box_half_extents = isBoxConvex ? convex_col.shape.half_extents : null;
|
|
658
|
+
|
|
659
|
+
// Capsule fast-path: closed-form {@link capsule_triangle_contact}
|
|
660
|
+
// via segment-vs-triangle closest-point + cap-centre sphere
|
|
661
|
+
// queries for a multi-point manifold. Same motivation as the
|
|
662
|
+
// sphere and box paths.
|
|
663
|
+
const isCapsuleConvex = convex_col.shape.isCapsuleShape3D === true;
|
|
664
|
+
const capsule_shape = isCapsuleConvex ? convex_col.shape : null;
|
|
665
|
+
|
|
666
|
+
for (let i = 0; i < tri_count; i++) {
|
|
667
|
+
const tri_offset = i * TRIANGLE_FLOAT_STRIDE;
|
|
668
|
+
triangle_shape.bind(triangle_buffer, tri_offset);
|
|
669
|
+
|
|
670
|
+
const ax = triangle_buffer[tri_offset ];
|
|
671
|
+
const ay = triangle_buffer[tri_offset + 1];
|
|
672
|
+
const az = triangle_buffer[tri_offset + 2];
|
|
673
|
+
|
|
674
|
+
const bx = triangle_buffer[tri_offset + 3];
|
|
675
|
+
const by = triangle_buffer[tri_offset + 4];
|
|
676
|
+
const bz = triangle_buffer[tri_offset + 5];
|
|
677
|
+
|
|
678
|
+
const cx_v = triangle_buffer[tri_offset + 6];
|
|
679
|
+
const cy_v = triangle_buffer[tri_offset + 7];
|
|
680
|
+
const cz_v = triangle_buffer[tri_offset + 8];
|
|
681
|
+
|
|
682
|
+
// Triangle decomposition emits a stable per-triangle feature_id
|
|
683
|
+
// at offset 9 (TRIANGLE_FEATURE_ID_OFFSET) — same triangle of
|
|
684
|
+
// the same shape gets the same id across frames. This is the
|
|
685
|
+
// gold-standard fid for the match-and-merge pass.
|
|
686
|
+
const tri_fid = triangle_buffer[tri_offset + 9];
|
|
687
|
+
|
|
688
|
+
// Triangle face normal in body-local frame: (B − A) × (C − A).
|
|
689
|
+
// Winding convention (CCW from outside) gives an outward face
|
|
690
|
+
// normal — the heightmap / mesh enumerators both promise this.
|
|
691
|
+
const e1x_l = bx - ax, e1y_l = by - ay, e1z_l = bz - az;
|
|
692
|
+
const e2x_l = cx_v - ax, e2y_l = cy_v - ay, e2z_l = cz_v - az;
|
|
693
|
+
|
|
694
|
+
const fnx_l = e1y_l * e2z_l - e1z_l * e2y_l;
|
|
695
|
+
const fny_l = e1z_l * e2x_l - e1x_l * e2z_l;
|
|
696
|
+
const fnz_l = e1x_l * e2y_l - e1y_l * e2x_l;
|
|
697
|
+
|
|
698
|
+
// Rotate face normal to world via concave's quaternion
|
|
699
|
+
// (q · v · q⁻¹). Inlined for V8 inliner — see PosedShape.support.
|
|
700
|
+
const fnix = cqw * fnx_l + cqy * fnz_l - cqz * fny_l;
|
|
701
|
+
const fniy = cqw * fny_l + cqz * fnx_l - cqx * fnz_l;
|
|
702
|
+
const fniz = cqw * fnz_l + cqx * fny_l - cqy * fnx_l;
|
|
703
|
+
const fniw = -cqx * fnx_l - cqy * fny_l - cqz * fnz_l;
|
|
704
|
+
|
|
705
|
+
const fnx_w = fnix * cqw - fniw * cqx - fniy * cqz + fniz * cqy;
|
|
706
|
+
const fny_w = fniy * cqw - fniw * cqy - fniz * cqx + fnix * cqz;
|
|
707
|
+
const fnz_w = fniz * cqw - fniw * cqz - fnix * cqy + fniy * cqx;
|
|
708
|
+
|
|
709
|
+
// Sphere-vs-triangle closed-form fast-path.
|
|
710
|
+
if (isSphereConvex) {
|
|
711
|
+
|
|
712
|
+
// Rotate each triangle vertex from concave-local to world.
|
|
713
|
+
// Per-vertex cost: ~21 flops × 3 vertices = 63 flops; vs.
|
|
714
|
+
// the GJK + EPA cost we're replacing, this is essentially
|
|
715
|
+
// free.
|
|
716
|
+
// Inlined q · v · q* + translate per vertex — see PosedShape.support.
|
|
717
|
+
const axi = cqw * ax + cqy * az - cqz * ay;
|
|
718
|
+
const ayi = cqw * ay + cqz * ax - cqx * az;
|
|
719
|
+
const azi = cqw * az + cqx * ay - cqy * ax;
|
|
720
|
+
const awi = -cqx * ax - cqy * ay - cqz * az;
|
|
721
|
+
|
|
722
|
+
const ax_w = axi * cqw + awi * -cqx + ayi * -cqz - azi * -cqy + c_pos_x;
|
|
723
|
+
const ay_w = ayi * cqw + awi * -cqy + azi * -cqx - axi * -cqz + c_pos_y;
|
|
724
|
+
const az_w = azi * cqw + awi * -cqz + axi * -cqy - ayi * -cqx + c_pos_z;
|
|
725
|
+
|
|
726
|
+
const bxi = cqw * bx + cqy * bz - cqz * by;
|
|
727
|
+
const byi = cqw * by + cqz * bx - cqx * bz;
|
|
728
|
+
const bzi = cqw * bz + cqx * by - cqy * bx;
|
|
729
|
+
const bwi = -cqx * bx - cqy * by - cqz * bz;
|
|
730
|
+
|
|
731
|
+
const bx_w = bxi * cqw + bwi * -cqx + byi * -cqz - bzi * -cqy + c_pos_x;
|
|
732
|
+
const by_w = byi * cqw + bwi * -cqy + bzi * -cqx - bxi * -cqz + c_pos_y;
|
|
733
|
+
const bz_w = bzi * cqw + bwi * -cqz + bxi * -cqy - byi * -cqx + c_pos_z;
|
|
734
|
+
|
|
735
|
+
const cxi = cqw * cx_v + cqy * cz_v - cqz * cy_v;
|
|
736
|
+
const cyi = cqw * cy_v + cqz * cx_v - cqx * cz_v;
|
|
737
|
+
const czi = cqw * cz_v + cqx * cy_v - cqy * cx_v;
|
|
738
|
+
const cwi = -cqx * cx_v - cqy * cy_v - cqz * cz_v;
|
|
739
|
+
|
|
740
|
+
const cx_w = cxi * cqw + cwi * -cqx + cyi * -cqz - czi * -cqy + c_pos_x;
|
|
741
|
+
const cy_w = cyi * cqw + cwi * -cqy + czi * -cqx - cxi * -cqz + c_pos_y;
|
|
742
|
+
const cz_w = czi * cqw + cwi * -cqz + cxi * -cqy - cyi * -cqx + c_pos_z;
|
|
743
|
+
|
|
744
|
+
const ok = sphere_triangle_contact(
|
|
745
|
+
sphere_triangle_result,
|
|
746
|
+
convex_wx, convex_wy, convex_wz, 1,
|
|
747
|
+
ax_w, ay_w, az_w,
|
|
748
|
+
bx_w, by_w, bz_w,
|
|
749
|
+
cx_w, cy_w, cz_w
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
if (!ok) continue;
|
|
753
|
+
|
|
754
|
+
// sphere_triangle_contact's normal points from the
|
|
755
|
+
// triangle surface toward the sphere centre — same
|
|
756
|
+
// direction as the post-sign-check EPA MTV in the
|
|
757
|
+
// fallback path below (concave A → convex B).
|
|
758
|
+
const sd = sphere_triangle_result[3];
|
|
759
|
+
const n_t2s_x = sphere_triangle_result[0];
|
|
760
|
+
const n_t2s_y = sphere_triangle_result[1];
|
|
761
|
+
const n_t2s_z = sphere_triangle_result[2];
|
|
762
|
+
|
|
763
|
+
// One-sided rejection: the sphere must lie on the
|
|
764
|
+
// outward side of the triangle. If the contact normal
|
|
765
|
+
// opposes the face normal, the sphere is behind /
|
|
766
|
+
// inside the solid — skip rather than push it deeper.
|
|
767
|
+
if (n_t2s_x * fnx_w + n_t2s_y * fny_w + n_t2s_z * fnz_w <= 0) continue;
|
|
768
|
+
|
|
769
|
+
// Stored normal convention is "B → A". `nx, ny, nz` here
|
|
770
|
+
// points convex → concave (which the existing EPA branch
|
|
771
|
+
// also produces just before append). Dedup uses the
|
|
772
|
+
// post-swap stored_n so adjacent-triangle duplicates
|
|
773
|
+
// collapse the same way regardless of code path.
|
|
774
|
+
const nx_s = -n_t2s_x;
|
|
775
|
+
const ny_s = -n_t2s_y;
|
|
776
|
+
const nz_s = -n_t2s_z;
|
|
777
|
+
|
|
778
|
+
const stored_nx_s = isConcaveA ? nx_s : -nx_s;
|
|
779
|
+
const stored_ny_s = isConcaveA ? ny_s : -ny_s;
|
|
780
|
+
const stored_nz_s = isConcaveA ? nz_s : -nz_s;
|
|
781
|
+
let is_duplicate_s = false;
|
|
782
|
+
for (let k = pair_start_count; k < count; k++) {
|
|
783
|
+
const ko = k * CANDIDATE_STRIDE;
|
|
784
|
+
const dnx = candidates[ko + 6] - stored_nx_s;
|
|
785
|
+
const dny = candidates[ko + 7] - stored_ny_s;
|
|
786
|
+
const dnz = candidates[ko + 8] - stored_nz_s;
|
|
787
|
+
if (dnx * dnx + dny * dny + dnz * dnz < 0.001) {
|
|
788
|
+
is_duplicate_s = true;
|
|
789
|
+
break;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
if (is_duplicate_s) continue;
|
|
793
|
+
|
|
794
|
+
// Surface witnesses from the closed-form solver:
|
|
795
|
+
// result[4..6] = sphere surface point (convex side)
|
|
796
|
+
// result[7..9] = triangle closest point (concave side)
|
|
797
|
+
// Use the actual witnesses rather than body centres —
|
|
798
|
+
// unlike the EPA fallback (which uses body centres
|
|
799
|
+
// because flat-faced support witnesses can be arbitrarily
|
|
800
|
+
// far from the contact patch), closest-point-on-triangle
|
|
801
|
+
// is exact and lies on the contact patch.
|
|
802
|
+
const sphere_wx = sphere_triangle_result[4];
|
|
803
|
+
const sphere_wy = sphere_triangle_result[5];
|
|
804
|
+
const sphere_wz = sphere_triangle_result[6];
|
|
805
|
+
const tri_wx = sphere_triangle_result[7];
|
|
806
|
+
const tri_wy = sphere_triangle_result[8];
|
|
807
|
+
const tri_wz = sphere_triangle_result[9];
|
|
808
|
+
if (isConcaveA) {
|
|
809
|
+
count = append_contact(count,
|
|
810
|
+
tri_wx, tri_wy, tri_wz,
|
|
811
|
+
sphere_wx, sphere_wy, sphere_wz,
|
|
812
|
+
nx_s, ny_s, nz_s, sd, tri_fid);
|
|
813
|
+
} else {
|
|
814
|
+
count = append_contact(count,
|
|
815
|
+
sphere_wx, sphere_wy, sphere_wz,
|
|
816
|
+
tri_wx, tri_wy, tri_wz,
|
|
817
|
+
-nx_s, -ny_s, -nz_s, sd, tri_fid);
|
|
818
|
+
}
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// Box-vs-triangle closed-form fast-path.
|
|
823
|
+
if (isBoxConvex) {
|
|
824
|
+
// Rotate each triangle vertex from concave-local to world.
|
|
825
|
+
// Inlined q · v · q* + translate per vertex — see PosedShape.support.
|
|
826
|
+
const axi = cqw * ax + cqy * az - cqz * ay;
|
|
827
|
+
const ayi = cqw * ay + cqz * ax - cqx * az;
|
|
828
|
+
const azi = cqw * az + cqx * ay - cqy * ax;
|
|
829
|
+
const awi = -cqx * ax - cqy * ay - cqz * az;
|
|
830
|
+
|
|
831
|
+
const ax_w = axi * cqw + awi * -cqx + ayi * -cqz - azi * -cqy + c_pos_x;
|
|
832
|
+
const ay_w = ayi * cqw + awi * -cqy + azi * -cqx - axi * -cqz + c_pos_y;
|
|
833
|
+
const az_w = azi * cqw + awi * -cqz + axi * -cqy - ayi * -cqx + c_pos_z;
|
|
834
|
+
|
|
835
|
+
const bxi = cqw * bx + cqy * bz - cqz * by;
|
|
836
|
+
const byi = cqw * by + cqz * bx - cqx * bz;
|
|
837
|
+
const bzi = cqw * bz + cqx * by - cqy * bx;
|
|
838
|
+
const bwi = -cqx * bx - cqy * by - cqz * bz;
|
|
839
|
+
|
|
840
|
+
const bx_w = bxi * cqw + bwi * -cqx + byi * -cqz - bzi * -cqy + c_pos_x;
|
|
841
|
+
const by_w = byi * cqw + bwi * -cqy + bzi * -cqx - bxi * -cqz + c_pos_y;
|
|
842
|
+
const bz_w = bzi * cqw + bwi * -cqz + bxi * -cqy - byi * -cqx + c_pos_z;
|
|
843
|
+
|
|
844
|
+
const cxi = cqw * cx_v + cqy * cz_v - cqz * cy_v;
|
|
845
|
+
const cyi = cqw * cy_v + cqz * cx_v - cqx * cz_v;
|
|
846
|
+
const czi = cqw * cz_v + cqx * cy_v - cqy * cx_v;
|
|
847
|
+
const cwi = -cqx * cx_v - cqy * cy_v - cqz * cz_v;
|
|
848
|
+
const cx_w = cxi * cqw + cwi * -cqx + cyi * -cqz - czi * -cqy + c_pos_x;
|
|
849
|
+
const cy_w = cyi * cqw + cwi * -cqy + czi * -cqx - cxi * -cqz + c_pos_y;
|
|
850
|
+
const cz_w = czi * cqw + cwi * -cqz + cxi * -cqy - cyi * -cqx + c_pos_z;
|
|
851
|
+
|
|
852
|
+
const ok = box_triangle_contact(
|
|
853
|
+
box_triangle_result,
|
|
854
|
+
convex_tr.position.x, convex_tr.position.y, convex_tr.position.z,
|
|
855
|
+
convex_tr.rotation.x, convex_tr.rotation.y, convex_tr.rotation.z, convex_tr.rotation.w,
|
|
856
|
+
box_half_extents.x, box_half_extents.y, box_half_extents.z,
|
|
857
|
+
ax_w, ay_w, az_w,
|
|
858
|
+
bx_w, by_w, bz_w,
|
|
859
|
+
cx_w, cy_w, cz_w
|
|
860
|
+
);
|
|
861
|
+
if (!ok) continue;
|
|
862
|
+
|
|
863
|
+
// Same convention as sphere_triangle: result normal points
|
|
864
|
+
// from triangle surface toward box centre. To match the
|
|
865
|
+
// EPA branch's `nx, ny, nz` (which points convex →
|
|
866
|
+
// concave = box → triangle), negate.
|
|
867
|
+
const n_t2box_x = box_triangle_result[0];
|
|
868
|
+
const n_t2box_y = box_triangle_result[1];
|
|
869
|
+
const n_t2box_z = box_triangle_result[2];
|
|
870
|
+
|
|
871
|
+
// One-sided rejection: the box must lie on the outward
|
|
872
|
+
// side of the triangle. If the contact normal opposes
|
|
873
|
+
// the face normal, the box is inside the solid → skip.
|
|
874
|
+
if (n_t2box_x * fnx_w + n_t2box_y * fny_w + n_t2box_z * fnz_w <= 0) continue;
|
|
875
|
+
|
|
876
|
+
const nx_b = -n_t2box_x;
|
|
877
|
+
const ny_b = -n_t2box_y;
|
|
878
|
+
const nz_b = -n_t2box_z;
|
|
879
|
+
const stored_nx_b = isConcaveA ? nx_b : -nx_b;
|
|
880
|
+
const stored_ny_b = isConcaveA ? ny_b : -ny_b;
|
|
881
|
+
const stored_nz_b = isConcaveA ? nz_b : -nz_b;
|
|
882
|
+
|
|
883
|
+
// Emit each clipped contact. The contact count from
|
|
884
|
+
// box_triangle_contact is at most 4; combined with the
|
|
885
|
+
// existing per-pair candidate cap (64 entries), tens of
|
|
886
|
+
// overlapping triangles per pair are safely accommodated.
|
|
887
|
+
const cc = box_triangle_result[3] | 0;
|
|
888
|
+
for (let k = 0; k < cc; k++) {
|
|
889
|
+
const base = 4 + k * 7;
|
|
890
|
+
const t_wx = box_triangle_result[base];
|
|
891
|
+
const t_wy = box_triangle_result[base + 1];
|
|
892
|
+
const t_wz = box_triangle_result[base + 2];
|
|
893
|
+
const b_wx = box_triangle_result[base + 3];
|
|
894
|
+
const b_wy = box_triangle_result[base + 4];
|
|
895
|
+
const b_wz = box_triangle_result[base + 5];
|
|
896
|
+
const d_k = box_triangle_result[base + 6];
|
|
897
|
+
|
|
898
|
+
// Dedup against earlier candidates in THIS dispatch
|
|
899
|
+
// (adjacent triangles can produce coincident contacts
|
|
900
|
+
// — same as the sphere and EPA paths).
|
|
901
|
+
let is_duplicate_k = false;
|
|
902
|
+
for (let q = pair_start_count; q < count; q++) {
|
|
903
|
+
const qo = q * CANDIDATE_STRIDE;
|
|
904
|
+
const dnx = candidates[qo + 6] - stored_nx_b;
|
|
905
|
+
const dny = candidates[qo + 7] - stored_ny_b;
|
|
906
|
+
const dnz = candidates[qo + 8] - stored_nz_b;
|
|
907
|
+
if (dnx * dnx + dny * dny + dnz * dnz < 0.001) {
|
|
908
|
+
// Same normal; check positions too (a
|
|
909
|
+
// multi-point manifold has distinct points
|
|
910
|
+
// with the same normal — those should NOT
|
|
911
|
+
// be deduped). Compare the world-A side
|
|
912
|
+
// (triangle surface or box surface depending
|
|
913
|
+
// on dispatcher swap).
|
|
914
|
+
const pa_x = isConcaveA ? t_wx : b_wx;
|
|
915
|
+
const pa_y = isConcaveA ? t_wy : b_wy;
|
|
916
|
+
const pa_z = isConcaveA ? t_wz : b_wz;
|
|
917
|
+
const dpx = candidates[qo] - pa_x;
|
|
918
|
+
const dpy = candidates[qo + 1] - pa_y;
|
|
919
|
+
const dpz = candidates[qo + 2] - pa_z;
|
|
920
|
+
if (dpx * dpx + dpy * dpy + dpz * dpz < 1e-6) {
|
|
921
|
+
is_duplicate_k = true;
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
if (is_duplicate_k) continue;
|
|
927
|
+
|
|
928
|
+
if (isConcaveA) {
|
|
929
|
+
count = append_contact(count,
|
|
930
|
+
t_wx, t_wy, t_wz,
|
|
931
|
+
b_wx, b_wy, b_wz,
|
|
932
|
+
nx_b, ny_b, nz_b, d_k, tri_fid);
|
|
933
|
+
} else {
|
|
934
|
+
count = append_contact(count,
|
|
935
|
+
b_wx, b_wy, b_wz,
|
|
936
|
+
t_wx, t_wy, t_wz,
|
|
937
|
+
-nx_b, -ny_b, -nz_b, d_k, tri_fid);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// Capsule-vs-triangle closed-form fast-path.
|
|
944
|
+
if (isCapsuleConvex) {
|
|
945
|
+
// Rotate each triangle vertex from concave-local to world.
|
|
946
|
+
// Inlined q · v · q* + translate per vertex — see PosedShape.support.
|
|
947
|
+
const axi = cqw * ax + cqy * az - cqz * ay;
|
|
948
|
+
const ayi = cqw * ay + cqz * ax - cqx * az;
|
|
949
|
+
const azi = cqw * az + cqx * ay - cqy * ax;
|
|
950
|
+
const awi = -cqx * ax - cqy * ay - cqz * az;
|
|
951
|
+
const ax_w = axi * cqw + awi * -cqx + ayi * -cqz - azi * -cqy + c_pos_x;
|
|
952
|
+
const ay_w = ayi * cqw + awi * -cqy + azi * -cqx - axi * -cqz + c_pos_y;
|
|
953
|
+
const az_w = azi * cqw + awi * -cqz + axi * -cqy - ayi * -cqx + c_pos_z;
|
|
954
|
+
|
|
955
|
+
const bxi = cqw * bx + cqy * bz - cqz * by;
|
|
956
|
+
const byi = cqw * by + cqz * bx - cqx * bz;
|
|
957
|
+
const bzi = cqw * bz + cqx * by - cqy * bx;
|
|
958
|
+
const bwi = -cqx * bx - cqy * by - cqz * bz;
|
|
959
|
+
const bx_w = bxi * cqw + bwi * -cqx + byi * -cqz - bzi * -cqy + c_pos_x;
|
|
960
|
+
const by_w = byi * cqw + bwi * -cqy + bzi * -cqx - bxi * -cqz + c_pos_y;
|
|
961
|
+
const bz_w = bzi * cqw + bwi * -cqz + bxi * -cqy - byi * -cqx + c_pos_z;
|
|
962
|
+
|
|
963
|
+
const cxi = cqw * cx_v + cqy * cz_v - cqz * cy_v;
|
|
964
|
+
const cyi = cqw * cy_v + cqz * cx_v - cqx * cz_v;
|
|
965
|
+
const czi = cqw * cz_v + cqx * cy_v - cqy * cx_v;
|
|
966
|
+
const cwi = -cqx * cx_v - cqy * cy_v - cqz * cz_v;
|
|
967
|
+
const cx_w = cxi * cqw + cwi * -cqx + cyi * -cqz - czi * -cqy + c_pos_x;
|
|
968
|
+
const cy_w = cyi * cqw + cwi * -cqy + czi * -cqx - cxi * -cqz + c_pos_y;
|
|
969
|
+
const cz_w = czi * cqw + cwi * -cqz + cxi * -cqy - cyi * -cqx + c_pos_z;
|
|
970
|
+
|
|
971
|
+
const cap_cc = capsule_triangle_contact(
|
|
972
|
+
capsule_triangle_result,
|
|
973
|
+
convex_tr.position.x, convex_tr.position.y, convex_tr.position.z,
|
|
974
|
+
convex_tr.rotation.x, convex_tr.rotation.y, convex_tr.rotation.z, convex_tr.rotation.w,
|
|
975
|
+
capsule_shape.radius, capsule_shape.height * 0.5,
|
|
976
|
+
ax_w, ay_w, az_w,
|
|
977
|
+
bx_w, by_w, bz_w,
|
|
978
|
+
cx_w, cy_w, cz_w
|
|
979
|
+
);
|
|
980
|
+
if (cap_cc === 0) continue;
|
|
981
|
+
|
|
982
|
+
// Each sub-contact's normal can be distinct (a flat
|
|
983
|
+
// capsule produces primary + endpoint contacts whose
|
|
984
|
+
// normals all point ≈ same direction, but an edge or
|
|
985
|
+
// vertex contact at a cap can deviate). Apply
|
|
986
|
+
// face-normal rejection per-contact.
|
|
987
|
+
for (let k = 0; k < cap_cc; k++) {
|
|
988
|
+
const base = k * CAPSULE_TRIANGLE_CONTACT_STRIDE;
|
|
989
|
+
const cap_x = capsule_triangle_result[base];
|
|
990
|
+
const cap_y = capsule_triangle_result[base + 1];
|
|
991
|
+
const cap_z = capsule_triangle_result[base + 2];
|
|
992
|
+
const ct_x = capsule_triangle_result[base + 3];
|
|
993
|
+
const ct_y = capsule_triangle_result[base + 4];
|
|
994
|
+
const ct_z = capsule_triangle_result[base + 5];
|
|
995
|
+
const n_t2cap_x = capsule_triangle_result[base + 6];
|
|
996
|
+
const n_t2cap_y = capsule_triangle_result[base + 7];
|
|
997
|
+
const n_t2cap_z = capsule_triangle_result[base + 8];
|
|
998
|
+
const d_c = capsule_triangle_result[base + 9];
|
|
999
|
+
|
|
1000
|
+
// One-sided face-normal rejection: capsule must be
|
|
1001
|
+
// on the outward side of the triangle.
|
|
1002
|
+
if (n_t2cap_x * fnx_w + n_t2cap_y * fny_w + n_t2cap_z * fnz_w <= 0) continue;
|
|
1003
|
+
|
|
1004
|
+
// Stored normal "convex → concave" = "capsule →
|
|
1005
|
+
// triangle" = negated t2cap.
|
|
1006
|
+
const nx_c = -n_t2cap_x;
|
|
1007
|
+
const ny_c = -n_t2cap_y;
|
|
1008
|
+
const nz_c = -n_t2cap_z;
|
|
1009
|
+
const stored_nx_c = isConcaveA ? nx_c : -nx_c;
|
|
1010
|
+
const stored_ny_c = isConcaveA ? ny_c : -ny_c;
|
|
1011
|
+
const stored_nz_c = isConcaveA ? nz_c : -nz_c;
|
|
1012
|
+
|
|
1013
|
+
// Dedup against earlier candidates in THIS dispatch.
|
|
1014
|
+
let is_duplicate_c = false;
|
|
1015
|
+
for (let q = pair_start_count; q < count; q++) {
|
|
1016
|
+
const qo = q * CANDIDATE_STRIDE;
|
|
1017
|
+
const dnx = candidates[qo + 6] - stored_nx_c;
|
|
1018
|
+
const dny = candidates[qo + 7] - stored_ny_c;
|
|
1019
|
+
const dnz = candidates[qo + 8] - stored_nz_c;
|
|
1020
|
+
if (dnx * dnx + dny * dny + dnz * dnz < 0.001) {
|
|
1021
|
+
const pa_x = isConcaveA ? ct_x : cap_x;
|
|
1022
|
+
const pa_y = isConcaveA ? ct_y : cap_y;
|
|
1023
|
+
const pa_z = isConcaveA ? ct_z : cap_z;
|
|
1024
|
+
const dpx = candidates[qo] - pa_x;
|
|
1025
|
+
const dpy = candidates[qo + 1] - pa_y;
|
|
1026
|
+
const dpz = candidates[qo + 2] - pa_z;
|
|
1027
|
+
if (dpx * dpx + dpy * dpy + dpz * dpz < 1e-6) {
|
|
1028
|
+
is_duplicate_c = true;
|
|
1029
|
+
break;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
if (is_duplicate_c) continue;
|
|
1034
|
+
|
|
1035
|
+
if (isConcaveA) {
|
|
1036
|
+
count = append_contact(count,
|
|
1037
|
+
ct_x, ct_y, ct_z,
|
|
1038
|
+
cap_x, cap_y, cap_z,
|
|
1039
|
+
nx_c, ny_c, nz_c, d_c, tri_fid);
|
|
1040
|
+
} else {
|
|
1041
|
+
count = append_contact(count,
|
|
1042
|
+
cap_x, cap_y, cap_z,
|
|
1043
|
+
ct_x, ct_y, ct_z,
|
|
1044
|
+
-nx_c, -ny_c, -nz_c, d_c, tri_fid);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// P2.1 step 6: pass the manifold's cached separating axis
|
|
1051
|
+
// here too. Per-triangle iteration means the cache gets
|
|
1052
|
+
// written multiple times per body-pair per frame (one per
|
|
1053
|
+
// overlapping triangle); the final state reflects the
|
|
1054
|
+
// last triangle's converged direction. Still a useful
|
|
1055
|
+
// seed for next frame's first triangle.
|
|
1056
|
+
if (!gjk_with_axis(simplex_buf, posed_a, posed_b, gjk_axis_buf, gjk_axis_off)) continue;
|
|
1057
|
+
expanding_polytope_algorithm(
|
|
1058
|
+
epa_result, 0,
|
|
1059
|
+
simplex_a, simplex_b, simplex_c, simplex_d,
|
|
1060
|
+
posed_a, posed_b
|
|
1061
|
+
);
|
|
1062
|
+
|
|
1063
|
+
let ex = epa_result[0], ey = epa_result[1], ez = epa_result[2];
|
|
1064
|
+
let depth = Math.sqrt(ex * ex + ey * ey + ez * ez);
|
|
1065
|
+
// P3.1: MPR fallback. EPA can return zero/negative/NaN
|
|
1066
|
+
// depth on Triangle3D's degenerate face-normal support
|
|
1067
|
+
// (the same precision pit the closed-form sphere/box/
|
|
1068
|
+
// capsule paths above were added to dodge). MPR has
|
|
1069
|
+
// different convergence properties and often succeeds
|
|
1070
|
+
// here. Only kicks in for non-(sphere/box/capsule) convex
|
|
1071
|
+
// shapes against the concave side — a narrow path now
|
|
1072
|
+
// that closed-form covers the common cases.
|
|
1073
|
+
if (!(depth > 0) || !Number.isFinite(depth)) {
|
|
1074
|
+
if (!mpr(epa_result, 0, posed_a, posed_b)) continue;
|
|
1075
|
+
ex = epa_result[0]; ey = epa_result[1]; ez = epa_result[2];
|
|
1076
|
+
depth = Math.sqrt(ex * ex + ey * ey + ez * ez);
|
|
1077
|
+
if (!(depth > 0) || !Number.isFinite(depth)) continue;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// Sign-check: validate MTV direction against (convex −
|
|
1081
|
+
// triangle_centroid). EPA's polytope-closest-face normal
|
|
1082
|
+
// can come out on the wrong side of the origin; this flip
|
|
1083
|
+
// canonicalises MTV to point from triangle (A) toward the
|
|
1084
|
+
// convex shape (B).
|
|
1085
|
+
const cent_lx = (ax + bx + cx_v) / 3;
|
|
1086
|
+
const cent_ly = (ay + by + cy_v) / 3;
|
|
1087
|
+
const cent_lz = (az + bz + cz_v) / 3;
|
|
1088
|
+
// Inlined q · v · q* + translate.
|
|
1089
|
+
const cix = cqw * cent_lx + cqy * cent_lz - cqz * cent_ly;
|
|
1090
|
+
const ciy = cqw * cent_ly + cqz * cent_lx - cqx * cent_lz;
|
|
1091
|
+
const ciz = cqw * cent_lz + cqx * cent_ly - cqy * cent_lx;
|
|
1092
|
+
const ciw = -cqx * cent_lx - cqy * cent_ly - cqz * cent_lz;
|
|
1093
|
+
const cent_wx = cix * cqw + ciw * -cqx + ciy * -cqz - ciz * -cqy + c_pos_x;
|
|
1094
|
+
const cent_wy = ciy * cqw + ciw * -cqy + ciz * -cqx - cix * -cqz + c_pos_y;
|
|
1095
|
+
const cent_wz = ciz * cqw + ciw * -cqz + cix * -cqy - ciy * -cqx + c_pos_z;
|
|
1096
|
+
|
|
1097
|
+
const tri_ab_x = convex_wx - cent_wx;
|
|
1098
|
+
const tri_ab_y = convex_wy - cent_wy;
|
|
1099
|
+
const tri_ab_z = convex_wz - cent_wz;
|
|
1100
|
+
if (ex * tri_ab_x + ey * tri_ab_y + ez * tri_ab_z < 0) {
|
|
1101
|
+
ex = -ex; ey = -ey; ez = -ez;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// One-sided rejection (post-sign-check). MTV now points
|
|
1105
|
+
// from triangle (A) toward convex shape (B). For a CCW
|
|
1106
|
+
// outward-wound triangle, the convex shape is on the
|
|
1107
|
+
// OUTWARD side iff MTV aligns with the face normal. If it
|
|
1108
|
+
// opposes the face normal, the convex shape is on the
|
|
1109
|
+
// back / inward side — invalid for heightmap/mesh (it's
|
|
1110
|
+
// inside the solid). Skip the triangle rather than push
|
|
1111
|
+
// the body deeper into the solid through the opposite
|
|
1112
|
+
// face on the next step.
|
|
1113
|
+
if (ex * fnx_w + ey * fny_w + ez * fnz_w <= 0) continue;
|
|
1114
|
+
|
|
1115
|
+
// After the validated flip, MTV points from triangle
|
|
1116
|
+
// (concave-side A) into convex (B). Stored normal is
|
|
1117
|
+
// "B → A" so we negate. Final A/B order matches
|
|
1118
|
+
// append_contact's contract: original A first, original B
|
|
1119
|
+
// second, normal "from original B toward original A".
|
|
1120
|
+
const inv = 1 / depth;
|
|
1121
|
+
const nx = -ex * inv;
|
|
1122
|
+
const ny = -ey * inv;
|
|
1123
|
+
const nz = -ez * inv;
|
|
1124
|
+
|
|
1125
|
+
// Dedup against earlier contacts emitted in THIS dispatch.
|
|
1126
|
+
// Adjacent triangles (heightmap cells sharing a diagonal;
|
|
1127
|
+
// mesh triangles sharing an edge or vertex) often report
|
|
1128
|
+
// identical contacts (same normal, same body-centre
|
|
1129
|
+
// application points), and feeding duplicates to the
|
|
1130
|
+
// sequential-impulse solver makes it escalate the impulse
|
|
1131
|
+
// across iterations without bound. We deduplicate by
|
|
1132
|
+
// contact normal within a small angular threshold: same
|
|
1133
|
+
// body pair + same normal + same body-centre positions =
|
|
1134
|
+
// the same physical contact, only one copy belongs in the
|
|
1135
|
+
// manifold.
|
|
1136
|
+
const stored_nx = isConcaveA ? nx : -nx;
|
|
1137
|
+
const stored_ny = isConcaveA ? ny : -ny;
|
|
1138
|
+
const stored_nz = isConcaveA ? nz : -nz;
|
|
1139
|
+
let is_duplicate = false;
|
|
1140
|
+
for (let k = pair_start_count; k < count; k++) {
|
|
1141
|
+
const ko = k * CANDIDATE_STRIDE;
|
|
1142
|
+
const dnx = candidates[ko + 6] - stored_nx;
|
|
1143
|
+
const dny = candidates[ko + 7] - stored_ny;
|
|
1144
|
+
const dnz = candidates[ko + 8] - stored_nz;
|
|
1145
|
+
if (dnx * dnx + dny * dny + dnz * dnz < 0.001) {
|
|
1146
|
+
is_duplicate = true;
|
|
1147
|
+
break;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
if (is_duplicate) continue;
|
|
1151
|
+
|
|
1152
|
+
// Contact application points: body centres rather than
|
|
1153
|
+
// the triangle's centroid. The existing GJK+EPA fallback
|
|
1154
|
+
// (below) uses the same convention — for vertical contacts
|
|
1155
|
+
// (sphere/box on a flat surface, the common case), the
|
|
1156
|
+
// lever arm r × n vanishes and the impulse resolves
|
|
1157
|
+
// cleanly.
|
|
1158
|
+
if (isConcaveA) {
|
|
1159
|
+
count = append_contact(count,
|
|
1160
|
+
c_pos_x, c_pos_y, c_pos_z,
|
|
1161
|
+
convex_wx, convex_wy, convex_wz,
|
|
1162
|
+
nx, ny, nz, depth, tri_fid);
|
|
1163
|
+
} else {
|
|
1164
|
+
count = append_contact(count,
|
|
1165
|
+
convex_wx, convex_wy, convex_wz,
|
|
1166
|
+
c_pos_x, c_pos_y, c_pos_z,
|
|
1167
|
+
-nx, -ny, -nz, depth, tri_fid);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
return count;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
// GJK + EPA fallback for everything else. P2.1 step 5: pass the
|
|
1175
|
+
// manifold's cached separating axis as GJK's initial search
|
|
1176
|
+
// direction. Cold start (zero-axis) on first contact; subsequent
|
|
1177
|
+
// frames reuse the converged direction, typically converging in
|
|
1178
|
+
// 1–2 iterations instead of the ~6–10 cold-start cost.
|
|
1179
|
+
posed_a.setup(colA.shape, trA.position, trA.rotation);
|
|
1180
|
+
posed_b.setup(colB.shape, trB.position, trB.rotation);
|
|
1181
|
+
|
|
1182
|
+
if (!gjk_with_axis(simplex_buf, posed_a, posed_b, gjk_axis_buf, gjk_axis_off)){
|
|
1183
|
+
return count;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
expanding_polytope_algorithm(
|
|
1187
|
+
epa_result, 0,
|
|
1188
|
+
simplex_a, simplex_b, simplex_c, simplex_d,
|
|
1189
|
+
posed_a, posed_b
|
|
1190
|
+
);
|
|
1191
|
+
|
|
1192
|
+
let ex = epa_result[0], ey = epa_result[1], ez = epa_result[2];
|
|
1193
|
+
let depth = Math.sqrt(ex * ex + ey * ey + ez * ez);
|
|
1194
|
+
// EPA can return zero-depth (touching), negative (impossible) or
|
|
1195
|
+
// NaN/Inf (degenerate iteration-cap exit on smooth high-poly
|
|
1196
|
+
// colliders). P3.1: fall back to MPR — different convergence
|
|
1197
|
+
// properties (works portal-based rather than face-expansion), often
|
|
1198
|
+
// succeeds where EPA fails on the same geometry. `shape_cast` and
|
|
1199
|
+
// `compute_penetration` already use MPR for the same reason.
|
|
1200
|
+
if (!(depth > 0) || !Number.isFinite(depth)) {
|
|
1201
|
+
if (!mpr(epa_result, 0, posed_a, posed_b)) return count;
|
|
1202
|
+
ex = epa_result[0]; ey = epa_result[1]; ez = epa_result[2];
|
|
1203
|
+
depth = Math.sqrt(ex * ex + ey * ey + ez * ez);
|
|
1204
|
+
if (!(depth > 0) || !Number.isFinite(depth)) return count;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// EPA's output vector should point along the minimum-translation
|
|
1208
|
+
// axis from A into B (the direction you'd push B by to separate).
|
|
1209
|
+
// On non-convergent EPA — common on smooth high-poly colliders like
|
|
1210
|
+
// a torus knot, where the polytope can't tighten onto the true
|
|
1211
|
+
// closest face within the iteration cap — the fallback returns
|
|
1212
|
+
// *some* face's normal, and that face can be on either side of the
|
|
1213
|
+
// origin. Resulting MTV occasionally points B *into* A instead of
|
|
1214
|
+
// *away from* A; the solver then sees a "separating" relative
|
|
1215
|
+
// velocity, clamps the normal impulse to zero, and the bodies pass
|
|
1216
|
+
// straight through each other.
|
|
1217
|
+
//
|
|
1218
|
+
// Validate against the vector from body A's centre to body B's
|
|
1219
|
+
// centre — EPA's direction must correlate positively with it. Flip
|
|
1220
|
+
// if not. For convex bodies whose origin is inside the geometry,
|
|
1221
|
+
// body-centre-to-body-centre is a strong proxy for the correct
|
|
1222
|
+
// separation direction. For pathological cases (deeply
|
|
1223
|
+
// interpenetrating bodies with coincident centres) the dot product
|
|
1224
|
+
// is near zero and we leave EPA's guess intact.
|
|
1225
|
+
const ab_x = trB.position.x - trA.position.x;
|
|
1226
|
+
const ab_y = trB.position.y - trA.position.y;
|
|
1227
|
+
const ab_z = trB.position.z - trA.position.z;
|
|
1228
|
+
if (ex * ab_x + ey * ab_y + ez * ab_z < 0) {
|
|
1229
|
+
ex = -ex; ey = -ey; ez = -ez;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
const inv = 1 / depth;
|
|
1233
|
+
const nx = -ex * inv, ny = -ey * inv, nz = -ez * inv; // stored normal, B → A
|
|
1234
|
+
|
|
1235
|
+
// Contact application points: body centres rather than support-function
|
|
1236
|
+
// witnesses. The natural-sounding alternative — `posed_a.support(+EPA_dir)`
|
|
1237
|
+
// and `posed_b.support(-EPA_dir)` — interacts badly with flat-faced
|
|
1238
|
+
// supports: a large floor's support in `+Y` returns a *corner* (the box
|
|
1239
|
+
// support is multi-valued along an axis-aligned direction and picks the
|
|
1240
|
+
// sign-tied corner), which can be tens of metres from the actual
|
|
1241
|
+
// contact patch. That blows up the solver's lever arm `r × n` and
|
|
1242
|
+
// collapses `m_eff` to ~0, leaving the impulse vanishingly small.
|
|
1243
|
+
//
|
|
1244
|
+
// Body centres give `r ‖ n` for vertical contacts (sphere/knot landing
|
|
1245
|
+
// on a floor — the common case), so `r × n = 0` and `m_eff = invM`
|
|
1246
|
+
// resolves the impact cleanly. For oblique contacts the application
|
|
1247
|
+
// point lies on the line between centres, which is the right-ish lever
|
|
1248
|
+
// arm for translational impulse and degrades gracefully on angular
|
|
1249
|
+
// response. A future closed-form mesh-vs-box path will compute a
|
|
1250
|
+
// proper face-projected witness; until then, body centres are the
|
|
1251
|
+
// robust fallback for the GJK+EPA route.
|
|
1252
|
+
// GJK+EPA fallback has no stable per-frame feature info; emit fid = 0
|
|
1253
|
+
// so match-and-merge uses position-fallback. Single contact per pair.
|
|
1254
|
+
return append_contact(count,
|
|
1255
|
+
trA.position.x, trA.position.y, trA.position.z,
|
|
1256
|
+
trB.position.x, trB.position.y, trB.position.z,
|
|
1257
|
+
nx, ny, nz,
|
|
1258
|
+
depth, 0
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* For every pair in `pair_list`, do a cross-product over A's collider list ×
|
|
1264
|
+
* B's collider list, accumulate candidate contacts, reduce to ≤4, and write
|
|
1265
|
+
* to the manifold slot.
|
|
1266
|
+
*
|
|
1267
|
+
* @param {PairList} pair_list
|
|
1268
|
+
* @param {ManifoldStore} manifolds
|
|
1269
|
+
* @param {Array<Array<{collider: Collider, transform: Transform}>>} lists
|
|
1270
|
+
* per-body collider lists, indexed by body-storage slot index. Typically
|
|
1271
|
+
* `system.__body_collider_lists` — passed in directly so this helper
|
|
1272
|
+
* has no dependency on `PhysicsSystem`.
|
|
1273
|
+
*/
|
|
1274
|
+
export function narrowphase_step(pair_list, manifolds, lists) {
|
|
1275
|
+
const count = pair_list.count;
|
|
1276
|
+
|
|
1277
|
+
for (let i = 0; i < count; i++) {
|
|
1278
|
+
const idA = pair_list.get_a(i);
|
|
1279
|
+
const idB = pair_list.get_b(i);
|
|
1280
|
+
const idxA = body_id_index(idA);
|
|
1281
|
+
const idxB = body_id_index(idB);
|
|
1282
|
+
|
|
1283
|
+
const list_a = lists[idxA];
|
|
1284
|
+
const list_b = lists[idxB];
|
|
1285
|
+
const slot = manifolds.find(idA, idB);
|
|
1286
|
+
|
|
1287
|
+
if (list_a === undefined || list_b === undefined
|
|
1288
|
+
|| list_a.length === 0 || list_b.length === 0) {
|
|
1289
|
+
manifolds.clear_contacts(slot);
|
|
1290
|
+
continue;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
let cand_count = 0;
|
|
1294
|
+
const la_len = list_a.length;
|
|
1295
|
+
const lb_len = list_b.length;
|
|
1296
|
+
// Per-manifold cached GJK separating-axis seed. Threaded into
|
|
1297
|
+
// dispatch_pair for the GJK fallback paths. For single-collider
|
|
1298
|
+
// bodies (the common case) there's no contention across the
|
|
1299
|
+
// inner loop; for compound bodies the cache reflects whichever
|
|
1300
|
+
// collider-pair invoked GJK last, which is a noisy-but-useful
|
|
1301
|
+
// seed (better than a cold (1, 0, 0) restart every frame).
|
|
1302
|
+
const gjk_axis_buf = manifolds.slot_axis_buffer;
|
|
1303
|
+
const gjk_axis_off = manifolds.slot_axis_offset(slot);
|
|
1304
|
+
for (let a = 0; a < la_len; a++) {
|
|
1305
|
+
const ea = list_a[a];
|
|
1306
|
+
for (let b = 0; b < lb_len; b++) {
|
|
1307
|
+
const eb = list_b[b];
|
|
1308
|
+
cand_count = dispatch_pair(cand_count, ea.collider, ea.transform, eb.collider, eb.transform,
|
|
1309
|
+
gjk_axis_buf, gjk_axis_off);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
if (cand_count === 0) {
|
|
1314
|
+
// No contacts this frame for an existing manifold. Keep the
|
|
1315
|
+
// slot in the cache (the grace window in advance_frame() will
|
|
1316
|
+
// evict it if this persists), but zero out impulses and
|
|
1317
|
+
// contact count — there's nothing for the solver to act on
|
|
1318
|
+
// and stale impulses would mislead next frame's warm-start
|
|
1319
|
+
// if contact re-establishes at a different feature.
|
|
1320
|
+
manifolds.clear_contacts(slot);
|
|
1321
|
+
continue;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
const kept = reduce_candidates(cand_count);
|
|
1325
|
+
|
|
1326
|
+
// ── Match-and-merge: feature-id (with position fallback) ──────
|
|
1327
|
+
//
|
|
1328
|
+
// Snapshot prev-frame state for matching + impulse carry-over.
|
|
1329
|
+
const data = manifolds.data_buffer;
|
|
1330
|
+
const slot_off = manifolds.slot_data_offset(slot);
|
|
1331
|
+
const prev_count_raw = manifolds.contact_count(slot);
|
|
1332
|
+
const prev_count = prev_count_raw > MAX_CONTACTS_PER_MANIFOLD
|
|
1333
|
+
? MAX_CONTACTS_PER_MANIFOLD
|
|
1334
|
+
: prev_count_raw;
|
|
1335
|
+
for (let j = 0; j < prev_count; j++) {
|
|
1336
|
+
const off = slot_off + j * CONTACT_STRIDE;
|
|
1337
|
+
const snap_off = j * 7;
|
|
1338
|
+
prev_snapshot[snap_off] = data[off + 13]; // feature_id
|
|
1339
|
+
prev_snapshot[snap_off + 1] = data[off]; // world_a x
|
|
1340
|
+
prev_snapshot[snap_off + 2] = data[off + 1]; // world_a y
|
|
1341
|
+
prev_snapshot[snap_off + 3] = data[off + 2]; // world_a z
|
|
1342
|
+
prev_snapshot[snap_off + 4] = data[off + 10]; // j_n
|
|
1343
|
+
prev_snapshot[snap_off + 5] = data[off + 11]; // j_t1
|
|
1344
|
+
prev_snapshot[snap_off + 6] = data[off + 12]; // j_t2
|
|
1345
|
+
prev_claimed[j] = 0;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
// For each new candidate, find a matching prev contact.
|
|
1349
|
+
// Step 1: feature-id match (only if BOTH sides have a non-zero
|
|
1350
|
+
// feature_id — fid = 0 means "no info").
|
|
1351
|
+
// Step 2: position-fallback within MATCH_TOL_SQR.
|
|
1352
|
+
for (let k = 0; k < kept; k++) {
|
|
1353
|
+
const cand_off = k * CANDIDATE_STRIDE;
|
|
1354
|
+
const cand_fid = candidates[cand_off + 10];
|
|
1355
|
+
const cand_ax = candidates[cand_off];
|
|
1356
|
+
const cand_ay = candidates[cand_off + 1];
|
|
1357
|
+
const cand_az = candidates[cand_off + 2];
|
|
1358
|
+
|
|
1359
|
+
let best_prev = -1;
|
|
1360
|
+
if (cand_fid !== 0) {
|
|
1361
|
+
for (let j = 0; j < prev_count; j++) {
|
|
1362
|
+
if (prev_claimed[j]) continue;
|
|
1363
|
+
if (prev_snapshot[j * 7] === cand_fid) {
|
|
1364
|
+
best_prev = j;
|
|
1365
|
+
break;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
if (best_prev === -1) {
|
|
1370
|
+
let best_d2 = MATCH_TOL_SQR;
|
|
1371
|
+
for (let j = 0; j < prev_count; j++) {
|
|
1372
|
+
if (prev_claimed[j]) continue;
|
|
1373
|
+
const snap_off = j * 7;
|
|
1374
|
+
const dx = prev_snapshot[snap_off + 1] - cand_ax;
|
|
1375
|
+
const dy = prev_snapshot[snap_off + 2] - cand_ay;
|
|
1376
|
+
const dz = prev_snapshot[snap_off + 3] - cand_az;
|
|
1377
|
+
const d2 = dx * dx + dy * dy + dz * dz;
|
|
1378
|
+
if (d2 < best_d2) { best_d2 = d2; best_prev = j; }
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
cand_to_prev[k] = best_prev;
|
|
1382
|
+
if (best_prev !== -1) prev_claimed[best_prev] = 1;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
// Reset count without zeroing the data slab — set_contact below
|
|
1386
|
+
// will overwrite geometry, and matched candidates will inherit
|
|
1387
|
+
// the impulses we snapshotted above (written back at the new
|
|
1388
|
+
// slot index after set_contact).
|
|
1389
|
+
manifolds.begin_refill(slot);
|
|
1390
|
+
|
|
1391
|
+
for (let k = 0; k < kept; k++) {
|
|
1392
|
+
const off = k * CANDIDATE_STRIDE;
|
|
1393
|
+
// Target slot index = k (we write candidates 0..kept-1 into
|
|
1394
|
+
// slot indices 0..kept-1). The impulse carry-over below
|
|
1395
|
+
// copies the matched prev contact's impulses into THIS k,
|
|
1396
|
+
// so the geometric and warm-start state stay correlated
|
|
1397
|
+
// even if the matching mapping permuted the order.
|
|
1398
|
+
manifolds.set_contact(
|
|
1399
|
+
slot, k,
|
|
1400
|
+
candidates[off], candidates[off + 1], candidates[off + 2],
|
|
1401
|
+
candidates[off + 3], candidates[off + 4], candidates[off + 5],
|
|
1402
|
+
candidates[off + 6], candidates[off + 7], candidates[off + 8],
|
|
1403
|
+
candidates[off + 9],
|
|
1404
|
+
candidates[off + 10]
|
|
1405
|
+
);
|
|
1406
|
+
const prev_j = cand_to_prev[k];
|
|
1407
|
+
if (prev_j !== -1) {
|
|
1408
|
+
// Copy prev_j's impulse to this slot index.
|
|
1409
|
+
const dst_off = slot_off + k * CONTACT_STRIDE;
|
|
1410
|
+
const src_off = prev_j * 7;
|
|
1411
|
+
data[dst_off + 10] = prev_snapshot[src_off + 4];
|
|
1412
|
+
data[dst_off + 11] = prev_snapshot[src_off + 5];
|
|
1413
|
+
data[dst_off + 12] = prev_snapshot[src_off + 6];
|
|
1414
|
+
} else {
|
|
1415
|
+
// No match — explicitly zero the impulses, since this
|
|
1416
|
+
// slot index may have stale data from an earlier frame's
|
|
1417
|
+
// contact at the same index.
|
|
1418
|
+
manifolds.clear_impulses(slot, k);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
}
|