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