anput_physics/
collisions.rs

1use crate::{
2    PhysicsAccessView, PhysicsSimulation, Scalar,
3    components::{
4        AngularVelocity, BodyAccessInfo, BodyParentRelation, BodyParticleRelation, LinearVelocity,
5        Mass, PhysicsBody, PhysicsParticle, Position, Rotation,
6    },
7    density_fields::{DensityField, DensityFieldBox},
8    queries::shape::{ShapeOverlapCell, ShapeOverlapQuery},
9    utils::quat_from_axis_angle,
10};
11use anput::{
12    entity::Entity,
13    event::EventDispatcher,
14    query::{Include, Lookup},
15    systems::{System, SystemContext},
16    universe::{Local, Res},
17    world::{Relation, World},
18};
19use anput_spatial::{
20    SpatialExtractor, SpatialPartitioning,
21    third_party::rstar::{
22        AABB, Envelope, Point, PointDistance, RTreeObject, primitives::Rectangle,
23    },
24};
25use serde::{Deserialize, Serialize};
26use std::{
27    collections::{HashMap, HashSet},
28    error::Error,
29    hash::Hash,
30    ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Range},
31};
32use vek::{Aabb, Vec3};
33
34#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
35#[repr(transparent)]
36pub struct CollisionMask(pub u128);
37
38impl CollisionMask {
39    pub fn flag(index: u128) -> Self {
40        Self(1 << index)
41    }
42
43    pub fn with(mut self, index: u128) -> Self {
44        self.enable(index);
45        self
46    }
47
48    pub fn without(mut self, index: u128) -> Self {
49        self.disable(index);
50        self
51    }
52
53    pub fn enable(&mut self, index: u128) {
54        self.0 |= 1 << index;
55    }
56
57    pub fn disable(&mut self, index: u128) {
58        self.0 &= !(1 << index);
59    }
60
61    pub fn toggle(&mut self, index: u128) {
62        self.0 ^= 1 << index;
63    }
64
65    pub fn is_enabled(&self, index: u128) -> bool {
66        (self.0 & (1 << index)) != 0
67    }
68
69    pub fn does_match(&self, other: Self) -> bool {
70        (self.0 & other.0) != 0
71    }
72
73    pub fn is_superset_of(&self, other: Self) -> bool {
74        (self.0 & other.0) == other.0
75    }
76
77    pub fn is_subset_of(&self, other: Self) -> bool {
78        (self.0 & other.0) == self.0
79    }
80}
81
82impl BitAnd for CollisionMask {
83    type Output = Self;
84
85    fn bitand(self, other: Self) -> Self::Output {
86        Self(self.0.bitand(other.0))
87    }
88}
89
90impl BitAndAssign for CollisionMask {
91    fn bitand_assign(&mut self, other: Self) {
92        self.0.bitand_assign(other.0);
93    }
94}
95
96impl BitOr for CollisionMask {
97    type Output = Self;
98
99    fn bitor(self, other: Self) -> Self::Output {
100        Self(self.0.bitor(other.0))
101    }
102}
103
104impl BitOrAssign for CollisionMask {
105    fn bitor_assign(&mut self, other: Self) {
106        self.0.bitor_assign(other.0);
107    }
108}
109
110impl BitXor for CollisionMask {
111    type Output = Self;
112
113    fn bitxor(self, other: Self) -> Self::Output {
114        Self(self.0.bitxor(other.0))
115    }
116}
117
118impl BitXorAssign for CollisionMask {
119    fn bitxor_assign(&mut self, other: Self) {
120        self.0.bitxor_assign(other.0);
121    }
122}
123
124impl From<u128> for CollisionMask {
125    fn from(value: u128) -> Self {
126        Self(value)
127    }
128}
129
130#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
131pub struct CollisionProfile {
132    pub block: CollisionMask,
133    pub overlap: CollisionMask,
134    pub trace: CollisionMask,
135}
136
137impl CollisionProfile {
138    pub fn new(block: CollisionMask, overlap: CollisionMask, trace: CollisionMask) -> Self {
139        Self {
140            block,
141            overlap,
142            trace,
143        }
144    }
145
146    pub fn with_block(mut self, mask: CollisionMask) -> Self {
147        self.block |= mask;
148        self
149    }
150
151    pub fn with_overlap(mut self, mask: CollisionMask) -> Self {
152        self.overlap |= mask;
153        self
154    }
155
156    pub fn with_trace(mut self, mask: CollisionMask) -> Self {
157        self.trace |= mask;
158        self
159    }
160
161    pub fn does_block(&self, other: &Self) -> bool {
162        self.block.does_match(other.block)
163    }
164
165    pub fn does_overlap(&self, other: &Self) -> bool {
166        self.overlap.does_match(other.overlap)
167    }
168
169    pub fn does_overlap_permissive(&self, other: &Self) -> bool {
170        self.overlap.does_match(other.overlap)
171            || self.overlap.does_match(other.block)
172            || self.block.does_match(other.overlap)
173    }
174
175    pub fn does_trace(&self, other: &Self) -> bool {
176        self.trace.does_match(other.trace)
177    }
178
179    pub fn does_trace_permissive(&self, other: &Self) -> bool {
180        self.trace.does_match(other.trace)
181            || self.trace.does_match(other.block)
182            || self.block.does_match(other.trace)
183    }
184}
185
186pub struct CollisionProfilesRegistry<Key: Eq + Hash> {
187    registry: HashMap<Key, CollisionProfile>,
188}
189
190impl<Key: Eq + Hash> Default for CollisionProfilesRegistry<Key> {
191    fn default() -> Self {
192        Self {
193            registry: Default::default(),
194        }
195    }
196}
197
198impl<Key: Eq + Hash> CollisionProfilesRegistry<Key> {
199    pub fn with(mut self, key: Key, profile: CollisionProfile) -> Self {
200        self.register(key, profile);
201        self
202    }
203
204    pub fn register(&mut self, key: Key, profile: CollisionProfile) {
205        self.registry.insert(key, profile);
206    }
207
208    pub fn unregister(&mut self, key: &Key) -> Option<CollisionProfile> {
209        self.registry.remove(key)
210    }
211
212    pub fn get(&self, key: &Key) -> Option<&CollisionProfile> {
213        self.registry.get(key)
214    }
215
216    pub fn contains(&self, key: &Key) -> bool {
217        self.registry.contains_key(key)
218    }
219}
220
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
222pub enum ContactEventKind {
223    Began,
224    Continue,
225    Ended,
226}
227
228#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
229pub struct ContactEvent {
230    pub kind: ContactEventKind,
231    pub blocking: bool,
232    pub self_body: Entity,
233    pub other_body: Entity,
234    pub self_density_field: Entity,
235    pub other_density_field: Entity,
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
239pub struct ContactDetection {
240    pub enabled: bool,
241    pub density_threshold: Option<Scalar>,
242    pub voxelization_size_limit: Option<Scalar>,
243    pub depth_limit: usize,
244}
245
246impl Default for ContactDetection {
247    fn default() -> Self {
248        Self {
249            enabled: true,
250            density_threshold: None,
251            voxelization_size_limit: None,
252            depth_limit: usize::MAX,
253        }
254    }
255}
256
257pub struct DensityFieldSpatialObject {
258    pub body_entity: Entity,
259    pub aabb: Aabb<Scalar>,
260    pub collision_profile: CollisionProfile,
261}
262
263impl RTreeObject for DensityFieldSpatialObject {
264    type Envelope = AABB<[Scalar; 3]>;
265
266    fn envelope(&self) -> Self::Envelope {
267        AABB::from_corners(self.aabb.min.into_array(), self.aabb.max.into_array())
268    }
269}
270
271impl PointDistance for DensityFieldSpatialObject {
272    fn distance_2(
273        &self,
274        point: &<Self::Envelope as Envelope>::Point,
275    ) -> <<Self::Envelope as Envelope>::Point as Point>::Scalar {
276        Rectangle::from_corners(self.aabb.min.into_array(), self.aabb.max.into_array())
277            .distance_2(point)
278    }
279
280    fn contains_point(&self, point: &<Self::Envelope as Envelope>::Point) -> bool {
281        Rectangle::from_corners(self.aabb.min.into_array(), self.aabb.max.into_array())
282            .contains_point(point)
283    }
284
285    fn distance_2_if_less_or_equal(
286        &self,
287        point: &<Self::Envelope as Envelope>::Point,
288        max_distance_2: <<Self::Envelope as Envelope>::Point as Point>::Scalar,
289    ) -> Option<<<Self::Envelope as Envelope>::Point as Point>::Scalar> {
290        Rectangle::from_corners(self.aabb.min.into_array(), self.aabb.max.into_array())
291            .distance_2_if_less_or_equal(point, max_distance_2)
292    }
293}
294
295pub struct DensityFieldSpatialExtractor;
296
297impl SpatialExtractor for DensityFieldSpatialExtractor {
298    type SpatialObject = DensityFieldSpatialObject;
299
300    fn extract<const LOCKING: bool>(
301        world: &World,
302    ) -> impl Iterator<Item = (Entity, Self::SpatialObject)> {
303        let view = PhysicsAccessView::new(world);
304        world
305            .query::<LOCKING, (
306                Entity,
307                &DensityFieldBox,
308                Option<&CollisionProfile>,
309                &Relation<BodyParentRelation>,
310            )>()
311            .flat_map(move |(entity, density_field, collision_profile, parents)| {
312                let view = view.clone();
313                parents.iter().map(move |(_, parent)| {
314                    let info = BodyAccessInfo {
315                        entity: parent,
316                        view: view.clone(),
317                    };
318                    let aabb = density_field.aabb(&info);
319                    (
320                        entity,
321                        DensityFieldSpatialObject {
322                            body_entity: parent,
323                            aabb,
324                            collision_profile: collision_profile.cloned().unwrap_or_default(),
325                        },
326                    )
327                })
328            })
329    }
330}
331
332#[derive(Debug)]
333struct Contact {
334    cells_range: Range<usize>,
335    bodies: [Entity; 2],
336    density_fields: [Entity; 2],
337    overlap_region: Aabb<Scalar>,
338}
339
340#[derive(Debug, Clone, Copy, PartialEq)]
341pub struct DensityFieldContact<'a> {
342    pub cells: &'a [ShapeOverlapCell],
343    pub bodies: [Entity; 2],
344    pub density_fields: [Entity; 2],
345    pub overlap_region: Aabb<Scalar>,
346}
347
348#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
349pub struct EntityPair([Entity; 2]);
350
351impl EntityPair {
352    pub fn new(a: Entity, b: Entity) -> Self {
353        if a < b { Self([a, b]) } else { Self([b, a]) }
354    }
355
356    pub fn from_array([a, b]: [Entity; 2]) -> Self {
357        Self::new(a, b)
358    }
359
360    pub fn into_array(self) -> [Entity; 2] {
361        self.0
362    }
363
364    pub fn a(&self) -> Entity {
365        self.0[0]
366    }
367
368    pub fn b(&self) -> Entity {
369        self.0[1]
370    }
371
372    pub fn has(&self, entity: Entity) -> bool {
373        self.0[0] == entity || self.0[1] == entity
374    }
375}
376
377impl From<[Entity; 2]> for EntityPair {
378    fn from(array: [Entity; 2]) -> Self {
379        Self::from_array(array)
380    }
381}
382
383#[derive(Debug, Default)]
384pub struct ContactsCache {
385    cells: Vec<ShapeOverlapCell>,
386    overlapping_contacts: HashMap<EntityPair, Contact>,
387    blocking_contacts: HashMap<EntityPair, Contact>,
388    saved_overlapping_contacts: HashMap<EntityPair, Contact>,
389    saved_blocking_contacts: HashMap<EntityPair, Contact>,
390    contacts_began: HashSet<EntityPair>,
391    contacts_ended: HashSet<EntityPair>,
392}
393
394impl ContactsCache {
395    pub fn len(&self) -> usize {
396        self.overlapping_contacts.len() + self.blocking_contacts.len()
397    }
398
399    pub fn is_empty(&self) -> bool {
400        self.overlapping_contacts.is_empty() && self.blocking_contacts.is_empty()
401    }
402
403    pub fn clear(&mut self) {
404        self.cells.clear();
405        self.overlapping_contacts.clear();
406        self.blocking_contacts.clear();
407        self.saved_overlapping_contacts.clear();
408        self.saved_blocking_contacts.clear();
409        self.contacts_began.clear();
410        self.contacts_ended.clear();
411    }
412
413    pub fn begin_contacts_update(&mut self) {
414        self.cells.clear();
415        self.saved_overlapping_contacts.clear();
416        self.saved_overlapping_contacts
417            .extend(self.overlapping_contacts.drain());
418        self.saved_blocking_contacts.clear();
419        self.saved_blocking_contacts
420            .extend(self.blocking_contacts.drain());
421    }
422
423    pub fn end_contacts_update(&mut self) {
424        self.contacts_began.clear();
425        self.contacts_began.extend(
426            self.overlapping_contacts
427                .keys()
428                .filter(|pair| !self.saved_overlapping_contacts.contains_key(pair))
429                .chain(
430                    self.blocking_contacts
431                        .keys()
432                        .filter(|pair| !self.saved_blocking_contacts.contains_key(pair)),
433                ),
434        );
435
436        self.contacts_ended.clear();
437        self.contacts_ended.extend(
438            self.saved_overlapping_contacts
439                .keys()
440                .filter(|pair| !self.overlapping_contacts.contains_key(pair))
441                .chain(
442                    self.saved_blocking_contacts
443                        .keys()
444                        .filter(|pair| !self.blocking_contacts.contains_key(pair)),
445                ),
446        );
447    }
448
449    pub fn contacts_began(&self) -> impl Iterator<Item = EntityPair> + '_ {
450        self.contacts_began.iter().copied()
451    }
452
453    pub fn contacts_ended(&self) -> impl Iterator<Item = EntityPair> + '_ {
454        self.contacts_ended.iter().copied()
455    }
456
457    pub fn cancel_contact(&mut self, a: Entity, b: Entity) {
458        let pair = EntityPair::new(a, b);
459        self.overlapping_contacts.remove(&pair);
460        self.blocking_contacts.remove(&pair);
461    }
462
463    pub fn convert_to_overlapping(&mut self, a: Entity, b: Entity) {
464        let pair = EntityPair::new(a, b);
465        if let Some(contact) = self.blocking_contacts.remove(&pair) {
466            self.overlapping_contacts.insert(pair, contact);
467        }
468    }
469
470    pub fn convert_to_blocking(&mut self, a: Entity, b: Entity) {
471        let pair = EntityPair::new(a, b);
472        if let Some(contact) = self.overlapping_contacts.remove(&pair) {
473            self.blocking_contacts.insert(pair, contact);
474        }
475    }
476
477    pub fn does_overlap(&self, a: Entity, b: Entity) -> bool {
478        let pair = EntityPair::new(a, b);
479        self.overlapping_contacts.contains_key(&pair)
480    }
481
482    pub fn does_block(&self, a: Entity, b: Entity) -> bool {
483        let pair = EntityPair::new(a, b);
484        self.blocking_contacts.contains_key(&pair)
485    }
486
487    pub fn has_contact(&self, a: Entity, b: Entity) -> bool {
488        let pair = EntityPair::new(a, b);
489        self.overlapping_contacts.contains_key(&pair) || self.blocking_contacts.contains_key(&pair)
490    }
491
492    pub fn overlapping_contact_between(&self, a: Entity, b: Entity) -> Option<DensityFieldContact> {
493        let pair = EntityPair::new(a, b);
494        self.overlapping_contacts
495            .get(&pair)
496            .map(|contact| DensityFieldContact {
497                cells: &self.cells[contact.cells_range.clone()],
498                bodies: contact.bodies,
499                density_fields: contact.density_fields,
500                overlap_region: contact.overlap_region,
501            })
502    }
503
504    pub fn blocking_contact_between(&self, a: Entity, b: Entity) -> Option<DensityFieldContact> {
505        let pair = EntityPair::new(a, b);
506        self.blocking_contacts
507            .get(&pair)
508            .map(|contact| DensityFieldContact {
509                cells: &self.cells[contact.cells_range.clone()],
510                bodies: contact.bodies,
511                density_fields: contact.density_fields,
512                overlap_region: contact.overlap_region,
513            })
514    }
515
516    pub fn any_contact_between(&self, a: Entity, b: Entity) -> Option<DensityFieldContact> {
517        self.overlapping_contact_between(a, b)
518            .or_else(|| self.blocking_contact_between(a, b))
519    }
520
521    pub fn overlapping_contacts_of(
522        &self,
523        entity: Entity,
524    ) -> impl Iterator<Item = DensityFieldContact> + '_ {
525        self.overlapping_contacts
526            .iter()
527            .filter(move |(pair, _)| pair.has(entity))
528            .map(move |(_, contact)| DensityFieldContact {
529                cells: &self.cells[contact.cells_range.clone()],
530                bodies: contact.bodies,
531                density_fields: contact.density_fields,
532                overlap_region: contact.overlap_region,
533            })
534    }
535
536    pub fn blocking_contacts_of(
537        &self,
538        entity: Entity,
539    ) -> impl Iterator<Item = DensityFieldContact> + '_ {
540        self.blocking_contacts
541            .iter()
542            .filter(move |(pair, _)| pair.has(entity))
543            .map(move |(_, contact)| DensityFieldContact {
544                cells: &self.cells[contact.cells_range.clone()],
545                bodies: contact.bodies,
546                density_fields: contact.density_fields,
547                overlap_region: contact.overlap_region,
548            })
549    }
550
551    pub fn any_contacts_of(
552        &self,
553        entity: Entity,
554    ) -> impl Iterator<Item = DensityFieldContact> + '_ {
555        self.overlapping_contacts_of(entity)
556            .chain(self.blocking_contacts_of(entity))
557    }
558
559    pub fn overlapping_contacts(&self) -> impl Iterator<Item = DensityFieldContact> + '_ {
560        self.overlapping_contacts
561            .values()
562            .map(move |contact| DensityFieldContact {
563                cells: &self.cells[contact.cells_range.clone()],
564                bodies: contact.bodies,
565                density_fields: contact.density_fields,
566                overlap_region: contact.overlap_region,
567            })
568    }
569
570    pub fn blocking_contacts(&self) -> impl Iterator<Item = DensityFieldContact> + '_ {
571        self.blocking_contacts
572            .values()
573            .map(move |contact| DensityFieldContact {
574                cells: &self.cells[contact.cells_range.clone()],
575                bodies: contact.bodies,
576                density_fields: contact.density_fields,
577                overlap_region: contact.overlap_region,
578            })
579    }
580
581    pub fn any_contacts(&self) -> impl Iterator<Item = DensityFieldContact> + '_ {
582        self.overlapping_contacts().chain(self.blocking_contacts())
583    }
584}
585
586pub fn collect_contacts<const LOCKING: bool>(context: SystemContext) -> Result<(), Box<dyn Error>> {
587    let (world, mut contacts, spatial, density_field_lookup, shape_query_local) = context
588        .fetch::<(
589            &World,
590            Res<LOCKING, &mut ContactsCache>,
591            Res<LOCKING, &SpatialPartitioning<DensityFieldSpatialExtractor>>,
592            // density field lookup
593            Lookup<LOCKING, (&DensityFieldBox, &ContactDetection)>,
594            Local<LOCKING, &ShapeOverlapQuery>,
595        )>()?;
596
597    contacts.begin_contacts_update();
598
599    let view = PhysicsAccessView::new(world);
600    let mut lookup_access = density_field_lookup.lookup_access(world);
601    let tree = spatial.tree();
602
603    for a in tree.iter() {
604        for b in tree.locate_in_envelope_intersecting(&a.envelope()) {
605            if a.data == b.data {
606                continue;
607            }
608            let pair = EntityPair::new(a.data, b.data);
609            if contacts.blocking_contacts.contains_key(&pair)
610                || contacts.overlapping_contacts.contains_key(&pair)
611            {
612                continue;
613            }
614
615            let is_overlapping = a
616                .geom()
617                .collision_profile
618                .does_overlap_permissive(&b.geom().collision_profile);
619            let is_blocking = a
620                .geom()
621                .collision_profile
622                .does_block(&b.geom().collision_profile);
623            if !is_overlapping && !is_blocking {
624                continue;
625            }
626
627            let Some((field_a, detection_a)) = lookup_access.access(a.data) else {
628                continue;
629            };
630            let Some((field_b, detection_b)) = lookup_access.access(b.data) else {
631                continue;
632            };
633            if !detection_a.enabled || !detection_b.enabled {
634                continue;
635            }
636
637            let fields: [&dyn DensityField; 2] = [&**field_a, &**field_b];
638            let infos = [
639                &BodyAccessInfo {
640                    entity: a.geom().body_entity,
641                    view: view.clone(),
642                },
643                &BodyAccessInfo {
644                    entity: b.geom().body_entity,
645                    view: view.clone(),
646                },
647            ];
648            let mut query = shape_query_local.clone();
649            query.region_limit = if let Some(region_limit) = query.region_limit {
650                Some(
651                    a.geom()
652                        .aabb
653                        .intersection(b.geom().aabb)
654                        .intersection(region_limit),
655                )
656            } else {
657                Some(a.geom().aabb.intersection(b.geom().aabb))
658            };
659            for detection in [detection_a, detection_b] {
660                if let Some(value) = detection.density_threshold {
661                    query.density_threshold = query.density_threshold.min(value);
662                }
663                if let Some(value) = detection.voxelization_size_limit {
664                    query.voxelization_size_limit = query.voxelization_size_limit.min(value);
665                }
666            }
667            query.depth_limit = query
668                .depth_limit
669                .min(detection_a.depth_limit)
670                .min(detection_b.depth_limit);
671            let start = contacts.cells.len();
672            let Some(overlap_region) = query.query_field_pair(fields, infos, &mut contacts.cells)
673            else {
674                continue;
675            };
676            let end = contacts.cells.len();
677            if end > start {
678                let contact = Contact {
679                    cells_range: start..end,
680                    bodies: [a.geom().body_entity, b.geom().body_entity],
681                    density_fields: [a.data, b.data],
682                    overlap_region,
683                };
684                if is_blocking {
685                    contacts.blocking_contacts.insert(pair, contact);
686                } else {
687                    contacts.overlapping_contacts.insert(pair, contact);
688                }
689            }
690        }
691    }
692
693    contacts.end_contacts_update();
694
695    Ok(())
696}
697
698pub fn dispatch_contact_events<const LOCKING: bool>(
699    context: SystemContext,
700) -> Result<(), Box<dyn Error>> {
701    let (world, contacts, events_lookup) = context.fetch::<(
702        &World,
703        Res<LOCKING, &ContactsCache>,
704        // body lookup.
705        Lookup<LOCKING, &EventDispatcher<ContactEvent>>,
706    )>()?;
707
708    let mut events_lookup = events_lookup.lookup_access(world);
709
710    for (contact, blocking, began) in contacts
711        .blocking_contacts
712        .values()
713        .map(|contact| {
714            (
715                contact,
716                true,
717                !contacts
718                    .saved_blocking_contacts
719                    .contains_key(&EntityPair::from_array(contact.density_fields)),
720            )
721        })
722        .chain(contacts.overlapping_contacts.values().map(|contact| {
723            (
724                contact,
725                false,
726                !contacts
727                    .saved_overlapping_contacts
728                    .contains_key(&EntityPair::from_array(contact.density_fields)),
729            )
730        }))
731    {
732        let body_events = contact.bodies.map(|entity| events_lookup.access(entity));
733
734        if began {
735            if let Some(event) = body_events[0] {
736                event.dispatch(&ContactEvent {
737                    kind: ContactEventKind::Began,
738                    blocking,
739                    self_body: contact.bodies[0],
740                    other_body: contact.bodies[1],
741                    self_density_field: contact.density_fields[0],
742                    other_density_field: contact.density_fields[1],
743                });
744            }
745            if let Some(event) = body_events[1] {
746                event.dispatch(&ContactEvent {
747                    kind: ContactEventKind::Began,
748                    blocking,
749                    self_body: contact.bodies[1],
750                    other_body: contact.bodies[0],
751                    self_density_field: contact.density_fields[1],
752                    other_density_field: contact.density_fields[0],
753                });
754            }
755        } else {
756            if let Some(event) = body_events[0] {
757                event.dispatch(&ContactEvent {
758                    kind: ContactEventKind::Continue,
759                    blocking,
760                    self_body: contact.bodies[0],
761                    other_body: contact.bodies[1],
762                    self_density_field: contact.density_fields[0],
763                    other_density_field: contact.density_fields[1],
764                });
765            }
766            if let Some(event) = body_events[1] {
767                event.dispatch(&ContactEvent {
768                    kind: ContactEventKind::Continue,
769                    blocking,
770                    self_body: contact.bodies[1],
771                    other_body: contact.bodies[0],
772                    self_density_field: contact.density_fields[1],
773                    other_density_field: contact.density_fields[0],
774                });
775            }
776        }
777    }
778
779    for (contact, blocking) in contacts
780        .saved_blocking_contacts
781        .iter()
782        .filter(|(pair, _)| !contacts.blocking_contacts.contains_key(pair))
783        .map(|(_, contact)| (contact, true))
784        .chain(
785            contacts
786                .saved_overlapping_contacts
787                .iter()
788                .filter(|(pair, _)| !contacts.overlapping_contacts.contains_key(pair))
789                .map(|(_, contact)| (contact, false)),
790        )
791    {
792        let body_events = contact.bodies.map(|entity| events_lookup.access(entity));
793
794        if let Some(event) = body_events[0] {
795            event.dispatch(&ContactEvent {
796                kind: ContactEventKind::Ended,
797                blocking,
798                self_body: contact.bodies[0],
799                other_body: contact.bodies[1],
800                self_density_field: contact.density_fields[0],
801                other_density_field: contact.density_fields[1],
802            });
803        }
804        if let Some(event) = body_events[1] {
805            event.dispatch(&ContactEvent {
806                kind: ContactEventKind::Ended,
807                blocking,
808                self_body: contact.bodies[1],
809                other_body: contact.bodies[0],
810                self_density_field: contact.density_fields[1],
811                other_density_field: contact.density_fields[0],
812            });
813        }
814    }
815
816    Ok(())
817}
818
819pub struct RepulsiveCollisionCorrection<'a> {
820    pub linear_correction: &'a mut Vec3<Scalar>,
821    pub angular_correction: &'a mut Vec3<Scalar>,
822    pub position: &'a Position,
823    pub rotation: Option<&'a Rotation>,
824    pub contact: DensityFieldContact<'a>,
825    pub body_index: usize,
826    pub weight: [Scalar; 2],
827    pub inverse_mass: [Scalar; 2],
828    pub callbacks: &'a RepulsiveCollisionCallbacks,
829}
830
831pub struct RepulsiveCollisionModifier<'a> {
832    pub penetration: &'a mut Scalar,
833    pub normal: &'a mut Vec3<Scalar>,
834    pub point: &'a mut Vec3<Scalar>,
835    pub position: &'a Position,
836    pub rotation: Option<&'a Rotation>,
837    pub contact: DensityFieldContact<'a>,
838    pub body_index: usize,
839    pub inverse_mass: [Scalar; 2],
840    pub callbacks: &'a RepulsiveCollisionCallbacks,
841}
842
843pub struct RepulsiveCollisionCallbacks {
844    #[allow(clippy::type_complexity)]
845    corrections: Vec<Box<dyn Fn(RepulsiveCollisionCorrection<'_>) + Send + Sync>>,
846    #[allow(clippy::type_complexity)]
847    modifiers: Vec<Box<dyn Fn(RepulsiveCollisionModifier<'_>) + Send + Sync>>,
848}
849
850impl Default for RepulsiveCollisionCallbacks {
851    fn default() -> Self {
852        Self::empty().correction(default_repulsive_collision_correction)
853    }
854}
855
856impl RepulsiveCollisionCallbacks {
857    pub fn empty() -> Self {
858        Self {
859            corrections: Default::default(),
860            modifiers: Default::default(),
861        }
862    }
863
864    pub fn correction(
865        mut self,
866        callback: impl Fn(RepulsiveCollisionCorrection<'_>) + Send + Sync + 'static,
867    ) -> Self {
868        self.corrections.push(Box::new(callback));
869        self
870    }
871
872    pub fn modifier(
873        mut self,
874        callback: impl Fn(RepulsiveCollisionModifier<'_>) + Send + Sync + 'static,
875    ) -> Self {
876        self.modifiers.push(Box::new(callback));
877        self
878    }
879
880    pub fn run_corrections(&self, correction: RepulsiveCollisionCorrection<'_>) {
881        if self.corrections.is_empty() {
882            return;
883        }
884
885        let RepulsiveCollisionCorrection {
886            linear_correction,
887            angular_correction,
888            position,
889            rotation,
890            contact,
891            body_index,
892            weight,
893            inverse_mass,
894            callbacks,
895        } = correction;
896
897        for callback in &self.corrections {
898            callback(RepulsiveCollisionCorrection {
899                linear_correction,
900                angular_correction,
901                position,
902                rotation,
903                contact,
904                body_index,
905                weight,
906                inverse_mass,
907                callbacks,
908            });
909        }
910    }
911
912    pub fn run_modifiers(&self, modifier: RepulsiveCollisionModifier<'_>) {
913        if self.modifiers.is_empty() {
914            return;
915        }
916
917        let RepulsiveCollisionModifier {
918            penetration,
919            normal,
920            point,
921            position,
922            rotation,
923            contact,
924            body_index,
925            inverse_mass,
926            callbacks,
927        } = modifier;
928
929        for callback in &self.modifiers {
930            callback(RepulsiveCollisionModifier {
931                penetration,
932                normal,
933                point,
934                position,
935                rotation,
936                contact,
937                body_index,
938                inverse_mass,
939                callbacks,
940            });
941        }
942    }
943}
944
945pub struct RepulsiveCollisionSolver<const LOCKING: bool>;
946
947impl<const LOCKING: bool> System for RepulsiveCollisionSolver<LOCKING> {
948    fn run(&self, context: SystemContext) -> Result<(), Box<dyn Error>> {
949        let (world, simulation, contacts, body_lookup, particle_lookup, callbacks) = context
950            .fetch::<(
951                &World,
952                Res<LOCKING, &PhysicsSimulation>,
953                Res<LOCKING, &ContactsCache>,
954                // body lookup
955                Lookup<
956                    LOCKING,
957                    (
958                        Option<&Relation<BodyParticleRelation>>,
959                        Option<&Mass>,
960                        Include<PhysicsBody>,
961                    ),
962                >,
963                // particle lookup
964                Lookup<
965                    LOCKING,
966                    (
967                        &mut Position,
968                        Option<&mut Rotation>,
969                        &mut LinearVelocity,
970                        Option<&mut AngularVelocity>,
971                        Include<PhysicsParticle>,
972                    ),
973                >,
974                Local<LOCKING, &RepulsiveCollisionCallbacks>,
975            )>()?;
976
977        if contacts.is_empty() {
978            return Ok(());
979        }
980
981        let inverse_delta_time = simulation.inverse_delta_time();
982        let mut body_lookup_access = body_lookup.lookup_access(world);
983        let mut particle_lookup_access = particle_lookup.lookup_access(world);
984
985        for contact in contacts.blocking_contacts() {
986            let body_access = contact
987                .bodies
988                .map(|entity| body_lookup_access.access(entity));
989            let Some((relations_a, mass_a, _)) = body_access[0] else {
990                continue;
991            };
992            let Some((relations_b, mass_b, _)) = body_access[1] else {
993                continue;
994            };
995            if (mass_a.is_none() && mass_b.is_none())
996                || (relations_a.is_none() && relations_b.is_none())
997            {
998                continue;
999            }
1000
1001            let inverse_mass_a = mass_a.map(|mass| mass.inverse()).unwrap_or_default();
1002            let inverse_mass_b = mass_b.map(|mass| mass.inverse()).unwrap_or_default();
1003            let inverse_mass = [inverse_mass_a, inverse_mass_b];
1004
1005            let weight_a = inverse_mass_a / (inverse_mass_a + inverse_mass_b);
1006            let weight_b = 1.0 - weight_a;
1007            let weight = [weight_a, weight_b];
1008
1009            for (entity, body_index) in relations_a
1010                .into_iter()
1011                .flat_map(|relation| relation.iter())
1012                .map(|(_, entity)| (entity, 0))
1013                .chain(
1014                    relations_b
1015                        .into_iter()
1016                        .flat_map(|relation| relation.iter())
1017                        .map(|(_, entity)| (entity, 1)),
1018                )
1019            {
1020                let Some((position, rotation, linear_velocity, angular_velocity, _)) =
1021                    particle_lookup_access.access(entity)
1022                else {
1023                    continue;
1024                };
1025
1026                let mut linear_correction = Vec3::<Scalar>::zero();
1027                let mut angular_correction = Vec3::<Scalar>::zero();
1028
1029                callbacks.run_corrections(RepulsiveCollisionCorrection {
1030                    linear_correction: &mut linear_correction,
1031                    angular_correction: &mut angular_correction,
1032                    position,
1033                    rotation: rotation.as_deref(),
1034                    contact,
1035                    body_index,
1036                    weight,
1037                    inverse_mass,
1038                    callbacks: &callbacks,
1039                });
1040
1041                position.current += linear_correction;
1042                linear_velocity.value += linear_correction * inverse_delta_time;
1043
1044                if let Some(rotation) = rotation {
1045                    let angle = angular_correction.magnitude();
1046                    if angle > Scalar::EPSILON {
1047                        let axis = angular_correction / angle;
1048                        let delta = quat_from_axis_angle(axis, angle);
1049                        rotation.current = (rotation.current * delta).normalized();
1050
1051                        if let Some(angular_velocity) = angular_velocity {
1052                            let axis = angular_correction / angle;
1053                            angular_velocity.value += axis * (angle * inverse_delta_time);
1054                        }
1055                    }
1056                }
1057            }
1058        }
1059
1060        Ok(())
1061    }
1062}
1063
1064pub fn default_repulsive_collision_correction(correction: RepulsiveCollisionCorrection) {
1065    let RepulsiveCollisionCorrection {
1066        linear_correction,
1067        angular_correction,
1068        position,
1069        rotation,
1070        contact,
1071        body_index,
1072        weight,
1073        inverse_mass,
1074        callbacks,
1075    } = correction;
1076
1077    let mut normal = contact
1078        .cells
1079        .iter()
1080        .map(|cell| cell.normal[body_index])
1081        .sum::<Vec3<Scalar>>()
1082        .try_normalized()
1083        .unwrap_or_default();
1084    let mut penetration = 0.0;
1085    let mut total_area = 0.0;
1086    let mut response_normal = Vec3::<Scalar>::zero();
1087    let mut center_of_mass = Vec3::<Scalar>::zero();
1088    for cell in contact.cells {
1089        let area = cell.area();
1090        penetration += Vec3::from(cell.region.size()).dot(normal).abs() * area;
1091        total_area += area;
1092        response_normal += cell.normal_response(body_index);
1093        center_of_mass += cell.region.center();
1094    }
1095    penetration /= total_area;
1096    response_normal = response_normal.try_normalized().unwrap_or_default();
1097    center_of_mass /= contact.cells.len() as Scalar;
1098    let mut point = position.current;
1099
1100    callbacks.run_modifiers(RepulsiveCollisionModifier {
1101        penetration: &mut penetration,
1102        normal: &mut normal,
1103        point: &mut point,
1104        position,
1105        rotation,
1106        contact,
1107        body_index,
1108        inverse_mass,
1109        callbacks,
1110    });
1111
1112    let impulse = response_normal * penetration * inverse_mass[body_index];
1113    *linear_correction += impulse * weight[body_index];
1114    if rotation.is_some() {
1115        *angular_correction += (point - center_of_mass).cross(impulse) * weight[body_index];
1116    }
1117}
1118
1119#[cfg(test)]
1120mod tests {
1121    #![allow(clippy::approx_constant)]
1122
1123    use super::*;
1124    use crate::{
1125        PhysicsPlugin,
1126        components::{BodyDensityFieldRelation, ExternalForces, LinearVelocity, PhysicsBody},
1127        density_fields::{aabb::AabbDensityField, sphere::SphereDensityField},
1128    };
1129    use anput::{scheduler::GraphScheduler, third_party::anput_jobs::Jobs, universe::Universe};
1130    use vek::Vec3;
1131
1132    #[test]
1133    fn test_entity_pair() {
1134        let a = Entity::new(0, 0).unwrap();
1135        let b = Entity::new(1, 0).unwrap();
1136        let c = Entity::new(0, 1).unwrap();
1137        let d = Entity::new(1, 1).unwrap();
1138
1139        assert_eq!(EntityPair::new(a, b), EntityPair([a, b]));
1140        assert_eq!(EntityPair::new(b, a), EntityPair([a, b]));
1141        assert_eq!(EntityPair::new(c, d), EntityPair([c, d]));
1142        assert_eq!(EntityPair::new(d, c), EntityPair([c, d]));
1143        assert_eq!(EntityPair::new(a, c), EntityPair([a, c]));
1144        assert_eq!(EntityPair::new(c, a), EntityPair([a, c]));
1145        assert_eq!(EntityPair::new(b, c), EntityPair([b, c]));
1146        assert_eq!(EntityPair::new(c, b), EntityPair([b, c]));
1147    }
1148
1149    #[test]
1150    fn test_collision_profile() {
1151        let a = CollisionProfile::default();
1152        let b = CollisionProfile::default().with_block(CollisionMask::flag(0));
1153        let c = CollisionProfile::default().with_block(CollisionMask::flag(1));
1154        let d = CollisionProfile::default()
1155            .with_block(CollisionMask::flag(0))
1156            .with_block(CollisionMask::flag(1));
1157
1158        assert!(!a.does_block(&a));
1159        assert!(!a.does_block(&b));
1160        assert!(!a.does_block(&c));
1161        assert!(!a.does_block(&d));
1162
1163        assert!(!b.does_block(&a));
1164        assert!(b.does_block(&b));
1165        assert!(!b.does_block(&c));
1166        assert!(b.does_block(&d));
1167
1168        assert!(!c.does_block(&a));
1169        assert!(!c.does_block(&b));
1170        assert!(c.does_block(&c));
1171        assert!(c.does_block(&d));
1172
1173        assert!(!d.does_block(&a));
1174        assert!(d.does_block(&b));
1175        assert!(d.does_block(&c));
1176        assert!(d.does_block(&d));
1177    }
1178
1179    #[test]
1180    fn test_collision_system() -> Result<(), Box<dyn Error>> {
1181        let mut universe = Universe::default().with_plugin(
1182            PhysicsPlugin::<true>::default()
1183                .simulation(PhysicsSimulation {
1184                    delta_time: 1.0,
1185                    ..Default::default()
1186                })
1187                .make(),
1188        );
1189        let jobs = Jobs::default();
1190        let scheduler = GraphScheduler::<true>::default();
1191
1192        let a = universe.simulation.spawn((
1193            PhysicsBody,
1194            DensityFieldBox::new(AabbDensityField {
1195                aabb: Aabb {
1196                    min: Vec3::new(-100.0, -100.0, 0.0),
1197                    max: Vec3::new(100.0, 0.0, 0.0),
1198                },
1199                density: 1.0,
1200            }),
1201            CollisionProfile::default().with_block(CollisionMask::flag(0)),
1202            ContactDetection::default(),
1203        ))?;
1204        universe
1205            .simulation
1206            .relate::<true, _>(BodyDensityFieldRelation, a, a)
1207            .unwrap();
1208        universe
1209            .simulation
1210            .relate::<true, _>(BodyParentRelation, a, a)
1211            .unwrap();
1212
1213        let b = universe.simulation.spawn((
1214            PhysicsBody,
1215            PhysicsParticle,
1216            DensityFieldBox::new(SphereDensityField::<true>::new_hard(1.0, 10.0)),
1217            CollisionProfile::default().with_block(CollisionMask::flag(0)),
1218            ContactDetection {
1219                depth_limit: 0,
1220                ..Default::default()
1221            },
1222            Mass::new(1.0),
1223            Position::new(Vec3::new(0.0, 10.0, 0.0)),
1224            LinearVelocity {
1225                value: Vec3::new(-5.0, -5.0, 0.0),
1226            },
1227            ExternalForces::default(),
1228        ))?;
1229        universe
1230            .simulation
1231            .relate::<true, _>(BodyParticleRelation, b, b)
1232            .unwrap();
1233        universe
1234            .simulation
1235            .relate::<true, _>(BodyDensityFieldRelation, b, b)
1236            .unwrap();
1237        universe
1238            .simulation
1239            .relate::<true, _>(BodyParentRelation, b, b)
1240            .unwrap();
1241
1242        scheduler.run(&jobs, &mut universe)?;
1243
1244        // TODO: responses are a bit delayed? might be worth looking into it.
1245        assert_eq!(
1246            universe
1247                .simulation
1248                .component::<true, Position>(b)
1249                .unwrap()
1250                .current,
1251            Vec3::new(-5.0, 5.0, 0.0)
1252        );
1253        assert_eq!(
1254            universe
1255                .simulation
1256                .component::<true, LinearVelocity>(b)
1257                .unwrap()
1258                .value,
1259            Vec3::new(-5.0, -5.0, 0.0)
1260        );
1261
1262        Ok(())
1263    }
1264}