Skip to main content

rapier2d/pipeline/
collision_pipeline.rs

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