box3d_rust/geometry/types.rs
1//! Geometry shape types from include/box3d/types.h.
2//!
3//! `CastOutput` lives in [`crate::distance`] and is re-exported from the
4//! geometry module.
5//!
6//! SPDX-FileCopyrightText: 2026 Erin Catto
7//! SPDX-License-Identifier: MIT
8
9use crate::distance::ShapeProxy;
10use crate::math_functions::{Matrix3, Plane, Vec3, MAT3_ZERO, VEC3_ZERO};
11
12/// This holds the mass data computed for a shape. (b3MassData)
13#[derive(Debug, Clone, Copy, PartialEq)]
14pub struct MassData {
15 /// The shape mass
16 pub mass: f32,
17 /// The local center of mass position.
18 pub center: Vec3,
19 /// The inertia tensor about the shape center of mass.
20 pub inertia: Matrix3,
21}
22
23impl Default for MassData {
24 fn default() -> Self {
25 MassData {
26 mass: 0.0,
27 center: VEC3_ZERO,
28 inertia: MAT3_ZERO,
29 }
30 }
31}
32
33/// A solid sphere. (b3Sphere)
34#[derive(Debug, Clone, Copy, PartialEq, Default)]
35pub struct Sphere {
36 /// The local center
37 pub center: Vec3,
38 /// The radius
39 pub radius: f32,
40}
41
42/// A solid capsule can be viewed as two hemispheres connected by a cylinder.
43/// (b3Capsule)
44#[derive(Debug, Clone, Copy, PartialEq, Default)]
45pub struct Capsule {
46 /// Local center of the first hemisphere
47 pub center1: Vec3,
48 /// Local center of the second hemisphere
49 pub center2: Vec3,
50 /// The radius of the hemispheres
51 pub radius: f32,
52}
53
54/// Low level ray cast input data. (b3RayCastInput)
55#[derive(Debug, Clone, Copy, PartialEq, Default)]
56pub struct RayCastInput {
57 /// Start point of the ray cast.
58 pub origin: Vec3,
59 /// Translation of the ray cast. `end = start + translation`.
60 pub translation: Vec3,
61 /// The maximum fraction of the translation to consider, typically 1
62 pub max_fraction: f32,
63}
64
65/// Low level shape cast input in generic form. This allows casting an arbitrary
66/// point cloud wrapped with a radius. For example, a sphere is a single point
67/// with a non-zero radius. A capsule is two points with a non-zero radius. A box
68/// is eight points with a zero radius. (b3ShapeCastInput)
69#[derive(Debug, Clone, Copy, PartialEq, Default)]
70pub struct ShapeCastInput {
71 /// A generic query shape.
72 pub proxy: ShapeProxy,
73 /// The translation of the shape cast.
74 pub translation: Vec3,
75 /// The maximum fraction of the translation to consider, typically 1.
76 pub max_fraction: f32,
77 /// Allow shape cast to encroach when initially touching. This only works if
78 /// the radius is greater than zero.
79 pub can_encroach: bool,
80}
81
82/// The plane between a character mover and a shape. (b3PlaneResult)
83#[derive(Debug, Clone, Copy, PartialEq)]
84pub struct PlaneResult {
85 /// Outward pointing plane.
86 pub plane: Plane,
87 /// Closest point on the shape. May not be unique.
88 pub point: Vec3,
89}
90
91impl Default for PlaneResult {
92 fn default() -> Self {
93 PlaneResult {
94 plane: Plane {
95 normal: VEC3_ZERO,
96 offset: 0.0,
97 },
98 point: VEC3_ZERO,
99 }
100 }
101}
102
103/// Collision planes that can be fed to [`crate::mover::solve_planes`].
104/// Normally assembled by the user from [`PlaneResult`] values. (b3CollisionPlane)
105#[derive(Debug, Clone, Copy, PartialEq)]
106pub struct CollisionPlane {
107 /// The collision plane between the mover and some shape.
108 pub plane: Plane,
109 /// Setting this to `f32::MAX` makes the plane as rigid as possible. Lower
110 /// values can make the plane collision soft. Usually in meters.
111 pub push_limit: f32,
112 /// The push on the mover determined by `solve_planes`. Usually in meters.
113 pub push: f32,
114 /// Indicates if `clip_vector` should clip against this plane. Should be
115 /// false for soft collision.
116 pub clip_velocity: bool,
117}
118
119impl Default for CollisionPlane {
120 fn default() -> Self {
121 CollisionPlane {
122 plane: Plane {
123 normal: VEC3_ZERO,
124 offset: 0.0,
125 },
126 push_limit: 0.0,
127 push: 0.0,
128 clip_velocity: false,
129 }
130 }
131}
132
133/// Result returned by [`crate::mover::solve_planes`]. (b3PlaneSolverResult)
134#[derive(Debug, Clone, Copy, PartialEq, Default)]
135pub struct PlaneSolverResult {
136 /// The final relative translation.
137 pub delta: Vec3,
138 /// The number of iterations used by the plane solver. For diagnostics.
139 pub iteration_count: i32,
140}
141
142/// Material properties supported per triangle on meshes and height fields.
143/// (b3SurfaceMaterial)
144#[derive(Debug, Clone, Copy, PartialEq)]
145#[repr(C)]
146pub struct SurfaceMaterial {
147 /// The Coulomb (dry) friction coefficient, usually in the range [0,1].
148 pub friction: f32,
149 /// The coefficient of restitution (bounce) usually in the range [0,1].
150 pub restitution: f32,
151 /// The rolling resistance usually in the range [0,1].
152 pub rolling_resistance: f32,
153 /// The tangent velocity for conveyor belts.
154 pub tangent_velocity: Vec3,
155 /// User material identifier.
156 pub user_material_id: u64,
157 /// Custom debug draw color. Ignored if 0.
158 pub custom_color: u32,
159}
160
161impl Default for SurfaceMaterial {
162 fn default() -> Self {
163 default_surface_material()
164 }
165}
166
167/// Use this to initialize your surface material. (b3DefaultSurfaceMaterial)
168pub fn default_surface_material() -> SurfaceMaterial {
169 SurfaceMaterial {
170 friction: 0.6,
171 restitution: 0.0,
172 rolling_resistance: 0.0,
173 tangent_velocity: VEC3_ZERO,
174 user_material_id: 0,
175 custom_color: 0,
176 }
177}
178
179/// Size of C `b3SurfaceMaterial` including trailing padding to 8-byte alignment.
180pub const SURFACE_MATERIAL_SIZE: usize = 40;
181
182impl SurfaceMaterial {
183 /// Serialize to the C layout (40 bytes with padding).
184 pub fn to_bytes(self) -> [u8; SURFACE_MATERIAL_SIZE] {
185 let mut buf = [0u8; SURFACE_MATERIAL_SIZE];
186 buf[0..4].copy_from_slice(&self.friction.to_le_bytes());
187 buf[4..8].copy_from_slice(&self.restitution.to_le_bytes());
188 buf[8..12].copy_from_slice(&self.rolling_resistance.to_le_bytes());
189 buf[12..16].copy_from_slice(&self.tangent_velocity.x.to_le_bytes());
190 buf[16..20].copy_from_slice(&self.tangent_velocity.y.to_le_bytes());
191 buf[20..24].copy_from_slice(&self.tangent_velocity.z.to_le_bytes());
192 buf[24..32].copy_from_slice(&self.user_material_id.to_le_bytes());
193 buf[32..36].copy_from_slice(&self.custom_color.to_le_bytes());
194 buf
195 }
196
197 /// Parse from the C layout.
198 pub fn from_bytes(buf: &[u8]) -> Self {
199 debug_assert!(buf.len() >= SURFACE_MATERIAL_SIZE);
200 let read_f32 = |o: usize| f32::from_le_bytes(buf[o..o + 4].try_into().unwrap());
201 Self {
202 friction: read_f32(0),
203 restitution: read_f32(4),
204 rolling_resistance: read_f32(8),
205 tangent_velocity: Vec3 {
206 x: read_f32(12),
207 y: read_f32(16),
208 z: read_f32(20),
209 },
210 user_material_id: u64::from_le_bytes(buf[24..32].try_into().unwrap()),
211 custom_color: u32::from_le_bytes(buf[32..36].try_into().unwrap()),
212 }
213 }
214}
215
216/// Shape type. (b3ShapeType)
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
218#[repr(i32)]
219pub enum ShapeType {
220 /// A capsule is an extruded sphere.
221 #[default]
222 Capsule = 0,
223 /// A baked compound shape.
224 Compound = 1,
225 /// A height field useful for terrain.
226 Height = 2,
227 /// A convex hull.
228 Hull = 3,
229 /// A triangle soup.
230 Mesh = 4,
231 /// A sphere with an offset.
232 Sphere = 5,
233}
234
235/// Minimum and maximum extent of a shape relative to a local origin.
236/// (math_internal.h: b3ShapeExtent)
237#[derive(Debug, Clone, Copy, PartialEq, Default)]
238pub struct ShapeExtent {
239 pub min_extent: f32,
240 pub max_extent: Vec3,
241}