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 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 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 Lookup<
1021 LOCKING,
1022 (
1023 Option<&Relation<BodyParticleRelation>>,
1024 Option<&Mass>,
1025 Option<&BodyMaterial>,
1026 Include<PhysicsBody>,
1027 ),
1028 >,
1029 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 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 }
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 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}