1use std;
4#[cfg(feature = "debug_dump")]
5use std::sync::atomic;
6use std::cmp::Reverse;
7use either::Either;
8use log;
9use sorted_vec::{SortedSet, ReverseSortedSet};
10use vec_map::VecMap;
11#[cfg(feature = "derive_serdes")]
12use serde::{Deserialize, Serialize};
13
14use geometry::Aabb3;
15use math::*;
16
17use crate::{constraint, event, geometry, math, Constraint};
18use crate::constraint::contact::CONTACT_DISTANCE;
19use crate::object::{self, InternalId, ObjectPair};
20
21pub mod proximity;
22pub mod intersection;
23mod broad;
24
25pub use self::proximity::Proximity;
26pub use self::intersection::Intersection;
27use self::broad::Broad;
28
29#[cfg(feature = "debug_dump")]
34#[cfg_attr(docsrs, doc(cfg(feature="debug_dump")))]
35pub static DEBUG_DUMP : atomic::AtomicBool = atomic::AtomicBool::new (false);
36#[cfg(feature = "debug_dump")]
37#[cfg_attr(docsrs, doc(cfg(feature="debug_dump")))]
38pub const DEBUG_DUMP_DETECT_RESOLVE_MAX_ITERS : u64 = 200;
39
40#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
46#[derive(Clone, Debug, Default)]
47pub(crate) struct Collision {
48 broad : Broad,
50 #[cfg_attr(feature = "derive_serdes", serde(skip))]
56 pipeline : Pipeline,
57 #[cfg(debug_assertions)]
59 detect_resolve_max_iter_count : u64,
60 #[cfg(debug_assertions)]
62 narrow_toi_max_iter_count : u64
63}
64
65#[derive(Clone, Debug, Default, PartialEq)]
69struct Pipeline {
70 pub detect_resolve_iter : u64,
73 pub broad_overlap_pairs : Vec <ObjectPair>,
75 pub mid_toi_pairs :
77 ReverseSortedSet <(Normalized <f64>, Normalized <f64>, ObjectPair)>,
78 pub narrow_toi_contacts :
80 ReverseSortedSet <(Normalized <f64>, ObjectPair, constraint::Contact)>,
81 pub resolved_collisions : Vec <event::CollisionResolve>,
82 pub overlaps : Vec <event::Overlap>,
84}
85
86pub fn report_sizes() {
91 use std::mem::size_of;
92 println!("collision report sizes...");
93
94 println!(" size of Collision: {}", size_of::<Collision>());
95
96 println!("...collision report sizes");
97}
98
99impl Collision {
104 pub(crate) const fn broad (&self) -> &Broad {
105 &self.broad
106 }
107 pub(crate) fn try_add_object_static (&mut self,
114 objects_dynamic : &VecMap <object::Dynamic>,
115 object : &object::Static,
116 key : object::Key
117 ) -> Result <(), Vec <(object::Id, Intersection)>> {
118 let mut intersections = Vec::new();
119 if object.collidable {
120 let aabb_discrete = object.aabb_dilated();
121 let overlaps : Vec <object::Id> = self.broad.overlaps_discrete (aabb_discrete)
122 .into_iter().map (object::Id::from).collect::<Vec <_>>();
123 for object_id in overlaps {
124 let proximity = match object_id.kind {
125 object::Kind::Static => continue, object::Kind::Dynamic => {
127 let object_dynamic = &objects_dynamic[object_id.key.index()];
128 if !object_dynamic.collidable {
129 continue }
131 Proximity::query (object_dynamic, object)
132 }
133 object::Kind::Nodetect => unreachable!()
134 };
135 if let Ok (intersection) = proximity.try_into() {
136 intersections.push ((object_id, intersection));
137 }
138 }
139 }
140 if intersections.is_empty() {
141 self.add_object_static (object, key);
142 Ok (())
143 } else {
144 Err (intersections)
145 }
146 }
147
148 #[inline]
149 pub(crate) fn remove_object_static (&mut self, object_key : object::Key) {
150 debug_assert!(self.pipeline.is_empty());
151 let id = InternalId::new_static (object_key);
152 self.broad.remove_object (id);
153 }
154
155 pub(crate) fn try_add_object_dynamic (&mut self,
162 pseudo_velocities : &mut VecMap <Vector3 <f64>>,
163 objects_static : &VecMap <object::Static>,
164 objects_dynamic : &VecMap <object::Dynamic>,
165 object : &object::Dynamic,
166 key : object::Key
167 ) -> Result <(), Vec <(object::Id, Intersection)>> {
168 let mut intersections = Vec::new();
169 if object.collidable {
170 let aabb_discrete = object.aabb_dilated();
171 let overlaps : Vec <object::Id> = self.broad.overlaps_discrete (aabb_discrete)
172 .into_iter().map (object::Id::from).collect::<Vec <_>>();
173 for object_id in overlaps {
174 let proximity = match object_id.kind {
175 object::Kind::Static => {
176 let object_static = &objects_static[object_id.key.index()];
177 if !object_static.collidable {
178 continue }
180 Proximity::query (object_static, object)
181 }
182 object::Kind::Dynamic => {
183 let object_dynamic = &objects_dynamic[object_id.key.index()];
184 if !object_dynamic.collidable {
185 continue }
187 Proximity::query (object_dynamic, object)
188 }
189 object::Kind::Nodetect => unreachable!()
190 };
191 if let Ok (intersection) = proximity.try_into() {
192 intersections.push ((object_id, intersection));
193 }
194 }
195 }
196 if intersections.is_empty() {
197 self.add_object_dynamic (pseudo_velocities, object, key);
198 Ok (())
199 } else {
200 Err (intersections)
201 }
202 }
203
204 #[inline]
205 pub(crate) fn remove_object_dynamic (&mut self, key : object::Key) {
206 debug_assert!(self.pipeline.is_empty());
207 let id = InternalId::new_dynamic (key);
208 self.broad.remove_object (id);
209 }
210
211 #[inline]
212 pub(crate) fn update_object_static (&mut self,
213 object : &object::Static, key : object::Key
214 ) {
215 debug_assert!(self.pipeline.is_empty());
216 self.broad.update_aabb_static (object.aabb_dilated(), key);
217 unimplemented!("TODO: collision detect and resolve any overlaps caused by \
218 updating the static object")
219 }
220
221 #[inline]
222 pub(crate) fn update_object_dynamic (&mut self,
223 object : &object::Dynamic, key : object::Key
224 ) {
225 debug_assert!(self.pipeline.is_empty());
226 let aabb = object.aabb_dilated();
227 self.broad.update_aabb_dynamic_discrete (aabb, key);
228 self.broad.update_aabb_dynamic_continuous (aabb, key);
230 log::warn!("TODO: collision detect and resolve any overlaps caused by \
231 updating the dynamic object")
232 }
233
234 pub(crate) fn detect_resolve_loop (&mut self,
253 constraint_manager : &mut constraint::Manager,
254 objects_static : &VecMap <object::Static>,
255 objects_dynamic : &mut VecMap <object::Dynamic>,
256 step : u64,
257 output : &mut Vec <event::Output>
258 ) {
259 log::trace!(step; "detect/resolve loop");
260
261 debug_assert!(self.pipeline.is_empty());
263 self.pipeline.detect_resolve_iter = 0;
264
265 self.broad.begin_step (objects_dynamic, step);
270
271 loop {
273 log::debug!(iter=self.pipeline.detect_resolve_iter;
274 "detect/resolve loop iteration");
275 self.detect_continuous (constraint_manager, objects_static, objects_dynamic);
277 if !self.resolve_first_collision (
280 constraint_manager, objects_static, objects_dynamic
281 ) {
282 debug_assert!(self.pipeline.mid_toi_pairs.is_empty());
283 debug_assert!(self.pipeline.narrow_toi_contacts.is_empty());
284 break
285 }
286 self.pipeline.detect_resolve_iter += 1;
287 }
288
289 log::trace!(iters=self.pipeline.detect_resolve_iter+1;
290 "detect/resolve loop complete");
291 #[cfg(debug_assertions)]
292 {
293 if self.detect_resolve_max_iter_count
294 < (self.pipeline.detect_resolve_iter + 1)
295 {
296 #[cfg(feature = "debug_dump")]
297 if self.pipeline.detect_resolve_iter >
298 DEBUG_DUMP_DETECT_RESOLVE_MAX_ITERS
299 {
300 DEBUG_DUMP.store (true, atomic::Ordering::SeqCst);
301 }
302 self.detect_resolve_max_iter_count = self.pipeline.detect_resolve_iter;
303 }
304 log::debug!(max_iter_count=self.detect_resolve_max_iter_count;
305 "detect/resolve loop max iters");
306 }
307
308 for collision_resolve in self.pipeline.resolved_collisions.drain (..) {
310 output.push (collision_resolve.into());
311 }
312 for overlap in self.pipeline.overlaps.drain (..) {
313 output.push (overlap.into());
314 }
315
316 constraint_manager.output_contacts (output);
318
319 for (_, pseudovelocity) in constraint_manager.pseudo_velocities.iter_mut() {
321 *pseudovelocity = Vector3::zero();
322 }
323
324 debug_assert!(self.pipeline.is_empty());
326 }
327
328 fn detect_continuous (&mut self,
334 constraint_manager : &constraint::Manager,
335 objects_static : &VecMap <object::Static>,
336 objects_dynamic : &VecMap <object::Dynamic>
337 ) {
338 self.broad_overlap_pairs_continuous();
340 self.mid_toi_pairs (constraint_manager, objects_dynamic);
342 self.narrow_toi_contacts (objects_static, objects_dynamic,
344 &constraint_manager.pseudo_velocities);
345 }
346
347 fn resolve_first_collision (&mut self,
356 constraint_manager : &mut constraint::Manager,
357 objects_static : &VecMap <object::Static>,
358 objects_dynamic : &mut VecMap <object::Dynamic>
359 ) -> bool {
360 if let Some (Reverse ((toi, object_pair, contact))) =
361 self.pipeline.narrow_toi_contacts.pop()
362 {
363 let (object_id_a, object_id_b) = object_pair.into();
366 let kind_a = object_id_a.kind();
367 let kind_b = object_id_b.kind();
368 let index_a = object_id_a.key().index();
369 let index_b = object_id_b.key().index();
370 let collidable = {
372 let collidable_a = match kind_a {
373 object::Kind::Static => objects_static[index_a].collidable,
374 object::Kind::Dynamic => objects_dynamic[index_a].collidable,
375 object::Kind::Nodetect => unreachable!()
376 };
377 debug_assert_eq!(kind_b, object::Kind::Dynamic);
378 let collidable_b = objects_dynamic[index_b].collidable;
379 collidable_a && collidable_b
380 };
381 if !collidable {
382 let overlap = event::Overlap {
383 object_id_a: object_id_a.into(),
384 object_id_b: object_id_b.into()
385 };
386 log::debug!(toi=*toi, overlap:?; "nocollide overlap");
387 self.pipeline.overlaps.push (overlap);
388 return true
389 }
390 log::debug!(toi=*toi, object_pair:?=(object_id_a, object_id_b);
391 "resolve collision");
392 let maybe_island_a = constraint_manager.get_island_key (object_id_a);
394 let maybe_island_b = {
395 let maybe_island_b = constraint_manager.get_island_key (object_id_b);
396 if maybe_island_a == maybe_island_b {
397 None
399 } else {
400 maybe_island_b
401 }
402 };
403 let collision_resolve = self.resolve_collision (
405 constraint_manager, objects_static, objects_dynamic, toi, object_id_a,
406 object_id_b, contact, maybe_island_a, maybe_island_b);
407 log::trace!(collision_resolve:?; "resolved collision");
408 self.pipeline.resolved_collisions.push (collision_resolve);
409 true
410 } else {
411 debug_assert!(self.pipeline.mid_toi_pairs.is_empty());
413 false
414 }
415 }
416
417 #[expect(clippy::too_many_arguments)]
418 fn resolve_collision (
419 &mut self,
420 constraint_manager : &mut constraint::Manager,
421 objects_static : &VecMap <object::Static>,
422 objects_dynamic : &mut VecMap <object::Dynamic>,
423 toi : Normalized <f64>,
424 object_id_a : InternalId,
425 object_id_b : InternalId,
426 collision : constraint::Contact,
427 island_key_a : Option <constraint::island::KeyType>,
428 island_key_b : Option <constraint::island::KeyType>
429 ) -> event::CollisionResolve {
430 use num::Zero;
437 let mut dynamic_ids = SortedSet::new();
438 if object_id_a.kind() == object::Kind::Dynamic {
439 dynamic_ids.push (object_id_a);
440 }
441 dynamic_ids.push (object_id_b);
442 let mut islands = constraint_manager.islands.take().unwrap();
443 let island_a = island_key_a.map (|key| &islands[key as usize]);
444 let island_b = island_key_b.map (|key| &islands[key as usize]);
445 island_a.map (|island| dynamic_ids.extend (island.dynamic_ids().into_vec()));
446 island_b.map (|island| dynamic_ids.extend (island.dynamic_ids().into_vec()));
447 let mut dynamic_modified = SortedSet::new();
450 let lerp_objects = |objects_dynamic : &mut VecMap <object::Dynamic>, time_delta|
452 for id in dynamic_ids.iter() {
453 let index = id.key().index();
454 let object = &mut objects_dynamic[index];
455 let pseudovelocity = constraint_manager.pseudo_velocities[index];
456 object::lerp (object, pseudovelocity, time_delta);
457 };
458 let (t_reverse, t_forward) = {
459 let toi_f64 : f64 = *toi; (-1.0 + toi_f64, 1.0 - toi_f64)
461 };
462 lerp_objects (objects_dynamic, t_reverse);
463 let toi_aabbs = dynamic_ids.iter().map (|id|{
464 let index = id.key().index();
465 objects_dynamic[index].aabb_dilated()
466 }).collect::<Vec <_>>();
467 let constrain_velocity = |
469 objects_dynamic : &mut VecMap <object::Dynamic>,
470 pseudovelocities : &VecMap <Vector3 <f64>>,
471 constraint : constraint::Variant,
472 colliding : bool,
473 object_id_a : InternalId,
474 object_id_b : InternalId
475 | {
476 let key_a = object_id_a.key();
477 let key_b = object_id_b.key();
478 let index_a = key_a.index();
479 let index_b = key_b.index();
480 let (mut object_a, object_a_restitution) = match object_id_a.kind() {
481 object::Kind::Static => {
482 let object = objects_static[index_a].clone();
483 let restitution = object.material.restitution;
484 (Either::Left (object), restitution)
485 }
486 object::Kind::Dynamic => {
487 let object = objects_dynamic[index_a].clone();
488 let restitution = object.material.restitution;
489 (Either::Right (object), restitution)
490 }
491 object::Kind::Nodetect => unreachable!()
492 };
493 debug_assert_eq!(object_id_b.kind(), object::Kind::Dynamic);
494 let mut object_b = objects_dynamic[index_b].clone();
495 let restitution = if colliding {
496 object_a_restitution * object_b.material.restitution
497 } else {
498 Normalized::zero()
499 };
500 let pseudovelocity_a = if object_id_a.kind() == object::Kind::Dynamic {
501 Some (pseudovelocities[index_a])
502 } else {
503 None
504 };
505 let pseudovelocity_b = pseudovelocities[index_b];
506 let maybe_impulses = match constraint {
507 constraint::Variant::Contact (contact) =>
508 contact.constrain_velocity_with_restitution (
509 object_a.as_mut().map_left (|object_static| &*object_static),
510 &mut object_b, pseudovelocity_a, pseudovelocity_b, restitution),
511 constraint::Variant::Distance (distance) => distance.constrain_velocity (
512 object_a.as_mut().map_left (|object_static| &*object_static),
513 &mut object_b, pseudovelocity_a, pseudovelocity_b)
514 };
515 if maybe_impulses.is_some() {
516 match object_a {
517 Either::Right (object_a) => objects_dynamic[index_a] = object_a,
518 Either::Left (_) => {}
519 }
520 objects_dynamic[index_b] = object_b;
521 }
522 maybe_impulses
523 };
524 let mut impulse_normal_a = Vector3::zero();
525 let mut impulse_normal_b = Vector3::zero();
526 let mut impulse_tangent_a = Vector3::zero();
527 let mut impulse_tangent_b = Vector3::zero();
528 let mut iter = 0;
529 loop {
530 let mut satisfied = true;
531 if let Some (impulses) = constrain_velocity (
532 objects_dynamic, &constraint_manager.pseudo_velocities, collision.clone().into(),
533 true, object_id_a, object_id_b
534 ) {
535 impulse_normal_a += impulses.impulse_normal_a;
536 impulse_normal_b += impulses.impulse_normal_b;
537 impulse_tangent_a += impulses.impulse_tangent_a;
538 impulse_tangent_b += impulses.impulse_tangent_b;
539 }
540 let mut constrain_island = |island : &constraint::Island| {
541 for (object_pair, constraint) in island.constraints.iter().cloned() {
542 let (object_id_a, object_id_b) = object_pair.into();
543 if constrain_velocity (
544 objects_dynamic, &constraint_manager.pseudo_velocities, constraint, false,
545 object_id_a, object_id_b
546 ).is_some() {
547 if object_id_a.kind() == object::Kind::Dynamic {
548 dynamic_modified.push (object_id_a);
549 }
550 dynamic_modified.push (object_id_b);
551 satisfied = false;
552 }
553 }
554 };
555 if let Some (island) = island_a {
556 constrain_island (island);
557 }
558 if let Some (island) = island_b {
559 constrain_island (island);
560 }
561 iter += 1;
562 if satisfied {
563 log::debug!(iters=iter; "collision velocity constraint satisfied");
564 break
565 }
566 }
567 lerp_objects (objects_dynamic, t_forward);
569 let constrain_position = |
571 objects_dynamic : &mut VecMap <object::Dynamic>,
572 pseudovelocities : &mut VecMap <Vector3 <f64>>,
573 constraint : &mut constraint::Variant,
574 object_id_a : InternalId,
575 object_id_b : InternalId
576 | {
577 let key_a = object_id_a.key();
578 let key_b = object_id_b.key();
579 let index_a = key_a.index();
580 let index_b = key_b.index();
581 let mut object_a = match object_id_a.kind() {
582 object::Kind::Static => Either::Left (objects_static[index_a].clone()),
583 object::Kind::Dynamic => Either::Right (objects_dynamic[index_a].clone()),
584 object::Kind::Nodetect => unreachable!()
585 };
586 debug_assert_eq!(object_id_b.kind(), object::Kind::Dynamic);
587 let mut object_b = objects_dynamic[index_b].clone();
588 let mut pseudovelocity_a = if object_id_a.kind() == object::Kind::Dynamic {
589 Some (pseudovelocities[index_a])
590 } else {
591 None
592 };
593 let mut pseudovelocity_b = pseudovelocities[index_b];
594 let maybe_impulses = constraint.constrain_position (
595 t_reverse, object_a.as_mut().map_left (|object_static| &*object_static),
596 &mut object_b, pseudovelocity_a.as_mut(), &mut pseudovelocity_b);
597 if maybe_impulses.is_some() {
598 match object_a {
599 Either::Right (object_a) => {
600 objects_dynamic[index_a] = object_a;
601 pseudovelocities[index_a] = pseudovelocity_a.unwrap();
602 }
603 Either::Left (_) => {}
604 }
605 objects_dynamic[index_b] = object_b;
606 pseudovelocities[index_b] = pseudovelocity_b;
607 }
608 maybe_impulses
609 };
610 let mut pseudo_impulse_a = Vector3::zero();
612 let mut pseudo_impulse_b = Vector3::zero();
613 let mut iter = 0;
614 let mut collision_constraint = constraint::Variant::from (collision.clone());
615 loop {
616 let mut satisfied = true;
617 let maybe_impulses = constrain_position (
618 objects_dynamic, &mut constraint_manager.pseudo_velocities,
619 &mut collision_constraint, object_id_a, object_id_b);
620 if let Some (impulses) = maybe_impulses {
621 pseudo_impulse_a += impulses.pseudo_impulse_a;
622 pseudo_impulse_b += impulses.pseudo_impulse_b;
623 }
624 let mut constrain_island = |island : &mut constraint::Island|
625 for (object_pair, constraint) in island.constraints.iter_mut() {
626 let (object_id_a, object_id_b) = (*object_pair).into();
627 let maybe_impulses = constrain_position (
628 objects_dynamic, &mut constraint_manager.pseudo_velocities, constraint,
629 object_id_a, object_id_b
630 );
631 if maybe_impulses.is_some() {
632 if object_id_a.kind() == object::Kind::Dynamic {
633 dynamic_modified.push (object_id_a);
634 }
635 dynamic_modified.push (object_id_b);
636 satisfied = false;
637 }
638 };
639 if let Some (key) = island_key_a {
640 constrain_island (&mut islands[key as usize]);
641 }
642 if let Some (key) = island_key_b {
643 constrain_island (&mut islands[key as usize]);
644 }
645 iter += 1;
646 if satisfied {
647 log::debug!(iters=iter; "collision position constraint satisfied");
648 break
649 }
650 }
651 constraint_manager.islands = Some (islands);
652 let object_pair = (object_id_a, object_id_b).into();
656 if !impulse_normal_b.is_zero() || !impulse_tangent_b.is_zero() ||
657 !pseudo_impulse_b.is_zero()
658 {
659 if object_id_a.kind() == object::Kind::Dynamic {
660 debug_assert!(!impulse_normal_a.is_zero() ||
661 !impulse_tangent_a.is_zero() || !pseudo_impulse_a.is_zero());
662 dynamic_modified.push (object_id_a);
663 }
664 dynamic_modified.push (object_id_b);
665 }
666 self.broad.add_resolved (object_pair);
667 let mut update_island = |island_key : constraint::island::KeyType| {
671 let mut islands = constraint_manager.islands.take().unwrap();
672 let island = &mut islands[island_key as usize];
673 let mut remove_contacts = vec![];
674 for (constraint_index, (object_pair, contact)) in island.constraints.iter()
675 .enumerate().filter_map (|(index, (pair, constraint))|
676 if let constraint::Variant::Contact (contact) = constraint {
677 Some ((index, (pair, contact)))
678 } else {
679 None
680 }
681 )
682 {
683 self.broad.add_resolved (*object_pair);
684 if contact.distance >= CONTACT_DISTANCE {
685 remove_contacts.push (constraint_index as u32)
686 }
687 }
688 if !remove_contacts.is_empty() {
689 constraint_manager.remove_constraints (island, remove_contacts.as_slice());
690 if island.constraints.is_empty() {
691 let _ = islands.take (island_key as usize).unwrap();
692 } else {
693 let partitions = island.partition();
694 if !partitions.is_empty() {
695 let _ = islands.take (island_key as usize).unwrap();
696 for new_island in partitions {
697 let dynamic_ids = new_island.dynamic_ids();
698 let island_key = islands.put (new_island) as constraint::island::KeyType;
699 for dynamic_id in dynamic_ids.into_vec() {
700 constraint_manager.change_dynamic_island_key (dynamic_id, island_key);
701 }
702 }
703 }
704 }
705 }
706 constraint_manager.islands = Some (islands);
707 };
708 if let Some (island_key) = island_key_a {
709 update_island (island_key);
710 }
711 if let Some (island_key) = island_key_b {
712 update_island (island_key);
713 }
714 let constraint::Variant::Contact (contact) = collision_constraint
715 else { unreachable!() };
716 if contact.distance < CONTACT_DISTANCE {
717 debug_assert!(contact.distance >= 0.0);
718 log::trace!(object_pair:?, contact:?; "creating contact @ T==1.0");
719 constraint_manager.add_constraint (object_pair, contact.into());
720 }
721 for id in dynamic_modified.iter() {
724 let i = dynamic_ids.binary_search_by_key (&id, |x| x).unwrap();
725 let key = id.key();
726 let index = key.index();
727 let object = &objects_dynamic[index];
728 let aabb_discrete = object.aabb_dilated();
729 let aabb_continuous = Aabb3::union (toi_aabbs[i], aabb_discrete);
730 self.broad.update_aabb_dynamic_discrete (aabb_discrete, key);
731 self.broad.update_aabb_dynamic_continuous (aabb_continuous, key);
732 }
733 self.pipeline.remove_resolved_dynamic (&dynamic_modified);
734 self.broad.set_modified (dynamic_modified);
736 let object_id_a = object_id_a.into();
738 let object_id_b = object_id_b.into();
739 event::CollisionResolve {
740 toi, collision,
741 object_id_a, object_id_b,
742 impulse_normal_a, impulse_normal_b,
743 impulse_tangent_a, impulse_tangent_b,
744 pseudo_impulse_a, pseudo_impulse_b
745 }
746 }
747
748 #[inline]
753 fn broad_overlap_pairs_continuous (&mut self) {
754 self.broad.overlap_pairs_continuous (
755 &mut self.pipeline.broad_overlap_pairs, self.pipeline.detect_resolve_iter);
756 self.pipeline.broad_overlap_pairs.retain (
758 |pair|{
759 let (id_a, id_b) = (*pair).into();
760 let id_a = object::Id::from (id_a);
761 let id_b = object::Id::from (id_b);
762 for event::Overlap { object_id_a, object_id_b } in
763 self.pipeline.overlaps.iter()
764 {
765 if object_id_a == &id_a && object_id_b == &id_b {
766 return false
767 }
768 }
769 true
770 }
771 );
772 log::trace!(overlap_pairs:?=self.pipeline.broad_overlap_pairs;
773 "broad overlap pairs");
774 }
775
776 fn mid_toi_pairs (&mut self,
783 constraint_manager : &constraint::Manager,
784 objects_dynamic : &VecMap <object::Dynamic>
785 ) {
786 use sorted_vec::FindOrInsert;
787 let last_toi = self.pipeline.resolved_collisions.last()
788 .map_or (0.0, |collision_resolve| *collision_resolve.toi);
789 'outer: for object_pair in self.pipeline.broad_overlap_pairs.drain (..) {
790 let (object_id_a, object_id_b) = object_pair.into();
791 debug_assert_eq!(object_id_b.kind(), object::Kind::Dynamic);
792 match object_id_a.kind() {
793 object::Kind::Static => {
797 let id_static = object::Id::from (object_id_a);
798 let id_dynamic = object::Id::from (object_id_b);
799 let key_static = id_static.key;
800 let key_dynamic = id_dynamic.key;
801 let _index_static = key_static.index();
802 let index_dynamic = key_dynamic.index();
803 if let (Some (static_contact_count), Some (dynamic_island_key)) = (
804 constraint_manager.get_constraint_count (object_id_a),
805 constraint_manager.get_island_key (object_id_b)
806 ) {
807 debug_assert!(static_contact_count > 0);
808 let island = constraint_manager.get_island (dynamic_island_key).unwrap();
809 for (pair, _) in island.constraints.iter() {
810 if *pair == object_pair {
811 continue 'outer
812 }
813 }
814 }
815 let dynamic_object = &objects_dynamic[index_dynamic];
817 let dynamic_velocity = dynamic_object.derivatives.velocity;
818 let dynamic_pseudovelocity = constraint_manager.pseudo_velocities[index_dynamic];
819 let dynamic_velocity_effective = dynamic_velocity + dynamic_pseudovelocity;
820 let static_aabb = self.broad.get_aabb_static (key_static);
821 let dynamic_aabb_current = self.broad
822 .get_aabb_dynamic_discrete (key_dynamic);
823 let _dynamic_aabb_swept = self.broad
824 .get_aabb_dynamic_continuous (key_dynamic);
825 let dynamic_aabb_previous = Aabb3::with_minmax_unchecked (
826 dynamic_aabb_current.min() - dynamic_velocity_effective,
827 dynamic_aabb_current.max() - dynamic_velocity_effective);
828 let dynamic_velocity_effective_reciprocal = dynamic_velocity_effective.recip();
830 let t_margin_x =
832 (0.5 * CONTACT_DISTANCE * dynamic_velocity_effective_reciprocal.x).abs();
833 let t_margin_y =
834 (0.5 * CONTACT_DISTANCE * dynamic_velocity_effective_reciprocal.y).abs();
835 let t_margin_z =
836 (0.5 * CONTACT_DISTANCE * dynamic_velocity_effective_reciprocal.z).abs();
837
838 let (interval_start_x, interval_end_x) = if
839 dynamic_velocity_effective.x == 0.0
840 {
841 debug_assert!(
842 dynamic_aabb_current.max().0.x > static_aabb.min().0.x &&
843 static_aabb.max().0.x > dynamic_aabb_current.min().0.x);
844 debug_assert!(
845 dynamic_aabb_previous.max().0.x > static_aabb.min().0.x &&
846 static_aabb.max().0.x > dynamic_aabb_previous.min().0.x);
847 (f64::NEG_INFINITY, f64::INFINITY)
848 } else if dynamic_velocity_effective.x > 0.0 {
849 ( (static_aabb.min().0.x - dynamic_aabb_previous.max().0.x)
852 .mul_add (dynamic_velocity_effective_reciprocal.x, -t_margin_x),
853 (static_aabb.max().0.x - dynamic_aabb_previous.min().0.x)
856 .mul_add (dynamic_velocity_effective_reciprocal.x, t_margin_x)
857 )
858 } else {
859 debug_assert!(dynamic_velocity_effective.x < 0.0);
860 ( (static_aabb.max().0.x - dynamic_aabb_previous.min().0.x)
863 .mul_add (dynamic_velocity_effective_reciprocal.x, -t_margin_x),
864 (static_aabb.min().0.x - dynamic_aabb_previous.max().0.x)
867 .mul_add (dynamic_velocity_effective_reciprocal.x, t_margin_x)
868 )
869 };
870
871 let (interval_start_y, interval_end_y) = if
872 dynamic_velocity_effective.y == 0.0
873 {
874 debug_assert!(
875 dynamic_aabb_current.max().0.y > static_aabb.min().0.y &&
876 static_aabb.max().0.y > dynamic_aabb_current.min().0.y);
877 debug_assert!(
878 dynamic_aabb_previous.max().0.y > static_aabb.min().0.y &&
879 static_aabb.max().0.y > dynamic_aabb_previous.min().0.y);
880 (f64::NEG_INFINITY, f64::INFINITY)
881 } else if dynamic_velocity_effective.y > 0.0 {
882 ( dynamic_velocity_effective_reciprocal.y.mul_add (
885 static_aabb.min().0.y - dynamic_aabb_previous.max().0.y, -t_margin_y),
886 dynamic_velocity_effective_reciprocal.y.mul_add (
889 static_aabb.max().0.y - dynamic_aabb_previous.min().0.y, t_margin_y)
890 )
891 } else {
892 debug_assert!(dynamic_velocity_effective.y < 0.0);
893 ( dynamic_velocity_effective_reciprocal.y.mul_add (
896 static_aabb.max().0.y - dynamic_aabb_previous.min().0.y, -t_margin_y),
897 dynamic_velocity_effective_reciprocal.y.mul_add (
900 static_aabb.min().0.y - dynamic_aabb_previous.max().0.y, t_margin_y)
901 )
902 };
903
904 let (interval_start_z, interval_end_z) = if
905 dynamic_velocity_effective.z == 0.0
906 {
907 debug_assert!(
908 dynamic_aabb_current.max().0.z > static_aabb.min().0.z &&
909 static_aabb.max().0.z > dynamic_aabb_current.min().0.z);
910 debug_assert!(
911 dynamic_aabb_previous.max().0.z > static_aabb.min().0.z &&
912 static_aabb.max().0.z > dynamic_aabb_previous.min().0.z);
913 (f64::NEG_INFINITY, f64::INFINITY)
914 } else if dynamic_velocity_effective.z > 0.0 {
915 ( dynamic_velocity_effective_reciprocal.z.mul_add (
918 static_aabb.min().0.z - dynamic_aabb_previous.max().0.z, -t_margin_z),
919 dynamic_velocity_effective_reciprocal.z.mul_add (
922 static_aabb.max().0.z - dynamic_aabb_previous.min().0.z, t_margin_z)
923 )
924 } else {
925 debug_assert!(dynamic_velocity_effective.z < 0.0);
926 ( dynamic_velocity_effective_reciprocal.z.mul_add (
929 static_aabb.max().0.z - dynamic_aabb_previous.min().0.z, -t_margin_z),
930 dynamic_velocity_effective_reciprocal.z.mul_add (
933 static_aabb.min().0.z - dynamic_aabb_previous.max().0.z, t_margin_z)
934 )
935 };
936
937 let interval = {
938 let interval_xy =
939 if interval_start_x < interval_end_y && interval_start_y < interval_end_x {
940 Some ((
941 f64::max (interval_start_x, interval_start_y),
942 f64::min (interval_end_x, interval_end_y)
943 ))
944 } else {
945 None
946 };
947 if let Some ((interval_start_xy, interval_end_xy)) = interval_xy {
948 if interval_start_xy < interval_end_z &&
949 interval_start_z < interval_end_xy
950 {
951 Some ((
952 f64::max (interval_start_xy, interval_start_z),
953 f64::min (interval_end_xy, interval_end_z)
954 ))
955 } else {
956 None
957 }
958 } else {
959 None
960 }
961 };
962
963 if let Some ((interval_start, interval_end)) = interval &&
964 interval_start < 1.0 && 0.0 < interval_end
965 {
966 let mid_toi_pair = (
967 Normalized::clamp (f64::max (interval_start, last_toi)),
968 Normalized::clamp (interval_end),
969 object_pair
970 );
971 match self.pipeline.mid_toi_pairs.find_or_insert (Reverse (mid_toi_pair)) {
972 FindOrInsert::Inserted (_) => {}
973 FindOrInsert::Found (_) => unreachable!()
974 }
975 }
976 }
977 object::Kind::Dynamic => {
981 let key_a = object_id_a.key();
982 let key_b = object_id_b.key();
983 let index_a = key_a.index();
984 let index_b = key_b.index();
985 if let (Some (island_key_a), Some (island_key_b)) = (
986 constraint_manager.get_island_key (object_id_a),
987 constraint_manager.get_island_key (object_id_b)
988 ) && island_key_a == island_key_b {
989 let island = constraint_manager.get_island (island_key_a).unwrap();
990 for (pair, _) in island.constraints.iter() {
991 if *pair == object_pair {
992 continue 'outer
993 }
994 }
995 }
996 let object_a = &objects_dynamic[index_a];
999 let object_b = &objects_dynamic[index_b];
1000 let velocity_a = object_a.derivatives.velocity;
1001 let velocity_b = object_b.derivatives.velocity;
1002 let pseudovelocity_a = constraint_manager.pseudo_velocities[key_a.index()];
1003 let pseudovelocity_b = constraint_manager.pseudo_velocities[key_b.index()];
1004 let velocity_effective_a = velocity_a + pseudovelocity_a;
1005 let velocity_effective_b = velocity_b + pseudovelocity_b;
1006 let velocity_effective_relative =
1007 velocity_effective_a - velocity_effective_b;
1008 let velocity_effective_relative_reciprocal =
1009 velocity_effective_relative.recip();
1010 let aabb_current_a = self.broad.get_aabb_dynamic_discrete (key_a);
1011 let aabb_current_b = self.broad.get_aabb_dynamic_discrete (key_b);
1012 let _aabb_swept_a = self.broad.get_aabb_dynamic_continuous (key_a);
1013 let _aabb_swept_b = self.broad.get_aabb_dynamic_continuous (key_b);
1014 let aabb_previous_a = Aabb3::with_minmax_unchecked (
1015 aabb_current_a.min() - velocity_effective_a,
1016 aabb_current_a.max() - velocity_effective_a);
1017 let aabb_previous_b = Aabb3::with_minmax_unchecked (
1018 aabb_current_b.min() - velocity_effective_b,
1019 aabb_current_b.max() - velocity_effective_b);
1020
1021 let overlap_interval_axis = |axis : Axis3| {
1023 let axis_index = axis as usize;
1024 let t_margin = (0.5 * CONTACT_DISTANCE *
1027 velocity_effective_relative_reciprocal[axis_index]).abs();
1028 let velocity_relative = velocity_effective_relative[axis_index];
1029 let velocity_relative_reciprocal =
1030 velocity_effective_relative_reciprocal[axis_index];
1031 let prev_a_min = aabb_previous_a.min().0[axis_index];
1032 let prev_a_max = aabb_previous_a.max().0[axis_index];
1033 let prev_b_min = aabb_previous_b.min().0[axis_index];
1034 let prev_b_max = aabb_previous_b.max().0[axis_index];
1035 if velocity_relative == 0.0 {
1036 geometry::Interval::with_minmax_unchecked (
1040 f64::NEG_INFINITY, f64::INFINITY)
1041 } else if velocity_relative > 0.0 {
1042 geometry::Interval::with_minmax_unchecked (
1043 (prev_b_min - prev_a_max)
1045 .mul_add (velocity_relative_reciprocal, -t_margin),
1046 (prev_b_max - prev_a_min)
1048 .mul_add (velocity_relative_reciprocal, t_margin)
1049 )
1050 } else {
1051 debug_assert!(velocity_relative < 0.0);
1052 geometry::Interval::with_minmax_unchecked (
1053 (prev_b_max - prev_a_min)
1055 .mul_add (velocity_relative_reciprocal, -t_margin),
1056 (prev_b_min - prev_a_max)
1058 .mul_add (velocity_relative_reciprocal, t_margin)
1059 )
1060 }
1061 };
1062 let overlap_interval_x = overlap_interval_axis (Axis3::X);
1063 let overlap_interval_y = overlap_interval_axis (Axis3::Y);
1064 let overlap_interval_z = overlap_interval_axis (Axis3::Z);
1065
1066 if let Some (interval) = overlap_interval_x.intersection (overlap_interval_y)
1067 .and_then (|overlap_interval_xy|
1068 overlap_interval_xy.intersection (overlap_interval_z))
1069 && interval.min() < 1.0 && 0.0 < interval.max()
1070 {
1071 let mid_toi_pair = (
1072 Normalized::clamp (f64::max (interval.min(), last_toi)),
1073 Normalized::clamp (interval.max()),
1074 object_pair
1075 );
1076 match self.pipeline.mid_toi_pairs.find_or_insert (Reverse (mid_toi_pair)) {
1077 FindOrInsert::Inserted (_) => {}
1078 FindOrInsert::Found (_) => unreachable!()
1079 }
1080 }
1081 }
1082 object::Kind::Nodetect => unreachable!()
1083 }
1084 }
1085
1086 log::trace!(mid_toi_pairs:?=self.pipeline.mid_toi_pairs; "mid TOI pairs");
1087
1088 self.pipeline.broad_overlap_pairs.clear();
1089 }
1090
1091 fn narrow_toi_contacts (&mut self,
1096 objects_static : &VecMap <object::Static>,
1097 objects_dynamic : &VecMap <object::Dynamic>,
1098 pseudo_velocities : &VecMap <Vector3 <f64>>
1099 ) {
1100 use sorted_vec::FindOrInsert;
1101
1102 let narrow_toi_contacts = &mut self.pipeline.narrow_toi_contacts;
1103 while let Some (mid_toi_pair) = self.pipeline.mid_toi_pairs.last().copied() {
1104 let earliest_toi_contact = if !narrow_toi_contacts.is_empty() {
1105 narrow_toi_contacts[0].0.0
1106 } else {
1107 Normalized::clamp (1.0)
1108 };
1109
1110 if mid_toi_pair.0.0 < earliest_toi_contact {
1111 let (mid_toi_start, _mid_toi_end, object_pair) = mid_toi_pair.0;
1112 let (object_id_a, object_id_b) = object_pair.into();
1113 let kind_a = object_id_a.kind();
1114 let kind_b = object_id_b.kind();
1115 match (kind_a, kind_b) {
1116 (object::Kind::Static, object::Kind::Dynamic) => {
1117 let key_static = object_id_a.key();
1118 let key_dynamic = object_id_b.key();
1119 let index_static = key_static.index();
1120 let index_dynamic = key_dynamic.index();
1121 let mut dynamic_object = objects_dynamic[index_dynamic].clone();
1123 let dynamic_velocity = dynamic_object.derivatives.velocity;
1124 let dynamic_pseudovelocity = pseudo_velocities[index_dynamic];
1125 let dynamic_velocity_effective = dynamic_velocity - dynamic_pseudovelocity;
1126 let static_object = objects_static[index_static].clone();
1127 let mut narrow_toi = *mid_toi_start;
1128 object::lerp (&mut dynamic_object, dynamic_pseudovelocity, -1.0 + narrow_toi);
1131 let mut t_remaining = 1.0 - narrow_toi;
1132 #[expect(clippy::used_underscore_binding)]
1133 let mut _iter = 0;
1134 loop {
1135 #[cfg(debug_assertions)]
1136 if self.narrow_toi_max_iter_count < _iter {
1137 self.narrow_toi_max_iter_count = _iter;
1138 }
1139 let proximity = Proximity::query (&static_object, &dynamic_object);
1140 if cfg!(debug_assertions) &&
1141 dynamic_object.collidable && static_object.collidable
1142 {
1143 debug_assert!(proximity.distance >= 0.0,
1144 "objects should not be overlapping, distance: {}", proximity.distance);
1145 }
1146 let dynamic_velocity_effective_normal =
1147 dynamic_velocity_effective.dot (-*proximity.normal);
1148 if dynamic_velocity_effective_normal > 0.0 &&
1149 dynamic_object.collidable && static_object.collidable
1150 {
1151 log::trace!(
1153 t=narrow_toi, velocity=dynamic_velocity_effective_normal;
1154 "collision rejected, separating velocity");
1155 break
1156 } else if proximity.distance < CONTACT_DISTANCE {
1157 debug_assert!(dynamic_velocity_effective_normal <= 0.0 ||
1158 !dynamic_object.collidable || !static_object.collidable);
1159 log::trace!(t=narrow_toi, proximity:?; "collision detected");
1161 let collision = constraint::Contact {
1165 normal: proximity.normal,
1166 distance: proximity.distance
1167 };
1168 let narrow_toi_contact = Reverse ((
1169 Normalized::noisy (narrow_toi), object_pair, collision
1170 ));
1171 match narrow_toi_contacts.find_or_insert (narrow_toi_contact) {
1172 FindOrInsert::Inserted (_) => {}
1173 FindOrInsert::Found (_) => unreachable!()
1174 }
1175 break
1176 } else if dynamic_velocity_effective_normal == 0.0 {
1177 log::trace!(
1178 t=narrow_toi, velocity=dynamic_velocity_effective_normal;
1179 "collision rejected, zero relative velocity and objects not \
1180 in contact");
1181 break
1182 }
1183 let dynamic_velocity_effective_normal_reciprocal =
1184 1.0 / dynamic_velocity_effective_normal;
1185 let toi_axis =
1186 0.5f64.mul_add (-CONTACT_DISTANCE, proximity.distance)
1190 .mul_add (-dynamic_velocity_effective_normal_reciprocal, narrow_toi);
1191 debug_assert!(toi_axis > narrow_toi ||
1192 !dynamic_object.collidable || !static_object.collidable);
1193 if toi_axis > 1.0 || toi_axis < 0.0 {
1194 log::trace!(t=narrow_toi, proximity:?;
1196 "collision rejected, final proximity");
1197 break
1198 }
1199 let t_advance = toi_axis - narrow_toi;
1200 object::lerp (&mut dynamic_object, dynamic_pseudovelocity, t_advance);
1201 narrow_toi = toi_axis;
1202 t_remaining -= t_advance;
1203 debug_assert!(t_remaining > 0.0);
1204 _iter += 1;
1205 }
1206 }
1207
1208 (object::Kind::Dynamic, object::Kind::Dynamic) => {
1209 let key_a = object_id_a.key();
1210 let key_b = object_id_b.key();
1211 let index_a = key_a.index();
1212 let index_b = key_b.index();
1213 let mut object_a = objects_dynamic[index_a].clone();
1216 let mut object_b = objects_dynamic[index_b].clone();
1217 let pseudovelocity_a = pseudo_velocities[index_a];
1218 let pseudovelocity_b = pseudo_velocities[index_b];
1219 let velocity_a = object_a.derivatives.velocity;
1220 let velocity_b = object_b.derivatives.velocity;
1221 let velocity_effective_a = velocity_a + pseudovelocity_a;
1222 let velocity_effective_b = velocity_b + pseudovelocity_b;
1223 let velocity_effective_relative =
1224 velocity_effective_a - velocity_effective_b;
1225 let mut narrow_toi = *mid_toi_start;
1226 let mut t_remaining = 1.0 - narrow_toi;
1227 object::lerp (&mut object_a, pseudovelocity_a, -1.0 + narrow_toi);
1228 object::lerp (&mut object_b, pseudovelocity_b, -1.0 + narrow_toi);
1229 loop {
1230 debug_assert!(narrow_toi <= 1.0);
1231 debug_assert!(t_remaining >= 0.0);
1232 let proximity = Proximity::query (&object_a, &object_b);
1233 if cfg!(debug_assertions) && object_a.collidable && object_b.collidable {
1234 debug_assert!(proximity.distance >= 0.0);
1235 }
1236 let velocity_effective_relative_normal =
1237 velocity_effective_relative.dot (*proximity.normal);
1238 if velocity_effective_relative_normal > 0.0 &&
1239 object_a.collidable && object_b.collidable
1240 {
1241 log::trace!(
1243 t=narrow_toi, velocity=velocity_effective_relative_normal;
1244 "collision rejected, separating velocity");
1245 break
1246 } else if proximity.distance < CONTACT_DISTANCE {
1247 debug_assert!(velocity_effective_relative_normal <= 0.0 ||
1248 !object_a.collidable || !object_b.collidable);
1249 log::trace!(t=narrow_toi, proximity:?; "collision detected");
1251 let contact = constraint::Contact::try_from (proximity).unwrap();
1252 let narrow_toi_contact = Reverse ((
1253 Normalized::noisy (narrow_toi), object_pair, contact
1254 ));
1255 match narrow_toi_contacts.find_or_insert (narrow_toi_contact) {
1256 FindOrInsert::Inserted (_) => {}
1257 FindOrInsert::Found (_) => unreachable!()
1258 }
1259 break
1260 } else if velocity_effective_relative_normal == 0.0 {
1261 log::trace!(
1262 t=narrow_toi, velocity=velocity_effective_relative_normal;
1263 "collision rejected, zero relative velocity and objects not in \
1264 contact");
1265 break
1266 }
1267 let velocity_effective_relative_normal_reciprocal =
1268 1.0 / velocity_effective_relative_normal;
1269 let toi_axis =
1270 0.5f64.mul_add (-CONTACT_DISTANCE, proximity.distance)
1274 .mul_add (-velocity_effective_relative_normal_reciprocal, narrow_toi);
1275 debug_assert!(toi_axis > narrow_toi ||
1276 !object_a.collidable || !object_b.collidable);
1277 if toi_axis > 1.0 || toi_axis < 0.0 {
1278 log::trace!(t=narrow_toi, proximity:?;
1280 "collision rejected, final proximity");
1281 break
1282 }
1283 let t_advance = toi_axis - narrow_toi;
1284 object::lerp (&mut object_a, pseudovelocity_a, t_advance);
1285 object::lerp (&mut object_b, pseudovelocity_b, t_advance);
1286 narrow_toi = toi_axis;
1287 t_remaining -= t_advance;
1288 }
1289 }
1290 _ => unreachable!()
1291 }
1292 self.pipeline.mid_toi_pairs.pop();
1293 } else { break }
1294 }
1295 #[cfg(debug_assertions)]
1296 log::debug!(max_iter_count=self.narrow_toi_max_iter_count;
1297 "narrow TOI max iters");
1298 log::trace!(contacts:?=narrow_toi_contacts; "narrow TOI contacts");
1299 }
1300
1301 #[inline]
1302 fn add_object_static (&mut self,
1303 object : &object::Static,
1304 object_key : object::Key,
1305 ) {
1306 self.broad.add_object_static (object.aabb_dilated(), object_key);
1307 }
1308
1309 #[inline]
1310 fn add_object_dynamic (&mut self,
1311 pseudo_velocities : &mut VecMap <Vector3 <f64>>,
1312 object : &object::Dynamic,
1313 object_key : object::Key
1314 ) {
1315 let aabb_discrete = object.aabb_dilated();
1316 let aabb_continuous = Aabb3::with_minmax_unchecked (
1317 aabb_discrete.min() - object.derivatives.velocity,
1318 aabb_discrete.max() - object.derivatives.velocity);
1319 self.broad.add_object_dynamic (aabb_discrete, aabb_continuous, object_key);
1323 assert!(pseudo_velocities.insert (object_key.index(), Vector3::zero()).is_none());
1324 }
1325
1326}
1327
1328impl Pipeline {
1329 #[inline]
1330 fn is_empty (&self) -> bool {
1331 self.broad_overlap_pairs.is_empty() &&
1332 self.mid_toi_pairs.is_empty() &&
1333 self.narrow_toi_contacts.is_empty() &&
1334 self.resolved_collisions.is_empty() &&
1335 self.overlaps.is_empty()
1336 }
1337
1338 #[inline]
1350 fn remove_resolved_dynamic (&mut self, resolved : &SortedSet <InternalId>) {
1351 debug_assert!(self.broad_overlap_pairs.is_empty());
1352
1353 let (mut i, mut len);
1354
1355 i = 0;
1357 len = self.mid_toi_pairs.len();
1358 while i < len {
1359 let (_, _, object_pair) = self.mid_toi_pairs[i].0;
1360 let (id_a, id_b) = object_pair.into();
1361 if id_a.kind() == object::Kind::Dynamic && resolved.binary_search (&id_a).is_ok() {
1362 self.mid_toi_pairs.remove_index (i);
1363 len -= 1;
1364 continue
1365 }
1366 debug_assert_eq!(id_b.kind(), object::Kind::Dynamic);
1367 if resolved.binary_search (&id_b).is_ok() {
1368 self.mid_toi_pairs.remove_index (i);
1369 len -= 1;
1370 continue
1371 }
1372 i += 1;
1373 }
1374
1375 i = 0;
1377 len = self.narrow_toi_contacts.len();
1378 while i < len {
1379 let (_, object_pair, _) = self.narrow_toi_contacts[i].clone().0;
1380 let (id_a, id_b) = object_pair.into();
1381 if id_a.kind() == object::Kind::Dynamic && resolved.binary_search (&id_a).is_ok() {
1382 self.narrow_toi_contacts.remove_index (i);
1383 len -= 1;
1384 continue
1385 }
1386 debug_assert_eq!(id_b.kind(), object::Kind::Dynamic);
1387 if resolved.binary_search (&id_b).is_ok() {
1388 self.narrow_toi_contacts.remove_index (i);
1389 len -= 1;
1390 continue
1391 }
1392 i += 1;
1393 }
1394 }
1395}