@woosh/meep-engine 2.138.20 → 2.139.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/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/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/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/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/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 +673 -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 +365 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_smooth_vertex.d.ts +31 -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 +112 -0
- package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_vertex_is_boundary.d.ts +22 -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 +55 -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 +22 -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 +49 -0
- 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/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 +102 -9
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerController.js +38 -3
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +533 -4
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +315 -6
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +220 -22
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +858 -241
- package/src/engine/control/first-person/TODO.md +127 -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 +158 -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 +550 -119
- 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 +28 -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/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/PLAN.md +236 -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/aabb_transform_oriented.d.ts +30 -0
- package/src/engine/physics/broadphase/aabb_transform_oriented.d.ts.map +1 -0
- package/src/engine/physics/broadphase/aabb_transform_oriented.js +93 -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 +226 -0
- package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -0
- package/src/engine/physics/contact/ManifoldStore.js +499 -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 +520 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -0
- package/src/engine/physics/ecs/PhysicsSystem.js +1159 -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 +455 -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_solve_pressure_unmasked_legacy.d.ts +20 -0
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_unmasked_legacy.js +83 -0
- package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts +26 -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 +70 -0
- package/src/engine/physics/gjk/expanding_polytope_algorithm.d.ts.map +1 -1
- package/src/engine/physics/gjk/expanding_polytope_algorithm.js +8 -10
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts +29 -0
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -0
- package/src/engine/physics/inertia/world_inverse_inertia.js +79 -0
- package/src/engine/physics/integration/integrate_position.d.ts +16 -0
- package/src/engine/physics/integration/integrate_position.d.ts.map +1 -0
- package/src/engine/physics/integration/integrate_position.js +48 -0
- package/src/engine/physics/integration/integrate_velocity.d.ts +25 -0
- package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -0
- package/src/engine/physics/integration/integrate_velocity.js +79 -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 +59 -0
- package/src/engine/physics/narrowphase/PosedShape.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/PosedShape.js +110 -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 +543 -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 +508 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts +11 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/narrowphase_step.js +382 -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 +130 -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/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/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/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 +36 -0
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -0
- package/src/engine/physics/solver/solve_contacts.js +598 -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,1159 @@
|
|
|
1
|
+
import { assert } from "../../../core/assert.js";
|
|
2
|
+
import { BVH } from "../../../core/bvh2/bvh3/BVH.js";
|
|
3
|
+
import Signal from "../../../core/events/signal/Signal.js";
|
|
4
|
+
import Vector3 from "../../../core/geom/Vector3.js";
|
|
5
|
+
import { ResourceAccessKind } from "../../../core/model/ResourceAccessKind.js";
|
|
6
|
+
import { ResourceAccessSpecification } from "../../../core/model/ResourceAccessSpecification.js";
|
|
7
|
+
import { System } from "../../ecs/System.js";
|
|
8
|
+
import { Transform } from "../../ecs/transform/Transform.js";
|
|
9
|
+
import { body_id_index, BodyStorage } from "../body/BodyStorage.js";
|
|
10
|
+
import { aabb_transform_oriented } from "../broadphase/aabb_transform_oriented.js";
|
|
11
|
+
import { compute_fat_world_aabb } from "../broadphase/compute_fat_world_aabb.js";
|
|
12
|
+
import { generate_pairs } from "../broadphase/generate_pairs.js";
|
|
13
|
+
import { PairList } from "../broadphase/PairList.js";
|
|
14
|
+
import { CONTACT_STRIDE, ManifoldStore } from "../contact/ManifoldStore.js";
|
|
15
|
+
import { ContactEventBuffer, ContactEventKind } from "../events/ContactEventBuffer.js";
|
|
16
|
+
import { diff_manifolds } from "../events/diff_manifolds.js";
|
|
17
|
+
import { integrate_position } from "../integration/integrate_position.js";
|
|
18
|
+
import { integrate_velocity } from "../integration/integrate_velocity.js";
|
|
19
|
+
import { IslandBuilder } from "../island/IslandBuilder.js";
|
|
20
|
+
import { narrowphase_step } from "../narrowphase/narrowphase_step.js";
|
|
21
|
+
import { raycast as raycast_query } from "../queries/raycast.js";
|
|
22
|
+
import { returnTrue } from "../../../core/function/returnTrue.js";
|
|
23
|
+
import { solve_contacts } from "../solver/solve_contacts.js";
|
|
24
|
+
import { world_inverse_inertia_apply } from "../inertia/world_inverse_inertia.js";
|
|
25
|
+
import { PhysicsEvents } from "./PhysicsEvents.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Scratch for {@link applyImpulseAt}'s angular delta calculation.
|
|
29
|
+
* @type {Float64Array}
|
|
30
|
+
*/
|
|
31
|
+
const scratch_angular_delta = new Float64Array(3);
|
|
32
|
+
import { BodyKind } from "./BodyKind.js";
|
|
33
|
+
import { Collider, COLLIDER_UNBOUND } from "./Collider.js";
|
|
34
|
+
import { RIGID_BODY_UNALLOCATED, RigidBody } from "./RigidBody.js";
|
|
35
|
+
import { RigidBodyFlags } from "./RigidBodyFlags.js";
|
|
36
|
+
import { SleepState } from "./SleepState.js";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Reusable scratch buffer for world-AABB construction so the link path is
|
|
40
|
+
* allocation-free in steady state.
|
|
41
|
+
* @type {Float64Array}
|
|
42
|
+
*/
|
|
43
|
+
const scratch_world_aabb = new Float64Array(6);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Reusable scratch buffer for the local AABB returned by
|
|
47
|
+
* {@link AbstractShape3D#compute_bounding_box}.
|
|
48
|
+
* @type {Float64Array}
|
|
49
|
+
*/
|
|
50
|
+
const scratch_local_aabb = new Float64Array(6);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Rigid-body physics system.
|
|
54
|
+
*
|
|
55
|
+
* v1 scope: pool + active list + two BVHs (static / dynamic), Transform sync
|
|
56
|
+
* contract, and the user-facing API surface (gravity, force / impulse, layer
|
|
57
|
+
* filtering, wake / sleep, contact filter). The per-step simulation pipeline
|
|
58
|
+
* (broadphase pair generation, narrowphase, solver, islands, sleep test,
|
|
59
|
+
* contact-event emission) is built on top of this skeleton in subsequent
|
|
60
|
+
* iterations.
|
|
61
|
+
*
|
|
62
|
+
* Dependency tuple is `(RigidBody, Collider, Transform)` — every body has
|
|
63
|
+
* exactly one collider on the same entity. Compound bodies via child collider
|
|
64
|
+
* entities are an extension point handled by a follow-up sub-observer.
|
|
65
|
+
*
|
|
66
|
+
* @author Alex Goldring
|
|
67
|
+
* @copyright Company Named Limited (c) 2026
|
|
68
|
+
*/
|
|
69
|
+
export class PhysicsSystem extends System {
|
|
70
|
+
|
|
71
|
+
constructor() {
|
|
72
|
+
super();
|
|
73
|
+
|
|
74
|
+
this.dependencies = [RigidBody, Transform];
|
|
75
|
+
|
|
76
|
+
this.components_used = [
|
|
77
|
+
ResourceAccessSpecification.from(RigidBody, ResourceAccessKind.Read | ResourceAccessKind.Write),
|
|
78
|
+
ResourceAccessSpecification.from(Collider, ResourceAccessKind.Read),
|
|
79
|
+
ResourceAccessSpecification.from(Transform, ResourceAccessKind.Read | ResourceAccessKind.Write),
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @type {BodyStorage}
|
|
84
|
+
*/
|
|
85
|
+
this.storage = new BodyStorage();
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @type {BVH}
|
|
89
|
+
*/
|
|
90
|
+
this.staticBvh = new BVH();
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @type {BVH}
|
|
94
|
+
*/
|
|
95
|
+
this.dynamicBvh = new BVH();
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Persistent contact-manifold cache. One slot per active pair.
|
|
99
|
+
* @type {ManifoldStore}
|
|
100
|
+
*/
|
|
101
|
+
this.manifolds = new ManifoldStore();
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Per-frame list of broadphase-overlapping pairs (canonical
|
|
105
|
+
* `(min, max)`). Cleared at the top of each step.
|
|
106
|
+
* @type {PairList}
|
|
107
|
+
*/
|
|
108
|
+
this.pairs = new PairList();
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Per-frame contact-event buffer. Populated by the manifold diff
|
|
112
|
+
* pass at end-of-step and consumed by the dispatch pass.
|
|
113
|
+
* @type {ContactEventBuffer}
|
|
114
|
+
*/
|
|
115
|
+
this.contactEvents = new ContactEventBuffer();
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Per-frame island partitioning of the awake-body + contact graph.
|
|
119
|
+
* Rebuilt after narrowphase, consumed by the solver (and, in the
|
|
120
|
+
* follow-up slice, by the per-island atomic sleep test). Bodies
|
|
121
|
+
* static and kinematic act as constraint anchors and do not enlarge
|
|
122
|
+
* islands.
|
|
123
|
+
* @type {IslandBuilder}
|
|
124
|
+
*/
|
|
125
|
+
this.islands = new IslandBuilder();
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Velocity-squared threshold below which a body is eligible to start
|
|
129
|
+
* accumulating sleep time. Combined linear + angular kinetic-ish
|
|
130
|
+
* metric: `vx²+vy²+vz² + ωx²+ωy²+ωz²`. Default 0.01 corresponds to
|
|
131
|
+
* ~0.1 m/s linear or ~0.1 rad/s angular.
|
|
132
|
+
* @type {number}
|
|
133
|
+
*/
|
|
134
|
+
this.sleepVelocitySqrThreshold = 0.01;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Number of TGS substeps per fixed tick. Each substep does
|
|
138
|
+
* `integrate_velocity → solve_contacts → integrate_position` with
|
|
139
|
+
* `dt / substeps` as its time step; narrowphase + island building
|
|
140
|
+
* run once per tick before the substep loop. Higher values give
|
|
141
|
+
* better convergence on tall stacks (impulses propagate through
|
|
142
|
+
* deeper contact chains thanks to per-substep position
|
|
143
|
+
* integration) at proportionally higher solver cost.
|
|
144
|
+
*
|
|
145
|
+
* Box2D's default is 4; we match. Set to 1 to fall back to
|
|
146
|
+
* classic PGS behaviour (one solve per tick at full `dt`).
|
|
147
|
+
* @type {number}
|
|
148
|
+
*/
|
|
149
|
+
this.solverSubsteps = 4;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Velocity iterations within each TGS substep. With 4 substeps,
|
|
153
|
+
* the total iterations per tick is `solverSubsteps × this` = 8 —
|
|
154
|
+
* comparable to the classic PGS default of 10, but distributed
|
|
155
|
+
* across substeps with position integration between them, which
|
|
156
|
+
* is dramatically more stable on stacks.
|
|
157
|
+
* @type {number}
|
|
158
|
+
*/
|
|
159
|
+
this.solverItersPerSubstep = 2;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Seconds of below-threshold motion before a body is moved to the
|
|
163
|
+
* sleeping set. Box2D default is 0.5 s.
|
|
164
|
+
* @type {number}
|
|
165
|
+
*/
|
|
166
|
+
this.sleepTimeThreshold = 0.5;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Reusable contact-event payload. Listeners must copy any fields they
|
|
170
|
+
* intend to retain past their own scope. Reset before each dispatch.
|
|
171
|
+
* @private
|
|
172
|
+
*/
|
|
173
|
+
this.__contact_payload = {
|
|
174
|
+
entityA: -1,
|
|
175
|
+
entityB: -1,
|
|
176
|
+
kind: 0,
|
|
177
|
+
depth: 0,
|
|
178
|
+
normal: new Vector3(),
|
|
179
|
+
point: new Vector3(),
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* World gravity, m/s². Applied each step scaled by per-body gravityScale.
|
|
184
|
+
* @readonly
|
|
185
|
+
* @type {Vector3}
|
|
186
|
+
*/
|
|
187
|
+
this.gravity = new Vector3(0, -9.81, 0);
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Emitted at end-of-step for newly established contact pairs.
|
|
191
|
+
* @readonly
|
|
192
|
+
* @type {Signal}
|
|
193
|
+
*/
|
|
194
|
+
this.onContactBegin = new Signal();
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Emitted at end-of-step for contact pairs that persisted from the previous step.
|
|
198
|
+
* @readonly
|
|
199
|
+
* @type {Signal}
|
|
200
|
+
*/
|
|
201
|
+
this.onContactStay = new Signal();
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Emitted at end-of-step for contact pairs that disappeared.
|
|
205
|
+
* @readonly
|
|
206
|
+
* @type {Signal}
|
|
207
|
+
*/
|
|
208
|
+
this.onContactEnd = new Signal();
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Optional global contact filter. Called for each surviving broadphase
|
|
212
|
+
* pair; returning `false` discards the pair.
|
|
213
|
+
* @type {((entityA: number, entityB: number, colliderA: Collider, colliderB: Collider) => boolean) | null}
|
|
214
|
+
* @private
|
|
215
|
+
*/
|
|
216
|
+
this.__contact_filter = null;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Per-body component side-tables indexed by body index (NOT packed id).
|
|
220
|
+
* Sparse arrays — populated at link, cleared at unlink. The hot loop
|
|
221
|
+
* iterates `storage.awake_at(i)` and dereferences these.
|
|
222
|
+
* @type {RigidBody[]}
|
|
223
|
+
*/
|
|
224
|
+
this.__bodies = [];
|
|
225
|
+
/** @type {Transform[]} */
|
|
226
|
+
this.__transforms = [];
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Per-body list of attached colliders. Each entry stores the
|
|
230
|
+
* Collider component, its world Transform, the entity that owns
|
|
231
|
+
* it (the body entity for same-entity attachments, a child entity
|
|
232
|
+
* for compound bodies), and the BVH leaf id assigned at attach
|
|
233
|
+
* time. A body may have zero or more attached colliders.
|
|
234
|
+
*
|
|
235
|
+
* @type {Array<Array<{collider: Collider, transform: Transform, entity: number, bvhNode: number}>>}
|
|
236
|
+
*/
|
|
237
|
+
this.__body_collider_lists = [];
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Bound reference to {@link __pair_filter} so we hand the same
|
|
241
|
+
* callable to {@link generate_pairs} each step without per-step
|
|
242
|
+
* allocation.
|
|
243
|
+
* @private
|
|
244
|
+
*/
|
|
245
|
+
this.__pair_filter_bound = (idA, idB) => this.__pair_filter(idA, idB);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Symmetric layer/mask check + optional user callback. Called per
|
|
250
|
+
* candidate pair during broadphase. Returns `true` to accept the pair.
|
|
251
|
+
*
|
|
252
|
+
* Layer/mask rule: pair (A, B) collides iff
|
|
253
|
+
* `(A.layer & B.mask) !== 0 && (B.layer & A.mask) !== 0`.
|
|
254
|
+
* The user's contact-filter callback is consulted only for pairs that
|
|
255
|
+
* pass the bitmask test (cheap gate first).
|
|
256
|
+
*
|
|
257
|
+
* @private
|
|
258
|
+
* @param {number} idA packed body id
|
|
259
|
+
* @param {number} idB packed body id
|
|
260
|
+
* @returns {boolean}
|
|
261
|
+
*/
|
|
262
|
+
__pair_filter(idA, idB) {
|
|
263
|
+
const idxA = body_id_index(idA);
|
|
264
|
+
const idxB = body_id_index(idB);
|
|
265
|
+
const rbA = this.__bodies[idxA];
|
|
266
|
+
const rbB = this.__bodies[idxB];
|
|
267
|
+
if (rbA === undefined || rbB === undefined) return false;
|
|
268
|
+
|
|
269
|
+
// Layer/mask gate (symmetric).
|
|
270
|
+
if (((rbA.layer & rbB.mask) | 0) === 0) return false;
|
|
271
|
+
if (((rbB.layer & rbA.mask) | 0) === 0) return false;
|
|
272
|
+
|
|
273
|
+
// User callback gate, if installed.
|
|
274
|
+
const fn = this.__contact_filter;
|
|
275
|
+
if (fn !== null) {
|
|
276
|
+
const entA = this.storage.entity_at(idxA);
|
|
277
|
+
const entB = this.storage.entity_at(idxB);
|
|
278
|
+
const colA = this.__primary_collider(idxA);
|
|
279
|
+
const colB = this.__primary_collider(idxB);
|
|
280
|
+
if (!fn(entA, entB, colA, colB)) return false;
|
|
281
|
+
}
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* First attached collider of a body, or `null` if none. Used by the
|
|
287
|
+
* solver to read material parameters (friction / restitution) when the
|
|
288
|
+
* per-contact source-collider identity isn't tracked yet (v1 limitation
|
|
289
|
+
* — multi-collider bodies with mixed materials lose precision here).
|
|
290
|
+
*
|
|
291
|
+
* @private
|
|
292
|
+
* @param {number} body_idx
|
|
293
|
+
* @returns {Collider|null}
|
|
294
|
+
*/
|
|
295
|
+
__primary_collider(body_idx) {
|
|
296
|
+
const list = this.__body_collider_lists[body_idx];
|
|
297
|
+
return (list !== undefined && list.length > 0) ? list[0].collider : null;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Replace the world gravity vector. Effective on the next step.
|
|
302
|
+
* @param {Vector3|{x:number,y:number,z:number}} v
|
|
303
|
+
*/
|
|
304
|
+
setGravity(v) {
|
|
305
|
+
this.gravity.set(v.x, v.y, v.z);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Install (or remove with `null`) the contact filter callback.
|
|
310
|
+
* @param {((entityA:number, entityB:number, colliderA:Collider, colliderB:Collider) => boolean) | null} fn
|
|
311
|
+
*/
|
|
312
|
+
setContactFilter(fn) {
|
|
313
|
+
this.__contact_filter = fn;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* @returns {((entityA:number, entityB:number, colliderA:Collider, colliderB:Collider) => boolean) | null}
|
|
318
|
+
*/
|
|
319
|
+
getContactFilter() {
|
|
320
|
+
return this.__contact_filter;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* @private
|
|
325
|
+
* @param {RigidBody} rb
|
|
326
|
+
* @param {Collider} collider
|
|
327
|
+
* @param {Transform} transform
|
|
328
|
+
* @returns {number} BVH node id
|
|
329
|
+
*/
|
|
330
|
+
__insert_into_broadphase(rb, collider, transform) {
|
|
331
|
+
const shape = collider.shape;
|
|
332
|
+
|
|
333
|
+
assert.notNull(shape, 'Collider.shape must be set before attaching');
|
|
334
|
+
|
|
335
|
+
shape.compute_bounding_box(scratch_local_aabb);
|
|
336
|
+
|
|
337
|
+
const p = transform.position;
|
|
338
|
+
const q = transform.rotation;
|
|
339
|
+
|
|
340
|
+
aabb_transform_oriented(
|
|
341
|
+
scratch_world_aabb, 0,
|
|
342
|
+
scratch_local_aabb[0], scratch_local_aabb[1], scratch_local_aabb[2],
|
|
343
|
+
scratch_local_aabb[3], scratch_local_aabb[4], scratch_local_aabb[5],
|
|
344
|
+
p.x, p.y, p.z,
|
|
345
|
+
q.x, q.y, q.z, q.w
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
const bvh = rb.kind === BodyKind.Static ? this.staticBvh : this.dynamicBvh;
|
|
349
|
+
const node = bvh.allocate_node();
|
|
350
|
+
|
|
351
|
+
bvh.node_set_aabb_primitive(
|
|
352
|
+
node,
|
|
353
|
+
scratch_world_aabb[0], scratch_world_aabb[1], scratch_world_aabb[2],
|
|
354
|
+
scratch_world_aabb[3], scratch_world_aabb[4], scratch_world_aabb[5]
|
|
355
|
+
);
|
|
356
|
+
bvh.node_set_user_data(node, rb._bodyId);
|
|
357
|
+
bvh.insert_leaf(node);
|
|
358
|
+
|
|
359
|
+
return node;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* @private
|
|
364
|
+
* @param {RigidBody} rb
|
|
365
|
+
* @param {number} node
|
|
366
|
+
*/
|
|
367
|
+
__remove_from_broadphase(rb, node) {
|
|
368
|
+
const bvh = rb.kind === BodyKind.Static ? this.staticBvh : this.dynamicBvh;
|
|
369
|
+
bvh.remove_leaf(node);
|
|
370
|
+
bvh.release_node(node);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Lifecycle entry: invoked when an entity gains the (RigidBody, Transform)
|
|
375
|
+
* tuple. Allocates the body's slot in storage and seeds an empty collider
|
|
376
|
+
* list. Colliders are attached separately via {@link attach_collider} (the
|
|
377
|
+
* paired {@link ColliderObserverSystem} drives this from the dataset; in
|
|
378
|
+
* tests, call manually).
|
|
379
|
+
*
|
|
380
|
+
* @param {RigidBody} rigidBody
|
|
381
|
+
* @param {Transform} transform
|
|
382
|
+
* @param {number} entity
|
|
383
|
+
*/
|
|
384
|
+
link(rigidBody, transform, entity) {
|
|
385
|
+
const packed = this.storage.allocate(entity);
|
|
386
|
+
rigidBody._bodyId = packed;
|
|
387
|
+
rigidBody.sleepState = SleepState.Awake;
|
|
388
|
+
|
|
389
|
+
const index = body_id_index(packed);
|
|
390
|
+
this.storage.set_kind(index, rigidBody.kind);
|
|
391
|
+
this.storage.set_flags(index, rigidBody.flags);
|
|
392
|
+
|
|
393
|
+
this.__bodies[index] = rigidBody;
|
|
394
|
+
this.__transforms[index] = transform;
|
|
395
|
+
this.__body_collider_lists[index] = [];
|
|
396
|
+
|
|
397
|
+
// Static bodies do not need to live in the active list — they never move.
|
|
398
|
+
if (rigidBody.kind === BodyKind.Static) {
|
|
399
|
+
this.storage.mark_sleeping(index);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Detach every collider attached to this body, free its slot, and clear
|
|
405
|
+
* the side-tables.
|
|
406
|
+
*
|
|
407
|
+
* @param {RigidBody} rigidBody
|
|
408
|
+
* @param {Transform} transform
|
|
409
|
+
* @param {number} entity
|
|
410
|
+
*/
|
|
411
|
+
unlink(rigidBody, transform, entity) {
|
|
412
|
+
const packed = rigidBody._bodyId;
|
|
413
|
+
|
|
414
|
+
assert.equal(this.storage.is_valid(packed), true, 'unlink: stale or absent body id');
|
|
415
|
+
|
|
416
|
+
// If the body is sleeping inside a multi-member sleep group, dissolve
|
|
417
|
+
// the group by waking every member first. Otherwise the surviving
|
|
418
|
+
// members would hold dangling indices in their sleep-group chain
|
|
419
|
+
// pointing at a slot that has just been freed.
|
|
420
|
+
if (rigidBody.sleep_group_next !== -1) {
|
|
421
|
+
this.__wake_body(rigidBody);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const index = body_id_index(packed);
|
|
425
|
+
const list = this.__body_collider_lists[index];
|
|
426
|
+
if (list !== undefined) {
|
|
427
|
+
for (let i = 0; i < list.length; i++) {
|
|
428
|
+
const entry = list[i];
|
|
429
|
+
if (entry.bvhNode !== COLLIDER_UNBOUND) {
|
|
430
|
+
this.__remove_from_broadphase(rigidBody, entry.bvhNode);
|
|
431
|
+
}
|
|
432
|
+
entry.collider._bvhNode = COLLIDER_UNBOUND;
|
|
433
|
+
entry.collider._bodyId = -1;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
this.__bodies[index] = undefined;
|
|
438
|
+
this.__transforms[index] = undefined;
|
|
439
|
+
this.__body_collider_lists[index] = undefined;
|
|
440
|
+
|
|
441
|
+
this.storage.free(packed);
|
|
442
|
+
rigidBody._bodyId = RIGID_BODY_UNALLOCATED;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Attach a collider to an existing body. The collider can live on the
|
|
447
|
+
* same entity as the body (single-collider body) or on a child entity
|
|
448
|
+
* (compound body). The world transform passed here is the collider's
|
|
449
|
+
* own — for a same-entity collider it is the body's Transform; for a
|
|
450
|
+
* child collider it is the child entity's Transform.
|
|
451
|
+
*
|
|
452
|
+
* Idempotent — re-attaching the same collider is a no-op.
|
|
453
|
+
*
|
|
454
|
+
* @param {number} body_entity entity that owns the body
|
|
455
|
+
* @param {Collider} collider
|
|
456
|
+
* @param {Transform} transform world transform of the collider
|
|
457
|
+
* @param {number} [collider_entity] entity owning the collider (defaults to body_entity)
|
|
458
|
+
*/
|
|
459
|
+
attach_collider(body_entity, collider, transform, collider_entity = body_entity) {
|
|
460
|
+
// Find the body by walking the storage entity table. The body must
|
|
461
|
+
// have been allocated via `link` before any colliders are attached.
|
|
462
|
+
const body_index = this.__find_body_index_by_entity(body_entity);
|
|
463
|
+
assert.notEqual(body_index, -1, `attach_collider: no body found for entity ${body_entity}`);
|
|
464
|
+
|
|
465
|
+
const rb = this.__bodies[body_index];
|
|
466
|
+
// Idempotent: skip if collider already attached.
|
|
467
|
+
if (collider._bvhNode !== COLLIDER_UNBOUND) return;
|
|
468
|
+
|
|
469
|
+
const node = this.__insert_into_broadphase(rb, collider, transform);
|
|
470
|
+
|
|
471
|
+
collider._bvhNode = node;
|
|
472
|
+
collider._bodyId = rb._bodyId;
|
|
473
|
+
this.__body_collider_lists[body_index].push({
|
|
474
|
+
collider, transform, entity: collider_entity, bvhNode: node,
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Reverse of {@link attach_collider}. Idempotent.
|
|
480
|
+
*
|
|
481
|
+
* @param {number} body_entity
|
|
482
|
+
* @param {Collider} collider
|
|
483
|
+
*/
|
|
484
|
+
detach_collider(body_entity, collider) {
|
|
485
|
+
if (collider._bvhNode === COLLIDER_UNBOUND) return;
|
|
486
|
+
|
|
487
|
+
const body_index = this.__find_body_index_by_entity(body_entity);
|
|
488
|
+
if (body_index === -1) return;
|
|
489
|
+
|
|
490
|
+
const rb = this.__bodies[body_index];
|
|
491
|
+
this.__remove_from_broadphase(rb, collider._bvhNode);
|
|
492
|
+
|
|
493
|
+
const list = this.__body_collider_lists[body_index];
|
|
494
|
+
for (let i = 0; i < list.length; i++) {
|
|
495
|
+
if (list[i].collider === collider) {
|
|
496
|
+
list.splice(i, 1);
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
collider._bvhNode = COLLIDER_UNBOUND;
|
|
502
|
+
collider._bodyId = -1;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Linear scan over body slots looking for the one whose entity matches.
|
|
507
|
+
* O(N) where N is the live body count — only called on the link/unlink
|
|
508
|
+
* paths, not during simulation, so the scan cost is bounded.
|
|
509
|
+
*
|
|
510
|
+
* @private
|
|
511
|
+
* @param {number} entity
|
|
512
|
+
* @returns {number} body index or -1
|
|
513
|
+
*/
|
|
514
|
+
__find_body_index_by_entity(entity) {
|
|
515
|
+
const hwm = this.storage.high_water_mark;
|
|
516
|
+
for (let i = 0; i < hwm; i++) {
|
|
517
|
+
if (this.storage.entity_at(i) === entity) return i;
|
|
518
|
+
}
|
|
519
|
+
return -1;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Resolve a packed body id to its entity, or `-1` if the id is stale.
|
|
524
|
+
* @param {number} packed_body_id
|
|
525
|
+
* @returns {number}
|
|
526
|
+
*/
|
|
527
|
+
entityOf(packed_body_id) {
|
|
528
|
+
if (!this.storage.is_valid(packed_body_id)) return -1;
|
|
529
|
+
return this.storage.entity_at(body_id_index(packed_body_id));
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Number of live bodies (regardless of awake/sleeping state).
|
|
534
|
+
* @returns {number}
|
|
535
|
+
*/
|
|
536
|
+
get bodyCount() {
|
|
537
|
+
return this.storage.size;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Apply an instantaneous change of momentum at the body's centre of mass.
|
|
542
|
+
* Linear-only — see {@link applyImpulseAt} for an off-centre impulse that
|
|
543
|
+
* also produces angular response.
|
|
544
|
+
*
|
|
545
|
+
* Wakes the body if it is asleep.
|
|
546
|
+
*
|
|
547
|
+
* @param {RigidBody} rigidBody
|
|
548
|
+
* @param {Vector3|{x:number,y:number,z:number}} impulse
|
|
549
|
+
*/
|
|
550
|
+
applyImpulse(rigidBody, impulse) {
|
|
551
|
+
if (rigidBody.kind !== BodyKind.Dynamic) {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const inv_m = rigidBody.mass > 0 ? 1 / rigidBody.mass : 0;
|
|
555
|
+
rigidBody.linearVelocity.addScaled(impulse, inv_m);
|
|
556
|
+
|
|
557
|
+
this.__wake_body(rigidBody);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Apply an instantaneous change of momentum at a specific world-space point.
|
|
562
|
+
* Off-centre impulses produce both linear (Δv = P/m) and angular
|
|
563
|
+
* (Δω = I_w⁻¹·(r × P)) response.
|
|
564
|
+
*
|
|
565
|
+
* Wakes the body if it is asleep.
|
|
566
|
+
*
|
|
567
|
+
* @param {RigidBody} rigidBody
|
|
568
|
+
* @param {Transform} transform body's current world Transform (used for r and I_w)
|
|
569
|
+
* @param {Vector3|{x:number,y:number,z:number}} impulse
|
|
570
|
+
* @param {Vector3|{x:number,y:number,z:number}} worldPoint
|
|
571
|
+
*/
|
|
572
|
+
applyImpulseAt(rigidBody, transform, impulse, worldPoint) {
|
|
573
|
+
if (rigidBody.kind !== BodyKind.Dynamic) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const inv_m = rigidBody.mass > 0 ? 1 / rigidBody.mass : 0;
|
|
577
|
+
|
|
578
|
+
rigidBody.linearVelocity.set(
|
|
579
|
+
rigidBody.linearVelocity.x + impulse.x * inv_m,
|
|
580
|
+
rigidBody.linearVelocity.y + impulse.y * inv_m,
|
|
581
|
+
rigidBody.linearVelocity.z + impulse.z * inv_m
|
|
582
|
+
);
|
|
583
|
+
|
|
584
|
+
const rx = worldPoint.x - transform.position.x;
|
|
585
|
+
const ry = worldPoint.y - transform.position.y;
|
|
586
|
+
const rz = worldPoint.z - transform.position.z;
|
|
587
|
+
|
|
588
|
+
// Δω = I_w⁻¹ · (r × P)
|
|
589
|
+
const tx = ry * impulse.z - rz * impulse.y;
|
|
590
|
+
const ty = rz * impulse.x - rx * impulse.z;
|
|
591
|
+
const tz = rx * impulse.y - ry * impulse.x;
|
|
592
|
+
|
|
593
|
+
world_inverse_inertia_apply(scratch_angular_delta, 0, rigidBody, transform, tx, ty, tz);
|
|
594
|
+
|
|
595
|
+
rigidBody.angularVelocity.set(
|
|
596
|
+
rigidBody.angularVelocity.x + scratch_angular_delta[0],
|
|
597
|
+
rigidBody.angularVelocity.y + scratch_angular_delta[1],
|
|
598
|
+
rigidBody.angularVelocity.z + scratch_angular_delta[2]
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
this.__wake_body(rigidBody);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Accumulate a continuous torque (world-space) for integration on the
|
|
606
|
+
* next fixedUpdate. Pairs with {@link applyForce} for the rotational case.
|
|
607
|
+
*
|
|
608
|
+
* Wakes the body if asleep.
|
|
609
|
+
*
|
|
610
|
+
* @param {RigidBody} rigidBody
|
|
611
|
+
* @param {Vector3|{x:number,y:number,z:number}} torque
|
|
612
|
+
*/
|
|
613
|
+
applyTorque(rigidBody, torque) {
|
|
614
|
+
if (rigidBody.kind !== BodyKind.Dynamic) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
rigidBody.accumulatedTorque.set(
|
|
618
|
+
rigidBody.accumulatedTorque.x + torque.x,
|
|
619
|
+
rigidBody.accumulatedTorque.y + torque.y,
|
|
620
|
+
rigidBody.accumulatedTorque.z + torque.z
|
|
621
|
+
);
|
|
622
|
+
this.__wake_body(rigidBody);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Apply a continuous force at a specific world-space point. The force
|
|
627
|
+
* generates both a linear acceleration (F/m) and a torque (r × F) about
|
|
628
|
+
* the body's centre of mass.
|
|
629
|
+
*
|
|
630
|
+
* Wakes the body if asleep.
|
|
631
|
+
*
|
|
632
|
+
* @param {RigidBody} rigidBody
|
|
633
|
+
* @param {Transform} transform body's current world Transform
|
|
634
|
+
* @param {Vector3|{x:number,y:number,z:number}} force
|
|
635
|
+
* @param {Vector3|{x:number,y:number,z:number}} worldPoint
|
|
636
|
+
*/
|
|
637
|
+
applyForceAt(rigidBody, transform, force, worldPoint) {
|
|
638
|
+
if (rigidBody.kind !== BodyKind.Dynamic) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
rigidBody.accumulatedForce.set(
|
|
642
|
+
rigidBody.accumulatedForce.x + force.x,
|
|
643
|
+
rigidBody.accumulatedForce.y + force.y,
|
|
644
|
+
rigidBody.accumulatedForce.z + force.z
|
|
645
|
+
);
|
|
646
|
+
|
|
647
|
+
const rx = worldPoint.x - transform.position.x;
|
|
648
|
+
const ry = worldPoint.y - transform.position.y;
|
|
649
|
+
const rz = worldPoint.z - transform.position.z;
|
|
650
|
+
|
|
651
|
+
rigidBody.accumulatedTorque.set(
|
|
652
|
+
rigidBody.accumulatedTorque.x + (ry * force.z - rz * force.y),
|
|
653
|
+
rigidBody.accumulatedTorque.y + (rz * force.x - rx * force.z),
|
|
654
|
+
rigidBody.accumulatedTorque.z + (rx * force.y - ry * force.x)
|
|
655
|
+
);
|
|
656
|
+
|
|
657
|
+
this.__wake_body(rigidBody);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Accumulate a continuous force to be integrated next fixedUpdate step.
|
|
662
|
+
* Wakes the body if asleep.
|
|
663
|
+
*
|
|
664
|
+
* @param {RigidBody} rigidBody
|
|
665
|
+
* @param {Vector3|{x:number,y:number,z:number}} force
|
|
666
|
+
*/
|
|
667
|
+
applyForce(rigidBody, force) {
|
|
668
|
+
if (rigidBody.kind !== BodyKind.Dynamic) {
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
rigidBody.accumulatedForce.add( force);
|
|
672
|
+
this.__wake_body(rigidBody);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Replace the linear velocity. Wakes the body if asleep.
|
|
677
|
+
*
|
|
678
|
+
* @param {RigidBody} rigidBody
|
|
679
|
+
* @param {Vector3|{x:number,y:number,z:number}} v
|
|
680
|
+
*/
|
|
681
|
+
setLinearVelocity(rigidBody, v) {
|
|
682
|
+
rigidBody.linearVelocity.copy(v);
|
|
683
|
+
if (rigidBody.kind === BodyKind.Dynamic) {
|
|
684
|
+
this.__wake_body(rigidBody);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Force the body awake. Static bodies are ignored.
|
|
690
|
+
* @param {RigidBody} rigidBody
|
|
691
|
+
*/
|
|
692
|
+
wake(rigidBody) {
|
|
693
|
+
this.__wake_body(rigidBody);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Force the body asleep. Dynamic bodies will not re-enter the active list
|
|
698
|
+
* until a wake event occurs.
|
|
699
|
+
* @param {RigidBody} rigidBody
|
|
700
|
+
*/
|
|
701
|
+
sleep(rigidBody) {
|
|
702
|
+
if (rigidBody.kind !== BodyKind.Dynamic) {
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
if (rigidBody.sleepState === SleepState.Sleeping) {
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
rigidBody.sleepState = SleepState.Sleeping;
|
|
709
|
+
const index = body_id_index(rigidBody._bodyId);
|
|
710
|
+
this.storage.mark_sleeping(index);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Wake a body and atomically wake every other body it was last sleeping
|
|
715
|
+
* with (its "sleep group"). Sleep groups are circular doubly-linked lists
|
|
716
|
+
* threaded through every member of an island when it sleeps atomically;
|
|
717
|
+
* waking any one member walks the chain and wakes the rest in the same
|
|
718
|
+
* call.
|
|
719
|
+
*
|
|
720
|
+
* Without this, a 100-block stack hit at the base would wake one block
|
|
721
|
+
* per frame as the broadphase propagated awareness up the stack — a
|
|
722
|
+
* visible ~1.6 s wave at 60 fps. Atomic wake eliminates the wave.
|
|
723
|
+
*
|
|
724
|
+
* No-op for non-dynamic bodies. Idempotent for already-awake bodies.
|
|
725
|
+
* @private
|
|
726
|
+
* @param {RigidBody} rb
|
|
727
|
+
*/
|
|
728
|
+
__wake_body(rb) {
|
|
729
|
+
if (rb.kind !== BodyKind.Dynamic) return;
|
|
730
|
+
if (rb.sleepState === SleepState.Awake) return;
|
|
731
|
+
const index = body_id_index(rb._bodyId);
|
|
732
|
+
|
|
733
|
+
// Remember the next-in-chain before clearing the body's own pointers;
|
|
734
|
+
// the rest of the group is reached by walking forward from there.
|
|
735
|
+
const start_next = rb.sleep_group_next;
|
|
736
|
+
|
|
737
|
+
rb.sleepState = SleepState.Awake;
|
|
738
|
+
rb.sleep_timer = 0;
|
|
739
|
+
rb.sleep_group_next = -1;
|
|
740
|
+
rb.sleep_group_prev = -1;
|
|
741
|
+
this.storage.mark_awake(index);
|
|
742
|
+
|
|
743
|
+
if (start_next === -1 || start_next === index) return;
|
|
744
|
+
|
|
745
|
+
// Walk the (now-broken) chain forward until we loop back. The chain
|
|
746
|
+
// is circular so we know when to stop; defensive `-1` guards against
|
|
747
|
+
// corruption from a body being unlinked mid-sleep-group.
|
|
748
|
+
let cur = start_next;
|
|
749
|
+
while (cur !== -1 && cur !== index) {
|
|
750
|
+
const cur_rb = this.__bodies[cur];
|
|
751
|
+
if (cur_rb === undefined) break;
|
|
752
|
+
const nxt = cur_rb.sleep_group_next;
|
|
753
|
+
cur_rb.sleepState = SleepState.Awake;
|
|
754
|
+
cur_rb.sleep_timer = 0;
|
|
755
|
+
cur_rb.sleep_group_next = -1;
|
|
756
|
+
cur_rb.sleep_group_prev = -1;
|
|
757
|
+
this.storage.mark_awake(cur);
|
|
758
|
+
cur = nxt;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Atomically put every body in a contiguous range of island members to
|
|
764
|
+
* sleep. Members are threaded into a circular doubly-linked list so any
|
|
765
|
+
* future `wake` on any member walks the chain and revives them all.
|
|
766
|
+
*
|
|
767
|
+
* Velocities are zeroed because the body is by definition at rest at
|
|
768
|
+
* this point — the alternative (storing residual velocities for "softer"
|
|
769
|
+
* wake) is what Bullet does, but for a deterministic game-physics target
|
|
770
|
+
* fully resetting is simpler and avoids drift while sleeping.
|
|
771
|
+
*
|
|
772
|
+
* @private
|
|
773
|
+
* @param {Uint32Array} member_array view (or full array) of body indices
|
|
774
|
+
* @param {number} start
|
|
775
|
+
* @param {number} end
|
|
776
|
+
*/
|
|
777
|
+
__atomic_sleep_island_range(member_array, start, end) {
|
|
778
|
+
const count = end - start;
|
|
779
|
+
if (count === 0) return;
|
|
780
|
+
const bodies = this.__bodies;
|
|
781
|
+
const storage = this.storage;
|
|
782
|
+
|
|
783
|
+
if (count === 1) {
|
|
784
|
+
const idx = member_array[start];
|
|
785
|
+
const rb = bodies[idx];
|
|
786
|
+
if (rb === undefined) return;
|
|
787
|
+
rb.sleep_group_next = -1;
|
|
788
|
+
rb.sleep_group_prev = -1;
|
|
789
|
+
rb.sleepState = SleepState.Sleeping;
|
|
790
|
+
rb.linearVelocity[0] = 0; rb.linearVelocity[1] = 0; rb.linearVelocity[2] = 0;
|
|
791
|
+
rb.angularVelocity[0] = 0; rb.angularVelocity[1] = 0; rb.angularVelocity[2] = 0;
|
|
792
|
+
storage.mark_sleeping(idx);
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
for (let i = 0; i < count; i++) {
|
|
797
|
+
const idx = member_array[start + i];
|
|
798
|
+
const rb = bodies[idx];
|
|
799
|
+
if (rb === undefined) continue;
|
|
800
|
+
const next_idx = member_array[start + ((i + 1) % count)];
|
|
801
|
+
const prev_idx = member_array[start + ((i - 1 + count) % count)];
|
|
802
|
+
rb.sleep_group_next = next_idx;
|
|
803
|
+
rb.sleep_group_prev = prev_idx;
|
|
804
|
+
rb.sleepState = SleepState.Sleeping;
|
|
805
|
+
rb.linearVelocity[0] = 0; rb.linearVelocity[1] = 0; rb.linearVelocity[2] = 0;
|
|
806
|
+
rb.angularVelocity[0] = 0; rb.angularVelocity[1] = 0; rb.angularVelocity[2] = 0;
|
|
807
|
+
storage.mark_sleeping(idx);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Get the body index for a packed body id without revalidation. Used by
|
|
813
|
+
* query traversals that already trust the id came from a live BVH leaf.
|
|
814
|
+
* @param {number} packed_body_id
|
|
815
|
+
* @returns {number}
|
|
816
|
+
*/
|
|
817
|
+
__index_of(packed_body_id) {
|
|
818
|
+
return body_id_index(packed_body_id);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Broadphase raycast against both BVHs. Fills `result` with the nearest
|
|
823
|
+
* hit and returns `true` on hit, `false` on miss.
|
|
824
|
+
*
|
|
825
|
+
* Narrowphase refinement against the actual shape geometry is a
|
|
826
|
+
* follow-up — for now `result.t` is the distance to the leaf's
|
|
827
|
+
* inflated AABB and `result.normal` is the AABB face normal. Both are
|
|
828
|
+
* exact for AABB-shaped colliders.
|
|
829
|
+
*
|
|
830
|
+
* @param {Ray3} ray origin + unit direction + `tMax`
|
|
831
|
+
* @param {PhysicsSurfacePoint} result populated on hit; untouched on miss
|
|
832
|
+
* @param {(entity:number, collider:Collider)=>boolean} [filter] defaults
|
|
833
|
+
* to {@link returnTrue} (accept every candidate)
|
|
834
|
+
* @returns {boolean}
|
|
835
|
+
*/
|
|
836
|
+
raycast(ray, result, filter = returnTrue) {
|
|
837
|
+
return raycast_query(this, ray, result, filter);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Run one simulation step. v1 pipeline: integrate velocity → integrate
|
|
842
|
+
* position → refit broadphase AABBs. Narrowphase, solver, islands and
|
|
843
|
+
* contact-event emission land in subsequent slices.
|
|
844
|
+
*
|
|
845
|
+
* @param {number} dt
|
|
846
|
+
*/
|
|
847
|
+
/**
|
|
848
|
+
* Wake any sleeping body that appears in this step's broadphase pair list.
|
|
849
|
+
* A pair means the BVH AABBs overlap — even if the sleeper hasn't moved,
|
|
850
|
+
* an awake neighbour has come into contact range and the sleeper must
|
|
851
|
+
* participate in narrowphase / solve.
|
|
852
|
+
* @private
|
|
853
|
+
*/
|
|
854
|
+
__wake_pairs() {
|
|
855
|
+
const list = this.pairs;
|
|
856
|
+
const n = list.count;
|
|
857
|
+
const bodies = this.__bodies;
|
|
858
|
+
for (let i = 0; i < n; i++) {
|
|
859
|
+
const idA = list.get_a(i);
|
|
860
|
+
const idB = list.get_b(i);
|
|
861
|
+
const idxA = body_id_index(idA);
|
|
862
|
+
const idxB = body_id_index(idB);
|
|
863
|
+
const a = bodies[idxA];
|
|
864
|
+
const b = bodies[idxB];
|
|
865
|
+
if (a !== undefined && a.sleepState === SleepState.Sleeping) {
|
|
866
|
+
this.__wake_body(a);
|
|
867
|
+
}
|
|
868
|
+
if (b !== undefined && b.sleepState === SleepState.Sleeping) {
|
|
869
|
+
this.__wake_body(b);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Per-island atomic sleep test. Walks each island once and applies the
|
|
876
|
+
* decision uniformly across all members:
|
|
877
|
+
*
|
|
878
|
+
* - If any member carries {@link RigidBodyFlags.DisableSleep}, the
|
|
879
|
+
* entire island is exempt; every member's sleep_timer is reset.
|
|
880
|
+
* - If `max(|v|² + |ω|²)` across all members is below
|
|
881
|
+
* {@link sleepVelocitySqrThreshold}, every member's sleep_timer is
|
|
882
|
+
* incremented by `dt`. When the smallest member's timer crosses
|
|
883
|
+
* {@link sleepTimeThreshold}, the whole island sleeps atomically in
|
|
884
|
+
* the same step (members get threaded into a sleep-group chain so
|
|
885
|
+
* {@link __wake_body} can wake them all in one call).
|
|
886
|
+
* - Otherwise the island has at least one active member, so every
|
|
887
|
+
* member's timer is reset.
|
|
888
|
+
*
|
|
889
|
+
* This is the design-plan atomic-island sleep — replaces the per-body
|
|
890
|
+
* approximation that lived in this slot during the previous slice.
|
|
891
|
+
* Weakly-connected piles no longer chatter awake when a single member
|
|
892
|
+
* blips above threshold; piles fall asleep and wake up as one.
|
|
893
|
+
*
|
|
894
|
+
* @private
|
|
895
|
+
* @param {number} dt
|
|
896
|
+
*/
|
|
897
|
+
__sleep_test(dt) {
|
|
898
|
+
const threshold_sqr = this.sleepVelocitySqrThreshold;
|
|
899
|
+
const time_threshold = this.sleepTimeThreshold;
|
|
900
|
+
const bodies = this.__bodies;
|
|
901
|
+
const islands = this.islands;
|
|
902
|
+
const island_count = islands.island_count;
|
|
903
|
+
const body_offsets = islands.body_offsets;
|
|
904
|
+
const body_data = islands.body_data;
|
|
905
|
+
|
|
906
|
+
for (let isl = 0; isl < island_count; isl++) {
|
|
907
|
+
const start = body_offsets[isl];
|
|
908
|
+
const end = body_offsets[isl + 1];
|
|
909
|
+
if (end === start) continue;
|
|
910
|
+
|
|
911
|
+
// Pass 1: find max v² + check DisableSleep across the island.
|
|
912
|
+
let max_v_sqr = 0;
|
|
913
|
+
let any_disable_sleep = false;
|
|
914
|
+
for (let i = start; i < end; i++) {
|
|
915
|
+
const idx = body_data[i];
|
|
916
|
+
const rb = bodies[idx];
|
|
917
|
+
if (rb === undefined) continue;
|
|
918
|
+
if ((rb.flags & RigidBodyFlags.DisableSleep) !== 0) {
|
|
919
|
+
any_disable_sleep = true;
|
|
920
|
+
break;
|
|
921
|
+
}
|
|
922
|
+
const lv = rb.linearVelocity;
|
|
923
|
+
const av = rb.angularVelocity;
|
|
924
|
+
const v_sqr = lv[0] * lv[0] + lv[1] * lv[1] + lv[2] * lv[2]
|
|
925
|
+
+ av[0] * av[0] + av[1] * av[1] + av[2] * av[2];
|
|
926
|
+
if (v_sqr > max_v_sqr) max_v_sqr = v_sqr;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
if (any_disable_sleep) {
|
|
930
|
+
// Whole island is exempt — reset every member's timer.
|
|
931
|
+
for (let i = start; i < end; i++) {
|
|
932
|
+
const rb = bodies[body_data[i]];
|
|
933
|
+
if (rb !== undefined) rb.sleep_timer = 0;
|
|
934
|
+
}
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
if (max_v_sqr < threshold_sqr) {
|
|
939
|
+
// Island is at rest — increment every member's timer; if the
|
|
940
|
+
// slowest-stabilising member has crossed the time threshold,
|
|
941
|
+
// every member has (they were incremented together this step),
|
|
942
|
+
// so atomic-sleep the island.
|
|
943
|
+
let min_timer = Infinity;
|
|
944
|
+
for (let i = start; i < end; i++) {
|
|
945
|
+
const rb = bodies[body_data[i]];
|
|
946
|
+
if (rb === undefined) continue;
|
|
947
|
+
rb.sleep_timer += dt;
|
|
948
|
+
if (rb.sleep_timer < min_timer) min_timer = rb.sleep_timer;
|
|
949
|
+
}
|
|
950
|
+
if (min_timer >= time_threshold) {
|
|
951
|
+
this.__atomic_sleep_island_range(body_data, start, end);
|
|
952
|
+
}
|
|
953
|
+
} else {
|
|
954
|
+
// At least one member is active — reset every timer.
|
|
955
|
+
for (let i = start; i < end; i++) {
|
|
956
|
+
const rb = bodies[body_data[i]];
|
|
957
|
+
if (rb !== undefined) rb.sleep_timer = 0;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* Dispatch every buffered contact event through:
|
|
965
|
+
* - the system-level Signals ({@link onContactBegin}/Stay/End), always; and
|
|
966
|
+
* - the entity-level event channel via `dataset.sendEvent(entity, name, payload)`,
|
|
967
|
+
* when a dataset is attached.
|
|
968
|
+
*
|
|
969
|
+
* Payload is a reused scratch object; listeners must copy anything they
|
|
970
|
+
* intend to retain past the listener body.
|
|
971
|
+
* @private
|
|
972
|
+
*/
|
|
973
|
+
__dispatch_contact_events() {
|
|
974
|
+
const events = this.contactEvents;
|
|
975
|
+
const n = events.count;
|
|
976
|
+
if (n === 0) return;
|
|
977
|
+
|
|
978
|
+
const ecd = (this.entityManager !== null && this.entityManager !== undefined)
|
|
979
|
+
? this.entityManager.dataset
|
|
980
|
+
: null;
|
|
981
|
+
const manifolds = this.manifolds;
|
|
982
|
+
const data = manifolds.data_buffer;
|
|
983
|
+
|
|
984
|
+
const payload = this.__contact_payload;
|
|
985
|
+
|
|
986
|
+
for (let i = 0; i < n; i++) {
|
|
987
|
+
const kind = events.kind_at(i);
|
|
988
|
+
const entA = events.entityA_at(i);
|
|
989
|
+
const entB = events.entityB_at(i);
|
|
990
|
+
const slot = events.slot_at(i);
|
|
991
|
+
|
|
992
|
+
// Use the deepest contact of the manifold as the representative
|
|
993
|
+
// point/normal/depth for the event. v1: contact 0 only.
|
|
994
|
+
const slot_off = manifolds.slot_data_offset(slot);
|
|
995
|
+
const has_contact = manifolds.contact_count(slot) > 0;
|
|
996
|
+
|
|
997
|
+
// Scratch payload — no observers subscribe to its component
|
|
998
|
+
// vectors; write indices directly to skip the Signal dispatch.
|
|
999
|
+
const pt = payload.point;
|
|
1000
|
+
const nm = payload.normal;
|
|
1001
|
+
if (has_contact) {
|
|
1002
|
+
const wax = data[slot_off], way = data[slot_off + 1], waz = data[slot_off + 2];
|
|
1003
|
+
const wbx = data[slot_off + 3], wby = data[slot_off + 4], wbz = data[slot_off + 5];
|
|
1004
|
+
pt[0] = (wax + wbx) * 0.5; pt[1] = (way + wby) * 0.5; pt[2] = (waz + wbz) * 0.5;
|
|
1005
|
+
nm[0] = data[slot_off + 6]; nm[1] = data[slot_off + 7]; nm[2] = data[slot_off + 8];
|
|
1006
|
+
payload.depth = data[slot_off + 9];
|
|
1007
|
+
} else {
|
|
1008
|
+
pt[0] = 0; pt[1] = 0; pt[2] = 0;
|
|
1009
|
+
nm[0] = 0; nm[1] = 0; nm[2] = 0;
|
|
1010
|
+
payload.depth = 0;
|
|
1011
|
+
}
|
|
1012
|
+
payload.entityA = entA;
|
|
1013
|
+
payload.entityB = entB;
|
|
1014
|
+
|
|
1015
|
+
let event_name;
|
|
1016
|
+
let signal;
|
|
1017
|
+
if (kind === ContactEventKind.Begin) { event_name = PhysicsEvents.ContactBegin; signal = this.onContactBegin; }
|
|
1018
|
+
else if (kind === ContactEventKind.Stay) { event_name = PhysicsEvents.ContactStay; signal = this.onContactStay; }
|
|
1019
|
+
else { event_name = PhysicsEvents.ContactEnd; signal = this.onContactEnd; }
|
|
1020
|
+
|
|
1021
|
+
signal.send1(payload);
|
|
1022
|
+
|
|
1023
|
+
if (ecd !== null && ecd !== undefined) {
|
|
1024
|
+
if (entA >= 0) ecd.sendEvent(entA, event_name, payload);
|
|
1025
|
+
if (entB >= 0) ecd.sendEvent(entB, event_name, payload);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
fixedUpdate(dt) {
|
|
1031
|
+
const gx = this.gravity.x;
|
|
1032
|
+
const gy = this.gravity.y;
|
|
1033
|
+
const gz = this.gravity.z;
|
|
1034
|
+
|
|
1035
|
+
const lists = this.__body_collider_lists;
|
|
1036
|
+
|
|
1037
|
+
// ─── Once-per-tick stages (broadphase, narrowphase, islands) ────
|
|
1038
|
+
//
|
|
1039
|
+
// Position-dependent work — broadphase refit, pair generation,
|
|
1040
|
+
// narrowphase contact extraction, island partitioning — runs ONCE
|
|
1041
|
+
// at the top of the tick using the pose at tick-start. Substep
|
|
1042
|
+
// position integration happens inside the inner loop further down;
|
|
1043
|
+
// intra-tick motion is bounded by the fat AABB margin (it uses the
|
|
1044
|
+
// full-tick dt for sweep volume) so manifolds stay valid across
|
|
1045
|
+
// substeps without re-running narrowphase.
|
|
1046
|
+
|
|
1047
|
+
// Stage 1: refit each awake body's collider leaves with a fat AABB
|
|
1048
|
+
// padded for the full tick's swept extent.
|
|
1049
|
+
const count = this.storage.awake_count;
|
|
1050
|
+
for (let i = 0; i < count; i++) {
|
|
1051
|
+
const idx = this.storage.awake_at(i);
|
|
1052
|
+
const rb = this.__bodies[idx];
|
|
1053
|
+
const list = lists[idx];
|
|
1054
|
+
if (list === undefined) continue;
|
|
1055
|
+
const lv = rb.linearVelocity;
|
|
1056
|
+
for (let k = 0; k < list.length; k++) {
|
|
1057
|
+
const entry = list[k];
|
|
1058
|
+
compute_fat_world_aabb(
|
|
1059
|
+
scratch_world_aabb, 0,
|
|
1060
|
+
entry.collider.shape, entry.transform,
|
|
1061
|
+
lv[0], lv[1], lv[2],
|
|
1062
|
+
dt
|
|
1063
|
+
);
|
|
1064
|
+
this.dynamicBvh.node_move_aabb(entry.bvhNode, scratch_world_aabb);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// Stage 2: broadphase pair generation (canonical min,max body pairs).
|
|
1069
|
+
generate_pairs(
|
|
1070
|
+
this.storage,
|
|
1071
|
+
this.dynamicBvh,
|
|
1072
|
+
this.staticBvh,
|
|
1073
|
+
this.manifolds,
|
|
1074
|
+
lists,
|
|
1075
|
+
this.pairs,
|
|
1076
|
+
this.__pair_filter_bound,
|
|
1077
|
+
);
|
|
1078
|
+
|
|
1079
|
+
// Stage 3: wake propagation. Any sleeping body whose leaf overlaps
|
|
1080
|
+
// an awake leaf this tick must participate in narrowphase + solve.
|
|
1081
|
+
this.__wake_pairs();
|
|
1082
|
+
|
|
1083
|
+
// Stage 4: narrowphase — closed-form fast paths or GJK+EPA per pair.
|
|
1084
|
+
narrowphase_step(this.pairs, this.manifolds, this);
|
|
1085
|
+
|
|
1086
|
+
// Stage 5: partition awake bodies + touched contacts into islands.
|
|
1087
|
+
this.islands.build(this.storage, this.manifolds, this.__bodies, this.__body_collider_lists);
|
|
1088
|
+
|
|
1089
|
+
// ─── TGS substep loop ───────────────────────────────────────────
|
|
1090
|
+
//
|
|
1091
|
+
// Each substep advances velocity by `gravity * sub_dt`, runs a
|
|
1092
|
+
// short velocity-iteration sweep with Baumgarte bias scaled to
|
|
1093
|
+
// `sub_dt`, and integrates position by `sub_dt`. Warm-start only
|
|
1094
|
+
// fires on substep 0 — subsequent substeps continue from the
|
|
1095
|
+
// impulses already accumulated in the manifold data buffer (those
|
|
1096
|
+
// are reflected in the current velocity state; re-applying would
|
|
1097
|
+
// double-count).
|
|
1098
|
+
//
|
|
1099
|
+
// Position correction is K× stronger over a tick than classic PGS
|
|
1100
|
+
// for the same Baumgarte beta, because the velocity-correcting
|
|
1101
|
+
// impulse fires K times and each one's position contribution
|
|
1102
|
+
// integrates immediately. This is the mechanism that lets TGS
|
|
1103
|
+
// resolve tall stacks where single-shot PGS just spreads info
|
|
1104
|
+
// through the chain without resolving cumulative drift.
|
|
1105
|
+
const substeps = this.solverSubsteps | 0;
|
|
1106
|
+
const iters_per_substep = this.solverItersPerSubstep | 0;
|
|
1107
|
+
const sub_dt = dt / substeps;
|
|
1108
|
+
|
|
1109
|
+
// Re-read awake_count because __wake_pairs may have grown it.
|
|
1110
|
+
const count_after_wake = this.storage.awake_count;
|
|
1111
|
+
|
|
1112
|
+
for (let s = 0; s < substeps; s++) {
|
|
1113
|
+
// Integrate velocity (gravity + accumulated forces) for this substep.
|
|
1114
|
+
for (let i = 0; i < count_after_wake; i++) {
|
|
1115
|
+
const idx = this.storage.awake_at(i);
|
|
1116
|
+
const rb = this.__bodies[idx];
|
|
1117
|
+
const tr = this.__transforms[idx];
|
|
1118
|
+
integrate_velocity(rb, tr, gx, gy, gz, sub_dt);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// Solve velocity constraints over the current pose. Bias term
|
|
1122
|
+
// inside the solver uses `sub_dt`, so per-substep Baumgarte
|
|
1123
|
+
// correction is K× stronger than full-dt PGS would apply.
|
|
1124
|
+
solve_contacts(this.manifolds, this, sub_dt, iters_per_substep, s === 0);
|
|
1125
|
+
|
|
1126
|
+
// Advance position with the solved velocity.
|
|
1127
|
+
for (let i = 0; i < count_after_wake; i++) {
|
|
1128
|
+
const idx = this.storage.awake_at(i);
|
|
1129
|
+
const rb = this.__bodies[idx];
|
|
1130
|
+
const tr = this.__transforms[idx];
|
|
1131
|
+
integrate_position(rb, tr, sub_dt);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
// ─── End-of-tick stages (sleep, events, manifold cleanup) ───────
|
|
1136
|
+
|
|
1137
|
+
// Stage 6: sleep test using the full-tick `dt` for the sleep timer.
|
|
1138
|
+
this.__sleep_test(dt);
|
|
1139
|
+
|
|
1140
|
+
// Stage 7: diff manifolds against the previous frame and dispatch
|
|
1141
|
+
// Begin / Stay / End events. MUST run before advance_frame, which
|
|
1142
|
+
// rolls the touched flags.
|
|
1143
|
+
diff_manifolds(this.manifolds, this.storage, this.contactEvents);
|
|
1144
|
+
this.__dispatch_contact_events();
|
|
1145
|
+
|
|
1146
|
+
// Stage 8 (end-of-step): roll touched → prev_touched and evict slots
|
|
1147
|
+
// whose pair has not been touched within the grace window.
|
|
1148
|
+
this.manifolds.advance_frame();
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/**
|
|
1153
|
+
* @readonly
|
|
1154
|
+
* @type {boolean}
|
|
1155
|
+
*/
|
|
1156
|
+
PhysicsSystem.prototype.isPhysicsSystem = true;
|
|
1157
|
+
|
|
1158
|
+
// Re-export for convenience.
|
|
1159
|
+
export { BodyKind, RigidBodyFlags };
|