Skip to main content

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}