avian3d/collision/narrow_phase/
system_param.rs

1#[cfg(feature = "parallel")]
2use core::cell::RefCell;
3
4use crate::{
5    collision::{
6        collider::EnlargedAabb,
7        contact_types::{ContactEdgeFlags, ContactId},
8    },
9    data_structures::{bit_vec::BitVec, pair_key::PairKey},
10    dynamics::solver::{
11        constraint_graph::ConstraintGraph,
12        islands::{BodyIslandNode, IslandId, PhysicsIslands, WakeIslands},
13        joint_graph::JointGraph,
14    },
15    prelude::*,
16};
17use bevy::{
18    ecs::{
19        entity_disabling::Disabled,
20        query::QueryData,
21        system::{SystemParam, SystemParamItem, lifetimeless::Read},
22    },
23    prelude::*,
24};
25#[cfg(feature = "parallel")]
26use thread_local::ThreadLocal;
27
28#[derive(QueryData)]
29struct ColliderQuery<C: AnyCollider> {
30    entity: Entity,
31    of: Option<Read<ColliderOf>>,
32    shape: Read<C>,
33    enlarged_aabb: Read<EnlargedAabb>,
34    position: Read<Position>,
35    rotation: Read<Rotation>,
36    transform: Option<Read<ColliderTransform>>,
37    layers: Read<CollisionLayers>,
38    friction: Option<Read<Friction>>,
39    restitution: Option<Read<Restitution>>,
40    collision_margin: Option<Read<CollisionMargin>>,
41    speculative_margin: Option<Read<SpeculativeMargin>>,
42    is_sensor: Has<Sensor>,
43}
44
45#[derive(QueryData)]
46struct RigidBodyQuery {
47    entity: Entity,
48    rb: Read<RigidBody>,
49    position: Read<Position>,
50    rotation: Read<Rotation>,
51    center_of_mass: Read<ComputedCenterOfMass>,
52    linear_velocity: Read<LinearVelocity>,
53    angular_velocity: Read<AngularVelocity>,
54    // TODO: We should define these as purely collider components and not query for them here.
55    friction: Option<Read<Friction>>,
56    restitution: Option<Read<Restitution>>,
57    collision_margin: Option<Read<CollisionMargin>>,
58    speculative_margin: Option<Read<SpeculativeMargin>>,
59}
60
61/// A system parameter for managing the narrow phase.
62///
63/// Responsibilities:
64///
65/// - Updates each active [`ContactPair`] in the [`ContactGraph`].
66/// - Sends [collision events](crate::collision::collision_events) when colliders start or stop touching.
67/// - Removes contact pairs from the [`ContactGraph`] when AABBs stop overlapping.
68/// - Adds [`ContactManifold`]s to the [`ConstraintGraph`] when they are created.
69/// - Removes [`ContactManifold`]s from the [`ConstraintGraph`] when they are destroyed.
70#[derive(SystemParam)]
71#[expect(missing_docs)]
72pub struct NarrowPhase<'w, 's, C: AnyCollider> {
73    collider_query: Query<'w, 's, ColliderQuery<C>, Without<ColliderDisabled>>,
74    colliding_entities_query: Query<'w, 's, &'static mut CollidingEntities>,
75    body_query: Query<'w, 's, RigidBodyQuery, Without<RigidBodyDisabled>>,
76    body_islands:
77        Query<'w, 's, &'static mut BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
78    pub contact_graph: ResMut<'w, ContactGraph>,
79    pub joint_graph: ResMut<'w, JointGraph>,
80    pub constraint_graph: ResMut<'w, ConstraintGraph>,
81    pub islands: Option<ResMut<'w, PhysicsIslands>>,
82    contact_status_bits: ResMut<'w, ContactStatusBits>,
83    #[cfg(feature = "parallel")]
84    thread_local_contact_status_bits: ResMut<'w, ThreadLocalContactStatusBits>,
85    pub config: Res<'w, NarrowPhaseConfig>,
86    default_friction: Res<'w, DefaultFriction>,
87    default_restitution: Res<'w, DefaultRestitution>,
88    length_unit: Res<'w, PhysicsLengthUnit>,
89    // These are scaled by the length unit.
90    default_speculative_margin: Local<'s, Scalar>,
91    contact_tolerance: Local<'s, Scalar>,
92}
93
94/// A bit vector for tracking contact status changes.
95/// Set bits correspond to contact pairs that were either added or removed.
96#[derive(Resource, Default, Deref, DerefMut)]
97pub(super) struct ContactStatusBits(pub BitVec);
98
99// TODO: We could just combine all the bit vectors into the first one
100//       instead of having a separate `ContactStatusBits` resource.
101/// A thread-local bit vector for tracking contact status changes.
102/// Set bits correspond to contact pairs that were either added or removed.
103///
104/// The thread-local bit vectors are combined with the global [`ContactStatusBits`].
105#[cfg(feature = "parallel")]
106#[derive(Resource, Default, Deref, DerefMut)]
107pub(super) struct ThreadLocalContactStatusBits(pub ThreadLocal<RefCell<BitVec>>);
108
109impl<C: AnyCollider> NarrowPhase<'_, '_, C> {
110    /// Updates the narrow phase.
111    ///
112    /// - Updates each active [`ContactPair`] in the [`ContactGraph`].
113    /// - Sends [collision events](crate::collision::collision_events) when colliders start or stop touching.
114    /// - Removes contact pairs from the [`ContactGraph`] when AABBs stop overlapping.
115    /// - Adds [`ContactManifold`]s to the [`ConstraintGraph`] when they are created.
116    /// - Removes [`ContactManifold`]s from the [`ConstraintGraph`] when they are destroyed.
117    pub fn update<H: CollisionHooks>(
118        &mut self,
119        collision_started_writer: &mut MessageWriter<CollisionStart>,
120        collision_ended_writer: &mut MessageWriter<CollisionEnd>,
121        delta_secs: Scalar,
122        hooks: &SystemParamItem<H>,
123        context: &SystemParamItem<C::Context>,
124        commands: &mut ParallelCommands,
125    ) where
126        for<'w, 's> SystemParamItem<'w, 's, H>: CollisionHooks,
127    {
128        // Cache default margins scaled by the length unit.
129        if self.config.is_changed() {
130            *self.default_speculative_margin =
131                self.length_unit.0 * self.config.default_speculative_margin;
132            *self.contact_tolerance = self.length_unit.0 * self.config.contact_tolerance;
133        }
134
135        // Update contacts for all contact pairs.
136        self.update_contacts::<H>(delta_secs, hooks, context, commands);
137
138        let mut islands_to_wake: Vec<IslandId> = Vec::with_capacity(128);
139
140        // Process contact status changes, iterating over set bits serially to maintain determinism.
141        //
142        // Iterating over set bits is done efficiently with the "count trailing zeros" method:
143        // https://lemire.me/blog/2018/02/21/iterating-over-set-bits-quickly/
144        for (i, mut bits) in self.contact_status_bits.blocks().enumerate() {
145            while bits != 0 {
146                let trailing_zeros = bits.trailing_zeros();
147                let contact_id = ContactId(i as u32 * 64 + trailing_zeros);
148
149                let (contact_edge, contact_pair) = self
150                    .contact_graph
151                    .get_mut_by_id(contact_id)
152                    .unwrap_or_else(|| panic!("Contact pair not found for {contact_id:?}"));
153
154                // Three options:
155                // 1. The AABBs are no longer overlapping, and the contact pair should be removed.
156                // 2. The colliders started touching, and a collision started event should be sent.
157                // 3. The colliders stopped touching, and a collision ended event should be sent.
158                if contact_pair.aabbs_disjoint() {
159                    // Send a collision ended event if the contact pair was touching.
160                    let send_event = contact_edge
161                        .flags
162                        .contains(ContactEdgeFlags::TOUCHING | ContactEdgeFlags::CONTACT_EVENTS);
163                    if send_event {
164                        collision_ended_writer.write(CollisionEnd {
165                            collider1: contact_pair.collider1,
166                            collider2: contact_pair.collider2,
167                            body1: contact_pair.body1,
168                            body2: contact_pair.body2,
169                        });
170                    }
171
172                    // Remove from `CollidingEntities`.
173                    Self::remove_colliding_entities(
174                        &mut self.colliding_entities_query,
175                        contact_pair.collider1,
176                        contact_pair.collider2,
177                    );
178
179                    let pair_key = PairKey::new(
180                        contact_pair.collider1.index_u32(),
181                        contact_pair.collider2.index_u32(),
182                    );
183
184                    if contact_pair.generates_constraints()
185                        && let (Some(body1), Some(body2)) = (contact_pair.body1, contact_pair.body2)
186                    {
187                        let has_island = contact_edge.island.is_some();
188
189                        // Remove the contact pair from the constraint graph.
190                        for _ in 0..contact_edge.constraint_handles.len() {
191                            self.constraint_graph.pop_manifold(
192                                &mut self.contact_graph.edges,
193                                contact_id,
194                                body1,
195                                body2,
196                            );
197                        }
198
199                        // Unlink the contact pair from its island.
200                        if has_island && let Some(islands) = &mut self.islands {
201                            islands.remove_contact(
202                                contact_id,
203                                &mut self.body_islands,
204                                &mut self.contact_graph.edges,
205                                &self.joint_graph,
206                            );
207                        }
208                    }
209
210                    // Remove the contact edge from the contact graph.
211                    self.contact_graph.remove_edge_by_id(&pair_key, contact_id);
212                } else if contact_pair.collision_started() {
213                    // Send collision started event.
214                    if contact_edge.events_enabled() {
215                        collision_started_writer.write(CollisionStart {
216                            collider1: contact_pair.collider1,
217                            collider2: contact_pair.collider2,
218                            body1: contact_pair.body1,
219                            body2: contact_pair.body2,
220                        });
221                    }
222
223                    // Add to `CollidingEntities`.
224                    Self::add_colliding_entities(
225                        &mut self.colliding_entities_query,
226                        contact_pair.collider1,
227                        contact_pair.collider2,
228                    );
229
230                    debug_assert!(
231                        !contact_pair.manifolds.is_empty(),
232                        "Manifolds should not be empty when colliders start touching"
233                    );
234
235                    contact_edge.flags.set(ContactEdgeFlags::TOUCHING, true);
236                    contact_pair
237                        .flags
238                        .set(ContactPairFlags::STARTED_TOUCHING, false);
239
240                    if contact_pair.generates_constraints() {
241                        // Add the contact pair to the constraint graph.
242                        for _ in contact_pair.manifolds.iter() {
243                            self.constraint_graph
244                                .push_manifold(contact_edge, contact_pair);
245                        }
246
247                        // Link the contact pair to an island.
248                        if let Some(islands) = &mut self.islands {
249                            let island = islands.add_contact(
250                                contact_id,
251                                &mut self.body_islands,
252                                &mut self.contact_graph,
253                                &mut self.joint_graph,
254                            );
255
256                            if let Some(island) = island
257                                && island.is_sleeping
258                            {
259                                // Wake up the island if it was previously sleeping.
260                                islands_to_wake.push(island.id);
261                            }
262                        }
263                    }
264                } else if contact_pair
265                    .flags
266                    .contains(ContactPairFlags::STOPPED_TOUCHING)
267                {
268                    // Send collision ended event.
269                    if contact_edge.events_enabled() {
270                        collision_ended_writer.write(CollisionEnd {
271                            collider1: contact_pair.collider1,
272                            collider2: contact_pair.collider2,
273                            body1: contact_pair.body1,
274                            body2: contact_pair.body2,
275                        });
276                    }
277
278                    // Remove from `CollidingEntities`.
279                    Self::remove_colliding_entities(
280                        &mut self.colliding_entities_query,
281                        contact_pair.collider1,
282                        contact_pair.collider2,
283                    );
284
285                    debug_assert!(
286                        contact_pair.manifolds.is_empty(),
287                        "Manifolds should be empty when colliders stopped touching"
288                    );
289
290                    contact_edge.flags.set(ContactEdgeFlags::TOUCHING, false);
291                    contact_pair
292                        .flags
293                        .set(ContactPairFlags::STOPPED_TOUCHING, false);
294
295                    // Remove the contact pair from the constraint graph.
296                    if contact_pair.generates_constraints()
297                        && !contact_edge.constraint_handles.is_empty()
298                        && let (Some(body1), Some(body2)) = (contact_pair.body1, contact_pair.body2)
299                    {
300                        for _ in 0..contact_edge.constraint_handles.len() {
301                            self.constraint_graph.pop_manifold(
302                                &mut self.contact_graph.edges,
303                                contact_id,
304                                body1,
305                                body2,
306                            );
307                        }
308
309                        // Unlink the contact pair from its island.
310                        if let Some(islands) = &mut self.islands {
311                            let island = islands.remove_contact(
312                                contact_id,
313                                &mut self.body_islands,
314                                &mut self.contact_graph.edges,
315                                &self.joint_graph,
316                            );
317
318                            // TODO: Do we need this?
319                            if island.is_sleeping {
320                                // Wake up the island if it was previously sleeping.
321                                islands_to_wake.push(island.id);
322                            }
323                        }
324                    }
325                } else if contact_pair.is_touching()
326                    && contact_pair
327                        .flags
328                        .contains(ContactPairFlags::STARTED_GENERATING_CONSTRAINTS)
329                {
330                    // The contact pair should start generating constraints.
331                    contact_pair
332                        .flags
333                        .set(ContactPairFlags::STARTED_GENERATING_CONSTRAINTS, false);
334
335                    // Add the contact pair to the constraint graph.
336                    for _ in contact_pair.manifolds.iter() {
337                        self.constraint_graph
338                            .push_manifold(contact_edge, contact_pair);
339                    }
340
341                    // Link the contact pair to an island.
342                    if let Some(islands) = &mut self.islands {
343                        let island = islands.add_contact(
344                            contact_id,
345                            &mut self.body_islands,
346                            &mut self.contact_graph,
347                            &mut self.joint_graph,
348                        );
349
350                        if let Some(island) = island
351                            && island.is_sleeping
352                        {
353                            // Wake up the island if it was previously sleeping.
354                            islands_to_wake.push(island.id);
355                        }
356                    }
357                } else if contact_pair.is_touching()
358                    && contact_pair.generates_constraints()
359                    && contact_pair.manifold_count_change > 0
360                {
361                    // The contact pair is still touching, but the manifold count has increased.
362                    // Add the new manifolds to the constraint graph.
363                    for _ in 0..contact_pair.manifold_count_change {
364                        self.constraint_graph
365                            .push_manifold(contact_edge, contact_pair);
366                    }
367                    contact_pair.manifold_count_change = 0;
368                } else if contact_pair.is_touching()
369                    && contact_pair.generates_constraints()
370                    && contact_pair.manifold_count_change < 0
371                {
372                    // The contact pair is still touching, but the manifold count has decreased.
373                    // Remove the excess manifolds from the constraint graph.
374                    let removal_count = contact_pair.manifold_count_change.unsigned_abs() as usize;
375                    contact_pair.manifold_count_change = 0;
376
377                    if let (Some(body1), Some(body2)) = (contact_pair.body1, contact_pair.body2) {
378                        for _ in 0..removal_count {
379                            self.constraint_graph.pop_manifold(
380                                &mut self.contact_graph.edges,
381                                contact_id,
382                                body1,
383                                body2,
384                            );
385                        }
386                    }
387                }
388
389                // Clear the least significant set bit.
390                bits &= bits - 1;
391            }
392        }
393
394        if !islands_to_wake.is_empty() {
395            islands_to_wake.sort_unstable();
396            islands_to_wake.dedup();
397
398            // Wake up the islands that were previously sleeping.
399            commands.command_scope(|mut commands| {
400                commands.queue(WakeIslands(islands_to_wake));
401            });
402        }
403    }
404
405    /// Adds the colliding entities to their respective [`CollidingEntities`] components.
406    fn add_colliding_entities(
407        query: &mut Query<&mut CollidingEntities>,
408        entity1: Entity,
409        entity2: Entity,
410    ) {
411        if let Ok(mut colliding_entities1) = query.get_mut(entity1) {
412            colliding_entities1.insert(entity2);
413        }
414        if let Ok(mut colliding_entities2) = query.get_mut(entity2) {
415            colliding_entities2.insert(entity1);
416        }
417    }
418
419    /// Removes the colliding entities from their respective [`CollidingEntities`] components.
420    fn remove_colliding_entities(
421        query: &mut Query<&mut CollidingEntities>,
422        entity1: Entity,
423        entity2: Entity,
424    ) {
425        if let Ok(mut colliding_entities1) = query.get_mut(entity1) {
426            colliding_entities1.remove(&entity2);
427        }
428        if let Ok(mut colliding_entities2) = query.get_mut(entity2) {
429            colliding_entities2.remove(&entity1);
430        }
431    }
432
433    /// Updates contacts for all contact pairs in the [`ContactGraph`].
434    ///
435    /// Also updates the [`ContactStatusBits`] resource to track status changes for each contact pair.
436    /// Set bits correspond to contact pairs that were either added or removed,
437    /// while unset bits correspond to contact pairs that were persisted.
438    ///
439    /// The order of contact pairs is preserved.
440    fn update_contacts<H: CollisionHooks>(
441        &mut self,
442        delta_secs: Scalar,
443        hooks: &SystemParamItem<H>,
444        collider_context: &SystemParamItem<C::Context>,
445        par_commands: &mut ParallelCommands,
446    ) where
447        for<'w, 's> SystemParamItem<'w, 's, H>: CollisionHooks,
448    {
449        // Contact bit vecs must be sized based on the full contact capacity,
450        // not the number of active contact pairs, because pair indices
451        // are unstable and can be invalidated when pairs are removed.
452        let bit_count = self.contact_graph.edges.raw_edges().len();
453
454        // Clear the bit vector used to track status changes for each contact pair.
455        self.contact_status_bits.set_bit_count_and_clear(bit_count);
456
457        #[cfg(feature = "parallel")]
458        self.thread_local_contact_status_bits
459            .iter_mut()
460            .for_each(|context| {
461                let bit_vec_mut = &mut context.borrow_mut();
462                bit_vec_mut.set_bit_count_and_clear(bit_count);
463            });
464
465        // Compute contacts for all contact pairs in parallel or serially
466        // based on the `parallel` feature.
467        //
468        // Constraints are also generated for each contact pair that is touching
469        // or expected to start touching.
470        //
471        // If parallelism is used, status changes are written to thread-local bit vectors,
472        // where set bits correspond to contact pairs that were added or removed.
473        // At the end, the bit vectors are combined using bit-wise OR.
474        //
475        // If parallelism is not used, status changes are instead written
476        // directly to the global bit vector.
477        //
478        // TODO: An alternative to thread-local bit vectors could be to have one larger bit vector
479        //       and to chunk it into smaller bit vectors for each thread. Might not be any faster though.
480        crate::utils::par_for_each(self.contact_graph.active_pairs_mut(), 64, |_i, contacts| {
481            let contact_id = contacts.contact_id.0 as usize;
482
483            #[cfg(not(feature = "parallel"))]
484            let status_change_bits = &mut self.contact_status_bits;
485
486            // TODO: Move this out of the chunk iteration? Requires refactoring `par_for_each!`.
487            #[cfg(feature = "parallel")]
488            // Get the thread-local narrow phase context.
489            let mut thread_context = self
490                .thread_local_contact_status_bits
491                .get_or(|| {
492                    // No thread-local bit vector exists for this thread yet.
493                    // Create a new one with the same capacity as the global bit vector.
494                    let mut contact_status_bits = BitVec::new(bit_count);
495                    contact_status_bits.set_bit_count_and_clear(bit_count);
496                    RefCell::new(contact_status_bits)
497                })
498                .borrow_mut();
499            #[cfg(feature = "parallel")]
500            let status_change_bits = &mut *thread_context;
501
502            // Get the colliders for the contact pair.
503            let Ok([collider1, collider2]) = self
504                .collider_query
505                .get_many([contacts.collider1, contacts.collider2])
506            else {
507                return;
508            };
509
510            // Check if the AABBs of the colliders still overlap and the contact pair is valid.
511            let overlap = collider1.enlarged_aabb.intersects(collider2.enlarged_aabb);
512
513            // Also check if the collision layers are still compatible and the contact pair is valid.
514            // TODO: Ideally, we would have fine-grained change detection for `CollisionLayers`
515            //       rather than checking it for every pair here.
516            if !overlap || !collider1.layers.interacts_with(*collider2.layers) {
517                // The AABBs no longer overlap. The contact pair should be removed.
518                contacts.flags.set(ContactPairFlags::DISJOINT_AABB, true);
519                status_change_bits.set(contact_id);
520            } else {
521                // The AABBs overlap. Compute contacts.
522
523                let body1_bundle = collider1
524                    .of
525                    .and_then(|&ColliderOf { body }| self.body_query.get(body).ok());
526                let body2_bundle = collider2
527                    .of
528                    .and_then(|&ColliderOf { body }| self.body_query.get(body).ok());
529
530                // The rigid body's friction, restitution, collision margin, and speculative margin
531                // will be used if the collider doesn't have them specified.
532                let (
533                    is_static1,
534                    collider_offset1,
535                    world_com1,
536                    mut lin_vel1,
537                    ang_vel1,
538                    rb_friction1,
539                    rb_collision_margin1,
540                    rb_speculative_margin1,
541                ) = body1_bundle
542                    .as_ref()
543                    .map(|body| {
544                        (
545                            body.rb.is_static(),
546                            collider1.position.0 - body.position.0,
547                            body.rotation * body.center_of_mass.0,
548                            body.linear_velocity.0,
549                            body.angular_velocity.0,
550                            body.friction,
551                            body.collision_margin,
552                            body.speculative_margin,
553                        )
554                    })
555                    .unwrap_or_default();
556                let (
557                    is_static2,
558                    collider_offset2,
559                    world_com2,
560                    mut lin_vel2,
561                    ang_vel2,
562                    rb_friction2,
563                    rb_collision_margin2,
564                    rb_speculative_margin2,
565                ) = body2_bundle
566                    .as_ref()
567                    .map(|body| {
568                        (
569                            body.rb.is_static(),
570                            collider2.position.0 - body.position.0,
571                            body.rotation * body.center_of_mass.0,
572                            body.linear_velocity.0,
573                            body.angular_velocity.0,
574                            body.friction,
575                            body.collision_margin,
576                            body.speculative_margin,
577                        )
578                    })
579                    .unwrap_or_default();
580
581                // Store these to avoid having to query for the bodies
582                // when processing status changes for the constraint graph.
583                contacts.flags.set(ContactPairFlags::STATIC1, is_static1);
584                contacts.flags.set(ContactPairFlags::STATIC2, is_static2);
585
586                // If either collider is a sensor or either body is disabled, no constraints should be generated.
587                let is_disabled = body1_bundle.is_none()
588                    || body2_bundle.is_none()
589                    || collider1.is_sensor
590                    || collider2.is_sensor;
591
592                if !is_disabled && !contacts.generates_constraints() {
593                    // This can happen when a sensor is removed, or when a collider is attached to a body.
594                    contacts
595                        .flags
596                        .set(ContactPairFlags::STARTED_GENERATING_CONSTRAINTS, true);
597                    status_change_bits.set(contact_id);
598                }
599
600                contacts
601                    .flags
602                    .set(ContactPairFlags::GENERATE_CONSTRAINTS, !is_disabled);
603
604                // Get combined friction and restitution coefficients of the colliders
605                // or the bodies they are attached to. Fall back to the global defaults.
606                let friction = collider1
607                    .friction
608                    .or(rb_friction1)
609                    .copied()
610                    .unwrap_or(self.default_friction.0)
611                    .combine(
612                        collider2
613                            .friction
614                            .or(rb_friction2)
615                            .copied()
616                            .unwrap_or(self.default_friction.0),
617                    )
618                    .dynamic_coefficient;
619                let restitution = collider1
620                    .restitution
621                    .copied()
622                    .unwrap_or(self.default_restitution.0)
623                    .combine(
624                        collider2
625                            .restitution
626                            .copied()
627                            .unwrap_or(self.default_restitution.0),
628                    )
629                    .coefficient;
630
631                // Use the collider's own collision margin if specified, and fall back to the body's
632                // collision margin.
633                //
634                // The collision margin adds artificial thickness to colliders for performance
635                // and stability. See the `CollisionMargin` documentation for more details.
636                let collision_margin1 = collider1
637                    .collision_margin
638                    .or(rb_collision_margin1)
639                    .map_or(0.0, |margin| margin.0);
640                let collision_margin2 = collider2
641                    .collision_margin
642                    .or(rb_collision_margin2)
643                    .map_or(0.0, |margin| margin.0);
644                let collision_margin_sum = collision_margin1 + collision_margin2;
645
646                // Use the collider's own speculative margin if specified, and fall back to the body's
647                // speculative margin.
648                //
649                // The speculative margin is used to predict contacts that might happen during the frame.
650                // This is used for speculative collision. See the CCD and `SpeculativeMargin` documentation
651                // for more details.
652                let speculative_margin1 = collider1
653                    .speculative_margin
654                    .map_or(rb_speculative_margin1.map(|margin| margin.0), |margin| {
655                        Some(margin.0)
656                    });
657                let speculative_margin2 = collider2
658                    .speculative_margin
659                    .map_or(rb_speculative_margin2.map(|margin| margin.0), |margin| {
660                        Some(margin.0)
661                    });
662
663                let relative_linear_velocity: Vector;
664
665                // Compute the effective speculative margin, clamping it based on velocities and the maximum bound.
666                let effective_speculative_margin = {
667                    let speculative_margin1 =
668                        speculative_margin1.unwrap_or(*self.default_speculative_margin);
669                    let speculative_margin2 =
670                        speculative_margin2.unwrap_or(*self.default_speculative_margin);
671                    let inv_delta_secs = delta_secs.recip();
672
673                    // Clamp velocities to the maximum speculative margins.
674                    if speculative_margin1 < Scalar::MAX {
675                        lin_vel1 = lin_vel1.clamp_length_max(speculative_margin1 * inv_delta_secs);
676                    }
677                    if speculative_margin2 < Scalar::MAX {
678                        lin_vel2 = lin_vel2.clamp_length_max(speculative_margin2 * inv_delta_secs);
679                    }
680
681                    // Compute the effective margin based on how much the bodies
682                    // are expected to move relative to each other.
683                    relative_linear_velocity = lin_vel2 - lin_vel1;
684                    delta_secs * relative_linear_velocity.length()
685                };
686
687                // The maximum distance at which contacts are detected.
688                // At least as large as the contact tolerance.
689                let max_contact_distance = effective_speculative_margin
690                    .max(*self.contact_tolerance)
691                    + collision_margin_sum;
692
693                let was_touching = contacts.flags.contains(ContactPairFlags::TOUCHING);
694
695                // Save the old manifolds for warm starting.
696                let old_manifolds = contacts.manifolds.clone();
697
698                // TODO: It'd be good to persist the manifolds and let Parry match contacts.
699                //       This isn't currently done because it requires using Parry's contact manifold type.
700                // Compute the contact manifolds using the effective speculative margin.
701                let context = ContactManifoldContext::new(
702                    collider1.entity,
703                    collider2.entity,
704                    collider_context,
705                );
706                collider1.shape.contact_manifolds_with_context(
707                    collider2.shape,
708                    collider1.position.0,
709                    *collider1.rotation,
710                    collider2.position.0,
711                    *collider2.rotation,
712                    max_contact_distance,
713                    &mut contacts.manifolds,
714                    context,
715                );
716
717                // Transform and prune contact data.
718                contacts.manifolds.retain_mut(|manifold| {
719                    // Set the initial surface properties.
720                    manifold.friction = friction;
721                    manifold.restitution = restitution;
722                    #[cfg(feature = "2d")]
723                    {
724                        manifold.tangent_speed = 0.0;
725                    }
726                    #[cfg(feature = "3d")]
727                    {
728                        manifold.tangent_velocity = Vector::ZERO;
729                    }
730
731                    let normal = manifold.normal;
732
733                    // Transform and prune contact points.
734                    manifold.retain_points_mut(|point| {
735                        // Transform contact points to be relative to the centers of mass of the bodies.
736                        point.anchor1 = point.anchor1 + collider_offset1 - world_com1;
737                        point.anchor2 = point.anchor2 + collider_offset2 - world_com2;
738
739                        // Add the collision margin to the penetration depth.
740                        point.penetration += collision_margin_sum;
741
742                        // Compute the relative velocity along the contact normal.
743                        #[cfg(feature = "2d")]
744                        let relative_velocity = relative_linear_velocity
745                            + ang_vel2 * point.anchor2.perp()
746                            - ang_vel1 * point.anchor1.perp();
747                        #[cfg(feature = "3d")]
748                        let relative_velocity = relative_linear_velocity
749                            + ang_vel2.cross(point.anchor2)
750                            - ang_vel1.cross(point.anchor1);
751                        let normal_speed = relative_velocity.dot(normal);
752                        point.normal_speed = normal_speed;
753
754                        // Keep the contact if (1) the separation distance is below the required threshold,
755                        // or if (2) the bodies are expected to come into contact within the next time step.
756                        -point.penetration < effective_speculative_margin || {
757                            let delta_distance = normal_speed * delta_secs;
758                            delta_distance - point.penetration < effective_speculative_margin
759                        }
760                    });
761
762                    // Prune extra contact points.
763                    #[cfg(feature = "3d")]
764                    if manifold.points.len() > 4 {
765                        manifold.prune_points();
766                    }
767
768                    !manifold.points.is_empty()
769                });
770
771                // Check if the colliders are now touching.
772                let mut touching = !contacts.manifolds.is_empty();
773
774                if touching && contacts.flags.contains(ContactPairFlags::MODIFY_CONTACTS) {
775                    par_commands.command_scope(|mut commands| {
776                        touching = hooks.modify_contacts(contacts, &mut commands);
777                    });
778                    if !touching {
779                        contacts.manifolds.clear();
780                    }
781                }
782
783                contacts.flags.set(ContactPairFlags::TOUCHING, touching);
784
785                // TODO: Manifold reduction
786
787                // TODO: This condition is pretty arbitrary, mainly to skip dense trimeshes.
788                //       If we let Parry handle contact matching, this wouldn't be needed.
789                if contacts.manifolds.len() <= 4 && self.config.match_contacts {
790                    // TODO: Cache this?
791                    let distance_threshold = 0.1 * self.length_unit.0;
792
793                    for manifold in contacts.manifolds.iter_mut() {
794                        for previous_manifold in old_manifolds.iter() {
795                            manifold.match_contacts(&previous_manifold.points, distance_threshold);
796                        }
797                    }
798                }
799
800                // Record how many manifolds were added or removed.
801                // This is used to add or remove contact constraints in the `ConstraintGraph` for persistent contacts.
802                contacts.manifold_count_change =
803                    (contacts.manifolds.len() as i32 - old_manifolds.len() as i32) as i16;
804
805                // TODO: For unmatched contacts, apply any leftover impulses from the previous frame.
806
807                if touching && !was_touching {
808                    // The colliders started touching.
809                    contacts.flags.set(ContactPairFlags::STARTED_TOUCHING, true);
810                    status_change_bits.set(contact_id);
811                } else if !touching && was_touching {
812                    // The colliders stopped touching.
813                    contacts.flags.set(ContactPairFlags::STOPPED_TOUCHING, true);
814                    status_change_bits.set(contact_id);
815                } else if contacts.manifold_count_change != 0 {
816                    // The manifold count changed, but the colliders are still touching.
817                    // This is used to add or remove contact constraints in the `ConstraintGraph`.
818                    status_change_bits.set(contact_id);
819                }
820            };
821        });
822
823        #[cfg(feature = "parallel")]
824        {
825            // Combine the thread-local bit vectors serially using bit-wise OR.
826            self.thread_local_contact_status_bits
827                .iter_mut()
828                .for_each(|context| {
829                    let contact_status_bits = context.borrow();
830                    self.contact_status_bits.or(&contact_status_bits);
831                });
832        }
833    }
834}