Skip to main content

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}