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