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