anput_physics/
collisions.rs

1use crate::{
2    PhysicsAccessView, PhysicsSimulation, Scalar,
3    components::{
4        AngularVelocity, BodyAccessInfo, BodyMaterial, BodyParentRelation, BodyParticleRelation,
5        LinearVelocity, 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    movement_since_last_step: Vec3<Scalar>,
339}
340
341#[derive(Debug, Clone, Copy, PartialEq)]
342pub struct DensityFieldContact<'a> {
343    pub cells: &'a [ShapeOverlapCell],
344    pub bodies: [Entity; 2],
345    pub density_fields: [Entity; 2],
346    pub overlap_region: Aabb<Scalar>,
347    pub movement_since_last_step: Vec3<Scalar>,
348}
349
350#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
351pub struct EntityPair([Entity; 2]);
352
353impl EntityPair {
354    pub fn new(a: Entity, b: Entity) -> Self {
355        if a < b { Self([a, b]) } else { Self([b, a]) }
356    }
357
358    pub fn from_array([a, b]: [Entity; 2]) -> Self {
359        Self::new(a, b)
360    }
361
362    pub fn into_array(self) -> [Entity; 2] {
363        self.0
364    }
365
366    pub fn a(&self) -> Entity {
367        self.0[0]
368    }
369
370    pub fn b(&self) -> Entity {
371        self.0[1]
372    }
373
374    pub fn has(&self, entity: Entity) -> bool {
375        self.0[0] == entity || self.0[1] == entity
376    }
377}
378
379impl From<[Entity; 2]> for EntityPair {
380    fn from(array: [Entity; 2]) -> Self {
381        Self::from_array(array)
382    }
383}
384
385#[derive(Debug, Default)]
386pub struct ContactsCache {
387    cells: Vec<ShapeOverlapCell>,
388    overlapping_contacts: HashMap<EntityPair, Contact>,
389    blocking_contacts: HashMap<EntityPair, Contact>,
390    saved_overlapping_contacts: HashMap<EntityPair, Contact>,
391    saved_blocking_contacts: HashMap<EntityPair, Contact>,
392    saved_contact_center_of_mass: HashMap<EntityPair, Vec3<Scalar>>,
393    contacts_began: HashSet<EntityPair>,
394    contacts_ended: HashSet<EntityPair>,
395}
396
397impl ContactsCache {
398    pub fn len(&self) -> usize {
399        self.overlapping_contacts.len() + self.blocking_contacts.len()
400    }
401
402    pub fn is_empty(&self) -> bool {
403        self.overlapping_contacts.is_empty() && self.blocking_contacts.is_empty()
404    }
405
406    pub fn clear(&mut self) {
407        self.cells.clear();
408        self.overlapping_contacts.clear();
409        self.blocking_contacts.clear();
410        self.saved_overlapping_contacts.clear();
411        self.saved_blocking_contacts.clear();
412        self.saved_contact_center_of_mass.clear();
413        self.contacts_began.clear();
414        self.contacts_ended.clear();
415    }
416
417    pub fn begin_contacts_update(&mut self) {
418        self.saved_contact_center_of_mass.clear();
419        self.saved_contact_center_of_mass.extend(
420            self.overlapping_contacts
421                .iter()
422                .chain(self.blocking_contacts.iter())
423                .map(|(pair, contact)| {
424                    (
425                        *pair,
426                        self.cells[contact.cells_range.clone()]
427                            .iter()
428                            .map(|cell| cell.region.center())
429                            .sum::<Vec3<Scalar>>()
430                            / contact.cells_range.len() as Scalar,
431                    )
432                }),
433        );
434
435        self.saved_overlapping_contacts.clear();
436        self.saved_overlapping_contacts
437            .extend(self.overlapping_contacts.drain());
438
439        self.saved_blocking_contacts.clear();
440        self.saved_blocking_contacts
441            .extend(self.blocking_contacts.drain());
442
443        self.cells.clear();
444    }
445
446    pub fn end_contacts_update(&mut self) {
447        self.contacts_began.clear();
448        self.contacts_began.extend(
449            self.overlapping_contacts
450                .keys()
451                .filter(|pair| !self.saved_overlapping_contacts.contains_key(pair))
452                .chain(
453                    self.blocking_contacts
454                        .keys()
455                        .filter(|pair| !self.saved_blocking_contacts.contains_key(pair)),
456                ),
457        );
458
459        self.contacts_ended.clear();
460        self.contacts_ended.extend(
461            self.saved_overlapping_contacts
462                .keys()
463                .filter(|pair| !self.overlapping_contacts.contains_key(pair))
464                .chain(
465                    self.saved_blocking_contacts
466                        .keys()
467                        .filter(|pair| !self.blocking_contacts.contains_key(pair)),
468                ),
469        );
470    }
471
472    pub fn contacts_began(&self) -> impl Iterator<Item = EntityPair> + '_ {
473        self.contacts_began.iter().copied()
474    }
475
476    pub fn contacts_ended(&self) -> impl Iterator<Item = EntityPair> + '_ {
477        self.contacts_ended.iter().copied()
478    }
479
480    pub fn cancel_contact(&mut self, a: Entity, b: Entity) {
481        let pair = EntityPair::new(a, b);
482        self.overlapping_contacts.remove(&pair);
483        self.blocking_contacts.remove(&pair);
484    }
485
486    pub fn convert_to_overlapping(&mut self, a: Entity, b: Entity) {
487        let pair = EntityPair::new(a, b);
488        if let Some(contact) = self.blocking_contacts.remove(&pair) {
489            self.overlapping_contacts.insert(pair, contact);
490        }
491    }
492
493    pub fn convert_to_blocking(&mut self, a: Entity, b: Entity) {
494        let pair = EntityPair::new(a, b);
495        if let Some(contact) = self.overlapping_contacts.remove(&pair) {
496            self.blocking_contacts.insert(pair, contact);
497        }
498    }
499
500    pub fn does_overlap(&self, a: Entity, b: Entity) -> bool {
501        let pair = EntityPair::new(a, b);
502        self.overlapping_contacts.contains_key(&pair)
503    }
504
505    pub fn does_block(&self, a: Entity, b: Entity) -> bool {
506        let pair = EntityPair::new(a, b);
507        self.blocking_contacts.contains_key(&pair)
508    }
509
510    pub fn has_contact_between(&self, a: Entity, b: Entity) -> bool {
511        let pair = EntityPair::new(a, b);
512        self.overlapping_contacts.contains_key(&pair) || self.blocking_contacts.contains_key(&pair)
513    }
514
515    pub fn has_blocking_contact_of(&self, entity: Entity) -> bool {
516        self.blocking_contacts.keys().any(|pair| pair.has(entity))
517    }
518
519    pub fn has_overlapping_contact_of(&self, entity: Entity) -> bool {
520        self.overlapping_contacts
521            .keys()
522            .any(|pair| pair.has(entity))
523    }
524
525    pub fn has_any_contact_of(&self, entity: Entity) -> bool {
526        self.has_blocking_contact_of(entity) || self.has_overlapping_contact_of(entity)
527    }
528
529    pub fn overlapping_contact_between(&self, a: Entity, b: Entity) -> Option<DensityFieldContact> {
530        let pair = EntityPair::new(a, b);
531        self.overlapping_contacts
532            .get(&pair)
533            .map(|contact| DensityFieldContact {
534                cells: &self.cells[contact.cells_range.clone()],
535                bodies: contact.bodies,
536                density_fields: contact.density_fields,
537                overlap_region: contact.overlap_region,
538                movement_since_last_step: contact.movement_since_last_step,
539            })
540    }
541
542    pub fn blocking_contact_between(&self, a: Entity, b: Entity) -> Option<DensityFieldContact> {
543        let pair = EntityPair::new(a, b);
544        self.blocking_contacts
545            .get(&pair)
546            .map(|contact| DensityFieldContact {
547                cells: &self.cells[contact.cells_range.clone()],
548                bodies: contact.bodies,
549                density_fields: contact.density_fields,
550                overlap_region: contact.overlap_region,
551                movement_since_last_step: contact.movement_since_last_step,
552            })
553    }
554
555    pub fn any_contact_between(&self, a: Entity, b: Entity) -> Option<DensityFieldContact> {
556        self.overlapping_contact_between(a, b)
557            .or_else(|| self.blocking_contact_between(a, b))
558    }
559
560    pub fn overlapping_contacts_of(
561        &self,
562        entity: Entity,
563    ) -> impl Iterator<Item = DensityFieldContact> + '_ {
564        self.overlapping_contacts
565            .iter()
566            .filter(move |(pair, _)| pair.has(entity))
567            .map(move |(_, contact)| DensityFieldContact {
568                cells: &self.cells[contact.cells_range.clone()],
569                bodies: contact.bodies,
570                density_fields: contact.density_fields,
571                overlap_region: contact.overlap_region,
572                movement_since_last_step: contact.movement_since_last_step,
573            })
574    }
575
576    pub fn blocking_contacts_of(
577        &self,
578        entity: Entity,
579    ) -> impl Iterator<Item = DensityFieldContact> + '_ {
580        self.blocking_contacts
581            .iter()
582            .filter(move |(pair, _)| pair.has(entity))
583            .map(move |(_, contact)| DensityFieldContact {
584                cells: &self.cells[contact.cells_range.clone()],
585                bodies: contact.bodies,
586                density_fields: contact.density_fields,
587                overlap_region: contact.overlap_region,
588                movement_since_last_step: contact.movement_since_last_step,
589            })
590    }
591
592    pub fn any_contacts_of(
593        &self,
594        entity: Entity,
595    ) -> impl Iterator<Item = DensityFieldContact> + '_ {
596        self.overlapping_contacts_of(entity)
597            .chain(self.blocking_contacts_of(entity))
598    }
599
600    pub fn overlapping_contacts(&self) -> impl Iterator<Item = DensityFieldContact> + '_ {
601        self.overlapping_contacts
602            .values()
603            .map(move |contact| DensityFieldContact {
604                cells: &self.cells[contact.cells_range.clone()],
605                bodies: contact.bodies,
606                density_fields: contact.density_fields,
607                overlap_region: contact.overlap_region,
608                movement_since_last_step: contact.movement_since_last_step,
609            })
610    }
611
612    pub fn blocking_contacts(&self) -> impl Iterator<Item = DensityFieldContact> + '_ {
613        self.blocking_contacts
614            .values()
615            .map(move |contact| DensityFieldContact {
616                cells: &self.cells[contact.cells_range.clone()],
617                bodies: contact.bodies,
618                density_fields: contact.density_fields,
619                overlap_region: contact.overlap_region,
620                movement_since_last_step: contact.movement_since_last_step,
621            })
622    }
623
624    pub fn any_contacts(&self) -> impl Iterator<Item = DensityFieldContact> + '_ {
625        self.overlapping_contacts().chain(self.blocking_contacts())
626    }
627}
628
629pub fn collect_contacts<const LOCKING: bool>(context: SystemContext) -> Result<(), Box<dyn Error>> {
630    let (world, mut contacts, spatial, density_field_lookup, shape_query_local) = context
631        .fetch::<(
632            &World,
633            Res<LOCKING, &mut ContactsCache>,
634            Res<LOCKING, &SpatialPartitioning<DensityFieldSpatialExtractor>>,
635            // density field lookup
636            Lookup<LOCKING, (&DensityFieldBox, &ContactDetection)>,
637            Local<LOCKING, &ShapeOverlapQuery>,
638        )>()?;
639
640    contacts.begin_contacts_update();
641
642    let view = PhysicsAccessView::new(world);
643    let mut lookup_access = density_field_lookup.lookup_access(world);
644    let tree = spatial.tree();
645
646    for a in tree.iter() {
647        for b in tree.locate_in_envelope_intersecting(&a.envelope()) {
648            if a.data == b.data {
649                continue;
650            }
651            let pair = EntityPair::new(a.data, b.data);
652            if contacts.blocking_contacts.contains_key(&pair)
653                || contacts.overlapping_contacts.contains_key(&pair)
654            {
655                continue;
656            }
657
658            let is_overlapping = a
659                .geom()
660                .collision_profile
661                .does_overlap_permissive(&b.geom().collision_profile);
662            let is_blocking = a
663                .geom()
664                .collision_profile
665                .does_block(&b.geom().collision_profile);
666            if !is_overlapping && !is_blocking {
667                continue;
668            }
669
670            let Some((field_a, detection_a)) = lookup_access.access(a.data) else {
671                continue;
672            };
673            let Some((field_b, detection_b)) = lookup_access.access(b.data) else {
674                continue;
675            };
676            if !detection_a.enabled || !detection_b.enabled {
677                continue;
678            }
679
680            let fields: [&dyn DensityField; 2] = [&**field_a, &**field_b];
681            let infos = [
682                &BodyAccessInfo {
683                    entity: a.geom().body_entity,
684                    view: view.clone(),
685                },
686                &BodyAccessInfo {
687                    entity: b.geom().body_entity,
688                    view: view.clone(),
689                },
690            ];
691            let mut query = shape_query_local.clone();
692            query.region_limit = if let Some(region_limit) = query.region_limit {
693                Some(
694                    a.geom()
695                        .aabb
696                        .intersection(b.geom().aabb)
697                        .intersection(region_limit),
698                )
699            } else {
700                Some(a.geom().aabb.intersection(b.geom().aabb))
701            };
702            for detection in [detection_a, detection_b] {
703                if let Some(value) = detection.density_threshold {
704                    query.density_threshold = query.density_threshold.min(value);
705                }
706                if let Some(value) = detection.voxelization_size_limit {
707                    query.voxelization_size_limit = query.voxelization_size_limit.min(value);
708                }
709            }
710            query.depth_limit = query
711                .depth_limit
712                .min(detection_a.depth_limit)
713                .min(detection_b.depth_limit);
714            let start = contacts.cells.len();
715            let Some(overlap_region) = query.query_field_pair(fields, infos, &mut contacts.cells)
716            else {
717                continue;
718            };
719            let end = contacts.cells.len();
720            if end > start {
721                let center_of_mass = contacts.cells[start..end]
722                    .iter()
723                    .map(|cell| cell.region.center())
724                    .sum::<Vec3<Scalar>>()
725                    / (end - start) as Scalar;
726                let prev_center_of_mass = contacts
727                    .saved_contact_center_of_mass
728                    .get(&pair)
729                    .copied()
730                    .unwrap_or(center_of_mass);
731                let contact = Contact {
732                    cells_range: start..end,
733                    bodies: [a.geom().body_entity, b.geom().body_entity],
734                    density_fields: [a.data, b.data],
735                    overlap_region,
736                    movement_since_last_step: center_of_mass - prev_center_of_mass,
737                };
738                if is_blocking {
739                    contacts.blocking_contacts.insert(pair, contact);
740                } else {
741                    contacts.overlapping_contacts.insert(pair, contact);
742                }
743            }
744        }
745    }
746
747    contacts.end_contacts_update();
748
749    Ok(())
750}
751
752pub fn dispatch_contact_events<const LOCKING: bool>(
753    context: SystemContext,
754) -> Result<(), Box<dyn Error>> {
755    let (world, contacts, events_lookup) = context.fetch::<(
756        &World,
757        Res<LOCKING, &ContactsCache>,
758        // body lookup.
759        Lookup<LOCKING, &EventDispatcher<ContactEvent>>,
760    )>()?;
761
762    let mut events_lookup = events_lookup.lookup_access(world);
763
764    for (contact, blocking, began) in contacts
765        .blocking_contacts
766        .values()
767        .map(|contact| {
768            (
769                contact,
770                true,
771                !contacts
772                    .saved_blocking_contacts
773                    .contains_key(&EntityPair::from_array(contact.density_fields)),
774            )
775        })
776        .chain(contacts.overlapping_contacts.values().map(|contact| {
777            (
778                contact,
779                false,
780                !contacts
781                    .saved_overlapping_contacts
782                    .contains_key(&EntityPair::from_array(contact.density_fields)),
783            )
784        }))
785    {
786        let body_events = contact.bodies.map(|entity| events_lookup.access(entity));
787
788        if began {
789            if let Some(event) = body_events[0] {
790                event.dispatch(&ContactEvent {
791                    kind: ContactEventKind::Began,
792                    blocking,
793                    self_body: contact.bodies[0],
794                    other_body: contact.bodies[1],
795                    self_density_field: contact.density_fields[0],
796                    other_density_field: contact.density_fields[1],
797                });
798            }
799            if let Some(event) = body_events[1] {
800                event.dispatch(&ContactEvent {
801                    kind: ContactEventKind::Began,
802                    blocking,
803                    self_body: contact.bodies[1],
804                    other_body: contact.bodies[0],
805                    self_density_field: contact.density_fields[1],
806                    other_density_field: contact.density_fields[0],
807                });
808            }
809        } else {
810            if let Some(event) = body_events[0] {
811                event.dispatch(&ContactEvent {
812                    kind: ContactEventKind::Continue,
813                    blocking,
814                    self_body: contact.bodies[0],
815                    other_body: contact.bodies[1],
816                    self_density_field: contact.density_fields[0],
817                    other_density_field: contact.density_fields[1],
818                });
819            }
820            if let Some(event) = body_events[1] {
821                event.dispatch(&ContactEvent {
822                    kind: ContactEventKind::Continue,
823                    blocking,
824                    self_body: contact.bodies[1],
825                    other_body: contact.bodies[0],
826                    self_density_field: contact.density_fields[1],
827                    other_density_field: contact.density_fields[0],
828                });
829            }
830        }
831    }
832
833    for (contact, blocking) in contacts
834        .saved_blocking_contacts
835        .iter()
836        .filter(|(pair, _)| !contacts.blocking_contacts.contains_key(pair))
837        .map(|(_, contact)| (contact, true))
838        .chain(
839            contacts
840                .saved_overlapping_contacts
841                .iter()
842                .filter(|(pair, _)| !contacts.overlapping_contacts.contains_key(pair))
843                .map(|(_, contact)| (contact, false)),
844        )
845    {
846        let body_events = contact.bodies.map(|entity| events_lookup.access(entity));
847
848        if let Some(event) = body_events[0] {
849            event.dispatch(&ContactEvent {
850                kind: ContactEventKind::Ended,
851                blocking,
852                self_body: contact.bodies[0],
853                other_body: contact.bodies[1],
854                self_density_field: contact.density_fields[0],
855                other_density_field: contact.density_fields[1],
856            });
857        }
858        if let Some(event) = body_events[1] {
859            event.dispatch(&ContactEvent {
860                kind: ContactEventKind::Ended,
861                blocking,
862                self_body: contact.bodies[1],
863                other_body: contact.bodies[0],
864                self_density_field: contact.density_fields[1],
865                other_density_field: contact.density_fields[0],
866            });
867        }
868    }
869
870    Ok(())
871}
872
873pub struct RepulsiveCollisionCorrection<'a> {
874    pub linear_correction: &'a mut Vec3<Scalar>,
875    pub angular_correction: &'a mut Vec3<Scalar>,
876    pub contact_normal: Vec3<Scalar>,
877    pub position: &'a Position,
878    pub rotation: Option<&'a Rotation>,
879    pub contact: DensityFieldContact<'a>,
880    pub body_index: usize,
881    pub weight: [Scalar; 2],
882    pub inverse_mass: [Scalar; 2],
883    pub callbacks: &'a RepulsiveCollisionCallbacks,
884}
885
886pub struct RepulsiveCollisionModifier<'a> {
887    pub penetration: &'a mut Scalar,
888    pub point: &'a mut Vec3<Scalar>,
889    pub contact_normal: Vec3<Scalar>,
890    pub position: &'a Position,
891    pub rotation: Option<&'a Rotation>,
892    pub contact: DensityFieldContact<'a>,
893    pub body_index: usize,
894    pub inverse_mass: [Scalar; 2],
895    pub callbacks: &'a RepulsiveCollisionCallbacks,
896}
897
898pub struct RepulsiveCollisionCallbacks {
899    #[allow(clippy::type_complexity)]
900    corrections: Vec<Box<dyn Fn(RepulsiveCollisionCorrection<'_>) + Send + Sync>>,
901    #[allow(clippy::type_complexity)]
902    modifiers: Vec<Box<dyn Fn(RepulsiveCollisionModifier<'_>) + Send + Sync>>,
903}
904
905impl Default for RepulsiveCollisionCallbacks {
906    fn default() -> Self {
907        Self::empty().correction(default_repulsive_collision_correction)
908    }
909}
910
911impl RepulsiveCollisionCallbacks {
912    pub fn empty() -> Self {
913        Self {
914            corrections: Default::default(),
915            modifiers: Default::default(),
916        }
917    }
918
919    pub fn correction(
920        mut self,
921        callback: impl Fn(RepulsiveCollisionCorrection<'_>) + Send + Sync + 'static,
922    ) -> Self {
923        self.corrections.push(Box::new(callback));
924        self
925    }
926
927    pub fn modifier(
928        mut self,
929        callback: impl Fn(RepulsiveCollisionModifier<'_>) + Send + Sync + 'static,
930    ) -> Self {
931        self.modifiers.push(Box::new(callback));
932        self
933    }
934
935    pub fn run_corrections(&self, correction: RepulsiveCollisionCorrection<'_>) {
936        if self.corrections.is_empty() {
937            return;
938        }
939
940        let RepulsiveCollisionCorrection {
941            linear_correction,
942            angular_correction,
943            contact_normal,
944            position,
945            rotation,
946            contact,
947            body_index,
948            weight,
949            inverse_mass,
950            callbacks,
951        } = correction;
952
953        for callback in &self.corrections {
954            callback(RepulsiveCollisionCorrection {
955                linear_correction,
956                angular_correction,
957                contact_normal,
958                position,
959                rotation,
960                contact,
961                body_index,
962                weight,
963                inverse_mass,
964                callbacks,
965            });
966        }
967    }
968
969    pub fn run_modifiers(&self, modifier: RepulsiveCollisionModifier<'_>) {
970        if self.modifiers.is_empty() {
971            return;
972        }
973
974        let RepulsiveCollisionModifier {
975            penetration,
976            point,
977            contact_normal,
978            position,
979            rotation,
980            contact,
981            body_index,
982            inverse_mass,
983            callbacks,
984        } = modifier;
985
986        for callback in &self.modifiers {
987            callback(RepulsiveCollisionModifier {
988                penetration,
989                point,
990                contact_normal,
991                position,
992                rotation,
993                contact,
994                body_index,
995                inverse_mass,
996                callbacks,
997            });
998        }
999    }
1000}
1001
1002pub struct RepulsiveCollisionSolver<const LOCKING: bool>;
1003
1004impl<const LOCKING: bool> System for RepulsiveCollisionSolver<LOCKING> {
1005    fn run(&self, context: SystemContext) -> Result<(), Box<dyn Error>> {
1006        let (world, simulation, contacts, body_lookup, particle_lookup, callbacks) = context
1007            .fetch::<(
1008                &World,
1009                Res<LOCKING, &PhysicsSimulation>,
1010                Res<LOCKING, &ContactsCache>,
1011                // body lookup
1012                Lookup<
1013                    LOCKING,
1014                    (
1015                        Option<&Relation<BodyParticleRelation>>,
1016                        Option<&Mass>,
1017                        Option<&BodyMaterial>,
1018                        Include<PhysicsBody>,
1019                    ),
1020                >,
1021                // particle lookup
1022                Lookup<
1023                    LOCKING,
1024                    (
1025                        &mut Position,
1026                        Option<&mut Rotation>,
1027                        &mut LinearVelocity,
1028                        Option<&mut AngularVelocity>,
1029                        Include<PhysicsParticle>,
1030                    ),
1031                >,
1032                Local<LOCKING, &RepulsiveCollisionCallbacks>,
1033            )>()?;
1034
1035        if contacts.is_empty() {
1036            return Ok(());
1037        }
1038
1039        let inverse_delta_time = simulation.inverse_delta_time();
1040        let mut body_lookup_access = body_lookup.lookup_access(world);
1041        let mut particle_lookup_access = particle_lookup.lookup_access(world);
1042
1043        for contact in contacts.blocking_contacts() {
1044            let body_access = contact
1045                .bodies
1046                .map(|entity| body_lookup_access.access(entity));
1047            let Some((relations_a, mass_a, material_a, _)) = body_access[0] else {
1048                continue;
1049            };
1050            let Some((relations_b, mass_b, material_b, _)) = body_access[1] else {
1051                continue;
1052            };
1053            if (mass_a.is_none() && mass_b.is_none())
1054                || (relations_a.is_none() && relations_b.is_none())
1055            {
1056                continue;
1057            }
1058
1059            let inverse_mass_a = mass_a.map(|mass| mass.inverse()).unwrap_or_default();
1060            let inverse_mass_b = mass_b.map(|mass| mass.inverse()).unwrap_or_default();
1061            let inverse_mass = [inverse_mass_a, inverse_mass_b];
1062
1063            let material_a = material_a.copied().unwrap_or_default();
1064            let material_b = material_b.copied().unwrap_or_default();
1065            let material = [material_a, material_b];
1066
1067            let weight_a = inverse_mass_a / (inverse_mass_a + inverse_mass_b);
1068            let weight_b = 1.0 - weight_a;
1069            let weight = [weight_a, weight_b];
1070
1071            for (entity, body_index) in relations_a
1072                .into_iter()
1073                .flat_map(|relation| relation.iter())
1074                .map(|(_, entity)| (entity, 0))
1075                .chain(
1076                    relations_b
1077                        .into_iter()
1078                        .flat_map(|relation| relation.iter())
1079                        .map(|(_, entity)| (entity, 1)),
1080                )
1081            {
1082                let Some((position, rotation, linear_velocity, angular_velocity, _)) =
1083                    particle_lookup_access.access(entity)
1084                else {
1085                    continue;
1086                };
1087
1088                let mut linear_correction = Vec3::<Scalar>::zero();
1089                let mut angular_correction = Vec3::<Scalar>::zero();
1090                let contact_normal = contact
1091                    .cells
1092                    .iter()
1093                    .map(|cell| cell.normal[body_index])
1094                    .sum::<Vec3<Scalar>>()
1095                    .try_normalized()
1096                    .unwrap_or_default();
1097
1098                callbacks.run_corrections(RepulsiveCollisionCorrection {
1099                    linear_correction: &mut linear_correction,
1100                    angular_correction: &mut angular_correction,
1101                    contact_normal,
1102                    position,
1103                    rotation: rotation.as_deref(),
1104                    contact,
1105                    body_index,
1106                    weight,
1107                    inverse_mass,
1108                    callbacks: &callbacks,
1109                });
1110
1111                position.current += linear_correction;
1112                linear_velocity.value += linear_correction * inverse_delta_time;
1113
1114                if let Some(rotation) = rotation {
1115                    let angle = angular_correction.magnitude();
1116                    if angle > Scalar::EPSILON {
1117                        let axis = angular_correction / angle;
1118                        let delta = quat_from_axis_angle(axis, angle);
1119                        rotation.current = (rotation.current * delta).normalized();
1120
1121                        if let Some(angular_velocity) = angular_velocity {
1122                            let axis = angular_correction / angle;
1123                            angular_velocity.value += axis * (angle * inverse_delta_time);
1124                        }
1125                    }
1126                }
1127
1128                let relative_velocity =
1129                    linear_velocity.value - contact.movement_since_last_step * inverse_delta_time;
1130                let normal_velocity = relative_velocity.dot(contact_normal);
1131                let tangent_velocity = relative_velocity - contact_normal * normal_velocity;
1132
1133                let restitution = material[body_index].restitution;
1134                let impulse = -normal_velocity * (1.0 - restitution);
1135                linear_velocity.value += contact_normal * impulse;
1136                // TODO: angular velocity.
1137
1138                let friction = material[body_index].friction;
1139                let friction_direction = -tangent_velocity.try_normalized().unwrap_or_default();
1140                let friction_magnitude = friction * normal_velocity.abs();
1141                linear_velocity.value += friction_direction * friction_magnitude;
1142                // TODO: angular velocity.
1143            }
1144        }
1145        Ok(())
1146    }
1147}
1148
1149pub fn default_repulsive_collision_correction(correction: RepulsiveCollisionCorrection) {
1150    let RepulsiveCollisionCorrection {
1151        linear_correction,
1152        angular_correction,
1153        contact_normal,
1154        position,
1155        rotation,
1156        contact,
1157        body_index,
1158        weight,
1159        inverse_mass,
1160        callbacks,
1161    } = correction;
1162
1163    let mut penetration = 0.0;
1164    let mut total_area = 0.0;
1165    let mut response_normal = Vec3::<Scalar>::zero();
1166    let mut center_of_mass = Vec3::<Scalar>::zero();
1167    for cell in contact.cells {
1168        let area = cell.area();
1169        penetration += Vec3::from(cell.region.size()).dot(contact_normal).abs() * area;
1170        total_area += area;
1171        response_normal += cell.normal_response(body_index);
1172        center_of_mass += cell.region.center();
1173    }
1174    penetration /= total_area;
1175    response_normal = response_normal.try_normalized().unwrap_or_default();
1176    center_of_mass /= contact.cells.len() as Scalar;
1177    let mut point = position.current;
1178
1179    callbacks.run_modifiers(RepulsiveCollisionModifier {
1180        penetration: &mut penetration,
1181        point: &mut point,
1182        contact_normal,
1183        position,
1184        rotation,
1185        contact,
1186        body_index,
1187        inverse_mass,
1188        callbacks,
1189    });
1190
1191    let impulse = response_normal * penetration * inverse_mass[body_index];
1192    *linear_correction += impulse * weight[body_index];
1193    if rotation.is_some() {
1194        *angular_correction += (point - center_of_mass).cross(impulse) * weight[body_index];
1195    }
1196}
1197
1198#[cfg(test)]
1199mod tests {
1200    #![allow(clippy::approx_constant)]
1201
1202    use super::*;
1203    use crate::{
1204        PhysicsPlugin,
1205        components::{BodyDensityFieldRelation, ExternalForces, LinearVelocity, PhysicsBody},
1206        density_fields::{aabb::AabbDensityField, sphere::SphereDensityField},
1207    };
1208    use anput::{scheduler::GraphScheduler, third_party::anput_jobs::Jobs, universe::Universe};
1209    use vek::Vec3;
1210
1211    #[test]
1212    fn test_entity_pair() {
1213        let a = Entity::new(0, 0).unwrap();
1214        let b = Entity::new(1, 0).unwrap();
1215        let c = Entity::new(0, 1).unwrap();
1216        let d = Entity::new(1, 1).unwrap();
1217
1218        assert_eq!(EntityPair::new(a, b), EntityPair([a, b]));
1219        assert_eq!(EntityPair::new(b, a), EntityPair([a, b]));
1220        assert_eq!(EntityPair::new(c, d), EntityPair([c, d]));
1221        assert_eq!(EntityPair::new(d, c), EntityPair([c, d]));
1222        assert_eq!(EntityPair::new(a, c), EntityPair([a, c]));
1223        assert_eq!(EntityPair::new(c, a), EntityPair([a, c]));
1224        assert_eq!(EntityPair::new(b, c), EntityPair([b, c]));
1225        assert_eq!(EntityPair::new(c, b), EntityPair([b, c]));
1226    }
1227
1228    #[test]
1229    fn test_collision_profile() {
1230        let a = CollisionProfile::default();
1231        let b = CollisionProfile::default().with_block(CollisionMask::flag(0));
1232        let c = CollisionProfile::default().with_block(CollisionMask::flag(1));
1233        let d = CollisionProfile::default()
1234            .with_block(CollisionMask::flag(0))
1235            .with_block(CollisionMask::flag(1));
1236
1237        assert!(!a.does_block(&a));
1238        assert!(!a.does_block(&b));
1239        assert!(!a.does_block(&c));
1240        assert!(!a.does_block(&d));
1241
1242        assert!(!b.does_block(&a));
1243        assert!(b.does_block(&b));
1244        assert!(!b.does_block(&c));
1245        assert!(b.does_block(&d));
1246
1247        assert!(!c.does_block(&a));
1248        assert!(!c.does_block(&b));
1249        assert!(c.does_block(&c));
1250        assert!(c.does_block(&d));
1251
1252        assert!(!d.does_block(&a));
1253        assert!(d.does_block(&b));
1254        assert!(d.does_block(&c));
1255        assert!(d.does_block(&d));
1256    }
1257
1258    #[test]
1259    fn test_collision_system() -> Result<(), Box<dyn Error>> {
1260        let mut universe = Universe::default().with_plugin(
1261            PhysicsPlugin::<true>::default()
1262                .simulation(PhysicsSimulation {
1263                    delta_time: 1.0,
1264                    ..Default::default()
1265                })
1266                .make(),
1267        );
1268        let jobs = Jobs::default();
1269        let scheduler = GraphScheduler::<true>;
1270
1271        let a = universe.simulation.spawn((
1272            PhysicsBody,
1273            DensityFieldBox::new(AabbDensityField {
1274                aabb: Aabb {
1275                    min: Vec3::new(-100.0, -100.0, 0.0),
1276                    max: Vec3::new(100.0, 0.0, 0.0),
1277                },
1278                density: 1.0,
1279            }),
1280            CollisionProfile::default().with_block(CollisionMask::flag(0)),
1281            ContactDetection::default(),
1282        ))?;
1283        universe
1284            .simulation
1285            .relate::<true, _>(BodyDensityFieldRelation, a, a)
1286            .unwrap();
1287        universe
1288            .simulation
1289            .relate::<true, _>(BodyParentRelation, a, a)
1290            .unwrap();
1291
1292        let b = universe.simulation.spawn((
1293            PhysicsBody,
1294            PhysicsParticle,
1295            DensityFieldBox::new(SphereDensityField::<true>::new_hard(1.0, 10.0)),
1296            CollisionProfile::default().with_block(CollisionMask::flag(0)),
1297            ContactDetection {
1298                depth_limit: 0,
1299                ..Default::default()
1300            },
1301            Mass::new(1.0),
1302            Position::new(Vec3::new(0.0, 10.0, 0.0)),
1303            LinearVelocity {
1304                value: Vec3::new(-5.0, -5.0, 0.0),
1305            },
1306            ExternalForces::default(),
1307        ))?;
1308        universe
1309            .simulation
1310            .relate::<true, _>(BodyParticleRelation, b, b)
1311            .unwrap();
1312        universe
1313            .simulation
1314            .relate::<true, _>(BodyDensityFieldRelation, b, b)
1315            .unwrap();
1316        universe
1317            .simulation
1318            .relate::<true, _>(BodyParentRelation, b, b)
1319            .unwrap();
1320
1321        scheduler.run(&jobs, &mut universe)?;
1322
1323        // TODO: responses are a bit delayed? might be worth looking into it.
1324        assert_eq!(
1325            universe
1326                .simulation
1327                .component::<true, Position>(b)
1328                .unwrap()
1329                .current,
1330            Vec3::new(-5.0, 5.0, 0.0)
1331        );
1332        assert_eq!(
1333            universe
1334                .simulation
1335                .component::<true, LinearVelocity>(b)
1336                .unwrap()
1337                .value,
1338            Vec3::new(-5.0, -5.0, 0.0)
1339        );
1340
1341        Ok(())
1342    }
1343}