Skip to main content

box3d_rust/shape/
dispatch.rs

1// Per-shape-type dispatch helpers from shape.c (AABB, mass, centroid, proxy).
2//
3// SPDX-FileCopyrightText: 2025 Erin Catto
4// SPDX-License-Identifier: MIT
5
6use super::Shape;
7use crate::aabb::farthest_point_on_aabb;
8use crate::broad_phase::{proxy_type, BroadPhase};
9use crate::compound::compute_compound_aabb;
10use crate::constants::speculative_distance;
11use crate::core::NULL_INDEX;
12use crate::geometry::{
13    compute_capsule_aabb, compute_capsule_mass, compute_sphere_aabb, compute_sphere_mass, MassData,
14    ShapeExtent,
15};
16use crate::height_field::compute_height_field_aabb;
17use crate::hull::{compute_hull_aabb, compute_hull_extent, compute_hull_mass};
18use crate::math_functions::{
19    aabb_center, abs, add, lerp, max, min_float, sub, Aabb, Transform, Vec3, WorldTransform,
20    TRANSFORM_IDENTITY, VEC3_ZERO,
21};
22use crate::mesh::compute_mesh_aabb;
23use crate::types::BodyType;
24
25use super::ShapeGeometry;
26
27/// Compute the shape AABB and fat AABB with speculative and movement margins.
28/// (static b3UpdateShapeAABBs)
29pub(crate) fn update_shape_aabbs(
30    shape: &mut Shape,
31    transform: WorldTransform,
32    proxy_type_: BodyType,
33) {
34    let speculative = speculative_distance();
35    let aabb_margin = shape.aabb_margin;
36
37    let aabb = compute_fat_shape_aabb(shape, transform, speculative);
38    shape.aabb = aabb;
39
40    // Smaller margin for static bodies. Cannot be zero due to TOI tolerance.
41    let margin = if proxy_type_ == BodyType::Static {
42        speculative
43    } else {
44        aabb_margin
45    };
46    shape.fat_aabb = Aabb {
47        lower_bound: Vec3 {
48            x: aabb.lower_bound.x - margin,
49            y: aabb.lower_bound.y - margin,
50            z: aabb.lower_bound.z - margin,
51        },
52        upper_bound: Vec3 {
53            x: aabb.upper_bound.x + margin,
54            y: aabb.upper_bound.y + margin,
55            z: aabb.upper_bound.z + margin,
56        },
57    };
58}
59
60/// (b3CreateShapeProxy)
61pub fn create_shape_proxy(
62    shape: &mut Shape,
63    bp: &mut BroadPhase,
64    type_: BodyType,
65    transform: WorldTransform,
66    force_pair_creation: bool,
67) {
68    debug_assert!(shape.proxy_key == NULL_INDEX);
69
70    update_shape_aabbs(shape, transform, type_);
71
72    shape.proxy_key = bp.create_proxy(
73        type_,
74        shape.fat_aabb,
75        shape.filter.category_bits,
76        shape.id,
77        force_pair_creation,
78    );
79    debug_assert!((proxy_type(shape.proxy_key) as usize) < crate::types::BODY_TYPE_COUNT);
80}
81
82/// (b3DestroyShapeProxy)
83pub fn destroy_shape_proxy(shape: &mut Shape, bp: &mut BroadPhase) {
84    if shape.proxy_key != NULL_INDEX {
85        bp.destroy_proxy(shape.proxy_key);
86        shape.proxy_key = NULL_INDEX;
87    }
88}
89
90/// (b3ComputeShapeAABB)
91pub fn compute_shape_aabb(shape: &Shape, transform: Transform) -> Aabb {
92    match &shape.geometry {
93        ShapeGeometry::Capsule(capsule) => compute_capsule_aabb(capsule, transform),
94        ShapeGeometry::Compound(compound) => compute_compound_aabb(compound, transform),
95        ShapeGeometry::HeightField(hf) => compute_height_field_aabb(hf, transform),
96        ShapeGeometry::Hull(hull) => compute_hull_aabb(hull, transform),
97        ShapeGeometry::Mesh { data, scale } => compute_mesh_aabb(data, transform, *scale),
98        ShapeGeometry::Sphere(sphere) => compute_sphere_aabb(sphere, transform),
99    }
100}
101
102/// (b3ComputeFatShapeAABB)
103pub fn compute_fat_shape_aabb(shape: &Shape, transform: WorldTransform, extra: f32) -> Aabb {
104    let r = Vec3 {
105        x: extra,
106        y: extra,
107        z: extra,
108    };
109    #[cfg(feature = "double-precision")]
110    {
111        use crate::math_functions::{offset_aabb, VEC3_ZERO};
112        // Build the box in the body local frame, inflate, then translate by the double
113        // origin and round outward.
114        let rotation = Transform {
115            p: VEC3_ZERO,
116            q: transform.q,
117        };
118        let mut local_box = compute_shape_aabb(shape, rotation);
119        local_box.lower_bound = sub(local_box.lower_bound, r);
120        local_box.upper_bound = add(local_box.upper_bound, r);
121        offset_aabb(local_box, transform.p)
122    }
123    #[cfg(not(feature = "double-precision"))]
124    {
125        let mut aabb = compute_shape_aabb(shape, transform);
126        aabb.lower_bound = sub(aabb.lower_bound, r);
127        aabb.upper_bound = add(aabb.upper_bound, r);
128        aabb
129    }
130}
131
132/// (b3GetShapeCentroid)
133pub fn get_shape_centroid(shape: &Shape) -> Vec3 {
134    match &shape.geometry {
135        ShapeGeometry::Capsule(capsule) => lerp(capsule.center1, capsule.center2, 0.5),
136        ShapeGeometry::Compound(compound) => {
137            let aabb = compute_compound_aabb(compound, TRANSFORM_IDENTITY);
138            aabb_center(aabb)
139        }
140        ShapeGeometry::Sphere(sphere) => sphere.center,
141        ShapeGeometry::Hull(hull) => hull.center,
142        ShapeGeometry::Mesh { data, scale } => {
143            let aabb = compute_mesh_aabb(data, TRANSFORM_IDENTITY, *scale);
144            aabb_center(aabb)
145        }
146        ShapeGeometry::HeightField(hf) => {
147            let aabb = compute_height_field_aabb(hf, TRANSFORM_IDENTITY);
148            aabb_center(aabb)
149        }
150    }
151}
152
153/// (b3ComputeShapeMass)
154pub fn compute_shape_mass(shape: &Shape) -> MassData {
155    match &shape.geometry {
156        ShapeGeometry::Capsule(capsule) => compute_capsule_mass(capsule, shape.density),
157        ShapeGeometry::Hull(hull) => compute_hull_mass(hull, shape.density),
158        ShapeGeometry::Sphere(sphere) => compute_sphere_mass(sphere, shape.density),
159        _ => MassData::default(),
160    }
161}
162
163/// Convex shape proxy for distance / TOI. Mesh, height, and compound need
164/// their own query paths. (b3MakeShapeProxy)
165pub fn make_shape_proxy(shape: &Shape) -> crate::distance::ShapeProxy {
166    use crate::distance::make_proxy;
167    use crate::hull::get_hull_points;
168
169    match &shape.geometry {
170        ShapeGeometry::Capsule(capsule) => {
171            make_proxy(&[capsule.center1, capsule.center2], capsule.radius)
172        }
173        ShapeGeometry::Sphere(sphere) => make_proxy(&[sphere.center], sphere.radius),
174        ShapeGeometry::Hull(hull) => make_proxy(get_hull_points(hull), 0.0),
175        _ => {
176            debug_assert!(false, "make_shape_proxy is for convex shapes only");
177            make_proxy(&[VEC3_ZERO], 0.0)
178        }
179    }
180}
181
182/// (b3ComputeShapeExtent)
183pub fn compute_shape_extent(shape: &Shape, local_center: Vec3) -> ShapeExtent {
184    let mut extent = ShapeExtent::default();
185
186    match &shape.geometry {
187        ShapeGeometry::Capsule(capsule) => {
188            let radius = capsule.radius;
189            extent.min_extent = radius;
190            let c1 = sub(capsule.center1, local_center);
191            let c2 = sub(capsule.center2, local_center);
192            let r = Vec3 {
193                x: radius,
194                y: radius,
195                z: radius,
196            };
197            extent.max_extent = add(max(c1, c2), r);
198        }
199
200        ShapeGeometry::Compound(compound) => {
201            let aabb = compute_compound_aabb(compound, TRANSFORM_IDENTITY);
202            let r1 = crate::math_functions::length(sub(aabb.lower_bound, local_center));
203            let r2 = crate::math_functions::length(sub(aabb.upper_bound, local_center));
204            extent.min_extent = min_float(r1, r2);
205            let p = farthest_point_on_aabb(aabb, local_center);
206            extent.max_extent = abs(sub(p, local_center));
207        }
208
209        ShapeGeometry::Sphere(sphere) => {
210            let radius = sphere.radius;
211            extent.min_extent = radius;
212            let r = Vec3 {
213                x: radius,
214                y: radius,
215                z: radius,
216            };
217            let p = add(sub(sphere.center, local_center), r);
218            extent.max_extent = abs(sub(p, local_center));
219        }
220
221        ShapeGeometry::Hull(hull) => {
222            extent = compute_hull_extent(hull, local_center);
223        }
224
225        ShapeGeometry::Mesh { data, scale } => {
226            let aabb = compute_mesh_aabb(data, TRANSFORM_IDENTITY, *scale);
227            let r1 = crate::math_functions::length(sub(aabb.lower_bound, local_center));
228            let r2 = crate::math_functions::length(sub(aabb.upper_bound, local_center));
229            extent.min_extent = min_float(r1, r2);
230            let p = farthest_point_on_aabb(aabb, local_center);
231            extent.max_extent = abs(p);
232        }
233
234        ShapeGeometry::HeightField(_) => {}
235    }
236
237    extent
238}