1use crate::core::NULL_INDEX;
14use crate::distance::Sweep;
15use crate::math_functions::{
16 sub_pos, Pos, Rot, Vec2, WorldTransform, POS_ZERO, ROT_IDENTITY, VEC2_ZERO,
17 WORLD_TRANSFORM_IDENTITY,
18};
19use crate::types::BodyType;
20
21pub mod body_flags {
23 pub const LOCK_LINEAR_X: u32 = 0x00000001;
25 pub const LOCK_LINEAR_Y: u32 = 0x00000002;
27 pub const LOCK_ANGULAR_Z: u32 = 0x00000004;
29 pub const IS_FAST: u32 = 0x00000008;
31 pub const IS_BULLET: u32 = 0x00000010;
33 pub const IS_SPEED_CAPPED: u32 = 0x00000020;
35 pub const HAD_TIME_OF_IMPACT: u32 = 0x00000040;
37 pub const ALLOW_FAST_ROTATION: u32 = 0x00000080;
39 pub const ENLARGE_BOUNDS: u32 = 0x00000100;
41 pub const DYNAMIC_FLAG: u32 = 0x00000200;
43 pub const DIRTY_MASS: u32 = 0x00000400;
46 pub const ENABLE_SLEEP: u32 = 0x00000800;
47 pub const BODY_ENABLE_CONTACT_RECYCLING: u32 = 0x00001000;
48
49 pub const ALL_LOCKS: u32 = LOCK_ANGULAR_Z | LOCK_LINEAR_X | LOCK_LINEAR_Y;
51 pub const FIXED_ROTATION: u32 = LOCK_ANGULAR_Z;
53 pub const BODY_TRANSIENT_FLAGS: u32 = IS_FAST | IS_SPEED_CAPPED | HAD_TIME_OF_IMPACT;
56}
57
58#[derive(Debug, Clone)]
60pub struct Body {
61 pub user_data: u64,
62
63 pub set_index: i32,
65
66 pub local_index: i32,
68
69 pub head_contact_key: i32,
71 pub contact_count: i32,
72
73 pub head_shape_id: i32,
74 pub shape_count: i32,
75
76 pub head_chain_id: i32,
77
78 pub head_joint_key: i32,
80 pub joint_count: i32,
81
82 pub island_id: i32,
84
85 pub island_index: i32,
87
88 pub mass: f32,
89
90 pub inertia: f32,
92
93 pub sleep_threshold: f32,
94 pub sleep_time: f32,
95
96 pub body_move_index: i32,
98
99 pub id: i32,
100
101 pub flags: u32,
103
104 pub type_: BodyType,
105
106 pub generation: u16,
109
110 pub name: String,
112}
113
114impl Default for Body {
115 fn default() -> Self {
116 Body {
117 user_data: 0,
118 set_index: NULL_INDEX,
119 local_index: NULL_INDEX,
120 head_contact_key: NULL_INDEX,
121 contact_count: 0,
122 head_shape_id: NULL_INDEX,
123 shape_count: 0,
124 head_chain_id: NULL_INDEX,
125 head_joint_key: NULL_INDEX,
126 joint_count: 0,
127 island_id: NULL_INDEX,
128 island_index: NULL_INDEX,
129 mass: 0.0,
130 inertia: 0.0,
131 sleep_threshold: 0.0,
132 sleep_time: 0.0,
133 body_move_index: NULL_INDEX,
134 id: NULL_INDEX,
135 flags: 0,
136 type_: BodyType::Static,
137 generation: 0,
138 name: String::new(),
139 }
140 }
141}
142
143#[derive(Debug, Clone, Copy, PartialEq)]
150pub struct BodyState {
151 pub linear_velocity: Vec2,
152 pub angular_velocity: f32,
153 pub flags: u32,
155 pub delta_position: Vec2,
157 pub delta_rotation: Rot,
160}
161
162pub const IDENTITY_BODY_STATE: BodyState = BodyState {
165 linear_velocity: VEC2_ZERO,
166 angular_velocity: 0.0,
167 flags: 0,
168 delta_position: VEC2_ZERO,
169 delta_rotation: ROT_IDENTITY,
170};
171
172impl Default for BodyState {
173 fn default() -> Self {
174 IDENTITY_BODY_STATE
175 }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq)]
181pub struct BodySim {
182 pub transform: WorldTransform,
184
185 pub center: Pos,
187
188 pub rotation0: Rot,
190 pub center0: Pos,
191
192 pub local_center: Vec2,
194
195 pub force: Vec2,
196 pub torque: f32,
197
198 pub inv_mass: f32,
200 pub inv_inertia: f32,
201
202 pub min_extent: f32,
203 pub max_extent: f32,
204 pub linear_damping: f32,
205 pub angular_damping: f32,
206 pub gravity_scale: f32,
207
208 pub body_id: i32,
210
211 pub flags: u32,
213}
214
215impl Default for BodySim {
216 fn default() -> Self {
217 BodySim {
218 transform: WORLD_TRANSFORM_IDENTITY,
219 center: POS_ZERO,
220 rotation0: ROT_IDENTITY,
221 center0: POS_ZERO,
222 local_center: VEC2_ZERO,
223 force: VEC2_ZERO,
224 torque: 0.0,
225 inv_mass: 0.0,
226 inv_inertia: 0.0,
227 min_extent: 0.0,
228 max_extent: 0.0,
229 linear_damping: 0.0,
230 angular_damping: 0.0,
231 gravity_scale: 0.0,
232 body_id: NULL_INDEX,
233 flags: 0,
234 }
235 }
236}
237
238pub fn make_relative_sweep(body_sim: &BodySim, base: Pos) -> Sweep {
242 Sweep {
243 c1: sub_pos(body_sim.center0, base),
244 c2: sub_pos(body_sim.center, base),
245 q1: body_sim.rotation0,
246 q2: body_sim.transform.q,
247 local_center: body_sim.local_center,
248 }
249}
250
251mod lifecycle;
252mod plumbing;
253
254pub use lifecycle::*;
255pub use plumbing::*;
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260 use crate::math_functions::{make_rot, to_pos, Vec2};
261 use crate::solver_set::{AWAKE_SET, DISABLED_SET, STATIC_SET};
262 use crate::types::{default_body_def, default_world_def};
263 use crate::world::World;
264
265 #[test]
266 fn create_static_dynamic_and_sleeping_bodies() {
267 let mut world = World::new(&default_world_def());
268
269 let s = create_body(&mut world, &default_body_def());
271 let s_index = get_body_full_id(&world, s);
272 assert_eq!(world.bodies[s_index as usize].set_index, STATIC_SET);
273 assert_eq!(world.bodies[s_index as usize].island_id, NULL_INDEX);
274 assert!(world.solver_sets[STATIC_SET as usize]
275 .body_states
276 .is_empty());
277
278 let mut def = default_body_def();
280 def.type_ = BodyType::Dynamic;
281 def.position = to_pos(Vec2 { x: 2.0, y: 3.0 });
282 def.rotation = make_rot(0.5);
283 def.linear_velocity = Vec2 { x: 1.0, y: -1.0 };
284 def.name = "crate".into();
285 let d = create_body(&mut world, &def);
286 let d_index = get_body_full_id(&world, d);
287 {
288 let body = &world.bodies[d_index as usize];
289 assert_eq!(body.set_index, AWAKE_SET);
290 assert_eq!(body.type_, BodyType::Dynamic);
291 assert!(body.island_id != NULL_INDEX);
292 assert_eq!(body.name, "crate");
293 assert!(body.flags & body_flags::DYNAMIC_FLAG != 0);
294 }
295 let xf = get_body_transform(&world, d_index);
296 assert_eq!(crate::math_functions::to_vec2(xf.p).x, 2.0);
297 let awake = &world.solver_sets[AWAKE_SET as usize];
298 assert_eq!(awake.body_sims.len(), 1);
299 assert_eq!(awake.body_states.len(), 1);
300 assert_eq!(awake.body_states[0].linear_velocity.x, 1.0);
301 assert_eq!(awake.island_sims.len(), 1);
302
303 let mut sleep_def = default_body_def();
305 sleep_def.type_ = BodyType::Dynamic;
306 sleep_def.is_awake = false;
307 let z = create_body(&mut world, &sleep_def);
308 let z_index = get_body_full_id(&world, z);
309 let z_set = world.bodies[z_index as usize].set_index;
310 assert!(z_set >= crate::solver_set::FIRST_SLEEPING_SET);
311 assert_eq!(world.solver_sets[z_set as usize].body_sims.len(), 1);
312 assert!(world.solver_sets[z_set as usize].body_states.is_empty());
313 assert!(world.bodies[z_index as usize].island_id != NULL_INDEX);
314
315 let mut disabled_def = default_body_def();
317 disabled_def.type_ = BodyType::Dynamic;
318 disabled_def.is_enabled = false;
319 let x = create_body(&mut world, &disabled_def);
320 let x_index = get_body_full_id(&world, x);
321 assert_eq!(world.bodies[x_index as usize].set_index, DISABLED_SET);
322 assert_eq!(world.bodies[x_index as usize].island_id, NULL_INDEX);
323
324 assert_eq!(s.index1, 1);
326 assert_eq!(d.index1, 2);
327 assert!(d.generation >= 1);
328
329 world.validate_solver_sets();
330 }
331
332 #[test]
333 fn island_lifecycle_via_bodies() {
334 let mut world = World::new(&default_world_def());
335
336 let mut def = default_body_def();
337 def.type_ = BodyType::Dynamic;
338 let a = create_body(&mut world, &def);
339 let b = create_body(&mut world, &def);
340 let a_index = get_body_full_id(&world, a);
341 let b_index = get_body_full_id(&world, b);
342
343 assert_eq!(world.island_id_pool.id_count(), 2);
344
345 let a_island = world.bodies[a_index as usize].island_id;
347 remove_body_from_island(&mut world, a_index);
348 assert_eq!(world.bodies[a_index as usize].island_id, NULL_INDEX);
349 assert_eq!(world.islands[a_island as usize].set_index, NULL_INDEX);
350 assert_eq!(world.island_id_pool.id_count(), 1);
351
352 crate::island::validate_island(&world, world.bodies[b_index as usize].island_id);
354
355 let mut long_name = default_body_def();
357 long_name.name = "abcdefghijklmnop".into();
358 let c = create_body(&mut world, &long_name);
359 let c_index = get_body_full_id(&world, c);
360 assert_eq!(
361 world.bodies[c_index as usize].name.len(),
362 crate::constants::NAME_LENGTH as usize
363 );
364 }
365
366 #[test]
369 fn destroy_shape_and_body_lifecycle() {
370 use crate::broad_phase::update_broad_phase_pairs;
371 use crate::geometry::make_box;
372 use crate::shape::{create_polygon_shape, destroy_shape};
373 use crate::table::shape_pair_key;
374 use crate::types::default_shape_def;
375
376 let mut world = World::new(&default_world_def());
377
378 let mut def = default_body_def();
379 def.type_ = BodyType::Dynamic;
380 let body_a = create_body(&mut world, &def);
381 let body_b = create_body(&mut world, &def);
382 let a_index = get_body_full_id(&world, body_a);
383 let b_index = get_body_full_id(&world, body_b);
384
385 let box_poly = make_box(0.5, 0.5);
386 let shape_def = default_shape_def();
387 let sa = create_polygon_shape(&mut world, body_a, &shape_def, &box_poly);
388 let sb = create_polygon_shape(&mut world, body_b, &shape_def, &box_poly);
389
390 update_broad_phase_pairs(&mut world);
391 assert_eq!(world.contact_id_pool.id_count(), 1);
392
393 destroy_shape(&mut world, sb, true);
395 assert_eq!(world.contact_id_pool.id_count(), 0);
396 assert_eq!(world.shape_id_pool.id_count(), 1);
397 assert_eq!(world.bodies[a_index as usize].contact_count, 0);
398 assert_eq!(world.bodies[b_index as usize].contact_count, 0);
399 assert_eq!(world.bodies[b_index as usize].shape_count, 0);
400 assert_eq!(world.bodies[b_index as usize].head_shape_id, NULL_INDEX);
401 assert_eq!(world.shapes[(sb.index1 - 1) as usize].id, NULL_INDEX);
402 assert!(!world
403 .broad_phase
404 .pair_set
405 .contains_key(shape_pair_key(sa.index1 - 1, sb.index1 - 1)));
406
407 let island_a = world.bodies[a_index as usize].island_id;
409 assert!(island_a != NULL_INDEX);
410 destroy_body(&mut world, body_a);
411 assert_eq!(world.body_id_pool.id_count(), 1);
412 assert_eq!(world.shape_id_pool.id_count(), 0);
413 assert_eq!(world.bodies[a_index as usize].id, NULL_INDEX);
414 assert_eq!(world.bodies[a_index as usize].set_index, NULL_INDEX);
415 assert_eq!(world.islands[island_a as usize].island_id, NULL_INDEX);
416 assert_eq!(
417 world.solver_sets[AWAKE_SET as usize].body_sims.len(),
418 1,
419 "only body B remains awake"
420 );
421 assert_eq!(
423 world.solver_sets[AWAKE_SET as usize].body_sims[0].body_id,
424 b_index
425 );
426 assert_eq!(world.bodies[b_index as usize].local_index, 0);
427
428 destroy_body(&mut world, body_b);
429 assert_eq!(world.body_id_pool.id_count(), 0);
430 assert!(world.solver_sets[AWAKE_SET as usize].body_sims.is_empty());
431 assert!(world.solver_sets[AWAKE_SET as usize].island_sims.is_empty());
432
433 world.validate_solver_sets();
434
435 let reborn = create_body(&mut world, &def);
438 assert_eq!(get_body_full_id(&world, reborn), 1);
439 assert!(reborn.generation > body_b.generation);
440 }
441}