Skip to main content

box3d_rust/body/
types.rs

1// Port of the body data model from box3d-cpp-reference/src/body.h.
2//
3// SPDX-FileCopyrightText: 2025 Erin Catto
4// SPDX-License-Identifier: MIT
5
6use crate::core::NULL_INDEX;
7use crate::math_functions::{
8    Matrix3, Pos, Quat, Vec3, WorldTransform, MAT3_ZERO, POS_ZERO, QUAT_IDENTITY, VEC3_ZERO,
9    WORLD_TRANSFORM_IDENTITY,
10};
11use crate::types::BodyType;
12
13/// Body flag bits. (enum b3BodyFlags)
14pub mod body_flags {
15    /// Fixed translation along the x-axis
16    pub const LOCK_LINEAR_X: u32 = 0x0000_0001;
17    /// Fixed translation along the y-axis
18    pub const LOCK_LINEAR_Y: u32 = 0x0000_0002;
19    /// Fixed translation along the z-axis
20    pub const LOCK_LINEAR_Z: u32 = 0x0000_0004;
21    /// Fixed rotation around the x-axis
22    pub const LOCK_ANGULAR_X: u32 = 0x0000_0008;
23    /// Fixed rotation around the y-axis
24    pub const LOCK_ANGULAR_Y: u32 = 0x0000_0010;
25    /// Fixed rotation around the z-axis
26    pub const LOCK_ANGULAR_Z: u32 = 0x0000_0020;
27    /// Used for debug draw
28    pub const IS_FAST: u32 = 0x0000_0040;
29    /// Dynamic body does a final CCD pass against all body types, but not other bullets
30    pub const IS_BULLET: u32 = 0x0000_0080;
31    /// Speed capped in the current time step
32    pub const IS_SPEED_CAPPED: u32 = 0x0000_0100;
33    /// Had a time of impact event in the current time step
34    pub const HAD_TIME_OF_IMPACT: u32 = 0x0000_0200;
35    /// No limit on angular velocity
36    pub const ALLOW_FAST_ROTATION: u32 = 0x0000_0400;
37    /// Needs AABB increased
38    pub const ENLARGE_BOUNDS: u32 = 0x0000_0800;
39    /// Dynamic so the solver should write to it. Used for BodyState flags.
40    pub const DYNAMIC_FLAG: u32 = 0x0000_1000;
41    pub const ENABLE_SLEEP: u32 = 0x0000_2000;
42    pub const BODY_ENABLE_CONTACT_RECYCLING: u32 = 0x0000_4000;
43    /// User deferred mass computation and mass data still hasn't been set.
44    pub const DIRTY_MASS: u32 = 0x0000_8000;
45
46    /// All lock flags
47    pub const ALL_LOCKS: u32 = LOCK_LINEAR_X
48        | LOCK_LINEAR_Y
49        | LOCK_LINEAR_Z
50        | LOCK_ANGULAR_X
51        | LOCK_ANGULAR_Y
52        | LOCK_ANGULAR_Z;
53
54    /// If all these flags are set then the body has fixed rotation
55    pub const FIXED_ROTATION: u32 = LOCK_ANGULAR_X | LOCK_ANGULAR_Y | LOCK_ANGULAR_Z;
56
57    /// Transient per time step. May differ across Body, BodySim, and BodyState.
58    pub const BODY_TRANSIENT_FLAGS: u32 = IS_FAST | IS_SPEED_CAPPED | HAD_TIME_OF_IMPACT;
59}
60
61/// Body organizational details that are not used in the solver. (b3Body)
62#[derive(Debug, Clone)]
63pub struct Body {
64    pub user_data: u64,
65
66    /// Index of solver set stored in World. May be NULL_INDEX.
67    pub set_index: i32,
68
69    /// Body sim and state index within set. May be NULL_INDEX.
70    pub local_index: i32,
71
72    /// [31 : contactId | 1 : edgeIndex]
73    pub head_contact_key: i32,
74    pub contact_count: i32,
75
76    pub head_shape_id: i32,
77    pub shape_count: i32,
78
79    pub head_chain_id: i32,
80
81    /// [31 : jointId | 1 : edgeIndex]
82    pub head_joint_key: i32,
83    pub joint_count: i32,
84
85    /// All enabled dynamic and kinematic bodies are in an island.
86    pub island_id: i32,
87
88    /// Index into the island's bodies array for O(1) swap-removal.
89    /// NULL_INDEX when not in an island.
90    pub island_index: i32,
91
92    pub sleep_threshold: f32,
93    pub sleep_time: f32,
94    pub sleep_velocity: f32,
95    pub mass: f32,
96
97    /// Local space inertia
98    pub inertia: Matrix3,
99
100    /// Adjusts the fellAsleep flag in the body move array
101    pub body_move_index: i32,
102
103    pub id: i32,
104
105    /// body_flags bits
106    pub flags: u32,
107    pub name_id: u32,
108
109    pub type_: BodyType,
110
111    /// Monotonically advanced when a body is allocated in this slot.
112    /// Used to check for invalid BodyId.
113    pub generation: u16,
114}
115
116impl Default for Body {
117    fn default() -> Self {
118        Body {
119            user_data: 0,
120            set_index: NULL_INDEX,
121            local_index: NULL_INDEX,
122            head_contact_key: NULL_INDEX,
123            contact_count: 0,
124            head_shape_id: NULL_INDEX,
125            shape_count: 0,
126            head_chain_id: NULL_INDEX,
127            head_joint_key: NULL_INDEX,
128            joint_count: 0,
129            island_id: NULL_INDEX,
130            island_index: NULL_INDEX,
131            sleep_threshold: 0.0,
132            sleep_time: 0.0,
133            sleep_velocity: 0.0,
134            mass: 0.0,
135            inertia: MAT3_ZERO,
136            body_move_index: NULL_INDEX,
137            id: NULL_INDEX,
138            flags: 0,
139            name_id: 0,
140            type_: BodyType::Static,
141            generation: 0,
142        }
143    }
144}
145
146/// Body state designed for fast conversion to and from SIMD via scatter-gather.
147/// Only awake dynamic and kinematic bodies have a body state. Used in the
148/// performance critical constraint solver. (b3BodyState, 56 bytes)
149#[derive(Debug, Clone, Copy, PartialEq)]
150pub struct BodyState {
151    pub linear_velocity: Vec3,
152    pub angular_velocity: Vec3,
153
154    /// Using delta position reduces round-off error far from the origin
155    pub delta_position: Vec3,
156
157    /// Delta rotation; identity for static bodies via a dummy state
158    pub delta_rotation: Quat,
159
160    /// body_flags bits — important: locking, dynamic
161    pub flags: u32,
162}
163
164/// Identity body state; notice delta_rotation is identity. (b3_identityBodyState)
165pub const IDENTITY_BODY_STATE: BodyState = BodyState {
166    linear_velocity: VEC3_ZERO,
167    angular_velocity: VEC3_ZERO,
168    delta_position: VEC3_ZERO,
169    delta_rotation: QUAT_IDENTITY,
170    flags: 0,
171};
172
173impl Default for BodyState {
174    fn default() -> Self {
175        IDENTITY_BODY_STATE
176    }
177}
178
179/// Body simulation data used for integration of position and velocity.
180/// Transform data used for collision and solver preparation. (b3BodySim)
181#[derive(Debug, Clone, Copy, PartialEq)]
182pub struct BodySim {
183    /// Transform for body origin (double translation in large world mode)
184    pub transform: WorldTransform,
185
186    /// Center of mass position in world space
187    pub center: Pos,
188
189    /// Previous rotation and COM for TOI
190    pub rotation0: Quat,
191    pub center0: Pos,
192
193    /// Location of center of mass relative to the body origin
194    pub local_center: Vec3,
195
196    pub force: Vec3,
197    pub torque: Vec3,
198
199    pub inv_mass: f32,
200
201    /// Rotational inertia about the center of mass. World inverse inertia
202    /// must be updated whenever the body rotation is modified.
203    pub inv_inertia_local: Matrix3,
204    pub inv_inertia_world: Matrix3,
205
206    pub min_extent: f32,
207    pub max_extent: Vec3,
208    pub max_angular_velocity: f32,
209    pub linear_damping: f32,
210    pub angular_damping: f32,
211    pub gravity_scale: f32,
212
213    /// Index of Body
214    pub body_id: i32,
215
216    /// body_flags bits
217    pub flags: u32,
218}
219
220impl Default for BodySim {
221    fn default() -> Self {
222        BodySim {
223            transform: WORLD_TRANSFORM_IDENTITY,
224            center: POS_ZERO,
225            rotation0: QUAT_IDENTITY,
226            center0: POS_ZERO,
227            local_center: VEC3_ZERO,
228            force: VEC3_ZERO,
229            torque: VEC3_ZERO,
230            inv_mass: 0.0,
231            inv_inertia_local: MAT3_ZERO,
232            inv_inertia_world: MAT3_ZERO,
233            min_extent: 0.0,
234            max_extent: VEC3_ZERO,
235            max_angular_velocity: 0.0,
236            linear_damping: 0.0,
237            angular_damping: 0.0,
238            gravity_scale: 1.0,
239            body_id: NULL_INDEX,
240            flags: 0,
241        }
242    }
243}
244
245/// Body plane result for movers. (b3BodyPlaneResult)
246#[derive(Debug, Clone, Copy, PartialEq)]
247pub struct BodyPlaneResult {
248    /// The shape id on the body.
249    pub shape_id: crate::id::ShapeId,
250    /// The plane result.
251    pub result: crate::geometry::PlaneResult,
252}
253
254impl Default for BodyPlaneResult {
255    fn default() -> Self {
256        BodyPlaneResult {
257            shape_id: crate::id::NULL_SHAPE_ID,
258            result: crate::geometry::PlaneResult::default(),
259        }
260    }
261}
262
263/// Body cast result for ray and shape casts. (b3BodyCastResult)
264#[derive(Debug, Clone, Copy, PartialEq)]
265pub struct BodyCastResult {
266    /// The shape hit.
267    pub shape_id: crate::id::ShapeId,
268    /// The world point on the shape surface.
269    pub point: crate::math_functions::Pos,
270    /// The world normal vector on the shape surface.
271    pub normal: crate::math_functions::Vec3,
272    /// The fraction along the ray hit.
273    /// hit point = origin + fraction * translation
274    pub fraction: f32,
275    /// The triangle index if the shape is a mesh or height-field.
276    pub triangle_index: i32,
277    /// The user material id at the hit point.
278    pub user_material_id: u64,
279    /// The number of iterations used. Diagnostic.
280    pub iterations: i32,
281    /// Did the cast hit? If false, all other fields are invalid.
282    pub hit: bool,
283}
284
285impl Default for BodyCastResult {
286    fn default() -> Self {
287        BodyCastResult {
288            shape_id: crate::id::NULL_SHAPE_ID,
289            point: crate::math_functions::POS_ZERO,
290            normal: crate::math_functions::VEC3_ZERO,
291            fraction: 0.0,
292            triangle_index: 0,
293            user_material_id: 0,
294            iterations: 0,
295            hit: false,
296        }
297    }
298}