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