Skip to main content

boxddd/types/
contact.rs

1//! Contact manifold snapshots returned by collision and world contact APIs.
2
3use super::*;
4
5/// Maximum number of points in a Box3D contact manifold.
6pub const MAX_MANIFOLD_POINTS: usize = 4;
7
8/// One point in a contact manifold.
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[derive(Copy, Clone, Debug, Default, PartialEq)]
11pub struct ManifoldPoint {
12    /// Contact anchor on shape A in local contact coordinates.
13    pub anchor_a: Vec3,
14    /// Contact anchor on shape B in local contact coordinates.
15    pub anchor_b: Vec3,
16    /// Current separation at the contact point.
17    pub separation: f32,
18    /// Separation before solver updates.
19    pub base_separation: f32,
20    /// Normal impulse applied at this point during the current solve.
21    pub normal_impulse: f32,
22    /// Accumulated normal impulse for this contact point.
23    pub total_normal_impulse: f32,
24    /// Relative normal velocity at the contact point.
25    pub normal_velocity: f32,
26    /// Feature id used by Box3D to track contact persistence.
27    pub feature_id: u32,
28    /// Triangle index for mesh or height-field contacts, or Box3D's sentinel value.
29    pub triangle_index: i32,
30    /// Whether this contact point persisted from the previous step.
31    pub persisted: bool,
32}
33
34impl ManifoldPoint {
35    /// Converts a raw Box3D manifold point into the Rust value type.
36    #[inline]
37    pub const fn from_raw(raw: ffi::b3ManifoldPoint) -> Self {
38        Self {
39            anchor_a: Vec3::from_raw(raw.anchorA),
40            anchor_b: Vec3::from_raw(raw.anchorB),
41            separation: raw.separation,
42            base_separation: raw.baseSeparation,
43            normal_impulse: raw.normalImpulse,
44            total_normal_impulse: raw.totalNormalImpulse,
45            normal_velocity: raw.normalVelocity,
46            feature_id: raw.featureId,
47            triangle_index: raw.triangleIndex,
48            persisted: raw.persisted,
49        }
50    }
51}
52
53/// Contact manifold containing up to [`MAX_MANIFOLD_POINTS`] points.
54#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
55#[derive(Copy, Clone, Debug, Default, PartialEq)]
56pub struct Manifold {
57    /// Fixed-size storage for native contact points.
58    pub points: [ManifoldPoint; MAX_MANIFOLD_POINTS],
59    /// Contact normal.
60    pub normal: Vec3,
61    /// Twist friction impulse.
62    pub twist_impulse: f32,
63    /// Friction impulse vector.
64    pub friction_impulse: Vec3,
65    /// Rolling friction impulse vector.
66    pub rolling_impulse: Vec3,
67    /// Number of valid entries in `points`.
68    pub point_count: i32,
69}
70
71impl Manifold {
72    /// Returns only the valid manifold points.
73    #[inline]
74    pub fn points(&self) -> &[ManifoldPoint] {
75        let count = self.point_count.clamp(0, MAX_MANIFOLD_POINTS as i32) as usize;
76        &self.points[..count]
77    }
78
79    /// Converts a raw Box3D manifold into the Rust value type.
80    #[inline]
81    pub fn from_raw(raw: ffi::b3Manifold) -> Self {
82        Self {
83            points: raw.points.map(ManifoldPoint::from_raw),
84            normal: Vec3::from_raw(raw.normal),
85            twist_impulse: raw.twistImpulse,
86            friction_impulse: Vec3::from_raw(raw.frictionImpulse),
87            rolling_impulse: Vec3::from_raw(raw.rollingImpulse),
88            point_count: raw.pointCount.clamp(0, MAX_MANIFOLD_POINTS as i32),
89        }
90    }
91}
92
93/// Contact data snapshot for a native contact pair.
94#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
95#[derive(Clone, Debug, Default, PartialEq)]
96pub struct ContactData {
97    /// Native contact id.
98    pub contact_id: ContactId,
99    /// First native shape in the contact pair.
100    pub shape_id_a: ShapeId,
101    /// Second native shape in the contact pair.
102    pub shape_id_b: ShapeId,
103    /// Contact manifolds copied from Box3D.
104    pub manifolds: Vec<Manifold>,
105}
106
107impl ContactData {
108    /// Copies contact data from a raw Box3D contact snapshot.
109    ///
110    /// # Safety
111    ///
112    /// `raw.manifolds` must either be null or point to `raw.manifoldCount`
113    /// initialized `b3Manifold` values for the duration of this call.
114    #[inline]
115    pub unsafe fn from_raw(raw: ffi::b3ContactData) -> Self {
116        let manifolds = if raw.manifolds.is_null() || raw.manifoldCount <= 0 {
117            Vec::new()
118        } else {
119            unsafe { std::slice::from_raw_parts(raw.manifolds, raw.manifoldCount as usize) }
120                .iter()
121                .copied()
122                .map(Manifold::from_raw)
123                .collect()
124        };
125
126        Self {
127            contact_id: ContactId::from_raw(raw.contactId),
128            shape_id_a: ShapeId::from_raw(raw.shapeIdA),
129            shape_id_b: ShapeId::from_raw(raw.shapeIdB),
130            manifolds,
131        }
132    }
133}