ncollide3d/pipeline/narrow_phase/
narrow_phase.rs

1use na::RealField;
2use slotmap::{Key, SlotMap};
3
4use crate::pipeline::narrow_phase::{
5    ContactDispatcher, ContactEvent, ContactEvents, ContactManifoldGenerator, Interaction,
6    InteractionGraph, ProximityDetector, ProximityDispatcher, ProximityEvent, ProximityEvents,
7};
8use crate::pipeline::object::{
9    CollisionObjectHandle, CollisionObjectRef, CollisionObjectSet, GeometricQueryType,
10};
11use crate::query::{ContactId, ContactManifold, Proximity};
12
13/// Collision detector dispatcher for collision objects.
14pub struct NarrowPhase<N: RealField + Copy, Handle: CollisionObjectHandle> {
15    contact_dispatcher: Box<dyn ContactDispatcher<N>>,
16    proximity_dispatcher: Box<dyn ProximityDispatcher<N>>,
17    contact_events: ContactEvents<Handle>,
18    proximity_events: ProximityEvents<Handle>,
19    id_allocator: SlotMap<ContactId, bool>,
20}
21
22impl<N: RealField + Copy, Handle: CollisionObjectHandle> NarrowPhase<N, Handle> {
23    /// Creates a new `NarrowPhase`.
24    pub fn new(
25        contact_dispatcher: Box<dyn ContactDispatcher<N>>,
26        proximity_dispatcher: Box<dyn ProximityDispatcher<N>>,
27    ) -> NarrowPhase<N, Handle> {
28        NarrowPhase {
29            contact_dispatcher,
30            proximity_dispatcher,
31            contact_events: ContactEvents::new(),
32            proximity_events: ProximityEvents::new(),
33            id_allocator: SlotMap::with_key(),
34        }
35    }
36
37    fn garbage_collect_ids(&mut self, interactions: &mut InteractionGraph<N, Handle>) {
38        for interaction in interactions.0.edge_weights_mut() {
39            match interaction {
40                Interaction::Contact(_, manifold) => {
41                    for contact in manifold.contacts() {
42                        if !contact.id.is_null() {
43                            self.id_allocator[contact.id] = true;
44                        }
45                    }
46                }
47                Interaction::Proximity(..) => {}
48            }
49        }
50
51        self.id_allocator
52            .retain(|_, is_valid| std::mem::replace(is_valid, false))
53    }
54
55    /// Update the specified contact manifold between two collision objects.
56    pub fn update_contact(
57        &mut self,
58        co1: &impl CollisionObjectRef<N>,
59        co2: &impl CollisionObjectRef<N>,
60        handle1: Handle,
61        handle2: Handle,
62        detector: &mut dyn ContactManifoldGenerator<N>,
63        manifold: &mut ContactManifold<N>,
64    ) {
65        let had_contacts = manifold.len() != 0;
66
67        if let Some(prediction) = co1
68            .query_type()
69            .contact_queries_to_prediction(co2.query_type())
70        {
71            manifold.save_cache_and_clear();
72            let _ = detector.generate_contacts(
73                &*self.contact_dispatcher,
74                &co1.position(),
75                co1.shape(),
76                None,
77                &co2.position(),
78                co2.shape(),
79                None,
80                &prediction,
81                manifold,
82            );
83
84            for contact in manifold.contacts_mut() {
85                if contact.id.is_null() {
86                    contact.id = self.id_allocator.insert(false)
87                }
88            }
89        } else {
90            panic!("Unable to compute contact between collision objects with query types different from `GeometricQueryType::Contacts(..)`.")
91        }
92
93        if manifold.len() == 0 {
94            if had_contacts {
95                self.contact_events
96                    .push(ContactEvent::Stopped(handle1, handle2));
97            }
98        } else {
99            if !had_contacts {
100                self.contact_events
101                    .push(ContactEvent::Started(handle1, handle2));
102            }
103        }
104    }
105
106    // FIXME: the fact this is public is only useful for nphysics.
107    // Perhaps the event pools should not be owned by the NarrowPhase struct?
108    #[doc(hidden)]
109    pub fn emit_proximity_event(
110        &mut self,
111        handle1: Handle,
112        handle2: Handle,
113        prev_prox: Proximity,
114        new_prox: Proximity,
115    ) {
116        if prev_prox != new_prox {
117            self.proximity_events
118                .push(ProximityEvent::new(handle1, handle2, prev_prox, new_prox));
119        }
120    }
121
122    /// Update the specified proximity between two collision objects.
123    pub fn update_proximity(
124        &mut self,
125        co1: &impl CollisionObjectRef<N>,
126        co2: &impl CollisionObjectRef<N>,
127        handle1: Handle,
128        handle2: Handle,
129        detector: &mut dyn ProximityDetector<N>,
130        curr_proximity: &mut Proximity,
131    ) {
132        if let Some(new_proximity) = detector.update(
133            &*self.proximity_dispatcher,
134            &co1.position(),
135            co1.shape(),
136            &co2.position(),
137            co2.shape(),
138            co1.query_type().query_limit() + co2.query_type().query_limit(),
139        ) {
140            self.emit_proximity_event(handle1, handle2, *curr_proximity, new_proximity);
141            *curr_proximity = new_proximity;
142        }
143    }
144
145    /// Update the specified interaction between two collision objects.
146    pub fn update_interaction(
147        &mut self,
148        co1: &impl CollisionObjectRef<N>,
149        co2: &impl CollisionObjectRef<N>,
150        handle1: Handle,
151        handle2: Handle,
152        interaction: &mut Interaction<N>,
153    ) {
154        match interaction {
155            Interaction::Contact(detector, manifold) => {
156                self.update_contact(co1, co2, handle1, handle2, &mut **detector, manifold)
157            }
158            Interaction::Proximity(detector, prox) => {
159                self.update_proximity(co1, co2, handle1, handle2, &mut **detector, prox)
160            }
161        }
162    }
163
164    /// Updates the narrow-phase by actually computing contact points and proximities between the
165    /// interactions pairs reported by the broad-phase.
166    ///
167    /// This will push relevant events to `contact_events` and `proximity_events`.
168    pub fn update<Objects>(
169        &mut self,
170        interactions: &mut InteractionGraph<N, Objects::CollisionObjectHandle>,
171        objects: &Objects,
172    ) where
173        Objects: CollisionObjectSet<N, CollisionObjectHandle = Handle>,
174    {
175        for eid in interactions.0.edge_indices() {
176            let (id1, id2) = interactions.0.edge_endpoints(eid).unwrap();
177            let handle1 = interactions.0[id1];
178            let handle2 = interactions.0[id2];
179            let co1 = objects.collision_object(handle1).unwrap();
180            let co2 = objects.collision_object(handle2).unwrap();
181            let flags1 = co1.update_flags();
182            let flags2 = co2.update_flags();
183
184            if flags1.needs_narrow_phase_update() || flags2.needs_narrow_phase_update() {
185                self.update_interaction(
186                    co1,
187                    co2,
188                    handle1,
189                    handle2,
190                    interactions.0.edge_weight_mut(eid).unwrap(),
191                )
192            }
193        }
194
195        // FIXME: don't do this at each update?
196        self.garbage_collect_ids(interactions)
197    }
198
199    /// Handles a pair of collision objects detected as either started or stopped interacting.
200    pub fn handle_interaction<Objects>(
201        &mut self,
202        interactions: &mut InteractionGraph<N, Objects::CollisionObjectHandle>,
203        objects: &Objects,
204        handle1: Objects::CollisionObjectHandle,
205        handle2: Objects::CollisionObjectHandle,
206        started: bool,
207    ) where
208        Objects: CollisionObjectSet<N, CollisionObjectHandle = Handle>,
209    {
210        let co1 = objects.collision_object(handle1).unwrap();
211        let co2 = objects.collision_object(handle2).unwrap();
212        let id1 = co1.graph_index().expect(crate::NOT_REGISTERED_ERROR);
213        let id2 = co2.graph_index().expect(crate::NOT_REGISTERED_ERROR);
214
215        if started {
216            if !interactions.0.contains_edge(id1, id2) {
217                match (co1.query_type(), co2.query_type()) {
218                    (GeometricQueryType::Contacts(..), GeometricQueryType::Contacts(..)) => {
219                        let dispatcher = &self.contact_dispatcher;
220
221                        if let Some(detector) =
222                            dispatcher.get_contact_algorithm(co1.shape(), co2.shape())
223                        {
224                            let manifold = detector.init_manifold();
225                            let _ = interactions.0.add_edge(
226                                id1,
227                                id2,
228                                Interaction::Contact(detector, manifold),
229                            );
230                        }
231                    }
232                    (_, GeometricQueryType::Proximity(_))
233                    | (GeometricQueryType::Proximity(_), _) => {
234                        let dispatcher = &self.proximity_dispatcher;
235
236                        if let Some(detector) =
237                            dispatcher.get_proximity_algorithm(co1.shape(), co2.shape())
238                        {
239                            let _ = interactions.0.add_edge(
240                                id1,
241                                id2,
242                                Interaction::Proximity(detector, Proximity::Disjoint),
243                            );
244                        }
245                    }
246                }
247            }
248        } else {
249            if let Some(eid) = interactions.0.find_edge(id1, id2) {
250                let endpoints = interactions.0.edge_endpoints(eid).unwrap();
251                let handle1 = *interactions.0.node_weight(endpoints.0).unwrap();
252                let handle2 = *interactions.0.node_weight(endpoints.1).unwrap();
253
254                if let Some(detector) = interactions.0.remove_edge(eid) {
255                    match detector {
256                        Interaction::Contact(_, mut manifold) => {
257                            // Register a collision lost event if there was a contact.
258                            if manifold.len() != 0 {
259                                self.contact_events
260                                    .push(ContactEvent::Stopped(handle1, handle2));
261                            }
262
263                            manifold.clear();
264                        }
265                        Interaction::Proximity(_, prev_prox) => {
266                            // Register a proximity lost signal if they were not disjoint.
267                            self.emit_proximity_event(
268                                handle1,
269                                handle2,
270                                prev_prox,
271                                Proximity::Disjoint,
272                            );
273                        }
274                    }
275                }
276            }
277        }
278    }
279
280    /// The set of contact events generated by this narrow-phase.
281    pub fn contact_events(&self) -> &ContactEvents<Handle> {
282        &self.contact_events
283    }
284
285    /// The set of proximity events generated by this narrow-phase.
286    pub fn proximity_events(&self) -> &ProximityEvents<Handle> {
287        &self.proximity_events
288    }
289
290    /// Clear the events generated by this narrow-phase.
291    pub fn clear_events(&mut self) {
292        self.contact_events.clear();
293        self.proximity_events.clear();
294    }
295}