//! Physics pipeline structures. use crate::data::{ComponentSet, ComponentSetMut, ComponentSetOption}; use crate::dynamics::{ RigidBodyActivation, RigidBodyChanges, RigidBodyColliders, RigidBodyDominance, RigidBodyHandle, RigidBodyIds, RigidBodyPosition, RigidBodyType, RigidBodyVelocity, }; use crate::geometry::{ BroadPhase, BroadPhasePairEvent, ColliderBroadPhaseData, ColliderChanges, ColliderFlags, ColliderHandle, ColliderMaterial, ColliderPair, ColliderParent, ColliderPosition, ColliderShape, ColliderType, NarrowPhase, }; use crate::math::Real; use crate::pipeline::{EventHandler, PhysicsHooks}; #[cfg(feature = "default-sets")] use crate::{dynamics::RigidBodySet, geometry::ColliderSet}; /// The collision pipeline, responsible for performing collision detection between colliders. /// /// This structure only contains temporary data buffers. It can be dropped and replaced by a fresh /// copy at any time. For performance reasons it is recommended to reuse the same physics pipeline /// instance to benefit from the cached data. // NOTE: this contains only workspace data, so there is no point in making this serializable. pub struct CollisionPipeline { broadphase_collider_pairs: Vec, broad_phase_events: Vec, } #[allow(dead_code)] fn check_pipeline_send_sync() { fn do_test() {} do_test::(); } impl Default for CollisionPipeline { fn default() -> Self { Self::new() } } impl CollisionPipeline { /// Initializes a new physics pipeline. pub fn new() -> CollisionPipeline { CollisionPipeline { broadphase_collider_pairs: Vec::new(), broad_phase_events: Vec::new(), } } fn detect_collisions( &mut self, prediction_distance: Real, broad_phase: &mut BroadPhase, narrow_phase: &mut NarrowPhase, bodies: &mut Bodies, colliders: &mut Colliders, modified_colliders: &[ColliderHandle], removed_colliders: &[ColliderHandle], hooks: &dyn PhysicsHooks, events: &dyn EventHandler, handle_user_changes: bool, ) where Bodies: ComponentSetMut + ComponentSet + ComponentSetMut + ComponentSet, Colliders: ComponentSetMut + ComponentSet + ComponentSet + ComponentSet + ComponentSetOption + ComponentSet + ComponentSet + ComponentSet, { // Update broad-phase. self.broad_phase_events.clear(); self.broadphase_collider_pairs.clear(); broad_phase.update( prediction_distance, colliders, modified_colliders, removed_colliders, &mut self.broad_phase_events, ); // Update narrow-phase. if handle_user_changes { narrow_phase.handle_user_changes( None, modified_colliders, removed_colliders, colliders, bodies, events, ); } narrow_phase.register_pairs(None, colliders, bodies, &self.broad_phase_events, events); narrow_phase.compute_contacts( prediction_distance, bodies, colliders, modified_colliders, hooks, events, ); narrow_phase.compute_intersections(bodies, colliders, modified_colliders, hooks, events); } fn clear_modified_colliders( &mut self, colliders: &mut impl ComponentSetMut, modified_colliders: &mut Vec, ) { for handle in modified_colliders.drain(..) { colliders.set_internal(handle.0, ColliderChanges::empty()) } } /// Executes one step of the collision detection. #[cfg(feature = "default-sets")] pub fn step( &mut self, prediction_distance: Real, broad_phase: &mut BroadPhase, narrow_phase: &mut NarrowPhase, bodies: &mut RigidBodySet, colliders: &mut ColliderSet, hooks: &dyn PhysicsHooks, events: &dyn EventHandler, ) { let mut modified_bodies = bodies.take_modified(); let mut modified_colliders = colliders.take_modified(); let mut removed_colliders = colliders.take_removed(); self.step_generic( prediction_distance, broad_phase, narrow_phase, bodies, colliders, &mut modified_bodies, &mut modified_colliders, &mut removed_colliders, hooks, events, ); } /// Executes one step of the collision detection. pub fn step_generic( &mut self, prediction_distance: Real, broad_phase: &mut BroadPhase, narrow_phase: &mut NarrowPhase, bodies: &mut Bodies, colliders: &mut Colliders, modified_bodies: &mut Vec, modified_colliders: &mut Vec, removed_colliders: &mut Vec, hooks: &dyn PhysicsHooks, events: &dyn EventHandler, ) where Bodies: ComponentSetMut + ComponentSetMut + ComponentSetMut + ComponentSetMut + ComponentSetMut + ComponentSet + ComponentSet + ComponentSet, Colliders: ComponentSetMut + ComponentSetMut + ComponentSetMut + ComponentSet + ComponentSetOption + ComponentSet + ComponentSet + ComponentSet, { super::user_changes::handle_user_changes_to_colliders( bodies, colliders, &modified_colliders[..], ); super::user_changes::handle_user_changes_to_rigid_bodies( None, bodies, colliders, &modified_bodies, modified_colliders, ); self.detect_collisions( prediction_distance, broad_phase, narrow_phase, bodies, colliders, &modified_colliders[..], removed_colliders, hooks, events, true, ); self.clear_modified_colliders(colliders, modified_colliders); removed_colliders.clear(); } } #[cfg(test)] mod tests { #[test] #[cfg(feature = "dim3")] pub fn test_no_rigid_bodies() { use crate::prelude::*; let mut rigid_body_set = RigidBodySet::new(); let mut collider_set = ColliderSet::new(); /* Create the ground. */ let collider_a = ColliderBuilder::cuboid(1.0, 1.0, 1.0) .active_collision_types(ActiveCollisionTypes::all()) .sensor(true) .active_events(ActiveEvents::CONTACT_EVENTS | ActiveEvents::INTERSECTION_EVENTS) .build(); let a_handle = collider_set.insert(collider_a); let collider_b = ColliderBuilder::cuboid(1.0, 1.0, 1.0) .active_collision_types(ActiveCollisionTypes::all()) .sensor(true) .active_events(ActiveEvents::CONTACT_EVENTS | ActiveEvents::INTERSECTION_EVENTS) .build(); let _ = collider_set.insert(collider_b); let integration_parameters = IntegrationParameters::default(); let mut broad_phase = BroadPhase::new(); let mut narrow_phase = NarrowPhase::new(); let mut collision_pipeline = CollisionPipeline::new(); let physics_hooks = (); collision_pipeline.step( integration_parameters.prediction_distance, &mut broad_phase, &mut narrow_phase, &mut rigid_body_set, &mut collider_set, &physics_hooks, &(), ); let mut hit = false; for (_, _, intersecting) in narrow_phase.intersections_with(a_handle) { if intersecting { hit = true; } } assert!(hit, "No hit found"); } #[test] #[cfg(feature = "dim2")] pub fn test_no_rigid_bodies() { use crate::prelude::*; let mut rigid_body_set = RigidBodySet::new(); let mut collider_set = ColliderSet::new(); /* Create the ground. */ let collider_a = ColliderBuilder::cuboid(1.0, 1.0) .active_collision_types(ActiveCollisionTypes::all()) .sensor(true) .active_events(ActiveEvents::CONTACT_EVENTS | ActiveEvents::INTERSECTION_EVENTS) .build(); let a_handle = collider_set.insert(collider_a); let collider_b = ColliderBuilder::cuboid(1.0, 1.0) .active_collision_types(ActiveCollisionTypes::all()) .sensor(true) .active_events(ActiveEvents::CONTACT_EVENTS | ActiveEvents::INTERSECTION_EVENTS) .build(); let _ = collider_set.insert(collider_b); let integration_parameters = IntegrationParameters::default(); let mut broad_phase = BroadPhase::new(); let mut narrow_phase = NarrowPhase::new(); let mut collision_pipeline = CollisionPipeline::new(); let physics_hooks = (); collision_pipeline.step( integration_parameters.prediction_distance, &mut broad_phase, &mut narrow_phase, &mut rigid_body_set, &mut collider_set, &physics_hooks, &(), ); let mut hit = false; for (_, _, intersecting) in narrow_phase.intersections_with(a_handle) { if intersecting { hit = true; } } assert!(hit, "No hit found"); } }