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}