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 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 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 Lookup<
1013 LOCKING,
1014 (
1015 Option<&Relation<BodyParticleRelation>>,
1016 Option<&Mass>,
1017 Option<&BodyMaterial>,
1018 Include<PhysicsBody>,
1019 ),
1020 >,
1021 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 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 }
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 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}