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#[derive(Clone, Debug, PartialEq)]
95pub struct ContactData {
96    /// Native contact id.
97    pub contact_id: ContactId,
98    /// First native shape in the contact pair.
99    pub shape_id_a: ShapeId,
100    /// Second native shape in the contact pair.
101    pub shape_id_b: ShapeId,
102    /// Contact manifolds copied from Box3D.
103    pub manifolds: Vec<Manifold>,
104}
105
106impl ContactData {
107    /// Copies contact data from a raw Box3D contact snapshot.
108    ///
109    /// # Safety
110    ///
111    /// `raw.manifolds` must either be null or point to `raw.manifoldCount`
112    /// initialized `b3Manifold` values for the duration of this call.
113    #[inline]
114    pub(crate) unsafe fn from_raw_parts(
115        raw: ffi::b3ContactData,
116        contact_id: ContactId,
117        shape_id_a: ShapeId,
118        shape_id_b: ShapeId,
119    ) -> Self {
120        let manifolds = if raw.manifolds.is_null() || raw.manifoldCount <= 0 {
121            Vec::new()
122        } else {
123            unsafe { std::slice::from_raw_parts(raw.manifolds, raw.manifoldCount as usize) }
124                .iter()
125                .copied()
126                .map(Manifold::from_raw)
127                .collect()
128        };
129
130        Self {
131            contact_id,
132            shape_id_a,
133            shape_id_b,
134            manifolds,
135        }
136    }
137}