rapier3d/pipeline/
collision_pipeline.rs

1//! Physics pipeline structures.
2
3use crate::dynamics::{ImpulseJointSet, IntegrationParameters, MultibodyJointSet};
4use crate::geometry::{
5    BroadPhase, BroadPhasePairEvent, ColliderChanges, ColliderHandle, ColliderPair,
6    ModifiedColliders, NarrowPhase,
7};
8use crate::math::Real;
9use crate::pipeline::{EventHandler, PhysicsHooks};
10use crate::{dynamics::RigidBodySet, geometry::ColliderSet};
11
12/// The collision pipeline, responsible for performing collision detection between colliders.
13///
14/// This structure only contains temporary data buffers. It can be dropped and replaced by a fresh
15/// copy at any time. For performance reasons it is recommended to reuse the same physics pipeline
16/// instance to benefit from the cached data.
17// NOTE: this contains only workspace data, so there is no point in making this serializable.
18pub struct CollisionPipeline {
19    broadphase_collider_pairs: Vec<ColliderPair>,
20    broad_phase_events: Vec<BroadPhasePairEvent>,
21}
22
23#[allow(dead_code)]
24fn check_pipeline_send_sync() {
25    fn do_test<T: Sync>() {}
26    do_test::<CollisionPipeline>();
27}
28
29impl Default for CollisionPipeline {
30    fn default() -> Self {
31        Self::new()
32    }
33}
34
35impl CollisionPipeline {
36    /// Initializes a new physics pipeline.
37    pub fn new() -> CollisionPipeline {
38        CollisionPipeline {
39            broadphase_collider_pairs: Vec::new(),
40            broad_phase_events: Vec::new(),
41        }
42    }
43
44    fn detect_collisions(
45        &mut self,
46        prediction_distance: Real,
47        broad_phase: &mut dyn BroadPhase,
48        narrow_phase: &mut NarrowPhase,
49        bodies: &mut RigidBodySet,
50        colliders: &mut ColliderSet,
51        modified_colliders: &[ColliderHandle],
52        removed_colliders: &[ColliderHandle],
53        hooks: &dyn PhysicsHooks,
54        events: &dyn EventHandler,
55        handle_user_changes: bool,
56    ) {
57        // Update broad-phase.
58        self.broad_phase_events.clear();
59        self.broadphase_collider_pairs.clear();
60
61        let params = IntegrationParameters {
62            normalized_prediction_distance: prediction_distance,
63            dt: 0.0,
64            ..Default::default()
65        };
66
67        broad_phase.update(
68            &params,
69            colliders,
70            bodies,
71            modified_colliders,
72            removed_colliders,
73            &mut self.broad_phase_events,
74        );
75
76        // Update narrow-phase.
77        if handle_user_changes {
78            narrow_phase.handle_user_changes(
79                None,
80                modified_colliders,
81                removed_colliders,
82                colliders,
83                bodies,
84                events,
85            );
86        }
87
88        narrow_phase.register_pairs(None, colliders, bodies, &self.broad_phase_events, events);
89        narrow_phase.compute_contacts(
90            prediction_distance,
91            0.0,
92            bodies,
93            colliders,
94            &ImpulseJointSet::new(),
95            &MultibodyJointSet::new(),
96            modified_colliders,
97            hooks,
98            events,
99        );
100        narrow_phase.compute_intersections(bodies, colliders, modified_colliders, hooks, events);
101    }
102
103    fn clear_modified_colliders(
104        &mut self,
105        colliders: &mut ColliderSet,
106        modified_colliders: &mut ModifiedColliders,
107    ) {
108        for handle in modified_colliders.iter() {
109            if let Some(co) = colliders.get_mut_internal(*handle) {
110                co.changes = ColliderChanges::empty();
111            }
112        }
113
114        modified_colliders.clear();
115    }
116
117    /// Executes one step of the collision detection.
118    pub fn step(
119        &mut self,
120        prediction_distance: Real,
121        broad_phase: &mut dyn BroadPhase,
122        narrow_phase: &mut NarrowPhase,
123        bodies: &mut RigidBodySet,
124        colliders: &mut ColliderSet,
125        hooks: &dyn PhysicsHooks,
126        events: &dyn EventHandler,
127    ) {
128        let modified_bodies = bodies.take_modified();
129        let mut modified_colliders = colliders.take_modified();
130        let mut removed_colliders = colliders.take_removed();
131
132        super::user_changes::handle_user_changes_to_colliders(
133            bodies,
134            colliders,
135            &modified_colliders[..],
136        );
137        super::user_changes::handle_user_changes_to_rigid_bodies(
138            None,
139            bodies,
140            colliders,
141            &mut ImpulseJointSet::new(),
142            &mut MultibodyJointSet::new(),
143            &modified_bodies,
144            &mut modified_colliders,
145        );
146
147        // Disabled colliders are treated as if they were removed.
148        removed_colliders.extend(
149            modified_colliders
150                .iter()
151                .copied()
152                .filter(|h| colliders.get(*h).map(|c| !c.is_enabled()).unwrap_or(false)),
153        );
154
155        self.detect_collisions(
156            prediction_distance,
157            broad_phase,
158            narrow_phase,
159            bodies,
160            colliders,
161            &modified_colliders[..],
162            &removed_colliders,
163            hooks,
164            events,
165            true,
166        );
167
168        self.clear_modified_colliders(colliders, &mut modified_colliders);
169        removed_colliders.clear();
170    }
171}
172
173#[cfg(test)]
174mod tests {
175
176    #[test]
177    #[cfg(feature = "dim3")]
178    pub fn test_no_rigid_bodies() {
179        use crate::prelude::*;
180        let mut rigid_body_set = RigidBodySet::new();
181        let mut collider_set = ColliderSet::new();
182
183        /* Create the ground. */
184        let collider_a = ColliderBuilder::cuboid(1.0, 1.0, 1.0)
185            .active_collision_types(ActiveCollisionTypes::all())
186            .sensor(true)
187            .active_events(ActiveEvents::COLLISION_EVENTS)
188            .build();
189
190        let a_handle = collider_set.insert(collider_a);
191
192        let collider_b = ColliderBuilder::cuboid(1.0, 1.0, 1.0)
193            .active_collision_types(ActiveCollisionTypes::all())
194            .sensor(true)
195            .active_events(ActiveEvents::COLLISION_EVENTS)
196            .build();
197
198        let _ = collider_set.insert(collider_b);
199
200        let integration_parameters = IntegrationParameters::default();
201        let mut broad_phase = BroadPhaseBvh::new();
202        let mut narrow_phase = NarrowPhase::new();
203        let mut collision_pipeline = CollisionPipeline::new();
204        let physics_hooks = ();
205
206        collision_pipeline.step(
207            integration_parameters.prediction_distance(),
208            &mut broad_phase,
209            &mut narrow_phase,
210            &mut rigid_body_set,
211            &mut collider_set,
212            &physics_hooks,
213            &(),
214        );
215
216        let mut hit = false;
217
218        for (_, _, intersecting) in narrow_phase.intersection_pairs_with(a_handle) {
219            if intersecting {
220                hit = true;
221            }
222        }
223
224        assert!(hit, "No hit found");
225    }
226
227    #[test]
228    #[cfg(feature = "dim2")]
229    pub fn test_no_rigid_bodies() {
230        use crate::prelude::*;
231        let mut rigid_body_set = RigidBodySet::new();
232        let mut collider_set = ColliderSet::new();
233
234        /* Create the ground. */
235        let collider_a = ColliderBuilder::cuboid(1.0, 1.0)
236            .active_collision_types(ActiveCollisionTypes::all())
237            .sensor(true)
238            .active_events(ActiveEvents::COLLISION_EVENTS)
239            .build();
240
241        let a_handle = collider_set.insert(collider_a);
242
243        let collider_b = ColliderBuilder::cuboid(1.0, 1.0)
244            .active_collision_types(ActiveCollisionTypes::all())
245            .sensor(true)
246            .active_events(ActiveEvents::COLLISION_EVENTS)
247            .build();
248
249        let _ = collider_set.insert(collider_b);
250
251        let integration_parameters = IntegrationParameters::default();
252        let mut broad_phase = BroadPhaseBvh::new();
253        let mut narrow_phase = NarrowPhase::new();
254        let mut collision_pipeline = CollisionPipeline::new();
255        let physics_hooks = ();
256
257        collision_pipeline.step(
258            integration_parameters.prediction_distance(),
259            &mut broad_phase,
260            &mut narrow_phase,
261            &mut rigid_body_set,
262            &mut collider_set,
263            &physics_hooks,
264            &(),
265        );
266
267        let mut hit = false;
268
269        for (_, _, intersecting) in narrow_phase.intersection_pairs_with(a_handle) {
270            if intersecting {
271                hit = true;
272            }
273        }
274
275        assert!(hit, "No hit found");
276    }
277}