Skip to main content

box3d_rust/shape/
geometry_set.rs

1//! Geometry get/set for sphere, capsule, and hull. (shape.c Set*/Get*)
2//!
3//! Changing geometry destroys contacts and rebuilds the broad-phase proxy.
4//!
5//! SPDX-FileCopyrightText: 2025 Erin Catto
6//! SPDX-License-Identifier: MIT
7
8use super::lifecycle::{
9    compute_shape_margin, destroy_shape_allocation_for_shape_change, get_shape,
10};
11use super::mutators::reset_proxy;
12use super::ShapeGeometry;
13use crate::geometry::{Capsule, Sphere};
14use crate::hull::{is_valid_hull, HullData};
15use crate::id::ShapeId;
16use crate::world::World;
17use std::rc::Rc;
18
19/// (b3Shape_GetSphere)
20pub fn shape_get_sphere(world: &World, shape_id: ShapeId) -> Sphere {
21    let index = get_shape(world, shape_id);
22    match &world.shapes[index as usize].geometry {
23        ShapeGeometry::Sphere(sphere) => *sphere,
24        _ => {
25            debug_assert!(false, "shape is not a sphere");
26            Sphere::default()
27        }
28    }
29}
30
31/// (b3Shape_GetCapsule)
32pub fn shape_get_capsule(world: &World, shape_id: ShapeId) -> Capsule {
33    let index = get_shape(world, shape_id);
34    match &world.shapes[index as usize].geometry {
35        ShapeGeometry::Capsule(capsule) => *capsule,
36        _ => {
37            debug_assert!(false, "shape is not a capsule");
38            Capsule::default()
39        }
40    }
41}
42
43/// (b3Shape_SetSphere)
44pub fn shape_set_sphere(world: &mut World, shape_id: ShapeId, sphere: &Sphere) {
45    crate::recording::with_recording(world, |rec| {
46        rec.write_shape_set_sphere(shape_id, *sphere);
47    });
48    debug_assert!(!world.locked);
49    if world.locked {
50        return;
51    }
52
53    world.locked = true;
54
55    let index = get_shape(world, shape_id);
56    destroy_shape_allocation_for_shape_change(world, index);
57
58    {
59        let shape = &mut world.shapes[index as usize];
60        shape.geometry = ShapeGeometry::Sphere(*sphere);
61        shape.aabb_margin = compute_shape_margin(shape);
62    }
63
64    // Need to wake bodies so they can react to the shape change.
65    let wake_bodies = true;
66    let destroy_proxy = true;
67    reset_proxy(world, index, wake_bodies, destroy_proxy);
68
69    world.locked = false;
70}
71
72/// (b3Shape_SetCapsule)
73pub fn shape_set_capsule(world: &mut World, shape_id: ShapeId, capsule: &Capsule) {
74    crate::recording::with_recording(world, |rec| {
75        rec.write_shape_set_capsule(shape_id, *capsule);
76    });
77    debug_assert!(!world.locked);
78    if world.locked {
79        return;
80    }
81
82    world.locked = true;
83
84    let index = get_shape(world, shape_id);
85    destroy_shape_allocation_for_shape_change(world, index);
86
87    {
88        let shape = &mut world.shapes[index as usize];
89        shape.geometry = ShapeGeometry::Capsule(*capsule);
90        shape.aabb_margin = compute_shape_margin(shape);
91    }
92
93    let wake_bodies = true;
94    let destroy_proxy = true;
95    reset_proxy(world, index, wake_bodies, destroy_proxy);
96
97    world.locked = false;
98}
99
100/// (b3Shape_SetHull)
101///
102/// Acquires the new hull before releasing the old so `hull` may safely alias
103/// the shape's current shared data.
104pub fn shape_set_hull(world: &mut World, shape_id: ShapeId, hull: &HullData) {
105    debug_assert!(is_valid_hull(hull));
106    debug_assert!(hull.hash != 0);
107
108    debug_assert!(!world.locked);
109    if world.locked {
110        return;
111    }
112
113    world.locked = true;
114
115    let index = get_shape(world, shape_id);
116
117    // Acquire the new hull before releasing the old so the input may safely
118    // alias the shape's current shared data.
119    let data = world.hull_database.add(hull);
120
121    // Same shared hull — avoid destroying contacts and recreating the proxy.
122    if let ShapeGeometry::Hull(existing) = &world.shapes[index as usize].geometry {
123        if Rc::ptr_eq(existing, &data) {
124            // Drop the extra Rc from add(); do not release from the database.
125            drop(data);
126            world.locked = false;
127            return;
128        }
129    }
130
131    destroy_shape_allocation_for_shape_change(world, index);
132
133    {
134        let shape = &mut world.shapes[index as usize];
135        shape.geometry = ShapeGeometry::Hull(data);
136        shape.aabb_margin = compute_shape_margin(shape);
137    }
138
139    let wake_bodies = true;
140    let destroy_proxy = true;
141    reset_proxy(world, index, wake_bodies, destroy_proxy);
142
143    world.locked = false;
144}