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}