Skip to main content

rapier3d/pipeline/
collision_pipeline.rs

1//! Physics pipeline structures.
2
3use crate::alloc_prelude::*;
4
5use crate::dynamics::{
6    ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet, RigidBodyChanges,
7};
8use crate::geometry::{
9    BroadPhaseBvh, BroadPhasePairEvent, ColliderChanges, ColliderHandle, ModifiedColliders,
10    NarrowPhase,
11};
12use crate::math::Real;
13use crate::pipeline::{EventHandler, PhysicsHooks};
14use crate::prelude::ModifiedRigidBodies;
15use crate::{dynamics::RigidBodySet, geometry::ColliderSet};
16
17/// A collision detection pipeline that can be used without full physics simulation.
18///
19/// This runs only collision detection (broad-phase + narrow-phase) without dynamics/forces.
20/// Use when you want to detect collisions but don't need physics simulation.
21///
22/// **For full physics**, use [`PhysicsPipeline`](crate::pipeline::PhysicsPipeline) instead which includes this internally.
23///
24/// ## Use cases
25///
26/// - Collision detection in a non-physics game
27/// - Custom physics integration where you handle forces yourself
28/// - Debugging collision detection separately from dynamics
29///
30/// Like PhysicsPipeline, this only holds temporary buffers. Reuse the same instance for performance.
31///
32/// Bodies are never integrated: their contacts are updated on the steps you move them (or modify
33/// their colliders). There is no sleeping either, so the [`IslandManager`] passed to
34/// [`Self::step`] stays empty (bodies are never registered in it).
35// NOTE: this contains only workspace data, so there is no point in making this serializable.
36pub struct CollisionPipeline {
37    broad_phase_events: Vec<BroadPhasePairEvent>,
38}
39
40#[allow(dead_code)]
41fn check_pipeline_send_sync() {
42    fn do_test<T: Sync>() {}
43    do_test::<CollisionPipeline>();
44}
45
46impl Default for CollisionPipeline {
47    fn default() -> Self {
48        Self::new()
49    }
50}
51
52impl CollisionPipeline {
53    /// Initializes a new physics pipeline.
54    pub fn new() -> CollisionPipeline {
55        CollisionPipeline {
56            broad_phase_events: Vec::new(),
57        }
58    }
59
60    fn detect_collisions(
61        &mut self,
62        prediction_distance: Real,
63        islands: &mut IslandManager,
64        broad_phase: &mut BroadPhaseBvh,
65        narrow_phase: &mut NarrowPhase,
66        bodies: &mut RigidBodySet,
67        colliders: &mut ColliderSet,
68        modified_colliders: &[ColliderHandle],
69        removed_colliders: &[ColliderHandle],
70        hooks: &dyn PhysicsHooks,
71        events: &dyn EventHandler,
72        handle_user_changes: bool,
73    ) {
74        // Update broad-phase.
75        self.broad_phase_events.clear();
76
77        let params = IntegrationParameters {
78            normalized_prediction_distance: prediction_distance,
79            dt: 0.0,
80            ..Default::default()
81        };
82
83        broad_phase.update(
84            &params,
85            colliders,
86            bodies,
87            modified_colliders,
88            removed_colliders,
89            &mut self.broad_phase_events,
90        );
91
92        // Update narrow-phase.
93        if handle_user_changes {
94            narrow_phase.handle_user_changes(
95                None,
96                modified_colliders,
97                removed_colliders,
98                colliders,
99                bodies,
100                events,
101            );
102        }
103
104        narrow_phase.register_pairs(None, colliders, bodies, &self.broad_phase_events, events);
105        narrow_phase.compute_contacts(
106            prediction_distance,
107            0.0,
108            false,
109            0.0,
110            islands,
111            bodies,
112            colliders,
113            &ImpulseJointSet::new(),
114            &MultibodyJointSet::new(),
115            modified_colliders,
116            hooks,
117            events,
118            None,
119        );
120        narrow_phase.compute_intersections(
121            islands,
122            bodies,
123            colliders,
124            modified_colliders,
125            hooks,
126            events,
127        );
128    }
129
130    fn clear_modified_colliders(
131        &mut self,
132        colliders: &mut ColliderSet,
133        modified_colliders: &mut ModifiedColliders,
134    ) {
135        for handle in modified_colliders.iter() {
136            if let Some(co) = colliders.get_mut_internal(*handle) {
137                co.changes = ColliderChanges::empty();
138            }
139        }
140
141        modified_colliders.clear();
142    }
143
144    fn clear_modified_bodies(
145        &mut self,
146        bodies: &mut RigidBodySet,
147        modified_bodies: &mut ModifiedRigidBodies,
148    ) {
149        // Without this, a body modified by the user keeps its MODIFIED flag forever, so
150        // `RigidBodySet::get_mut` never re-inserts it into the modified set and later user
151        // changes stop propagating to its colliders (same as `PhysicsPipeline`).
152        for handle in modified_bodies.iter() {
153            if let Some(rb) = bodies.get_mut_internal(*handle) {
154                rb.changes = RigidBodyChanges::empty();
155            }
156        }
157
158        modified_bodies.clear();
159    }
160
161    /// Executes one step of the collision detection.
162    pub fn step(
163        &mut self,
164        prediction_distance: Real,
165        islands: &mut IslandManager,
166        broad_phase: &mut BroadPhaseBvh,
167        narrow_phase: &mut NarrowPhase,
168        bodies: &mut RigidBodySet,
169        colliders: &mut ColliderSet,
170        hooks: &dyn PhysicsHooks,
171        events: &dyn EventHandler,
172    ) {
173        let mut modified_bodies = bodies.take_modified();
174        let mut modified_colliders = colliders.take_modified();
175        let mut removed_colliders = colliders.take_removed();
176
177        super::user_changes::handle_user_changes_to_colliders(
178            bodies,
179            colliders,
180            &modified_colliders[..],
181        );
182        super::user_changes::handle_user_changes_to_rigid_bodies(
183            None,
184            bodies,
185            colliders,
186            &mut ImpulseJointSet::new(),
187            &mut MultibodyJointSet::new(),
188            &modified_bodies,
189            &mut modified_colliders,
190        );
191
192        // Disabled colliders are treated as if they were removed.
193        removed_colliders.extend(
194            modified_colliders
195                .iter()
196                .copied()
197                .filter(|h| colliders.get(*h).map(|c| !c.is_enabled()).unwrap_or(false)),
198        );
199
200        self.detect_collisions(
201            prediction_distance,
202            islands,
203            broad_phase,
204            narrow_phase,
205            bodies,
206            colliders,
207            &modified_colliders[..],
208            &removed_colliders,
209            hooks,
210            events,
211            true,
212        );
213
214        self.clear_modified_colliders(colliders, &mut modified_colliders);
215        self.clear_modified_bodies(bodies, &mut modified_bodies);
216        removed_colliders.clear();
217    }
218}
219
220#[cfg(test)]
221mod tests {
222
223    #[test]
224    #[cfg(feature = "dim3")]
225    pub fn test_no_rigid_bodies() {
226        use crate::prelude::*;
227        let mut rigid_body_set = RigidBodySet::new();
228        let mut collider_set = ColliderSet::new();
229
230        /* Create the ground. */
231        let collider_a = ColliderBuilder::cuboid(1.0, 1.0, 1.0)
232            .active_collision_types(ActiveCollisionTypes::all())
233            .sensor(true)
234            .active_events(ActiveEvents::COLLISION_EVENTS)
235            .build();
236
237        let a_handle = collider_set.insert(collider_a);
238
239        let collider_b = ColliderBuilder::cuboid(1.0, 1.0, 1.0)
240            .active_collision_types(ActiveCollisionTypes::all())
241            .sensor(true)
242            .active_events(ActiveEvents::COLLISION_EVENTS)
243            .build();
244
245        let _ = collider_set.insert(collider_b);
246
247        let integration_parameters = IntegrationParameters::default();
248        let mut islands = IslandManager::new();
249        let mut broad_phase = BroadPhaseBvh::new();
250        let mut narrow_phase = NarrowPhase::new();
251        let mut collision_pipeline = CollisionPipeline::new();
252        let physics_hooks = ();
253
254        collision_pipeline.step(
255            integration_parameters.prediction_distance(),
256            &mut islands,
257            &mut broad_phase,
258            &mut narrow_phase,
259            &mut rigid_body_set,
260            &mut collider_set,
261            &physics_hooks,
262            &(),
263        );
264
265        let mut hit = false;
266
267        for (_, _, intersecting) in narrow_phase.intersection_pairs_with(a_handle) {
268            if intersecting {
269                hit = true;
270            }
271        }
272
273        assert!(hit, "No hit found");
274    }
275
276    #[test]
277    #[cfg(feature = "dim2")]
278    pub fn test_no_rigid_bodies() {
279        use crate::prelude::*;
280        let mut rigid_body_set = RigidBodySet::new();
281        let mut collider_set = ColliderSet::new();
282
283        /* Create the ground. */
284        let collider_a = ColliderBuilder::cuboid(1.0, 1.0)
285            .active_collision_types(ActiveCollisionTypes::all())
286            .sensor(true)
287            .active_events(ActiveEvents::COLLISION_EVENTS)
288            .build();
289
290        let a_handle = collider_set.insert(collider_a);
291
292        let collider_b = ColliderBuilder::cuboid(1.0, 1.0)
293            .active_collision_types(ActiveCollisionTypes::all())
294            .sensor(true)
295            .active_events(ActiveEvents::COLLISION_EVENTS)
296            .build();
297
298        let _ = collider_set.insert(collider_b);
299
300        let integration_parameters = IntegrationParameters::default();
301        let mut islands = IslandManager::new();
302        let mut broad_phase = BroadPhaseBvh::new();
303        let mut narrow_phase = NarrowPhase::new();
304        let mut collision_pipeline = CollisionPipeline::new();
305        let physics_hooks = ();
306
307        collision_pipeline.step(
308            integration_parameters.prediction_distance(),
309            &mut islands,
310            &mut broad_phase,
311            &mut narrow_phase,
312            &mut rigid_body_set,
313            &mut collider_set,
314            &physics_hooks,
315            &(),
316        );
317
318        let mut hit = false;
319
320        for (_, _, intersecting) in narrow_phase.intersection_pairs_with(a_handle) {
321            if intersecting {
322                hit = true;
323            }
324        }
325
326        assert!(hit, "No hit found");
327    }
328}