avian3d/collision/contact_types/mod.rs
1//! Contact types and data structures used in the collision pipeline.
2
3mod contact_graph;
4mod feature_id;
5mod system_param;
6
7pub use contact_graph::{ContactGraph, ContactGraphInternal};
8pub use feature_id::PackedFeatureId;
9pub use system_param::Collisions;
10
11use crate::{data_structures::graph::EdgeIndex, prelude::*};
12use bevy::prelude::*;
13
14/// A stable identifier for a [`ContactEdge`].
15#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Reflect)]
16#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
17#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
18#[reflect(Debug, PartialEq)]
19pub struct ContactId(pub u32);
20
21impl ContactId {
22 /// A placeholder identifier for a [`ContactEdge`].
23 ///
24 /// Used as a temporary value before the contact pair is added to the [`ContactGraph`].
25 pub const PLACEHOLDER: Self = Self(u32::MAX);
26}
27
28impl From<ContactId> for EdgeIndex {
29 fn from(id: ContactId) -> Self {
30 Self(id.0)
31 }
32}
33
34impl From<EdgeIndex> for ContactId {
35 fn from(id: EdgeIndex) -> Self {
36 Self(id.0)
37 }
38}
39
40impl core::fmt::Display for ContactId {
41 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
42 write!(f, "ContactId({})", self.0)
43 }
44}
45
46/// Cold contact data stored in the [`ContactGraph`]. Used as a persistent handle for a [`ContactPair`].
47#[derive(Clone, Debug, PartialEq)]
48#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
49pub struct ContactEdge {
50 /// The stable identifier of this contact edge.
51 pub id: ContactId,
52
53 /// The first collider entity in the contact.
54 pub collider1: Entity,
55
56 /// The second collider entity in the contact.
57 pub collider2: Entity,
58
59 /// The entity of the first body involved in the contact.
60 pub body1: Option<Entity>,
61
62 /// The entity of the second body involved in the contact.
63 pub body2: Option<Entity>,
64
65 /// The index of the [`ContactPair`] in the [`ContactGraph`].
66 pub pair_index: usize,
67
68 /// Flags for the contact edge.
69 pub flags: ContactEdgeFlags,
70}
71
72impl ContactEdge {
73 /// Creates a new non-touching [`ContactEdge`] with the given entities.
74 #[inline]
75 pub fn new(collider1: Entity, collider2: Entity) -> Self {
76 Self {
77 // This gets set to a valid ID when the contact pair is added to the `ContactGraph`.
78 id: ContactId::PLACEHOLDER,
79 collider1,
80 collider2,
81 body1: None,
82 body2: None,
83 pair_index: 0,
84 flags: ContactEdgeFlags::empty(),
85 }
86 }
87
88 /// Returns `true` if the colliders are touching.
89 #[inline]
90 pub fn is_touching(&self) -> bool {
91 self.flags.contains(ContactEdgeFlags::TOUCHING)
92 }
93
94 /// Returns `true` if the contact pair is between sleeping bodies.
95 #[inline]
96 pub fn is_sleeping(&self) -> bool {
97 self.flags.contains(ContactEdgeFlags::SLEEPING)
98 }
99
100 /// Returns `true` if collision events are enabled for the contact.
101 #[inline]
102 pub fn events_enabled(&self) -> bool {
103 self.flags.contains(ContactEdgeFlags::CONTACT_EVENTS)
104 }
105}
106
107// These are stored separately from `ContactPairFlags` to avoid needing to fetch
108// the `ContactPair` when for example querying for touching contacts.
109/// Flags for a [`ContactEdge`].
110#[repr(transparent)]
111#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
112#[derive(Hash, Clone, Copy, PartialEq, Eq, Debug, Reflect)]
113#[reflect(opaque, Hash, PartialEq, Debug)]
114pub struct ContactEdgeFlags(u8);
115
116bitflags::bitflags! {
117 impl ContactEdgeFlags: u8 {
118 /// Set if the colliders are touching, including sensors.
119 const TOUCHING = 1 << 0;
120 /// Set if the contact pair is between sleeping bodies.
121 const SLEEPING = 1 << 1;
122 /// Set if the contact pair should emit contact events or sensor events.
123 const CONTACT_EVENTS = 1 << 2;
124 }
125}
126
127/// A contact pair between two colliders.
128///
129/// Each contact pair has one or more [contact manifolds](ContactManifold),
130/// which represent contact surfaces between the two colliders.
131/// Each of these manifolds contains one or more [contact points](ContactPoint).
132///
133/// Contact pairs exist in the [`ContactGraph`] between colliders whose [`ColliderAabb`]s
134/// are overlapping, even if the colliders themselves are not touching.
135#[derive(Clone, Debug, PartialEq)]
136#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
137pub struct ContactPair {
138 /// The stable identifier of the [`ContactEdge`] in the [`ContactGraph`].
139 pub contact_id: ContactId,
140 /// The first collider entity in the contact.
141 pub collider1: Entity,
142 /// The second collider entity in the contact.
143 pub collider2: Entity,
144 /// The entity of the first body involved in the contact.
145 pub body1: Option<Entity>,
146 /// The entity of the second body involved in the contact.
147 pub body2: Option<Entity>,
148 /// A list of contact manifolds between two colliders.
149 /// Each manifold contains one or more contact points, but each contact
150 /// in a given manifold shares the same contact normal.
151 pub manifolds: Vec<ContactManifold>,
152 /// The number of manifolds that were added or removed during the current time step.
153 ///
154 /// This is used to manage contact constraint updates for persistent contacts in the [`ConstraintGraph`].
155 ///
156 /// [`ConstraintGraph`]: crate::dynamics::solver::constraint_graph::ConstraintGraph
157 pub(crate) manifold_count_change: i32,
158 /// A speculative contact distance used to predict contacts before they happen.
159 /// Used for [Continuous Collision Detection (CCD)](dynamics::ccd).
160 ///
161 /// This is used by Avian to ensure that all impacts found by CCD
162 /// are handled by the discrete solver, but it can also be used
163 /// for speculative contacts in general.
164 ///
165 /// The speculative distance is reset to zero each timestep.
166 pub speculative_distance: f32,
167 /// A cache of data used for recycling contacts across timesteps.
168 pub recycling_cache: Option<ContactRecyclingCache>,
169 /// Flag indicating the status and type of the contact pair.
170 pub flags: ContactPairFlags,
171}
172
173/// A cache of data used for recycling contacts across timesteps.
174#[derive(Clone, Debug, PartialEq)]
175#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
176pub struct ContactRecyclingCache {
177 /// The cached rotation of the first collider from the previous timestep.
178 pub rotation1: Rot,
179 /// The cached rotation of the second collider from the previous timestep.
180 pub rotation2: Rot,
181 /// The cached relative translation of the two colliders from the previous timestep.
182 pub relative_translation: Vector,
183 /// The cached relative rotation of the two colliders from the previous timestep.
184 pub relative_rotation: Rot,
185}
186
187/// Flags indicating the status and type of a [contact pair](ContactPair).
188#[repr(transparent)]
189#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
190#[derive(Hash, Clone, Copy, PartialEq, Eq, Debug, Reflect)]
191#[reflect(opaque, Hash, PartialEq, Debug)]
192pub struct ContactPairFlags(u16);
193
194bitflags::bitflags! {
195 impl ContactPairFlags: u16 {
196 /// Set if the colliders are touching, including sensors.
197 const TOUCHING = 1 << 0;
198 /// Set if the AABBs of the colliders are no longer overlapping.
199 const DISJOINT_AABB = 1 << 1;
200 /// Set if the colliders are touching and were not touching previously.
201 const STARTED_TOUCHING = 1 << 2;
202 /// Set if the colliders are not touching and were touching previously.
203 const STOPPED_TOUCHING = 1 << 3;
204 /// Set if the contact pair should generate contact constraints.
205 const GENERATE_CONSTRAINTS = 1 << 4;
206 /// Set if the contact pair just started generating contact constraints,
207 /// for example because a sensor became a normal collider or a collider was attached to a rigid body.
208 const STARTED_GENERATING_CONSTRAINTS = 1 << 5;
209 /// Set if the first rigid body is static.
210 const STATIC1 = 1 << 6;
211 /// Set if the second rigid body is static.
212 const STATIC2 = 1 << 7;
213 /// Set if the contact pair should have a custom contact modification hook applied.
214 const MODIFY_CONTACTS = 1 << 8;
215 }
216}
217
218impl ContactPair {
219 /// Creates a new [`ContactPair`] with the given entities.
220 #[inline]
221 pub fn new(collider1: Entity, collider2: Entity, contact_id: ContactId) -> Self {
222 Self {
223 contact_id,
224 collider1,
225 collider2,
226 body1: None,
227 body2: None,
228 manifolds: Vec::new(),
229 manifold_count_change: 0,
230 speculative_distance: 0.0,
231 recycling_cache: None,
232 flags: ContactPairFlags::empty(),
233 }
234 }
235
236 /// Computes the sum of all impulses applied along contact normals between the contact pair.
237 ///
238 /// To get the corresponding force, divide the impulse by the time step.
239 #[inline]
240 pub fn total_normal_impulse(&self) -> Vector {
241 self.manifolds.iter().fold(Vector::ZERO, |acc, manifold| {
242 acc + manifold.normal * manifold.total_normal_impulse()
243 })
244 }
245
246 /// Computes the sum of the magnitudes of all impulses applied along contact normals between the contact pair.
247 ///
248 /// This is the sum of impulse magnitudes, *not* the magnitude of the [`total_normal_impulse`](Self::total_normal_impulse).
249 ///
250 /// To get the corresponding force, divide the impulse by the time step.
251 #[inline]
252 pub fn total_normal_impulse_magnitude(&self) -> f32 {
253 self.manifolds
254 .iter()
255 .fold(0.0, |acc, manifold| acc + manifold.total_normal_impulse())
256 }
257
258 // TODO: We could also return a reference to the whole manifold. Would that be useful?
259 /// Finds the largest normal impulse between the contact pair, pointing along the world-space contact normal
260 /// from the first shape to the second.
261 ///
262 /// To get the corresponding force, divide the impulse by the time step.
263 #[inline]
264 pub fn max_normal_impulse(&self) -> Vector {
265 let mut magnitude: f32 = f32::MIN;
266 let mut normal = Vector::ZERO;
267
268 for manifold in &self.manifolds {
269 let impulse = manifold.max_normal_impulse();
270 if impulse > magnitude {
271 magnitude = impulse;
272 normal = manifold.normal;
273 }
274 }
275 normal * magnitude
276 }
277
278 /// Finds the magnitude of the largest normal impulse between the contact pair.
279 ///
280 /// To get the corresponding force, divide the impulse by the time step.
281 #[inline]
282 pub fn max_normal_impulse_magnitude(&self) -> f32 {
283 self.manifolds
284 .iter()
285 .fold(0.0, |acc, manifold| acc.max(manifold.max_normal_impulse()))
286 }
287
288 /// Returns `true` if the colliders are touching, including sensors.
289 #[inline]
290 pub fn is_touching(&self) -> bool {
291 self.flags.contains(ContactPairFlags::TOUCHING)
292 }
293
294 /// Returns `true` if the AABBs of the colliders are no longer overlapping.
295 #[inline]
296 pub fn aabbs_disjoint(&self) -> bool {
297 self.flags.contains(ContactPairFlags::DISJOINT_AABB)
298 }
299
300 /// Returns `true` if a collision started during the current frame.
301 #[inline]
302 pub fn collision_started(&self) -> bool {
303 self.flags.contains(ContactPairFlags::STARTED_TOUCHING)
304 }
305
306 /// Returns `true` if a collision ended during the current frame.
307 #[inline]
308 pub fn collision_ended(&self) -> bool {
309 self.flags.contains(ContactPairFlags::STOPPED_TOUCHING)
310 }
311
312 /// Returns `true` if the contact pair should generate contact constraints.
313 ///
314 /// This is typically `true` unless the contact pair involves a [`Sensor`] or a disabled rigid body.
315 #[inline]
316 pub fn generates_constraints(&self) -> bool {
317 self.flags.contains(ContactPairFlags::GENERATE_CONSTRAINTS)
318 }
319
320 /// Returns the contact with the largest penetration depth.
321 ///
322 /// If the objects are separated but there is still a speculative contact,
323 /// the penetration depth will be negative.
324 ///
325 /// If there are no contacts, `None` is returned.
326 #[inline]
327 pub fn find_deepest_contact(&self) -> Option<&ContactPoint> {
328 self.manifolds
329 .iter()
330 .filter_map(|manifold| manifold.find_deepest_contact())
331 .max_by(|a, b| {
332 a.penetration
333 .partial_cmp(&b.penetration)
334 .unwrap_or(core::cmp::Ordering::Equal)
335 })
336 }
337}
338
339/// A contact manifold describing a contact surface between two colliders,
340/// represented by a set of [contact points](ContactPoint) and surface properties.
341///
342/// A manifold can typically be a single point, a line segment, or a polygon formed by its contact points.
343/// Each contact point in a manifold shares the same contact normal.
344#[cfg_attr(
345 feature = "2d",
346 doc = "
347In 2D, contact manifolds are limited to 2 points."
348)]
349#[derive(Clone, Debug, PartialEq)]
350#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
351pub struct ContactManifold {
352 /// The contact points in this manifold. Limited to 2 points in 2D.
353 ///
354 /// Each point in a manifold shares the same `normal`.
355 #[cfg(feature = "2d")]
356 pub points: arrayvec::ArrayVec<ContactPoint, 2>,
357 /// The contact points in this manifold.
358 ///
359 /// Each point in a manifold shares the same `normal`.
360 #[cfg(feature = "3d")]
361 // TODO: Store a maximum of 4 points in 3D in an `ArrayVec`.
362 pub points: Vec<ContactPoint>,
363 /// The unit contact normal in world space, pointing from the first shape to the second.
364 ///
365 /// The same normal is shared by all `points` in a manifold.
366 pub normal: Vector,
367 /// The effective coefficient of dynamic [friction](Friction) used for the contact surface.
368 pub friction: f32,
369 /// The effective coefficient of [restitution](Restitution) used for the contact surface.
370 pub restitution: f32,
371 /// The desired relative linear speed of the bodies along the surface,
372 /// expressed in world space as `tangent_speed2 - tangent_speed1`.
373 ///
374 /// Defaults to zero. If set to a non-zero value, this can be used to simulate effects
375 /// such as conveyor belts.
376 #[cfg(feature = "2d")]
377 pub tangent_speed: f32,
378 // TODO: Jolt also supports a relative angular surface velocity, which can be used for making
379 // objects rotate on platforms. Would that be useful enough to warrant the extra memory usage?
380 /// The desired relative linear velocity of the bodies along the surface,
381 /// expressed in world space as `tangent_velocity2 - tangent_velocity1`.
382 ///
383 /// Defaults to zero. If set to a non-zero value, this can be used to simulate effects
384 /// such as conveyor belts.
385 #[cfg(feature = "3d")]
386 pub tangent_velocity: Vector,
387}
388
389impl ContactManifold {
390 /// Creates a new [`ContactManifold`] with the given contact points and surface normals,
391 /// expressed in local space.
392 ///
393 /// `index` represents the index of the manifold in the collision.
394 #[inline]
395 pub fn new(points: impl IntoIterator<Item = ContactPoint>, normal: Vector) -> Self {
396 Self {
397 #[cfg(feature = "2d")]
398 points: arrayvec::ArrayVec::from_iter(points),
399 #[cfg(feature = "3d")]
400 points: points.into_iter().collect(),
401 normal,
402 friction: 0.0,
403 restitution: 0.0,
404 #[cfg(feature = "2d")]
405 tangent_speed: 0.0,
406 #[cfg(feature = "3d")]
407 tangent_velocity: Vector::ZERO,
408 }
409 }
410
411 /// The sum of the impulses applied at the contact points in the manifold along the contact normal.
412 #[inline]
413 pub fn total_normal_impulse(&self) -> f32 {
414 self.points
415 .iter()
416 .fold(0.0, |acc, contact| acc + contact.normal_impulse)
417 }
418
419 /// The magnitude of the largest impulse applied at a contact point in the manifold along the contact normal.
420 #[inline]
421 pub fn max_normal_impulse(&self) -> f32 {
422 self.points
423 .iter()
424 .map(|contact| contact.normal_impulse)
425 .max_by(|a, b| a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal))
426 .unwrap_or(0.0)
427 }
428
429 /// Copies impulses from previous contacts to matching contacts in `self`.
430 ///
431 /// Contacts are first matched based on their [feature IDs](PackedFeatureId), and if they are unknown,
432 /// matching is done based on contact positions using the given `distance_threshold`
433 /// for determining if points are too far away from each other to be considered matching.
434 #[inline]
435 pub fn match_contacts(&mut self, previous_contacts: &[ContactPoint], distance_threshold: f32) {
436 // The squared maximum distance for two contact points to be considered matching.
437 let distance_threshold_squared = distance_threshold.powi(2);
438
439 for contact in self.points.iter_mut() {
440 for previous_contact in previous_contacts.iter() {
441 // If the feature IDs match, copy the contact impulses over for warm starting.
442 if (contact.feature_id1 == previous_contact.feature_id1
443 && contact.feature_id2 == previous_contact.feature_id2) ||
444 // we have to check both directions because the entities are sorted in order
445 // of aabb.min.x, which could have changed even the two objects in contact are the same
446 (contact.feature_id2 == previous_contact.feature_id1
447 && contact.feature_id1 == previous_contact.feature_id2)
448 {
449 contact.warm_start_normal_impulse = previous_contact.warm_start_normal_impulse;
450 contact.warm_start_tangent_impulse =
451 previous_contact.warm_start_tangent_impulse;
452 break;
453 }
454
455 let unknown_features = contact.feature_id1 == PackedFeatureId::UNKNOWN
456 || contact.feature_id2 == PackedFeatureId::UNKNOWN;
457
458 // If the feature IDs are unknown and the contact positions match closely enough,
459 // copy the contact impulses over for warm starting.
460 if unknown_features
461 && (contact.anchor1.distance_squared(previous_contact.anchor1)
462 < distance_threshold_squared
463 && contact.anchor2.distance_squared(previous_contact.anchor2)
464 < distance_threshold_squared)
465 || (contact.anchor1.distance_squared(previous_contact.anchor2)
466 < distance_threshold_squared
467 && contact.anchor2.distance_squared(previous_contact.anchor1)
468 < distance_threshold_squared)
469 {
470 contact.warm_start_normal_impulse = previous_contact.warm_start_normal_impulse;
471 contact.warm_start_tangent_impulse =
472 previous_contact.warm_start_tangent_impulse;
473 break;
474 }
475 }
476 }
477 }
478
479 /// Prunes the contact points in the manifold to a maximum of 4 points.
480 /// This is done to improve performance and stability.
481 #[inline]
482 #[cfg(feature = "3d")]
483 pub fn prune_points(&mut self) {
484 // Based on `PruneContactPoints` in Jolt by Jorrit Rouwe.
485 // https://github.com/jrouwe/JoltPhysics/blob/f3dbdd2dadac4a5510391f103f264c0427d55c50/Jolt/Physics/Collision/ManifoldBetweenTwoFaces.cpp#L16
486
487 debug_assert!(
488 self.points.len() > 4,
489 "`ContactManifold::prune_points` called on a manifold with 4 or fewer points"
490 );
491
492 // We use a heuristic of `distance_to_com * penetration` to find the points we should keep.
493 // Neither of these should become zero, so we clamp the minimum distance.
494 const MIN_DISTANCE_SQUARED: f32 = 1e-6;
495
496 // Project the contact points onto the contact normal and compute the squared penetration depths.
497 let (projected, penetrations_squared): (Vec<Vector>, Vec<f32>) = self
498 .points
499 .iter()
500 .map(|point| {
501 (
502 point.anchor1.reject_from_normalized(self.normal),
503 (point.penetration * point.penetration).max(MIN_DISTANCE_SQUARED),
504 )
505 })
506 .unzip();
507
508 // Find the point that is farthest from the center of mass, as its torque has the largest influence,
509 // while also taking into account the penetration depth heuristic.
510 let mut point1_index = 0;
511 let mut value = f32::MIN;
512 for (i, point) in projected.iter().enumerate() {
513 let v = point.length_squared().max(MIN_DISTANCE_SQUARED) * penetrations_squared[i];
514 if v > value {
515 value = v;
516 point1_index = i;
517 }
518 }
519 let point1 = projected[point1_index];
520
521 // Find the point farthest from the first point, forming a line segment.
522 // Also consider the penetration depth heuristic.
523 let mut point2_index = usize::MAX;
524 let mut max_distance = f32::MIN;
525 for (i, point) in projected.iter().enumerate() {
526 if i == point1_index {
527 continue;
528 }
529 let v =
530 point.distance_squared(point1).max(MIN_DISTANCE_SQUARED) * penetrations_squared[i];
531 if v > max_distance {
532 max_distance = v;
533 point2_index = i;
534 }
535 }
536 debug_assert!(point2_index != usize::MAX, "No second point found");
537 let point2 = projected[point2_index];
538
539 // Find the farthest points on both sides of the line segment in order to maximize the area.
540 let mut point3_index = usize::MAX;
541 let mut point4_index = usize::MAX;
542 let mut min_value = 0.0;
543 let mut max_value = 0.0;
544 let perp = (point2 - point1).cross(self.normal);
545 for (i, point) in projected.iter().enumerate() {
546 if i == point1_index || i == point2_index {
547 continue;
548 }
549 let v = perp.dot(point - point1);
550 if v < min_value {
551 min_value = v;
552 point3_index = i;
553 } else if v > max_value {
554 max_value = v;
555 point4_index = i;
556 }
557 }
558
559 // Construct the manifold, ensuring the order is correct to form a convex polygon.
560 // TODO: The points in the manifold should be in an `ArrayVec`, and the input points should be separate.
561 let points = core::mem::take(&mut self.points);
562 self.points.push(points[point1_index]);
563 if point3_index != usize::MAX {
564 self.points.push(points[point3_index]);
565 }
566 self.points.push(points[point2_index]);
567 if point4_index != usize::MAX {
568 debug_assert_ne!(point3_index, point4_index);
569 self.points.push(points[point4_index]);
570 }
571 }
572
573 /// Returns the contact point with the largest penetration depth.
574 ///
575 /// If the objects are separated but there is still a speculative contact,
576 /// the penetration depth will be negative.
577 ///
578 /// If there are no contacts, `None` is returned.
579 #[inline]
580 pub fn find_deepest_contact(&self) -> Option<&ContactPoint> {
581 self.points.iter().max_by(|a, b| {
582 a.penetration
583 .partial_cmp(&b.penetration)
584 .unwrap_or(core::cmp::Ordering::Equal)
585 })
586 }
587
588 /// Retains only the elements specified by the predicate.
589 #[inline]
590 pub(crate) fn retain_points_mut<F>(&mut self, f: F)
591 where
592 F: FnMut(&mut ContactPoint) -> bool,
593 {
594 #[cfg(feature = "2d")]
595 {
596 self.points.retain(f);
597 }
598 #[cfg(feature = "3d")]
599 {
600 self.points.retain_mut(f);
601 }
602 }
603}
604
605/// Data associated with a contact point in a [`ContactManifold`].
606#[derive(Clone, Copy, Debug, PartialEq)]
607#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
608pub struct ContactPoint {
609 /// The world-space contact point on the first shape relative to the center of mass.
610 pub anchor1: Vector,
611 /// The world-space contact point on the second shape relative to the center of mass.
612 pub anchor2: Vector,
613 /// The contact point in world space.
614 ///
615 /// This is the midpoint between the closest points on the surfaces of the two shapes.
616 ///
617 /// Note that because the contact point is expressed in world space,
618 /// it is subject to precision loss at large coordinates.
619 pub point: RVector,
620 /// The penetration depth.
621 ///
622 /// Can be negative if the objects are separated and [speculative collision] is enabled.
623 ///
624 /// [speculative collision]: crate::dynamics::ccd#speculative-collision
625 pub penetration: f32,
626 /// The original penetration depth computed by the narrow phase.
627 ///
628 /// The current [`penetration`](Self::penetration) may be updated by contact recycling
629 /// without computing the full contact manifolds again. The base penetration is stored
630 /// as a reference so that the updated penetration can be computed using the relative
631 /// motion of the two colliders.
632 pub base_penetration: f32,
633 /// The total normal impulse applied to the first body at this contact point.
634 /// The unit is typically N⋅s or kg⋅m/s.
635 ///
636 /// This can be used to determine how "strong" a contact is. To compute the corresponding
637 /// contact force, divide the impulse by the time step.
638 pub normal_impulse: f32,
639 /// The relative velocity of the bodies at the contact point along the contact normal.
640 /// If negative, the bodies are approaching. The unit is typically m/s.
641 ///
642 /// This is computed before the solver, and can be used as an impact velocity to determine
643 /// how "strong" the contact is in a mass-independent way.
644 ///
645 /// Internally, the `normal_speed` is used for restitution.
646 pub normal_speed: f32,
647 /// The normal impulse used to warm start the contact solver.
648 ///
649 /// This corresponds to the clamped accumulated impulse from the last substep
650 /// of the previous time step.
651 pub warm_start_normal_impulse: f32,
652 /// The frictional impulse used to warm start the contact solver.
653 ///
654 /// This corresponds to the clamped accumulated impulse from the last substep
655 /// of the previous time step.
656 #[cfg(feature = "2d")]
657 #[doc(alias = "warm_start_friction_impulse")]
658 pub warm_start_tangent_impulse: f32,
659 /// The frictional impulse used to warm start the contact solver.
660 ///
661 /// This corresponds to the clamped accumulated impulse from the last substep
662 /// of the previous time step.
663 #[cfg(feature = "3d")]
664 #[doc(alias = "warm_start_friction_impulse")]
665 pub warm_start_tangent_impulse: Vec2,
666 /// The contact feature ID on the first shape. This indicates the ID of
667 /// the vertex, edge, or face of the contact, if one can be determined.
668 pub feature_id1: PackedFeatureId,
669 /// The contact feature ID on the second shape. This indicates the ID of
670 /// the vertex, edge, or face of the contact, if one can be determined.
671 pub feature_id2: PackedFeatureId,
672}
673
674impl ContactPoint {
675 /// Creates a new [`ContactPoint`] with the given world-space contact points
676 /// relative to the centers of mass of the two shapes, the world-space contact point,
677 /// and the penetration depth.
678 ///
679 /// [Feature IDs](PackedFeatureId) can be specified for the contact points using [`with_feature_ids`](Self::with_feature_ids).
680 #[allow(clippy::too_many_arguments)]
681 pub fn new(anchor1: Vector, anchor2: Vector, world_point: RVector, penetration: f32) -> Self {
682 Self {
683 anchor1,
684 anchor2,
685 point: world_point,
686 penetration,
687 base_penetration: penetration,
688 normal_impulse: 0.0,
689 normal_speed: 0.0,
690 warm_start_normal_impulse: 0.0,
691 #[cfg(feature = "2d")]
692 warm_start_tangent_impulse: 0.0,
693 #[cfg(feature = "3d")]
694 warm_start_tangent_impulse: Vec2::ZERO,
695 feature_id1: PackedFeatureId::UNKNOWN,
696 feature_id2: PackedFeatureId::UNKNOWN,
697 }
698 }
699
700 /// Sets the [feature IDs](PackedFeatureId) of the contact points.
701 #[inline]
702 pub fn with_feature_ids(mut self, id1: PackedFeatureId, id2: PackedFeatureId) -> Self {
703 self.feature_id1 = id1;
704 self.feature_id2 = id2;
705 self
706 }
707
708 /// Flips the contact data, swapping the points and feature IDs,
709 /// and negating the impulses.
710 #[inline]
711 pub fn flip(&mut self) {
712 core::mem::swap(&mut self.anchor1, &mut self.anchor2);
713 core::mem::swap(&mut self.feature_id1, &mut self.feature_id2);
714 self.normal_impulse = -self.normal_impulse;
715 self.warm_start_normal_impulse = -self.warm_start_normal_impulse;
716 self.warm_start_tangent_impulse = -self.warm_start_tangent_impulse;
717 }
718
719 /// Returns a flipped copy of the contact data, swapping the points and feature IDs,
720 /// and negating the impulses.
721 #[inline]
722 pub fn flipped(&self) -> Self {
723 Self {
724 anchor1: self.anchor2,
725 anchor2: self.anchor1,
726 point: self.point,
727 penetration: self.penetration,
728 base_penetration: self.base_penetration,
729 normal_impulse: -self.normal_impulse,
730 normal_speed: self.normal_speed,
731 warm_start_normal_impulse: -self.warm_start_normal_impulse,
732 warm_start_tangent_impulse: -self.warm_start_tangent_impulse,
733 feature_id1: self.feature_id2,
734 feature_id2: self.feature_id1,
735 }
736 }
737}
738
739/// Data related to a single contact between two bodies.
740///
741/// If you want a contact that belongs to a [contact manifold](ContactManifold) and has more data,
742/// see [`ContactPoint`].
743#[derive(Clone, Copy, Debug, PartialEq)]
744#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
745pub struct SingleContact {
746 /// The contact point on the first shape in local space.
747 pub local_point1: Vector,
748 /// The contact point on the second shape in local space.
749 pub local_point2: Vector,
750 /// The contact normal expressed in the local space of the first shape.
751 pub local_normal1: Vector,
752 /// The contact normal expressed in the local space of the second shape.
753 pub local_normal2: Vector,
754 /// Penetration depth.
755 pub penetration: f32,
756}
757
758impl SingleContact {
759 /// Creates a new [`SingleContact`]. The contact points and normals should be given in local space.
760 #[inline]
761 pub fn new(
762 local_point1: Vector,
763 local_point2: Vector,
764 local_normal1: Vector,
765 local_normal2: Vector,
766 penetration: f32,
767 ) -> Self {
768 Self {
769 local_point1,
770 local_point2,
771 local_normal1,
772 local_normal2,
773 penetration,
774 }
775 }
776
777 /// Returns the global contact point on the first shape,
778 /// transforming the local point by the given position and rotation.
779 #[inline]
780 pub fn global_point1(&self, position: RVector, rotation: Rot) -> RVector {
781 position + (rotation * self.local_point1).real()
782 }
783
784 /// Returns the global contact point on the second shape,
785 /// transforming the local point by the given position and rotation.
786 #[inline]
787 pub fn global_point2(&self, position: RVector, rotation: Rot) -> RVector {
788 position + (rotation * self.local_point2).real()
789 }
790
791 /// Returns the world-space contact normal pointing from the first shape to the second.
792 #[inline]
793 pub fn global_normal1(&self, rotation: Rot) -> Vector {
794 rotation * self.local_normal1
795 }
796
797 /// Returns the world-space contact normal pointing from the second shape to the first.
798 #[inline]
799 pub fn global_normal2(&self, rotation: Rot) -> Vector {
800 rotation * self.local_normal2
801 }
802
803 /// Flips the contact data, swapping the points and normals.
804 #[inline]
805 pub fn flip(&mut self) {
806 core::mem::swap(&mut self.local_point1, &mut self.local_point2);
807 core::mem::swap(&mut self.local_normal1, &mut self.local_normal2);
808 }
809
810 /// Returns a flipped copy of the contact data, swapping the points and normals.
811 #[inline]
812 pub fn flipped(&self) -> Self {
813 Self {
814 local_point1: self.local_point2,
815 local_point2: self.local_point1,
816 local_normal1: self.local_normal2,
817 local_normal2: self.local_normal1,
818 penetration: self.penetration,
819 }
820 }
821}