@woosh/meep-engine 3.38.0 → 3.39.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/build/bundle-worker-image-decoder.js +1 -1
- package/build/bundle-worker-terrain.js +1 -1
- package/package.json +1 -1
- package/src/core/binary/BinaryBuffer.d.ts +11 -0
- package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
- package/src/core/binary/BinaryBuffer.js +30 -0
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_aabb.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/query/bvh_query_user_data_intersects_aabb.js +28 -7
- package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts +15 -0
- package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts.map +1 -1
- package/src/core/geom/3d/aabb/aabb3_transform_oriented.js +41 -0
- package/src/core/geom/3d/aabb/aabb3_transform_oriented_inverse.d.ts +12 -0
- package/src/core/geom/3d/aabb/aabb3_transform_oriented_inverse.d.ts.map +1 -1
- package/src/core/geom/3d/aabb/aabb3_transform_oriented_inverse.js +27 -0
- package/src/core/geom/3d/gjk/gjk.js +3 -3
- package/src/core/geom/3d/gjk/gjk_distance.d.ts +94 -0
- package/src/core/geom/3d/gjk/gjk_distance.d.ts.map +1 -0
- package/src/core/geom/3d/gjk/gjk_distance.js +782 -0
- package/src/core/geom/3d/gjk/gjk_epa_penetration.d.ts +5 -4
- package/src/core/geom/3d/gjk/gjk_epa_penetration.d.ts.map +1 -1
- package/src/core/geom/3d/gjk/gjk_epa_penetration.js +14 -9
- package/src/core/geom/3d/gjk/minkowski_support.d.ts +11 -7
- package/src/core/geom/3d/gjk/minkowski_support.d.ts.map +1 -1
- package/src/core/geom/3d/gjk/minkowski_support.js +33 -9
- package/src/core/geom/3d/gjk/mpr.d.ts.map +1 -1
- package/src/core/geom/3d/gjk/mpr.js +26 -5
- package/src/core/geom/3d/line/line3_compute_segment_closest_point_to_aabb3_t.d.ts +15 -0
- package/src/core/geom/3d/line/line3_compute_segment_closest_point_to_aabb3_t.d.ts.map +1 -1
- package/src/core/geom/3d/line/line3_compute_segment_closest_point_to_aabb3_t.js +106 -33
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.d.ts +2 -1
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.d.ts.map +1 -1
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale.js +4 -8
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_array.d.ts +32 -0
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_array.d.ts.map +1 -0
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_array.js +85 -0
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.d.ts +3 -3
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.d.ts.map +1 -1
- package/src/core/geom/3d/mat4/m4_from_rotation_translation_scale_scalar.js +24 -34
- package/src/core/geom/3d/quaternion/quat3_integrate.d.ts +17 -0
- package/src/core/geom/3d/quaternion/quat3_integrate.d.ts.map +1 -1
- package/src/core/geom/3d/quaternion/quat3_integrate.js +45 -4
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts +16 -0
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts.map +1 -1
- package/src/core/geom/3d/quaternion/quat3_multiply.js +25 -0
- package/src/core/geom/3d/quaternion/quat3_nlerp.d.ts +18 -0
- package/src/core/geom/3d/quaternion/quat3_nlerp.d.ts.map +1 -1
- package/src/core/geom/3d/quaternion/quat3_nlerp.js +45 -0
- package/src/core/geom/3d/quaternion/quat3_shortest_arc.d.ts +17 -0
- package/src/core/geom/3d/quaternion/quat3_shortest_arc.d.ts.map +1 -1
- package/src/core/geom/3d/quaternion/quat3_shortest_arc.js +155 -94
- package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts +12 -0
- package/src/core/geom/3d/quaternion/quat3_to_matrix3.d.ts.map +1 -1
- package/src/core/geom/3d/quaternion/quat3_to_matrix3.js +31 -0
- package/src/core/geom/3d/shape/AbstractShape3D.d.ts +20 -6
- package/src/core/geom/3d/shape/AbstractShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/AbstractShape3D.js +20 -6
- package/src/core/geom/3d/shape/BoxShape3D.d.ts +1 -1
- package/src/core/geom/3d/shape/BoxShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/BoxShape3D.js +5 -1
- package/src/core/geom/3d/shape/CapsuleShape3D.d.ts +1 -1
- package/src/core/geom/3d/shape/CapsuleShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/CapsuleShape3D.js +5 -1
- package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts +25 -11
- package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/ConvexHullShape3D.js +392 -22
- package/src/core/geom/3d/shape/CylinderShape3D.d.ts +8 -7
- package/src/core/geom/3d/shape/CylinderShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/CylinderShape3D.js +12 -7
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +51 -8
- package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/HeightMapShape3D.js +260 -14
- package/src/core/geom/3d/shape/MeshShape3D.d.ts +61 -7
- package/src/core/geom/3d/shape/MeshShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/MeshShape3D.js +111 -20
- package/src/core/geom/3d/shape/PointShape3D.d.ts +1 -1
- package/src/core/geom/3d/shape/PointShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/PointShape3D.js +1 -1
- package/src/core/geom/3d/shape/PosedShape3D.d.ts +9 -0
- package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/PosedShape3D.js +36 -6
- package/src/core/geom/3d/shape/SphereShape3D.d.ts +1 -1
- package/src/core/geom/3d/shape/SphereShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/SphereShape3D.js +5 -1
- package/src/core/geom/3d/shape/TransformedShape3D.d.ts +12 -10
- package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/TransformedShape3D.js +53 -19
- package/src/core/geom/3d/shape/Triangle3D.d.ts +1 -1
- package/src/core/geom/3d/shape/Triangle3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/Triangle3D.js +5 -1
- package/src/core/geom/3d/shape/UnionShape3D.d.ts +1 -1
- package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/UnionShape3D.js +19 -2
- package/src/core/geom/3d/shape/sdf/sdf3_box.d.ts +14 -0
- package/src/core/geom/3d/shape/sdf/sdf3_box.d.ts.map +1 -1
- package/src/core/geom/3d/shape/sdf/sdf3_box.js +67 -5
- package/src/core/geom/3d/shape/sdf/sdf3_capsule.d.ts +14 -0
- package/src/core/geom/3d/shape/sdf/sdf3_capsule.d.ts.map +1 -1
- package/src/core/geom/3d/shape/sdf/sdf3_capsule.js +61 -3
- package/src/core/geom/3d/shape/sdf/sdf3_cylinder.d.ts +14 -0
- package/src/core/geom/3d/shape/sdf/sdf3_cylinder.d.ts.map +1 -1
- package/src/core/geom/3d/shape/sdf/sdf3_cylinder.js +63 -4
- package/src/core/geom/3d/shape/sdf/sdf3_plane.d.ts +15 -0
- package/src/core/geom/3d/shape/sdf/sdf3_plane.d.ts.map +1 -1
- package/src/core/geom/3d/shape/sdf/sdf3_plane.js +64 -2
- package/src/core/geom/3d/shape/sdf/sdf3_sphere.d.ts +21 -0
- package/src/core/geom/3d/shape/sdf/sdf3_sphere.d.ts.map +1 -1
- package/src/core/geom/3d/shape/sdf/sdf3_sphere.js +66 -3
- package/src/core/geom/3d/shape/shape3_mesh_from_geometry.d.ts +2 -0
- package/src/core/geom/3d/shape/shape3_mesh_from_geometry.d.ts.map +1 -1
- package/src/core/geom/3d/shape/shape3_mesh_from_geometry.js +7 -0
- package/src/core/geom/3d/shape/util/mesh_compute_triangle_edge_flags.d.ts +41 -0
- package/src/core/geom/3d/shape/util/mesh_compute_triangle_edge_flags.d.ts.map +1 -0
- package/src/core/geom/3d/shape/util/mesh_compute_triangle_edge_flags.js +538 -0
- package/src/core/geom/3d/sphere/harmonics/sh_basis_at.d.ts +31 -0
- package/src/core/geom/3d/sphere/harmonics/sh_basis_at.d.ts.map +1 -0
- package/src/core/geom/3d/sphere/harmonics/sh_basis_at.js +85 -0
- package/src/core/geom/3d/sphere/harmonics/sh_vmf_window.d.ts +18 -0
- package/src/core/geom/3d/sphere/harmonics/sh_vmf_window.d.ts.map +1 -0
- package/src/core/geom/3d/sphere/harmonics/sh_vmf_window.js +19 -0
- package/src/core/geom/3d/triangle/TriangleEdgeFlags.d.ts +49 -0
- package/src/core/geom/3d/triangle/TriangleEdgeFlags.d.ts.map +1 -0
- package/src/core/geom/3d/triangle/TriangleEdgeFlags.js +55 -0
- package/src/core/geom/3d/triangle/tri3_classify_shared_edge.d.ts +42 -0
- package/src/core/geom/3d/triangle/tri3_classify_shared_edge.d.ts.map +1 -0
- package/src/core/geom/3d/triangle/tri3_classify_shared_edge.js +85 -0
- package/src/core/geom/3d/triangle/tri3_closest_point.d.ts +13 -0
- package/src/core/geom/3d/triangle/tri3_closest_point.d.ts.map +1 -1
- package/src/core/geom/3d/triangle/tri3_closest_point.js +36 -1
- package/src/core/geom/3d/triangle/tri3_closest_point_barycentric.d.ts +14 -0
- package/src/core/geom/3d/triangle/tri3_closest_point_barycentric.d.ts.map +1 -1
- package/src/core/geom/3d/triangle/tri3_closest_point_barycentric.js +40 -0
- package/src/core/geom/vec3/v3_quaternion_apply.d.ts +19 -0
- package/src/core/geom/vec3/v3_quaternion_apply.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_quaternion_apply.js +32 -0
- package/src/core/geom/vec3/v3_quaternion_apply_inverse.d.ts +19 -0
- package/src/core/geom/vec3/v3_quaternion_apply_inverse.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_quaternion_apply_inverse.js +32 -0
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts +135 -10
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +338 -105
- package/src/engine/control/first-person/abilities/Ability.d.ts +14 -5
- package/src/engine/control/first-person/abilities/Ability.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/Ability.js +14 -5
- package/src/engine/control/first-person/abilities/LedgeGrab.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/LedgeGrab.js +16 -12
- package/src/engine/control/first-person/abilities/Mantle.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/Mantle.js +5 -1
- package/src/engine/control/first-person/abilities/Slide.js +1 -1
- package/src/engine/control/first-person/collision/KinematicMover.d.ts +92 -14
- package/src/engine/control/first-person/collision/KinematicMover.d.ts.map +1 -1
- package/src/engine/control/first-person/collision/KinematicMover.js +218 -37
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts +5 -0
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js +17 -10
- package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
- package/src/engine/ecs/EntityComponentDataset.js +27 -13
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +4 -0
- package/src/engine/ecs/transform/Transform64.d.ts +4 -2
- package/src/engine/ecs/transform/Transform64.d.ts.map +1 -1
- package/src/engine/ecs/transform/Transform64.js +10 -16
- package/src/engine/graphics/ecs/water/Water.d.ts +25 -63
- package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/Water.js +110 -125
- package/src/engine/graphics/ecs/water/WaterSerializationAdapter.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/WaterSerializationAdapter.js +38 -74
- package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_1_2.d.ts +1 -1
- package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_1_2.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_1_2.js +27 -18
- package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_2_3.d.ts +12 -0
- package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_2_3.d.ts.map +1 -0
- package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_2_3.js +45 -0
- package/src/engine/graphics/ecs/water/WaterWaves.d.ts +33 -0
- package/src/engine/graphics/ecs/water/WaterWaves.d.ts.map +1 -0
- package/src/engine/graphics/ecs/water/WaterWaves.js +46 -0
- package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.d.ts +17 -0
- package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.d.ts.map +1 -0
- package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.js +26 -0
- package/src/engine/graphics/texture/3d/scs3d_sample_linear.d.ts +22 -0
- package/src/engine/graphics/texture/3d/scs3d_sample_linear.d.ts.map +1 -1
- package/src/engine/graphics/texture/3d/scs3d_sample_linear.js +43 -1
- package/src/engine/graphics/texture/sampler/Sampler2D.d.ts +29 -3
- package/src/engine/graphics/texture/sampler/Sampler2D.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/Sampler2D.js +102 -14
- package/src/engine/graphics3/WaterSystem.d.ts +39 -3
- package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/WaterSystem.js +134 -25
- package/src/engine/interpolation/BinaryInterpolationAdapter.d.ts +10 -4
- package/src/engine/interpolation/BinaryInterpolationAdapter.d.ts.map +1 -1
- package/src/engine/interpolation/BinaryInterpolationAdapter.js +11 -5
- package/src/engine/interpolation/InterpolationLog.d.ts +11 -5
- package/src/engine/interpolation/InterpolationLog.d.ts.map +1 -1
- package/src/engine/interpolation/InterpolationLog.js +12 -6
- package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
- package/src/engine/interpolation/InterpolationSystem.js +39 -8
- package/src/engine/interpolation/PoseInterpolationAdapter.d.ts +1 -1
- package/src/engine/interpolation/PoseInterpolationAdapter.d.ts.map +1 -1
- package/src/engine/interpolation/PoseInterpolationAdapter.js +51 -22
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -1
- package/src/engine/interpolation/TransformPoseSerializationAdapter.js +18 -23
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts.map +1 -1
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +3 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts.map +1 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.js +3 -1
- package/src/engine/network/adapters/Vector3InterpolationAdapter.d.ts.map +1 -1
- package/src/engine/network/adapters/Vector3InterpolationAdapter.js +5 -2
- package/src/engine/physics/body/SolverBodyState.d.ts.map +1 -1
- package/src/engine/physics/body/SolverBodyState.js +4 -5
- package/src/engine/physics/broadphase/compute_fat_world_aabb.d.ts +12 -0
- package/src/engine/physics/broadphase/compute_fat_world_aabb.d.ts.map +1 -1
- package/src/engine/physics/broadphase/compute_fat_world_aabb.js +28 -3
- package/src/engine/physics/ccd/linear_sweep.d.ts +42 -12
- package/src/engine/physics/ccd/linear_sweep.d.ts.map +1 -1
- package/src/engine/physics/ccd/linear_sweep.js +1131 -97
- package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts +13 -0
- package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts.map +1 -1
- package/src/engine/physics/cloth/collider/ClothColliderIndex.js +90 -22
- package/src/engine/physics/cloth/collider/cloth_collider_bounds.js +84 -24
- package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts +46 -3
- package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts.map +1 -1
- package/src/engine/physics/cloth/collider/cloth_collider_sdf.js +116 -68
- package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothWorld.js +59 -47
- package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +62 -55
- package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.js +46 -39
- package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/cloth_write_back.js +41 -36
- package/src/engine/physics/cloth/playground/garment_build.js +6 -6
- package/src/engine/physics/cloth/playground/garment_main.js +18 -14
- package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/cloth_contact_find.js +8 -5
- package/src/engine/physics/cloth/solver/cloth_solve_vertex.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/cloth_solve_vertex.js +8 -5
- package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.js +15 -15
- package/src/engine/physics/cloth/solver/find_non_finite_cloth_state.d.ts +5 -0
- package/src/engine/physics/cloth/solver/find_non_finite_cloth_state.d.ts.map +1 -1
- package/src/engine/physics/cloth/solver/find_non_finite_cloth_state.js +32 -9
- package/src/engine/physics/constraint/solve_constraints.d.ts +50 -8
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
- package/src/engine/physics/constraint/solve_constraints.js +703 -429
- package/src/engine/physics/contact/ManifoldStore.d.ts +12 -0
- package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
- package/src/engine/physics/contact/ManifoldStore.js +32 -0
- package/src/engine/physics/ecs/BodyKind.d.ts +16 -8
- package/src/engine/physics/ecs/BodyKind.d.ts.map +1 -1
- package/src/engine/physics/ecs/BodyKind.js +16 -8
- package/src/engine/physics/ecs/Collider.d.ts +7 -2
- package/src/engine/physics/ecs/Collider.d.ts.map +1 -1
- package/src/engine/physics/ecs/Collider.js +7 -2
- package/src/engine/physics/ecs/PhysicsEvents.d.ts +13 -0
- package/src/engine/physics/ecs/PhysicsEvents.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsEvents.js +13 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +320 -36
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +779 -81
- package/src/engine/physics/fluid/FluidField.d.ts.map +1 -1
- package/src/engine/physics/fluid/FluidField.js +5 -1
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +11 -9
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts.map +1 -1
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +7 -5
- package/src/engine/physics/fluid/ecs/fluid_reanchor_field.d.ts.map +1 -1
- package/src/engine/physics/fluid/ecs/fluid_reanchor_field.js +9 -7
- package/src/engine/physics/fluid/effector/ImpulseFluidEffector.d.ts.map +1 -1
- package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +3 -4
- package/src/engine/physics/fluid/effector/WakeFluidEffector.d.ts.map +1 -1
- package/src/engine/physics/fluid/effector/WakeFluidEffector.js +9 -7
- package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.js +65 -43
- package/src/engine/physics/inertia/MASS_PROPERTIES_SIZE.d.ts +23 -0
- package/src/engine/physics/inertia/MASS_PROPERTIES_SIZE.d.ts.map +1 -0
- package/src/engine/physics/inertia/MASS_PROPERTIES_SIZE.js +22 -0
- package/src/engine/physics/inertia/convex_hull_mass_properties.d.ts +29 -0
- package/src/engine/physics/inertia/convex_hull_mass_properties.d.ts.map +1 -0
- package/src/engine/physics/inertia/convex_hull_mass_properties.js +117 -0
- package/src/engine/physics/inertia/mass_properties_accumulate.d.ts +24 -0
- package/src/engine/physics/inertia/mass_properties_accumulate.d.ts.map +1 -0
- package/src/engine/physics/inertia/mass_properties_accumulate.js +93 -0
- package/src/engine/physics/inertia/primitive_mass_properties.d.ts +46 -0
- package/src/engine/physics/inertia/primitive_mass_properties.d.ts.map +1 -0
- package/src/engine/physics/inertia/primitive_mass_properties.js +118 -0
- package/src/engine/physics/inertia/rigid_body_set_mass_properties.d.ts +27 -0
- package/src/engine/physics/inertia/rigid_body_set_mass_properties.d.ts.map +1 -0
- package/src/engine/physics/inertia/rigid_body_set_mass_properties.js +62 -0
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts +67 -25
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
- package/src/engine/physics/inertia/world_inverse_inertia.js +149 -26
- package/src/engine/physics/integration/integrate_position.d.ts +18 -19
- package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_position.js +40 -37
- package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_velocity.js +13 -4
- package/src/engine/physics/island/IslandBuilder.d.ts +124 -6
- package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -1
- package/src/engine/physics/island/IslandBuilder.js +322 -19
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts +13 -0
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_box_manifold.js +95 -39
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts +40 -0
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_triangle_contact.js +415 -120
- package/src/engine/physics/narrowphase/capsule_contacts.d.ts +63 -0
- package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/capsule_contacts.js +234 -46
- package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts +13 -0
- package/src/engine/physics/narrowphase/capsule_triangle_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/capsule_triangle_contact.js +141 -93
- package/src/engine/physics/narrowphase/compute_penetration.d.ts +29 -11
- package/src/engine/physics/narrowphase/compute_penetration.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/compute_penetration.js +157 -34
- package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts +76 -0
- package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/convex_convex_manifold.js +243 -48
- package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts +13 -0
- package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/decomposition/decompose_to_triangles.js +28 -12
- package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts +13 -0
- package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/decomposition/heightmap_enumerate_triangles.js +27 -0
- package/src/engine/physics/narrowphase/decomposition/heightmap_triangle_edge_flags.d.ts +42 -0
- package/src/engine/physics/narrowphase/decomposition/heightmap_triangle_edge_flags.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/heightmap_triangle_edge_flags.js +250 -0
- package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts +27 -11
- package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.js +291 -12
- package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts +7 -1
- package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/decomposition/triangle_buffer_layout.js +7 -1
- package/src/engine/physics/narrowphase/decomposition/triangle_edge_flags.d.ts +35 -0
- package/src/engine/physics/narrowphase/decomposition/triangle_edge_flags.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/decomposition/triangle_edge_flags.js +61 -0
- package/src/engine/physics/narrowphase/hull_sphere_contact.d.ts +54 -0
- package/src/engine/physics/narrowphase/hull_sphere_contact.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/hull_sphere_contact.js +244 -0
- package/src/engine/physics/narrowphase/mesh_convex_hull.js +5 -1
- package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.d.ts +2 -0
- package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/mesh_mesh_tet_manifold.js +56 -31
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts +6 -5
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/narrowphase_step.js +2185 -483
- package/src/engine/physics/narrowphase/reduce_manifold_contacts.d.ts +56 -5
- package/src/engine/physics/narrowphase/reduce_manifold_contacts.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/reduce_manifold_contacts.js +546 -5
- package/src/engine/physics/narrowphase/refine_ray_concave.d.ts +29 -2
- package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/refine_ray_concave.js +628 -75
- package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +3 -2
- package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/refine_ray_hit.js +3 -2
- package/src/engine/physics/narrowphase/rigid_subject_pose.d.ts +27 -0
- package/src/engine/physics/narrowphase/rigid_subject_pose.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/rigid_subject_pose.js +96 -0
- package/src/engine/physics/narrowphase/sphere_box_contact.d.ts +14 -0
- package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/sphere_box_contact.js +51 -5
- package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts +12 -0
- package/src/engine/physics/narrowphase/sphere_sphere_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/sphere_sphere_contact.js +30 -0
- package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts +12 -0
- package/src/engine/physics/narrowphase/sphere_triangle_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/sphere_triangle_contact.js +37 -8
- package/src/engine/physics/narrowphase/support_feature_manifold.d.ts +139 -0
- package/src/engine/physics/narrowphase/support_feature_manifold.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/support_feature_manifold.js +949 -0
- package/src/engine/physics/queries/PhysicsSurfacePoint.d.ts +32 -13
- package/src/engine/physics/queries/PhysicsSurfacePoint.d.ts.map +1 -1
- package/src/engine/physics/queries/PhysicsSurfacePoint.js +36 -13
- package/src/engine/physics/queries/convex_overlaps_shape.d.ts +27 -0
- package/src/engine/physics/queries/convex_overlaps_shape.d.ts.map +1 -0
- package/src/engine/physics/queries/convex_overlaps_shape.js +169 -0
- package/src/engine/physics/queries/overlap_shape.d.ts.map +1 -1
- package/src/engine/physics/queries/overlap_shape.js +57 -79
- package/src/engine/physics/queries/raycast.d.ts +10 -4
- package/src/engine/physics/queries/raycast.d.ts.map +1 -1
- package/src/engine/physics/queries/raycast.js +133 -9
- package/src/engine/physics/queries/shape_cast.d.ts +55 -24
- package/src/engine/physics/queries/shape_cast.d.ts.map +1 -1
- package/src/engine/physics/queries/shape_cast.js +2061 -245
- package/src/engine/physics/solver/apply_impulse_to_body.d.ts +32 -10
- package/src/engine/physics/solver/apply_impulse_to_body.d.ts.map +1 -1
- package/src/engine/physics/solver/apply_impulse_to_body.js +69 -28
- package/src/engine/physics/solver/solve_contacts.d.ts +65 -18
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
- package/src/engine/physics/solver/solve_contacts.js +458 -234
- package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -1
- package/src/engine/physics/vehicle/RaycastVehicle.js +5 -6
- package/src/shade/assets/textures/split_sum.bin +0 -0
- package/src/shade/device/mock/SoftwareGPUQueue.d.ts.map +1 -1
- package/src/shade/device/mock/SoftwareGPUQueue.js +2 -1
- package/src/shade/playground/path_traced_media/README.md +78 -0
- package/src/shade/playground/path_traced_media/capture_radiance.d.ts +33 -0
- package/src/shade/playground/path_traced_media/capture_radiance.d.ts.map +1 -0
- package/src/shade/playground/path_traced_media/capture_radiance.js +174 -0
- package/src/shade/playground/path_traced_media/checks.d.ts +160 -0
- package/src/shade/playground/path_traced_media/checks.d.ts.map +1 -0
- package/src/shade/playground/path_traced_media/checks.js +921 -0
- package/src/shade/playground/path_traced_media/index.html +71 -0
- package/src/shade/playground/path_traced_media/main.d.ts +2 -0
- package/src/shade/playground/path_traced_media/main.d.ts.map +1 -0
- package/src/shade/playground/path_traced_media/main.js +440 -0
- package/src/shade/playground/path_traced_media/make_wave_pool_geometry.d.ts +26 -0
- package/src/shade/playground/path_traced_media/make_wave_pool_geometry.d.ts.map +1 -0
- package/src/shade/playground/path_traced_media/make_wave_pool_geometry.js +117 -0
- package/src/shade/playground/path_traced_media/views.d.ts +21 -0
- package/src/shade/playground/path_traced_media/views.d.ts.map +1 -0
- package/src/shade/playground/path_traced_media/views.js +161 -0
- package/src/shade/playground/sh_exponential/README.md +43 -0
- package/src/shade/playground/sh_exponential/index.html +59 -0
- package/src/shade/playground/sh_exponential/main.d.ts +2 -0
- package/src/shade/playground/sh_exponential/main.d.ts.map +1 -0
- package/src/shade/playground/sh_exponential/main.js +571 -0
- package/src/shade/playground/sh_exponential/sh_exp_playground_shaders.d.ts +46 -0
- package/src/shade/playground/sh_exponential/sh_exp_playground_shaders.d.ts.map +1 -0
- package/src/shade/playground/sh_exponential/sh_exp_playground_shaders.js +270 -0
- package/src/shade/playground/water_refraction/README.md +24 -15
- package/src/shade/playground/water_refraction/index.html +6 -8
- package/src/shade/playground/water_refraction/main.js +12 -11
- package/src/shade/playground/water_refraction/world.d.ts +2 -0
- package/src/shade/playground/water_refraction/world.d.ts.map +1 -1
- package/src/shade/playground/water_refraction/world.js +8 -4
- package/src/shade/renderer/Renderer.d.ts +2 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +23 -0
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.js +9 -1
- package/src/shade/renderer/geometry/sdf/chunk_scene_point_query_volume_sign.d.ts.map +1 -1
- package/src/shade/renderer/geometry/sdf/chunk_scene_point_query_volume_sign.js +6 -4
- package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.js +1 -0
- package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_rand1.d.ts +7 -0
- package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_rand1.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_rand1.js +12 -12
- package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_spherical.d.ts +4 -0
- package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_spherical.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/brick4/gpu/bake/sampling/chunk_direction_by_index_spherical.js +13 -9
- package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.js +2 -0
- package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.js +3 -0
- package/src/shade/renderer/global_illumination/lpv/deprecated/probe_trace_compute_shader_code.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/lpv/deprecated/probe_trace_compute_shader_code.js +4 -9
- package/src/shade/renderer/light/LIGHT_DATABASE_SPEC.d.ts.map +1 -1
- package/src/shade/renderer/light/LIGHT_DATABASE_SPEC.js +20 -10
- package/src/shade/renderer/light/environment/rgbe/load_environment_map.d.ts +2 -1
- package/src/shade/renderer/light/environment/rgbe/load_environment_map.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/rgbe/load_environment_map.js +2 -1
- package/src/shade/renderer/light/environment/sh_exponential/README.md +171 -0
- package/src/shade/renderer/light/environment/sh_exponential/SHExpEncoder.d.ts +170 -0
- package/src/shade/renderer/light/environment/sh_exponential/SHExpEncoder.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sh_exponential/SHExpEncoder.js +377 -0
- package/src/shade/renderer/light/environment/sh_exponential/SH_EXP_ENCODE_PARAMETERS_STRUCT.d.ts +16 -0
- package/src/shade/renderer/light/environment/sh_exponential/SH_EXP_ENCODE_PARAMETERS_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sh_exponential/SH_EXP_ENCODE_PARAMETERS_STRUCT.js +27 -0
- package/src/shade/renderer/light/environment/sh_exponential/chunk_sh_exp_workgroup_sum.d.ts +21 -0
- package/src/shade/renderer/light/environment/sh_exponential/chunk_sh_exp_workgroup_sum.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sh_exponential/chunk_sh_exp_workgroup_sum.js +53 -0
- package/src/shade/renderer/light/environment/sh_exponential/sh_exp_design_row.d.ts +33 -0
- package/src/shade/renderer/light/environment/sh_exponential/sh_exp_design_row.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sh_exponential/sh_exp_design_row.js +90 -0
- package/src/shade/renderer/light/environment/sh_exponential/sh_exp_fit_samples.d.ts +28 -0
- package/src/shade/renderer/light/environment/sh_exponential/sh_exp_fit_samples.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sh_exponential/sh_exp_fit_samples.js +92 -0
- package/src/shade/renderer/light/environment/sh_exponential/sh_exp_layout.d.ts +39 -0
- package/src/shade/renderer/light/environment/sh_exponential/sh_exp_layout.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sh_exponential/sh_exp_layout.js +44 -0
- package/src/shade/renderer/light/environment/sh_exponential/sh_exp_solve_matrix.d.ts +42 -0
- package/src/shade/renderer/light/environment/sh_exponential/sh_exp_solve_matrix.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sh_exponential/sh_exp_solve_matrix.js +123 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.d.ts +30 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.js +147 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_log_floor.d.ts +24 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_log_floor.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_log_floor.js +79 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_project.d.ts +17 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_project.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_project.js +99 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_reference_reflectance.d.ts +24 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_reference_reflectance.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_reference_reflectance.js +104 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_solve.d.ts +12 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_solve.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_solve.js +93 -0
- package/src/shade/renderer/lightmap/GPULightMap.d.ts.map +1 -1
- package/src/shade/renderer/lightmap/GPULightMap.js +1 -0
- package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_irradiance_flat.d.ts.map +1 -1
- package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_irradiance_flat.js +4 -0
- package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_sh.d.ts.map +1 -1
- package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_sh.js +4 -0
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/material/PIXEL_TEXTURE_ORM.d.ts +1 -1
- package/src/shade/renderer/material/PIXEL_TEXTURE_ORM.d.ts.map +1 -1
- package/src/shade/renderer/material/StandardShadeMaterial.d.ts +15 -0
- package/src/shade/renderer/material/StandardShadeMaterial.d.ts.map +1 -1
- package/src/shade/renderer/material/StandardShadeMaterial.js +17 -0
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +36 -2
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
- package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +218 -18
- package/src/shade/renderer/material/resident/INDIRECT_MATERIAL_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/material/resident/INDIRECT_MATERIAL_STRUCT.js +10 -0
- package/src/shade/renderer/medium/MEDIA_TABLE_STRUCT.d.ts +17 -0
- package/src/shade/renderer/medium/MEDIA_TABLE_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/medium/MEDIA_TABLE_STRUCT.js +24 -0
- package/src/shade/renderer/medium/MEDIUM_PHASE_LOBE_KIND.d.ts +24 -0
- package/src/shade/renderer/medium/MEDIUM_PHASE_LOBE_KIND.d.ts.map +1 -0
- package/src/shade/renderer/medium/MEDIUM_PHASE_LOBE_KIND.js +18 -0
- package/src/shade/renderer/medium/MEDIUM_STRUCT.d.ts +20 -0
- package/src/shade/renderer/medium/MEDIUM_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/medium/MEDIUM_STRUCT.js +43 -0
- package/src/shade/renderer/medium/ParticipatingMedium.d.ts +108 -0
- package/src/shade/renderer/medium/ParticipatingMedium.d.ts.map +1 -0
- package/src/shade/renderer/medium/ParticipatingMedium.js +204 -0
- package/src/shade/renderer/medium/chunk_medium_lobe_phase.d.ts +13 -0
- package/src/shade/renderer/medium/chunk_medium_lobe_phase.d.ts.map +1 -0
- package/src/shade/renderer/medium/chunk_medium_lobe_phase.js +33 -0
- package/src/shade/renderer/medium/participating_medium_serialization.d.ts +32 -0
- package/src/shade/renderer/medium/participating_medium_serialization.d.ts.map +1 -0
- package/src/shade/renderer/medium/participating_medium_serialization.js +181 -0
- package/src/shade/renderer/medium/phase/MediumPhaseFunction.d.ts +48 -0
- package/src/shade/renderer/medium/phase/MediumPhaseFunction.d.ts.map +1 -0
- package/src/shade/renderer/medium/phase/MediumPhaseFunction.js +42 -0
- package/src/shade/renderer/medium/phase/PhaseCornetteShanks.d.ts +23 -0
- package/src/shade/renderer/medium/phase/PhaseCornetteShanks.d.ts.map +1 -0
- package/src/shade/renderer/medium/phase/PhaseCornetteShanks.js +45 -0
- package/src/shade/renderer/medium/phase/PhaseDraine.d.ts +30 -0
- package/src/shade/renderer/medium/phase/PhaseDraine.d.ts.map +1 -0
- package/src/shade/renderer/medium/phase/PhaseDraine.js +58 -0
- package/src/shade/renderer/medium/phase/PhaseFournierForand.d.ts +24 -0
- package/src/shade/renderer/medium/phase/PhaseFournierForand.d.ts.map +1 -0
- package/src/shade/renderer/medium/phase/PhaseFournierForand.js +53 -0
- package/src/shade/renderer/medium/phase/PhaseHenyeyGreenstein.d.ts +21 -0
- package/src/shade/renderer/medium/phase/PhaseHenyeyGreenstein.d.ts.map +1 -0
- package/src/shade/renderer/medium/phase/PhaseHenyeyGreenstein.js +43 -0
- package/src/shade/renderer/medium/phase/PhaseJendersieDeon.d.ts +25 -0
- package/src/shade/renderer/medium/phase/PhaseJendersieDeon.d.ts.map +1 -0
- package/src/shade/renderer/medium/phase/PhaseJendersieDeon.js +66 -0
- package/src/shade/renderer/medium/phase/PhaseRayleigh.d.ts +9 -0
- package/src/shade/renderer/medium/phase/PhaseRayleigh.d.ts.map +1 -0
- package/src/shade/renderer/medium/phase/PhaseRayleigh.js +21 -0
- package/src/shade/renderer/medium/phase/PhaseWaterMolecular.d.ts +28 -0
- package/src/shade/renderer/medium/phase/PhaseWaterMolecular.d.ts.map +1 -0
- package/src/shade/renderer/medium/phase/PhaseWaterMolecular.js +49 -0
- package/src/shade/renderer/medium/water/WATER_DEFAULT_PATH_LENGTHS.d.ts +10 -0
- package/src/shade/renderer/medium/water/WATER_DEFAULT_PATH_LENGTHS.d.ts.map +1 -0
- package/src/shade/renderer/medium/water/WATER_DEFAULT_PATH_LENGTHS.js +25 -0
- package/src/shade/renderer/medium/water/water_iop_from_appearance.d.ts +47 -0
- package/src/shade/renderer/medium/water/water_iop_from_appearance.d.ts.map +1 -0
- package/src/shade/renderer/medium/water/water_iop_from_appearance.js +174 -0
- package/src/shade/renderer/medium/water/water_iop_from_constituents.d.ts +57 -0
- package/src/shade/renderer/medium/water/water_iop_from_constituents.d.ts.map +1 -0
- package/src/shade/renderer/medium/water/water_iop_from_constituents.js +242 -0
- package/src/shade/renderer/medium/water/water_iop_jerlov.d.ts +41 -0
- package/src/shade/renderer/medium/water/water_iop_jerlov.d.ts.map +1 -0
- package/src/shade/renderer/medium/water/water_iop_jerlov.js +231 -0
- package/src/shade/renderer/medium/water/water_iop_spectral_reference.d.ts +66 -0
- package/src/shade/renderer/medium/water/water_iop_spectral_reference.d.ts.map +1 -0
- package/src/shade/renderer/medium/water/water_iop_spectral_reference.js +223 -0
- package/src/shade/renderer/medium/water/water_medium_from_iop.d.ts +96 -0
- package/src/shade/renderer/medium/water/water_medium_from_iop.d.ts.map +1 -0
- package/src/shade/renderer/medium/water/water_medium_from_iop.js +145 -0
- package/src/shade/renderer/medium/water/water_presets.d.ts +69 -0
- package/src/shade/renderer/medium/water/water_presets.d.ts.map +1 -0
- package/src/shade/renderer/medium/water/water_presets.js +119 -0
- package/src/shade/renderer/medium/water/water_rgb_from_spectral.d.ts +69 -0
- package/src/shade/renderer/medium/water/water_rgb_from_spectral.d.ts.map +1 -0
- package/src/shade/renderer/medium/water/water_rgb_from_spectral.js +341 -0
- package/src/shade/renderer/particles/DESIGN.md +4 -2
- package/src/shade/renderer/particles/shaders/chunk_particle_avboit_voxel_fade.d.ts +26 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_avboit_voxel_fade.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_avboit_voxel_fade.js +71 -0
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +16 -1
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.js +6 -13
- package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +520 -0
- package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.d.ts.map +1 -1
- package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.js +1 -0
- package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.d.ts.map +1 -1
- package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.js +2 -0
- package/src/shade/renderer/path_tracer/graph_path_trace_scene.d.ts.map +1 -1
- package/src/shade/renderer/path_tracer/graph_path_trace_scene.js +188 -185
- package/src/shade/renderer/path_tracer/shader/STANDARD_MATERIAL_DATA_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/path_tracer/shader/STANDARD_MATERIAL_DATA_STRUCT.js +4 -1
- package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_tangent_normal.d.ts +13 -0
- package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_tangent_normal.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/chunk_indirect_sample_tangent_normal.js +29 -0
- package/src/shade/renderer/path_tracer/shader/chunk_render_trace_path.d.ts +79 -0
- package/src/shade/renderer/path_tracer/shader/chunk_render_trace_path.d.ts.map +1 -1
- package/src/shade/renderer/path_tracer/shader/chunk_render_trace_path.js +360 -70
- package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts.map +1 -1
- package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js +4 -3
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_beam.d.ts +24 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_beam.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_beam.js +99 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_connect.d.ts +68 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_connect.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_connect.js +263 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_constraint.d.ts +55 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_constraint.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_constraint.js +148 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light.d.ts +17 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light.js +41 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light_sample.d.ts +20 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light_sample.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_light_sample.js +105 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle.d.ts +28 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle.js +97 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle_of_hit.d.ts +12 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle_of_hit.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/manifold/chunk_manifold_triangle_of_hit.js +46 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_interface.d.ts +25 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_interface.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_interface.js +75 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_path.d.ts +54 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_path.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_path.js +245 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_phase.d.ts +42 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_phase.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_phase.js +236 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_scatter_nee.d.ts +24 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_scatter_nee.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_scatter_nee.js +97 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_stack.d.ts +57 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_stack.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_stack.js +222 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_transport.d.ts +88 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_transport.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_transport.js +304 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_volumes.d.ts +35 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_volumes.d.ts.map +1 -0
- package/src/shade/renderer/path_tracer/shader/media/chunk_medium_volumes.js +137 -0
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.js +85 -84
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSettings.d.ts +8 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSettings.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSettings.js +9 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts +12 -2
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js +12 -3
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.d.ts +13 -7
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.js +13 -7
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +309 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +54 -9
- package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.js +9 -1
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_footprint_bias.d.ts +33 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_footprint_bias.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_footprint_bias.js +45 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_integral_depth_coordinate.d.ts +4 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_integral_depth_coordinate.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_integral_depth_coordinate.js +6 -2
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_sample_transmittance.d.ts +2 -1
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_sample_transmittance.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_sample_transmittance.js +4 -2
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_level_word.d.ts +14 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_level_word.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_level_word.js +28 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_lut_entry.d.ts +10 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_lut_entry.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_lut_entry.js +18 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_marks.d.ts +19 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_marks.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_marks.js +44 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_step_count.d.ts +10 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_step_count.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_warp_step_count.js +17 -0
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +3 -1
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_build_warp.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_build_warp.js +163 -64
- package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
- package/src/shade/renderer/scene/GPUSceneContext.js +4 -1
- package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.d.ts.map +1 -1
- package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.js +5 -0
- package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.d.ts +12 -1
- package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.js +22 -3
- package/src/shade/renderer/shader/chunk/atmosphere/phase/chunk_sample_henyey_greenstein_cos.d.ts +18 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/chunk_sample_henyey_greenstein_cos.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/chunk_sample_henyey_greenstein_cos.js +31 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_phase_fournier_forand.d.ts +43 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_phase_fournier_forand.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_phase_fournier_forand.js +153 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_sample_fournier_forand.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_sample_fournier_forand.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/chunk_sample_fournier_forand.js +57 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/fournier_forand_parameters_from_backscatter.d.ts +45 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/fournier_forand_parameters_from_backscatter.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/ff/fournier_forand_parameters_from_backscatter.js +109 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_phase_draine.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_phase_draine.js +6 -3
- package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine.d.ts +24 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine.js +49 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine_cos.d.ts +18 -0
- package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine_cos.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/atmosphere/phase/jd/chunk_sample_draine_cos.js +18 -0
- package/src/shade/renderer/shader/chunk/brdf/dfg/chunk_integrate_brdf.d.ts +3 -0
- package/src/shade/renderer/shader/chunk/brdf/dfg/chunk_integrate_brdf.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/brdf/dfg/chunk_integrate_brdf.js +3 -0
- package/src/shade/renderer/shader/chunk/brdf/dfg/shader_split_sum_dgf_lut.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/brdf/dfg/shader_split_sum_dgf_lut.js +3 -1
- package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_dielectric.d.ts +24 -0
- package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_dielectric.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_dielectric.js +53 -0
- package/src/shade/renderer/shader/chunk/geometry/ray/chunk_scene_point_query_is_inside_geometry.d.ts +7 -1
- package/src/shade/renderer/shader/chunk/geometry/ray/chunk_scene_point_query_is_inside_geometry.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/geometry/ray/chunk_scene_point_query_is_inside_geometry.js +12 -60
- package/src/shade/renderer/shader/chunk/geometry/sphere/chunk_compute_spherical_excess.d.ts +26 -6
- package/src/shade/renderer/shader/chunk/geometry/sphere/chunk_compute_spherical_excess.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/geometry/sphere/chunk_compute_spherical_excess.js +30 -20
- package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_directional.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_directional.js +3 -1
- package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_point.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_point.js +3 -1
- package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_spot.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/light/sample/chunk_light_importance_spot.js +3 -1
- package/src/shade/renderer/shader/chunk/light/sample/chunk_light_receiver_cosine.d.ts +14 -0
- package/src/shade/renderer/shader/chunk/light/sample/chunk_light_receiver_cosine.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/light/sample/chunk_light_receiver_cosine.js +25 -0
- package/src/shade/renderer/shader/chunk/light/sample/chunk_select_light_importance.d.ts +9 -2
- package/src/shade/renderer/shader/chunk/light/sample/chunk_select_light_importance.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/light/sample/chunk_select_light_importance.js +33 -10
- package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_newton_ray.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_newton_ray.js +71 -37
- package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_band_of_index.d.ts +14 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_band_of_index.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_band_of_index.js +30 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_basis_function.d.ts +19 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_basis_function.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_basis_function.js +73 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_vmf_window.d.ts +19 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_vmf_window.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh_vmf_window.js +25 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/SH_EXP_P4Q2_PROBE_STRUCT.d.ts +17 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/SH_EXP_P4Q2_PROBE_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/SH_EXP_P4Q2_PROBE_STRUCT.js +23 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_p4q2_base_reflectance.d.ts +26 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_p4q2_base_reflectance.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_p4q2_base_reflectance.js +55 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_split_sum.d.ts +52 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_split_sum.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/exponential/chunk_sh_exp_split_sum.js +65 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_basis_at.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_basis_at.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_basis_at.js +229 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_color_evaluate_vmf.d.ts +23 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_color_evaluate_vmf.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh10_color_evaluate_vmf.js +278 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh2_color_evaluate_vmf.d.ts +23 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh2_color_evaluate_vmf.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh2_color_evaluate_vmf.js +58 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh3_color_evaluate_vmf.d.ts +23 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh3_color_evaluate_vmf.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh3_color_evaluate_vmf.js +75 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_basis_at.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_basis_at.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_basis_at.js +70 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_color_evaluate_vmf.d.ts +23 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_color_evaluate_vmf.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh4_color_evaluate_vmf.js +95 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_basis_at.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_basis_at.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_basis_at.js +89 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_color_evaluate_vmf.d.ts +23 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_color_evaluate_vmf.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh5_color_evaluate_vmf.js +118 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_basis_at.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_basis_at.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_basis_at.js +111 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_color_evaluate_vmf.d.ts +23 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_color_evaluate_vmf.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh6_color_evaluate_vmf.js +144 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_basis_at.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_basis_at.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_basis_at.js +136 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_color_evaluate_vmf.d.ts +23 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_color_evaluate_vmf.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh7_color_evaluate_vmf.js +173 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_basis_at.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_basis_at.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_basis_at.js +164 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_color_evaluate_vmf.d.ts +23 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_color_evaluate_vmf.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh8_color_evaluate_vmf.js +205 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_basis_at.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_basis_at.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_basis_at.js +195 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_color_evaluate_vmf.d.ts +23 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_color_evaluate_vmf.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/generated/chunk_sh9_color_evaluate_vmf.js +240 -0
- package/src/shade/renderer/shader/resource/ShaderResourceGroupDescriptor.d.ts +0 -6
- package/src/shade/renderer/shader/resource/ShaderResourceGroupDescriptor.d.ts.map +1 -1
- package/src/shade/renderer/shader/resource/ShaderResourceGroupDescriptor.js +46 -1
- package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.d.ts +7 -7
- package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.js +7 -7
- package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.d.ts +42 -10
- package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/DirectionalLightShadowmap.js +104 -48
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts +20 -4
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +186 -74
- package/src/shade/renderer/shadow/map/Shadowmap.d.ts +2 -1
- package/src/shade/renderer/shadow/map/Shadowmap.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/Shadowmap.js +2 -1
- package/src/shade/renderer/shadow/map/csm/CSM_CASCADE_STRUCT.d.ts +30 -0
- package/src/shade/renderer/shadow/map/csm/CSM_CASCADE_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/shadow/map/csm/CSM_CASCADE_STRUCT.js +52 -0
- package/src/shade/renderer/shadow/map/csm/CSM_SHADOWMAP_STRUCT.d.ts +14 -0
- package/src/shade/renderer/shadow/map/csm/CSM_SHADOWMAP_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/shadow/map/csm/CSM_SHADOWMAP_STRUCT.js +20 -0
- package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_DEFAULT_CASCADE_COUNT.d.ts +8 -0
- package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_DEFAULT_CASCADE_COUNT.d.ts.map +1 -0
- package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_DEFAULT_CASCADE_COUNT.js +7 -0
- package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.d.ts +8 -6
- package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.js +28 -34
- package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_csm_compute_cascade_blended.d.ts +3 -2
- package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_csm_compute_cascade_blended.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_csm_compute_cascade_blended.js +19 -14
- package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.d.ts +4 -3
- package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.js +12 -7
- package/src/shade/renderer/shadow/map/shadowmap_budget_carry_over.d.ts +4 -4
- package/src/shade/renderer/shadow/map/shadowmap_budget_carry_over.js +4 -4
- package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/GPUViewShadowMaps.js +39 -17
- package/src/shade/renderer/shadow/vsm/GPUVirtualShadowTextureResidence.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/GPUVirtualShadowTextureResidence.js +5 -36
- package/src/shade/renderer/shadow/vsm/VSM_UNIFORMS_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/VSM_UNIFORMS_STRUCT.js +3 -2
- package/src/shade/renderer/shadow/vsm/VirtualShadowMap.d.ts +1 -1
- package/src/shade/renderer/shadow/vsm/VirtualShadowMap.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/VirtualShadowMap.js +49 -35
- package/src/shade/renderer/shadow/vsm/VirtualShadowmapRenderer.d.ts +4 -1
- package/src/shade/renderer/shadow/vsm/VirtualShadowmapRenderer.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/VirtualShadowmapRenderer.js +8 -9
- package/src/shade/renderer/shadow/vsm/debug_inspect_residence_buffer.js +2 -2
- package/src/shade/renderer/shadow/vsm/decode_tile_address.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/decode_tile_address.js +15 -23
- package/src/shade/renderer/shadow/vsm/vms_pipeline_compute_coarse_bounds.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/vms_pipeline_compute_coarse_bounds.js +6 -9
- package/src/shade/renderer/shadow/vsm/vsm_residence_pipeline.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/vsm_residence_pipeline.js +19 -9
- package/src/shade/renderer/shadow/vsm/vsm_tile_allocation_pipeline.d.ts.map +1 -1
- package/src/shade/renderer/shadow/vsm/vsm_tile_allocation_pipeline.js +18 -6
- package/src/shade/renderer/texture/util/BLACK_PIXEL_TEXTURE.d.ts +1 -1
- package/src/shade/renderer/texture/util/BLACK_PIXEL_TEXTURE.d.ts.map +1 -1
- package/src/shade/renderer/texture/util/PIXEL_TEXTURE_NORMAL.d.ts +18 -1
- package/src/shade/renderer/texture/util/PIXEL_TEXTURE_NORMAL.d.ts.map +1 -1
- package/src/shade/renderer/texture/util/PIXEL_TEXTURE_NORMAL.js +24 -3
- package/src/shade/renderer/texture/util/WHITE_PIXEL_TEXTURE.d.ts +1 -1
- package/src/shade/renderer/texture/util/WHITE_PIXEL_TEXTURE.d.ts.map +1 -1
- package/src/shade/renderer/texture/util/get_single_pixel_texture.d.ts +0 -1
- package/src/shade/renderer/texture/util/get_single_pixel_texture.d.ts.map +1 -1
- package/src/shade/renderer/texture/util/get_single_pixel_texture.js +2 -23
- package/src/shade/renderer/texture/util/single_pixel_texture_from_sampler.d.ts +11 -0
- package/src/shade/renderer/texture/util/single_pixel_texture_from_sampler.d.ts.map +1 -0
- package/src/shade/renderer/texture/util/single_pixel_texture_from_sampler.js +34 -0
- package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.d.ts +17 -8
- package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.js +35 -64
- package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.d.ts +21 -0
- package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.d.ts.map +1 -0
- package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.js +70 -0
- package/src/shade/renderer/volumetrics/chunk_participating_media_volumes_iterate.d.ts +31 -0
- package/src/shade/renderer/volumetrics/chunk_participating_media_volumes_iterate.d.ts.map +1 -0
- package/src/shade/renderer/volumetrics/chunk_participating_media_volumes_iterate.js +86 -0
- package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +2 -4
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.js +6 -7
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_scattering_lut.js +7 -7
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts +13 -1
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
- package/src/shade/renderer/water/GPUWaterRenderer.js +74 -25
- package/src/shade/renderer/water/chunk_water_gi.d.ts +14 -0
- package/src/shade/renderer/water/chunk_water_gi.d.ts.map +1 -0
- package/src/shade/renderer/water/chunk_water_gi.js +56 -0
- package/src/shade/renderer/water/chunk_water_transport.d.ts +2 -0
- package/src/shade/renderer/water/chunk_water_transport.d.ts.map +1 -1
- package/src/shade/renderer/water/chunk_water_transport.js +51 -40
- package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +29 -0
- package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +1 -0
- package/src/shade/renderer/water/medium/GPUWaterBodies.js +116 -0
- package/src/shade/renderer/water/medium/WATER_BODIES_STRUCT.d.ts +23 -0
- package/src/shade/renderer/water/medium/WATER_BODIES_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/water/medium/WATER_BODIES_STRUCT.js +42 -0
- package/src/shade/renderer/water/medium/chunk_water_medium_derived.d.ts +9 -0
- package/src/shade/renderer/water/medium/chunk_water_medium_derived.d.ts.map +1 -0
- package/src/shade/renderer/water/medium/chunk_water_medium_derived.js +55 -0
- package/src/shade/renderer/water/medium/chunk_water_medium_scattering.d.ts +8 -0
- package/src/shade/renderer/water/medium/chunk_water_medium_scattering.d.ts.map +1 -0
- package/src/shade/renderer/water/medium/chunk_water_medium_scattering.js +17 -0
- package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts +8 -0
- package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts.map +1 -0
- package/src/shade/renderer/water/medium/shader_water_bodies_derive.js +30 -0
- package/src/shade/renderer/water/optics/chunk_water_inscatter.d.ts +28 -0
- package/src/shade/renderer/water/optics/chunk_water_inscatter.d.ts.map +1 -0
- package/src/shade/renderer/water/optics/chunk_water_inscatter.js +123 -0
- package/src/shade/renderer/water/optics/chunk_water_light_transmittance.d.ts +23 -0
- package/src/shade/renderer/water/optics/chunk_water_light_transmittance.d.ts.map +1 -0
- package/src/shade/renderer/water/optics/chunk_water_light_transmittance_dry.d.ts +18 -0
- package/src/shade/renderer/water/optics/chunk_water_light_transmittance_dry.d.ts.map +1 -0
- package/src/shade/renderer/water/optics/chunk_water_optics.d.ts +12 -0
- package/src/shade/renderer/water/optics/chunk_water_optics.d.ts.map +1 -0
- package/src/shade/renderer/water/optics/chunk_water_optics.js +75 -0
- package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.d.ts +27 -0
- package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.d.ts.map +1 -0
- package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.js +74 -0
- package/src/shade/renderer/water/shader_water.d.ts +10 -4
- package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
- package/src/shade/renderer/water/shader_water.js +406 -384
- package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
- package/src/shade/renderer/water/shader_water_avboit.js +19 -9
- package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts +98 -0
- package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.js +408 -0
- package/src/shade/renderer/water/surface/WATER_SURFACE_VERTEX_STRUCT.d.ts +12 -0
- package/src/shade/renderer/water/surface/WATER_SURFACE_VERTEX_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/WATER_SURFACE_VERTEX_STRUCT.js +27 -0
- package/src/shade/renderer/water/surface/WATER_WAVE_PARAMETERS_STRUCT.d.ts +14 -0
- package/src/shade/renderer/water/surface/WATER_WAVE_PARAMETERS_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/WATER_WAVE_PARAMETERS_STRUCT.js +49 -0
- package/src/shade/renderer/water/surface/chunk_water_wave.d.ts +15 -0
- package/src/shade/renderer/water/surface/chunk_water_wave.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/chunk_water_wave.js +48 -0
- package/src/shade/renderer/water/surface/chunk_water_wave_grid.d.ts +9 -0
- package/src/shade/renderer/water/surface/chunk_water_wave_grid.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/chunk_water_wave_grid.js +47 -0
- package/src/shade/renderer/water/surface/shader_water_surface_write.d.ts +10 -0
- package/src/shade/renderer/water/surface/shader_water_surface_write.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/shader_water_surface_write.js +51 -0
- package/src/shade/renderer/water/surface/shader_water_wave_step.d.ts +12 -0
- package/src/shade/renderer/water/surface/shader_water_wave_step.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/shader_water_wave_step.js +54 -0
- package/src/shade/renderer/water/surface/water_surface_grid_indices.d.ts +13 -0
- package/src/shade/renderer/water/surface/water_surface_grid_indices.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/water_surface_grid_indices.js +39 -0
- package/src/shade/renderer/water/water_missing_receiver_path.d.ts +17 -0
- package/src/shade/renderer/water/water_missing_receiver_path.d.ts.map +1 -0
- package/src/shade/renderer/water/water_missing_receiver_path.js +40 -0
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +247 -23
- package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts +18 -14
- package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLLanguageCore.js +48 -6
- package/src/shade/renderer/shadow/map/csm/CSM_CASTER_RANGE_STRUCT.d.ts +0 -29
- package/src/shade/renderer/shadow/map/csm/CSM_CASTER_RANGE_STRUCT.d.ts.map +0 -1
- package/src/shade/renderer/shadow/map/csm/CSM_CASTER_RANGE_STRUCT.js +0 -43
- package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_CASCADE_COUNT.d.ts +0 -7
- package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_CASCADE_COUNT.d.ts.map +0 -1
- package/src/shade/renderer/shadow/map/csm/SHADOWMAP_CSM_CASCADE_COUNT.js +0 -6
|
@@ -1,39 +1,65 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { aabb3_transform_oriented_array } from "../../../core/geom/3d/aabb/aabb3_transform_oriented.js";
|
|
2
2
|
import { Triangle3D } from "../../../core/geom/3d/shape/Triangle3D.js";
|
|
3
3
|
import { body_id_index } from "../body/BodyStorage.js";
|
|
4
4
|
import { ColliderFlags } from "../ecs/ColliderFlags.js";
|
|
5
5
|
import { combine_friction, combine_restitution } from "../contact/combine_material.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
CONTACT_FLAG_SENSOR,
|
|
8
|
+
CONTACT_FLAGS_OFFSET,
|
|
9
|
+
CONTACT_STRIDE,
|
|
10
|
+
MAX_CONTACTS_PER_MANIFOLD
|
|
11
|
+
} from "../contact/ManifoldStore.js";
|
|
7
12
|
import { GJK_SEPARATED, gjk_epa_penetration } from "../../../core/geom/3d/gjk/gjk_epa_penetration.js";
|
|
8
|
-
import {
|
|
13
|
+
import { reduce_manifold_contacts_by_plane } from "./reduce_manifold_contacts.js";
|
|
9
14
|
import { mpr } from "../../../core/geom/3d/gjk/mpr.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
15
|
+
import { box_box_manifold_array, BOX_BOX_OUT_LENGTH } from "./box_box_manifold.js";
|
|
16
|
+
import { convex_hull_clip_array, CONVEX_CONVEX_OUT_LENGTH } from "./convex_convex_manifold.js";
|
|
12
17
|
import { get_mesh_convex_hull } from "./mesh_convex_hull.js";
|
|
13
18
|
import { mesh_mesh_tet_contacts } from "./mesh_mesh_tet_manifold.js";
|
|
14
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
BOX_TRIANGLE_AXIS_BOX_FACE,
|
|
21
|
+
BOX_TRIANGLE_AXIS_OFFSET,
|
|
22
|
+
BOX_TRIANGLE_AXIS_TRIANGLE_FACE,
|
|
23
|
+
box_triangle_contact_array,
|
|
24
|
+
BOX_TRIANGLE_OUT_LENGTH
|
|
25
|
+
} from "./box_triangle_contact.js";
|
|
15
26
|
import {
|
|
16
27
|
CAPSULE_BOX_CONTACT_STRIDE,
|
|
17
28
|
CAPSULE_BOX_MAX_CONTACTS,
|
|
18
|
-
|
|
19
|
-
capsule_capsule_contact,
|
|
29
|
+
capsule_box_multi_contacts_array,
|
|
20
30
|
CAPSULE_CAPSULE_CONTACT_STRIDE,
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
capsule_capsule_multi_contacts_array,
|
|
32
|
+
capsule_sphere_contact_array,
|
|
23
33
|
} from "./capsule_contacts.js";
|
|
24
34
|
import {
|
|
25
|
-
|
|
35
|
+
capsule_triangle_contact_array,
|
|
26
36
|
CAPSULE_TRIANGLE_CONTACT_STRIDE,
|
|
27
37
|
CAPSULE_TRIANGLE_MAX_CONTACTS,
|
|
28
38
|
} from "./capsule_triangle_contact.js";
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
39
|
+
import { aabb3_transform_oriented_inverse_array } from "../../../core/geom/3d/aabb/aabb3_transform_oriented_inverse.js";
|
|
40
|
+
import { decompose_to_triangles_array } from "./decomposition/decompose_to_triangles.js";
|
|
31
41
|
import { TRIANGLE_FLOAT_STRIDE } from "./decomposition/triangle_buffer_layout.js";
|
|
42
|
+
import { triangle_edge_flags, triangle_vertex_flat } from "./decomposition/triangle_edge_flags.js";
|
|
43
|
+
import { FLAT_EDGE_COS, TriangleEdgeFlags } from "../../../core/geom/3d/triangle/TriangleEdgeFlags.js";
|
|
32
44
|
import { PosedShape3D } from "../../../core/geom/3d/shape/PosedShape3D.js";
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
import { hull_sphere_contact_array } from "./hull_sphere_contact.js";
|
|
46
|
+
import {
|
|
47
|
+
feature_manifold,
|
|
48
|
+
FEATURE_MANIFOLD_OUT_LENGTH,
|
|
49
|
+
FEATURE_POLYGON,
|
|
50
|
+
support_feature,
|
|
51
|
+
SupportFeature,
|
|
52
|
+
triangle_feature
|
|
53
|
+
} from "./support_feature_manifold.js";
|
|
54
|
+
import { sphere_box_contact_array } from "./sphere_box_contact.js";
|
|
55
|
+
import { sphere_sphere_contact_array } from "./sphere_sphere_contact.js";
|
|
56
|
+
import { sphere_triangle_contact_array } from "./sphere_triangle_contact.js";
|
|
57
|
+
import {
|
|
58
|
+
Transform64,
|
|
59
|
+
TRANSFORM64_ROTATION_OFFSET,
|
|
60
|
+
TRANSFORM64_TRANSLATION_OFFSET
|
|
61
|
+
} from "../../ecs/transform/Transform64.js";
|
|
62
|
+
import { rigid_subject_pose } from "./rigid_subject_pose.js";
|
|
37
63
|
|
|
38
64
|
const posed_a = new PosedShape3D();
|
|
39
65
|
const posed_b = new PosedShape3D();
|
|
@@ -52,11 +78,26 @@ const box_triangle_result = new Float64Array(BOX_TRIANGLE_OUT_LENGTH);
|
|
|
52
78
|
const capsule_triangle_result = new Float64Array(CAPSULE_TRIANGLE_MAX_CONTACTS * CAPSULE_TRIANGLE_CONTACT_STRIDE);
|
|
53
79
|
const box_manifold_result = new Float64Array(BOX_BOX_OUT_LENGTH);
|
|
54
80
|
const convex_manifold_result = new Float64Array(CONVEX_CONVEX_OUT_LENGTH);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The two poses {@link convex_hull_clip} takes as arrays, copied out of the transforms: reading a
|
|
84
|
+
* Transform64's `translation` or `rotation` builds a fresh view on every read.
|
|
85
|
+
*/
|
|
86
|
+
const hull_position_a = new Float64Array(3);
|
|
87
|
+
const hull_rotation_a = new Float64Array(4);
|
|
88
|
+
const hull_position_b = new Float64Array(3);
|
|
89
|
+
const hull_rotation_b = new Float64Array(4);
|
|
90
|
+
|
|
55
91
|
const capsule_box_multi_result = new Float64Array(CAPSULE_BOX_MAX_CONTACTS * CAPSULE_BOX_CONTACT_STRIDE);
|
|
56
92
|
|
|
93
|
+
/** Supporting features of the two sides, and their manifold, for the GJK + EPA paths. */
|
|
94
|
+
const feature_a = new SupportFeature();
|
|
95
|
+
const feature_b = new SupportFeature();
|
|
96
|
+
const feature_manifold_result = new Float64Array(FEATURE_MANIFOLD_OUT_LENGTH);
|
|
97
|
+
|
|
57
98
|
/**
|
|
58
99
|
* Candidate-contact stride: wax, way, waz, wbx, wby, wbz, nx, ny, nz, depth,
|
|
59
|
-
* feature_id, friction, restitution.
|
|
100
|
+
* feature_id, friction, restitution, flags.
|
|
60
101
|
*
|
|
61
102
|
* The `feature_id` (offset 10) is a stable cross-frame identifier of the
|
|
62
103
|
* geometric feature pair that produced this contact — used by the
|
|
@@ -71,6 +112,9 @@ const capsule_box_multi_result = new Float64Array(CAPSULE_BOX_MAX_CONTACTS * CAP
|
|
|
71
112
|
* on each side) and carried into the manifold so a compound body's per-collider
|
|
72
113
|
* materials are honoured per-contact.
|
|
73
114
|
*
|
|
115
|
+
* `flags` (offset 13) are that same collider pair's per-contact bits — see
|
|
116
|
+
* {@link CONTACT_FLAG_SENSOR}.
|
|
117
|
+
*
|
|
74
118
|
* @type {number}
|
|
75
119
|
*/
|
|
76
120
|
const CANDIDATE_STRIDE = 14;
|
|
@@ -191,6 +235,9 @@ const CANDIDATE_CAPACITY_INITIAL = 64;
|
|
|
191
235
|
*/
|
|
192
236
|
let redetect_claimed = new Uint8Array(CANDIDATE_CAPACITY_INITIAL);
|
|
193
237
|
|
|
238
|
+
/** Data offsets of the contacts a re-detection found no candidate for (see {@link redetect_pair_geometry}). */
|
|
239
|
+
const redetect_stale = new Int32Array(64);
|
|
240
|
+
|
|
194
241
|
/**
|
|
195
242
|
* Per body-pair scratch buffer for candidate contacts produced by the
|
|
196
243
|
* cross-product of A's colliders × B's colliders.
|
|
@@ -230,36 +277,1110 @@ function grow_candidates() {
|
|
|
230
277
|
redetect_claimed = new Uint8Array(capacity);
|
|
231
278
|
}
|
|
232
279
|
|
|
280
|
+
/*
|
|
281
|
+
* ## Concave-dispatch contact dedup index
|
|
282
|
+
*
|
|
283
|
+
* Adjacent triangles report the same contact where they share an edge or a
|
|
284
|
+
* vertex, so each per-triangle path drops a new contact when an earlier one of
|
|
285
|
+
* the same dispatch matches it: the sphere path when a normal lies within
|
|
286
|
+
* 0.001 (squared) of the new one, the box / capsule / feature paths when a
|
|
287
|
+
* normal does AND the stored A-side witness lies within 1e-6 (squared) of the
|
|
288
|
+
* new one. That was a scan over every earlier contact of the dispatch, per
|
|
289
|
+
* contact: quadratic in contacts, and with the triangle cap gone a 4 m crate
|
|
290
|
+
* on a floor meshed at 10 cm paid ~100 ms a step for it.
|
|
291
|
+
*
|
|
292
|
+
* The test is unchanged; what changes is which earlier contacts it is run
|
|
293
|
+
* against. Each kept contact is filed under a grid cell of its key — the
|
|
294
|
+
* witness for the point paths, the normal for the sphere path — and a new
|
|
295
|
+
* contact is tested, with the very same arithmetic, against the contacts filed
|
|
296
|
+
* in the cells its tolerance can reach. Whether a match exists does not depend
|
|
297
|
+
* on the order the earlier contacts are tested in, so the contacts kept, and
|
|
298
|
+
* their order, are exactly what the scan kept.
|
|
299
|
+
*
|
|
300
|
+
* Reach: a squared distance that rounds below 1e-6 has every component under
|
|
301
|
+
* 1e-3 (1 + 3 ulp); cells of 1/256 put that at 0.256 of a cell, so only the
|
|
302
|
+
* neighbour across a face nearer than {@link DEDUP_REACH} can hold a match.
|
|
303
|
+
* Normals: under sqrt(0.001) = 0.0316 per component, cells of 1/8, 0.253 of a
|
|
304
|
+
* cell. The scales are powers of two, so `key * scale` and its fraction are
|
|
305
|
+
* exact. A component that scales to a non-finite value files and looks up in
|
|
306
|
+
* cell 0 on that axis alike, so equal keys still meet (a non-finite key never
|
|
307
|
+
* passes the test anyway).
|
|
308
|
+
*
|
|
309
|
+
* Buckets are reset by stamp, entries by count, so a dispatch costs nothing
|
|
310
|
+
* per bucket; both grow with the high-water mark and are kept.
|
|
311
|
+
*/
|
|
312
|
+
|
|
313
|
+
/** Cell size 1/256 m for witness points (tolerance 1 mm). */
|
|
314
|
+
const DEDUP_POINT_SCALE = 256;
|
|
315
|
+
|
|
316
|
+
/** Cell size 1/8 for unit normals (tolerance 0.0316). */
|
|
317
|
+
const DEDUP_NORMAL_SCALE = 8;
|
|
318
|
+
|
|
319
|
+
/** Either tolerance in cell units (0.256, 0.253), rounded up. */
|
|
320
|
+
const DEDUP_REACH = 0.26;
|
|
321
|
+
|
|
322
|
+
/** @type {Int32Array} first entry of each bucket, valid when its stamp is current */
|
|
323
|
+
let dedup_bucket_head = new Int32Array(1024);
|
|
324
|
+
/** @type {Int32Array} */
|
|
325
|
+
let dedup_bucket_stamp = new Int32Array(1024);
|
|
326
|
+
/** @type {Int32Array} candidate index of each entry */
|
|
327
|
+
let dedup_entry_candidate = new Int32Array(1024);
|
|
328
|
+
/** @type {Int32Array} next entry in the same bucket, -1 ends */
|
|
329
|
+
let dedup_entry_next = new Int32Array(1024);
|
|
330
|
+
let dedup_entry_count = 0;
|
|
331
|
+
let dedup_stamp = 0;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Key of the dispatch in progress: the normal (sphere path) or the A-side
|
|
335
|
+
* witness (every other path). One dispatch takes one path.
|
|
336
|
+
* @type {boolean}
|
|
337
|
+
*/
|
|
338
|
+
let dedup_by_normal = false;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Start an empty index for a concave dispatch.
|
|
342
|
+
* @param {boolean} by_normal
|
|
343
|
+
*/
|
|
344
|
+
function dedup_begin(by_normal) {
|
|
345
|
+
dedup_by_normal = by_normal;
|
|
346
|
+
dedup_entry_count = 0;
|
|
347
|
+
dedup_stamp++;
|
|
348
|
+
|
|
349
|
+
if (dedup_stamp === 0x7FFFFFFF) {
|
|
350
|
+
dedup_bucket_stamp.fill(0);
|
|
351
|
+
dedup_stamp = 1;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* @param {number} cx integer-valued cell coordinate (any double; non-finite folds to 0)
|
|
357
|
+
* @param {number} cy
|
|
358
|
+
* @param {number} cz
|
|
359
|
+
* @returns {number} bucket index
|
|
360
|
+
*/
|
|
361
|
+
function dedup_bucket(cx, cy, cz) {
|
|
362
|
+
return (Math.imul(cx | 0, 73856093) ^ Math.imul(cy | 0, 19349663) ^ Math.imul(cz | 0, 83492791))
|
|
363
|
+
& (dedup_bucket_head.length - 1);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* File entry `e` (already holding its candidate index) under its key's cell.
|
|
368
|
+
* @param {number} e
|
|
369
|
+
*/
|
|
370
|
+
function dedup_file(e) {
|
|
371
|
+
const scale = dedup_by_normal ? DEDUP_NORMAL_SCALE : DEDUP_POINT_SCALE;
|
|
372
|
+
const o = dedup_entry_candidate[e] * CANDIDATE_STRIDE + (dedup_by_normal ? 6 : 0);
|
|
373
|
+
|
|
374
|
+
const b = dedup_bucket(
|
|
375
|
+
Math.floor(candidates[o] * scale),
|
|
376
|
+
Math.floor(candidates[o + 1] * scale),
|
|
377
|
+
Math.floor(candidates[o + 2] * scale)
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
if (dedup_bucket_stamp[b] !== dedup_stamp) {
|
|
381
|
+
dedup_bucket_stamp[b] = dedup_stamp;
|
|
382
|
+
dedup_bucket_head[b] = -1;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
dedup_entry_next[e] = dedup_bucket_head[b];
|
|
386
|
+
dedup_bucket_head[b] = e;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Index candidate `index`, just appended by the dispatch in progress.
|
|
391
|
+
* @param {number} index
|
|
392
|
+
*/
|
|
393
|
+
function dedup_insert(index) {
|
|
394
|
+
const e = dedup_entry_count;
|
|
395
|
+
|
|
396
|
+
if (e === dedup_entry_candidate.length) {
|
|
397
|
+
const candidate = new Int32Array(e * 2);
|
|
398
|
+
candidate.set(dedup_entry_candidate);
|
|
399
|
+
dedup_entry_candidate = candidate;
|
|
400
|
+
dedup_entry_next = new Int32Array(e * 2);
|
|
401
|
+
|
|
402
|
+
// re-chain below, with the buckets
|
|
403
|
+
dedup_entry_count = e;
|
|
404
|
+
dedup_rebuild(dedup_bucket_head.length);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
dedup_entry_candidate[e] = index;
|
|
408
|
+
dedup_entry_count = e + 1;
|
|
409
|
+
|
|
410
|
+
if (dedup_entry_count * 2 > dedup_bucket_head.length) {
|
|
411
|
+
dedup_rebuild(dedup_bucket_head.length * 2);
|
|
412
|
+
} else {
|
|
413
|
+
dedup_file(e);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Re-file every entry into `bucket_count` buckets.
|
|
419
|
+
* @param {number} bucket_count power of two
|
|
420
|
+
*/
|
|
421
|
+
function dedup_rebuild(bucket_count) {
|
|
422
|
+
if (bucket_count !== dedup_bucket_head.length) {
|
|
423
|
+
dedup_bucket_head = new Int32Array(bucket_count);
|
|
424
|
+
dedup_bucket_stamp = new Int32Array(bucket_count);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// a fresh stamp empties every bucket
|
|
428
|
+
dedup_stamp++;
|
|
429
|
+
|
|
430
|
+
if (dedup_stamp === 0x7FFFFFFF) {
|
|
431
|
+
dedup_bucket_stamp.fill(0);
|
|
432
|
+
dedup_stamp = 1;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
for (let e = 0; e < dedup_entry_count; e++) {
|
|
436
|
+
dedup_file(e);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
233
440
|
/**
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
441
|
+
* Whether an earlier contact of the dispatch in progress matches a new one —
|
|
442
|
+
* the per-path test, run against the contacts filed where it can pass.
|
|
443
|
+
*
|
|
444
|
+
* The new contact is read from a record laid out as {@link append_contact}
|
|
445
|
+
* takes its numbers: A-side witness at `offset` (ignored when keyed by
|
|
446
|
+
* normal), stored normal at `offset + 6`. See {@link staged_contact}.
|
|
447
|
+
*
|
|
448
|
+
* @param {Float64Array} contact
|
|
449
|
+
* @param {number} offset
|
|
450
|
+
* @returns {boolean}
|
|
451
|
+
*/
|
|
452
|
+
function dedup_contains(contact, offset) {
|
|
453
|
+
const px = contact[offset], py = contact[offset + 1], pz = contact[offset + 2];
|
|
454
|
+
const nx = contact[offset + 6], ny = contact[offset + 7], nz = contact[offset + 8];
|
|
455
|
+
|
|
456
|
+
const by_normal = dedup_by_normal;
|
|
457
|
+
const scale = by_normal ? DEDUP_NORMAL_SCALE : DEDUP_POINT_SCALE;
|
|
458
|
+
|
|
459
|
+
const sx = (by_normal ? nx : px) * scale;
|
|
460
|
+
const sy = (by_normal ? ny : py) * scale;
|
|
461
|
+
const sz = (by_normal ? nz : pz) * scale;
|
|
462
|
+
|
|
463
|
+
const cx = Math.floor(sx), cy = Math.floor(sy), cz = Math.floor(sz);
|
|
464
|
+
const fx = sx - cx, fy = sy - cy, fz = sz - cz;
|
|
465
|
+
|
|
466
|
+
// cells per axis: the key's own, plus a neighbour whose face is in reach
|
|
467
|
+
const x0 = fx < DEDUP_REACH ? cx - 1 : cx;
|
|
468
|
+
const y0 = fy < DEDUP_REACH ? cy - 1 : cy;
|
|
469
|
+
const z0 = fz < DEDUP_REACH ? cz - 1 : cz;
|
|
470
|
+
const nx_cells = 1 + (fx < DEDUP_REACH ? 1 : 0) + (fx > 1 - DEDUP_REACH ? 1 : 0);
|
|
471
|
+
const ny_cells = 1 + (fy < DEDUP_REACH ? 1 : 0) + (fy > 1 - DEDUP_REACH ? 1 : 0);
|
|
472
|
+
const nz_cells = 1 + (fz < DEDUP_REACH ? 1 : 0) + (fz > 1 - DEDUP_REACH ? 1 : 0);
|
|
473
|
+
|
|
474
|
+
for (let ix = 0; ix < nx_cells; ix++) {
|
|
475
|
+
for (let iy = 0; iy < ny_cells; iy++) {
|
|
476
|
+
for (let iz = 0; iz < nz_cells; iz++) {
|
|
477
|
+
const b = dedup_bucket(x0 + ix, y0 + iy, z0 + iz);
|
|
478
|
+
|
|
479
|
+
if (dedup_bucket_stamp[b] !== dedup_stamp) continue;
|
|
480
|
+
|
|
481
|
+
for (let e = dedup_bucket_head[b]; e !== -1; e = dedup_entry_next[e]) {
|
|
482
|
+
const qo = dedup_entry_candidate[e] * CANDIDATE_STRIDE;
|
|
483
|
+
|
|
484
|
+
const dnx = candidates[qo + 6] - nx;
|
|
485
|
+
const dny = candidates[qo + 7] - ny;
|
|
486
|
+
const dnz = candidates[qo + 8] - nz;
|
|
487
|
+
|
|
488
|
+
if (dnx * dnx + dny * dny + dnz * dnz < 0.001) {
|
|
489
|
+
if (by_normal) return true;
|
|
490
|
+
|
|
491
|
+
const dpx = candidates[qo] - px;
|
|
492
|
+
const dpy = candidates[qo + 1] - py;
|
|
493
|
+
const dpz = candidates[qo + 2] - pz;
|
|
494
|
+
|
|
495
|
+
if (dpx * dpx + dpy * dpy + dpz * dpz < 1e-6) return true;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/*
|
|
506
|
+
* ## Internal edges ("ghost" contacts)
|
|
238
507
|
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
508
|
+
* A flat floor cut into triangles is collided one triangle at a time, and each triangle on its own has
|
|
509
|
+
* edges the floor does not. A body sliding from one triangle onto the next first touches the next
|
|
510
|
+
* one's near edge — a sliver of overlap the per-triangle routine resolves along whatever axis is
|
|
511
|
+
* shortest for that triangle alone: the box's leading face (pushing it back), or an edge-edge axis
|
|
512
|
+
* tilted off the floor's normal (kicking it up and spinning it). A sphere crossing the same edge gets
|
|
513
|
+
* a second contact on it, beside the face contact of the triangle it is over, tilted and as deep. A
|
|
514
|
+
* frictionless box crossing a flat triangulated floor at 6 m/s hopped 0.1 m and lost a third of its
|
|
515
|
+
* speed; a sphere hopped by centimetres; both crossed a box floor without either.
|
|
245
516
|
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
517
|
+
* Box3D's answer (mesh_contact.c, `b3ComputeMeshManifolds`, MIT, Erin Catto), ported here: each
|
|
518
|
+
* triangle's contacts are FACE contacts — on the triangle's face, or a face of the convex body lying
|
|
519
|
+
* within 60° of the triangle's plane — or TENTATIVE: on an edge or a vertex of the triangle. Face
|
|
520
|
+
* contacts are kept as they are. (Box3D counts a deep contact on a body face as a face contact at any
|
|
521
|
+
* angle, so that it is never dropped; here no contact is dropped for lying on a flat feature, and the
|
|
522
|
+
* rule only kept a body face's normal where the plane's belonged: a box landing tilted on a small
|
|
523
|
+
* triangle, its back face 12 mm into it, was pushed sideways by its own face and stopped dead on a flat
|
|
524
|
+
* floor.) Tentative ones:
|
|
253
525
|
*
|
|
526
|
+
* - a sphere's are kept, closest first, when the feature they touch (an edge or a vertex) is not yet
|
|
527
|
+
* claimed — by a face contact's triangle, or by a nearer tentative one's (each claims its edges and
|
|
528
|
+
* vertices whether or not it was kept) — and dropped otherwise, as Box3D does. A sphere's face
|
|
529
|
+
* contact is the nearest point of the surface, so a contact on an edge or vertex of that face's
|
|
530
|
+
* triangle can only repeat it, further off and tilted. Claims go by coordinates, though, and a
|
|
531
|
+
* neighbour can have the edge under other ones — its copy of a vertex a hair away, or the edge
|
|
532
|
+
* split at a T-junction — so a kept contact on a FLAT edge or corner is taken as the plane's,
|
|
533
|
+
* like everything else's below;
|
|
534
|
+
* - anything else's are judged by where on its triangle the contact lies ({@link
|
|
535
|
+
* triangle_witness_feature}). Inside the triangle, or on an edge or a corner that is {@link
|
|
536
|
+
* TriangleEdgeFlags FLAT} — no feature of the surface, an artefact of its triangulation — the
|
|
537
|
+
* contact is taken as the plane's: its normal becomes the triangle's, its depth the body's depth
|
|
538
|
+
* below that plane (dropped if not below it). On a real edge or corner — convex, concave, the rim,
|
|
539
|
+
* a vertex any real edge meets — it is kept as it is: the surface has that feature. A round body
|
|
540
|
+
* (sphere, capsule) touches a plane under its core. Where that point lies off the triangle and the
|
|
541
|
+
* contact meets the feature from more than a flat edge's bend off the plane's normal, the body is
|
|
542
|
+
* not on this face — a capsule riding over a 1 cm step touched the riser's internal diagonal from
|
|
543
|
+
* above, and taken as the riser's plane that contact pushed it back by its whole 8 cm intrusion into
|
|
544
|
+
* that plane, turning it round — and the contact goes. Otherwise it is the plane's: on the top of a
|
|
545
|
+
* convex crease flatter than that the point lies off both triangles, and neither holds the body.
|
|
546
|
+
*
|
|
547
|
+
* Box3D judges the triangle instead: a tentative contact goes when every edge of its triangle is
|
|
548
|
+
* flat, or one of them is flat and shared with a triangle that made a face contact — wherever on
|
|
549
|
+
* the triangle the contact lies. The triangle beside a ridge whose one corner is on the crest has
|
|
550
|
+
* every edge flat, so its contact at that corner, on the crest, went too: a box resting on two
|
|
551
|
+
* knife-edged rails got the flanks' normals there, 72° off vertical, and slid off sideways, and a
|
|
552
|
+
* lying cylinder driven into a 0.3 m riser spun up to 3.2 rad/s. And Box3D drops these contacts,
|
|
553
|
+
* where this takes them as the plane's: its triangle-hull SAT prefers a face axis, falling back to
|
|
554
|
+
* an edge pair only when face clipping fails, so its tentative contacts are the odd few; this
|
|
555
|
+
* box-triangle SAT takes whichever axis overlaps least, so a box corner pressed onto a vertex of a
|
|
556
|
+
* finely cut convex surface gets all its deep contacts on edge axes — and dropping them sank a cube
|
|
557
|
+
* balanced on a corner of a dome 166 mm where it used to sink 2 mm.
|
|
558
|
+
*
|
|
559
|
+
* Features are recognised by their coordinates in the shape's frame, which a vertex shared by two
|
|
560
|
+
* triangles has bit for bit in both (see triangle_buffer_layout.js). Edge and corner classes are
|
|
561
|
+
* looked up ({@link triangle_edge_flags}, {@link triangle_vertex_flat}) for the triangles of tentative
|
|
562
|
+
* contacts only: a mesh keeps them, a heightmap samples them. Classifying every enumerated heightmap
|
|
563
|
+
* triangle instead — a ring of extra samples around each query — cost a heightmap terrain scene a
|
|
564
|
+
* fifth of its step.
|
|
565
|
+
*/
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* A contact whose normal is within this cosine of its triangle's face normal is on the face. The
|
|
569
|
+
* closest-point paths (sphere, capsule) leave the normal exactly the face's when the nearest point is
|
|
570
|
+
* inside the triangle, so this only has to absorb rounding.
|
|
571
|
+
* @type {number}
|
|
572
|
+
*/
|
|
573
|
+
const FACE_CONTACT_COS = 1 - 1e-6;
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* A face of the convex body clipping a triangle makes a face contact when within this cosine of the
|
|
577
|
+
* triangle's normal: Box3D's 0.5 (60°) for a hull face.
|
|
254
578
|
* @type {number}
|
|
255
579
|
*/
|
|
256
|
-
const
|
|
580
|
+
const HULL_FACE_COS = 0.5;
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Feature of a triangle a tentative contact touches: edges A→B, B→C, C→A, then vertices A, B, C, then
|
|
585
|
+
* the face; NONE for a triangle with no area to place a point in.
|
|
586
|
+
*/
|
|
587
|
+
const FEATURE_EDGE_AB = 0;
|
|
588
|
+
const FEATURE_VERTEX_A = 3;
|
|
589
|
+
const FEATURE_FACE = 6;
|
|
590
|
+
const FEATURE_NONE = 7;
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* A point is on an edge of a triangle when its barycentric weight of the vertex across from that edge
|
|
594
|
+
* is at most this: a micrometre from the edge of a triangle a metre across. The contact routines
|
|
595
|
+
* place their points on an edge by interpolating along it, which is exact to rounding, many orders
|
|
596
|
+
* below this; a point really inside the triangle, as near the edge as this, is on the plane either way.
|
|
597
|
+
* @type {number}
|
|
598
|
+
*/
|
|
599
|
+
const ON_EDGE_WEIGHT = 1e-6;
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Tentative contacts of the concave dispatch in progress. Stride: the eleven values
|
|
603
|
+
* {@link append_contact} takes (A witness, B witness, stored normal, depth, fid), the index of the
|
|
604
|
+
* triangle in {@link triangle_buffer}, its feature on that triangle, and the sort key (squared distance
|
|
605
|
+
* to the sphere's centre). Grown by doubling, high-water mark kept.
|
|
606
|
+
*/
|
|
607
|
+
const TENTATIVE_STRIDE = 14;
|
|
608
|
+
let tentatives = new Float64Array(64 * TENTATIVE_STRIDE);
|
|
609
|
+
let tentative_count = 0;
|
|
610
|
+
|
|
611
|
+
/** Processing order of the tentatives (closest first, for the sphere rule). */
|
|
612
|
+
let tentative_order = new Int32Array(64);
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Indices in {@link triangle_buffer} of the triangles with a face contact in the sphere dispatch in
|
|
616
|
+
* progress (the sphere rule's claims).
|
|
617
|
+
*/
|
|
618
|
+
let face_triangles = new Int32Array(64);
|
|
619
|
+
let face_triangle_count = 0;
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* A contact the concave dispatch is about to keep, stash or test, in the order {@link append_contact}
|
|
623
|
+
* takes its numbers — A witness (0..2), B witness (3..5), stored normal (6..8), depth (9), fid (10) —
|
|
624
|
+
* then a tentative contact's sort key (11). The per-contact helpers below take it as an array: none of
|
|
625
|
+
* them is inlined into dispatch_pair, and a call that is not inlined boxes each number passed to it.
|
|
626
|
+
*/
|
|
627
|
+
const staged_contact = new Float64Array(12);
|
|
257
628
|
|
|
258
629
|
/**
|
|
259
|
-
*
|
|
630
|
+
* Stash a tentative contact, already oriented as {@link append_contact} wants it: the eleven values
|
|
631
|
+
* and the sort key of `contact` (see {@link staged_contact}).
|
|
632
|
+
*
|
|
633
|
+
* @param {Float64Array} contact
|
|
634
|
+
* @param {number} triangle
|
|
635
|
+
* @param {number} feature
|
|
636
|
+
*/
|
|
637
|
+
function tentative_add(contact, triangle, feature) {
|
|
638
|
+
if ((tentative_count + 1) * TENTATIVE_STRIDE > tentatives.length) {
|
|
639
|
+
const grown = new Float64Array(tentatives.length * 2);
|
|
640
|
+
grown.set(tentatives);
|
|
641
|
+
tentatives = grown;
|
|
642
|
+
tentative_order = new Int32Array(grown.length / TENTATIVE_STRIDE);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const o = tentative_count * TENTATIVE_STRIDE;
|
|
646
|
+
|
|
647
|
+
tentatives[o] = contact[0]; tentatives[o + 1] = contact[1]; tentatives[o + 2] = contact[2];
|
|
648
|
+
tentatives[o + 3] = contact[3]; tentatives[o + 4] = contact[4]; tentatives[o + 5] = contact[5];
|
|
649
|
+
tentatives[o + 6] = contact[6]; tentatives[o + 7] = contact[7]; tentatives[o + 8] = contact[8];
|
|
650
|
+
tentatives[o + 9] = contact[9];
|
|
651
|
+
tentatives[o + 10] = contact[10];
|
|
652
|
+
tentatives[o + 11] = triangle;
|
|
653
|
+
tentatives[o + 12] = feature;
|
|
654
|
+
tentatives[o + 13] = contact[11];
|
|
655
|
+
|
|
656
|
+
tentative_count++;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Note that triangle `i` (index in {@link triangle_buffer}) made a face contact. Its contacts arrive
|
|
661
|
+
* together, so a repeat is the last entry.
|
|
662
|
+
* @param {number} i
|
|
663
|
+
*/
|
|
664
|
+
function face_triangle_add(i) {
|
|
665
|
+
if (face_triangle_count > 0 && face_triangles[face_triangle_count - 1] === i) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
if (face_triangle_count === face_triangles.length) {
|
|
670
|
+
const grown = new Int32Array(face_triangle_count * 2);
|
|
671
|
+
grown.set(face_triangles);
|
|
672
|
+
face_triangles = grown;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
face_triangles[face_triangle_count++] = i;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Which feature of a triangle faces a direction: the vertices tied at its extreme along it — an edge
|
|
680
|
+
* for two, a vertex for one, the face for three. For a closest-point contact, the direction from the
|
|
681
|
+
* triangle to the body, this is the region the nearest point lies in.
|
|
682
|
+
*
|
|
683
|
+
* The triangle (world) and the direction are read from arrays, as the concave dispatch has them.
|
|
684
|
+
*
|
|
685
|
+
* @param {Float64Array} triangle vertices A, B, C
|
|
686
|
+
* @param {Float64Array} direction
|
|
687
|
+
* @param {number} direction_offset
|
|
688
|
+
* @returns {number} {@link FEATURE_EDGE_AB} + k for edge k, {@link FEATURE_VERTEX_A} + k for vertex k,
|
|
689
|
+
* {@link FEATURE_FACE}
|
|
690
|
+
*/
|
|
691
|
+
function triangle_facing_feature(triangle, direction, direction_offset) {
|
|
692
|
+
const ax = triangle[0], ay = triangle[1], az = triangle[2];
|
|
693
|
+
const bx = triangle[3], by = triangle[4], bz = triangle[5];
|
|
694
|
+
const cx = triangle[6], cy = triangle[7], cz = triangle[8];
|
|
695
|
+
const dx = direction[direction_offset], dy = direction[direction_offset + 1], dz = direction[direction_offset + 2];
|
|
696
|
+
|
|
697
|
+
const da = ax * dx + ay * dy + az * dz;
|
|
698
|
+
const db = bx * dx + by * dy + bz * dz;
|
|
699
|
+
const dc = cx * dx + cy * dy + cz * dz;
|
|
700
|
+
|
|
701
|
+
const hi = Math.max(da, db, dc);
|
|
702
|
+
const floor = hi - (hi - Math.min(da, db, dc)) * 1e-6;
|
|
703
|
+
|
|
704
|
+
const ta = da >= floor, tb = db >= floor, tc = dc >= floor;
|
|
705
|
+
|
|
706
|
+
if (ta && tb && tc) return FEATURE_FACE;
|
|
707
|
+
if (ta && tb) return FEATURE_EDGE_AB;
|
|
708
|
+
if (tb && tc) return FEATURE_EDGE_AB + 1;
|
|
709
|
+
if (tc && ta) return FEATURE_EDGE_AB + 2;
|
|
710
|
+
if (ta) return FEATURE_VERTEX_A;
|
|
711
|
+
if (tb) return FEATURE_VERTEX_A + 1;
|
|
712
|
+
return FEATURE_VERTEX_A + 2;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Which feature of a triangle a point on it lies on: an edge when within {@link ON_EDGE_WEIGHT} of it
|
|
717
|
+
* (or past it), the vertex two such edges meet, else the face. The point is taken where it projects
|
|
718
|
+
* onto the triangle's plane.
|
|
719
|
+
*
|
|
720
|
+
* The triangle (world) and the point are read from arrays, as the concave dispatch has them.
|
|
721
|
+
*
|
|
722
|
+
* @param {Float64Array} triangle vertices A, B, C
|
|
723
|
+
* @param {Float64Array} point
|
|
724
|
+
* @param {number} point_offset
|
|
725
|
+
* @returns {number} {@link FEATURE_EDGE_AB} + k for edge k, {@link FEATURE_VERTEX_A} + k for vertex k,
|
|
726
|
+
* {@link FEATURE_FACE}, or {@link FEATURE_NONE} for a triangle without area
|
|
727
|
+
*/
|
|
728
|
+
function triangle_witness_feature(triangle, point, point_offset) {
|
|
729
|
+
const ax = triangle[0], ay = triangle[1], az = triangle[2];
|
|
730
|
+
const bx = triangle[3], by = triangle[4], bz = triangle[5];
|
|
731
|
+
const cx = triangle[6], cy = triangle[7], cz = triangle[8];
|
|
732
|
+
const px = point[point_offset], py = point[point_offset + 1], pz = point[point_offset + 2];
|
|
733
|
+
|
|
734
|
+
const e1x = bx - ax, e1y = by - ay, e1z = bz - az;
|
|
735
|
+
const e2x = cx - ax, e2y = cy - ay, e2z = cz - az;
|
|
736
|
+
|
|
737
|
+
const nx = e1y * e2z - e1z * e2y;
|
|
738
|
+
const ny = e1z * e2x - e1x * e2z;
|
|
739
|
+
const nz = e1x * e2y - e1y * e2x;
|
|
740
|
+
|
|
741
|
+
const nn = nx * nx + ny * ny + nz * nz;
|
|
742
|
+
|
|
743
|
+
if (!(nn > 0)) {
|
|
744
|
+
return FEATURE_NONE;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// weights of A and B: the signed areas of (P, B, C) and (P, C, A) over the triangle's
|
|
748
|
+
const pax = ax - px, pay = ay - py, paz = az - pz;
|
|
749
|
+
const pbx = bx - px, pby = by - py, pbz = bz - pz;
|
|
750
|
+
const pcx = cx - px, pcy = cy - py, pcz = cz - pz;
|
|
751
|
+
|
|
752
|
+
const wa = ((pby * pcz - pbz * pcy) * nx + (pbz * pcx - pbx * pcz) * ny + (pbx * pcy - pby * pcx) * nz) / nn;
|
|
753
|
+
const wb = ((pcy * paz - pcz * pay) * nx + (pcz * pax - pcx * paz) * ny + (pcx * pay - pcy * pax) * nz) / nn;
|
|
754
|
+
const wc = 1 - wa - wb;
|
|
755
|
+
|
|
756
|
+
// on edge k: the weight of the vertex across from it, k + 2, vanishes
|
|
757
|
+
const on_ab = wc <= ON_EDGE_WEIGHT;
|
|
758
|
+
const on_bc = wa <= ON_EDGE_WEIGHT;
|
|
759
|
+
const on_ca = wb <= ON_EDGE_WEIGHT;
|
|
760
|
+
|
|
761
|
+
if (on_ab) {
|
|
762
|
+
if (on_bc) return on_ca ? FEATURE_NONE : FEATURE_VERTEX_A + 1;
|
|
763
|
+
return on_ca ? FEATURE_VERTEX_A : FEATURE_EDGE_AB;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
if (on_bc) {
|
|
767
|
+
return on_ca ? FEATURE_VERTEX_A + 2 : FEATURE_EDGE_AB + 1;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
return on_ca ? FEATURE_EDGE_AB + 2 : FEATURE_FACE;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/** Bits of a float, for hashing coordinates. */
|
|
774
|
+
const key_float = new Float64Array(1);
|
|
775
|
+
const key_words = new Uint32Array(key_float.buffer);
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* A set of points (width 3) or point pairs (width 6) keyed by their exact coordinates — the vertices
|
|
779
|
+
* and edges a concave dispatch has claimed. Open addressing; emptied by stamp, grown by doubling, the
|
|
780
|
+
* high-water mark kept.
|
|
781
|
+
*/
|
|
782
|
+
class CoordinateKeySet {
|
|
783
|
+
/**
|
|
784
|
+
* @param {number} width floats per key
|
|
785
|
+
*/
|
|
786
|
+
constructor(width) {
|
|
787
|
+
this.width = width;
|
|
788
|
+
this.keys = new Float64Array(64 * width);
|
|
789
|
+
this.stamps = new Int32Array(64);
|
|
790
|
+
this.stamp = 1;
|
|
791
|
+
this.size = 0;
|
|
792
|
+
|
|
793
|
+
/** Rehash scratch: a caller's key must survive the growth its add triggers. */
|
|
794
|
+
this.rehash_key = new Float64Array(width);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
clear() {
|
|
798
|
+
this.size = 0;
|
|
799
|
+
this.stamp++;
|
|
800
|
+
|
|
801
|
+
if (this.stamp === 0x7FFFFFFF) {
|
|
802
|
+
this.stamps.fill(0);
|
|
803
|
+
this.stamp = 1;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* @param {Float64Array} key `width` floats
|
|
809
|
+
* @returns {number}
|
|
810
|
+
*/
|
|
811
|
+
hash(key) {
|
|
812
|
+
let h = 0;
|
|
813
|
+
|
|
814
|
+
for (let i = 0; i < this.width; i++) {
|
|
815
|
+
// + 0 folds -0 into 0: equal coordinates must hash equal
|
|
816
|
+
key_float[0] = key[i] + 0;
|
|
817
|
+
h = Math.imul(h ^ key_words[0], 0x85EBCA6B) ^ Math.imul(key_words[1], 0xC2B2AE35);
|
|
818
|
+
h ^= h >>> 15;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
return h;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Add a key.
|
|
826
|
+
* @param {Float64Array} key `width` floats
|
|
827
|
+
* @returns {boolean} whether it was not there before
|
|
828
|
+
*/
|
|
829
|
+
add(key) {
|
|
830
|
+
if ((this.size + 1) * 2 > this.stamps.length) {
|
|
831
|
+
this.grow();
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const width = this.width;
|
|
835
|
+
const mask = this.stamps.length - 1;
|
|
836
|
+
|
|
837
|
+
for (let slot = this.hash(key) & mask; ; slot = (slot + 1) & mask) {
|
|
838
|
+
if (this.stamps[slot] !== this.stamp) {
|
|
839
|
+
this.stamps[slot] = this.stamp;
|
|
840
|
+
this.keys.set(key, slot * width);
|
|
841
|
+
this.size++;
|
|
842
|
+
return true;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
if (this.equals(slot, key)) {
|
|
846
|
+
return false;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* @param {Float64Array} key `width` floats
|
|
853
|
+
* @returns {boolean}
|
|
854
|
+
*/
|
|
855
|
+
has(key) {
|
|
856
|
+
const mask = this.stamps.length - 1;
|
|
857
|
+
|
|
858
|
+
for (let slot = this.hash(key) & mask; ; slot = (slot + 1) & mask) {
|
|
859
|
+
if (this.stamps[slot] !== this.stamp) {
|
|
860
|
+
return false;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
if (this.equals(slot, key)) {
|
|
864
|
+
return true;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
equals(slot, key) {
|
|
870
|
+
const width = this.width;
|
|
871
|
+
const o = slot * width;
|
|
872
|
+
|
|
873
|
+
for (let i = 0; i < width; i++) {
|
|
874
|
+
if (this.keys[o + i] !== key[i]) {
|
|
875
|
+
return false;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return true;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
grow() {
|
|
883
|
+
const width = this.width;
|
|
884
|
+
const old_keys = this.keys;
|
|
885
|
+
const old_stamps = this.stamps;
|
|
886
|
+
const old_stamp = this.stamp;
|
|
887
|
+
const capacity = old_stamps.length * 2;
|
|
888
|
+
|
|
889
|
+
this.keys = new Float64Array(capacity * width);
|
|
890
|
+
this.stamps = new Int32Array(capacity);
|
|
891
|
+
this.stamp = 1;
|
|
892
|
+
this.size = 0;
|
|
893
|
+
|
|
894
|
+
const key = this.rehash_key;
|
|
895
|
+
|
|
896
|
+
for (let slot = 0; slot < old_stamps.length; slot++) {
|
|
897
|
+
if (old_stamps[slot] !== old_stamp) continue;
|
|
898
|
+
|
|
899
|
+
for (let i = 0; i < width; i++) key[i] = old_keys[slot * width + i];
|
|
900
|
+
this.add(key);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/** Key scratch: a vertex, and an edge with its end points in lexicographic order. */
|
|
906
|
+
const key_vertex = new Float64Array(3);
|
|
907
|
+
const key_edge = new Float64Array(6);
|
|
908
|
+
|
|
909
|
+
/** Vertices and edges claimed in the sphere dispatch in progress (the sphere rule). */
|
|
910
|
+
const claimed_vertices = new CoordinateKeySet(3);
|
|
911
|
+
const claimed_edges = new CoordinateKeySet(6);
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Write vertex `k` of the triangle at `o` in {@link triangle_buffer} into {@link key_vertex}.
|
|
915
|
+
*/
|
|
916
|
+
function key_set_vertex(o, k) {
|
|
917
|
+
key_vertex[0] = triangle_buffer[o + k * 3];
|
|
918
|
+
key_vertex[1] = triangle_buffer[o + k * 3 + 1];
|
|
919
|
+
key_vertex[2] = triangle_buffer[o + k * 3 + 2];
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Write edge `k` (vertex k to vertex k + 1) of the triangle at `o` in {@link triangle_buffer} into
|
|
924
|
+
* {@link key_edge}, its end points in lexicographic order so both triangles sharing it write one key.
|
|
925
|
+
*/
|
|
926
|
+
function key_set_edge(o, k) {
|
|
927
|
+
const p = o + k * 3;
|
|
928
|
+
const q = o + ((k + 1) % 3) * 3;
|
|
929
|
+
|
|
930
|
+
const px = triangle_buffer[p], py = triangle_buffer[p + 1], pz = triangle_buffer[p + 2];
|
|
931
|
+
const qx = triangle_buffer[q], qy = triangle_buffer[q + 1], qz = triangle_buffer[q + 2];
|
|
932
|
+
|
|
933
|
+
const p_first = px < qx || (px === qx && (py < qy || (py === qy && pz <= qz)));
|
|
934
|
+
|
|
935
|
+
if (p_first) {
|
|
936
|
+
key_edge[0] = px; key_edge[1] = py; key_edge[2] = pz;
|
|
937
|
+
key_edge[3] = qx; key_edge[4] = qy; key_edge[5] = qz;
|
|
938
|
+
} else {
|
|
939
|
+
key_edge[0] = qx; key_edge[1] = qy; key_edge[2] = qz;
|
|
940
|
+
key_edge[3] = px; key_edge[4] = py; key_edge[5] = pz;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* Append tentative contact `t` unless an earlier contact of the dispatch repeats it.
|
|
946
|
+
* @param {number} count
|
|
947
|
+
* @param {number} t
|
|
948
|
+
* @returns {number}
|
|
949
|
+
*/
|
|
950
|
+
function tentative_append(count, t) {
|
|
951
|
+
const o = t * TENTATIVE_STRIDE;
|
|
952
|
+
|
|
953
|
+
if (dedup_contains(tentatives, o)) {
|
|
954
|
+
return count;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
count = append_contact_record(count, tentatives, o);
|
|
958
|
+
|
|
959
|
+
dedup_insert(count - 1);
|
|
960
|
+
|
|
961
|
+
return count;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* Box3D's sphere rule: tentative contacts closest first, each kept when the feature it touches is
|
|
966
|
+
* claimed for the first time; face contacts' triangles claim theirs before any. A kept one whose
|
|
967
|
+
* feature is FLAT is taken as the plane's, as everything else's is ({@link
|
|
968
|
+
* resolve_tentatives_by_features}): claims go by coordinates, and a neighbour whose copy of the edge
|
|
969
|
+
* is apart from this one's by float rounding, or that meets it at a T-junction, claims a different
|
|
970
|
+
* key.
|
|
971
|
+
*
|
|
972
|
+
* @param {number} count
|
|
973
|
+
* @param {AbstractShape3D} shape the concave shape the triangles are of
|
|
974
|
+
* @param {boolean} concave_is_a whether the concave shape is the contacts' A side
|
|
975
|
+
* @param {Transform64} concave_tr its pose
|
|
976
|
+
* @returns {number}
|
|
977
|
+
*/
|
|
978
|
+
function resolve_tentatives_by_claims(count, shape, concave_is_a, concave_tr) {
|
|
979
|
+
claimed_vertices.clear();
|
|
980
|
+
claimed_edges.clear();
|
|
981
|
+
|
|
982
|
+
for (let f = 0; f < face_triangle_count; f++) {
|
|
983
|
+
const o = face_triangles[f] * TRIANGLE_FLOAT_STRIDE;
|
|
984
|
+
|
|
985
|
+
for (let k = 0; k < 3; k++) {
|
|
986
|
+
key_set_vertex(o, k);
|
|
987
|
+
claimed_vertices.add(key_vertex);
|
|
988
|
+
key_set_edge(o, k);
|
|
989
|
+
claimed_edges.add(key_edge);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// closest first; a shell sort, as the tentatives are few and this allocates nothing
|
|
994
|
+
const n = tentative_count;
|
|
995
|
+
const order = tentative_order;
|
|
996
|
+
|
|
997
|
+
for (let t = 0; t < n; t++) order[t] = t;
|
|
998
|
+
|
|
999
|
+
for (let gap = n >> 1; gap > 0; gap >>= 1) {
|
|
1000
|
+
for (let t = gap; t < n; t++) {
|
|
1001
|
+
const v = order[t];
|
|
1002
|
+
const key = tentatives[v * TENTATIVE_STRIDE + 13];
|
|
1003
|
+
|
|
1004
|
+
let s = t;
|
|
1005
|
+
|
|
1006
|
+
while (s >= gap && tentatives[order[s - gap] * TENTATIVE_STRIDE + 13] > key) {
|
|
1007
|
+
order[s] = order[s - gap];
|
|
1008
|
+
s -= gap;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
order[s] = v;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
for (let r = 0; r < n; r++) {
|
|
1016
|
+
const t = order[r];
|
|
1017
|
+
const o = t * TENTATIVE_STRIDE;
|
|
1018
|
+
const tri = tentatives[o + 11] * TRIANGLE_FLOAT_STRIDE;
|
|
1019
|
+
const feature = tentatives[o + 12];
|
|
1020
|
+
|
|
1021
|
+
// claim every vertex and edge of the triangle, noting whether the touched one is new
|
|
1022
|
+
let fresh = feature === FEATURE_FACE;
|
|
1023
|
+
|
|
1024
|
+
for (let k = 0; k < 3; k++) {
|
|
1025
|
+
key_set_vertex(tri, k);
|
|
1026
|
+
if (claimed_vertices.add(key_vertex) && feature === FEATURE_VERTEX_A + k) fresh = true;
|
|
1027
|
+
|
|
1028
|
+
key_set_edge(tri, k);
|
|
1029
|
+
if (claimed_edges.add(key_edge) && feature === FEATURE_EDGE_AB + k) fresh = true;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
if (!fresh) {
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
if (feature_is_flat(shape, tri, feature)) {
|
|
1037
|
+
triangle_plane_world(tri, concave_tr);
|
|
1038
|
+
count = tentative_append_on_plane(count, t, concave_is_a);
|
|
1039
|
+
} else {
|
|
1040
|
+
count = tentative_append(count, t);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
return count;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/**
|
|
1048
|
+
* Whether `feature` of the triangle at float index `tri` of {@link triangle_buffer} is no feature of
|
|
1049
|
+
* the surface: the face, a FLAT edge, a FLAT corner.
|
|
1050
|
+
* @param {AbstractShape3D} shape
|
|
1051
|
+
* @param {number} tri
|
|
1052
|
+
* @param {number} feature
|
|
1053
|
+
* @returns {boolean}
|
|
1054
|
+
*/
|
|
1055
|
+
function feature_is_flat(shape, tri, feature) {
|
|
1056
|
+
if (feature === FEATURE_FACE) {
|
|
1057
|
+
return true;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
if (feature < FEATURE_VERTEX_A) {
|
|
1061
|
+
return (triangle_edge_flags(shape, triangle_buffer, tri) & (TriangleEdgeFlags.FlatAB << feature)) !== 0;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
if (feature < FEATURE_FACE) {
|
|
1065
|
+
return triangle_vertex_flat(shape, triangle_buffer, tri, feature - FEATURE_VERTEX_A);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
return false;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* A triangle's plane, from {@link triangle_plane_world}: its unit outward normal (world) at 0..2, the
|
|
1073
|
+
* plane's offset along it at 3, and its corners (world) at 4..12.
|
|
1074
|
+
*/
|
|
1075
|
+
const tri_plane = new Float64Array(13);
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* The plane of the triangle at float index `tri` of {@link triangle_buffer}, posed by `concave_tr`,
|
|
1079
|
+
* into {@link tri_plane}.
|
|
1080
|
+
* @param {number} tri
|
|
1081
|
+
* @param {Transform64} concave_tr
|
|
1082
|
+
*/
|
|
1083
|
+
function triangle_plane_world(tri, concave_tr) {
|
|
1084
|
+
pose_pack(plane_pose, concave_tr);
|
|
1085
|
+
triangle_vertices_to_world(scratch_tri_world, triangle_buffer, tri, plane_pose);
|
|
1086
|
+
|
|
1087
|
+
const w = scratch_tri_world;
|
|
1088
|
+
const e1x = w[3] - w[0], e1y = w[4] - w[1], e1z = w[5] - w[2];
|
|
1089
|
+
const e2x = w[6] - w[0], e2y = w[7] - w[1], e2z = w[8] - w[2];
|
|
1090
|
+
const nx = e1y * e2z - e1z * e2y, ny = e1z * e2x - e1x * e2z, nz = e1x * e2y - e1y * e2x;
|
|
1091
|
+
const inv = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
1092
|
+
|
|
1093
|
+
tri_plane[0] = nx * inv;
|
|
1094
|
+
tri_plane[1] = ny * inv;
|
|
1095
|
+
tri_plane[2] = nz * inv;
|
|
1096
|
+
tri_plane[3] = tri_plane[0] * w[0] + tri_plane[1] * w[1] + tri_plane[2] * w[2];
|
|
1097
|
+
|
|
1098
|
+
for (let i = 0; i < 9; i++) {
|
|
1099
|
+
tri_plane[4 + i] = w[i];
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/**
|
|
1104
|
+
* The round core of the dispatch's convex body, for {@link plane_witness_of}: every point of a
|
|
1105
|
+
* sphere's or a capsule's surface lies `round_core[ROUND_RADIUS]` from its core segment (world, from
|
|
1106
|
+
* `ROUND_P0` to `ROUND_P1`; a sphere's has no length). A radius of 0: the body has corners, or no core
|
|
1107
|
+
* known here. In an array, not module `let`s: a number written to a module `let` is boxed.
|
|
1108
|
+
*
|
|
1109
|
+
* `round_core[ROUND_HALF]` is the core's half-length, staged with the radius for
|
|
1110
|
+
* {@link round_core_place}.
|
|
1111
|
+
*/
|
|
1112
|
+
const ROUND_RADIUS = 0;
|
|
1113
|
+
const ROUND_P0 = 1;
|
|
1114
|
+
const ROUND_P1 = 4;
|
|
1115
|
+
const ROUND_HALF = 7;
|
|
1116
|
+
const round_core = new Float64Array(8);
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Place the round core of the dispatch's convex body, whose radius and half-length the caller has
|
|
1120
|
+
* staged at ROUND_RADIUS and ROUND_HALF: `half` along its local y either side of its centre,
|
|
1121
|
+
* `radius` out from that. They are staged rather than passed because the call is not inlined into
|
|
1122
|
+
* dispatch_pair, and it would box the two numbers.
|
|
1123
|
+
* @param {Transform64} tr the body's pose
|
|
1124
|
+
*/
|
|
1125
|
+
function round_core_place(tr) {
|
|
1126
|
+
const half = round_core[ROUND_HALF];
|
|
1127
|
+
|
|
1128
|
+
// q · (0, half, 0) · q*
|
|
1129
|
+
const qx = tr[TRANSFORM64_ROTATION_OFFSET], qy = tr[TRANSFORM64_ROTATION_OFFSET + 1];
|
|
1130
|
+
const qz = tr[TRANSFORM64_ROTATION_OFFSET + 2], qw = tr[TRANSFORM64_ROTATION_OFFSET + 3];
|
|
1131
|
+
const ax = 2 * (qx * qy - qw * qz) * half;
|
|
1132
|
+
const ay = (1 - 2 * (qx * qx + qz * qz)) * half;
|
|
1133
|
+
const az = 2 * (qy * qz + qw * qx) * half;
|
|
1134
|
+
|
|
1135
|
+
const px = tr[TRANSFORM64_TRANSLATION_OFFSET];
|
|
1136
|
+
const py = tr[TRANSFORM64_TRANSLATION_OFFSET + 1];
|
|
1137
|
+
const pz = tr[TRANSFORM64_TRANSLATION_OFFSET + 2];
|
|
1138
|
+
|
|
1139
|
+
round_core[ROUND_P0] = px - ax;
|
|
1140
|
+
round_core[ROUND_P0 + 1] = py - ay;
|
|
1141
|
+
round_core[ROUND_P0 + 2] = pz - az;
|
|
1142
|
+
round_core[ROUND_P1] = px + ax;
|
|
1143
|
+
round_core[ROUND_P1 + 1] = py + ay;
|
|
1144
|
+
round_core[ROUND_P1 + 2] = pz + az;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/** The convex body's point touching the plane, of the contact {@link tentative_append_on_plane} is making. */
|
|
1148
|
+
const plane_witness = new Float64Array(3);
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* Where the convex body touches a plane of outward normal f, near its witness `(bx, by, bz)` of a
|
|
1152
|
+
* contact along some other normal — into {@link plane_witness}.
|
|
1153
|
+
*
|
|
1154
|
+
* A body with corners touches with the same corner or edge point whichever way the normal turns, so
|
|
1155
|
+
* the witness stays. A round body does not: its witness slides around its surface with the normal,
|
|
1156
|
+
* and its point touching the plane is the one under its core, `radius` along -f from the core point
|
|
1157
|
+
* nearest the witness. Left where it was, the witness of a capsule touching a flat edge a few
|
|
1158
|
+
* millimetres off the line under its axis put the plane's push there, off the axis, and a
|
|
1159
|
+
* frictionless capsule sliding across a triangulated floor picked up spin at every edge — 0.1 to
|
|
1160
|
+
* 0.5 rad/s, where a box floor leaves it none.
|
|
1161
|
+
*/
|
|
1162
|
+
function plane_witness_of(bx, by, bz, fx, fy, fz) {
|
|
1163
|
+
const round_radius = round_core[ROUND_RADIUS];
|
|
1164
|
+
|
|
1165
|
+
if (round_radius === 0) {
|
|
1166
|
+
plane_witness[0] = bx;
|
|
1167
|
+
plane_witness[1] = by;
|
|
1168
|
+
plane_witness[2] = bz;
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
const round_x0 = round_core[ROUND_P0], round_y0 = round_core[ROUND_P0 + 1], round_z0 = round_core[ROUND_P0 + 2];
|
|
1173
|
+
const round_x1 = round_core[ROUND_P1], round_y1 = round_core[ROUND_P1 + 1], round_z1 = round_core[ROUND_P1 + 2];
|
|
1174
|
+
|
|
1175
|
+
const sx = round_x1 - round_x0, sy = round_y1 - round_y0, sz = round_z1 - round_z0;
|
|
1176
|
+
const ss = sx * sx + sy * sy + sz * sz;
|
|
1177
|
+
|
|
1178
|
+
let u = ss > 0 ? ((bx - round_x0) * sx + (by - round_y0) * sy + (bz - round_z0) * sz) / ss : 0;
|
|
1179
|
+
u = u < 0 ? 0 : u > 1 ? 1 : u;
|
|
1180
|
+
|
|
1181
|
+
plane_witness[0] = round_x0 + sx * u - fx * round_radius;
|
|
1182
|
+
plane_witness[1] = round_y0 + sy * u - fy * round_radius;
|
|
1183
|
+
plane_witness[2] = round_z0 + sz * u - fz * round_radius;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* Whether a point of the plane in {@link tri_plane} lies on its triangle, to {@link ON_EDGE_WEIGHT}
|
|
1188
|
+
* of its barycentric weights.
|
|
1189
|
+
* @param {Float64Array} point
|
|
1190
|
+
* @param {number} point_offset
|
|
1191
|
+
* @returns {boolean}
|
|
1192
|
+
*/
|
|
1193
|
+
function tri_plane_contains(point, point_offset) {
|
|
1194
|
+
const px = point[point_offset], py = point[point_offset + 1], pz = point[point_offset + 2];
|
|
1195
|
+
|
|
1196
|
+
const ax = tri_plane[4], ay = tri_plane[5], az = tri_plane[6];
|
|
1197
|
+
const bx = tri_plane[7], by = tri_plane[8], bz = tri_plane[9];
|
|
1198
|
+
const cx = tri_plane[10], cy = tri_plane[11], cz = tri_plane[12];
|
|
1199
|
+
|
|
1200
|
+
const e1x = bx - ax, e1y = by - ay, e1z = bz - az;
|
|
1201
|
+
const e2x = cx - ax, e2y = cy - ay, e2z = cz - az;
|
|
1202
|
+
const nx = e1y * e2z - e1z * e2y, ny = e1z * e2x - e1x * e2z, nz = e1x * e2y - e1y * e2x;
|
|
1203
|
+
const nn = nx * nx + ny * ny + nz * nz;
|
|
1204
|
+
|
|
1205
|
+
if (!(nn > 0)) {
|
|
1206
|
+
return false;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// the weights of A and B: the signed areas of (P, B, C) and (P, C, A) over the triangle's
|
|
1210
|
+
const pax = ax - px, pay = ay - py, paz = az - pz;
|
|
1211
|
+
const pbx = bx - px, pby = by - py, pbz = bz - pz;
|
|
1212
|
+
const pcx = cx - px, pcy = cy - py, pcz = cz - pz;
|
|
1213
|
+
|
|
1214
|
+
const wa = ((pby * pcz - pbz * pcy) * nx + (pbz * pcx - pbx * pcz) * ny + (pbx * pcy - pby * pcx) * nz) / nn;
|
|
1215
|
+
const wb = ((pcy * paz - pcz * pay) * nx + (pcz * pax - pcx * paz) * ny + (pcx * pay - pcy * pax) * nz) / nn;
|
|
1216
|
+
|
|
1217
|
+
return wa >= -ON_EDGE_WEIGHT && wb >= -ON_EDGE_WEIGHT && 1 - wa - wb >= -ON_EDGE_WEIGHT;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/**
|
|
1221
|
+
* Append tentative contact `t` as a contact with the plane of its triangle, the one in {@link
|
|
1222
|
+
* tri_plane}: the normal the triangle's, the depth below that plane of the convex body's point touching
|
|
1223
|
+
* it ({@link plane_witness_of}), the triangle's witness that point's projection onto it. Not appended
|
|
1224
|
+
* when the point is not below the plane, nor for a round body whose point projects off the triangle and
|
|
1225
|
+
* whose contact's own normal is more than a flat edge's bend off the plane's.
|
|
1226
|
+
*
|
|
1227
|
+
* The plane is the one in {@link tri_plane}: its unit outward normal f (world) and its offset along
|
|
1228
|
+
* it, `f · p` for any point p of it.
|
|
1229
|
+
*
|
|
1230
|
+
* @param {number} count
|
|
1231
|
+
* @param {number} t
|
|
1232
|
+
* @param {boolean} concave_is_a whether the triangle is the contact's A side
|
|
1233
|
+
* @returns {number}
|
|
1234
|
+
*/
|
|
1235
|
+
function tentative_append_on_plane(count, t, concave_is_a) {
|
|
1236
|
+
const fx = tri_plane[0], fy = tri_plane[1], fz = tri_plane[2];
|
|
1237
|
+
const plane = tri_plane[3];
|
|
1238
|
+
|
|
1239
|
+
const o = t * TENTATIVE_STRIDE;
|
|
1240
|
+
|
|
1241
|
+
// the convex body's witness, moved to where it touches the plane
|
|
1242
|
+
const bo = concave_is_a ? o + 3 : o;
|
|
1243
|
+
plane_witness_of(tentatives[bo], tentatives[bo + 1], tentatives[bo + 2], fx, fy, fz);
|
|
1244
|
+
const bx = plane_witness[0], by = plane_witness[1], bz = plane_witness[2];
|
|
1245
|
+
|
|
1246
|
+
const depth = plane - (fx * bx + fy * by + fz * bz);
|
|
1247
|
+
|
|
1248
|
+
if (!(depth > 0)) {
|
|
1249
|
+
return count;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
const px = bx + fx * depth, py = by + fy * depth, pz = bz + fz * depth;
|
|
1253
|
+
|
|
1254
|
+
// the contact, staged: the triangle's point is A's witness when the triangle is A
|
|
1255
|
+
const c = staged_contact;
|
|
1256
|
+
const p_offset = concave_is_a ? 0 : 3;
|
|
1257
|
+
const b_offset = concave_is_a ? 3 : 0;
|
|
1258
|
+
|
|
1259
|
+
c[p_offset] = px; c[p_offset + 1] = py; c[p_offset + 2] = pz;
|
|
1260
|
+
c[b_offset] = bx; c[b_offset + 1] = by; c[b_offset + 2] = bz;
|
|
1261
|
+
|
|
1262
|
+
// A round body's point under its core, off this triangle, and the contact meeting the feature from
|
|
1263
|
+
// further off this plane's normal than a flat edge bends: the body is not on this face. A capsule over
|
|
1264
|
+
// a 1 cm step touched the riser's internal diagonal from above, and taken as the riser's plane that
|
|
1265
|
+
// contact pushed it back by its whole 8 cm intrusion into the plane, which there is not the surface.
|
|
1266
|
+
// Nearer the plane's normal it is the plane's, off the triangle or not: on a convex crease flatter
|
|
1267
|
+
// than a flat edge's bend, the point lies past the crease on either side, off both triangles, and
|
|
1268
|
+
// neither holds the body. Dropped on both, a sphere set on a 1° roof's ridge fell through it.
|
|
1269
|
+
if (round_core[ROUND_RADIUS] !== 0 && !tri_plane_contains(c, p_offset)
|
|
1270
|
+
&& Math.abs(tentatives[o + 6] * fx + tentatives[o + 7] * fy + tentatives[o + 8] * fz) < FLAT_EDGE_COS) {
|
|
1271
|
+
return count;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// stored B → A: the triangle is A, so toward it
|
|
1275
|
+
c[6] = concave_is_a ? -fx : fx;
|
|
1276
|
+
c[7] = concave_is_a ? -fy : fy;
|
|
1277
|
+
c[8] = concave_is_a ? -fz : fz;
|
|
1278
|
+
c[9] = depth;
|
|
1279
|
+
c[10] = tentatives[o + 10];
|
|
1280
|
+
|
|
1281
|
+
if (dedup_contains(c, 0)) {
|
|
1282
|
+
return count;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
count = append_contact_record(count, c, 0);
|
|
1286
|
+
|
|
1287
|
+
dedup_insert(count - 1);
|
|
1288
|
+
|
|
1289
|
+
return count;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
* The rule for everything but a sphere: a tentative contact lying inside its triangle, or on an edge or
|
|
1294
|
+
* a corner of it that is FLAT, touches no feature of the surface — the surface there is the plane — and
|
|
1295
|
+
* is taken as a contact with the triangle's plane ({@link tentative_append_on_plane}). One on a real
|
|
1296
|
+
* edge or a real corner is kept as it is. See "Internal edges".
|
|
1297
|
+
*
|
|
1298
|
+
* @param {number} count
|
|
1299
|
+
* @param {AbstractShape3D} shape the concave shape the triangles are of
|
|
1300
|
+
* @param {boolean} concave_is_a whether the concave shape is the contacts' A side
|
|
1301
|
+
* @param {Transform64} concave_tr its pose
|
|
1302
|
+
* @returns {number}
|
|
1303
|
+
*/
|
|
1304
|
+
function resolve_tentatives_by_features(count, shape, concave_is_a, concave_tr) {
|
|
1305
|
+
// a triangle's tentative contacts arrive together: classify its edges, and find its plane, once
|
|
1306
|
+
let edges_tri = -1, edges = 0;
|
|
1307
|
+
let plane_tri = -1;
|
|
1308
|
+
|
|
1309
|
+
for (let t = 0; t < tentative_count; t++) {
|
|
1310
|
+
const o = t * TENTATIVE_STRIDE;
|
|
1311
|
+
const tri = tentatives[o + 11] * TRIANGLE_FLOAT_STRIDE;
|
|
1312
|
+
const feature = tentatives[o + 12];
|
|
1313
|
+
|
|
1314
|
+
let flat;
|
|
1315
|
+
|
|
1316
|
+
if (feature < FEATURE_VERTEX_A) {
|
|
1317
|
+
if (edges_tri !== tri) {
|
|
1318
|
+
edges = triangle_edge_flags(shape, triangle_buffer, tri);
|
|
1319
|
+
edges_tri = tri;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
flat = (edges & (TriangleEdgeFlags.FlatAB << feature)) !== 0;
|
|
1323
|
+
} else {
|
|
1324
|
+
flat = feature_is_flat(shape, tri, feature);
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
if (!flat) {
|
|
1328
|
+
count = tentative_append(count, t);
|
|
1329
|
+
continue;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
if (plane_tri !== tri) {
|
|
1333
|
+
triangle_plane_world(tri, concave_tr);
|
|
1334
|
+
plane_tri = tri;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
count = tentative_append_on_plane(count, t, concave_is_a);
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
return count;
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Most triangles one concave-vs-convex pair is collided against: the bound
|
|
1344
|
+
* on {@link triangle_buffer}'s growth (65,536 triangles, 5 MiB of scratch).
|
|
1345
|
+
*
|
|
1346
|
+
* The count is the convex side's footprint over the concave side's
|
|
1347
|
+
* triangle density, not a property of the pair kind: a 4 m crate on a
|
|
1348
|
+
* floor meshed at 10 cm covers some 3,500 triangles, an 8 m box on a
|
|
1349
|
+
* tessellated heightmap 32,768. The buffer used to be a fixed 1,024 and
|
|
1350
|
+
* the overflow was dropped — a drop that is not "a far edge", because the
|
|
1351
|
+
* enumerators emit in index (mesh) or row (heightmap) order, so what was
|
|
1352
|
+
* lost was every triangle past a line through the footprint. The crate
|
|
1353
|
+
* kept the triangles under one side, tipped about that line, and fell
|
|
1354
|
+
* through the floor, the same way every step.
|
|
1355
|
+
*
|
|
1356
|
+
* Past this bound the rest of the pair's triangles are not collided
|
|
1357
|
+
* against, and the first time it happens the process is told so.
|
|
1358
|
+
*
|
|
1359
|
+
* @type {number}
|
|
1360
|
+
*/
|
|
1361
|
+
const MAX_TRIANGLES_PER_PAIR = 1 << 16;
|
|
1362
|
+
|
|
1363
|
+
/**
|
|
1364
|
+
* Per-pair scratch for the concave-side triangle decomposition. Grows (by
|
|
1365
|
+
* doubling, re-enumerating once) when a pair overlaps more triangles than
|
|
1366
|
+
* it holds, up to {@link MAX_TRIANGLES_PER_PAIR}; the high-water mark is
|
|
1367
|
+
* kept for the process.
|
|
1368
|
+
*
|
|
1369
|
+
* Enumerators return the TRUE overlap count and drop writes past the
|
|
1370
|
+
* buffer's end (typed-array OOB semantics), so the consume loop must never
|
|
1371
|
+
* run past the capacity — it would read `undefined` and poison the
|
|
1372
|
+
* manifold with NaN.
|
|
260
1373
|
* @type {Float64Array}
|
|
261
1374
|
*/
|
|
262
|
-
|
|
1375
|
+
let triangle_buffer = new Float64Array(1024 * TRIANGLE_FLOAT_STRIDE);
|
|
1376
|
+
|
|
1377
|
+
/**
|
|
1378
|
+
* Whether a pair overlapping more than {@link MAX_TRIANGLES_PER_PAIR}
|
|
1379
|
+
* triangles has been reported. Once per process: the condition belongs to
|
|
1380
|
+
* the content, and it would recur every substep.
|
|
1381
|
+
* @type {boolean}
|
|
1382
|
+
*/
|
|
1383
|
+
let triangle_overflow_reported = false;
|
|
263
1384
|
|
|
264
1385
|
/**
|
|
265
1386
|
* Flyweight triangle shape — rebound to each successive triangle slice
|
|
@@ -290,8 +1411,25 @@ const concave_query_aabb = new Float64Array(6);
|
|
|
290
1411
|
*
|
|
291
1412
|
* Shared by the sphere / box / capsule closed-form triangle fast-paths in
|
|
292
1413
|
* the concave dispatch, which each carried an identical inlined copy.
|
|
1414
|
+
*
|
|
1415
|
+
* The vertices and the pose are read from arrays: the dispatch calls this
|
|
1416
|
+
* per triangle from more sites than it can inline, and a call that is not
|
|
1417
|
+
* inlined boxes each number passed to it — sixteen per triangle.
|
|
1418
|
+
*
|
|
1419
|
+
* @param {Float64Array} out
|
|
1420
|
+
* @param {Float64Array} triangle local vertices A, B, C
|
|
1421
|
+
* @param {number} triangle_offset
|
|
1422
|
+
* @param {Float64Array} pose the concave body's position then orientation:
|
|
1423
|
+
* `[px, py, pz, qx, qy, qz, qw]`
|
|
293
1424
|
*/
|
|
294
|
-
function triangle_vertices_to_world(out,
|
|
1425
|
+
function triangle_vertices_to_world(out, triangle, triangle_offset, pose) {
|
|
1426
|
+
const ax = triangle[triangle_offset], ay = triangle[triangle_offset + 1], az = triangle[triangle_offset + 2];
|
|
1427
|
+
const bx = triangle[triangle_offset + 3], by = triangle[triangle_offset + 4], bz = triangle[triangle_offset + 5];
|
|
1428
|
+
const cx = triangle[triangle_offset + 6], cy = triangle[triangle_offset + 7], cz = triangle[triangle_offset + 8];
|
|
1429
|
+
|
|
1430
|
+
const px = pose[0], py = pose[1], pz = pose[2];
|
|
1431
|
+
const qx = pose[3], qy = pose[4], qz = pose[5], qw = pose[6];
|
|
1432
|
+
|
|
295
1433
|
const axi = qw * ax + qy * az - qz * ay;
|
|
296
1434
|
const ayi = qw * ay + qz * ax - qx * az;
|
|
297
1435
|
const azi = qw * az + qx * ay - qy * ax;
|
|
@@ -323,6 +1461,195 @@ function triangle_vertices_to_world(out, ax, ay, az, bx, by, bz, cx, cy, cz, qx,
|
|
|
323
1461
|
/** Destination for {@link triangle_vertices_to_world}. */
|
|
324
1462
|
const scratch_tri_world = new Float64Array(9);
|
|
325
1463
|
|
|
1464
|
+
/**
|
|
1465
|
+
* Poses, `[px, py, pz, qx, qy, qz, qw]`: the concave and the convex body's in the concave dispatch,
|
|
1466
|
+
* packed once per dispatch, and {@link triangle_plane_world}'s.
|
|
1467
|
+
*/
|
|
1468
|
+
const concave_pose = new Float64Array(7);
|
|
1469
|
+
const convex_pose = new Float64Array(7);
|
|
1470
|
+
const plane_pose = new Float64Array(7);
|
|
1471
|
+
|
|
1472
|
+
/**
|
|
1473
|
+
* The concave dispatch's convex body, packed once per dispatch for the closed-form triangle kernels'
|
|
1474
|
+
* array forms: a box's centre, orientation and half-extents (10); a sphere's centre and radius (4); a
|
|
1475
|
+
* capsule's centre, orientation, radius and half-height (9).
|
|
1476
|
+
*/
|
|
1477
|
+
const convex_record = new Float64Array(10);
|
|
1478
|
+
|
|
1479
|
+
/**
|
|
1480
|
+
* Pack a pose, `[px, py, pz, qx, qy, qz, qw]`, from a transform.
|
|
1481
|
+
*
|
|
1482
|
+
* Read off the transform's lanes (a Transform64 is a Float64Array), not through `translation_x` and
|
|
1483
|
+
* the rest: inlined into dispatch_pair, those accessor calls land on a branch too rarely taken for
|
|
1484
|
+
* TurboFan to inline them, and each then returns its number boxed.
|
|
1485
|
+
*
|
|
1486
|
+
* @param {Float64Array} out
|
|
1487
|
+
* @param {Transform64} tr
|
|
1488
|
+
*/
|
|
1489
|
+
function pose_pack(out, tr) {
|
|
1490
|
+
out[0] = tr[TRANSFORM64_TRANSLATION_OFFSET];
|
|
1491
|
+
out[1] = tr[TRANSFORM64_TRANSLATION_OFFSET + 1];
|
|
1492
|
+
out[2] = tr[TRANSFORM64_TRANSLATION_OFFSET + 2];
|
|
1493
|
+
out[3] = tr[TRANSFORM64_ROTATION_OFFSET];
|
|
1494
|
+
out[4] = tr[TRANSFORM64_ROTATION_OFFSET + 1];
|
|
1495
|
+
out[5] = tr[TRANSFORM64_ROTATION_OFFSET + 2];
|
|
1496
|
+
out[6] = tr[TRANSFORM64_ROTATION_OFFSET + 3];
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
// dispatch_pair branches on the shape pair, so each branch runs on a fraction of its calls: too few
|
|
1500
|
+
// for TurboFan to inline a call made there, small or not, and a call that is not inlined boxes each
|
|
1501
|
+
// number handed to it. The closed-form pair kernels therefore get their bodies packed into these
|
|
1502
|
+
// records, and their contacts are appended from arrays.
|
|
1503
|
+
|
|
1504
|
+
/** dispatch_pair's boxes, `[px, py, pz, qx, qy, qz, qw, hx, hy, hz]`. */
|
|
1505
|
+
const pair_box_a = new Float64Array(10);
|
|
1506
|
+
const pair_box_b = new Float64Array(10);
|
|
1507
|
+
|
|
1508
|
+
/** dispatch_pair's spheres, `[px, py, pz, radius]`. */
|
|
1509
|
+
const pair_sphere_a = new Float64Array(4);
|
|
1510
|
+
const pair_sphere_b = new Float64Array(4);
|
|
1511
|
+
|
|
1512
|
+
/** dispatch_pair's capsules, `[px, py, pz, qx, qy, qz, qw, radius, half_height]`. */
|
|
1513
|
+
const pair_capsule_a = new Float64Array(9);
|
|
1514
|
+
const pair_capsule_b = new Float64Array(9);
|
|
1515
|
+
|
|
1516
|
+
/** A hull's pose, `[px, py, pz, qx, qy, qz, qw]`, for the hull-sphere kernel. */
|
|
1517
|
+
const pair_pose = new Float64Array(7);
|
|
1518
|
+
|
|
1519
|
+
/** The two poses of a hull pair, for {@link hull_pair_contacts}. */
|
|
1520
|
+
const pair_pose_a = new Float64Array(7);
|
|
1521
|
+
const pair_pose_b = new Float64Array(7);
|
|
1522
|
+
|
|
1523
|
+
/** One contact for {@link append_contact_record}, staged by dispatch_pair's closed-form branches. */
|
|
1524
|
+
const pair_contact = new Float64Array(11);
|
|
1525
|
+
|
|
1526
|
+
/**
|
|
1527
|
+
* Pack a sphere record, `[px, py, pz, radius]`.
|
|
1528
|
+
* @param {Float64Array} out
|
|
1529
|
+
* @param {Transform64} tr
|
|
1530
|
+
* @param {SphereShape3D} shape
|
|
1531
|
+
*/
|
|
1532
|
+
function sphere_record_pack(out, tr, shape) {
|
|
1533
|
+
out[0] = tr[TRANSFORM64_TRANSLATION_OFFSET];
|
|
1534
|
+
out[1] = tr[TRANSFORM64_TRANSLATION_OFFSET + 1];
|
|
1535
|
+
out[2] = tr[TRANSFORM64_TRANSLATION_OFFSET + 2];
|
|
1536
|
+
out[3] = shape.radius;
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
/**
|
|
1540
|
+
* Pack a capsule record, `[px, py, pz, qx, qy, qz, qw, radius, half_height]`.
|
|
1541
|
+
* @param {Float64Array} out
|
|
1542
|
+
* @param {Transform64} tr
|
|
1543
|
+
* @param {CapsuleShape3D} shape
|
|
1544
|
+
*/
|
|
1545
|
+
function capsule_record_pack(out, tr, shape) {
|
|
1546
|
+
pose_pack(out, tr);
|
|
1547
|
+
out[7] = shape.radius;
|
|
1548
|
+
out[8] = shape.height * 0.5;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
/**
|
|
1552
|
+
* Append a closed-form contact from a result laid out `[normal, depth, witness 1, witness 2]` — the
|
|
1553
|
+
* sphere-box, capsule-sphere and hull-sphere kernels' layout, whose normal points from witness 2's
|
|
1554
|
+
* body toward witness 1's.
|
|
1555
|
+
*
|
|
1556
|
+
* @param {number} count
|
|
1557
|
+
* @param {Float64Array} result
|
|
1558
|
+
* @param {boolean} first_is_a witness 1 is on A; else the witnesses swap and the normal turns round
|
|
1559
|
+
* @param {number} feature_id
|
|
1560
|
+
* @returns {number}
|
|
1561
|
+
*/
|
|
1562
|
+
function append_closed_form_contact(count, result, first_is_a, feature_id) {
|
|
1563
|
+
const c = pair_contact;
|
|
1564
|
+
|
|
1565
|
+
if (first_is_a) {
|
|
1566
|
+
c[0] = result[4]; c[1] = result[5]; c[2] = result[6];
|
|
1567
|
+
c[3] = result[7]; c[4] = result[8]; c[5] = result[9];
|
|
1568
|
+
c[6] = result[0]; c[7] = result[1]; c[8] = result[2];
|
|
1569
|
+
} else {
|
|
1570
|
+
c[0] = result[7]; c[1] = result[8]; c[2] = result[9];
|
|
1571
|
+
c[3] = result[4]; c[4] = result[5]; c[5] = result[6];
|
|
1572
|
+
c[6] = -result[0]; c[7] = -result[1]; c[8] = -result[2];
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
c[9] = result[3];
|
|
1576
|
+
c[10] = feature_id;
|
|
1577
|
+
|
|
1578
|
+
return append_contact_record(count, c, 0);
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
/**
|
|
1582
|
+
* Append contact `offset` of a multi-contact result laid out `[witness 1, witness 2, normal, depth]`
|
|
1583
|
+
* per contact — the capsule-capsule and capsule-box kernels' layout, whose normal points from
|
|
1584
|
+
* witness 2's body toward witness 1's.
|
|
1585
|
+
*
|
|
1586
|
+
* @param {number} count
|
|
1587
|
+
* @param {Float64Array} result
|
|
1588
|
+
* @param {number} offset
|
|
1589
|
+
* @param {boolean} first_is_a witness 1 is on A; else the witnesses swap and the normal turns round
|
|
1590
|
+
* @param {number} feature_id
|
|
1591
|
+
* @returns {number}
|
|
1592
|
+
*/
|
|
1593
|
+
function append_manifold_contact(count, result, offset, first_is_a, feature_id) {
|
|
1594
|
+
const c = pair_contact;
|
|
1595
|
+
|
|
1596
|
+
if (first_is_a) {
|
|
1597
|
+
c[0] = result[offset]; c[1] = result[offset + 1]; c[2] = result[offset + 2];
|
|
1598
|
+
c[3] = result[offset + 3]; c[4] = result[offset + 4]; c[5] = result[offset + 5];
|
|
1599
|
+
c[6] = result[offset + 6]; c[7] = result[offset + 7]; c[8] = result[offset + 8];
|
|
1600
|
+
} else {
|
|
1601
|
+
c[0] = result[offset + 3]; c[1] = result[offset + 4]; c[2] = result[offset + 5];
|
|
1602
|
+
c[3] = result[offset]; c[4] = result[offset + 1]; c[5] = result[offset + 2];
|
|
1603
|
+
c[6] = -result[offset + 6]; c[7] = -result[offset + 7]; c[8] = -result[offset + 8];
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
c[9] = result[offset + 9];
|
|
1607
|
+
c[10] = feature_id;
|
|
1608
|
+
|
|
1609
|
+
return append_contact_record(count, c, 0);
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
/**
|
|
1613
|
+
* Pack a box record, `[px, py, pz, qx, qy, qz, qw, hx, hy, hz]`: its centre and orientation from the
|
|
1614
|
+
* transform, then its half-extents, read off their lanes for {@link pose_pack}'s reason (a Vector3 is
|
|
1615
|
+
* a Float64Array).
|
|
1616
|
+
* @param {Float64Array} out
|
|
1617
|
+
* @param {Transform64} tr
|
|
1618
|
+
* @param {Vector3} half_extents
|
|
1619
|
+
*/
|
|
1620
|
+
function box_record_pack(out, tr, half_extents) {
|
|
1621
|
+
pose_pack(out, tr);
|
|
1622
|
+
out[7] = half_extents[0];
|
|
1623
|
+
out[8] = half_extents[1];
|
|
1624
|
+
out[9] = half_extents[2];
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
/**
|
|
1628
|
+
* Append contact `offset` of a box-box or convex-convex manifold (the layout is shared) — A witness,
|
|
1629
|
+
* B witness, depth, after the manifold's shared normal at [0..2] — with no feature id.
|
|
1630
|
+
* @param {number} count
|
|
1631
|
+
* @param {Float64Array} manifold a {@link box_box_manifold_array} or {@link convex_hull_clip_array} result
|
|
1632
|
+
* @param {number} offset
|
|
1633
|
+
* @returns {number}
|
|
1634
|
+
*/
|
|
1635
|
+
function append_box_manifold_contact(count, manifold, offset) {
|
|
1636
|
+
const c = pair_contact;
|
|
1637
|
+
|
|
1638
|
+
c[0] = manifold[offset];
|
|
1639
|
+
c[1] = manifold[offset + 1];
|
|
1640
|
+
c[2] = manifold[offset + 2];
|
|
1641
|
+
c[3] = manifold[offset + 3];
|
|
1642
|
+
c[4] = manifold[offset + 4];
|
|
1643
|
+
c[5] = manifold[offset + 5];
|
|
1644
|
+
c[6] = manifold[0];
|
|
1645
|
+
c[7] = manifold[1];
|
|
1646
|
+
c[8] = manifold[2];
|
|
1647
|
+
c[9] = manifold[offset + 6];
|
|
1648
|
+
c[10] = 0;
|
|
1649
|
+
|
|
1650
|
+
return append_contact_record(count, c, 0);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
326
1653
|
/**
|
|
327
1654
|
* Append one contact to the candidate buffer. Returns the new count.
|
|
328
1655
|
*
|
|
@@ -360,6 +1687,37 @@ function append_contact(count, wax, way, waz, wbx, wby, wbz, nx, ny, nz, depth,
|
|
|
360
1687
|
return count + 1;
|
|
361
1688
|
}
|
|
362
1689
|
|
|
1690
|
+
/**
|
|
1691
|
+
* {@link append_contact} with the contact read from a record laid out as append_contact takes it —
|
|
1692
|
+
* A witness, B witness, stored normal, depth, feature id — at `offset`: for the concave dispatch's
|
|
1693
|
+
* per-triangle contacts, where a call that is not inlined would box all eleven. (append_contact keeps
|
|
1694
|
+
* its own copy of the stores rather than packing for this: as it is, the contact producers that take
|
|
1695
|
+
* it as their callback inline it.)
|
|
1696
|
+
*
|
|
1697
|
+
* @param {number} count
|
|
1698
|
+
* @param {Float64Array} contact
|
|
1699
|
+
* @param {number} offset
|
|
1700
|
+
* @returns {number}
|
|
1701
|
+
*/
|
|
1702
|
+
function append_contact_record(count, contact, offset) {
|
|
1703
|
+
if (count * CANDIDATE_STRIDE >= candidates.length){
|
|
1704
|
+
grow_candidates();
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
const off = count * CANDIDATE_STRIDE;
|
|
1708
|
+
|
|
1709
|
+
candidates[off] = contact[offset]; candidates[off + 1] = contact[offset + 1]; candidates[off + 2] = contact[offset + 2];
|
|
1710
|
+
candidates[off + 3] = contact[offset + 3]; candidates[off + 4] = contact[offset + 4]; candidates[off + 5] = contact[offset + 5];
|
|
1711
|
+
candidates[off + 6] = contact[offset + 6]; candidates[off + 7] = contact[offset + 7]; candidates[off + 8] = contact[offset + 8];
|
|
1712
|
+
candidates[off + 9] = contact[offset + 9];
|
|
1713
|
+
candidates[off + 10] = contact[offset + 10];
|
|
1714
|
+
candidates[off + 11] = g_pair_friction;
|
|
1715
|
+
candidates[off + 12] = g_pair_restitution;
|
|
1716
|
+
candidates[off + 13] = g_pair_flags;
|
|
1717
|
+
|
|
1718
|
+
return count + 1;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
363
1721
|
/**
|
|
364
1722
|
* Compute a stable voronoi-region feature id for a sphere-vs-box contact.
|
|
365
1723
|
* Returns one of 27 values in `[1, 27]` based on which of the 27 voronoi
|
|
@@ -375,22 +1733,22 @@ function append_contact(count, wax, way, waz, wbx, wby, wbz, nx, ny, nz, depth,
|
|
|
375
1733
|
* The "+1" offset ensures the result is never 0, since 0 is reserved
|
|
376
1734
|
* for "no feature info, use position-fallback".
|
|
377
1735
|
*
|
|
378
|
-
*
|
|
379
|
-
* @
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
* @param {
|
|
383
|
-
* @param {number}
|
|
384
|
-
* @param {
|
|
385
|
-
* @param {number}
|
|
386
|
-
* @param {number} bqz
|
|
387
|
-
* @param {number} bqw
|
|
388
|
-
* @param {number} hx box half-extent x
|
|
389
|
-
* @param {number} hy
|
|
390
|
-
* @param {number} hz
|
|
1736
|
+
* The sphere and the box are read from their records (see {@link sphere_record_pack} and
|
|
1737
|
+
* {@link box_record_pack}): this is called from a dispatch_pair branch, where the thirteen numbers
|
|
1738
|
+
* would each be boxed into a new heap object.
|
|
1739
|
+
*
|
|
1740
|
+
* @param {Float64Array} sphere `[centre, radius]`
|
|
1741
|
+
* @param {number} sphere_offset
|
|
1742
|
+
* @param {Float64Array} box `[centre, orientation, half-extents]`
|
|
1743
|
+
* @param {number} box_offset
|
|
391
1744
|
* @returns {number}
|
|
392
1745
|
*/
|
|
393
|
-
function
|
|
1746
|
+
function sphere_box_voronoi_fid_array(sphere, sphere_offset, box, box_offset) {
|
|
1747
|
+
const sx = sphere[sphere_offset], sy = sphere[sphere_offset + 1], sz = sphere[sphere_offset + 2];
|
|
1748
|
+
const bx = box[box_offset], by = box[box_offset + 1], bz = box[box_offset + 2];
|
|
1749
|
+
const bqx = box[box_offset + 3], bqy = box[box_offset + 4], bqz = box[box_offset + 5], bqw = box[box_offset + 6];
|
|
1750
|
+
const hx = box[box_offset + 7], hy = box[box_offset + 8], hz = box[box_offset + 9];
|
|
1751
|
+
|
|
394
1752
|
// Inverse-rotate (sx - bx, sy - by, sz - bz) by the box's quaternion
|
|
395
1753
|
// to get the sphere centre in box-local frame. Inlined for the same
|
|
396
1754
|
// V8-inliner reason described in PosedShape3D.support — see
|
|
@@ -430,45 +1788,182 @@ function sphere_box_voronoi_fid(sx, sy, sz, bx, by, bz, bqx, bqy, bqz, bqw, hx,
|
|
|
430
1788
|
* @param {Transform64} trA
|
|
431
1789
|
* @param {{vertices,face_offsets,face_loops,support}} hullB in B's local frame
|
|
432
1790
|
* @param {Transform64} trB
|
|
433
|
-
* @param {Function} append_contact
|
|
434
1791
|
* @returns {number}
|
|
435
1792
|
*/
|
|
436
|
-
function hull_pair_contacts(count, hullA, trA, hullB, trB
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
);
|
|
442
|
-
posed_b.
|
|
443
|
-
hullB,
|
|
444
|
-
trB.translation_x, trB.translation_y, trB.translation_z,
|
|
445
|
-
trB.rotation_x, trB.rotation_y, trB.rotation_z, trB.rotation_w
|
|
446
|
-
);
|
|
1793
|
+
function hull_pair_contacts(count, hullA, trA, hullB, trB) {
|
|
1794
|
+
// Poses go to the posed shapes and the clip in arrays, read off the transforms' lanes: this is
|
|
1795
|
+
// called from a dispatch_pair branch, and numbers handed across a call there are boxed.
|
|
1796
|
+
pose_pack(pair_pose_a, trA);
|
|
1797
|
+
pose_pack(pair_pose_b, trB);
|
|
1798
|
+
posed_a.setup_pose(hullA, pair_pose_a, 0);
|
|
1799
|
+
posed_b.setup_pose(hullB, pair_pose_b, 0);
|
|
447
1800
|
const depth = gjk_epa_penetration(epa_result, posed_a, posed_b);
|
|
448
1801
|
if (!(depth > 0) || !Number.isFinite(depth)) return count;
|
|
449
1802
|
|
|
1803
|
+
hull_position_a[0] = pair_pose_a[0];
|
|
1804
|
+
hull_position_a[1] = pair_pose_a[1];
|
|
1805
|
+
hull_position_a[2] = pair_pose_a[2];
|
|
1806
|
+
hull_rotation_a[0] = pair_pose_a[3];
|
|
1807
|
+
hull_rotation_a[1] = pair_pose_a[4];
|
|
1808
|
+
hull_rotation_a[2] = pair_pose_a[5];
|
|
1809
|
+
hull_rotation_a[3] = pair_pose_a[6];
|
|
1810
|
+
|
|
1811
|
+
hull_position_b[0] = pair_pose_b[0];
|
|
1812
|
+
hull_position_b[1] = pair_pose_b[1];
|
|
1813
|
+
hull_position_b[2] = pair_pose_b[2];
|
|
1814
|
+
hull_rotation_b[0] = pair_pose_b[3];
|
|
1815
|
+
hull_rotation_b[1] = pair_pose_b[4];
|
|
1816
|
+
hull_rotation_b[2] = pair_pose_b[5];
|
|
1817
|
+
hull_rotation_b[3] = pair_pose_b[6];
|
|
1818
|
+
|
|
450
1819
|
// convex_hull_clip orients the axis B→A robustly (via vertex centroids) and
|
|
451
1820
|
// writes the final normal into convex_manifold_result[0..2].
|
|
452
|
-
|
|
453
|
-
hullA,
|
|
454
|
-
hullB,
|
|
455
|
-
epa_result
|
|
456
|
-
const nx = convex_manifold_result[0], ny = convex_manifold_result[1], nz = convex_manifold_result[2];
|
|
1821
|
+
convex_hull_clip_array(convex_manifold_result,
|
|
1822
|
+
hullA, hull_position_a, hull_rotation_a,
|
|
1823
|
+
hullB, hull_position_b, hull_rotation_b,
|
|
1824
|
+
epa_result, 0);
|
|
457
1825
|
const cc = convex_manifold_result[3] | 0;
|
|
458
1826
|
// Clipped points migrate across features as the hulls rotate, so leave
|
|
459
1827
|
// fid = 0 and let the match-and-merge pass position-match (as box-box does).
|
|
460
1828
|
for (let k = 0; k < cc; k++) {
|
|
461
|
-
|
|
462
|
-
count = append_contact(count,
|
|
463
|
-
convex_manifold_result[base], convex_manifold_result[base + 1], convex_manifold_result[base + 2],
|
|
464
|
-
convex_manifold_result[base + 3], convex_manifold_result[base + 4], convex_manifold_result[base + 5],
|
|
465
|
-
nx, ny, nz,
|
|
466
|
-
convex_manifold_result[base + 6],
|
|
467
|
-
0);
|
|
1829
|
+
count = append_box_manifold_contact(count, convex_manifold_result, 4 + k * 7);
|
|
468
1830
|
}
|
|
469
1831
|
return count;
|
|
470
1832
|
}
|
|
471
1833
|
|
|
1834
|
+
/**
|
|
1835
|
+
* A box as a polytope, for {@link hull_pair_contacts}: corners `(±hx, ±hy, ±hz)` numbered
|
|
1836
|
+
* 0 −−−, 1 +−−, 2 ++−, 3 −+−, 4 −−+, 5 +−+, 6 +++, 7 −++ (the −z face's square, then the +z face's),
|
|
1837
|
+
* six quad faces wound CCW from outside, and the exact box support. {@link box_polytope_set} sizes it
|
|
1838
|
+
* before each use.
|
|
1839
|
+
*/
|
|
1840
|
+
class BoxPolytope {
|
|
1841
|
+
vertices = new Float64Array(24);
|
|
1842
|
+
face_offsets = new Uint32Array([0, 4, 8, 12, 16, 20, 24]);
|
|
1843
|
+
face_loops = new Uint32Array([
|
|
1844
|
+
1, 2, 6, 5, // +X
|
|
1845
|
+
0, 4, 7, 3, // -X
|
|
1846
|
+
3, 7, 6, 2, // +Y
|
|
1847
|
+
0, 1, 5, 4, // -Y
|
|
1848
|
+
4, 5, 6, 7, // +Z
|
|
1849
|
+
0, 3, 2, 1, // -Z
|
|
1850
|
+
]);
|
|
1851
|
+
|
|
1852
|
+
// a typed array, not three number fields: a double field of a plain object is a heap box, and
|
|
1853
|
+
// reading one in the GJK support loop allocated ~320 B per dispatch
|
|
1854
|
+
half_extents = new Float64Array(3);
|
|
1855
|
+
|
|
1856
|
+
support(result, result_offset, direction, direction_offset) {
|
|
1857
|
+
const dx = direction[direction_offset];
|
|
1858
|
+
const dy = direction[direction_offset + 1];
|
|
1859
|
+
const dz = direction[direction_offset + 2];
|
|
1860
|
+
|
|
1861
|
+
const h = this.half_extents;
|
|
1862
|
+
result[result_offset] = dx >= 0 ? h[0] : -h[0];
|
|
1863
|
+
result[result_offset + 1] = dy >= 0 ? h[1] : -h[1];
|
|
1864
|
+
result[result_offset + 2] = dz >= 0 ? h[2] : -h[2];
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
const box_polytope = new BoxPolytope();
|
|
1869
|
+
|
|
1870
|
+
/**
|
|
1871
|
+
* Size {@link box_polytope} to a box's half-extents.
|
|
1872
|
+
* @param {{x: number, y: number, z: number}} half_extents
|
|
1873
|
+
*/
|
|
1874
|
+
function box_polytope_set(half_extents) {
|
|
1875
|
+
const hx = half_extents.x, hy = half_extents.y, hz = half_extents.z;
|
|
1876
|
+
const v = box_polytope.vertices;
|
|
1877
|
+
const h = box_polytope.half_extents;
|
|
1878
|
+
|
|
1879
|
+
h[0] = hx;
|
|
1880
|
+
h[1] = hy;
|
|
1881
|
+
h[2] = hz;
|
|
1882
|
+
|
|
1883
|
+
for (let i = 0; i < 8; i++) {
|
|
1884
|
+
// +x at corners 1, 2, 5, 6; +y at 2, 3, 6, 7; +z at 4..7
|
|
1885
|
+
const px = (i === 1 || i === 2 || i === 5 || i === 6) ? hx : -hx;
|
|
1886
|
+
const py = (i === 2 || i === 3 || i === 6 || i === 7) ? hy : -hy;
|
|
1887
|
+
const pz = i >= 4 ? hz : -hz;
|
|
1888
|
+
|
|
1889
|
+
v[i * 3] = px;
|
|
1890
|
+
v[i * 3 + 1] = py;
|
|
1891
|
+
v[i * 3 + 2] = pz;
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
/**
|
|
1896
|
+
* Stand-in colliders and world poses for the subjects of rigid TransformedShape3D wrappers, one per
|
|
1897
|
+
* level of nesting (a wrapper may wrap a wrapper, and both sides of a pair may be wrapped).
|
|
1898
|
+
* @type {Array<{shape: AbstractShape3D|null, friction: number|undefined, restitution: number|undefined, flags: number|undefined}>}
|
|
1899
|
+
*/
|
|
1900
|
+
const subject_colliders = [];
|
|
1901
|
+
|
|
1902
|
+
/** @type {Transform64[]} */
|
|
1903
|
+
const subject_poses = [];
|
|
1904
|
+
|
|
1905
|
+
let subject_depth = 0;
|
|
1906
|
+
|
|
1907
|
+
/**
|
|
1908
|
+
* The subject pose of the current level of nesting, made on first use.
|
|
1909
|
+
*
|
|
1910
|
+
* @returns {Transform64}
|
|
1911
|
+
*/
|
|
1912
|
+
function subject_pose_here() {
|
|
1913
|
+
const level = subject_depth;
|
|
1914
|
+
|
|
1915
|
+
if (level === subject_colliders.length) {
|
|
1916
|
+
subject_colliders.push({ shape: null, friction: undefined, restitution: undefined, flags: undefined });
|
|
1917
|
+
subject_poses.push(new Transform64());
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
return subject_poses[level];
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
/**
|
|
1924
|
+
* Dispatch a pair one of whose shapes is a rigid {@link TransformedShape3D}, as its SUBJECT at the
|
|
1925
|
+
* composed world pose, which {@link rigid_subject_pose} must just have written into this level's
|
|
1926
|
+
* {@link subject_pose_here}.
|
|
1927
|
+
*
|
|
1928
|
+
* A rigid wrapper moves its subject without changing it, so the subject collides through whatever
|
|
1929
|
+
* routine its own type has — a wrapped box is a box, with the box's closed forms and its four-point
|
|
1930
|
+
* manifold. Left wrapped it had no routine: it fell to the GJK + EPA fallback, which put the contact at
|
|
1931
|
+
* the bodies' origins. That is how the first-person player's capsule, lifted off the feet by a
|
|
1932
|
+
* wrapper, met everything it touched.
|
|
1933
|
+
*
|
|
1934
|
+
* @param {number} count
|
|
1935
|
+
* @param {Collider} colA
|
|
1936
|
+
* @param {Transform64} trA
|
|
1937
|
+
* @param {Collider} colB
|
|
1938
|
+
* @param {Transform64} trB
|
|
1939
|
+
* @param {boolean} a_side the wrapper is A's shape (else B's)
|
|
1940
|
+
* @returns {number}
|
|
1941
|
+
*/
|
|
1942
|
+
function dispatch_subject(count, colA, trA, colB, trB, a_side) {
|
|
1943
|
+
const level = subject_depth;
|
|
1944
|
+
|
|
1945
|
+
const wrapper_col = a_side ? colA : colB;
|
|
1946
|
+
|
|
1947
|
+
// the materials and the sensor flag belong to the collider, not the shape
|
|
1948
|
+
const col = subject_colliders[level];
|
|
1949
|
+
col.shape = wrapper_col.shape.subject;
|
|
1950
|
+
col.friction = wrapper_col.friction;
|
|
1951
|
+
col.restitution = wrapper_col.restitution;
|
|
1952
|
+
col.flags = wrapper_col.flags;
|
|
1953
|
+
|
|
1954
|
+
const pose = subject_poses[level];
|
|
1955
|
+
|
|
1956
|
+
subject_depth++;
|
|
1957
|
+
|
|
1958
|
+
const result = a_side
|
|
1959
|
+
? dispatch_pair(count, col, pose, colB, trB)
|
|
1960
|
+
: dispatch_pair(count, colA, trA, col, pose);
|
|
1961
|
+
|
|
1962
|
+
subject_depth--;
|
|
1963
|
+
|
|
1964
|
+
return result;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
472
1967
|
/**
|
|
473
1968
|
* Run pairwise narrowphase for one (colliderA, colliderB) tuple — dispatches
|
|
474
1969
|
* by shape type and appends 0..K contacts to the candidate buffer. Returns
|
|
@@ -485,6 +1980,23 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
485
1980
|
const shapeA = colA.shape;
|
|
486
1981
|
const shapeB = colB.shape;
|
|
487
1982
|
|
|
1983
|
+
// A rigid wrapper is its subject, moved: see dispatch_subject. (A scaling one is not, and stays.)
|
|
1984
|
+
if (shapeA.isTransformedShape3D === true && rigid_subject_pose(
|
|
1985
|
+
subject_pose_here(), shapeA,
|
|
1986
|
+
trA.translation_x, trA.translation_y, trA.translation_z,
|
|
1987
|
+
trA.rotation_x, trA.rotation_y, trA.rotation_z, trA.rotation_w
|
|
1988
|
+
)) {
|
|
1989
|
+
return dispatch_subject(count, colA, trA, colB, trB, true);
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
if (shapeB.isTransformedShape3D === true && rigid_subject_pose(
|
|
1993
|
+
subject_pose_here(), shapeB,
|
|
1994
|
+
trB.translation_x, trB.translation_y, trB.translation_z,
|
|
1995
|
+
trB.rotation_x, trB.rotation_y, trB.rotation_z, trB.rotation_w
|
|
1996
|
+
)) {
|
|
1997
|
+
return dispatch_subject(count, colA, trA, colB, trB, false);
|
|
1998
|
+
}
|
|
1999
|
+
|
|
488
2000
|
// Per-contact materials: combine the two source colliders' coefficients
|
|
489
2001
|
// once here (this is the only place that knows the exact collider on each
|
|
490
2002
|
// side) and stamp them onto every contact this dispatch appends. The
|
|
@@ -528,85 +2040,54 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
528
2040
|
|
|
529
2041
|
// sphere-sphere
|
|
530
2042
|
if (isSphereA && isSphereB) {
|
|
531
|
-
const
|
|
2043
|
+
const a = pair_sphere_a, b = pair_sphere_b;
|
|
2044
|
+
sphere_record_pack(a, trA, shapeA);
|
|
2045
|
+
sphere_record_pack(b, trB, shapeB);
|
|
532
2046
|
|
|
533
|
-
const ok =
|
|
534
|
-
sphere_result,
|
|
535
|
-
trA.translation_x, trA.translation_y, trA.translation_z,
|
|
536
|
-
trB.translation_x, trB.translation_y, trB.translation_z,
|
|
537
|
-
ra, rb
|
|
538
|
-
);
|
|
2047
|
+
const ok = sphere_sphere_contact_array(sphere_result, a, 0, b, 0);
|
|
539
2048
|
|
|
540
2049
|
if (!ok) return count;
|
|
541
2050
|
|
|
542
2051
|
const nx = sphere_result[0], ny = sphere_result[1], nz = sphere_result[2];
|
|
2052
|
+
const ra = a[3], rb = b[3];
|
|
543
2053
|
|
|
544
2054
|
// Sphere-sphere produces exactly one contact per pair; fid = 1
|
|
545
2055
|
// identifies it as a real feature (distinguishes from "no info" = 0)
|
|
546
2056
|
// and is trivially stable across frames. Witnesses are the surface
|
|
547
2057
|
// points along the (unit) normal, scaled by each sphere's radius.
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
2058
|
+
const c = pair_contact;
|
|
2059
|
+
c[0] = a[0] - nx * ra; c[1] = a[1] - ny * ra; c[2] = a[2] - nz * ra;
|
|
2060
|
+
c[3] = b[0] + nx * rb; c[4] = b[1] + ny * rb; c[5] = b[2] + nz * rb;
|
|
2061
|
+
c[6] = nx; c[7] = ny; c[8] = nz;
|
|
2062
|
+
c[9] = sphere_result[3];
|
|
2063
|
+
c[10] = 1;
|
|
2064
|
+
return append_contact_record(count, c, 0);
|
|
552
2065
|
}
|
|
553
2066
|
|
|
554
2067
|
// sphere ↔ box
|
|
555
2068
|
if ((isSphereA && isBoxB) || (isBoxA && isSphereB)) {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
const boxTr = isSphereA ? trB : trA;
|
|
559
|
-
const boxShape = isSphereA ? shapeB : shapeA;
|
|
560
|
-
const bh = boxShape.half_extents;
|
|
561
|
-
|
|
562
|
-
const ok = sphere_box_contact(
|
|
563
|
-
closed_form_result,
|
|
564
|
-
sphereTr.translation_x, sphereTr.translation_y, sphereTr.translation_z, sphereShape.radius,
|
|
565
|
-
boxTr.translation_x, boxTr.translation_y, boxTr.translation_z,
|
|
566
|
-
boxTr.rotation_x, boxTr.rotation_y, boxTr.rotation_z, boxTr.rotation_w,
|
|
567
|
-
bh.x, bh.y, bh.z
|
|
568
|
-
);
|
|
2069
|
+
sphere_record_pack(pair_sphere_a, isSphereA ? trA : trB, isSphereA ? shapeA : shapeB);
|
|
2070
|
+
box_record_pack(pair_box_b, isSphereA ? trB : trA, (isSphereA ? shapeB : shapeA).half_extents);
|
|
569
2071
|
|
|
570
|
-
|
|
2072
|
+
const ok = sphere_box_contact_array(closed_form_result, pair_sphere_a, 0, pair_box_b, 0);
|
|
571
2073
|
|
|
572
|
-
|
|
2074
|
+
if (!ok) return count;
|
|
573
2075
|
|
|
574
|
-
const depth = closed_form_result[3];
|
|
575
|
-
const wsx = closed_form_result[4], wsy = closed_form_result[5], wsz = closed_form_result[6];
|
|
576
|
-
const wbx = closed_form_result[7], wby = closed_form_result[8], wbz = closed_form_result[9];
|
|
577
2076
|
// Feature id from the box-local voronoi region (1..27) — stable
|
|
578
2077
|
// while the sphere stays on the same face / edge / vertex of the
|
|
579
2078
|
// box, which is the steady-state case for resting / sliding contact.
|
|
2079
|
+
const fid = sphere_box_voronoi_fid_array(pair_sphere_a, 0, pair_box_b, 0);
|
|
580
2080
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
boxTr.translation_x, boxTr.translation_y, boxTr.translation_z,
|
|
584
|
-
boxTr.rotation_x, boxTr.rotation_y, boxTr.rotation_z, boxTr.rotation_w,
|
|
585
|
-
bh.x, bh.y, bh.z
|
|
586
|
-
);
|
|
587
|
-
|
|
588
|
-
if (isSphereA) {
|
|
589
|
-
return append_contact(count, wsx, wsy, wsz, wbx, wby, wbz, nx, ny, nz, depth, fid);
|
|
590
|
-
} else {
|
|
591
|
-
return append_contact(count, wbx, wby, wbz, wsx, wsy, wsz, -nx, -ny, -nz, depth, fid);
|
|
592
|
-
}
|
|
2081
|
+
// normal box → sphere, [4..6] on the sphere, [7..9] on the box
|
|
2082
|
+
return append_closed_form_contact(count, closed_form_result, isSphereA, fid);
|
|
593
2083
|
}
|
|
594
2084
|
|
|
595
2085
|
// box-box multi-point
|
|
596
2086
|
if (isBoxA && isBoxB) {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
const ok =
|
|
600
|
-
box_manifold_result,
|
|
601
|
-
trA.translation_x, trA.translation_y, trA.translation_z,
|
|
602
|
-
trA.rotation_x, trA.rotation_y, trA.rotation_z, trA.rotation_w,
|
|
603
|
-
ah.x, ah.y, ah.z,
|
|
604
|
-
trB.translation_x, trB.translation_y, trB.translation_z,
|
|
605
|
-
trB.rotation_x, trB.rotation_y, trB.rotation_z, trB.rotation_w,
|
|
606
|
-
bh.x, bh.y, bh.z
|
|
607
|
-
);
|
|
2087
|
+
box_record_pack(pair_box_a, trA, shapeA.half_extents);
|
|
2088
|
+
box_record_pack(pair_box_b, trB, shapeB.half_extents);
|
|
2089
|
+
const ok = box_box_manifold_array(box_manifold_result, pair_box_a, 0, pair_box_b, 0);
|
|
608
2090
|
if (!ok) return count;
|
|
609
|
-
const nx = box_manifold_result[0], ny = box_manifold_result[1], nz = box_manifold_result[2];
|
|
610
2091
|
const cc = box_manifold_result[3] | 0;
|
|
611
2092
|
// Box-box manifolds: closed-form clipping doesn't expose stable
|
|
612
2093
|
// per-contact feature ids (a contact migrates from "incident-vertex k"
|
|
@@ -616,13 +2097,7 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
616
2097
|
// typically stay >>MATCH_TOL apart, so position matching is
|
|
617
2098
|
// unambiguous frame-to-frame.
|
|
618
2099
|
for (let k = 0; k < cc; k++) {
|
|
619
|
-
|
|
620
|
-
count = append_contact(count,
|
|
621
|
-
box_manifold_result[base], box_manifold_result[base + 1], box_manifold_result[base + 2],
|
|
622
|
-
box_manifold_result[base + 3], box_manifold_result[base + 4], box_manifold_result[base + 5],
|
|
623
|
-
nx, ny, nz,
|
|
624
|
-
box_manifold_result[base + 6],
|
|
625
|
-
0);
|
|
2100
|
+
count = append_box_manifold_contact(count, box_manifold_result, 4 + k * 7);
|
|
626
2101
|
}
|
|
627
2102
|
return count;
|
|
628
2103
|
}
|
|
@@ -630,100 +2105,74 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
630
2105
|
// capsule-capsule (multi-point: near-parallel overlap emits both ends of
|
|
631
2106
|
// the overlap interval; everything else the single closest-point contact)
|
|
632
2107
|
if (isCapsuleA && isCapsuleB) {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
trA.translation_x, trA.translation_y, trA.translation_z,
|
|
637
|
-
trA.rotation_x, trA.rotation_y, trA.rotation_z, trA.rotation_w,
|
|
638
|
-
a_shape.radius, a_shape.height * 0.5,
|
|
639
|
-
trB.translation_x, trB.translation_y, trB.translation_z,
|
|
640
|
-
trB.rotation_x, trB.rotation_y, trB.rotation_z, trB.rotation_w,
|
|
641
|
-
b_shape.radius, b_shape.height * 0.5
|
|
642
|
-
);
|
|
2108
|
+
capsule_record_pack(pair_capsule_a, trA, shapeA);
|
|
2109
|
+
capsule_record_pack(pair_capsule_b, trB, shapeB);
|
|
2110
|
+
const n_cc = capsule_capsule_multi_contacts_array(capsule_capsule_result, pair_capsule_a, 0, pair_capsule_b, 0);
|
|
643
2111
|
for (let k = 0; k < n_cc; k++) {
|
|
644
|
-
const off = k * CAPSULE_CAPSULE_CONTACT_STRIDE;
|
|
645
2112
|
// fid = k + 1: stable per interval end (1-based; 0 = no info).
|
|
646
|
-
count =
|
|
647
|
-
capsule_capsule_result[off], capsule_capsule_result[off + 1], capsule_capsule_result[off + 2],
|
|
648
|
-
capsule_capsule_result[off + 3], capsule_capsule_result[off + 4], capsule_capsule_result[off + 5],
|
|
649
|
-
capsule_capsule_result[off + 6], capsule_capsule_result[off + 7], capsule_capsule_result[off + 8],
|
|
650
|
-
capsule_capsule_result[off + 9], k + 1);
|
|
2113
|
+
count = append_manifold_contact(count, capsule_capsule_result, k * CAPSULE_CAPSULE_CONTACT_STRIDE, true, k + 1);
|
|
651
2114
|
}
|
|
652
2115
|
return count;
|
|
653
2116
|
}
|
|
654
2117
|
|
|
655
2118
|
// capsule ↔ sphere
|
|
656
2119
|
if ((isCapsuleA && isSphereB) || (isSphereA && isCapsuleB)) {
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
const
|
|
660
|
-
const sphereShape = isCapsuleA ? colB.shape : colA.shape;
|
|
661
|
-
const ok = capsule_sphere_contact(
|
|
662
|
-
closed_form_result,
|
|
663
|
-
capsuleTr.translation_x, capsuleTr.translation_y, capsuleTr.translation_z,
|
|
664
|
-
capsuleTr.rotation_x, capsuleTr.rotation_y, capsuleTr.rotation_z, capsuleTr.rotation_w,
|
|
665
|
-
capsuleShape.radius, capsuleShape.height * 0.5,
|
|
666
|
-
sphereTr.translation_x, sphereTr.translation_y, sphereTr.translation_z, sphereShape.radius
|
|
667
|
-
);
|
|
2120
|
+
capsule_record_pack(pair_capsule_a, isCapsuleA ? trA : trB, isCapsuleA ? shapeA : shapeB);
|
|
2121
|
+
sphere_record_pack(pair_sphere_b, isCapsuleA ? trB : trA, isCapsuleA ? shapeB : shapeA);
|
|
2122
|
+
const ok = capsule_sphere_contact_array(closed_form_result, pair_capsule_a, 0, pair_sphere_b, 0);
|
|
668
2123
|
if (!ok) return count;
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
const sph_x = closed_form_result[7], sph_y = closed_form_result[8], sph_z = closed_form_result[9];
|
|
673
|
-
// Single contact per capsule-sphere pair; fid = 1.
|
|
674
|
-
if (isCapsuleA) {
|
|
675
|
-
return append_contact(count, cap_x, cap_y, cap_z, sph_x, sph_y, sph_z, nx, ny, nz, depth, 1);
|
|
676
|
-
} else {
|
|
677
|
-
return append_contact(count, sph_x, sph_y, sph_z, cap_x, cap_y, cap_z, -nx, -ny, -nz, depth, 1);
|
|
678
|
-
}
|
|
2124
|
+
// normal sphere → capsule, [4..6] on the capsule, [7..9] on the sphere. Single contact per
|
|
2125
|
+
// capsule-sphere pair; fid = 1.
|
|
2126
|
+
return append_closed_form_contact(count, closed_form_result, isCapsuleA, 1);
|
|
679
2127
|
}
|
|
680
2128
|
|
|
681
2129
|
// capsule ↔ box (multi-point — 1 closest-segment + up to 2 cap-centres)
|
|
682
2130
|
if ((isCapsuleA && isBoxB) || (isBoxA && isCapsuleB)) {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
const
|
|
686
|
-
const boxShape = isCapsuleA ? shapeB : shapeA;
|
|
687
|
-
const bh = boxShape.half_extents;
|
|
688
|
-
const cc = capsule_box_multi_contacts(
|
|
689
|
-
capsule_box_multi_result,
|
|
690
|
-
capsuleTr.translation_x, capsuleTr.translation_y, capsuleTr.translation_z,
|
|
691
|
-
capsuleTr.rotation_x, capsuleTr.rotation_y, capsuleTr.rotation_z, capsuleTr.rotation_w,
|
|
692
|
-
capsuleShape.radius, capsuleShape.height * 0.5,
|
|
693
|
-
boxTr.translation_x, boxTr.translation_y, boxTr.translation_z,
|
|
694
|
-
boxTr.rotation_x, boxTr.rotation_y, boxTr.rotation_z, boxTr.rotation_w,
|
|
695
|
-
bh.x, bh.y, bh.z
|
|
696
|
-
);
|
|
2131
|
+
capsule_record_pack(pair_capsule_a, isCapsuleA ? trA : trB, isCapsuleA ? shapeA : shapeB);
|
|
2132
|
+
box_record_pack(pair_box_b, isCapsuleA ? trB : trA, (isCapsuleA ? shapeB : shapeA).half_extents);
|
|
2133
|
+
const cc = capsule_box_multi_contacts_array(capsule_box_multi_result, pair_capsule_a, 0, pair_box_b, 0);
|
|
697
2134
|
if (cc === 0) return count;
|
|
698
2135
|
// multi_result layout per contact: cap_x/y/z (A side), box_x/y/z (B side), nx/ny/nz, depth.
|
|
699
2136
|
// Feature id per sub-contact: 1 = closest-segment, 2/3 = caps. The
|
|
700
2137
|
// emission order from capsule_box_multi_contacts is stable across
|
|
701
2138
|
// frames for the same geometric configuration.
|
|
702
2139
|
for (let k = 0; k < cc; k++) {
|
|
703
|
-
|
|
704
|
-
const cap_x = capsule_box_multi_result[o];
|
|
705
|
-
const cap_y = capsule_box_multi_result[o + 1];
|
|
706
|
-
const cap_z = capsule_box_multi_result[o + 2];
|
|
707
|
-
const box_x = capsule_box_multi_result[o + 3];
|
|
708
|
-
const box_y = capsule_box_multi_result[o + 4];
|
|
709
|
-
const box_z = capsule_box_multi_result[o + 5];
|
|
710
|
-
const nx = capsule_box_multi_result[o + 6];
|
|
711
|
-
const ny = capsule_box_multi_result[o + 7];
|
|
712
|
-
const nz = capsule_box_multi_result[o + 8];
|
|
713
|
-
const depth = capsule_box_multi_result[o + 9];
|
|
714
|
-
const sub_fid = k + 1;
|
|
715
|
-
if (isCapsuleA) {
|
|
716
|
-
count = append_contact(count, cap_x, cap_y, cap_z, box_x, box_y, box_z, nx, ny, nz, depth, sub_fid);
|
|
717
|
-
} else {
|
|
718
|
-
count = append_contact(count, box_x, box_y, box_z, cap_x, cap_y, cap_z, -nx, -ny, -nz, depth, sub_fid);
|
|
719
|
-
}
|
|
2140
|
+
count = append_manifold_contact(count, capsule_box_multi_result, k * CAPSULE_BOX_CONTACT_STRIDE, isCapsuleA, k + 1);
|
|
720
2141
|
}
|
|
721
2142
|
return count;
|
|
722
2143
|
}
|
|
723
2144
|
|
|
724
2145
|
// convex hull ↔ convex hull (multi-point clipping via GJK + EPA + face-clip).
|
|
725
2146
|
if (shapeA.isConvexHullShape3D === true && shapeB.isConvexHullShape3D === true) {
|
|
726
|
-
return hull_pair_contacts(count, shapeA, trA, shapeB, trB
|
|
2147
|
+
return hull_pair_contacts(count, shapeA, trA, shapeB, trB);
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
// box ↔ convex hull: a box is a polytope too, so it takes the same axis and face clip — a
|
|
2151
|
+
// face-on-face patch, not the generic fallback's single contact.
|
|
2152
|
+
if (isBoxA && shapeB.isConvexHullShape3D === true) {
|
|
2153
|
+
box_polytope_set(shapeA.half_extents);
|
|
2154
|
+
return hull_pair_contacts(count, box_polytope, trA, shapeB, trB);
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
if (shapeA.isConvexHullShape3D === true && isBoxB) {
|
|
2158
|
+
box_polytope_set(shapeB.half_extents);
|
|
2159
|
+
return hull_pair_contacts(count, shapeA, trA, box_polytope, trB);
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
// convex hull ↔ sphere: closed form, the contact on the line through the sphere's centre
|
|
2163
|
+
if ((shapeA.isConvexHullShape3D === true && isSphereB) || (isSphereA && shapeB.isConvexHullShape3D === true)) {
|
|
2164
|
+
const hull_is_a = isSphereB;
|
|
2165
|
+
|
|
2166
|
+
pose_pack(pair_pose, hull_is_a ? trA : trB);
|
|
2167
|
+
sphere_record_pack(pair_sphere_b, hull_is_a ? trB : trA, hull_is_a ? shapeB : shapeA);
|
|
2168
|
+
|
|
2169
|
+
const ok = hull_sphere_contact_array(closed_form_result, hull_is_a ? shapeA : shapeB, pair_pose, 0, pair_sphere_b, 0);
|
|
2170
|
+
|
|
2171
|
+
if (!ok) return count;
|
|
2172
|
+
|
|
2173
|
+
// normal from the hull toward the sphere; [4..6] on the sphere, [7..9] on the hull; one
|
|
2174
|
+
// contact per pair, fid = 1
|
|
2175
|
+
return append_closed_form_contact(count, closed_form_result, !hull_is_a, 1);
|
|
727
2176
|
}
|
|
728
2177
|
|
|
729
2178
|
// mesh ↔ mesh. A globally-convex mesh collides as a single cached hull
|
|
@@ -735,7 +2184,7 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
735
2184
|
const hullA = get_mesh_convex_hull(shapeA);
|
|
736
2185
|
const hullB = get_mesh_convex_hull(shapeB);
|
|
737
2186
|
if (hullA !== null && hullB !== null) {
|
|
738
|
-
return hull_pair_contacts(count, hullA, trA, hullB, trB
|
|
2187
|
+
return hull_pair_contacts(count, hullA, trA, hullB, trB);
|
|
739
2188
|
}
|
|
740
2189
|
return mesh_mesh_tet_contacts(count, shapeA, trA, shapeB, trB, append_contact);
|
|
741
2190
|
}
|
|
@@ -762,34 +2211,41 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
762
2211
|
const convex_col = isConcaveA ? colB : colA;
|
|
763
2212
|
const convex_tr = isConcaveA ? trB : trA;
|
|
764
2213
|
|
|
2214
|
+
// The two bodies' poses, packed once for the calls below and the per-triangle kernels: passed
|
|
2215
|
+
// as numbers to a call that is not inlined, every one would be boxed (see
|
|
2216
|
+
// triangle_vertices_to_world).
|
|
2217
|
+
pose_pack(convex_pose, convex_tr);
|
|
2218
|
+
pose_pack(concave_pose, concave_tr);
|
|
2219
|
+
|
|
765
2220
|
// 1. Convex shape's world AABB.
|
|
766
2221
|
convex_col.shape.compute_bounding_box(concave_local_aabb);
|
|
767
|
-
|
|
768
|
-
concave_world_aabb, 0,
|
|
769
|
-
concave_local_aabb[0], concave_local_aabb[1], concave_local_aabb[2],
|
|
770
|
-
concave_local_aabb[3], concave_local_aabb[4], concave_local_aabb[5],
|
|
771
|
-
convex_tr.translation_x, convex_tr.translation_y, convex_tr.translation_z,
|
|
772
|
-
convex_tr.rotation_x, convex_tr.rotation_y, convex_tr.rotation_z, convex_tr.rotation_w
|
|
773
|
-
);
|
|
2222
|
+
aabb3_transform_oriented_array(concave_world_aabb, 0, concave_local_aabb, 0, convex_pose, 0);
|
|
774
2223
|
|
|
775
2224
|
// 2. Project into concave's body-local frame.
|
|
776
|
-
|
|
777
|
-
concave_query_aabb, 0,
|
|
778
|
-
concave_world_aabb,
|
|
779
|
-
concave_tr.translation_x, concave_tr.translation_y, concave_tr.translation_z,
|
|
780
|
-
concave_tr.rotation_x, concave_tr.rotation_y, concave_tr.rotation_z, concave_tr.rotation_w
|
|
781
|
-
);
|
|
2225
|
+
aabb3_transform_oriented_inverse_array(concave_query_aabb, 0, concave_world_aabb, concave_pose, 0);
|
|
782
2226
|
|
|
783
2227
|
// 3. Decompose concave shape into triangles overlapping the query.
|
|
784
|
-
// The enumerator returns the TRUE overlap count
|
|
785
|
-
//
|
|
786
|
-
|
|
787
|
-
let
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
2228
|
+
// The enumerator returns the TRUE overlap count; when that did not
|
|
2229
|
+
// fit, grow and enumerate again (deterministic, so once suffices).
|
|
2230
|
+
let tri_count = decompose_to_triangles_array(triangle_buffer, 0, concave_col.shape, concave_query_aabb, 0);
|
|
2231
|
+
let tri_capacity = triangle_buffer.length / TRIANGLE_FLOAT_STRIDE;
|
|
2232
|
+
if (tri_count > tri_capacity && tri_capacity < MAX_TRIANGLES_PER_PAIR) {
|
|
2233
|
+
tri_capacity = Math.min(MAX_TRIANGLES_PER_PAIR, Math.max(tri_count, tri_capacity * 2));
|
|
2234
|
+
triangle_buffer = new Float64Array(tri_capacity * TRIANGLE_FLOAT_STRIDE);
|
|
2235
|
+
tri_count = decompose_to_triangles_array(triangle_buffer, 0, concave_col.shape, concave_query_aabb, 0);
|
|
2236
|
+
}
|
|
2237
|
+
if (tri_count > tri_capacity) {
|
|
2238
|
+
// Past the bound: overflow writes were dropped, so never read them.
|
|
2239
|
+
if (!triangle_overflow_reported) {
|
|
2240
|
+
triangle_overflow_reported = true;
|
|
2241
|
+
console.warn(
|
|
2242
|
+
`narrowphase: a ${convex_col.shape.constructor.name} overlaps ${tri_count} triangles of a`
|
|
2243
|
+
+ ` ${concave_col.shape.constructor.name}; ${tri_capacity} are collided against and the`
|
|
2244
|
+
+ ` rest are not. Coarsen the collision surface under it, or split the body.`
|
|
2245
|
+
);
|
|
2246
|
+
}
|
|
2247
|
+
tri_count = tri_capacity;
|
|
2248
|
+
}
|
|
793
2249
|
if (tri_count === 0) return count;
|
|
794
2250
|
|
|
795
2251
|
// 4. Set up the convex side once; the concave side gets a
|
|
@@ -797,11 +2253,7 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
797
2253
|
// the concave side as our internal "A" so EPA's sign-check
|
|
798
2254
|
// convention matches the convex fallback below; the final
|
|
799
2255
|
// append_contact() swaps if the original A was the convex one.
|
|
800
|
-
posed_b.
|
|
801
|
-
convex_col.shape,
|
|
802
|
-
convex_tr.translation_x, convex_tr.translation_y, convex_tr.translation_z,
|
|
803
|
-
convex_tr.rotation_x, convex_tr.rotation_y, convex_tr.rotation_z, convex_tr.rotation_w
|
|
804
|
-
);
|
|
2256
|
+
posed_b.setup_pose(convex_col.shape, convex_pose, 0);
|
|
805
2257
|
posed_a.shape = triangle_shape;
|
|
806
2258
|
posed_a.px = concave_tr.translation_x;
|
|
807
2259
|
posed_a.py = concave_tr.translation_y;
|
|
@@ -817,12 +2269,6 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
817
2269
|
const convex_wy = convex_tr.translation_y;
|
|
818
2270
|
const convex_wz = convex_tr.translation_z;
|
|
819
2271
|
|
|
820
|
-
// Track the candidate-buffer index at the start of this
|
|
821
|
-
// concave dispatch — the per-triangle dedup pass scans from
|
|
822
|
-
// here to the current count, ignoring contacts from earlier
|
|
823
|
-
// collider pairs in the same body pair.
|
|
824
|
-
const pair_start_count = count;
|
|
825
|
-
|
|
826
2272
|
// Pre-compute concave's rotation components for the q · v · q⁻¹
|
|
827
2273
|
// rotations done per-triangle below (face normal + centroid).
|
|
828
2274
|
const cqx = concave_tr.rotation_x;
|
|
@@ -858,6 +2304,49 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
858
2304
|
const isCapsuleConvex = convex_col.shape.isCapsuleShape3D === true;
|
|
859
2305
|
const capsule_shape = isCapsuleConvex ? convex_col.shape : null;
|
|
860
2306
|
|
|
2307
|
+
if (isSphereConvex) {
|
|
2308
|
+
convex_record[0] = convex_wx;
|
|
2309
|
+
convex_record[1] = convex_wy;
|
|
2310
|
+
convex_record[2] = convex_wz;
|
|
2311
|
+
convex_record[3] = sphere_radius;
|
|
2312
|
+
} else if (isBoxConvex || isCapsuleConvex) {
|
|
2313
|
+
pose_pack(convex_record, convex_tr);
|
|
2314
|
+
|
|
2315
|
+
if (isBoxConvex) {
|
|
2316
|
+
convex_record[7] = box_half_extents.x;
|
|
2317
|
+
convex_record[8] = box_half_extents.y;
|
|
2318
|
+
convex_record[9] = box_half_extents.z;
|
|
2319
|
+
} else {
|
|
2320
|
+
convex_record[7] = capsule_shape.radius;
|
|
2321
|
+
convex_record[8] = capsule_shape.height * 0.5;
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
// The per-triangle dedup compares each new contact with the earlier
|
|
2326
|
+
// contacts of THIS concave dispatch only — not those of earlier
|
|
2327
|
+
// collider pairs in the same body pair — through an index that starts
|
|
2328
|
+
// empty here (see dedup_contains). The sphere path matches on the
|
|
2329
|
+
// normal alone, every other path on the normal and the A-side point.
|
|
2330
|
+
dedup_begin(isSphereConvex);
|
|
2331
|
+
|
|
2332
|
+
// Contacts on an edge or a vertex of a triangle wait here until every triangle has been
|
|
2333
|
+
// collided, then go through Box3D's internal-edge rules (see "Internal edges" above).
|
|
2334
|
+
tentative_count = 0;
|
|
2335
|
+
face_triangle_count = 0;
|
|
2336
|
+
|
|
2337
|
+
// where a round body touches a plane, for the contacts taken as a plane's
|
|
2338
|
+
if (isSphereConvex) {
|
|
2339
|
+
round_core[ROUND_RADIUS] = sphere_radius;
|
|
2340
|
+
round_core[ROUND_HALF] = 0;
|
|
2341
|
+
round_core_place(convex_tr);
|
|
2342
|
+
} else if (isCapsuleConvex) {
|
|
2343
|
+
round_core[ROUND_RADIUS] = capsule_shape.radius;
|
|
2344
|
+
round_core[ROUND_HALF] = capsule_shape.height * 0.5;
|
|
2345
|
+
round_core_place(convex_tr);
|
|
2346
|
+
} else {
|
|
2347
|
+
round_core[ROUND_RADIUS] = 0;
|
|
2348
|
+
}
|
|
2349
|
+
|
|
861
2350
|
for (let i = 0; i < tri_count; i++) {
|
|
862
2351
|
const tri_offset = i * TRIANGLE_FLOAT_STRIDE;
|
|
863
2352
|
triangle_shape.bind(triangle_buffer, tri_offset);
|
|
@@ -906,23 +2395,9 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
906
2395
|
|
|
907
2396
|
// Rotate each triangle vertex from concave-local to world
|
|
908
2397
|
// via the shared helper.
|
|
909
|
-
triangle_vertices_to_world(
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
cqx, cqy, cqz, cqw,
|
|
913
|
-
c_pos_x, c_pos_y, c_pos_z
|
|
914
|
-
);
|
|
915
|
-
const ax_w = scratch_tri_world[0], ay_w = scratch_tri_world[1], az_w = scratch_tri_world[2];
|
|
916
|
-
const bx_w = scratch_tri_world[3], by_w = scratch_tri_world[4], bz_w = scratch_tri_world[5];
|
|
917
|
-
const cx_w = scratch_tri_world[6], cy_w = scratch_tri_world[7], cz_w = scratch_tri_world[8];
|
|
918
|
-
|
|
919
|
-
const ok = sphere_triangle_contact(
|
|
920
|
-
sphere_triangle_result,
|
|
921
|
-
convex_wx, convex_wy, convex_wz, sphere_radius,
|
|
922
|
-
ax_w, ay_w, az_w,
|
|
923
|
-
bx_w, by_w, bz_w,
|
|
924
|
-
cx_w, cy_w, cz_w
|
|
925
|
-
);
|
|
2398
|
+
triangle_vertices_to_world(scratch_tri_world, triangle_buffer, tri_offset, concave_pose);
|
|
2399
|
+
|
|
2400
|
+
const ok = sphere_triangle_contact_array(sphere_triangle_result, convex_record, 0, scratch_tri_world, 0);
|
|
926
2401
|
|
|
927
2402
|
if (!ok) continue;
|
|
928
2403
|
|
|
@@ -939,7 +2414,8 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
939
2414
|
// outward side of the triangle. If the contact normal
|
|
940
2415
|
// opposes the face normal, the sphere is behind /
|
|
941
2416
|
// inside the solid — skip rather than push it deeper.
|
|
942
|
-
|
|
2417
|
+
const n_dot_f = n_t2s_x * fnx_w + n_t2s_y * fny_w + n_t2s_z * fnz_w;
|
|
2418
|
+
if (n_dot_f <= 0) continue;
|
|
943
2419
|
|
|
944
2420
|
// Stored normal convention is "B → A". `nx, ny, nz` here
|
|
945
2421
|
// points convex → concave (which the existing EPA branch
|
|
@@ -953,44 +2429,48 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
953
2429
|
const stored_nx_s = isConcaveA ? nx_s : -nx_s;
|
|
954
2430
|
const stored_ny_s = isConcaveA ? ny_s : -ny_s;
|
|
955
2431
|
const stored_nz_s = isConcaveA ? nz_s : -nz_s;
|
|
956
|
-
let is_duplicate_s = false;
|
|
957
|
-
for (let k = pair_start_count; k < count; k++) {
|
|
958
|
-
const ko = k * CANDIDATE_STRIDE;
|
|
959
|
-
const dnx = candidates[ko + 6] - stored_nx_s;
|
|
960
|
-
const dny = candidates[ko + 7] - stored_ny_s;
|
|
961
|
-
const dnz = candidates[ko + 8] - stored_nz_s;
|
|
962
|
-
if (dnx * dnx + dny * dny + dnz * dnz < 0.001) {
|
|
963
|
-
is_duplicate_s = true;
|
|
964
|
-
break;
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
if (is_duplicate_s) continue;
|
|
968
2432
|
|
|
969
2433
|
// Surface witnesses from the closed-form solver:
|
|
970
2434
|
// result[4..6] = sphere surface point (convex side)
|
|
971
2435
|
// result[7..9] = triangle closest point (concave side)
|
|
972
|
-
//
|
|
973
|
-
//
|
|
974
|
-
// because flat-faced support witnesses can be arbitrarily
|
|
975
|
-
// far from the contact patch), closest-point-on-triangle
|
|
976
|
-
// is exact and lies on the contact patch.
|
|
2436
|
+
// Closest-point-on-triangle is exact and lies on the
|
|
2437
|
+
// contact patch.
|
|
977
2438
|
const sphere_wx = sphere_triangle_result[4];
|
|
978
2439
|
const sphere_wy = sphere_triangle_result[5];
|
|
979
2440
|
const sphere_wz = sphere_triangle_result[6];
|
|
980
2441
|
const tri_wx = sphere_triangle_result[7];
|
|
981
2442
|
const tri_wy = sphere_triangle_result[8];
|
|
982
2443
|
const tri_wz = sphere_triangle_result[9];
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
2444
|
+
|
|
2445
|
+
const c = staged_contact;
|
|
2446
|
+
c[0] = isConcaveA ? tri_wx : sphere_wx;
|
|
2447
|
+
c[1] = isConcaveA ? tri_wy : sphere_wy;
|
|
2448
|
+
c[2] = isConcaveA ? tri_wz : sphere_wz;
|
|
2449
|
+
c[3] = isConcaveA ? sphere_wx : tri_wx;
|
|
2450
|
+
c[4] = isConcaveA ? sphere_wy : tri_wy;
|
|
2451
|
+
c[5] = isConcaveA ? sphere_wz : tri_wz;
|
|
2452
|
+
c[6] = stored_nx_s; c[7] = stored_ny_s; c[8] = stored_nz_s;
|
|
2453
|
+
c[9] = sd;
|
|
2454
|
+
c[10] = tri_fid;
|
|
2455
|
+
|
|
2456
|
+
// The nearest point on an edge or a vertex: it waits for the others, nearest first.
|
|
2457
|
+
// (cos(n, face) < FACE_CONTACT_COS, squared: n is unit, n · face > 0.)
|
|
2458
|
+
if (n_dot_f * n_dot_f < FACE_CONTACT_COS * FACE_CONTACT_COS * (fnx_w * fnx_w + fny_w * fny_w + fnz_w * fnz_w)) {
|
|
2459
|
+
const distance = sphere_radius - sd;
|
|
2460
|
+
|
|
2461
|
+
c[11] = distance * distance;
|
|
2462
|
+
tentative_add(c, i, triangle_facing_feature(scratch_tri_world, sphere_triangle_result, 0));
|
|
2463
|
+
continue;
|
|
993
2464
|
}
|
|
2465
|
+
|
|
2466
|
+
face_triangle_add(i);
|
|
2467
|
+
|
|
2468
|
+
// a duplicate is any earlier contact of this dispatch with the same normal (keyed by
|
|
2469
|
+
// normal: the witness is not looked at)
|
|
2470
|
+
if (dedup_contains(c, 0)) continue;
|
|
2471
|
+
|
|
2472
|
+
count = append_contact_record(count, c, 0);
|
|
2473
|
+
dedup_insert(count - 1);
|
|
994
2474
|
continue;
|
|
995
2475
|
}
|
|
996
2476
|
|
|
@@ -998,25 +2478,9 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
998
2478
|
if (isBoxConvex) {
|
|
999
2479
|
// Rotate each triangle vertex from concave-local to world
|
|
1000
2480
|
// via the shared helper.
|
|
1001
|
-
triangle_vertices_to_world(
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
cqx, cqy, cqz, cqw,
|
|
1005
|
-
c_pos_x, c_pos_y, c_pos_z
|
|
1006
|
-
);
|
|
1007
|
-
const ax_w = scratch_tri_world[0], ay_w = scratch_tri_world[1], az_w = scratch_tri_world[2];
|
|
1008
|
-
const bx_w = scratch_tri_world[3], by_w = scratch_tri_world[4], bz_w = scratch_tri_world[5];
|
|
1009
|
-
const cx_w = scratch_tri_world[6], cy_w = scratch_tri_world[7], cz_w = scratch_tri_world[8];
|
|
1010
|
-
|
|
1011
|
-
const ok = box_triangle_contact(
|
|
1012
|
-
box_triangle_result,
|
|
1013
|
-
convex_tr.translation_x, convex_tr.translation_y, convex_tr.translation_z,
|
|
1014
|
-
convex_tr.rotation_x, convex_tr.rotation_y, convex_tr.rotation_z, convex_tr.rotation_w,
|
|
1015
|
-
box_half_extents.x, box_half_extents.y, box_half_extents.z,
|
|
1016
|
-
ax_w, ay_w, az_w,
|
|
1017
|
-
bx_w, by_w, bz_w,
|
|
1018
|
-
cx_w, cy_w, cz_w
|
|
1019
|
-
);
|
|
2481
|
+
triangle_vertices_to_world(scratch_tri_world, triangle_buffer, tri_offset, concave_pose);
|
|
2482
|
+
|
|
2483
|
+
const ok = box_triangle_contact_array(box_triangle_result, convex_record, 0, scratch_tri_world, 0);
|
|
1020
2484
|
if (!ok) continue;
|
|
1021
2485
|
|
|
1022
2486
|
// Same convention as sphere_triangle: result normal points
|
|
@@ -1030,7 +2494,8 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
1030
2494
|
// One-sided rejection: the box must lie on the outward
|
|
1031
2495
|
// side of the triangle. If the contact normal opposes
|
|
1032
2496
|
// the face normal, the box is inside the solid → skip.
|
|
1033
|
-
|
|
2497
|
+
const n_dot_f = n_t2box_x * fnx_w + n_t2box_y * fny_w + n_t2box_z * fnz_w;
|
|
2498
|
+
if (n_dot_f <= 0) continue;
|
|
1034
2499
|
|
|
1035
2500
|
const nx_b = -n_t2box_x;
|
|
1036
2501
|
const ny_b = -n_t2box_y;
|
|
@@ -1039,11 +2504,23 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
1039
2504
|
const stored_ny_b = isConcaveA ? ny_b : -ny_b;
|
|
1040
2505
|
const stored_nz_b = isConcaveA ? nz_b : -nz_b;
|
|
1041
2506
|
|
|
2507
|
+
const cc = box_triangle_result[3] | 0;
|
|
2508
|
+
|
|
2509
|
+
// A face contact: on the triangle's face, or on a face of the box lying within 60° of
|
|
2510
|
+
// it. An edge-edge axis, or a box face standing up off the triangle, is tentative, and
|
|
2511
|
+
// waits for the internal-edge rules.
|
|
2512
|
+
const axis = box_triangle_result[BOX_TRIANGLE_AXIS_OFFSET];
|
|
2513
|
+
let face = axis === BOX_TRIANGLE_AXIS_TRIANGLE_FACE;
|
|
2514
|
+
|
|
2515
|
+
if (axis === BOX_TRIANGLE_AXIS_BOX_FACE) {
|
|
2516
|
+
// cos(n, face) > HULL_FACE_COS, squared: n is unit, n · face > 0
|
|
2517
|
+
face = n_dot_f * n_dot_f > HULL_FACE_COS * HULL_FACE_COS * (fnx_w * fnx_w + fny_w * fny_w + fnz_w * fnz_w);
|
|
2518
|
+
}
|
|
2519
|
+
|
|
1042
2520
|
// Emit each clipped contact. The contact count from
|
|
1043
2521
|
// box_triangle_contact is at most 4; combined with the
|
|
1044
2522
|
// existing per-pair candidate cap (64 entries), tens of
|
|
1045
2523
|
// overlapping triangles per pair are safely accommodated.
|
|
1046
|
-
const cc = box_triangle_result[3] | 0;
|
|
1047
2524
|
for (let k = 0; k < cc; k++) {
|
|
1048
2525
|
const base = 4 + k * 7;
|
|
1049
2526
|
const t_wx = box_triangle_result[base];
|
|
@@ -1056,45 +2533,32 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
1056
2533
|
|
|
1057
2534
|
// Dedup against earlier candidates in THIS dispatch
|
|
1058
2535
|
// (adjacent triangles can produce coincident contacts
|
|
1059
|
-
// — same as the sphere and EPA paths)
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
if (dpx * dpx + dpy * dpy + dpz * dpz < 1e-6) {
|
|
1080
|
-
is_duplicate_k = true;
|
|
1081
|
-
break;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
if (is_duplicate_k) continue;
|
|
1086
|
-
|
|
1087
|
-
if (isConcaveA) {
|
|
1088
|
-
count = append_contact(count,
|
|
1089
|
-
t_wx, t_wy, t_wz,
|
|
1090
|
-
b_wx, b_wy, b_wz,
|
|
1091
|
-
nx_b, ny_b, nz_b, d_k, tri_fid);
|
|
1092
|
-
} else {
|
|
1093
|
-
count = append_contact(count,
|
|
1094
|
-
b_wx, b_wy, b_wz,
|
|
1095
|
-
t_wx, t_wy, t_wz,
|
|
1096
|
-
-nx_b, -ny_b, -nz_b, d_k, tri_fid);
|
|
2536
|
+
// — same as the sphere and EPA paths): same normal AND
|
|
2537
|
+
// same world-A point (triangle surface or box surface
|
|
2538
|
+
// depending on dispatcher swap) — a multi-point manifold
|
|
2539
|
+
// has distinct points with the same normal, and those
|
|
2540
|
+
// should NOT be deduped.
|
|
2541
|
+
const c = staged_contact;
|
|
2542
|
+
c[0] = isConcaveA ? t_wx : b_wx;
|
|
2543
|
+
c[1] = isConcaveA ? t_wy : b_wy;
|
|
2544
|
+
c[2] = isConcaveA ? t_wz : b_wz;
|
|
2545
|
+
c[3] = isConcaveA ? b_wx : t_wx;
|
|
2546
|
+
c[4] = isConcaveA ? b_wy : t_wy;
|
|
2547
|
+
c[5] = isConcaveA ? b_wz : t_wz;
|
|
2548
|
+
c[6] = stored_nx_b; c[7] = stored_ny_b; c[8] = stored_nz_b;
|
|
2549
|
+
c[9] = d_k;
|
|
2550
|
+
c[10] = tri_fid;
|
|
2551
|
+
|
|
2552
|
+
if (!face) {
|
|
2553
|
+
c[11] = 0;
|
|
2554
|
+
tentative_add(c, i, triangle_witness_feature(scratch_tri_world, box_triangle_result, base));
|
|
2555
|
+
continue;
|
|
1097
2556
|
}
|
|
2557
|
+
|
|
2558
|
+
if (dedup_contains(c, 0)) continue;
|
|
2559
|
+
|
|
2560
|
+
count = append_contact_record(count, c, 0);
|
|
2561
|
+
dedup_insert(count - 1);
|
|
1098
2562
|
}
|
|
1099
2563
|
continue;
|
|
1100
2564
|
}
|
|
@@ -1103,25 +2567,9 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
1103
2567
|
if (isCapsuleConvex) {
|
|
1104
2568
|
// Rotate each triangle vertex from concave-local to world
|
|
1105
2569
|
// via the shared helper.
|
|
1106
|
-
triangle_vertices_to_world(
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
cqx, cqy, cqz, cqw,
|
|
1110
|
-
c_pos_x, c_pos_y, c_pos_z
|
|
1111
|
-
);
|
|
1112
|
-
const ax_w = scratch_tri_world[0], ay_w = scratch_tri_world[1], az_w = scratch_tri_world[2];
|
|
1113
|
-
const bx_w = scratch_tri_world[3], by_w = scratch_tri_world[4], bz_w = scratch_tri_world[5];
|
|
1114
|
-
const cx_w = scratch_tri_world[6], cy_w = scratch_tri_world[7], cz_w = scratch_tri_world[8];
|
|
1115
|
-
|
|
1116
|
-
const cap_cc = capsule_triangle_contact(
|
|
1117
|
-
capsule_triangle_result,
|
|
1118
|
-
convex_tr.translation_x, convex_tr.translation_y, convex_tr.translation_z,
|
|
1119
|
-
convex_tr.rotation_x, convex_tr.rotation_y, convex_tr.rotation_z, convex_tr.rotation_w,
|
|
1120
|
-
capsule_shape.radius, capsule_shape.height * 0.5,
|
|
1121
|
-
ax_w, ay_w, az_w,
|
|
1122
|
-
bx_w, by_w, bz_w,
|
|
1123
|
-
cx_w, cy_w, cz_w
|
|
1124
|
-
);
|
|
2570
|
+
triangle_vertices_to_world(scratch_tri_world, triangle_buffer, tri_offset, concave_pose);
|
|
2571
|
+
|
|
2572
|
+
const cap_cc = capsule_triangle_contact_array(capsule_triangle_result, convex_record, 0, scratch_tri_world, 0);
|
|
1125
2573
|
if (cap_cc === 0) continue;
|
|
1126
2574
|
|
|
1127
2575
|
// Each sub-contact's normal can be distinct (a flat
|
|
@@ -1144,7 +2592,8 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
1144
2592
|
|
|
1145
2593
|
// One-sided face-normal rejection: capsule must be
|
|
1146
2594
|
// on the outward side of the triangle.
|
|
1147
|
-
|
|
2595
|
+
const n_dot_f = n_t2cap_x * fnx_w + n_t2cap_y * fny_w + n_t2cap_z * fnz_w;
|
|
2596
|
+
if (n_dot_f <= 0) continue;
|
|
1148
2597
|
|
|
1149
2598
|
// Stored normal "convex → concave" = "capsule →
|
|
1150
2599
|
// triangle" = negated t2cap.
|
|
@@ -1155,39 +2604,31 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
1155
2604
|
const stored_ny_c = isConcaveA ? ny_c : -ny_c;
|
|
1156
2605
|
const stored_nz_c = isConcaveA ? nz_c : -nz_c;
|
|
1157
2606
|
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
if (is_duplicate_c) continue;
|
|
1179
|
-
|
|
1180
|
-
if (isConcaveA) {
|
|
1181
|
-
count = append_contact(count,
|
|
1182
|
-
ct_x, ct_y, ct_z,
|
|
1183
|
-
cap_x, cap_y, cap_z,
|
|
1184
|
-
nx_c, ny_c, nz_c, d_c, tri_fid);
|
|
1185
|
-
} else {
|
|
1186
|
-
count = append_contact(count,
|
|
1187
|
-
cap_x, cap_y, cap_z,
|
|
1188
|
-
ct_x, ct_y, ct_z,
|
|
1189
|
-
-nx_c, -ny_c, -nz_c, d_c, tri_fid);
|
|
2607
|
+
const c = staged_contact;
|
|
2608
|
+
c[0] = isConcaveA ? ct_x : cap_x;
|
|
2609
|
+
c[1] = isConcaveA ? ct_y : cap_y;
|
|
2610
|
+
c[2] = isConcaveA ? ct_z : cap_z;
|
|
2611
|
+
c[3] = isConcaveA ? cap_x : ct_x;
|
|
2612
|
+
c[4] = isConcaveA ? cap_y : ct_y;
|
|
2613
|
+
c[5] = isConcaveA ? cap_z : ct_z;
|
|
2614
|
+
c[6] = stored_nx_c; c[7] = stored_ny_c; c[8] = stored_nz_c;
|
|
2615
|
+
c[9] = d_c;
|
|
2616
|
+
c[10] = tri_fid;
|
|
2617
|
+
|
|
2618
|
+
// Each sub-contact is a closest-point contact: on the face when its normal is the
|
|
2619
|
+
// face's; otherwise on an edge or a vertex, and it waits for the internal-edge rules.
|
|
2620
|
+
if (n_dot_f * n_dot_f < FACE_CONTACT_COS * FACE_CONTACT_COS * (fnx_w * fnx_w + fny_w * fny_w + fnz_w * fnz_w)) {
|
|
2621
|
+
c[11] = 0;
|
|
2622
|
+
tentative_add(c, i, triangle_witness_feature(scratch_tri_world, capsule_triangle_result, base + 3));
|
|
2623
|
+
continue;
|
|
1190
2624
|
}
|
|
2625
|
+
|
|
2626
|
+
// Dedup against earlier candidates in THIS dispatch:
|
|
2627
|
+
// same normal AND same world-A point.
|
|
2628
|
+
if (dedup_contains(c, 0)) continue;
|
|
2629
|
+
|
|
2630
|
+
count = append_contact_record(count, c, 0);
|
|
2631
|
+
dedup_insert(count - 1);
|
|
1191
2632
|
}
|
|
1192
2633
|
continue;
|
|
1193
2634
|
}
|
|
@@ -1260,58 +2701,105 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
1260
2701
|
const ny = -ey * inv;
|
|
1261
2702
|
const nz = -ez * inv;
|
|
1262
2703
|
|
|
1263
|
-
//
|
|
1264
|
-
//
|
|
1265
|
-
//
|
|
1266
|
-
//
|
|
1267
|
-
//
|
|
1268
|
-
//
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
2704
|
+
// Contact points: the triangle's supporting feature toward the convex body (the triangle
|
|
2705
|
+
// itself, for a body lying on it) and the body's toward the triangle, clipped against each
|
|
2706
|
+
// other — see support_feature_manifold. These used to be the two bodies' ORIGINS: under the
|
|
2707
|
+
// contact only for a body directly above the concave body's origin, and a lever arm
|
|
2708
|
+
// reaching across to it everywhere else. Internally the triangle is A and the convex body
|
|
2709
|
+
// B, so (nx, ny, nz) — convex → triangle — is B → A.
|
|
2710
|
+
triangle_vertices_to_world(scratch_tri_world, triangle_buffer, tri_offset, concave_pose);
|
|
2711
|
+
|
|
2712
|
+
const fn_inv = 1 / Math.sqrt(fnx_w * fnx_w + fny_w * fny_w + fnz_w * fnz_w);
|
|
2713
|
+
|
|
2714
|
+
triangle_feature(feature_a,
|
|
2715
|
+
scratch_tri_world[0], scratch_tri_world[1], scratch_tri_world[2],
|
|
2716
|
+
scratch_tri_world[3], scratch_tri_world[4], scratch_tri_world[5],
|
|
2717
|
+
scratch_tri_world[6], scratch_tri_world[7], scratch_tri_world[8],
|
|
2718
|
+
fnx_w * fn_inv, fny_w * fn_inv, fnz_w * fn_inv,
|
|
2719
|
+
-nx, -ny, -nz
|
|
2720
|
+
);
|
|
2721
|
+
support_feature(feature_b, convex_col.shape,
|
|
2722
|
+
convex_tr.translation_x, convex_tr.translation_y, convex_tr.translation_z,
|
|
2723
|
+
convex_tr.rotation_x, convex_tr.rotation_y, convex_tr.rotation_z, convex_tr.rotation_w,
|
|
2724
|
+
nx, ny, nz
|
|
2725
|
+
);
|
|
2726
|
+
feature_manifold(feature_manifold_result, feature_a, feature_b, nx, ny, nz, depth);
|
|
2727
|
+
|
|
2728
|
+
const mnx = feature_manifold_result[0];
|
|
2729
|
+
const mny = feature_manifold_result[1];
|
|
2730
|
+
const mnz = feature_manifold_result[2];
|
|
2731
|
+
const stored_nx = isConcaveA ? mnx : -mnx;
|
|
2732
|
+
const stored_ny = isConcaveA ? mny : -mny;
|
|
2733
|
+
const stored_nz = isConcaveA ? mnz : -mnz;
|
|
2734
|
+
|
|
2735
|
+
const fc = feature_manifold_result[3];
|
|
2736
|
+
|
|
2737
|
+
// A face contact: the triangle's face facing the body — its polygon clipped, on its own
|
|
2738
|
+
// normal — or a face of the body within 60° of the triangle's plane. Anything else is
|
|
2739
|
+
// tentative, and waits for the internal-edge rules. The triangle's polygon on some other
|
|
2740
|
+
// normal is not its face: feature_manifold falls back to the EPA axis when the clip of the
|
|
2741
|
+
// two features finds nothing, and that axis off an edge of the triangle is a ghost.
|
|
2742
|
+
// (mn is convex → triangle.)
|
|
2743
|
+
let face = feature_a.kind === FEATURE_POLYGON
|
|
2744
|
+
&& -(mnx * fnx_w + mny * fny_w + mnz * fnz_w) * fn_inv >= FACE_CONTACT_COS;
|
|
2745
|
+
|
|
2746
|
+
if (!face && feature_b.kind === FEATURE_POLYGON) {
|
|
2747
|
+
face = -(mnx * fnx_w + mny * fny_w + mnz * fnz_w) * fn_inv > HULL_FACE_COS;
|
|
1287
2748
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
2749
|
+
|
|
2750
|
+
for (let k = 0; k < fc; k++) {
|
|
2751
|
+
const base = 4 + k * 7;
|
|
2752
|
+
// [base..+2] on the triangle, [base+3..+5] on the convex body
|
|
2753
|
+
const t_wx = feature_manifold_result[base];
|
|
2754
|
+
const t_wy = feature_manifold_result[base + 1];
|
|
2755
|
+
const t_wz = feature_manifold_result[base + 2];
|
|
2756
|
+
const c_wx = feature_manifold_result[base + 3];
|
|
2757
|
+
const c_wy = feature_manifold_result[base + 4];
|
|
2758
|
+
const c_wz = feature_manifold_result[base + 5];
|
|
2759
|
+
const d_k = feature_manifold_result[base + 6];
|
|
2760
|
+
|
|
2761
|
+
// Dedup against earlier contacts in THIS dispatch: adjacent triangles report the
|
|
2762
|
+
// same contact where they share an edge or a vertex, and duplicates make the
|
|
2763
|
+
// sequential-impulse solver escalate. Same normal AND same point — a body resting
|
|
2764
|
+
// across several coplanar triangles has distinct contacts sharing one normal.
|
|
2765
|
+
const c = staged_contact;
|
|
2766
|
+
c[0] = isConcaveA ? t_wx : c_wx;
|
|
2767
|
+
c[1] = isConcaveA ? t_wy : c_wy;
|
|
2768
|
+
c[2] = isConcaveA ? t_wz : c_wz;
|
|
2769
|
+
c[3] = isConcaveA ? c_wx : t_wx;
|
|
2770
|
+
c[4] = isConcaveA ? c_wy : t_wy;
|
|
2771
|
+
c[5] = isConcaveA ? c_wz : t_wz;
|
|
2772
|
+
c[6] = stored_nx; c[7] = stored_ny; c[8] = stored_nz;
|
|
2773
|
+
c[9] = d_k;
|
|
2774
|
+
c[10] = tri_fid;
|
|
2775
|
+
|
|
2776
|
+
if (!face) {
|
|
2777
|
+
c[11] = 0;
|
|
2778
|
+
tentative_add(c, i, triangle_witness_feature(scratch_tri_world, feature_manifold_result, base));
|
|
2779
|
+
continue;
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
if (dedup_contains(c, 0)) continue;
|
|
2783
|
+
|
|
2784
|
+
count = append_contact_record(count, c, 0);
|
|
2785
|
+
dedup_insert(count - 1);
|
|
1306
2786
|
}
|
|
1307
2787
|
}
|
|
1308
2788
|
|
|
2789
|
+
// The contacts on edges and vertices: kept where the surface has the edge; where it has none,
|
|
2790
|
+
// dropped (a sphere's) or taken as contacts with the plane (anything else's).
|
|
2791
|
+
if (tentative_count > 0) {
|
|
2792
|
+
count = isSphereConvex
|
|
2793
|
+
? resolve_tentatives_by_claims(count, concave_col.shape, isConcaveA, concave_tr)
|
|
2794
|
+
: resolve_tentatives_by_features(count, concave_col.shape, isConcaveA, concave_tr);
|
|
2795
|
+
}
|
|
2796
|
+
|
|
1309
2797
|
return count;
|
|
1310
2798
|
}
|
|
1311
2799
|
|
|
1312
2800
|
// Robust GJK + EPA fallback for any convex pair without a closed form or a
|
|
1313
|
-
// dedicated branch above (
|
|
1314
|
-
// a
|
|
2801
|
+
// dedicated branch above (a cylinder against anything, a capsule against a
|
|
2802
|
+
// hull, a scaling TransformedShape3D). gjk_epa_penetration returns the depth and writes a UNIT
|
|
1315
2803
|
// axis into epa_result; we scale to the MTV vector (ex,ey,ez) the body-centre
|
|
1316
2804
|
// sign-guard below consumes. Replaces the old gjk_with_axis +
|
|
1317
2805
|
// expanding_polytope_algorithm pair, which returned a non-minimal axis on the
|
|
@@ -1383,31 +2871,38 @@ function dispatch_pair(count, colA, trA, colB, trB) {
|
|
|
1383
2871
|
const inv = 1 / depth;
|
|
1384
2872
|
const nx = -ex * inv, ny = -ey * inv, nz = -ez * inv; // stored normal, B → A
|
|
1385
2873
|
|
|
1386
|
-
// Contact
|
|
1387
|
-
//
|
|
1388
|
-
//
|
|
1389
|
-
//
|
|
1390
|
-
//
|
|
1391
|
-
//
|
|
1392
|
-
|
|
1393
|
-
// collapses `m_eff` to ~0, leaving the impulse vanishingly small.
|
|
1394
|
-
//
|
|
1395
|
-
// Body centres give `r ‖ n` for vertical contacts (sphere/knot landing
|
|
1396
|
-
// on a floor — the common case), so `r × n = 0` and `m_eff = invM`
|
|
1397
|
-
// resolves the impact cleanly. For oblique contacts the application
|
|
1398
|
-
// point lies on the line between centres, which is the right-ish lever
|
|
1399
|
-
// arm for translational impulse and degrades gracefully on angular
|
|
1400
|
-
// response. A future closed-form mesh-vs-box path will compute a
|
|
1401
|
-
// proper face-projected witness; until then, body centres are the
|
|
1402
|
-
// robust fallback for the GJK+EPA route.
|
|
1403
|
-
// GJK+EPA fallback has no stable per-frame feature info; emit fid = 0
|
|
1404
|
-
// so match-and-merge uses position-fallback. Single contact per pair.
|
|
1405
|
-
return append_contact(count,
|
|
2874
|
+
// Contact points from the two supporting features along the axis (see support_feature_manifold):
|
|
2875
|
+
// where the bodies actually touch. Raw support points will not do — a large flat side's support
|
|
2876
|
+
// along its own normal is a tie between its corners, tens of metres from the patch — and the body
|
|
2877
|
+
// origins this used to report are only under the contact directly above the other's origin;
|
|
2878
|
+
// anywhere else the lever arm reached across to it and bodies with rotation enabled spun and fell
|
|
2879
|
+
// through. No stable per-frame feature info: fid = 0, so match-and-merge matches by position.
|
|
2880
|
+
support_feature(feature_a, colA.shape,
|
|
1406
2881
|
trA.translation_x, trA.translation_y, trA.translation_z,
|
|
2882
|
+
trA.rotation_x, trA.rotation_y, trA.rotation_z, trA.rotation_w,
|
|
2883
|
+
-nx, -ny, -nz
|
|
2884
|
+
);
|
|
2885
|
+
support_feature(feature_b, colB.shape,
|
|
1407
2886
|
trB.translation_x, trB.translation_y, trB.translation_z,
|
|
1408
|
-
|
|
1409
|
-
|
|
2887
|
+
trB.rotation_x, trB.rotation_y, trB.rotation_z, trB.rotation_w,
|
|
2888
|
+
nx, ny, nz
|
|
1410
2889
|
);
|
|
2890
|
+
feature_manifold(feature_manifold_result, feature_a, feature_b, nx, ny, nz, depth);
|
|
2891
|
+
|
|
2892
|
+
const mnx = feature_manifold_result[0], mny = feature_manifold_result[1], mnz = feature_manifold_result[2];
|
|
2893
|
+
const cc = feature_manifold_result[3];
|
|
2894
|
+
|
|
2895
|
+
for (let k = 0; k < cc; k++) {
|
|
2896
|
+
const base = 4 + k * 7;
|
|
2897
|
+
count = append_contact(count,
|
|
2898
|
+
feature_manifold_result[base], feature_manifold_result[base + 1], feature_manifold_result[base + 2],
|
|
2899
|
+
feature_manifold_result[base + 3], feature_manifold_result[base + 4], feature_manifold_result[base + 5],
|
|
2900
|
+
mnx, mny, mnz,
|
|
2901
|
+
feature_manifold_result[base + 6], 0
|
|
2902
|
+
);
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
return count;
|
|
1411
2906
|
}
|
|
1412
2907
|
|
|
1413
2908
|
// Reusable single-pair adapters for the penetration query below — no per-call
|
|
@@ -1446,8 +2941,9 @@ const _pp_trB = new Transform64();
|
|
|
1446
2941
|
* step (depenetration, overlap depth, tooling) — never from inside one.
|
|
1447
2942
|
*
|
|
1448
2943
|
* Both poses are read BY INDEX on the way into the stand-in `Transform64`s —
|
|
1449
|
-
* a `Transform64
|
|
1450
|
-
* `Vector3` / `Quaternion` (both
|
|
2944
|
+
* a retained `Float64Array` (not a Transform64's `translation` / `rotation`,
|
|
2945
|
+
* which allocate a view per read), a `Vector3` / `Quaternion` (both
|
|
2946
|
+
* `Float64Array` subclasses) and plain
|
|
1451
2947
|
* `[x, y, z]` / `[x, y, z, w]` arrays all work; an `{x, y, z, w}` object does
|
|
1452
2948
|
* not. Its components read as `undefined`, which trips `setTranslation`'s
|
|
1453
2949
|
* `assert.isNumber` in development and, with `assert.*` stripped from a
|
|
@@ -1509,6 +3005,159 @@ export function deepest_pair_penetration(out_normal, shapeA, posA, rotA, shapeB,
|
|
|
1509
3005
|
return best_depth;
|
|
1510
3006
|
}
|
|
1511
3007
|
|
|
3008
|
+
/**
|
|
3009
|
+
* Drop the sensor candidates among the first `count` when any solid candidate is there too,
|
|
3010
|
+
* compacting the solids to the front in their enumeration order. Returns the surviving count.
|
|
3011
|
+
*
|
|
3012
|
+
* A body pair's collider cross-product feeds ONE candidate list, which the reduction cuts to
|
|
3013
|
+
* {@link MAX_KEPT} by depth and spread, blind to the per-contact flags. A sensor overlaps the whole
|
|
3014
|
+
* volume it covers, so its candidates are the deepest in the list and used to take every slot; the
|
|
3015
|
+
* solver then skipped them for being sensors, and the solid contacts that should have held the body
|
|
3016
|
+
* were never in the manifold. A cube on a thin pedestal wrapped by a trigger fell straight through it,
|
|
3017
|
+
* and a crate carrying a 4 m pick-up zone fell through the floor.
|
|
3018
|
+
*
|
|
3019
|
+
* Sensor candidates are kept when the pair has nothing else, so `contact_count > 0` — the only thing
|
|
3020
|
+
* the contact events key on (diff_manifolds) — is unchanged: the pair is in contact whenever any of
|
|
3021
|
+
* its collider pairs is. Nothing that reaches gameplay says which collider pair a contact came from
|
|
3022
|
+
* (the event is per body pair and its payload carries no flags), so a sensor overlap that coincides
|
|
3023
|
+
* with a solid contact was never separately observable; the payload's representative contact is now
|
|
3024
|
+
* the deepest SOLID one rather than the sensor's whole-volume depth.
|
|
3025
|
+
*
|
|
3026
|
+
* @param {number} count
|
|
3027
|
+
* @returns {number}
|
|
3028
|
+
*/
|
|
3029
|
+
function drop_sensor_candidates_under_solids(count) {
|
|
3030
|
+
let solids = 0;
|
|
3031
|
+
|
|
3032
|
+
for (let i = 0; i < count; i++) {
|
|
3033
|
+
if ((candidates[i * CANDIDATE_STRIDE + 13] & CONTACT_FLAG_SENSOR) === 0) {
|
|
3034
|
+
solids++;
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
|
|
3038
|
+
if (solids === 0 || solids === count) {
|
|
3039
|
+
return count;
|
|
3040
|
+
}
|
|
3041
|
+
|
|
3042
|
+
let kept = 0;
|
|
3043
|
+
|
|
3044
|
+
for (let i = 0; i < count; i++) {
|
|
3045
|
+
const src = i * CANDIDATE_STRIDE;
|
|
3046
|
+
|
|
3047
|
+
if ((candidates[src + 13] & CONTACT_FLAG_SENSOR) !== 0) {
|
|
3048
|
+
continue;
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
if (kept !== i) {
|
|
3052
|
+
candidates.copyWithin(kept * CANDIDATE_STRIDE, src, src + CANDIDATE_STRIDE);
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
kept++;
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
return kept;
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
/**
|
|
3062
|
+
* World boxes of the colliders of the body pair in progress, six floats each: A's colliders, then B's.
|
|
3063
|
+
*
|
|
3064
|
+
* The broadphase pairs BODIES, so a body pair's every collider pair used to be dispatched: a static
|
|
3065
|
+
* compound of 256 hulls under one ball dispatched 256 hull-sphere tests a step for the one child the
|
|
3066
|
+
* ball sat on. A compound pair now tests each collider pair's boxes first, and dispatches only the pairs
|
|
3067
|
+
* whose boxes touch.
|
|
3068
|
+
*
|
|
3069
|
+
* Exactly what the dispatch would have found: no routine makes a contact between separated shapes —
|
|
3070
|
+
* every one needs them touching or overlapping (depth ≥ 0, no speculative margin) — and these are the
|
|
3071
|
+
* boxes the broadphase builds its leaves from (compute_world_aabb, broadphase/compute_fat_world_aabb.js),
|
|
3072
|
+
* at the very poses the dispatch reads. A collider whose box does not hold its surface (a height field
|
|
3073
|
+
* whose sampler was written without bumping its version) is already missed by the broadphase alone; a
|
|
3074
|
+
* compound inherits that, nothing more.
|
|
3075
|
+
*
|
|
3076
|
+
* Grown by doubling, high-water mark kept.
|
|
3077
|
+
* @type {Float64Array}
|
|
3078
|
+
*/
|
|
3079
|
+
let collider_boxes = new Float64Array(16 * 6);
|
|
3080
|
+
|
|
3081
|
+
/** A collider's box in its own frame, for {@link collider_box}. */
|
|
3082
|
+
const collider_local_box = new Float64Array(6);
|
|
3083
|
+
|
|
3084
|
+
/**
|
|
3085
|
+
* Slack on the box test, m: the boxes and the contact routines round differently, and a contact at
|
|
3086
|
+
* depth 0 (touching) is still a contact. Neither needs more than this anywhere a body can be.
|
|
3087
|
+
* @type {number}
|
|
3088
|
+
*/
|
|
3089
|
+
const COLLIDER_BOX_SLACK = 1e-6;
|
|
3090
|
+
|
|
3091
|
+
/**
|
|
3092
|
+
* Fill {@link collider_boxes} for a body pair's two collider lists.
|
|
3093
|
+
*
|
|
3094
|
+
* @param {Array<{collider: Collider, transform: Transform64}>} list_a
|
|
3095
|
+
* @param {Array<{collider: Collider, transform: Transform64}>} list_b
|
|
3096
|
+
*/
|
|
3097
|
+
function compute_collider_boxes(list_a, list_b) {
|
|
3098
|
+
const la_len = list_a.length;
|
|
3099
|
+
const lb_len = list_b.length;
|
|
3100
|
+
|
|
3101
|
+
if ((la_len + lb_len) * 6 > collider_boxes.length) {
|
|
3102
|
+
let length = collider_boxes.length * 2;
|
|
3103
|
+
|
|
3104
|
+
while (length < (la_len + lb_len) * 6) {
|
|
3105
|
+
length *= 2;
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
collider_boxes = new Float64Array(length);
|
|
3109
|
+
}
|
|
3110
|
+
|
|
3111
|
+
for (let a = 0; a < la_len; a++) {
|
|
3112
|
+
collider_box(a, list_a[a]);
|
|
3113
|
+
}
|
|
3114
|
+
|
|
3115
|
+
for (let b = 0; b < lb_len; b++) {
|
|
3116
|
+
collider_box(la_len + b, list_b[b]);
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
|
|
3120
|
+
/**
|
|
3121
|
+
* Box `i` of {@link collider_boxes}: the collider's local box carried to its pose, compute_world_aabb's
|
|
3122
|
+
* arithmetic read from the pose's components — that function takes the pose's translation and rotation
|
|
3123
|
+
* views first, two allocations a call, which here would double the box's cost (76 ns against 35).
|
|
3124
|
+
*
|
|
3125
|
+
* @param {number} i
|
|
3126
|
+
* @param {{collider: Collider, transform: Transform64}} entry
|
|
3127
|
+
*/
|
|
3128
|
+
function collider_box(i, entry) {
|
|
3129
|
+
const t = entry.transform;
|
|
3130
|
+
|
|
3131
|
+
entry.collider.shape.compute_bounding_box(collider_local_box);
|
|
3132
|
+
|
|
3133
|
+
pose_pack(collider_pose, t);
|
|
3134
|
+
aabb3_transform_oriented_array(collider_boxes, i * 6, collider_local_box, 0, collider_pose, 0);
|
|
3135
|
+
}
|
|
3136
|
+
|
|
3137
|
+
/** The pose {@link collider_box} hands {@link aabb3_transform_oriented_array}. */
|
|
3138
|
+
const collider_pose = new Float64Array(7);
|
|
3139
|
+
|
|
3140
|
+
/**
|
|
3141
|
+
* Whether boxes `i` and `j` of {@link collider_boxes} are apart on some axis. A box with NaN in it is
|
|
3142
|
+
* never apart: the dispatch decides what such a pair yields, as it did before the boxes were tested.
|
|
3143
|
+
*
|
|
3144
|
+
* @param {number} i
|
|
3145
|
+
* @param {number} j
|
|
3146
|
+
* @returns {boolean}
|
|
3147
|
+
*/
|
|
3148
|
+
function collider_boxes_apart(i, j) {
|
|
3149
|
+
const b = collider_boxes;
|
|
3150
|
+
const io = i * 6;
|
|
3151
|
+
const jo = j * 6;
|
|
3152
|
+
|
|
3153
|
+
return b[io] > b[jo + 3] + COLLIDER_BOX_SLACK
|
|
3154
|
+
|| b[jo] > b[io + 3] + COLLIDER_BOX_SLACK
|
|
3155
|
+
|| b[io + 1] > b[jo + 4] + COLLIDER_BOX_SLACK
|
|
3156
|
+
|| b[jo + 1] > b[io + 4] + COLLIDER_BOX_SLACK
|
|
3157
|
+
|| b[io + 2] > b[jo + 5] + COLLIDER_BOX_SLACK
|
|
3158
|
+
|| b[jo + 2] > b[io + 5] + COLLIDER_BOX_SLACK;
|
|
3159
|
+
}
|
|
3160
|
+
|
|
1512
3161
|
/**
|
|
1513
3162
|
* For every pair in `pair_list`, do a cross-product over A's collider list ×
|
|
1514
3163
|
* B's collider list, accumulate candidate contacts, reduce to ≤4, and write
|
|
@@ -1550,11 +3199,23 @@ export function narrowphase_step(pair_list, manifolds, lists) {
|
|
|
1550
3199
|
const la_len = list_a.length;
|
|
1551
3200
|
const lb_len = list_b.length;
|
|
1552
3201
|
|
|
3202
|
+
// A compound pair: the collider pairs whose boxes do not touch are not dispatched.
|
|
3203
|
+
const pretest = la_len + lb_len > 2;
|
|
3204
|
+
|
|
3205
|
+
if (pretest) {
|
|
3206
|
+
compute_collider_boxes(list_a, list_b);
|
|
3207
|
+
}
|
|
3208
|
+
|
|
1553
3209
|
let sub_pair = 0;
|
|
1554
3210
|
for (let a = 0; a < la_len; a++) {
|
|
1555
3211
|
const ea = list_a[a];
|
|
1556
3212
|
|
|
1557
|
-
for (let b = 0; b < lb_len; b++) {
|
|
3213
|
+
for (let b = 0; b < lb_len; b++, sub_pair++) {
|
|
3214
|
+
// Skipped pairs keep their number: it salts the fids below.
|
|
3215
|
+
if (pretest && collider_boxes_apart(a, la_len + b)) {
|
|
3216
|
+
continue;
|
|
3217
|
+
}
|
|
3218
|
+
|
|
1558
3219
|
const eb = list_b[b];
|
|
1559
3220
|
|
|
1560
3221
|
const before = cand_count;
|
|
@@ -1576,7 +3237,6 @@ export function narrowphase_step(pair_list, manifolds, lists) {
|
|
|
1576
3237
|
if (candidates[co + 10] !== 0) candidates[co + 10] += salt;
|
|
1577
3238
|
}
|
|
1578
3239
|
}
|
|
1579
|
-
sub_pair++;
|
|
1580
3240
|
}
|
|
1581
3241
|
}
|
|
1582
3242
|
|
|
@@ -1591,7 +3251,11 @@ export function narrowphase_step(pair_list, manifolds, lists) {
|
|
|
1591
3251
|
continue;
|
|
1592
3252
|
}
|
|
1593
3253
|
|
|
1594
|
-
|
|
3254
|
+
// Solids are reduced on their own; sensors only fill a manifold that has nothing solid.
|
|
3255
|
+
cand_count = drop_sensor_candidates_under_solids(cand_count);
|
|
3256
|
+
|
|
3257
|
+
// Shared out between the planes the contacts lie on: see reduce_manifold_contacts_by_plane.
|
|
3258
|
+
const kept = reduce_manifold_contacts_by_plane(candidates, cand_count, CANDIDATE_STRIDE, MAX_KEPT, 9, 6);
|
|
1595
3259
|
|
|
1596
3260
|
// ── Match-and-merge: feature-id (with position fallback) ──────
|
|
1597
3261
|
//
|
|
@@ -1679,16 +3343,7 @@ export function narrowphase_step(pair_list, manifolds, lists) {
|
|
|
1679
3343
|
// copies the matched prev contact's impulses into THIS k,
|
|
1680
3344
|
// so the geometric and warm-start state stay correlated
|
|
1681
3345
|
// even if the matching mapping permuted the order.
|
|
1682
|
-
manifolds.
|
|
1683
|
-
slot, k,
|
|
1684
|
-
candidates[off], candidates[off + 1], candidates[off + 2],
|
|
1685
|
-
candidates[off + 3], candidates[off + 4], candidates[off + 5],
|
|
1686
|
-
candidates[off + 6], candidates[off + 7], candidates[off + 8],
|
|
1687
|
-
candidates[off + 9],
|
|
1688
|
-
candidates[off + 10],
|
|
1689
|
-
candidates[off + 11], candidates[off + 12],
|
|
1690
|
-
candidates[off + 13]
|
|
1691
|
-
);
|
|
3346
|
+
manifolds.set_contact_from(slot, k, candidates, off);
|
|
1692
3347
|
const prev_j = cand_to_prev[k];
|
|
1693
3348
|
if (prev_j !== -1) {
|
|
1694
3349
|
// Copy prev_j's impulse to this slot index.
|
|
@@ -1723,9 +3378,9 @@ export function narrowphase_step(pair_list, manifolds, lists) {
|
|
|
1723
3378
|
*
|
|
1724
3379
|
* Matching is by feature id (stable per-triangle for the decomposition path),
|
|
1725
3380
|
* so a contact's geometry tracks the same triangle across substeps. A contact
|
|
1726
|
-
* whose triangle isn't found this substep keeps its previous geometry
|
|
1727
|
-
* transient; the once-per-frame
|
|
1728
|
-
* contact set next outer step). Count never changes here, so the solver's
|
|
3381
|
+
* whose triangle isn't found this substep keeps its previous geometry, moved by
|
|
3382
|
+
* how far the found contacts moved (a transient; the once-per-frame
|
|
3383
|
+
* {@link narrowphase_step} re-establishes the contact set next outer step). Count never changes here, so the solver's
|
|
1729
3384
|
* per-contact scratch (sized once at prepare) stays aligned.
|
|
1730
3385
|
*
|
|
1731
3386
|
* @param {ManifoldStore} manifolds
|
|
@@ -1742,11 +3397,22 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
|
|
|
1742
3397
|
const count = manifolds.contact_count(slot);
|
|
1743
3398
|
if (count === 0) return;
|
|
1744
3399
|
|
|
3400
|
+
// As in narrowphase_step: a compound pair dispatches only the collider pairs whose boxes touch.
|
|
3401
|
+
const pretest = la_len + lb_len > 2;
|
|
3402
|
+
|
|
3403
|
+
if (pretest) {
|
|
3404
|
+
compute_collider_boxes(list_a, list_b);
|
|
3405
|
+
}
|
|
3406
|
+
|
|
1745
3407
|
let cc = 0;
|
|
1746
3408
|
let sub_pair = 0;
|
|
1747
3409
|
for (let a = 0; a < la_len; a++) {
|
|
1748
3410
|
const ea = list_a[a];
|
|
1749
|
-
for (let b = 0; b < lb_len; b++) {
|
|
3411
|
+
for (let b = 0; b < lb_len; b++, sub_pair++) {
|
|
3412
|
+
if (pretest && collider_boxes_apart(a, la_len + b)) {
|
|
3413
|
+
continue;
|
|
3414
|
+
}
|
|
3415
|
+
|
|
1750
3416
|
const eb = list_b[b];
|
|
1751
3417
|
const before = cc;
|
|
1752
3418
|
cc = dispatch_pair(cc, ea.collider, ea.transform, eb.collider, eb.transform);
|
|
@@ -1760,7 +3426,6 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
|
|
|
1760
3426
|
if (candidates[co + 10] !== 0) candidates[co + 10] += salt;
|
|
1761
3427
|
}
|
|
1762
3428
|
}
|
|
1763
|
-
sub_pair++;
|
|
1764
3429
|
}
|
|
1765
3430
|
}
|
|
1766
3431
|
if (cc === 0) return; // nothing re-detected this substep — keep frozen geometry
|
|
@@ -1782,6 +3447,10 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
|
|
|
1782
3447
|
// single candidate exactly once — identical to the old behaviour.
|
|
1783
3448
|
for (let k = 0; k < cc; k++) redetect_claimed[k] = 0;
|
|
1784
3449
|
|
|
3450
|
+
// how far the matched contacts' points moved, summed, and the contacts left without a candidate
|
|
3451
|
+
let moved_x = 0, moved_y = 0, moved_z = 0, moved = 0;
|
|
3452
|
+
let stale = 0;
|
|
3453
|
+
|
|
1785
3454
|
for (let j = 0; j < count; j++) {
|
|
1786
3455
|
const off = slot_off + j * CONTACT_STRIDE;
|
|
1787
3456
|
const fid = data[off + 13];
|
|
@@ -1793,11 +3462,17 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
|
|
|
1793
3462
|
const pay = data[off + 1];
|
|
1794
3463
|
const paz = data[off + 2];
|
|
1795
3464
|
|
|
3465
|
+
// A contact only takes geometry from a candidate of its own kind. The fresh candidates cover
|
|
3466
|
+
// every collider pair, sensors included; a solid contact refreshed from a sensor's
|
|
3467
|
+
// whole-volume overlap would keep its solid flag and push with the sensor's depth and normal.
|
|
3468
|
+
const sensor = data[off + CONTACT_FLAGS_OFFSET] & CONTACT_FLAG_SENSOR;
|
|
3469
|
+
|
|
1796
3470
|
let best_k = -1;
|
|
1797
3471
|
let best_d2 = Infinity;
|
|
1798
3472
|
for (let k = 0; k < cc; k++) {
|
|
1799
3473
|
if (redetect_claimed[k] === 1) continue;
|
|
1800
3474
|
const co = k * CANDIDATE_STRIDE;
|
|
3475
|
+
if ((candidates[co + 13] & CONTACT_FLAG_SENSOR) !== sensor) continue;
|
|
1801
3476
|
if (candidates[co + 10] !== fid) continue;
|
|
1802
3477
|
const dx = candidates[co] - pax;
|
|
1803
3478
|
const dy = candidates[co + 1] - pay;
|
|
@@ -1825,6 +3500,7 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
|
|
|
1825
3500
|
for (let k = 0; k < cc; k++) {
|
|
1826
3501
|
if (redetect_claimed[k] === 1) continue;
|
|
1827
3502
|
const co = k * CANDIDATE_STRIDE;
|
|
3503
|
+
if ((candidates[co + 13] & CONTACT_FLAG_SENSOR) !== sensor) continue;
|
|
1828
3504
|
const dx = candidates[co] - pax;
|
|
1829
3505
|
const dy = candidates[co + 1] - pay;
|
|
1830
3506
|
const dz = candidates[co + 2] - paz;
|
|
@@ -1836,10 +3512,20 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
|
|
|
1836
3512
|
}
|
|
1837
3513
|
}
|
|
1838
3514
|
|
|
1839
|
-
if (best_k === -1)
|
|
3515
|
+
if (best_k === -1) {
|
|
3516
|
+
// no unclaimed candidate at all this substep: its geometry is kept, moved with the others
|
|
3517
|
+
// (below)
|
|
3518
|
+
if (stale < redetect_stale.length) redetect_stale[stale++] = off;
|
|
3519
|
+
continue;
|
|
3520
|
+
}
|
|
1840
3521
|
|
|
1841
3522
|
redetect_claimed[best_k] = 1;
|
|
1842
3523
|
const co = best_k * CANDIDATE_STRIDE;
|
|
3524
|
+
|
|
3525
|
+
moved_x += (candidates[co] + candidates[co + 3] - data[off] - data[off + 3]) * 0.5;
|
|
3526
|
+
moved_y += (candidates[co + 1] + candidates[co + 4] - data[off + 1] - data[off + 4]) * 0.5;
|
|
3527
|
+
moved_z += (candidates[co + 2] + candidates[co + 5] - data[off + 2] - data[off + 5]) * 0.5;
|
|
3528
|
+
moved++;
|
|
1843
3529
|
// Overwrite geometry only: witnesses, normal, depth. (Count, feature
|
|
1844
3530
|
// ids and accumulated impulses are intentionally left untouched — see
|
|
1845
3531
|
// the function contract.)
|
|
@@ -1854,4 +3540,20 @@ export function redetect_pair_geometry(manifolds, slot, list_a, list_b) {
|
|
|
1854
3540
|
data[off + 8] = candidates[co + 8];
|
|
1855
3541
|
data[off + 9] = candidates[co + 9];
|
|
1856
3542
|
}
|
|
3543
|
+
|
|
3544
|
+
// A contact left without a candidate keeps its geometry from before this substep, and its points
|
|
3545
|
+
// stay where they were while the body moves on: at 6 m/s, 25 mm behind it each substep. A capsule
|
|
3546
|
+
// crossing a T-junction's rows lost one end's contact this way for a substep, and the frozen point,
|
|
3547
|
+
// up to 100 mm behind its axis, spun it at 0.13 rad/s. Moved with the contacts that were found —
|
|
3548
|
+
// by their mean displacement, the body's motion for one substep — it stays under the body.
|
|
3549
|
+
if (stale > 0 && moved > 0) {
|
|
3550
|
+
const dx = moved_x / moved, dy = moved_y / moved, dz = moved_z / moved;
|
|
3551
|
+
|
|
3552
|
+
for (let i = 0; i < stale; i++) {
|
|
3553
|
+
const off = redetect_stale[i];
|
|
3554
|
+
|
|
3555
|
+
data[off] += dx; data[off + 1] += dy; data[off + 2] += dz;
|
|
3556
|
+
data[off + 3] += dx; data[off + 4] += dy; data[off + 5] += dz;
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
1857
3559
|
}
|