Skip to main content

dynamis_model/
body.rs

1use crate::collider::ColliderDesc;
2use crate::shape::{Shape, ShapeSourceHandle, SolidGeometry};
3
4#[derive(Clone, Copy, Debug, PartialEq)]
5pub struct BodyHandle {
6    pub id: u32,
7    pub generation: u32,
8}
9
10#[derive(Clone, Copy, Debug, PartialEq)]
11pub struct BodyState {
12    pub position: [f32; 3],
13    pub prev_position: [f32; 3],
14    pub orientation: [f32; 4],
15    pub velocity: [f32; 3],
16    pub angular_velocity: [f32; 3],
17    pub inverse_mass: f32,
18    pub com: [f32; 3],
19    pub sleeping: bool,
20    pub step: u64,
21}
22
23const DEFAULT_COLLISION_GROUP: u32 = 0x0000_0001;
24const DEFAULT_COLLISION_MASK: u32 = 0xFFFF_FFFF;
25
26#[derive(Clone, Debug)]
27pub struct BodyDesc {
28    pub colliders: Vec<ColliderDesc>,
29    pub position: [f32; 3],
30    pub orientation: [f32; 4],
31    pub velocity: [f32; 3],
32    pub angular_velocity: [f32; 3],
33    pub mass: f32,
34    pub density: Option<f32>,
35    pub com: Option<[f32; 3]>,
36    pub inertia: Option<[f32; 6]>,
37    pub collision_group: u32,
38    pub collision_mask: u32,
39    pub linear_damping: Option<f32>,
40    pub angular_damping: Option<f32>,
41    pub gravity_scale: f32,
42    pub sleep_velocity: Option<f32>,
43    pub sleep_angular_velocity: Option<f32>,
44    pub kinematic: bool,
45    pub ccd: bool,
46}
47
48impl BodyDesc {
49    pub fn new(collider: ColliderDesc) -> Self {
50        Self {
51            colliders: vec![collider],
52            position: [0.0; 3],
53            orientation: [0.0, 0.0, 0.0, 1.0],
54            velocity: [0.0; 3],
55            angular_velocity: [0.0; 3],
56            mass: 1.0,
57            density: None,
58            com: None,
59            inertia: None,
60            collision_group: DEFAULT_COLLISION_GROUP,
61            collision_mask: DEFAULT_COLLISION_MASK,
62            linear_damping: None,
63            angular_damping: None,
64            gravity_scale: 1.0,
65            sleep_velocity: None,
66            sleep_angular_velocity: None,
67            kinematic: false,
68            ccd: false,
69        }
70    }
71
72    pub fn collider(mut self, collider: ColliderDesc) -> Self {
73        self.colliders.push(collider);
74        self
75    }
76
77    pub fn sphere(radius: f32) -> Self {
78        Self::new(ColliderDesc::new(Shape::sphere(radius)))
79    }
80
81    pub fn cuboid(half_extents: [f32; 3]) -> Self {
82        Self::new(ColliderDesc::new(Shape::cuboid(half_extents)))
83    }
84
85    pub fn capsule(radius: f32, half_height: f32) -> Self {
86        Self::new(ColliderDesc::new(Shape::capsule(radius, half_height)))
87    }
88
89    pub fn cylinder(radius: f32, half_height: f32) -> Self {
90        Self::new(ColliderDesc::new(Shape::cylinder(radius, half_height)))
91    }
92
93    pub fn static_sphere(radius: f32) -> Self {
94        Self {
95            mass: 0.0,
96            ..Self::sphere(radius)
97        }
98    }
99
100    pub fn compound(handles: &[ShapeSourceHandle]) -> Self {
101        let first = handles
102            .first()
103            .expect("compound body requires at least one hull");
104        let mut body = Self::new(ColliderDesc::new(Shape::hull(*first)));
105        for handle in &handles[1..] {
106            body = body.collider(ColliderDesc::new(Shape::hull(*handle)));
107        }
108        body
109    }
110
111    pub fn inverse_mass(&self) -> f32 {
112        if self.kinematic || self.mass <= 0.0 {
113            0.0
114        } else {
115            1.0 / self.mass
116        }
117    }
118
119    pub fn position(mut self, position: [f32; 3]) -> Self {
120        self.position = position;
121        self
122    }
123
124    pub fn restitution(mut self, restitution: f32) -> Self {
125        self.colliders[0].restitution = restitution;
126        self
127    }
128
129    pub fn friction(mut self, friction: f32) -> Self {
130        assert!(friction >= 0.0, "friction must be non-negative");
131        self.colliders[0].friction = friction;
132        self
133    }
134
135    pub fn sensor(mut self, sensor: bool) -> Self {
136        self.colliders[0].sensor = sensor;
137        self
138    }
139
140    pub fn orientation(mut self, orientation: [f32; 4]) -> Self {
141        assert!(
142            (orientation[0] * orientation[0]
143                + orientation[1] * orientation[1]
144                + orientation[2] * orientation[2]
145                + orientation[3] * orientation[3]
146                - 1.0)
147                .abs()
148                < 1e-4,
149            "orientation must be a unit quaternion"
150        );
151        self.orientation = orientation;
152        self
153    }
154
155    pub fn velocity(mut self, velocity: [f32; 3]) -> Self {
156        self.velocity = velocity;
157        self
158    }
159
160    pub fn angular_velocity(mut self, angular_velocity: [f32; 3]) -> Self {
161        self.angular_velocity = angular_velocity;
162        self
163    }
164
165    pub fn mass(mut self, mass: f32) -> Self {
166        assert!(mass >= 0.0, "mass must be non-negative");
167        self.mass = mass;
168        self.density = None;
169        self
170    }
171
172    pub fn density(mut self, density: f32) -> Self {
173        assert!(density >= 0.0, "density must be non-negative");
174        self.density = Some(density);
175        self
176    }
177
178    pub fn damping(mut self, damping: f32) -> Self {
179        assert!(damping >= 0.0, "damping must be non-negative");
180        self.linear_damping = Some(damping);
181        self
182    }
183
184    pub fn angular_damping(mut self, angular_damping: f32) -> Self {
185        assert!(
186            angular_damping >= 0.0,
187            "angular damping must be non-negative"
188        );
189        self.angular_damping = Some(angular_damping);
190        self
191    }
192
193    pub fn gravity_scale(mut self, gravity_scale: f32) -> Self {
194        self.gravity_scale = gravity_scale;
195        self
196    }
197
198    pub fn sleep_thresholds(mut self, velocity: f32, angular_velocity: f32) -> Self {
199        assert!(velocity >= 0.0, "sleep velocity must be non-negative");
200        assert!(
201            angular_velocity >= 0.0,
202            "sleep angular velocity must be non-negative"
203        );
204        self.sleep_velocity = Some(velocity);
205        self.sleep_angular_velocity = Some(angular_velocity);
206        self
207    }
208
209    pub fn com(mut self, com: [f32; 3]) -> Self {
210        self.com = Some(com);
211        self
212    }
213
214    pub fn inertia(mut self, inertia: [f32; 6]) -> Self {
215        assert!(
216            inertia.iter().all(|value| value.is_finite()),
217            "inertia tensor must be finite"
218        );
219        self.inertia = Some(inertia);
220        self
221    }
222
223    pub fn mass_properties(
224        &self,
225        geometry: impl Fn(&Shape) -> Option<SolidGeometry>,
226    ) -> crate::mass::MassProperties {
227        if let Some(inertia) = self.inertia {
228            return crate::mass::mass_properties_of_intent(
229                &self.colliders,
230                self.mass,
231                self.com,
232                Some(inertia),
233                geometry,
234            );
235        }
236        match self.density {
237            Some(density) => crate::mass::compute_mass_properties(
238                &self.colliders,
239                crate::mass::MassSource::Density(density),
240                self.com,
241                geometry,
242            ),
243            None => crate::mass::mass_properties_of_intent(
244                &self.colliders,
245                self.mass,
246                self.com,
247                None,
248                geometry,
249            ),
250        }
251    }
252
253    pub fn effective_mass(&self, geometry: impl Fn(&Shape) -> Option<SolidGeometry>) -> f32 {
254        match self.density {
255            Some(density) => density * crate::mass::solid_volume_of(&self.colliders, &geometry),
256            None => self.mass,
257        }
258    }
259
260    pub fn collision_group(mut self, group: u32) -> Self {
261        self.collision_group = group;
262        self
263    }
264
265    pub fn collision_mask(mut self, mask: u32) -> Self {
266        self.collision_mask = mask;
267        self
268    }
269
270    pub fn kinematic(mut self, kinematic: bool) -> Self {
271        self.kinematic = kinematic;
272        self
273    }
274
275    pub fn ccd(mut self, ccd: bool) -> Self {
276        self.ccd = ccd;
277        self
278    }
279}