box3d_rust/types/world.rs
1// World creation types and defaults from types.h / types.c.
2// SPDX-FileCopyrightText: 2025 Erin Catto
3// SPDX-License-Identifier: MIT
4
5use crate::core::{get_length_units_per_meter, SECRET_COOKIE};
6use crate::debug_draw::{CreateDebugShapeCallback, DestroyDebugShapeCallback};
7use crate::math_functions::Vec3;
8
9/// Optional world capacities that can be used to avoid run-time allocations.
10/// (b3Capacity)
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12pub struct Capacity {
13 /// Number of expected static shapes.
14 pub static_shape_count: i32,
15 /// Number of expected dynamic and kinematic shapes.
16 pub dynamic_shape_count: i32,
17 /// Number of expected static bodies.
18 pub static_body_count: i32,
19 /// Number of expected dynamic and kinematic bodies.
20 pub dynamic_body_count: i32,
21 /// Number of expected contacts.
22 pub contact_count: i32,
23}
24
25/// Optional friction mixing callback. (b3FrictionCallback)
26///
27/// Args: `(friction_a, user_material_id_a, friction_b, user_material_id_b)`.
28pub type FrictionCallback = fn(f32, u64, f32, u64) -> f32;
29
30/// Optional restitution mixing callback. (b3RestitutionCallback)
31pub type RestitutionCallback = fn(f32, u64, f32, u64) -> f32;
32
33/// World definition used to create a simulation world.
34/// Must be initialized using [`default_world_def`]. (b3WorldDef)
35///
36/// Task-system fields from C (`workerCount`, enqueue/finish callbacks) are
37/// omitted: the Rust port is serial and never spawns workers.
38#[derive(Debug, Clone)]
39pub struct WorldDef {
40 /// Gravity vector. Box3D has no up-vector defined.
41 pub gravity: Vec3,
42 /// Restitution speed threshold, usually in m/s.
43 pub restitution_threshold: f32,
44 /// Hit event speed threshold, usually in m/s.
45 pub hit_event_threshold: f32,
46 /// Contact stiffness. Cycles per second.
47 pub contact_hertz: f32,
48 /// Contact bounciness. Non-dimensional.
49 pub contact_damping_ratio: f32,
50 /// Contact speed cap for overlap resolution, usually m/s.
51 pub contact_speed: f32,
52 /// Maximum linear speed, usually m/s.
53 pub maximum_linear_speed: f32,
54 /// Optional friction mixing callback.
55 pub friction_callback: Option<FrictionCallback>,
56 /// Optional restitution mixing callback.
57 pub restitution_callback: Option<RestitutionCallback>,
58 /// Can bodies go to sleep?
59 pub enable_sleep: bool,
60 /// Enable continuous collision.
61 pub enable_continuous: bool,
62 /// Application-specific world data.
63 pub user_data: u64,
64 /// Optional capacity hints to avoid run-time allocations.
65 pub capacity: Capacity,
66 /// Used to create debug draw shapes when a shape is first drawn.
67 pub create_debug_shape: Option<CreateDebugShapeCallback>,
68 /// Used to destroy debug draw shapes when a shape is modified or destroyed.
69 pub destroy_debug_shape: Option<DestroyDebugShapeCallback>,
70 /// Passed to the debug shape callbacks.
71 pub user_debug_shape_context: u64,
72 /// Used internally to detect a valid definition. DO NOT SET.
73 pub internal_value: i32,
74}
75
76/// Use this to initialize your world definition. (b3DefaultWorldDef)
77pub fn default_world_def() -> WorldDef {
78 let length_units = get_length_units_per_meter();
79 WorldDef {
80 gravity: Vec3 {
81 x: 0.0,
82 y: -10.0,
83 z: 0.0,
84 },
85 restitution_threshold: 1.0 * length_units,
86 hit_event_threshold: 1.0 * length_units,
87 contact_hertz: 30.0,
88 contact_damping_ratio: 10.0,
89 contact_speed: 3.0 * length_units,
90 maximum_linear_speed: 400.0 * length_units,
91 friction_callback: None,
92 restitution_callback: None,
93 enable_sleep: true,
94 enable_continuous: true,
95 user_data: 0,
96 capacity: Capacity::default(),
97 create_debug_shape: None,
98 destroy_debug_shape: None,
99 user_debug_shape_context: 0,
100 internal_value: SECRET_COOKIE,
101 }
102}
103
104impl Default for WorldDef {
105 fn default() -> Self {
106 default_world_def()
107 }
108}
109
110/// World-space cast output from a single-shape ray cast. (b3WorldCastOutput)
111///
112/// In single precision this matches [`crate::distance::CastOutput`] field-wise
113/// except `point` is [`Pos`] (an alias of [`Vec3`]). With `double-precision`,
114/// `point` stays a world [`Pos`].
115#[derive(Debug, Clone, Copy, PartialEq)]
116pub struct WorldCastOutput {
117 /// The surface normal at the hit point.
118 pub normal: Vec3,
119 /// The surface hit point in world space.
120 pub point: crate::math_functions::Pos,
121 /// The fraction of the input translation at collision.
122 pub fraction: f32,
123 /// The number of iterations used.
124 pub iterations: i32,
125 /// The index of the mesh or height field triangle hit.
126 pub triangle_index: i32,
127 /// The index of the compound child shape.
128 pub child_index: i32,
129 /// The material index. May be -1 for null.
130 pub material_index: i32,
131 /// Did the cast hit?
132 pub hit: bool,
133}
134
135impl Default for WorldCastOutput {
136 fn default() -> Self {
137 WorldCastOutput {
138 normal: crate::math_functions::VEC3_ZERO,
139 point: crate::math_functions::POS_ZERO,
140 fraction: 0.0,
141 iterations: 0,
142 triangle_index: crate::core::NULL_INDEX,
143 child_index: 0,
144 material_index: 0,
145 hit: false,
146 }
147 }
148}
149
150/// Result from b3World_CastRayClosest. (b3RayResult)
151#[derive(Debug, Clone, Copy, PartialEq)]
152pub struct RayResult {
153 /// The shape hit.
154 pub shape_id: crate::id::ShapeId,
155 /// The world point of the hit.
156 pub point: crate::math_functions::Pos,
157 /// The world normal of the shape surface at the hit point.
158 pub normal: Vec3,
159 /// The user material id at the hit point.
160 pub user_material_id: u64,
161 /// The fraction of the input ray.
162 pub fraction: f32,
163 /// The triangle index if the shape is a mesh, height-field, or compound with child mesh.
164 pub triangle_index: i32,
165 /// The child index if the shape is a compound.
166 pub child_index: i32,
167 /// The number of BVH nodes visited. Diagnostic.
168 pub node_visits: i32,
169 /// The number of BVH leaves visited. Diagnostic.
170 pub leaf_visits: i32,
171 /// Did the ray hit? If false, all other data is invalid.
172 pub hit: bool,
173}
174
175impl Default for RayResult {
176 fn default() -> Self {
177 RayResult {
178 shape_id: crate::id::ShapeId::default(),
179 point: crate::math_functions::POS_ZERO,
180 normal: crate::math_functions::VEC3_ZERO,
181 user_material_id: 0,
182 fraction: 0.0,
183 triangle_index: 0,
184 child_index: 0,
185 node_visits: 0,
186 leaf_visits: 0,
187 hit: false,
188 }
189 }
190}
191
192/// Counters that give details of the simulation size. (b3Counters)
193///
194/// byte_count, stack_used, arena_capacity, and task_count are always
195/// zero in this port: there is no global allocation tracker, no arena stack
196/// allocator, and no task system in the serial Rust implementation.
197#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
198pub struct Counters {
199 pub body_count: i32,
200 pub shape_count: i32,
201 pub contact_count: i32,
202 pub joint_count: i32,
203 pub island_count: i32,
204 pub stack_used: i32,
205 pub arena_capacity: i32,
206 pub static_tree_height: i32,
207 pub tree_height: i32,
208 pub sat_call_count: i32,
209 pub sat_cache_hit_count: i32,
210 pub byte_count: i32,
211 pub task_count: i32,
212 pub color_counts: [i32; crate::constants::GRAPH_COLOR_COUNT as usize],
213 pub manifold_counts: [i32; crate::constants::CONTACT_MANIFOLD_COUNT_BUCKETS],
214 /// Number of contacts touched by the collide pass (graph contacts +
215 /// awake-set non-touching).
216 pub awake_contact_count: i32,
217 /// Number of contacts recycled in the most recent step.
218 pub recycled_contact_count: i32,
219 /// Maximum number of time of impact iterations
220 pub distance_iterations: i32,
221 pub push_back_iterations: i32,
222 pub root_iterations: i32,
223}
224
225/// The explosion definition is used to configure options for explosions.
226/// Explosions consider shape geometry when computing the impulse.
227/// (b3ExplosionDef)
228#[derive(Debug, Clone, Copy, PartialEq)]
229pub struct ExplosionDef {
230 /// Mask bits to filter shapes
231 pub mask_bits: u64,
232 /// The center of the explosion in world space
233 pub position: crate::math_functions::Pos,
234 /// The radius of the explosion
235 pub radius: f32,
236 /// The falloff distance beyond the radius. Impulse is reduced to zero at
237 /// this distance.
238 pub falloff: f32,
239 /// Impulse per unit area. This applies an impulse according to the shape
240 /// area that is facing the explosion. Explosions only apply to spheres,
241 /// capsules, and hulls. This may be negative for implosions.
242 pub impulse_per_area: f32,
243}
244
245/// Use this to initialize your explosion definition. (b3DefaultExplosionDef)
246pub fn default_explosion_def() -> ExplosionDef {
247 ExplosionDef {
248 mask_bits: crate::dynamic_tree::DEFAULT_MASK_BITS,
249 position: crate::math_functions::POS_ZERO,
250 radius: 0.0,
251 falloff: 0.0,
252 impulse_per_area: 0.0,
253 }
254}
255
256impl Default for ExplosionDef {
257 fn default() -> Self {
258 default_explosion_def()
259 }
260}