1use super::{
10 compute_shape_mass, get_shape_index, make_shape_distance_proxy, ray_cast_shape,
11 update_shape_aabbs,
12};
13use crate::body::{
14 get_body_transform, get_body_transform_quick, make_body_id, update_body_mass_data,
15};
16use crate::collision::{MassData, RayCastInput, ShapeGeometry, ShapeType, WorldCastOutput};
17use crate::core::NULL_INDEX;
18use crate::distance::{make_proxy, shape_distance, DistanceInput, SimplexCache};
19use crate::events::ContactData;
20use crate::geometry::{point_in_capsule, point_in_circle, point_in_polygon};
21use crate::id::{BodyId, ContactId, ShapeId};
22use crate::math_functions::{
23 inv_mul_world_transforms, inv_transform_world_point, is_valid_float, is_valid_position,
24 is_valid_vec2, offset_pos, to_relative_transform, transform_world_point, Aabb, Pos, Vec2,
25 ROT_IDENTITY, VEC2_ZERO,
26};
27use crate::solver_set::AWAKE_SET;
28use crate::types::{Filter, SurfaceMaterial};
29use crate::world::World;
30
31pub fn shape_is_valid(world: &World, id: ShapeId) -> bool {
34 let shape_id = id.index1 - 1;
35 if shape_id < 0 || world.shapes.len() as i32 <= shape_id {
36 return false;
37 }
38
39 let shape = &world.shapes[shape_id as usize];
40 if shape.id == NULL_INDEX {
41 return false;
43 }
44
45 debug_assert!(shape.id == shape_id);
46
47 id.generation == shape.generation
48}
49
50pub fn shape_get_body(world: &World, shape_id: ShapeId) -> BodyId {
52 let shape_index = get_shape_index(world, shape_id);
53 make_body_id(world, world.shapes[shape_index as usize].body_id)
54}
55
56pub fn shape_set_user_data(world: &mut World, shape_id: ShapeId, user_data: u64) {
58 let shape_index = get_shape_index(world, shape_id);
59 world.shapes[shape_index as usize].user_data = user_data;
60}
61
62pub fn shape_get_user_data(world: &World, shape_id: ShapeId) -> u64 {
64 let shape_index = get_shape_index(world, shape_id);
65 world.shapes[shape_index as usize].user_data
66}
67
68pub fn shape_is_sensor(world: &World, shape_id: ShapeId) -> bool {
70 let shape_index = get_shape_index(world, shape_id);
71 world.shapes[shape_index as usize].sensor_index != NULL_INDEX
72}
73
74pub fn shape_test_point(world: &mut World, shape_id: ShapeId, point: Pos) -> bool {
76 let shape_index = get_shape_index(world, shape_id);
77 let shape = &world.shapes[shape_index as usize];
78
79 let transform = get_body_transform(world, shape.body_id);
80 let local_point = inv_transform_world_point(transform, point);
81
82 let result = match &shape.geometry {
83 ShapeGeometry::Capsule(capsule) => point_in_capsule(capsule, local_point),
84 ShapeGeometry::Circle(circle) => point_in_circle(circle, local_point),
85 ShapeGeometry::Polygon(polygon) => point_in_polygon(polygon, local_point),
86 _ => false,
87 };
88
89 crate::recording::record_query_result(
90 world,
91 crate::recording::OP_SHAPE_TEST_POINT,
92 |buf| {
93 crate::recording::rec_w_shapeid(buf, shape_id);
94 crate::recording::rec_w_position(buf, point);
95 },
96 |buf| crate::recording::rec_w_bool(buf, result),
97 );
98
99 result
100}
101
102pub fn shape_ray_cast(
104 world: &mut World,
105 shape_id: ShapeId,
106 origin: Pos,
107 translation: Vec2,
108) -> WorldCastOutput {
109 debug_assert!(is_valid_position(origin));
110 debug_assert!(is_valid_vec2(translation));
111
112 let shape_index = get_shape_index(world, shape_id);
113 let shape = &world.shapes[shape_index as usize];
114
115 let transform = to_relative_transform(get_body_transform(world, shape.body_id), origin);
117
118 let input = RayCastInput {
120 origin: VEC2_ZERO,
121 translation,
122 max_fraction: 1.0,
123 };
124
125 let local = ray_cast_shape(&input, shape, transform);
127 let output = WorldCastOutput {
128 normal: local.normal,
129 point: offset_pos(origin, local.point),
130 fraction: local.fraction,
131 iterations: local.iterations,
132 hit: local.hit,
133 };
134
135 crate::recording::record_query_result(
136 world,
137 crate::recording::OP_SHAPE_RAY_CAST,
138 |buf| {
139 crate::recording::rec_w_shapeid(buf, shape_id);
140 crate::recording::rec_w_position(buf, origin);
141 crate::recording::rec_w_vec2(buf, translation);
142 },
143 |buf| crate::recording::rec_w_worldcastoutput(buf, output),
144 );
145
146 output
147}
148
149pub fn shape_set_density(
151 world: &mut World,
152 shape_id: ShapeId,
153 density: f32,
154 update_body_mass: bool,
155) {
156 crate::recording::record_op(world, |rec, _| {
157 crate::recording::write_shape_set_density(rec, shape_id, density, update_body_mass)
158 });
159 debug_assert!(is_valid_float(density) && density >= 0.0);
160
161 debug_assert!(!world.locked);
162 if world.locked {
163 return;
164 }
165
166 let shape_index = get_shape_index(world, shape_id);
167 let shape = &mut world.shapes[shape_index as usize];
168 if density == shape.density {
169 return;
171 }
172
173 shape.density = density;
174
175 if update_body_mass {
176 let body_id = shape.body_id;
177 update_body_mass_data(world, body_id);
178 }
179}
180
181pub fn shape_get_density(world: &World, shape_id: ShapeId) -> f32 {
183 let shape_index = get_shape_index(world, shape_id);
184 world.shapes[shape_index as usize].density
185}
186
187pub fn shape_set_friction(world: &mut World, shape_id: ShapeId, friction: f32) {
189 crate::recording::record_op(world, |rec, _| {
190 crate::recording::write_shape_f32(
191 rec,
192 crate::recording::OP_SHAPE_SET_FRICTION,
193 shape_id,
194 friction,
195 )
196 });
197 debug_assert!(is_valid_float(friction) && friction >= 0.0);
198
199 debug_assert!(!world.locked);
200 if world.locked {
201 return;
202 }
203
204 let shape_index = get_shape_index(world, shape_id);
205 world.shapes[shape_index as usize].material.friction = friction;
206}
207
208pub fn shape_get_friction(world: &World, shape_id: ShapeId) -> f32 {
210 let shape_index = get_shape_index(world, shape_id);
211 world.shapes[shape_index as usize].material.friction
212}
213
214pub fn shape_set_restitution(world: &mut World, shape_id: ShapeId, restitution: f32) {
216 crate::recording::record_op(world, |rec, _| {
217 crate::recording::write_shape_f32(
218 rec,
219 crate::recording::OP_SHAPE_SET_RESTITUTION,
220 shape_id,
221 restitution,
222 )
223 });
224 debug_assert!(is_valid_float(restitution) && restitution >= 0.0);
225
226 debug_assert!(!world.locked);
227 if world.locked {
228 return;
229 }
230
231 let shape_index = get_shape_index(world, shape_id);
232 world.shapes[shape_index as usize].material.restitution = restitution;
233}
234
235pub fn shape_get_restitution(world: &World, shape_id: ShapeId) -> f32 {
237 let shape_index = get_shape_index(world, shape_id);
238 world.shapes[shape_index as usize].material.restitution
239}
240
241pub fn shape_set_user_material(world: &mut World, shape_id: ShapeId, material: u64) {
243 crate::recording::record_op(world, |rec, _| {
244 crate::recording::write_shape_set_user_material(rec, shape_id, material)
245 });
246 debug_assert!(!world.locked);
247 if world.locked {
248 return;
249 }
250
251 let shape_index = get_shape_index(world, shape_id);
252 world.shapes[shape_index as usize].material.user_material_id = material;
253}
254
255pub fn shape_get_user_material(world: &World, shape_id: ShapeId) -> u64 {
257 let shape_index = get_shape_index(world, shape_id);
258 world.shapes[shape_index as usize].material.user_material_id
259}
260
261pub fn shape_get_surface_material(world: &World, shape_id: ShapeId) -> SurfaceMaterial {
263 let shape_index = get_shape_index(world, shape_id);
264 world.shapes[shape_index as usize].material
265}
266
267pub fn shape_set_surface_material(
269 world: &mut World,
270 shape_id: ShapeId,
271 surface_material: SurfaceMaterial,
272) {
273 crate::recording::record_op(world, |rec, _| {
274 crate::recording::write_shape_set_surface_material(rec, shape_id, surface_material)
275 });
276 let shape_index = get_shape_index(world, shape_id);
277 world.shapes[shape_index as usize].material = surface_material;
278}
279
280pub fn shape_get_filter(world: &World, shape_id: ShapeId) -> Filter {
282 let shape_index = get_shape_index(world, shape_id);
283 world.shapes[shape_index as usize].filter
284}
285
286pub(crate) fn reset_proxy(
289 world: &mut World,
290 shape_index: i32,
291 wake_bodies: bool,
292 destroy_proxy: bool,
293) {
294 let body_id = world.shapes[shape_index as usize].body_id;
295
296 let mut contact_key = world.bodies[body_id as usize].head_contact_key;
298 while contact_key != NULL_INDEX {
299 let contact_id = contact_key >> 1;
300 let edge_index = contact_key & 1;
301
302 contact_key = world.contacts[contact_id as usize].edges[edge_index as usize].next_key;
303
304 let (contact_shape_a, contact_shape_b) = {
305 let contact = &world.contacts[contact_id as usize];
306 (contact.shape_id_a, contact.shape_id_b)
307 };
308 if contact_shape_a == shape_index || contact_shape_b == shape_index {
309 crate::contact::destroy_contact(world, contact_id, wake_bodies);
310 }
311 }
312
313 let transform = get_body_transform_quick(world, &world.bodies[body_id as usize]);
314 let proxy_key = world.shapes[shape_index as usize].proxy_key;
315 if proxy_key != NULL_INDEX {
316 let proxy_type = crate::broad_phase::proxy_type(proxy_key);
317 update_shape_aabbs(
318 &mut world.shapes[shape_index as usize],
319 transform,
320 proxy_type,
321 );
322
323 if destroy_proxy {
324 world.broad_phase.destroy_proxy(proxy_key);
325
326 let (fat_aabb, category_bits) = {
327 let shape = &world.shapes[shape_index as usize];
328 (shape.fat_aabb, shape.filter.category_bits)
329 };
330 let force_pair_creation = true;
331 world.shapes[shape_index as usize].proxy_key = world.broad_phase.create_proxy(
332 proxy_type,
333 fat_aabb,
334 category_bits,
335 shape_index,
336 force_pair_creation,
337 );
338 } else {
339 let fat_aabb = world.shapes[shape_index as usize].fat_aabb;
340 world.broad_phase.move_proxy(proxy_key, fat_aabb);
341 }
342 } else {
343 let proxy_type = world.bodies[body_id as usize].type_;
344 update_shape_aabbs(
345 &mut world.shapes[shape_index as usize],
346 transform,
347 proxy_type,
348 );
349 }
350
351 world.validate_solver_sets();
352}
353
354pub fn shape_set_filter(world: &mut World, shape_id: ShapeId, filter: Filter) {
358 crate::recording::record_op(world, |rec, _| {
359 crate::recording::write_shape_set_filter(rec, shape_id, filter)
360 });
361 debug_assert!(!world.locked);
362 if world.locked {
363 return;
364 }
365
366 let shape_index = get_shape_index(world, shape_id);
367 {
368 let shape = &world.shapes[shape_index as usize];
369 if filter.mask_bits == shape.filter.mask_bits
370 && filter.category_bits == shape.filter.category_bits
371 && filter.group_index == shape.filter.group_index
372 {
373 return;
374 }
375 }
376
377 let destroy_proxy =
380 filter.category_bits != world.shapes[shape_index as usize].filter.category_bits;
381
382 world.shapes[shape_index as usize].filter = filter;
383
384 let wake_bodies = true;
386 reset_proxy(world, shape_index, wake_bodies, destroy_proxy);
387
388 }
391
392pub fn shape_enable_sensor_events(world: &mut World, shape_id: ShapeId, flag: bool) {
394 crate::recording::record_op(world, |rec, _| {
395 crate::recording::write_shape_bool(
396 rec,
397 crate::recording::OP_SHAPE_ENABLE_SENSOR_EVENTS,
398 shape_id,
399 flag,
400 )
401 });
402 debug_assert!(!world.locked);
403 if world.locked {
404 return;
405 }
406
407 let shape_index = get_shape_index(world, shape_id);
408 world.shapes[shape_index as usize].enable_sensor_events = flag;
409}
410
411pub fn shape_are_sensor_events_enabled(world: &World, shape_id: ShapeId) -> bool {
413 let shape_index = get_shape_index(world, shape_id);
414 world.shapes[shape_index as usize].enable_sensor_events
415}
416
417pub fn shape_enable_contact_events(world: &mut World, shape_id: ShapeId, flag: bool) {
419 crate::recording::record_op(world, |rec, _| {
420 crate::recording::write_shape_bool(
421 rec,
422 crate::recording::OP_SHAPE_ENABLE_CONTACT_EVENTS,
423 shape_id,
424 flag,
425 )
426 });
427 debug_assert!(!world.locked);
428 if world.locked {
429 return;
430 }
431
432 let shape_index = get_shape_index(world, shape_id);
433 world.shapes[shape_index as usize].enable_contact_events = flag;
434}
435
436pub fn shape_are_contact_events_enabled(world: &World, shape_id: ShapeId) -> bool {
439 let shape_index = get_shape_index(world, shape_id);
440 world.shapes[shape_index as usize].enable_contact_events
441}
442
443pub fn shape_enable_pre_solve_events(world: &mut World, shape_id: ShapeId, flag: bool) {
447 crate::recording::record_op(world, |rec, _| {
448 crate::recording::write_shape_bool(
449 rec,
450 crate::recording::OP_SHAPE_ENABLE_PRE_SOLVE_EVENTS,
451 shape_id,
452 flag,
453 )
454 });
455 debug_assert!(!world.locked);
456 if world.locked {
457 return;
458 }
459
460 let shape_index = get_shape_index(world, shape_id);
461 world.shapes[shape_index as usize].enable_pre_solve_events = flag;
462}
463
464pub fn shape_are_pre_solve_events_enabled(world: &World, shape_id: ShapeId) -> bool {
467 let shape_index = get_shape_index(world, shape_id);
468 world.shapes[shape_index as usize].enable_pre_solve_events
469}
470
471pub fn shape_enable_hit_events(world: &mut World, shape_id: ShapeId, flag: bool) {
473 crate::recording::record_op(world, |rec, _| {
474 crate::recording::write_shape_bool(
475 rec,
476 crate::recording::OP_SHAPE_ENABLE_HIT_EVENTS,
477 shape_id,
478 flag,
479 )
480 });
481 debug_assert!(!world.locked);
482 if world.locked {
483 return;
484 }
485
486 let shape_index = get_shape_index(world, shape_id);
487 world.shapes[shape_index as usize].enable_hit_events = flag;
488}
489
490pub fn shape_are_hit_events_enabled(world: &World, shape_id: ShapeId) -> bool {
492 let shape_index = get_shape_index(world, shape_id);
493 world.shapes[shape_index as usize].enable_hit_events
494}
495
496pub fn shape_get_type(world: &World, shape_id: ShapeId) -> ShapeType {
498 let shape_index = get_shape_index(world, shape_id);
499 world.shapes[shape_index as usize].shape_type()
500}
501
502pub fn shape_get_contact_capacity(world: &World, shape_id: ShapeId) -> i32 {
505 debug_assert!(!world.locked);
506 if world.locked {
507 return 0;
508 }
509
510 let shape_index = get_shape_index(world, shape_id);
511 let shape = &world.shapes[shape_index as usize];
512 if shape.sensor_index != NULL_INDEX {
513 return 0;
514 }
515
516 world.bodies[shape.body_id as usize].contact_count
518}
519
520pub fn shape_get_contact_data(
523 world: &World,
524 shape_id: ShapeId,
525 capacity: usize,
526) -> Vec<ContactData> {
527 debug_assert!(!world.locked);
528 if world.locked {
529 return Vec::new();
530 }
531
532 let shape_index = get_shape_index(world, shape_id);
533 let shape = &world.shapes[shape_index as usize];
534 if shape.sensor_index != NULL_INDEX {
535 return Vec::new();
536 }
537
538 let mut out = Vec::new();
539 let mut contact_key = world.bodies[shape.body_id as usize].head_contact_key;
540 while contact_key != NULL_INDEX && out.len() < capacity {
541 let contact_id = contact_key >> 1;
542 let edge_index = contact_key & 1;
543
544 let contact = &world.contacts[contact_id as usize];
545 contact_key = contact.edges[edge_index as usize].next_key;
546
547 if (contact.shape_id_a == shape_index || contact.shape_id_b == shape_index)
549 && (contact.flags & crate::contact::contact_flags::TOUCHING) != 0
550 {
551 let shape_a = &world.shapes[contact.shape_id_a as usize];
552 let shape_b = &world.shapes[contact.shape_id_b as usize];
553
554 let contact_sim = if contact.set_index == AWAKE_SET && contact.color_index != NULL_INDEX
555 {
556 &world.constraint_graph.colors[contact.color_index as usize].contact_sims
557 [contact.local_index as usize]
558 } else {
559 &world.solver_sets[contact.set_index as usize].contact_sims
560 [contact.local_index as usize]
561 };
562
563 out.push(ContactData {
564 contact_id: ContactId {
565 index1: contact_id + 1,
566 world0: world.world_id,
567 padding: 0,
568 generation: contact.generation,
569 },
570 shape_id_a: ShapeId {
571 index1: shape_a.id + 1,
572 world0: world.world_id,
573 generation: shape_a.generation,
574 },
575 shape_id_b: ShapeId {
576 index1: shape_b.id + 1,
577 world0: world.world_id,
578 generation: shape_b.generation,
579 },
580 manifold: contact_sim.manifold,
581 });
582 }
583 }
584
585 out
586}
587
588pub fn shape_get_sensor_capacity(world: &World, shape_id: ShapeId) -> i32 {
592 debug_assert!(!world.locked);
593 if world.locked {
594 return 0;
595 }
596
597 let shape_index = get_shape_index(world, shape_id);
598 let shape = &world.shapes[shape_index as usize];
599 if shape.sensor_index == NULL_INDEX {
600 return 0;
601 }
602
603 world.sensors[shape.sensor_index as usize].overlaps2.len() as i32
604}
605
606pub fn shape_get_sensor_data(world: &World, shape_id: ShapeId, capacity: usize) -> Vec<ShapeId> {
610 debug_assert!(!world.locked);
611 if world.locked {
612 return Vec::new();
613 }
614
615 let shape_index = get_shape_index(world, shape_id);
616 let shape = &world.shapes[shape_index as usize];
617 if shape.sensor_index == NULL_INDEX {
618 return Vec::new();
619 }
620
621 let sensor = &world.sensors[shape.sensor_index as usize];
622 let count = sensor.overlaps2.len().min(capacity);
623 sensor.overlaps2[..count]
624 .iter()
625 .map(|visitor| ShapeId {
626 index1: visitor.shape_id + 1,
627 world0: world.world_id,
628 generation: visitor.generation,
629 })
630 .collect()
631}
632
633pub fn shape_get_aabb(world: &World, shape_id: ShapeId) -> Aabb {
636 let shape_index = get_shape_index(world, shape_id);
637 world.shapes[shape_index as usize].aabb
638}
639
640pub fn shape_compute_mass_data(world: &World, shape_id: ShapeId) -> MassData {
642 let shape_index = get_shape_index(world, shape_id);
643 compute_shape_mass(&world.shapes[shape_index as usize])
644}
645
646pub fn shape_get_closest_point(world: &World, shape_id: ShapeId, target: Pos) -> Pos {
649 let shape_index = get_shape_index(world, shape_id);
650 let shape = &world.shapes[shape_index as usize];
651 let body = &world.bodies[shape.body_id as usize];
652 let transform = get_body_transform_quick(world, body);
653
654 let zero = VEC2_ZERO;
657 let target_transform = crate::math_functions::WorldTransform {
658 p: target,
659 q: ROT_IDENTITY,
660 };
661
662 let input = DistanceInput {
663 proxy_a: make_shape_distance_proxy(shape),
664 proxy_b: make_proxy(&[zero], 0.0),
665 transform: inv_mul_world_transforms(transform, target_transform),
666 use_radii: true,
667 };
668
669 let mut cache = SimplexCache::default();
670 let output = shape_distance(&input, &mut cache, None);
671
672 transform_world_point(transform, output.point_a)
673}