avian2d/collision/narrow_phase/
system_param.rs

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