1use super::{get_body_full_id, get_body_transform_quick, wake_body, BodySim, BodyState};
13use crate::body::body_flags;
14use crate::collision::MassData;
15use crate::constants::speculative_distance;
16use crate::core::NULL_INDEX;
17use crate::geometry::compute_fat_shape_aabb;
18use crate::id::BodyId;
19use crate::math_functions::{
20 aabb_contains, aabb_union, abs_float, add, cross_sv, inv_rotate_vector,
21 inv_transform_world_point, is_valid_float, is_valid_position, is_valid_rotation, is_valid_vec2,
22 length, length_squared, mul_sv, relative_angle, rotate_vector, round_down_float,
23 round_up_float, sub, sub_pos, transform_world_point, Aabb, Pos, Rot, Vec2, WorldTransform,
24 VEC2_ZERO,
25};
26use crate::solver_set::{AWAKE_SET, DISABLED_SET};
27use crate::types::BodyType;
28use crate::world::World;
29
30pub fn body_is_valid(world: &World, id: BodyId) -> bool {
34 if id.index1 < 1 || (world.bodies.len() as i32) < id.index1 {
35 return false;
37 }
38
39 let body = &world.bodies[(id.index1 - 1) as usize];
40 if body.set_index == NULL_INDEX {
41 return false;
43 }
44
45 debug_assert!(body.local_index != NULL_INDEX);
46
47 if body.generation != id.generation {
48 return false;
50 }
51
52 true
53}
54
55pub fn body_get_position(world: &World, body_id: BodyId) -> Pos {
57 let body_index = get_body_full_id(world, body_id);
58 get_body_transform_quick(world, &world.bodies[body_index as usize]).p
59}
60
61pub fn body_get_rotation(world: &World, body_id: BodyId) -> Rot {
63 let body_index = get_body_full_id(world, body_id);
64 get_body_transform_quick(world, &world.bodies[body_index as usize]).q
65}
66
67pub fn body_get_transform(world: &World, body_id: BodyId) -> WorldTransform {
69 let body_index = get_body_full_id(world, body_id);
70 get_body_transform_quick(world, &world.bodies[body_index as usize])
71}
72
73pub fn body_get_local_point(world: &World, body_id: BodyId, world_point: Pos) -> Vec2 {
75 let transform = body_get_transform(world, body_id);
76 inv_transform_world_point(transform, world_point)
77}
78
79pub fn body_get_world_point(world: &World, body_id: BodyId, local_point: Vec2) -> Pos {
81 let transform = body_get_transform(world, body_id);
82 transform_world_point(transform, local_point)
83}
84
85pub fn body_get_local_vector(world: &World, body_id: BodyId, world_vector: Vec2) -> Vec2 {
87 let transform = body_get_transform(world, body_id);
88 inv_rotate_vector(transform.q, world_vector)
89}
90
91pub fn body_get_world_vector(world: &World, body_id: BodyId, local_vector: Vec2) -> Vec2 {
93 let transform = body_get_transform(world, body_id);
94 rotate_vector(transform.q, local_vector)
95}
96
97pub fn body_set_transform(world: &mut World, body_id: BodyId, position: Pos, rotation: Rot) {
99 crate::recording::record_op(world, |rec, _| {
100 crate::recording::write_body_set_transform(rec, body_id, position, rotation)
101 });
102 debug_assert!(is_valid_position(position));
103 debug_assert!(is_valid_rotation(rotation));
104 debug_assert!(body_is_valid(world, body_id));
105 debug_assert!(!world.locked);
106
107 let body_index = get_body_full_id(world, body_id);
108 let (set_index, local_index) = {
109 let body = &world.bodies[body_index as usize];
110 (body.set_index, body.local_index)
111 };
112
113 let transform = {
114 let body_sim = &mut world.solver_sets[set_index as usize].body_sims[local_index as usize];
115 body_sim.transform.p = position;
116 body_sim.transform.q = rotation;
117 body_sim.center = transform_world_point(body_sim.transform, body_sim.local_center);
118
119 body_sim.rotation0 = body_sim.transform.q;
120 body_sim.center0 = body_sim.center;
121 body_sim.transform
122 };
123
124 let speculative_distance_ = speculative_distance();
125
126 let mut shape_id = world.bodies[body_index as usize].head_shape_id;
127 while shape_id != NULL_INDEX {
128 let (next_shape_id, moved) = {
129 let shape = &mut world.shapes[shape_id as usize];
130 let aabb = compute_fat_shape_aabb(&shape.geometry, transform, speculative_distance_);
131 shape.aabb = aabb;
132
133 let mut moved = None;
134 if !aabb_contains(shape.fat_aabb, aabb) {
135 let margin = shape.aabb_margin;
136 let fat_aabb = Aabb {
137 lower_bound: Vec2 {
138 x: aabb.lower_bound.x - margin,
139 y: aabb.lower_bound.y - margin,
140 },
141 upper_bound: Vec2 {
142 x: aabb.upper_bound.x + margin,
143 y: aabb.upper_bound.y + margin,
144 },
145 };
146 shape.fat_aabb = fat_aabb;
147
148 if shape.proxy_key != NULL_INDEX {
150 moved = Some((shape.proxy_key, fat_aabb));
151 }
152 }
153 (shape.next_shape_id, moved)
154 };
155
156 if let Some((proxy_key, fat_aabb)) = moved {
157 world.broad_phase.move_proxy(proxy_key, fat_aabb);
158 }
159
160 shape_id = next_shape_id;
161 }
162}
163
164pub fn body_get_linear_velocity(world: &World, body_id: BodyId) -> Vec2 {
166 let body_index = get_body_full_id(world, body_id);
167 let body = &world.bodies[body_index as usize];
168 if body.set_index == AWAKE_SET {
169 return world.solver_sets[AWAKE_SET as usize].body_states[body.local_index as usize]
170 .linear_velocity;
171 }
172 VEC2_ZERO
173}
174
175pub fn body_get_angular_velocity(world: &World, body_id: BodyId) -> f32 {
177 let body_index = get_body_full_id(world, body_id);
178 let body = &world.bodies[body_index as usize];
179 if body.set_index == AWAKE_SET {
180 return world.solver_sets[AWAKE_SET as usize].body_states[body.local_index as usize]
181 .angular_velocity;
182 }
183 0.0
184}
185
186pub fn body_set_linear_velocity(world: &mut World, body_id: BodyId, linear_velocity: Vec2) {
188 crate::recording::record_op(world, |rec, _| {
189 crate::recording::write_body_vec2(
190 rec,
191 crate::recording::OP_BODY_SET_LINEAR_VELOCITY,
192 body_id,
193 linear_velocity,
194 )
195 });
196 let body_index = get_body_full_id(world, body_id);
197
198 if world.bodies[body_index as usize].type_ == BodyType::Static {
199 return;
200 }
201
202 if length_squared(linear_velocity) > 0.0 {
203 wake_body(world, body_index);
204 }
205
206 let body = &world.bodies[body_index as usize];
207 if body.set_index != AWAKE_SET {
208 return;
209 }
210 let local_index = body.local_index;
211 world.solver_sets[AWAKE_SET as usize].body_states[local_index as usize].linear_velocity =
212 linear_velocity;
213}
214
215pub fn body_set_angular_velocity(world: &mut World, body_id: BodyId, angular_velocity: f32) {
217 crate::recording::record_op(world, |rec, _| {
218 crate::recording::write_body_f32(
219 rec,
220 crate::recording::OP_BODY_SET_ANGULAR_VELOCITY,
221 body_id,
222 angular_velocity,
223 )
224 });
225 let body_index = get_body_full_id(world, body_id);
226
227 {
228 let body = &world.bodies[body_index as usize];
229 if body.type_ == BodyType::Static || body.flags & body_flags::LOCK_ANGULAR_Z != 0 {
230 return;
231 }
232 }
233
234 if angular_velocity != 0.0 {
235 wake_body(world, body_index);
236 }
237
238 let body = &world.bodies[body_index as usize];
239 if body.set_index != AWAKE_SET {
240 return;
241 }
242 let local_index = body.local_index;
243 world.solver_sets[AWAKE_SET as usize].body_states[local_index as usize].angular_velocity =
244 angular_velocity;
245}
246
247pub fn body_set_target_transform(
249 world: &mut World,
250 body_id: BodyId,
251 target: WorldTransform,
252 time_step: f32,
253 wake: bool,
254) {
255 crate::recording::record_op(world, |rec, _| {
256 crate::recording::write_body_set_target_transform(rec, body_id, target, time_step, wake)
257 });
258 let body_index = get_body_full_id(world, body_id);
259
260 {
261 let body = &world.bodies[body_index as usize];
262 if body.set_index == DISABLED_SET {
263 return;
264 }
265
266 if body.type_ == BodyType::Static || time_step <= 0.0 {
267 return;
268 }
269
270 if body.set_index != AWAKE_SET && !wake {
271 return;
272 }
273 }
274
275 let (sim_local_center, sim_center, sim_q, sim_max_extent) = {
276 let body = &world.bodies[body_index as usize];
277 let sim = &world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize];
278 (
279 sim.local_center,
280 sim.center,
281 sim.transform.q,
282 sim.max_extent,
283 )
284 };
285
286 let delta = sub_pos(transform_world_point(target, sim_local_center), sim_center);
289 let inv_time_step = 1.0 / time_step;
290 let linear_velocity = mul_sv(inv_time_step, delta);
291
292 let q1 = sim_q;
294 let q2 = target.q;
295 let delta_angle = relative_angle(q1, q2);
296 let angular_velocity = inv_time_step * delta_angle;
297
298 if world.bodies[body_index as usize].set_index != AWAKE_SET {
301 let max_velocity = length(linear_velocity) + abs_float(angular_velocity) * sim_max_extent;
302
303 if max_velocity < world.bodies[body_index as usize].sleep_threshold {
305 return;
306 }
307
308 wake_body(world, body_index);
310 }
311
312 debug_assert!(world.bodies[body_index as usize].set_index == AWAKE_SET);
313
314 let local_index = world.bodies[body_index as usize].local_index;
315 let state = &mut world.solver_sets[AWAKE_SET as usize].body_states[local_index as usize];
316 state.linear_velocity = linear_velocity;
317 state.angular_velocity = angular_velocity;
318}
319
320pub fn body_get_local_point_velocity(world: &World, body_id: BodyId, local_point: Vec2) -> Vec2 {
322 let body_index = get_body_full_id(world, body_id);
323 let body = &world.bodies[body_index as usize];
324 if body.set_index != AWAKE_SET {
325 return VEC2_ZERO;
326 }
327
328 let state: &BodyState =
329 &world.solver_sets[AWAKE_SET as usize].body_states[body.local_index as usize];
330 let body_sim: &BodySim =
331 &world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize];
332
333 let r = rotate_vector(
334 body_sim.transform.q,
335 sub(local_point, body_sim.local_center),
336 );
337 add(state.linear_velocity, cross_sv(state.angular_velocity, r))
338}
339
340pub fn body_get_world_point_velocity(world: &World, body_id: BodyId, world_point: Pos) -> Vec2 {
342 let body_index = get_body_full_id(world, body_id);
343 let body = &world.bodies[body_index as usize];
344 if body.set_index != AWAKE_SET {
345 return VEC2_ZERO;
346 }
347
348 let state: &BodyState =
349 &world.solver_sets[AWAKE_SET as usize].body_states[body.local_index as usize];
350 let body_sim: &BodySim =
351 &world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize];
352
353 let r = sub_pos(world_point, body_sim.center);
354 add(state.linear_velocity, cross_sv(state.angular_velocity, r))
355}
356
357pub fn body_get_type(world: &World, body_id: BodyId) -> BodyType {
359 let body_index = get_body_full_id(world, body_id);
360 world.bodies[body_index as usize].type_
361}
362
363pub fn body_compute_aabb(world: &World, body_id: BodyId) -> Aabb {
365 debug_assert!(!world.locked);
366
367 let body_index = get_body_full_id(world, body_id);
368 let body = &world.bodies[body_index as usize];
369 if body.head_shape_id == NULL_INDEX {
370 let transform = get_body_transform_quick(world, body);
373 return Aabb {
374 lower_bound: Vec2 {
375 x: round_down_float(transform.p.x as f64),
376 y: round_down_float(transform.p.y as f64),
377 },
378 upper_bound: Vec2 {
379 x: round_up_float(transform.p.x as f64),
380 y: round_up_float(transform.p.y as f64),
381 },
382 };
383 }
384
385 let mut shape = &world.shapes[body.head_shape_id as usize];
386 let mut aabb = shape.aabb;
387 while shape.next_shape_id != NULL_INDEX {
388 shape = &world.shapes[shape.next_shape_id as usize];
389 aabb = aabb_union(aabb, shape.aabb);
390 }
391
392 aabb
393}
394
395pub fn body_get_mass(world: &World, body_id: BodyId) -> f32 {
397 let body_index = get_body_full_id(world, body_id);
398 world.bodies[body_index as usize].mass
399}
400
401pub fn body_get_rotational_inertia(world: &World, body_id: BodyId) -> f32 {
403 let body_index = get_body_full_id(world, body_id);
404 world.bodies[body_index as usize].inertia
405}
406
407pub fn body_get_local_center(world: &World, body_id: BodyId) -> Vec2 {
409 let body_index = get_body_full_id(world, body_id);
410 let body = &world.bodies[body_index as usize];
411 world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize].local_center
412}
413
414pub fn body_get_world_center(world: &World, body_id: BodyId) -> Pos {
416 let body_index = get_body_full_id(world, body_id);
417 let body = &world.bodies[body_index as usize];
418 world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize].center
419}
420
421pub fn body_set_mass_data(world: &mut World, body_id: BodyId, mass_data: MassData) {
423 crate::recording::record_op(world, |rec, _| {
424 crate::recording::write_body_set_mass_data(rec, body_id, mass_data)
425 });
426 debug_assert!(is_valid_float(mass_data.mass) && mass_data.mass >= 0.0);
427 debug_assert!(
428 is_valid_float(mass_data.rotational_inertia) && mass_data.rotational_inertia >= 0.0
429 );
430 debug_assert!(is_valid_vec2(mass_data.center));
431 debug_assert!(!world.locked);
432 if world.locked {
433 return;
434 }
435
436 let body_index = get_body_full_id(world, body_id);
437 let (set_index, local_index) = {
438 let body = &mut world.bodies[body_index as usize];
439 body.mass = mass_data.mass;
440 body.inertia = mass_data.rotational_inertia;
441 (body.set_index, body.local_index)
442 };
443
444 let body_sim = &mut world.solver_sets[set_index as usize].body_sims[local_index as usize];
445 body_sim.local_center = mass_data.center;
446
447 let center = transform_world_point(body_sim.transform, mass_data.center);
448 body_sim.center = center;
449 body_sim.center0 = center;
450
451 body_sim.inv_mass = if mass_data.mass > 0.0 {
452 1.0 / mass_data.mass
453 } else {
454 0.0
455 };
456 body_sim.inv_inertia = if mass_data.rotational_inertia > 0.0 {
457 1.0 / mass_data.rotational_inertia
458 } else {
459 0.0
460 };
461}
462
463pub fn body_get_mass_data(world: &World, body_id: BodyId) -> MassData {
465 let body_index = get_body_full_id(world, body_id);
466 let body = &world.bodies[body_index as usize];
467 let body_sim = &world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize];
468 MassData {
469 mass: body.mass,
470 center: body_sim.local_center,
471 rotational_inertia: body.inertia,
472 }
473}
474
475pub fn body_apply_mass_from_shapes(world: &mut World, body_id: BodyId) {
477 crate::recording::record_op(world, |rec, _| {
478 crate::recording::write_body_marker(
479 rec,
480 crate::recording::OP_BODY_APPLY_MASS_FROM_SHAPES,
481 body_id,
482 )
483 });
484 debug_assert!(!world.locked);
485 if world.locked {
486 return;
487 }
488
489 let body_index = get_body_full_id(world, body_id);
490 super::update_body_mass_data(world, body_index);
491}
492
493pub fn body_set_linear_damping(world: &mut World, body_id: BodyId, linear_damping: f32) {
495 crate::recording::record_op(world, |rec, _| {
496 crate::recording::write_body_f32(
497 rec,
498 crate::recording::OP_BODY_SET_LINEAR_DAMPING,
499 body_id,
500 linear_damping,
501 )
502 });
503 debug_assert!(is_valid_float(linear_damping) && linear_damping >= 0.0);
504 debug_assert!(!world.locked);
505 if world.locked {
506 return;
507 }
508
509 let body_index = get_body_full_id(world, body_id);
510 let body = &world.bodies[body_index as usize];
511 world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize]
512 .linear_damping = linear_damping;
513}
514
515pub fn body_get_linear_damping(world: &World, body_id: BodyId) -> f32 {
517 let body_index = get_body_full_id(world, body_id);
518 let body = &world.bodies[body_index as usize];
519 world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize].linear_damping
520}
521
522pub fn body_set_angular_damping(world: &mut World, body_id: BodyId, angular_damping: f32) {
524 crate::recording::record_op(world, |rec, _| {
525 crate::recording::write_body_f32(
526 rec,
527 crate::recording::OP_BODY_SET_ANGULAR_DAMPING,
528 body_id,
529 angular_damping,
530 )
531 });
532 debug_assert!(is_valid_float(angular_damping) && angular_damping >= 0.0);
533 debug_assert!(!world.locked);
534 if world.locked {
535 return;
536 }
537
538 let body_index = get_body_full_id(world, body_id);
539 let body = &world.bodies[body_index as usize];
540 world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize]
541 .angular_damping = angular_damping;
542}
543
544pub fn body_get_angular_damping(world: &World, body_id: BodyId) -> f32 {
546 let body_index = get_body_full_id(world, body_id);
547 let body = &world.bodies[body_index as usize];
548 world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize].angular_damping
549}
550
551pub fn body_set_gravity_scale(world: &mut World, body_id: BodyId, gravity_scale: f32) {
553 crate::recording::record_op(world, |rec, _| {
554 crate::recording::write_body_f32(
555 rec,
556 crate::recording::OP_BODY_SET_GRAVITY_SCALE,
557 body_id,
558 gravity_scale,
559 )
560 });
561 debug_assert!(body_is_valid(world, body_id));
562 debug_assert!(is_valid_float(gravity_scale));
563 debug_assert!(!world.locked);
564 if world.locked {
565 return;
566 }
567
568 let body_index = get_body_full_id(world, body_id);
569 let body = &world.bodies[body_index as usize];
570 world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize].gravity_scale =
571 gravity_scale;
572}
573
574pub fn body_get_gravity_scale(world: &World, body_id: BodyId) -> f32 {
576 debug_assert!(body_is_valid(world, body_id));
577 let body_index = get_body_full_id(world, body_id);
578 let body = &world.bodies[body_index as usize];
579 world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize].gravity_scale
580}
581
582pub fn body_set_user_data(world: &mut World, body_id: BodyId, user_data: u64) {
584 let body_index = get_body_full_id(world, body_id);
585 world.bodies[body_index as usize].user_data = user_data;
586}
587
588pub fn body_get_user_data(world: &World, body_id: BodyId) -> u64 {
590 let body_index = get_body_full_id(world, body_id);
591 world.bodies[body_index as usize].user_data
592}
593
594pub fn body_set_name(world: &mut World, body_id: BodyId, name: &str) {
597 crate::recording::record_op(world, |rec, _| {
598 crate::recording::write_body_set_name(rec, body_id, name)
599 });
600 let body_index = get_body_full_id(world, body_id);
601 let body = &mut world.bodies[body_index as usize];
602 body.name = name
605 .chars()
606 .take(crate::constants::NAME_LENGTH as usize)
607 .collect();
608}
609
610pub fn body_get_name(world: &World, body_id: BodyId) -> &str {
612 let body_index = get_body_full_id(world, body_id);
613 &world.bodies[body_index as usize].name
614}