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 access;
252mod api;
253mod forces;
254mod lifecycle;
255mod plumbing;
256mod state;
257
258pub use access::*;
259pub use api::*;
260pub use forces::*;
261pub use lifecycle::*;
262pub use plumbing::*;
263pub use state::*;
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268 use crate::math_functions::{make_rot, to_pos, Vec2};
269 use crate::solver_set::{AWAKE_SET, DISABLED_SET, STATIC_SET};
270 use crate::types::{default_body_def, default_world_def};
271 use crate::world::World;
272
273 #[test]
274 fn create_static_dynamic_and_sleeping_bodies() {
275 let mut world = World::new(&default_world_def());
276
277 let s = create_body(&mut world, &default_body_def());
279 let s_index = get_body_full_id(&world, s);
280 assert_eq!(world.bodies[s_index as usize].set_index, STATIC_SET);
281 assert_eq!(world.bodies[s_index as usize].island_id, NULL_INDEX);
282 assert!(world.solver_sets[STATIC_SET as usize]
283 .body_states
284 .is_empty());
285
286 let mut def = default_body_def();
288 def.type_ = BodyType::Dynamic;
289 def.position = to_pos(Vec2 { x: 2.0, y: 3.0 });
290 def.rotation = make_rot(0.5);
291 def.linear_velocity = Vec2 { x: 1.0, y: -1.0 };
292 def.name = "crate".into();
293 let d = create_body(&mut world, &def);
294 let d_index = get_body_full_id(&world, d);
295 {
296 let body = &world.bodies[d_index as usize];
297 assert_eq!(body.set_index, AWAKE_SET);
298 assert_eq!(body.type_, BodyType::Dynamic);
299 assert!(body.island_id != NULL_INDEX);
300 assert_eq!(body.name, "crate");
301 assert!(body.flags & body_flags::DYNAMIC_FLAG != 0);
302 }
303 let xf = get_body_transform(&world, d_index);
304 assert_eq!(crate::math_functions::to_vec2(xf.p).x, 2.0);
305 let awake = &world.solver_sets[AWAKE_SET as usize];
306 assert_eq!(awake.body_sims.len(), 1);
307 assert_eq!(awake.body_states.len(), 1);
308 assert_eq!(awake.body_states[0].linear_velocity.x, 1.0);
309 assert_eq!(awake.island_sims.len(), 1);
310
311 let mut sleep_def = default_body_def();
313 sleep_def.type_ = BodyType::Dynamic;
314 sleep_def.is_awake = false;
315 let z = create_body(&mut world, &sleep_def);
316 let z_index = get_body_full_id(&world, z);
317 let z_set = world.bodies[z_index as usize].set_index;
318 assert!(z_set >= crate::solver_set::FIRST_SLEEPING_SET);
319 assert_eq!(world.solver_sets[z_set as usize].body_sims.len(), 1);
320 assert!(world.solver_sets[z_set as usize].body_states.is_empty());
321 assert!(world.bodies[z_index as usize].island_id != NULL_INDEX);
322
323 let mut disabled_def = default_body_def();
325 disabled_def.type_ = BodyType::Dynamic;
326 disabled_def.is_enabled = false;
327 let x = create_body(&mut world, &disabled_def);
328 let x_index = get_body_full_id(&world, x);
329 assert_eq!(world.bodies[x_index as usize].set_index, DISABLED_SET);
330 assert_eq!(world.bodies[x_index as usize].island_id, NULL_INDEX);
331
332 assert_eq!(s.index1, 1);
334 assert_eq!(d.index1, 2);
335 assert!(d.generation >= 1);
336
337 world.validate_solver_sets();
338 }
339
340 #[test]
341 fn island_lifecycle_via_bodies() {
342 let mut world = World::new(&default_world_def());
343
344 let mut def = default_body_def();
345 def.type_ = BodyType::Dynamic;
346 let a = create_body(&mut world, &def);
347 let b = create_body(&mut world, &def);
348 let a_index = get_body_full_id(&world, a);
349 let b_index = get_body_full_id(&world, b);
350
351 assert_eq!(world.island_id_pool.id_count(), 2);
352
353 let a_island = world.bodies[a_index as usize].island_id;
355 remove_body_from_island(&mut world, a_index);
356 assert_eq!(world.bodies[a_index as usize].island_id, NULL_INDEX);
357 assert_eq!(world.islands[a_island as usize].set_index, NULL_INDEX);
358 assert_eq!(world.island_id_pool.id_count(), 1);
359
360 crate::island::validate_island(&world, world.bodies[b_index as usize].island_id);
362
363 let mut long_name = default_body_def();
365 long_name.name = "abcdefghijklmnop".into();
366 let c = create_body(&mut world, &long_name);
367 let c_index = get_body_full_id(&world, c);
368 assert_eq!(
369 world.bodies[c_index as usize].name.len(),
370 crate::constants::NAME_LENGTH as usize
371 );
372 }
373
374 #[test]
377 fn destroy_shape_and_body_lifecycle() {
378 use crate::broad_phase::update_broad_phase_pairs;
379 use crate::geometry::make_box;
380 use crate::shape::{create_polygon_shape, destroy_shape};
381 use crate::table::shape_pair_key;
382 use crate::types::default_shape_def;
383
384 let mut world = World::new(&default_world_def());
385
386 let mut def = default_body_def();
387 def.type_ = BodyType::Dynamic;
388 let body_a = create_body(&mut world, &def);
389 let body_b = create_body(&mut world, &def);
390 let a_index = get_body_full_id(&world, body_a);
391 let b_index = get_body_full_id(&world, body_b);
392
393 let box_poly = make_box(0.5, 0.5);
394 let shape_def = default_shape_def();
395 let sa = create_polygon_shape(&mut world, body_a, &shape_def, &box_poly);
396 let sb = create_polygon_shape(&mut world, body_b, &shape_def, &box_poly);
397
398 update_broad_phase_pairs(&mut world);
399 assert_eq!(world.contact_id_pool.id_count(), 1);
400
401 destroy_shape(&mut world, sb, true);
403 assert_eq!(world.contact_id_pool.id_count(), 0);
404 assert_eq!(world.shape_id_pool.id_count(), 1);
405 assert_eq!(world.bodies[a_index as usize].contact_count, 0);
406 assert_eq!(world.bodies[b_index as usize].contact_count, 0);
407 assert_eq!(world.bodies[b_index as usize].shape_count, 0);
408 assert_eq!(world.bodies[b_index as usize].head_shape_id, NULL_INDEX);
409 assert_eq!(world.shapes[(sb.index1 - 1) as usize].id, NULL_INDEX);
410 assert!(!world
411 .broad_phase
412 .pair_set
413 .contains_key(shape_pair_key(sa.index1 - 1, sb.index1 - 1)));
414
415 let island_a = world.bodies[a_index as usize].island_id;
417 assert!(island_a != NULL_INDEX);
418 destroy_body(&mut world, body_a);
419 assert_eq!(world.body_id_pool.id_count(), 1);
420 assert_eq!(world.shape_id_pool.id_count(), 0);
421 assert_eq!(world.bodies[a_index as usize].id, NULL_INDEX);
422 assert_eq!(world.bodies[a_index as usize].set_index, NULL_INDEX);
423 assert_eq!(world.islands[island_a as usize].island_id, NULL_INDEX);
424 assert_eq!(
425 world.solver_sets[AWAKE_SET as usize].body_sims.len(),
426 1,
427 "only body B remains awake"
428 );
429 assert_eq!(
431 world.solver_sets[AWAKE_SET as usize].body_sims[0].body_id,
432 b_index
433 );
434 assert_eq!(world.bodies[b_index as usize].local_index, 0);
435
436 destroy_body(&mut world, body_b);
437 assert_eq!(world.body_id_pool.id_count(), 0);
438 assert!(world.solver_sets[AWAKE_SET as usize].body_sims.is_empty());
439 assert!(world.solver_sets[AWAKE_SET as usize].island_sims.is_empty());
440
441 world.validate_solver_sets();
442
443 let reborn = create_body(&mut world, &def);
446 assert_eq!(get_body_full_id(&world, reborn), 1);
447 assert!(reborn.generation > body_b.generation);
448 }
449}