avian3d/collision/narrow_phase/
system_param.rs

1#[cfg(feature = "parallel")]
2use core::cell::RefCell;
3
4use crate::{
5    collision::collider::ColliderQuery,
6    data_structures::{bit_vec::BitVec, graph::EdgeIndex},
7    dynamics::solver::{contact::ContactConstraint, ContactSoftnessCoefficients},
8    prelude::*,
9};
10use bevy::{
11    ecs::system::{SystemParam, SystemParamItem},
12    prelude::*,
13};
14use dynamics::solver::{
15    contact::{ContactConstraintPoint, ContactNormalPart, ContactTangentPart},
16    ContactConstraints,
17};
18#[cfg(feature = "parallel")]
19use thread_local::ThreadLocal;
20
21/// A system parameter for managing the narrow phase.
22///
23/// Responsibilities:
24///
25/// - Updates contacts for each contact pair in the [`ContactGraph`].
26/// - Sends collision events when colliders start or stop touching.
27/// - Removes contact pairs from the [`ContactGraph`] when AABBs stop overlapping.
28/// - Generates contact constraints for each contact pair that is touching or expected to start touching.
29#[derive(SystemParam)]
30#[expect(missing_docs)]
31pub struct NarrowPhase<'w, 's, C: AnyCollider> {
32    pub collider_query: Query<'w, 's, ColliderQuery<C>, Without<ColliderDisabled>>,
33    pub colliding_entities_query: Query<'w, 's, &'static mut CollidingEntities>,
34    pub body_query: Query<
35        'w,
36        's,
37        (
38            RigidBodyQueryReadOnly,
39            Option<&'static CollisionMargin>,
40            Option<&'static SpeculativeMargin>,
41        ),
42        Without<RigidBodyDisabled>,
43    >,
44    pub contact_graph: ResMut<'w, ContactGraph>,
45    contact_status_bits: ResMut<'w, ContactStatusBits>,
46    pub contact_constraints: ResMut<'w, ContactConstraints>,
47    #[cfg(feature = "parallel")]
48    thread_locals: ResMut<'w, NarrowPhaseThreadLocals>,
49    pub config: Res<'w, NarrowPhaseConfig>,
50    default_friction: Res<'w, DefaultFriction>,
51    default_restitution: Res<'w, DefaultRestitution>,
52    contact_softness: Res<'w, ContactSoftnessCoefficients>,
53    length_unit: Res<'w, PhysicsLengthUnit>,
54    // These are scaled by the length unit.
55    default_speculative_margin: Local<'s, Scalar>,
56    contact_tolerance: Local<'s, Scalar>,
57}
58
59/// A bit vector for tracking contact status changes.
60/// Set bits correspond to contact pairs that were either added or removed.
61#[derive(Resource, Default, Deref, DerefMut)]
62pub(super) struct ContactStatusBits(pub BitVec);
63
64/// A resource storing thread-local context for the narrow phase.
65#[cfg(feature = "parallel")]
66#[derive(Resource, Default, Deref, DerefMut)]
67pub(super) struct NarrowPhaseThreadLocals(pub ThreadLocal<RefCell<NarrowPhaseThreadContext>>);
68
69/// A thread-local context for the narrow phase.
70#[cfg(feature = "parallel")]
71#[derive(Default)]
72pub(super) struct NarrowPhaseThreadContext {
73    /// A bit vector for tracking contact status changes.
74    /// Set bits correspond to contact pairs that were either added or removed.
75    ///
76    /// The thread-local bit vectors are combined with the global [`ContactStatusBits`].
77    pub contact_status_bits: BitVec,
78    /// A vector for storing generated contact constraints.
79    ///
80    /// The thread-local constraints are combined with the global [`ContactConstraints`].
81    pub contact_constraints: Vec<ContactConstraint>,
82}
83
84impl<C: AnyCollider> NarrowPhase<'_, '_, C> {
85    /// Updates the narrow phase.
86    ///
87    /// - Updates contacts for each contact pair in the [`ContactGraph`].
88    /// - Sends collision events when colliders start or stop touching.
89    /// - Removes pairs from the [`ContactGraph`] when AABBs stop overlapping.
90    pub fn update<H: CollisionHooks>(
91        &mut self,
92        collision_started_event_writer: &mut EventWriter<CollisionStarted>,
93        collision_ended_event_writer: &mut EventWriter<CollisionEnded>,
94        delta_secs: Scalar,
95        hooks: &SystemParamItem<H>,
96        context: &SystemParamItem<C::Context>,
97        commands: &mut ParallelCommands,
98    ) where
99        for<'w, 's> SystemParamItem<'w, 's, H>: CollisionHooks,
100    {
101        // Cache default margins scaled by the length unit.
102        if self.config.is_changed() {
103            *self.default_speculative_margin =
104                self.length_unit.0 * self.config.default_speculative_margin;
105            *self.contact_tolerance = self.length_unit.0 * self.config.contact_tolerance;
106        }
107
108        // Update contacts for all contact pairs.
109        self.update_contacts::<H>(delta_secs, hooks, context, commands);
110
111        // Contact pairs that should be removed.
112        // TODO: This is needed because removing pairs while iterating over the bit vec can invalidate indices.
113        //       With a stable mapping between contact pair indices and bits, we could remove this.
114        // TODO: Pre-allocate this with some reasonable capacity?
115        let mut pairs_to_remove = Vec::<(Entity, Entity)>::new();
116
117        // Process contact status changes, iterating over set bits serially to maintain determinism.
118        //
119        // Iterating over set bits is done efficiently with the "count trailing zeros" method:
120        // https://lemire.me/blog/2018/02/21/iterating-over-set-bits-quickly/
121        for (i, mut bits) in self.contact_status_bits.blocks().enumerate() {
122            while bits != 0 {
123                let trailing_zeros = bits.trailing_zeros();
124                let pair_index = EdgeIndex(i as u32 * 64 + trailing_zeros);
125
126                let contact_pair = self
127                    .contact_graph
128                    .internal
129                    .edge_weight_mut(pair_index)
130                    .unwrap_or_else(|| panic!("Contact pair not found for {pair_index:?}"));
131
132                // Three options:
133                // 1. The AABBs are no longer overlapping, and the contact pair should be removed.
134                // 2. The colliders started touching, and a collision started event should be sent.
135                // 3. The colliders stopped touching, and a collision ended event should be sent.
136                if contact_pair.aabbs_disjoint() {
137                    // Send a collision ended event if the contact pair was touching.
138                    let send_event = contact_pair
139                        .flags
140                        .contains(ContactPairFlags::TOUCHING | ContactPairFlags::CONTACT_EVENTS);
141                    if send_event {
142                        collision_ended_event_writer.write(CollisionEnded(
143                            contact_pair.collider1,
144                            contact_pair.collider2,
145                        ));
146                    }
147
148                    // Remove from `CollidingEntities`.
149                    Self::remove_colliding_entities(
150                        &mut self.colliding_entities_query,
151                        contact_pair.collider1,
152                        contact_pair.collider2,
153                    );
154
155                    // Wake up the bodies.
156                    // TODO: When we have simulation islands, this will be more efficient.
157                    commands.command_scope(|mut commands| {
158                        commands.queue(WakeUpBody(
159                            contact_pair.body1.unwrap_or(contact_pair.collider1),
160                        ));
161                        commands.queue(WakeUpBody(
162                            contact_pair.body2.unwrap_or(contact_pair.collider2),
163                        ));
164                    });
165
166                    // Queue the contact pair for removal.
167                    pairs_to_remove.push((contact_pair.collider1, contact_pair.collider2));
168                } else if contact_pair.collision_started() {
169                    // Send collision started event.
170                    if contact_pair.events_enabled() {
171                        collision_started_event_writer.write(CollisionStarted(
172                            contact_pair.collider1,
173                            contact_pair.collider2,
174                        ));
175                    }
176
177                    // Add to `CollidingEntities`.
178                    Self::add_colliding_entities(
179                        &mut self.colliding_entities_query,
180                        contact_pair.collider1,
181                        contact_pair.collider2,
182                    );
183
184                    debug_assert!(
185                        !contact_pair.manifolds.is_empty(),
186                        "Manifolds should not be empty when colliders start touching"
187                    );
188
189                    contact_pair
190                        .flags
191                        .set(ContactPairFlags::STARTED_TOUCHING, false);
192                } else if contact_pair
193                    .flags
194                    .contains(ContactPairFlags::STOPPED_TOUCHING)
195                {
196                    // Send collision ended event.
197                    if contact_pair.events_enabled() {
198                        collision_ended_event_writer.write(CollisionEnded(
199                            contact_pair.collider1,
200                            contact_pair.collider2,
201                        ));
202                    }
203
204                    // Remove from `CollidingEntities`.
205                    Self::remove_colliding_entities(
206                        &mut self.colliding_entities_query,
207                        contact_pair.collider1,
208                        contact_pair.collider2,
209                    );
210
211                    // Wake up the bodies.
212                    // TODO: When we have simulation islands, this will be more efficient.
213                    commands.command_scope(|mut commands| {
214                        commands.queue(WakeUpBody(
215                            contact_pair.body1.unwrap_or(contact_pair.collider1),
216                        ));
217                        commands.queue(WakeUpBody(
218                            contact_pair.body2.unwrap_or(contact_pair.collider2),
219                        ));
220                    });
221
222                    debug_assert!(
223                        contact_pair.manifolds.is_empty(),
224                        "Manifolds should be empty when colliders stopped touching"
225                    );
226
227                    contact_pair
228                        .flags
229                        .set(ContactPairFlags::STOPPED_TOUCHING, false);
230                }
231
232                // Clear the least significant set bit.
233                bits &= bits - 1;
234            }
235        }
236
237        // Remove the contact pairs that were marked for removal.
238        for pair_index in pairs_to_remove.drain(..) {
239            self.contact_graph.remove_pair(pair_index.0, pair_index.1);
240        }
241    }
242
243    /// Adds the colliding entities to their respective [`CollidingEntities`] components.
244    fn add_colliding_entities(
245        query: &mut Query<&mut CollidingEntities>,
246        entity1: Entity,
247        entity2: Entity,
248    ) {
249        if let Ok(mut colliding_entities1) = query.get_mut(entity1) {
250            colliding_entities1.insert(entity2);
251        }
252        if let Ok(mut colliding_entities2) = query.get_mut(entity2) {
253            colliding_entities2.insert(entity1);
254        }
255    }
256
257    /// Removes the colliding entities from their respective [`CollidingEntities`] components.
258    fn remove_colliding_entities(
259        query: &mut Query<&mut CollidingEntities>,
260        entity1: Entity,
261        entity2: Entity,
262    ) {
263        if let Ok(mut colliding_entities1) = query.get_mut(entity1) {
264            colliding_entities1.remove(&entity2);
265        }
266        if let Ok(mut colliding_entities2) = query.get_mut(entity2) {
267            colliding_entities2.remove(&entity1);
268        }
269    }
270
271    /// Updates contacts for all contact pairs in the [`ContactGraph`].
272    ///
273    /// Also updates the [`ContactStatusBits`] resource to track status changes for each contact pair.
274    /// Set bits correspond to contact pairs that were either added or removed,
275    /// while unset bits correspond to contact pairs that were persisted.
276    ///
277    /// The order of contact pairs is preserved.
278    fn update_contacts<H: CollisionHooks>(
279        &mut self,
280        delta_secs: Scalar,
281        hooks: &SystemParamItem<H>,
282        collider_context: &SystemParamItem<C::Context>,
283        par_commands: &mut ParallelCommands,
284    ) where
285        for<'w, 's> SystemParamItem<'w, 's, H>: CollisionHooks,
286    {
287        let contact_pair_count = self.contact_graph.internal.edge_count();
288
289        // Clear the bit vector used to track status changes for each contact pair.
290        self.contact_status_bits
291            .set_bit_count_and_clear(contact_pair_count);
292
293        #[cfg(feature = "parallel")]
294        self.thread_locals.iter_mut().for_each(|context| {
295            let bit_vec_mut = &mut context.borrow_mut().contact_status_bits;
296            bit_vec_mut.set_bit_count_and_clear(contact_pair_count);
297        });
298
299        // Clear the contact constraints.
300        self.contact_constraints.clear();
301
302        // Compute contacts for all contact pairs in parallel or serially
303        // based on the `parallel` feature.
304        //
305        // Constraints are also generated for each contact pair that is touching
306        // or expected to start touching.
307        //
308        // If parallelism is used, status changes are written to thread-local bit vectors,
309        // where set bits correspond to contact pairs that were added or removed.
310        // At the end, the bit vectors are combined using bit-wise OR.
311        //
312        // If parallelism is not used, status changes are instead written
313        // directly to the global bit vector.
314        //
315        // TODO: An alternative to thread-local bit vectors could be to have one larger bit vector
316        //       and to chunk it into smaller bit vectors for each thread. Might not be any faster though.
317        crate::utils::par_for_each!(
318            self.contact_graph.internal.raw_edges_mut(),
319            |contact_index, contacts| {
320                #[cfg(not(feature = "parallel"))]
321                let status_change_bits = &mut self.contact_status_bits;
322                #[cfg(not(feature = "parallel"))]
323                let constraints = &mut self.contact_constraints;
324
325                // TODO: Move this out of the chunk iteration? Requires refactoring `par_for_each!`.
326                #[cfg(feature = "parallel")]
327                // Get the thread-local narrow phase context.
328                let mut thread_context = self
329                    .thread_locals
330                    .get_or(|| {
331                        // No thread-local bit vector exists for this thread yet.
332                        // Create a new one with the same capacity as the global bit vector.
333                        let mut contact_status_bits = BitVec::new(contact_pair_count);
334                        contact_status_bits.set_bit_count_and_clear(contact_pair_count);
335                        RefCell::new(NarrowPhaseThreadContext {
336                            contact_status_bits,
337                            contact_constraints: Vec::new(),
338                        })
339                    })
340                    .borrow_mut();
341                #[cfg(feature = "parallel")]
342                let NarrowPhaseThreadContext {
343                    contact_status_bits: status_change_bits,
344                    contact_constraints: constraints,
345                } = &mut *thread_context;
346
347                let contacts = &mut contacts.weight;
348
349                // Get the colliders for the contact pair.
350                let Ok([collider1, collider2]) = self
351                    .collider_query
352                    .get_many([contacts.collider1, contacts.collider2])
353                else {
354                    return;
355                };
356
357                // Check if the AABBs of the colliders still overlap and the contact pair is valid.
358                let overlap = collider1.aabb.intersects(&collider2.aabb);
359
360                // Also check if the collision layers are still compatible and the contact pair is valid.
361                // TODO: Ideally, we would have fine-grained change detection for `CollisionLayers`
362                //       rather than checking it for every pair here.
363                if !overlap || !collider1.layers.interacts_with(*collider2.layers) {
364                    // The AABBs no longer overlap. The contact pair should be removed.
365                    contacts.flags.set(ContactPairFlags::DISJOINT_AABB, true);
366                    status_change_bits.set(contact_index);
367                } else {
368                    // The AABBs overlap. Compute contacts.
369
370                    let body1_bundle = collider1
371                        .body()
372                        .and_then(|body| self.body_query.get(body).ok());
373                    let body2_bundle = collider2
374                        .body()
375                        .and_then(|body| self.body_query.get(body).ok());
376
377                    // The rigid body's friction, restitution, collision margin, and speculative margin
378                    // will be used if the collider doesn't have them specified.
379                    let (mut lin_vel1, rb_friction1, rb_collision_margin1, rb_speculative_margin1) =
380                        body1_bundle
381                            .as_ref()
382                            .map(|(body, collision_margin, speculative_margin)| {
383                                (
384                                    body.linear_velocity.0,
385                                    body.friction,
386                                    *collision_margin,
387                                    *speculative_margin,
388                                )
389                            })
390                            .unwrap_or_default();
391                    let (mut lin_vel2, rb_friction2, rb_collision_margin2, rb_speculative_margin2) =
392                        body2_bundle
393                            .as_ref()
394                            .map(|(body, collision_margin, speculative_margin)| {
395                                (
396                                    body.linear_velocity.0,
397                                    body.friction,
398                                    *collision_margin,
399                                    *speculative_margin,
400                                )
401                            })
402                            .unwrap_or_default();
403
404                    // Get combined friction and restitution coefficients of the colliders
405                    // or the bodies they are attached to. Fall back to the global defaults.
406                    let friction = collider1
407                        .friction
408                        .or(rb_friction1)
409                        .copied()
410                        .unwrap_or(self.default_friction.0)
411                        .combine(
412                            collider2
413                                .friction
414                                .or(rb_friction2)
415                                .copied()
416                                .unwrap_or(self.default_friction.0),
417                        )
418                        .dynamic_coefficient;
419                    let restitution = collider1
420                        .restitution
421                        .copied()
422                        .unwrap_or(self.default_restitution.0)
423                        .combine(
424                            collider2
425                                .restitution
426                                .copied()
427                                .unwrap_or(self.default_restitution.0),
428                        )
429                        .coefficient;
430
431                    // Use the collider's own collision margin if specified, and fall back to the body's
432                    // collision margin.
433                    //
434                    // The collision margin adds artificial thickness to colliders for performance
435                    // and stability. See the `CollisionMargin` documentation for more details.
436                    let collision_margin1 = collider1
437                        .collision_margin
438                        .or(rb_collision_margin1)
439                        .map_or(0.0, |margin| margin.0);
440                    let collision_margin2 = collider2
441                        .collision_margin
442                        .or(rb_collision_margin2)
443                        .map_or(0.0, |margin| margin.0);
444                    let collision_margin_sum = collision_margin1 + collision_margin2;
445
446                    // Use the collider's own speculative margin if specified, and fall back to the body's
447                    // speculative margin.
448                    //
449                    // The speculative margin is used to predict contacts that might happen during the frame.
450                    // This is used for speculative collision. See the CCD and `SpeculativeMargin` documentation
451                    // for more details.
452                    let speculative_margin1 = collider1
453                        .speculative_margin
454                        .map_or(rb_speculative_margin1.map(|margin| margin.0), |margin| {
455                            Some(margin.0)
456                        });
457                    let speculative_margin2 = collider2
458                        .speculative_margin
459                        .map_or(rb_speculative_margin2.map(|margin| margin.0), |margin| {
460                            Some(margin.0)
461                        });
462
463                    let relative_linear_velocity: Vector;
464
465                    // Compute the effective speculative margin, clamping it based on velocities and the maximum bound.
466                    let effective_speculative_margin = {
467                        let speculative_margin1 =
468                            speculative_margin1.unwrap_or(*self.default_speculative_margin);
469                        let speculative_margin2 =
470                            speculative_margin2.unwrap_or(*self.default_speculative_margin);
471                        let inv_delta_secs = delta_secs.recip();
472
473                        // Clamp velocities to the maximum speculative margins.
474                        if speculative_margin1 < Scalar::MAX {
475                            lin_vel1 =
476                                lin_vel1.clamp_length_max(speculative_margin1 * inv_delta_secs);
477                        }
478                        if speculative_margin2 < Scalar::MAX {
479                            lin_vel2 =
480                                lin_vel2.clamp_length_max(speculative_margin2 * inv_delta_secs);
481                        }
482
483                        // Compute the effective margin based on how much the bodies
484                        // are expected to move relative to each other.
485                        relative_linear_velocity = lin_vel2 - lin_vel1;
486                        delta_secs * relative_linear_velocity.length()
487                    };
488
489                    // The maximum distance at which contacts are detected.
490                    // At least as large as the contact tolerance.
491                    let max_contact_distance = effective_speculative_margin
492                        .max(*self.contact_tolerance)
493                        + collision_margin_sum;
494
495                    let position1 = collider1.current_position();
496                    let position2 = collider2.current_position();
497
498                    let was_touching = contacts.flags.contains(ContactPairFlags::TOUCHING);
499
500                    // Save the old manifolds for warm starting.
501                    let old_manifolds = contacts.manifolds.clone();
502
503                    // TODO: It'd be good to persist the manifolds and let Parry match contacts.
504                    //       This isn't currently done because it requires using Parry's contact manifold type.
505                    // Compute the contact manifolds using the effective speculative margin.
506                    let context = ContactManifoldContext::new(
507                        collider1.entity,
508                        collider2.entity,
509                        collider_context,
510                    );
511                    collider1.shape.contact_manifolds_with_context(
512                        collider2.shape,
513                        position1,
514                        *collider1.rotation,
515                        position2,
516                        *collider2.rotation,
517                        max_contact_distance,
518                        &mut contacts.manifolds,
519                        context,
520                    );
521
522                    // Set the initial surface properties.
523                    // TODO: This could be done in `contact_manifolds` to avoid the extra iteration.
524                    contacts.manifolds.iter_mut().for_each(|manifold| {
525                        manifold.friction = friction;
526                        manifold.restitution = restitution;
527                        #[cfg(feature = "2d")]
528                        {
529                            manifold.tangent_speed = 0.0;
530                        }
531                        #[cfg(feature = "3d")]
532                        {
533                            manifold.tangent_velocity = Vector::ZERO;
534                        }
535                    });
536
537                    // Check if the colliders are now touching.
538                    let mut touching = !contacts.manifolds.is_empty();
539
540                    if touching && contacts.flags.contains(ContactPairFlags::MODIFY_CONTACTS) {
541                        par_commands.command_scope(|mut commands| {
542                            touching = hooks.modify_contacts(contacts, &mut commands);
543                        });
544                        if !touching {
545                            contacts.manifolds.clear();
546                        }
547                    }
548
549                    contacts.flags.set(ContactPairFlags::TOUCHING, touching);
550
551                    // TODO: This condition is pretty arbitrary, mainly to skip dense trimeshes.
552                    //       If we let Parry handle contact matching, this wouldn't be needed.
553                    if contacts.manifolds.len() <= 4 && self.config.match_contacts {
554                        // TODO: Cache this?
555                        let distance_threshold = 0.1 * self.length_unit.0;
556
557                        for manifold in contacts.manifolds.iter_mut() {
558                            for previous_manifold in old_manifolds.iter() {
559                                manifold
560                                    .match_contacts(&previous_manifold.points, distance_threshold);
561                            }
562                        }
563                    }
564
565                    // TODO: For unmatched contacts, apply any leftover impulses from the previous frame.
566
567                    if touching && !was_touching {
568                        // The colliders started touching.
569                        contacts.flags.set(ContactPairFlags::STARTED_TOUCHING, true);
570                        status_change_bits.set(contact_index);
571                    } else if !touching && was_touching {
572                        // The colliders stopped touching.
573                        contacts.flags.set(ContactPairFlags::STOPPED_TOUCHING, true);
574                        status_change_bits.set(contact_index);
575                    }
576
577                    // Now, we generate contact constraints for the contact pair.
578                    let Some(((body1, _, _), (body2, _, _))) = body1_bundle.zip(body2_bundle)
579                    else {
580                        return;
581                    };
582
583                    let inactive1 = body1.rb.is_static() || body1.is_sleeping;
584                    let inactive2 = body2.rb.is_static() || body2.is_sleeping;
585
586                    // No collision response if both bodies are static or sleeping
587                    // or if either of the colliders is a sensor collider.
588                    if (inactive1 && inactive2) || contacts.is_sensor() {
589                        return;
590                    }
591
592                    // When an active body collides with a sleeping body, wake up the sleeping body.
593                    par_commands.command_scope(|mut commands| {
594                        if body1.is_sleeping {
595                            commands.queue(WakeUpBody(body1.entity));
596                        } else if body2.is_sleeping {
597                            commands.queue(WakeUpBody(body2.entity));
598                        }
599                    });
600
601                    // TODO: How should we properly take the locked axes into account for the mass here?
602                    let inverse_mass_sum = body1.mass().inverse() + body2.mass().inverse();
603                    let i1 = body1.effective_global_angular_inertia();
604                    let i2 = body2.effective_global_angular_inertia();
605
606                    let contact_softness = if inactive1 || inactive2 {
607                        self.contact_softness.non_dynamic
608                    } else {
609                        self.contact_softness.dynamic
610                    };
611
612                    // Generate a contact constraint for each contact manifold.
613                    for (manifold_index, manifold) in contacts.manifolds.iter_mut().enumerate() {
614                        let mut constraint = ContactConstraint {
615                            body1: body1.entity,
616                            body2: body2.entity,
617                            collider1: collider1.entity,
618                            collider2: collider2.entity,
619                            friction: manifold.friction,
620                            restitution: manifold.restitution,
621                            #[cfg(feature = "2d")]
622                            tangent_speed: manifold.tangent_speed,
623                            #[cfg(feature = "3d")]
624                            tangent_velocity: manifold.tangent_velocity,
625                            normal: manifold.normal,
626                            points: Vec::with_capacity(manifold.points.len()),
627                            #[cfg(feature = "parallel")]
628                            pair_index: contact_index,
629                            manifold_index,
630                        };
631
632                        let tangents = constraint
633                            .tangent_directions(body1.linear_velocity.0, body2.linear_velocity.0);
634
635                        for mut contact in manifold.points.iter().copied() {
636                            // Transform contact points from collider-space to body-space.
637                            if let Some(transform) = collider1.transform.copied() {
638                                contact.local_point1 = transform.rotation * contact.local_point1
639                                    + transform.translation;
640                            }
641                            if let Some(transform) = collider2.transform.copied() {
642                                contact.local_point2 = transform.rotation * contact.local_point2
643                                    + transform.translation;
644                            }
645
646                            contact.penetration += collision_margin_sum;
647
648                            let effective_distance = -contact.penetration;
649
650                            let local_anchor1 = contact.local_point1 - body1.center_of_mass.0;
651                            let local_anchor2 = contact.local_point2 - body2.center_of_mass.0;
652
653                            // Store fixed world-space anchors.
654                            // This improves rolling behavior for shapes like balls and capsules.
655                            let r1 = *body1.rotation * local_anchor1;
656                            let r2 = *body2.rotation * local_anchor2;
657
658                            // Relative velocity at the contact point.
659                            // body2.velocity_at_point(r2) - body1.velocity_at_point(r1)
660                            #[cfg(feature = "2d")]
661                            let relative_velocity = relative_linear_velocity
662                                + body2.angular_velocity.0 * r2.perp()
663                                - body1.angular_velocity.0 * r1.perp();
664                            #[cfg(feature = "3d")]
665                            let relative_velocity = relative_linear_velocity
666                                + body2.angular_velocity.0.cross(r2)
667                                - body1.angular_velocity.0.cross(r1);
668
669                            // Keep the contact if (1) the separation distance is below the required threshold,
670                            // or if (2) the bodies are expected to come into contact within the next frame.
671                            let normal_speed = relative_velocity.dot(constraint.normal);
672                            let keep_contact = effective_distance < effective_speculative_margin
673                                || {
674                                    let delta_distance = normal_speed * delta_secs;
675                                    effective_distance + delta_distance
676                                        < effective_speculative_margin
677                                };
678
679                            if !keep_contact {
680                                continue;
681                            }
682
683                            let point = ContactConstraintPoint {
684                                // TODO: Apply warm starting scale here instead of in `warm_start`?
685                                normal_part: ContactNormalPart::generate(
686                                    inverse_mass_sum,
687                                    i1,
688                                    i2,
689                                    r1,
690                                    r2,
691                                    constraint.normal,
692                                    self.config.match_contacts.then_some(contact.normal_impulse),
693                                    contact_softness,
694                                ),
695                                // There should only be a friction part if the coefficient of friction is non-negative.
696                                tangent_part: (friction > 0.0).then_some(
697                                    ContactTangentPart::generate(
698                                        inverse_mass_sum,
699                                        i1,
700                                        i2,
701                                        r1,
702                                        r2,
703                                        tangents,
704                                        self.config
705                                            .match_contacts
706                                            .then_some(contact.tangent_impulse),
707                                    ),
708                                ),
709                                max_normal_impulse: 0.0,
710                                local_anchor1,
711                                local_anchor2,
712                                anchor1: r1,
713                                anchor2: r2,
714                                normal_speed,
715                                initial_separation: -contact.penetration
716                                    - (r2 - r1).dot(constraint.normal),
717                            };
718
719                            constraint.points.push(point);
720                        }
721
722                        if !constraint.points.is_empty() {
723                            constraints.push(constraint);
724                        }
725                    }
726                };
727            }
728        );
729
730        #[cfg(feature = "parallel")]
731        {
732            // Combine the thread-local bit vectors serially using bit-wise OR,
733            // and drain the thread-local contact constraints into the global contact constraints.
734            self.thread_locals.iter_mut().for_each(|context| {
735                let mut context_mut = context.borrow_mut();
736                self.contact_status_bits
737                    .or(&context_mut.contact_status_bits);
738                self.contact_constraints
739                    .extend(context_mut.contact_constraints.drain(..));
740            });
741
742            // Sort the contact constraints by pair index to maintain determinism.
743            // NOTE: `sort_by_key` is faster than `sort_unstable_by_key` here,
744            //       because the constraints within chunks are already sorted.
745            // TODO: We should figure out an approach that doesn't require sorting.
746            self.contact_constraints
747                .sort_by_key(|constraint| constraint.pair_index);
748        }
749    }
750}