Skip to main content

box3d_rust/contact/
mod.rs

1// Port of the contact module from box3d-cpp-reference/src/contact.h + contact.c.
2//
3// This file holds the data model. Lifecycle (registers, create/destroy) is in
4// lifecycle.rs. Narrow-phase update lands in a later collide slice.
5//
6// SPDX-FileCopyrightText: 2025 Erin Catto
7// SPDX-License-Identifier: MIT
8
9use crate::core::NULL_INDEX;
10use crate::distance::SimplexCache;
11use crate::manifold::{Manifold, SatCache};
12use crate::math_functions::{Quat, Transform, Vec3, QUAT_IDENTITY, TRANSFORM_IDENTITY, VEC3_ZERO};
13
14/// Contact flag bits. (enum b3ContactFlags)
15pub mod contact_flags {
16    /// Set when the solid shapes are touching.
17    pub const TOUCHING: u32 = 0x0000_0001;
18    /// Contact has a hit event
19    pub const HIT_EVENT: u32 = 0x0000_0002;
20    /// This contact wants contact events
21    pub const ENABLE_CONTACT_EVENTS: u32 = 0x0000_0004;
22    /// Contact is between a dynamic and static body
23    pub const STATIC_FLAG: u32 = 0x0000_0008;
24    pub const RECYCLE: u32 = 0x0000_0010;
25
26    /// Set when the shapes are touching (sim flag)
27    pub const SIM_TOUCHING: u32 = 0x0001_0000;
28    /// This contact no longer has overlapping AABBs
29    pub const SIM_DISJOINT: u32 = 0x0002_0000;
30    /// This contact started touching
31    pub const SIM_STARTED_TOUCHING: u32 = 0x0004_0000;
32    /// This contact stopped touching
33    pub const SIM_STOPPED_TOUCHING: u32 = 0x0008_0000;
34    /// This contact has a hit event
35    pub const SIM_ENABLE_HIT_EVENT: u32 = 0x0010_0000;
36    /// This contact wants pre-solve events
37    pub const SIM_ENABLE_PRE_SOLVE_EVENTS: u32 = 0x0020_0000;
38    /// This is a mesh contact
39    pub const SIM_MESH_CONTACT: u32 = 0x0040_0000;
40    /// Relative transform is valid for recycling
41    pub const RELATIVE_TRANSFORM_VALID: u32 = 0x0080_0000;
42}
43
44/// Contact cache: SAT or GJK simplex. (b3ContactCache)
45#[derive(Debug, Clone, Copy, PartialEq)]
46pub enum ContactCache {
47    Sat(SatCache),
48    Simplex(SimplexCache),
49}
50
51impl Default for ContactCache {
52    fn default() -> Self {
53        ContactCache::Sat(SatCache::default())
54    }
55}
56
57/// Per-triangle cache for mesh contacts. (b3TriangleCache)
58#[derive(Debug, Clone, Copy, PartialEq)]
59pub struct TriangleCache {
60    pub triangle_index: i32,
61    pub cache: ContactCache,
62}
63
64impl Default for TriangleCache {
65    fn default() -> Self {
66        TriangleCache {
67            triangle_index: NULL_INDEX,
68            cache: ContactCache::default(),
69        }
70    }
71}
72
73/// Mesh contact state. (b3MeshContact)
74#[derive(Debug, Clone, PartialEq, Default)]
75pub struct MeshContact {
76    pub triangle_cache: Vec<TriangleCache>,
77    pub query_bounds: crate::math_functions::Aabb,
78}
79
80/// Convex contact state. (b3ConvexContact)
81#[derive(Debug, Clone, Copy, PartialEq, Default)]
82pub struct ConvexContact {
83    pub cache: ContactCache,
84}
85
86/// Contact geometry cache payload. (C union in b3Contact)
87#[derive(Debug, Clone, PartialEq)]
88pub enum ContactGeometry {
89    Convex(ConvexContact),
90    Mesh(MeshContact),
91}
92
93impl Default for ContactGeometry {
94    fn default() -> Self {
95        ContactGeometry::Convex(ConvexContact::default())
96    }
97}
98
99/// A contact edge connects bodies and contacts in a contact graph where each
100/// body is a node and each contact is an edge. (b3ContactEdge)
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub struct ContactEdge {
103    pub body_id: i32,
104    pub prev_key: i32,
105    pub next_key: i32,
106}
107
108impl Default for ContactEdge {
109    fn default() -> Self {
110        ContactEdge {
111            body_id: NULL_INDEX,
112            prev_key: NULL_INDEX,
113            next_key: NULL_INDEX,
114        }
115    }
116}
117
118/// Persistent interaction between two shapes. (b3Contact)
119#[derive(Debug, Clone)]
120pub struct Contact {
121    /// Index of simulation set stored in World. NULL_INDEX when slot is free.
122    pub set_index: i32,
123
124    /// Index into the constraint graph color array. NULL_INDEX for non-touching
125    /// or sleeping contacts, and when the slot is free.
126    pub color_index: i32,
127
128    /// Contact index within set or graph color. NULL_INDEX when slot is free.
129    pub local_index: i32,
130
131    pub edges: [ContactEdge; 2],
132    pub shape_id_a: i32,
133    pub shape_id_b: i32,
134    pub child_index: i32,
135
136    /// A contact only belongs to an island if touching, otherwise NULL_INDEX.
137    pub island_id: i32,
138
139    /// Index into the island's contacts array for O(1) swap-removal.
140    /// NULL_INDEX when not in an island.
141    pub island_index: i32,
142
143    /// Back index into World::contacts
144    pub contact_id: i32,
145
146    /// Transient and cached for performance. NULL_INDEX for static bodies.
147    pub body_sim_index_a: i32,
148    pub body_sim_index_b: i32,
149
150    /// contact_flags bits
151    pub flags: u32,
152
153    pub manifolds: Vec<Manifold>,
154
155    /// Cache for contact recycling.
156    pub cached_rotation_a: Quat,
157    pub cached_rotation_b: Quat,
158    pub cached_relative_pose: Transform,
159
160    /// Mixed friction and restitution
161    pub friction: f32,
162    pub restitution: f32,
163    pub rolling_resistance: f32,
164    pub tangent_velocity: Vec3,
165
166    /// Usage determined by SIM_MESH_CONTACT in flags
167    pub geometry: ContactGeometry,
168
169    /// Monotonically advanced when a contact is allocated in this slot.
170    /// Used to check for invalid ContactId.
171    pub generation: u32,
172}
173
174impl Contact {
175    /// Manifold count. (C: manifoldCount)
176    pub fn manifold_count(&self) -> i32 {
177        self.manifolds.len() as i32
178    }
179}
180
181impl Default for Contact {
182    fn default() -> Self {
183        Contact {
184            set_index: NULL_INDEX,
185            color_index: NULL_INDEX,
186            local_index: NULL_INDEX,
187            edges: [ContactEdge::default(); 2],
188            shape_id_a: NULL_INDEX,
189            shape_id_b: NULL_INDEX,
190            child_index: 0,
191            island_id: NULL_INDEX,
192            island_index: NULL_INDEX,
193            contact_id: NULL_INDEX,
194            body_sim_index_a: NULL_INDEX,
195            body_sim_index_b: NULL_INDEX,
196            flags: 0,
197            manifolds: Vec::new(),
198            cached_rotation_a: QUAT_IDENTITY,
199            cached_rotation_b: QUAT_IDENTITY,
200            cached_relative_pose: TRANSFORM_IDENTITY,
201            friction: 0.0,
202            restitution: 0.0,
203            rolling_resistance: 0.0,
204            tangent_velocity: VEC3_ZERO,
205            geometry: ContactGeometry::default(),
206            generation: 0,
207        }
208    }
209}
210
211/// Contact descriptor for graph-color mesh/overflow constraints. (b3ContactSpec)
212#[derive(Debug, Clone, Copy, PartialEq, Eq)]
213pub struct ContactSpec {
214    pub contact_id: i32,
215    /// Start of the global manifold constraint array
216    pub manifold_start: i32,
217    pub manifold_count: u16,
218}
219
220impl Default for ContactSpec {
221    fn default() -> Self {
222        ContactSpec {
223            contact_id: NULL_INDEX,
224            manifold_start: 0,
225            manifold_count: 0,
226        }
227    }
228}
229
230mod collide;
231mod lifecycle;
232mod mesh_cache;
233mod mesh_contact;
234mod mesh_cull;
235mod update;
236
237pub use collide::*;
238pub use lifecycle::*;
239pub use mesh_contact::{apply_mesh_hit_flags, compute_mesh_manifolds};
240pub use update::*;