rapier2d/geometry/
narrow_phase.rs

1#[cfg(feature = "parallel")]
2use rayon::prelude::*;
3
4use crate::data::Coarena;
5use crate::data::graph::EdgeIndex;
6use crate::dynamics::{
7    CoefficientCombineRule, ImpulseJointSet, IslandManager, RigidBodyDominance, RigidBodySet,
8    RigidBodyType,
9};
10use crate::geometry::{
11    BoundingVolume, BroadPhasePairEvent, ColliderChanges, ColliderGraphIndex, ColliderHandle,
12    ColliderPair, ColliderSet, CollisionEvent, ContactData, ContactManifold, ContactManifoldData,
13    ContactPair, InteractionGraph, IntersectionPair, SolverContact, SolverFlags,
14    TemporaryInteractionIndex,
15};
16use crate::math::{Real, Vector};
17use crate::pipeline::{
18    ActiveEvents, ActiveHooks, ContactModificationContext, EventHandler, PairFilterContext,
19    PhysicsHooks,
20};
21use crate::prelude::{CollisionEventFlags, MultibodyJointSet};
22use parry::query::{DefaultQueryDispatcher, PersistentQueryDispatcher};
23use parry::utils::IsometryOpt;
24use parry::utils::hashmap::HashMap;
25use std::sync::Arc;
26
27#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
28#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
29struct ColliderGraphIndices {
30    contact_graph_index: ColliderGraphIndex,
31    intersection_graph_index: ColliderGraphIndex,
32}
33
34impl ColliderGraphIndices {
35    fn invalid() -> Self {
36        Self {
37            contact_graph_index: InteractionGraph::<(), ()>::invalid_graph_index(),
38            intersection_graph_index: InteractionGraph::<(), ()>::invalid_graph_index(),
39        }
40    }
41}
42
43#[derive(Copy, Clone, PartialEq, Eq)]
44enum PairRemovalMode {
45    FromContactGraph,
46    FromIntersectionGraph,
47    Auto,
48}
49
50/// The narrow-phase collision detector that computes precise contact points between colliders.
51///
52/// After the broad-phase quickly filters out distant object pairs, the narrow-phase performs
53/// detailed geometric computations to find exact:
54/// - Contact points (where surfaces touch)
55/// - Contact normals (which direction surfaces face)
56/// - Penetration depths (how much objects overlap)
57///
58/// You typically don't interact with this directly - it's managed by [`PhysicsPipeline::step`](crate::pipeline::PhysicsPipeline::step).
59/// However, you can access it to query contact information or intersection state between specific colliders.
60///
61/// **For spatial queries** (raycasts, shape casts), use [`QueryPipeline`](crate::pipeline::QueryPipeline) instead.
62#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
63#[derive(Clone)]
64pub struct NarrowPhase {
65    #[cfg_attr(
66        feature = "serde-serialize",
67        serde(skip, default = "crate::geometry::default_persistent_query_dispatcher")
68    )]
69    query_dispatcher: Arc<dyn PersistentQueryDispatcher<ContactManifoldData, ContactData>>,
70    contact_graph: InteractionGraph<ColliderHandle, ContactPair>,
71    intersection_graph: InteractionGraph<ColliderHandle, IntersectionPair>,
72    graph_indices: Coarena<ColliderGraphIndices>,
73}
74
75pub(crate) type ContactManifoldIndex = usize;
76
77impl Default for NarrowPhase {
78    fn default() -> Self {
79        Self::new()
80    }
81}
82
83impl NarrowPhase {
84    /// Creates a new empty narrow-phase.
85    pub fn new() -> Self {
86        Self::with_query_dispatcher(DefaultQueryDispatcher)
87    }
88
89    /// Creates a new empty narrow-phase with a custom query dispatcher.
90    pub fn with_query_dispatcher<D>(d: D) -> Self
91    where
92        D: 'static + PersistentQueryDispatcher<ContactManifoldData, ContactData>,
93    {
94        Self {
95            query_dispatcher: Arc::new(d),
96            contact_graph: InteractionGraph::new(),
97            intersection_graph: InteractionGraph::new(),
98            graph_indices: Coarena::new(),
99        }
100    }
101
102    /// The query dispatcher used by this narrow-phase to select the right collision-detection
103    /// algorithms depending on the shape types.
104    pub fn query_dispatcher(
105        &self,
106    ) -> &dyn PersistentQueryDispatcher<ContactManifoldData, ContactData> {
107        &*self.query_dispatcher
108    }
109
110    /// The contact graph containing all contact pairs and their contact information.
111    pub fn contact_graph(&self) -> &InteractionGraph<ColliderHandle, ContactPair> {
112        &self.contact_graph
113    }
114
115    /// The intersection graph containing all intersection pairs and their intersection information.
116    pub fn intersection_graph(&self) -> &InteractionGraph<ColliderHandle, IntersectionPair> {
117        &self.intersection_graph
118    }
119
120    /// All the contacts involving the given collider.
121    ///
122    /// It is strongly recommended to use the [`NarrowPhase::contact_pairs_with`] method instead. This
123    /// method can be used if the generation number of the collider handle isn't known.
124    pub fn contact_pairs_with_unknown_gen(
125        &self,
126        collider: u32,
127    ) -> impl Iterator<Item = &ContactPair> {
128        self.graph_indices
129            .get_unknown_gen(collider)
130            .map(|id| id.contact_graph_index)
131            .into_iter()
132            .flat_map(move |id| self.contact_graph.interactions_with(id))
133            .map(|pair| pair.2)
134    }
135
136    /// All the contact pairs involving the given collider.
137    ///
138    /// The returned contact pairs identify pairs of colliders with intersecting bounding-volumes.
139    /// To check if any geometric contact happened between the collider shapes, check
140    /// [`ContactPair::has_any_active_contact`].
141    pub fn contact_pairs_with(
142        &self,
143        collider: ColliderHandle,
144    ) -> impl Iterator<Item = &ContactPair> {
145        self.graph_indices
146            .get(collider.0)
147            .map(|id| id.contact_graph_index)
148            .into_iter()
149            .flat_map(move |id| self.contact_graph.interactions_with(id))
150            .map(|pair| pair.2)
151    }
152
153    /// All the intersection pairs involving the given collider.
154    ///
155    /// It is strongly recommended to use the [`NarrowPhase::intersection_pairs_with`]  method instead.
156    /// This method can be used if the generation number of the collider handle isn't known.
157    pub fn intersection_pairs_with_unknown_gen(
158        &self,
159        collider: u32,
160    ) -> impl Iterator<Item = (ColliderHandle, ColliderHandle, bool)> + '_ {
161        self.graph_indices
162            .get_unknown_gen(collider)
163            .map(|id| id.intersection_graph_index)
164            .into_iter()
165            .flat_map(move |id| {
166                self.intersection_graph
167                    .interactions_with(id)
168                    .map(|e| (e.0, e.1, e.2.intersecting))
169            })
170    }
171
172    /// All the intersection pairs involving the given collider, where at least one collider
173    /// involved in the intersection is a sensor.
174    ///
175    /// The returned contact pairs identify pairs of colliders (where at least one is a sensor) with
176    /// intersecting bounding-volumes. To check if any geometric overlap happened between the collider shapes, check
177    /// the returned boolean.
178    pub fn intersection_pairs_with(
179        &self,
180        collider: ColliderHandle,
181    ) -> impl Iterator<Item = (ColliderHandle, ColliderHandle, bool)> + '_ {
182        self.graph_indices
183            .get(collider.0)
184            .map(|id| id.intersection_graph_index)
185            .into_iter()
186            .flat_map(move |id| {
187                self.intersection_graph
188                    .interactions_with(id)
189                    .map(|e| (e.0, e.1, e.2.intersecting))
190            })
191    }
192
193    /// Returns the contact pair at the given temporary index.
194    pub fn contact_pair_at_index(&self, id: TemporaryInteractionIndex) -> &ContactPair {
195        &self.contact_graph.graph.edges[id.index()].weight
196    }
197
198    /// The contact pair involving two specific colliders.
199    ///
200    /// It is strongly recommended to use the [`NarrowPhase::contact_pair`] method instead. This
201    /// method can be used if the generation number of the collider handle isn't known.
202    ///
203    /// If this returns `None`, there is no contact between the two colliders.
204    /// If this returns `Some`, then there may be a contact between the two colliders. Check the
205    /// result [`ContactPair::has_any_active_contact`] method to see if there is an actual contact.
206    pub fn contact_pair_unknown_gen(&self, collider1: u32, collider2: u32) -> Option<&ContactPair> {
207        let id1 = self.graph_indices.get_unknown_gen(collider1)?;
208        let id2 = self.graph_indices.get_unknown_gen(collider2)?;
209        self.contact_graph
210            .interaction_pair(id1.contact_graph_index, id2.contact_graph_index)
211            .map(|c| c.2)
212    }
213
214    /// The contact pair involving two specific colliders.
215    ///
216    /// If this returns `None`, there is no contact between the two colliders.
217    /// If this returns `Some`, then there may be a contact between the two colliders. Check the
218    /// result [`ContactPair::has_any_active_contact`] method to see if there is an actual contact.
219    pub fn contact_pair(
220        &self,
221        collider1: ColliderHandle,
222        collider2: ColliderHandle,
223    ) -> Option<&ContactPair> {
224        let id1 = self.graph_indices.get(collider1.0)?;
225        let id2 = self.graph_indices.get(collider2.0)?;
226        self.contact_graph
227            .interaction_pair(id1.contact_graph_index, id2.contact_graph_index)
228            .map(|c| c.2)
229    }
230
231    /// The intersection pair involving two specific colliders.
232    ///
233    /// It is strongly recommended to use the [`NarrowPhase::intersection_pair`] method instead. This
234    /// method can be used if the generation number of the collider handle isn't known.
235    ///
236    /// If this returns `None` or `Some(false)`, then there is no intersection between the two colliders.
237    /// If this returns `Some(true)`, then there may be an intersection between the two colliders.
238    pub fn intersection_pair_unknown_gen(&self, collider1: u32, collider2: u32) -> Option<bool> {
239        let id1 = self.graph_indices.get_unknown_gen(collider1)?;
240        let id2 = self.graph_indices.get_unknown_gen(collider2)?;
241        self.intersection_graph
242            .interaction_pair(id1.intersection_graph_index, id2.intersection_graph_index)
243            .map(|c| c.2.intersecting)
244    }
245
246    /// The intersection pair involving two specific colliders.
247    ///
248    /// If this returns `None` or `Some(false)`, then there is no intersection between the two colliders.
249    /// If this returns `Some(true)`, then there may be an intersection between the two colliders.
250    pub fn intersection_pair(
251        &self,
252        collider1: ColliderHandle,
253        collider2: ColliderHandle,
254    ) -> Option<bool> {
255        let id1 = self.graph_indices.get(collider1.0)?;
256        let id2 = self.graph_indices.get(collider2.0)?;
257        self.intersection_graph
258            .interaction_pair(id1.intersection_graph_index, id2.intersection_graph_index)
259            .map(|c| c.2.intersecting)
260    }
261
262    /// All the contact pairs maintained by this narrow-phase.
263    pub fn contact_pairs(&self) -> impl Iterator<Item = &ContactPair> {
264        self.contact_graph.interactions()
265    }
266
267    /// All the intersection pairs maintained by this narrow-phase.
268    pub fn intersection_pairs(
269        &self,
270    ) -> impl Iterator<Item = (ColliderHandle, ColliderHandle, bool)> + '_ {
271        self.intersection_graph
272            .interactions_with_endpoints()
273            .map(|e| (e.0, e.1, e.2.intersecting))
274    }
275
276    // #[cfg(feature = "parallel")]
277    // pub(crate) fn contact_pairs_vec_mut(&mut self) -> &mut Vec<ContactPair> {
278    //     &mut self.contact_graph.interactions
279    // }
280
281    /// Maintain the narrow-phase internal state by taking collider removal into account.
282    #[profiling::function]
283    pub fn handle_user_changes(
284        &mut self,
285        mut islands: Option<&mut IslandManager>,
286        modified_colliders: &[ColliderHandle],
287        removed_colliders: &[ColliderHandle],
288        colliders: &mut ColliderSet,
289        bodies: &mut RigidBodySet,
290        events: &dyn EventHandler,
291    ) {
292        // TODO: avoid these hash-maps.
293        // They are necessary to handle the swap-remove done internally
294        // by the contact/intersection graphs when a node is removed.
295        let mut prox_id_remap = HashMap::default();
296        let mut contact_id_remap = HashMap::default();
297
298        for collider in removed_colliders {
299            // NOTE: if the collider does not have any graph indices currently, there is nothing
300            // to remove in the narrow-phase for this collider.
301            if let Some(graph_idx) = self
302                .graph_indices
303                .remove(collider.0, ColliderGraphIndices::invalid())
304            {
305                let intersection_graph_id = prox_id_remap
306                    .get(collider)
307                    .copied()
308                    .unwrap_or(graph_idx.intersection_graph_index);
309                let contact_graph_id = contact_id_remap
310                    .get(collider)
311                    .copied()
312                    .unwrap_or(graph_idx.contact_graph_index);
313
314                self.remove_collider(
315                    intersection_graph_id,
316                    contact_graph_id,
317                    islands.as_deref_mut(),
318                    colliders,
319                    bodies,
320                    &mut prox_id_remap,
321                    &mut contact_id_remap,
322                    events,
323                );
324            }
325        }
326
327        self.handle_user_changes_on_colliders(
328            islands,
329            modified_colliders,
330            colliders,
331            bodies,
332            events,
333        );
334    }
335
336    #[profiling::function]
337    pub(crate) fn remove_collider(
338        &mut self,
339        intersection_graph_id: ColliderGraphIndex,
340        contact_graph_id: ColliderGraphIndex,
341        islands: Option<&mut IslandManager>,
342        colliders: &mut ColliderSet,
343        bodies: &mut RigidBodySet,
344        prox_id_remap: &mut HashMap<ColliderHandle, ColliderGraphIndex>,
345        contact_id_remap: &mut HashMap<ColliderHandle, ColliderGraphIndex>,
346        events: &dyn EventHandler,
347    ) {
348        // Wake up every body in contact with the deleted collider and generate Stopped collision events.
349        if let Some(islands) = islands {
350            for (a, b, pair) in self.contact_graph.interactions_with(contact_graph_id) {
351                if let Some(parent) = colliders.get(a).and_then(|c| c.parent.as_ref()) {
352                    islands.wake_up(bodies, parent.handle, true)
353                }
354
355                if let Some(parent) = colliders.get(b).and_then(|c| c.parent.as_ref()) {
356                    islands.wake_up(bodies, parent.handle, true)
357                }
358
359                if pair.start_event_emitted {
360                    events.handle_collision_event(
361                        bodies,
362                        colliders,
363                        CollisionEvent::Stopped(a, b, CollisionEventFlags::REMOVED),
364                        Some(pair),
365                    );
366                }
367            }
368        } else {
369            // If there is no island, don’t wake-up bodies, but do send the Stopped collision event.
370            for (a, b, pair) in self.contact_graph.interactions_with(contact_graph_id) {
371                if pair.start_event_emitted {
372                    events.handle_collision_event(
373                        bodies,
374                        colliders,
375                        CollisionEvent::Stopped(a, b, CollisionEventFlags::REMOVED),
376                        Some(pair),
377                    );
378                }
379            }
380        }
381
382        // Generate Stopped collision events for intersections.
383        for (a, b, pair) in self
384            .intersection_graph
385            .interactions_with(intersection_graph_id)
386        {
387            if pair.start_event_emitted {
388                events.handle_collision_event(
389                    bodies,
390                    colliders,
391                    CollisionEvent::Stopped(
392                        a,
393                        b,
394                        CollisionEventFlags::REMOVED | CollisionEventFlags::SENSOR,
395                    ),
396                    None,
397                );
398            }
399        }
400
401        // We have to manage the fact that one other collider will
402        // have its graph index changed because of the node's swap-remove.
403        if let Some(replacement) = self.intersection_graph.remove_node(intersection_graph_id) {
404            if let Some(replacement) = self.graph_indices.get_mut(replacement.0) {
405                replacement.intersection_graph_index = intersection_graph_id;
406            } else {
407                prox_id_remap.insert(replacement, intersection_graph_id);
408                // I feel like this should never happen now that the narrow-phase is the one owning
409                // the graph_indices. Let's put an unreachable in there and see if anybody still manages
410                // to reach it. If nobody does, we will remove this.
411                unreachable!();
412            }
413        }
414
415        if let Some(replacement) = self.contact_graph.remove_node(contact_graph_id) {
416            if let Some(replacement) = self.graph_indices.get_mut(replacement.0) {
417                replacement.contact_graph_index = contact_graph_id;
418            } else {
419                contact_id_remap.insert(replacement, contact_graph_id);
420                // I feel like this should never happen now that the narrow-phase is the one owning
421                // the graph_indices. Let's put an unreachable in there and see if anybody still manages
422                // to reach it. If nobody does, we will remove this.
423                unreachable!();
424            }
425        }
426    }
427
428    #[profiling::function]
429    pub(crate) fn handle_user_changes_on_colliders(
430        &mut self,
431        mut islands: Option<&mut IslandManager>,
432        modified_colliders: &[ColliderHandle],
433        colliders: &ColliderSet,
434        bodies: &mut RigidBodySet,
435        events: &dyn EventHandler,
436    ) {
437        let mut pairs_to_remove = vec![];
438
439        for handle in modified_colliders {
440            // NOTE: we use `get` because the collider may no longer
441            //       exist if it has been removed.
442            if let Some(co) = colliders.get(*handle) {
443                if !co.changes.needs_narrow_phase_update() {
444                    // No flag relevant to the narrow-phase is enabled for this collider.
445                    continue;
446                }
447
448                if let Some(gid) = self.graph_indices.get(handle.0) {
449                    // For each modified colliders, we need to wake-up the bodies it is in contact with
450                    // so that the narrow-phase properly takes into account the change in, e.g.,
451                    // collision groups. Waking up the modified collider's parent isn't enough because
452                    // it could be a fixed or kinematic body which don't propagate the wake-up state.
453                    if let Some(islands) = islands.as_deref_mut() {
454                        if let Some(co_parent) = &co.parent {
455                            islands.wake_up(bodies, co_parent.handle, true);
456                        }
457
458                        for inter in self
459                            .contact_graph
460                            .interactions_with(gid.contact_graph_index)
461                        {
462                            let other_handle = if *handle == inter.0 { inter.1 } else { inter.0 };
463                            let other_parent = colliders
464                                .get(other_handle)
465                                .and_then(|co| co.parent.as_ref());
466
467                            if let Some(other_parent) = other_parent {
468                                islands.wake_up(bodies, other_parent.handle, true);
469                            }
470                        }
471                    }
472
473                    // For each collider which had their sensor status modified, we need
474                    // to transfer their contact/intersection graph edges to the intersection/contact graph.
475                    // To achieve this we will remove the relevant contact/intersection pairs form the
476                    // contact/intersection graphs, and then add them into the other graph.
477                    if co.changes.intersects(ColliderChanges::TYPE) {
478                        if co.is_sensor() {
479                            // Find the contact pairs for this collider and
480                            // push them to `pairs_to_remove`.
481                            for inter in self
482                                .contact_graph
483                                .interactions_with(gid.contact_graph_index)
484                            {
485                                pairs_to_remove.push((
486                                    ColliderPair::new(inter.0, inter.1),
487                                    PairRemovalMode::FromContactGraph,
488                                ));
489                            }
490                        } else {
491                            // Find the contact pairs for this collider and
492                            // push them to `pairs_to_remove` if both involved
493                            // colliders are not sensors.
494                            for inter in self
495                                .intersection_graph
496                                .interactions_with(gid.intersection_graph_index)
497                                .filter(|(h1, h2, _)| {
498                                    !colliders[*h1].is_sensor() && !colliders[*h2].is_sensor()
499                                })
500                            {
501                                pairs_to_remove.push((
502                                    ColliderPair::new(inter.0, inter.1),
503                                    PairRemovalMode::FromIntersectionGraph,
504                                ));
505                            }
506                        }
507                    }
508
509                    // NOTE: if a collider only changed parent, we don’t need to remove it from any
510                    //       of the graphs as re-parenting doesn’t change the sensor status of a
511                    //       collider. If needed, their collision/intersection data will be
512                    //       updated/removed automatically in the contact or intersection update
513                    //       functions.
514                }
515            }
516        }
517
518        // Remove the pair from the relevant graph.
519        for pair in &pairs_to_remove {
520            self.remove_pair(
521                islands.as_deref_mut(),
522                colliders,
523                bodies,
524                &pair.0,
525                events,
526                pair.1,
527            );
528        }
529
530        // Add the removed pair to the relevant graph.
531        for pair in pairs_to_remove {
532            self.add_pair(colliders, &pair.0);
533        }
534    }
535
536    #[profiling::function]
537    fn remove_pair(
538        &mut self,
539        islands: Option<&mut IslandManager>,
540        colliders: &ColliderSet,
541        bodies: &mut RigidBodySet,
542        pair: &ColliderPair,
543        events: &dyn EventHandler,
544        mode: PairRemovalMode,
545    ) {
546        if let (Some(co1), Some(co2)) =
547            (colliders.get(pair.collider1), colliders.get(pair.collider2))
548        {
549            // TODO: could we just unwrap here?
550            // Don't we have the guarantee that we will get a `AddPair` before a `DeletePair`?
551            if let (Some(gid1), Some(gid2)) = (
552                self.graph_indices.get(pair.collider1.0),
553                self.graph_indices.get(pair.collider2.0),
554            ) {
555                if mode == PairRemovalMode::FromIntersectionGraph
556                    || (mode == PairRemovalMode::Auto && (co1.is_sensor() || co2.is_sensor()))
557                {
558                    let intersection = self
559                        .intersection_graph
560                        .remove_edge(gid1.intersection_graph_index, gid2.intersection_graph_index);
561
562                    // Emit an intersection lost event if we had an intersection before removing the edge.
563                    if let Some(mut intersection) = intersection {
564                        if intersection.intersecting
565                            && (co1.flags.active_events | co2.flags.active_events)
566                                .contains(ActiveEvents::COLLISION_EVENTS)
567                        {
568                            intersection.emit_stop_event(
569                                bodies,
570                                colliders,
571                                pair.collider1,
572                                pair.collider2,
573                                events,
574                            )
575                        }
576                    }
577                } else {
578                    let contact_pair = self
579                        .contact_graph
580                        .remove_edge(gid1.contact_graph_index, gid2.contact_graph_index);
581
582                    // Emit a contact stopped event if we had a contact before removing the edge.
583                    // Also wake up the dynamic bodies that were in contact.
584                    if let Some(mut ctct) = contact_pair {
585                        if ctct.has_any_active_contact {
586                            if let Some(islands) = islands {
587                                if let Some(co_parent1) = &co1.parent {
588                                    islands.wake_up(bodies, co_parent1.handle, true);
589                                }
590
591                                if let Some(co_parent2) = co2.parent {
592                                    islands.wake_up(bodies, co_parent2.handle, true);
593                                }
594                            }
595
596                            if (co1.flags.active_events | co2.flags.active_events)
597                                .contains(ActiveEvents::COLLISION_EVENTS)
598                            {
599                                ctct.emit_stop_event(bodies, colliders, events);
600                            }
601                        }
602                    }
603                }
604            }
605        }
606    }
607
608    #[profiling::function]
609    fn add_pair(&mut self, colliders: &ColliderSet, pair: &ColliderPair) {
610        if let (Some(co1), Some(co2)) =
611            (colliders.get(pair.collider1), colliders.get(pair.collider2))
612        {
613            // These colliders have no parents - continue.
614
615            let (gid1, gid2) = self.graph_indices.ensure_pair_exists(
616                pair.collider1.0,
617                pair.collider2.0,
618                ColliderGraphIndices::invalid(),
619            );
620
621            if co1.is_sensor() || co2.is_sensor() {
622                // NOTE: the collider won't have a graph index as long
623                // as it does not interact with anything.
624                if !InteractionGraph::<(), ()>::is_graph_index_valid(gid1.intersection_graph_index)
625                {
626                    gid1.intersection_graph_index =
627                        self.intersection_graph.graph.add_node(pair.collider1);
628                }
629
630                if !InteractionGraph::<(), ()>::is_graph_index_valid(gid2.intersection_graph_index)
631                {
632                    gid2.intersection_graph_index =
633                        self.intersection_graph.graph.add_node(pair.collider2);
634                }
635
636                if self
637                    .intersection_graph
638                    .graph
639                    .find_edge(gid1.intersection_graph_index, gid2.intersection_graph_index)
640                    .is_none()
641                {
642                    let _ = self.intersection_graph.add_edge(
643                        gid1.intersection_graph_index,
644                        gid2.intersection_graph_index,
645                        IntersectionPair::new(),
646                    );
647                }
648            } else {
649                // NOTE: same code as above, but for the contact graph.
650                // TODO: refactor both pieces of code somehow?
651
652                // NOTE: the collider won't have a graph index as long
653                // as it does not interact with anything.
654                if !InteractionGraph::<(), ()>::is_graph_index_valid(gid1.contact_graph_index) {
655                    gid1.contact_graph_index = self.contact_graph.graph.add_node(pair.collider1);
656                }
657
658                if !InteractionGraph::<(), ()>::is_graph_index_valid(gid2.contact_graph_index) {
659                    gid2.contact_graph_index = self.contact_graph.graph.add_node(pair.collider2);
660                }
661
662                if self
663                    .contact_graph
664                    .graph
665                    .find_edge(gid1.contact_graph_index, gid2.contact_graph_index)
666                    .is_none()
667                {
668                    let interaction = ContactPair::new(pair.collider1, pair.collider2);
669                    let _ = self.contact_graph.add_edge(
670                        gid1.contact_graph_index,
671                        gid2.contact_graph_index,
672                        interaction,
673                    );
674                }
675            }
676        }
677    }
678
679    pub(crate) fn register_pairs(
680        &mut self,
681        mut islands: Option<&mut IslandManager>,
682        colliders: &ColliderSet,
683        bodies: &mut RigidBodySet,
684        broad_phase_events: &[BroadPhasePairEvent],
685        events: &dyn EventHandler,
686    ) {
687        for event in broad_phase_events {
688            match event {
689                BroadPhasePairEvent::AddPair(pair) => {
690                    self.add_pair(colliders, pair);
691                }
692                BroadPhasePairEvent::DeletePair(pair) => {
693                    self.remove_pair(
694                        islands.as_deref_mut(),
695                        colliders,
696                        bodies,
697                        pair,
698                        events,
699                        PairRemovalMode::Auto,
700                    );
701                }
702            }
703        }
704    }
705
706    #[profiling::function]
707    pub(crate) fn compute_intersections(
708        &mut self,
709        bodies: &RigidBodySet,
710        colliders: &ColliderSet,
711        hooks: &dyn PhysicsHooks,
712        events: &dyn EventHandler,
713    ) {
714        let nodes = &self.intersection_graph.graph.nodes;
715        let query_dispatcher = &*self.query_dispatcher;
716
717        // TODO: don't iterate on all the edges.
718        par_iter_mut!(&mut self.intersection_graph.graph.edges).for_each(|edge| {
719            let handle1 = nodes[edge.source().index()].weight;
720            let handle2 = nodes[edge.target().index()].weight;
721            let had_intersection = edge.weight.intersecting;
722            let co1 = &colliders[handle1];
723            let co2 = &colliders[handle2];
724
725            'emit_events: {
726                if !co1.changes.needs_narrow_phase_update()
727                    && !co2.changes.needs_narrow_phase_update()
728                {
729                    // No update needed for these colliders.
730                    return;
731                }
732                if co1.parent.map(|p| p.handle) == co2.parent.map(|p| p.handle)
733                    && co1.parent.is_some()
734                {
735                    // Same parents. Ignore collisions.
736                    edge.weight.intersecting = false;
737                    break 'emit_events;
738                }
739                // TODO: avoid lookup into bodies.
740                let mut rb_type1 = RigidBodyType::Fixed;
741                let mut rb_type2 = RigidBodyType::Fixed;
742
743                if let Some(co_parent1) = &co1.parent {
744                    rb_type1 = bodies[co_parent1.handle].body_type;
745                }
746
747                if let Some(co_parent2) = &co2.parent {
748                    rb_type2 = bodies[co_parent2.handle].body_type;
749                }
750
751                // Filter based on the rigid-body types.
752                if !co1.flags.active_collision_types.test(rb_type1, rb_type2)
753                    && !co2.flags.active_collision_types.test(rb_type1, rb_type2)
754                {
755                    edge.weight.intersecting = false;
756                    break 'emit_events;
757                }
758
759                // Filter based on collision groups.
760                if !co1.flags.collision_groups.test(co2.flags.collision_groups) {
761                    edge.weight.intersecting = false;
762                    break 'emit_events;
763                }
764
765                let active_hooks = co1.flags.active_hooks | co2.flags.active_hooks;
766
767                if active_hooks.contains(ActiveHooks::FILTER_INTERSECTION_PAIR) {
768                    let context = PairFilterContext {
769                        bodies,
770                        colliders,
771                        rigid_body1: co1.parent.map(|p| p.handle),
772                        rigid_body2: co2.parent.map(|p| p.handle),
773                        collider1: handle1,
774                        collider2: handle2,
775                    };
776
777                    if !hooks.filter_intersection_pair(&context) {
778                        // No intersection allowed.
779                        edge.weight.intersecting = false;
780                        break 'emit_events;
781                    }
782                }
783
784                let pos12 = co1.pos.inv_mul(&co2.pos);
785                edge.weight.intersecting = query_dispatcher
786                    .intersection_test(&pos12, &*co1.shape, &*co2.shape)
787                    .unwrap_or(false);
788            }
789
790            let active_events = co1.flags.active_events | co2.flags.active_events;
791
792            if active_events.contains(ActiveEvents::COLLISION_EVENTS)
793                && had_intersection != edge.weight.intersecting
794            {
795                if edge.weight.intersecting {
796                    edge.weight
797                        .emit_start_event(bodies, colliders, handle1, handle2, events);
798                } else {
799                    edge.weight
800                        .emit_stop_event(bodies, colliders, handle1, handle2, events);
801                }
802            }
803        });
804    }
805
806    #[profiling::function]
807    pub(crate) fn compute_contacts(
808        &mut self,
809        prediction_distance: Real,
810        dt: Real,
811        bodies: &RigidBodySet,
812        colliders: &ColliderSet,
813        impulse_joints: &ImpulseJointSet,
814        multibody_joints: &MultibodyJointSet,
815        hooks: &dyn PhysicsHooks,
816        events: &dyn EventHandler,
817    ) {
818        let query_dispatcher = &*self.query_dispatcher;
819
820        // TODO: don't iterate on all the edges.
821        par_iter_mut!(&mut self.contact_graph.graph.edges).for_each(|edge| {
822            let pair = &mut edge.weight;
823            let had_any_active_contact = pair.has_any_active_contact;
824            let co1 = &colliders[pair.collider1];
825            let co2 = &colliders[pair.collider2];
826
827            'emit_events: {
828                if !co1.changes.needs_narrow_phase_update()
829                    && !co2.changes.needs_narrow_phase_update()
830                {
831                    // No update needed for these colliders.
832                    return;
833                }
834                if co1.parent.map(|p| p.handle) == co2.parent.map(|p| p.handle) && co1.parent.is_some()
835                {
836                    // Same parents. Ignore collisions.
837                    pair.clear();
838                    break 'emit_events;
839                }
840
841                let rb1 = co1.parent.map(|co_parent1| &bodies[co_parent1.handle]);
842                let rb2 = co2.parent.map(|co_parent2| &bodies[co_parent2.handle]);
843
844                let rb_type1 = rb1.map(|rb| rb.body_type).unwrap_or(RigidBodyType::Fixed);
845                let rb_type2 = rb2.map(|rb| rb.body_type).unwrap_or(RigidBodyType::Fixed);
846
847                // Deal with contacts disabled between bodies attached by joints.
848                if let (Some(co_parent1), Some(co_parent2)) = (&co1.parent, &co2.parent) {
849                    for (_, joint) in
850                        impulse_joints.joints_between(co_parent1.handle, co_parent2.handle)
851                    {
852                        if !joint.data.contacts_enabled {
853                            pair.clear();
854                            break 'emit_events;
855                        }
856                    }
857
858                    let link1 = multibody_joints.rigid_body_link(co_parent1.handle);
859                    let link2 = multibody_joints.rigid_body_link(co_parent2.handle);
860
861                    if let (Some(link1),Some(link2)) = (link1, link2) {
862                        // If both bodies belong to the same multibody, apply some additional built-in
863                        // contact filtering rules.
864                        if link1.multibody == link2.multibody {
865                            // 1) check if self-contacts is enabled.
866                            if let Some(mb) = multibody_joints.get_multibody(link1.multibody) {
867                                if !mb.self_contacts_enabled() {
868                                    pair.clear();
869                                    break 'emit_events;
870                                }
871                            }
872
873                            // 2) if they are attached by a joint, check if  contacts is disabled.
874                            if let Some((_, _, mb_link)) =
875                                multibody_joints.joint_between(co_parent1.handle, co_parent2.handle)
876                            {
877                                if !mb_link.joint.data.contacts_enabled {
878                                    pair.clear();
879                                    break 'emit_events;
880                                }
881                            }
882                        }
883                    }
884                }
885
886                // Filter based on the rigid-body types.
887                if !co1.flags.active_collision_types.test(rb_type1, rb_type2)
888                    && !co2.flags.active_collision_types.test(rb_type1, rb_type2)
889                {
890                    pair.clear();
891                    break 'emit_events;
892                }
893
894                // Filter based on collision groups.
895                if !co1.flags.collision_groups.test(co2.flags.collision_groups) {
896                    pair.clear();
897                    break 'emit_events;
898                }
899
900                let active_hooks = co1.flags.active_hooks | co2.flags.active_hooks;
901
902                let mut solver_flags = if active_hooks.contains(ActiveHooks::FILTER_CONTACT_PAIRS) {
903                    let context = PairFilterContext {
904                        bodies,
905                        colliders,
906                        rigid_body1: co1.parent.map(|p| p.handle),
907                        rigid_body2: co2.parent.map(|p| p.handle),
908                        collider1: pair.collider1,
909                        collider2: pair.collider2,
910                    };
911
912                    if let Some(solver_flags) = hooks.filter_contact_pair(&context) {
913                        solver_flags
914                    } else {
915                        // No contact allowed.
916                        pair.clear();
917                        break 'emit_events;
918                    }
919                } else {
920                    SolverFlags::default()
921                };
922
923                if !co1.flags.solver_groups.test(co2.flags.solver_groups) {
924                    solver_flags.remove(SolverFlags::COMPUTE_IMPULSES);
925                }
926
927                if co1.changes.contains(ColliderChanges::SHAPE)
928                    || co2.changes.contains(ColliderChanges::SHAPE)
929                {
930                    // The shape changed so the workspace is no longer valid.
931                    pair.workspace = None;
932                }
933
934                let pos12 = co1.pos.inv_mul(&co2.pos);
935
936                let contact_skin_sum = co1.contact_skin() + co2.contact_skin();
937                let soft_ccd_prediction1 = rb1.map(|rb| rb.soft_ccd_prediction()).unwrap_or(0.0);
938                let soft_ccd_prediction2 = rb2.map(|rb| rb.soft_ccd_prediction()).unwrap_or(0.0);
939                let effective_prediction_distance = if soft_ccd_prediction1 > 0.0 || soft_ccd_prediction2 > 0.0 {
940                        let aabb1 = co1.compute_collision_aabb(0.0);
941                        let aabb2 = co2.compute_collision_aabb(0.0);
942                        let inv_dt = crate::utils::inv(dt);
943
944                        let linvel1 = rb1.map(|rb| rb.linvel()
945                            .cap_magnitude(soft_ccd_prediction1 * inv_dt)).unwrap_or_default();
946                        let linvel2 = rb2.map(|rb| rb.linvel()
947                            .cap_magnitude(soft_ccd_prediction2 * inv_dt)).unwrap_or_default();
948
949                        if !aabb1.intersects(&aabb2) && !aabb1.intersects_moving_aabb(&aabb2, linvel2 - linvel1) {
950                            pair.clear();
951                            break 'emit_events;
952                        }
953
954
955                    prediction_distance.max(
956                        dt * (linvel1 - linvel2).norm()) + contact_skin_sum
957                } else {
958                    prediction_distance + contact_skin_sum
959                };
960
961                let _ = query_dispatcher.contact_manifolds(
962                    &pos12,
963                    &*co1.shape,
964                    &*co2.shape,
965                    effective_prediction_distance,
966                    &mut pair.manifolds,
967                    &mut pair.workspace,
968                );
969
970                let friction = CoefficientCombineRule::combine(
971                    co1.material.friction,
972                    co2.material.friction,
973                    co1.material.friction_combine_rule,
974                    co2.material.friction_combine_rule,
975                );
976                let restitution = CoefficientCombineRule::combine(
977                    co1.material.restitution,
978                    co2.material.restitution,
979                    co1.material.restitution_combine_rule,
980                    co2.material.restitution_combine_rule,
981                );
982
983                let zero = RigidBodyDominance(0); // The value doesn't matter, it will be MAX because of the effective groups.
984                let dominance1 = rb1.map(|rb| rb.dominance).unwrap_or(zero);
985                let dominance2 = rb2.map(|rb| rb.dominance).unwrap_or(zero);
986
987                pair.has_any_active_contact = false;
988
989                for manifold in &mut pair.manifolds {
990                    let world_pos1 = manifold.subshape_pos1.prepend_to(&co1.pos);
991                    let world_pos2 = manifold.subshape_pos2.prepend_to(&co2.pos);
992                    manifold.data.solver_contacts.clear();
993                    manifold.data.rigid_body1 = co1.parent.map(|p| p.handle);
994                    manifold.data.rigid_body2 = co2.parent.map(|p| p.handle);
995                    manifold.data.solver_flags = solver_flags;
996                    manifold.data.relative_dominance = dominance1.effective_group(&rb_type1)
997                        - dominance2.effective_group(&rb_type2);
998                    manifold.data.normal = world_pos1 * manifold.local_n1;
999
1000                    // Generate solver contacts.
1001                    for (contact_id, contact) in manifold.points.iter().enumerate() {
1002                        if contact_id > u8::MAX as usize {
1003                            log::warn!("A contact manifold cannot contain more than 255 contacts currently, dropping contact in excess.");
1004                            break;
1005                        }
1006
1007                        let effective_contact_dist = contact.dist - co1.contact_skin() - co2.contact_skin();
1008
1009                        let keep_solver_contact = effective_contact_dist < prediction_distance || {
1010                            let world_pt1 = world_pos1 * contact.local_p1;
1011                            let world_pt2 = world_pos2 * contact.local_p2;
1012                            let vel1 = rb1.map(|rb| rb.velocity_at_point(&world_pt1)).unwrap_or_default();
1013                            let vel2 = rb2.map(|rb| rb.velocity_at_point(&world_pt2)).unwrap_or_default();
1014                            effective_contact_dist + (vel2 - vel1).dot(&manifold.data.normal) * dt < prediction_distance
1015                        };
1016
1017                        if keep_solver_contact {
1018                            // Generate the solver contact.
1019                            let world_pt1 = world_pos1 * contact.local_p1;
1020                            let world_pt2 = world_pos2 * contact.local_p2;
1021                            let effective_point = na::center(&world_pt1, &world_pt2);
1022
1023                            let solver_contact = SolverContact {
1024                                contact_id: [contact_id as u32],
1025                                point: effective_point,
1026                                dist: effective_contact_dist,
1027                                friction,
1028                                restitution,
1029                                tangent_velocity: Vector::zeros(),
1030                                is_new: (contact.data.impulse == 0.0) as u32 as Real,
1031                                warmstart_impulse: contact.data.warmstart_impulse,
1032                                warmstart_tangent_impulse: contact.data.warmstart_tangent_impulse,
1033                                #[cfg(feature = "dim2")]
1034                                warmstart_twist_impulse: na::zero(),
1035                                #[cfg(feature = "dim3")]
1036                                warmstart_twist_impulse: contact.data.warmstart_twist_impulse,
1037                                #[cfg(feature = "dim3")]
1038                                padding: Default::default(),
1039                            };
1040
1041                            manifold.data.solver_contacts.push(solver_contact);
1042                            pair.has_any_active_contact = true;
1043                        }
1044                    }
1045
1046                    // Apply the user-defined contact modification.
1047                    if active_hooks.contains(ActiveHooks::MODIFY_SOLVER_CONTACTS) {
1048                        let mut modifiable_solver_contacts =
1049                            std::mem::take(&mut manifold.data.solver_contacts);
1050                        let mut modifiable_user_data = manifold.data.user_data;
1051                        let mut modifiable_normal = manifold.data.normal;
1052
1053                        let mut context = ContactModificationContext {
1054                            bodies,
1055                            colliders,
1056                            rigid_body1: co1.parent.map(|p| p.handle),
1057                            rigid_body2: co2.parent.map(|p| p.handle),
1058                            collider1: pair.collider1,
1059                            collider2: pair.collider2,
1060                            manifold,
1061                            solver_contacts: &mut modifiable_solver_contacts,
1062                            normal: &mut modifiable_normal,
1063                            user_data: &mut modifiable_user_data,
1064                        };
1065
1066                        hooks.modify_solver_contacts(&mut context);
1067
1068                        manifold.data.solver_contacts = modifiable_solver_contacts;
1069                        manifold.data.normal = modifiable_normal;
1070                        manifold.data.user_data = modifiable_user_data;
1071                    }
1072
1073                    /*
1074                     * TODO: When using the block solver in 3D, I’d expect this sort to help, but
1075                     *       it makes the domino demo worse. Needs more investigation.
1076                    fn sort_solver_contacts(mut contacts: &mut [SolverContact]) {
1077                        while contacts.len() > 2 {
1078                            let first = contacts[0];
1079                            let mut furthest_id = 1;
1080                            let mut furthest_dist = na::distance(&first.point, &contacts[1].point);
1081
1082                            for (candidate_id, candidate) in contacts.iter().enumerate().skip(2) {
1083                                let candidate_dist = na::distance(&first.point, &candidate.point);
1084
1085                                if candidate_dist > furthest_dist {
1086                                    furthest_dist = candidate_dist;
1087                                    furthest_id = candidate_id;
1088                                }
1089                            }
1090
1091                            if furthest_id > 1 {
1092                                contacts.swap(1, furthest_id);
1093                            }
1094
1095                            contacts = &mut contacts[2..];
1096                        }
1097                    }
1098
1099                    sort_solver_contacts(&mut manifold.data.solver_contacts);
1100                    */
1101                }
1102            }
1103
1104            let active_events = co1.flags.active_events | co2.flags.active_events;
1105
1106            if pair.has_any_active_contact != had_any_active_contact
1107                && active_events.contains(ActiveEvents::COLLISION_EVENTS)
1108            {
1109                if pair.has_any_active_contact {
1110                    pair.emit_start_event(bodies, colliders, events);
1111                } else {
1112                    pair.emit_stop_event(bodies, colliders, events);
1113                }
1114            }
1115        });
1116    }
1117
1118    /// Retrieve all the interactions with at least one contact point, happening between two active bodies.
1119    // NOTE: this is very similar to the code from ImpulseJointSet::select_active_interactions.
1120    pub(crate) fn select_active_contacts<'a>(
1121        &'a mut self,
1122        islands: &IslandManager,
1123        bodies: &RigidBodySet,
1124        out_contact_pairs: &mut Vec<TemporaryInteractionIndex>,
1125        out_manifolds: &mut Vec<&'a mut ContactManifold>,
1126        out: &mut [Vec<ContactManifoldIndex>],
1127    ) {
1128        for out_island in &mut out[..islands.num_islands()] {
1129            out_island.clear();
1130        }
1131
1132        // TODO: don't iterate through all the interactions.
1133        for (pair_id, inter) in self.contact_graph.graph.edges.iter_mut().enumerate() {
1134            let mut push_pair = false;
1135
1136            for manifold in &mut inter.weight.manifolds {
1137                if manifold
1138                    .data
1139                    .solver_flags
1140                    .contains(SolverFlags::COMPUTE_IMPULSES)
1141                    && manifold.data.num_active_contacts() != 0
1142                {
1143                    let (active_island_id1, rb_type1, sleeping1) =
1144                        if let Some(handle1) = manifold.data.rigid_body1 {
1145                            let rb1 = &bodies[handle1];
1146                            (
1147                                rb1.ids.active_island_id,
1148                                rb1.body_type,
1149                                rb1.activation.sleeping,
1150                            )
1151                        } else {
1152                            (0, RigidBodyType::Fixed, true)
1153                        };
1154
1155                    let (active_island_id2, rb_type2, sleeping2) =
1156                        if let Some(handle2) = manifold.data.rigid_body2 {
1157                            let rb2 = &bodies[handle2];
1158                            (
1159                                rb2.ids.active_island_id,
1160                                rb2.body_type,
1161                                rb2.activation.sleeping,
1162                            )
1163                        } else {
1164                            (0, RigidBodyType::Fixed, true)
1165                        };
1166
1167                    if (rb_type1.is_dynamic() || rb_type2.is_dynamic())
1168                        && (!rb_type1.is_dynamic() || !sleeping1)
1169                        && (!rb_type2.is_dynamic() || !sleeping2)
1170                    {
1171                        let island_index = if !rb_type1.is_dynamic() {
1172                            active_island_id2
1173                        } else {
1174                            active_island_id1
1175                        };
1176
1177                        out[island_index].push(out_manifolds.len());
1178                        out_manifolds.push(manifold);
1179                        push_pair = true;
1180                    }
1181                }
1182            }
1183
1184            if push_pair {
1185                out_contact_pairs.push(EdgeIndex::new(pair_id as u32));
1186            }
1187        }
1188    }
1189}
1190
1191#[cfg(test)]
1192#[cfg(feature = "f32")]
1193#[cfg(feature = "dim3")]
1194mod test {
1195    use na::vector;
1196
1197    use crate::prelude::{
1198        CCDSolver, ColliderBuilder, DefaultBroadPhase, IntegrationParameters, PhysicsPipeline,
1199        RigidBodyBuilder,
1200    };
1201
1202    use super::*;
1203
1204    /// Test for https://github.com/dimforge/rapier/issues/734.
1205    #[test]
1206    pub fn collider_set_parent_depenetration() {
1207        // This tests the scenario:
1208        // 1. Body A has two colliders attached (and overlapping), Body B has none.
1209        // 2. One of the colliders from Body A gets re-parented to Body B.
1210        //    -> Collision is properly detected between the colliders of A and B.
1211        let mut rigid_body_set = RigidBodySet::new();
1212        let mut collider_set = ColliderSet::new();
1213
1214        /* Create the ground. */
1215        let collider = ColliderBuilder::ball(0.5);
1216
1217        /* Create body 1, which will contain both colliders at first. */
1218        let rigid_body_1 = RigidBodyBuilder::dynamic()
1219            .translation(vector![0.0, 0.0, 0.0])
1220            .build();
1221        let body_1_handle = rigid_body_set.insert(rigid_body_1);
1222
1223        /* Create collider 1. Parent it to rigid body 1. */
1224        let collider_1_handle =
1225            collider_set.insert_with_parent(collider.build(), body_1_handle, &mut rigid_body_set);
1226
1227        /* Create collider 2. Parent it to rigid body 1. */
1228        let collider_2_handle =
1229            collider_set.insert_with_parent(collider.build(), body_1_handle, &mut rigid_body_set);
1230
1231        /* Create body 2. No attached colliders yet. */
1232        let rigid_body_2 = RigidBodyBuilder::dynamic()
1233            .translation(vector![0.0, 0.0, 0.0])
1234            .build();
1235        let body_2_handle = rigid_body_set.insert(rigid_body_2);
1236
1237        /* Create other structures necessary for the simulation. */
1238        let gravity = vector![0.0, 0.0, 0.0];
1239        let integration_parameters = IntegrationParameters::default();
1240        let mut physics_pipeline = PhysicsPipeline::new();
1241        let mut island_manager = IslandManager::new();
1242        let mut broad_phase = DefaultBroadPhase::new();
1243        let mut narrow_phase = NarrowPhase::new();
1244        let mut impulse_joint_set = ImpulseJointSet::new();
1245        let mut multibody_joint_set = MultibodyJointSet::new();
1246        let mut ccd_solver = CCDSolver::new();
1247        let physics_hooks = ();
1248        let event_handler = ();
1249
1250        physics_pipeline.step(
1251            &gravity,
1252            &integration_parameters,
1253            &mut island_manager,
1254            &mut broad_phase,
1255            &mut narrow_phase,
1256            &mut rigid_body_set,
1257            &mut collider_set,
1258            &mut impulse_joint_set,
1259            &mut multibody_joint_set,
1260            &mut ccd_solver,
1261            &physics_hooks,
1262            &event_handler,
1263        );
1264        let collider_1_position = collider_set.get(collider_1_handle).unwrap().pos;
1265        let collider_2_position = collider_set.get(collider_2_handle).unwrap().pos;
1266        assert!(
1267            (collider_1_position.translation.vector - collider_2_position.translation.vector)
1268                .magnitude()
1269                < 0.5f32
1270        );
1271
1272        let contact_pair = narrow_phase
1273            .contact_pair(collider_1_handle, collider_2_handle)
1274            .expect("The contact pair should exist.");
1275        assert_eq!(contact_pair.manifolds.len(), 0);
1276        assert!(
1277            narrow_phase
1278                .intersection_pair(collider_1_handle, collider_2_handle)
1279                .is_none(),
1280            "Interaction pair is for sensors"
1281        );
1282        /* Parent collider 2 to body 2. */
1283        collider_set.set_parent(collider_2_handle, Some(body_2_handle), &mut rigid_body_set);
1284
1285        physics_pipeline.step(
1286            &gravity,
1287            &integration_parameters,
1288            &mut island_manager,
1289            &mut broad_phase,
1290            &mut narrow_phase,
1291            &mut rigid_body_set,
1292            &mut collider_set,
1293            &mut impulse_joint_set,
1294            &mut multibody_joint_set,
1295            &mut ccd_solver,
1296            &physics_hooks,
1297            &event_handler,
1298        );
1299
1300        let contact_pair = narrow_phase
1301            .contact_pair(collider_1_handle, collider_2_handle)
1302            .expect("The contact pair should exist.");
1303        assert_eq!(contact_pair.manifolds.len(), 1);
1304        assert!(
1305            narrow_phase
1306                .intersection_pair(collider_1_handle, collider_2_handle)
1307                .is_none(),
1308            "Interaction pair is for sensors"
1309        );
1310
1311        /* Run the game loop, stepping the simulation once per frame. */
1312        for _ in 0..200 {
1313            physics_pipeline.step(
1314                &gravity,
1315                &integration_parameters,
1316                &mut island_manager,
1317                &mut broad_phase,
1318                &mut narrow_phase,
1319                &mut rigid_body_set,
1320                &mut collider_set,
1321                &mut impulse_joint_set,
1322                &mut multibody_joint_set,
1323                &mut ccd_solver,
1324                &physics_hooks,
1325                &event_handler,
1326            );
1327
1328            let collider_1_position = collider_set.get(collider_1_handle).unwrap().pos;
1329            let collider_2_position = collider_set.get(collider_2_handle).unwrap().pos;
1330            println!("collider 1 position: {}", collider_1_position.translation);
1331            println!("collider 2 position: {}", collider_2_position.translation);
1332        }
1333
1334        let collider_1_position = collider_set.get(collider_1_handle).unwrap().pos;
1335        let collider_2_position = collider_set.get(collider_2_handle).unwrap().pos;
1336        println!("collider 2 position: {}", collider_2_position.translation);
1337        assert!(
1338            (collider_1_position.translation.vector - collider_2_position.translation.vector)
1339                .magnitude()
1340                >= 0.5f32,
1341            "colliders should no longer be penetrating."
1342        );
1343    }
1344
1345    /// Test for https://github.com/dimforge/rapier/issues/734.
1346    #[test]
1347    pub fn collider_set_parent_no_self_intersection() {
1348        // This tests the scenario:
1349        // 1. Body A and Body B each have one collider attached.
1350        //    -> There should be a collision detected between A and B.
1351        // 2. The collider from Body B gets attached to Body A.
1352        //    -> There should no longer be any collision between A and B.
1353        // 3. Re-parent one of the collider from Body A to Body B again.
1354        //    -> There should a collision again.
1355        let mut rigid_body_set = RigidBodySet::new();
1356        let mut collider_set = ColliderSet::new();
1357
1358        /* Create the ground. */
1359        let collider = ColliderBuilder::ball(0.5);
1360
1361        /* Create body 1, which will contain collider 1. */
1362        let rigid_body_1 = RigidBodyBuilder::dynamic()
1363            .translation(vector![0.0, 0.0, 0.0])
1364            .build();
1365        let body_1_handle = rigid_body_set.insert(rigid_body_1);
1366
1367        /* Create collider 1. Parent it to rigid body 1. */
1368        let collider_1_handle =
1369            collider_set.insert_with_parent(collider.build(), body_1_handle, &mut rigid_body_set);
1370
1371        /* Create body 2, which will contain collider 2 at first. */
1372        let rigid_body_2 = RigidBodyBuilder::dynamic()
1373            .translation(vector![0.0, 0.0, 0.0])
1374            .build();
1375        let body_2_handle = rigid_body_set.insert(rigid_body_2);
1376
1377        /* Create collider 2. Parent it to rigid body 2. */
1378        let collider_2_handle =
1379            collider_set.insert_with_parent(collider.build(), body_2_handle, &mut rigid_body_set);
1380
1381        /* Create other structures necessary for the simulation. */
1382        let gravity = vector![0.0, 0.0, 0.0];
1383        let integration_parameters = IntegrationParameters::default();
1384        let mut physics_pipeline = PhysicsPipeline::new();
1385        let mut island_manager = IslandManager::new();
1386        let mut broad_phase = DefaultBroadPhase::new();
1387        let mut narrow_phase = NarrowPhase::new();
1388        let mut impulse_joint_set = ImpulseJointSet::new();
1389        let mut multibody_joint_set = MultibodyJointSet::new();
1390        let mut ccd_solver = CCDSolver::new();
1391        let physics_hooks = ();
1392        let event_handler = ();
1393
1394        physics_pipeline.step(
1395            &gravity,
1396            &integration_parameters,
1397            &mut island_manager,
1398            &mut broad_phase,
1399            &mut narrow_phase,
1400            &mut rigid_body_set,
1401            &mut collider_set,
1402            &mut impulse_joint_set,
1403            &mut multibody_joint_set,
1404            &mut ccd_solver,
1405            &physics_hooks,
1406            &event_handler,
1407        );
1408
1409        let contact_pair = narrow_phase
1410            .contact_pair(collider_1_handle, collider_2_handle)
1411            .expect("The contact pair should exist.");
1412        assert_eq!(
1413            contact_pair.manifolds.len(),
1414            1,
1415            "There should be a contact manifold."
1416        );
1417
1418        let collider_1_position = collider_set.get(collider_1_handle).unwrap().pos;
1419        let collider_2_position = collider_set.get(collider_2_handle).unwrap().pos;
1420        assert!(
1421            (collider_1_position.translation.vector - collider_2_position.translation.vector)
1422                .magnitude()
1423                < 0.5f32
1424        );
1425
1426        /* Parent collider 2 to body 1. */
1427        collider_set.set_parent(collider_2_handle, Some(body_1_handle), &mut rigid_body_set);
1428        physics_pipeline.step(
1429            &gravity,
1430            &integration_parameters,
1431            &mut island_manager,
1432            &mut broad_phase,
1433            &mut narrow_phase,
1434            &mut rigid_body_set,
1435            &mut collider_set,
1436            &mut impulse_joint_set,
1437            &mut multibody_joint_set,
1438            &mut ccd_solver,
1439            &physics_hooks,
1440            &event_handler,
1441        );
1442
1443        let contact_pair = narrow_phase
1444            .contact_pair(collider_1_handle, collider_2_handle)
1445            .expect("The contact pair should no longer exist.");
1446        assert_eq!(
1447            contact_pair.manifolds.len(),
1448            0,
1449            "Colliders with same parent should not be in contact together."
1450        );
1451
1452        /* Parent collider 2 back to body 1. */
1453        collider_set.set_parent(collider_2_handle, Some(body_2_handle), &mut rigid_body_set);
1454        physics_pipeline.step(
1455            &gravity,
1456            &integration_parameters,
1457            &mut island_manager,
1458            &mut broad_phase,
1459            &mut narrow_phase,
1460            &mut rigid_body_set,
1461            &mut collider_set,
1462            &mut impulse_joint_set,
1463            &mut multibody_joint_set,
1464            &mut ccd_solver,
1465            &physics_hooks,
1466            &event_handler,
1467        );
1468
1469        let contact_pair = narrow_phase
1470            .contact_pair(collider_1_handle, collider_2_handle)
1471            .expect("The contact pair should exist.");
1472        assert_eq!(
1473            contact_pair.manifolds.len(),
1474            1,
1475            "There should be a contact manifold."
1476        );
1477    }
1478}