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}