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