1use super::World;
10use crate::body::get_body_transform_quick;
11use crate::distance::{make_proxy, ShapeProxy};
12use crate::dynamic_tree::{BoxCastInput, TreeStats};
13use crate::geometry::{Capsule, PlaneResult, RayCastInput, ShapeCastInput};
14use crate::id::ShapeId;
15use crate::math_functions::{
16 add, clamp_int, is_valid_aabb, is_valid_position, is_valid_vec3, make_aabb, max, min,
17 offset_aabb, offset_pos, sub, to_relative_transform, to_vec3, Aabb, Pos, Vec3, VEC3_ZERO,
18};
19use crate::shape::{
20 collide_mover, overlap_shape, ray_cast_shape, shape_cast_shape, should_query_collide, Shape,
21};
22use crate::types::{QueryFilter, RayResult, BODY_TYPE_COUNT};
23
24fn query_shape_id(world: &World, shape: &Shape) -> ShapeId {
26 ShapeId {
27 index1: shape.id + 1,
28 world0: world.world_id,
29 generation: shape.generation,
30 }
31}
32
33pub fn world_overlap_aabb(
37 world: &World,
38 aabb: Aabb,
39 filter: &QueryFilter,
40 mut fcn: impl FnMut(ShapeId) -> bool,
41) -> TreeStats {
42 let mut tree_stats = TreeStats::default();
43
44 debug_assert!(!world.locked);
45 if world.locked {
46 return tree_stats;
47 }
48
49 debug_assert!(is_valid_aabb(aabb));
50
51 let mut rec = crate::recording::query_capture::maybe_begin(world, filter);
52 if let Some(w) = rec.as_mut() {
53 w.write_overlap_aabb_header(
54 crate::recording::query_capture::world_id(world),
55 aabb,
56 filter,
57 );
58 }
59
60 for i in 0..BODY_TYPE_COUNT {
61 let tree_result =
62 world.broad_phase.trees[i].query(aabb, filter.mask_bits, false, |_, user_data| {
63 let shape_id = user_data as i32;
64 let shape = &world.shapes[shape_id as usize];
65
66 if !should_query_collide(&shape.filter, filter) {
67 return true;
68 }
69
70 let id = ShapeId {
71 index1: shape_id + 1,
72 world0: world.world_id,
73 generation: shape.generation,
74 };
75 let ret = fcn(id);
76 if let Some(w) = rec.as_mut() {
77 w.append_overlap_hit(id, ret);
78 }
79 ret
80 });
81
82 tree_stats.node_visits += tree_result.node_visits;
83 tree_stats.leaf_visits += tree_result.leaf_visits;
84 }
85
86 if let Some(mut w) = rec {
87 w.finish_counted();
88 w.append_tree_stats(tree_stats);
89 crate::recording::query_capture::commit(
90 world,
91 w,
92 crate::recording::ops::RecOp::QueryOverlapAABB,
93 );
94 }
95
96 tree_stats
97}
98
99pub fn world_overlap_shape(
102 world: &World,
103 origin: Pos,
104 proxy: &ShapeProxy,
105 filter: &QueryFilter,
106 mut fcn: impl FnMut(ShapeId) -> bool,
107) -> TreeStats {
108 let mut tree_stats = TreeStats::default();
109
110 debug_assert!(!world.locked);
111 if world.locked {
112 return tree_stats;
113 }
114
115 debug_assert!(is_valid_position(origin));
116
117 let mut rec = crate::recording::query_capture::maybe_begin(world, filter);
118 if let Some(w) = rec.as_mut() {
119 w.write_overlap_shape_header(
120 crate::recording::query_capture::world_id(world),
121 origin,
122 proxy,
123 filter,
124 );
125 }
126
127 let aabb = offset_aabb(
129 make_aabb(&proxy.points[..proxy.count as usize], proxy.radius),
130 origin,
131 );
132
133 for i in 0..BODY_TYPE_COUNT {
134 let tree_result =
135 world.broad_phase.trees[i].query(aabb, filter.mask_bits, false, |_, user_data| {
136 let shape_id = user_data as i32;
137 let shape = &world.shapes[shape_id as usize];
138
139 if !should_query_collide(&shape.filter, filter) {
140 return true;
141 }
142
143 let body = &world.bodies[shape.body_id as usize];
145 let transform =
146 to_relative_transform(get_body_transform_quick(world, body), origin);
147
148 if !overlap_shape(shape, transform, proxy) {
149 return true;
150 }
151
152 let id = query_shape_id(world, shape);
153 let ret = fcn(id);
154 if let Some(w) = rec.as_mut() {
155 w.append_overlap_hit(id, ret);
156 }
157 ret
158 });
159
160 tree_stats.node_visits += tree_result.node_visits;
161 tree_stats.leaf_visits += tree_result.leaf_visits;
162 }
163
164 if let Some(mut w) = rec {
165 w.finish_counted();
166 w.append_tree_stats(tree_stats);
167 crate::recording::query_capture::commit(
168 world,
169 w,
170 crate::recording::ops::RecOp::QueryOverlapShape,
171 );
172 }
173
174 tree_stats
175}
176
177pub fn world_cast_ray(
183 world: &World,
184 origin: Pos,
185 translation: Vec3,
186 filter: &QueryFilter,
187 mut fcn: impl FnMut(ShapeId, Pos, Vec3, f32, u64, i32, i32) -> f32,
188) -> TreeStats {
189 let mut rec = crate::recording::query_capture::maybe_begin(world, filter);
190 if let Some(w) = rec.as_mut() {
191 w.write_cast_ray_header(
192 crate::recording::query_capture::world_id(world),
193 origin,
194 translation,
195 filter,
196 );
197 }
198
199 let tree_stats = cast_ray_impl(
200 world,
201 origin,
202 translation,
203 filter,
204 |id, point, normal, fraction, mid, tri, child| {
205 let user_fraction = fcn(id, point, normal, fraction, mid, tri, child);
206 if let Some(w) = rec.as_mut() {
207 w.append_cast_hit(id, point, normal, fraction, mid, tri, child, user_fraction);
208 }
209 user_fraction
210 },
211 );
212
213 if let Some(mut w) = rec {
214 w.finish_counted();
215 w.append_tree_stats(tree_stats);
216 crate::recording::query_capture::commit(
217 world,
218 w,
219 crate::recording::ops::RecOp::QueryCastRay,
220 );
221 }
222
223 tree_stats
224}
225
226fn cast_ray_impl(
227 world: &World,
228 origin: Pos,
229 translation: Vec3,
230 filter: &QueryFilter,
231 mut fcn: impl FnMut(ShapeId, Pos, Vec3, f32, u64, i32, i32) -> f32,
232) -> TreeStats {
233 let mut tree_stats = TreeStats::default();
234
235 debug_assert!(!world.locked);
236 if world.locked {
237 return tree_stats;
238 }
239
240 debug_assert!(is_valid_position(origin));
241 debug_assert!(is_valid_vec3(translation));
242
243 let mut input = RayCastInput {
244 origin: to_vec3(origin),
245 translation,
246 max_fraction: 1.0,
247 };
248
249 let mut fraction = 1.0f32;
250
251 for i in 0..BODY_TYPE_COUNT {
252 let tree_result = world.broad_phase.trees[i].ray_cast(
253 &input,
254 filter.mask_bits,
255 false,
256 |ray_input, _, user_data| {
257 let shape_id = user_data as i32;
258 let shape = &world.shapes[shape_id as usize];
259
260 if !should_query_collide(&shape.filter, filter) {
261 return ray_input.max_fraction;
262 }
263
264 let body = &world.bodies[shape.body_id as usize];
265 let body_transform = get_body_transform_quick(world, body);
266 let transform = to_relative_transform(body_transform, origin);
267
268 let local_input = RayCastInput {
269 origin: VEC3_ZERO,
270 translation: ray_input.translation,
271 max_fraction: ray_input.max_fraction,
272 };
273 let output = ray_cast_shape(shape, transform, &local_input);
274
275 if output.hit {
276 debug_assert!(output.fraction <= ray_input.max_fraction);
277
278 let id = ShapeId {
279 index1: shape_id + 1,
280 world0: world.world_id,
281 generation: shape.generation,
282 };
283 let point = offset_pos(origin, output.point);
284 let material_index =
285 clamp_int(output.material_index, 0, shape.material_count() - 1);
286 let user_material_id =
287 shape.shape_materials()[material_index as usize].user_material_id;
288
289 let user_fraction = fcn(
290 id,
291 point,
292 output.normal,
293 output.fraction,
294 user_material_id,
295 output.triangle_index,
296 output.child_index,
297 );
298
299 if (0.0..=1.0).contains(&user_fraction) {
301 fraction = user_fraction;
302 }
303
304 return user_fraction;
305 }
306
307 ray_input.max_fraction
308 },
309 );
310
311 tree_stats.node_visits += tree_result.node_visits;
312 tree_stats.leaf_visits += tree_result.leaf_visits;
313
314 if fraction == 0.0 {
315 break;
316 }
317
318 input.max_fraction = fraction;
319 }
320
321 tree_stats
322}
323
324pub fn world_cast_ray_closest(
327 world: &World,
328 origin: Pos,
329 translation: Vec3,
330 filter: &QueryFilter,
331) -> RayResult {
332 let mut result = RayResult::default();
333
334 debug_assert!(!world.locked);
335 if world.locked {
336 return result;
337 }
338
339 debug_assert!(is_valid_position(origin));
340 debug_assert!(is_valid_vec3(translation));
341
342 let stats = cast_ray_impl(
344 world,
345 origin,
346 translation,
347 filter,
348 |id, point, normal, fraction, user_material_id, triangle_index, child_index| {
349 if fraction == 0.0 {
351 return -1.0;
352 }
353
354 result.shape_id = id;
355 result.point = point;
356 result.normal = normal;
357 result.fraction = fraction;
358 result.user_material_id = user_material_id;
359 result.triangle_index = triangle_index;
360 result.child_index = child_index;
361 result.hit = true;
362 fraction
363 },
364 );
365
366 result.node_visits = stats.node_visits;
367 result.leaf_visits = stats.leaf_visits;
368
369 if let Some(mut w) = crate::recording::query_capture::maybe_begin(world, filter) {
370 w.write_cast_ray_closest_header(
371 crate::recording::query_capture::world_id(world),
372 origin,
373 translation,
374 filter,
375 );
376 w.append_ray_result(&result);
377 crate::recording::query_capture::commit(
378 world,
379 w,
380 crate::recording::ops::RecOp::QueryCastRayClosest,
381 );
382 }
383
384 result
385}
386
387pub fn world_cast_shape(
390 world: &World,
391 origin: Pos,
392 proxy: &ShapeProxy,
393 translation: Vec3,
394 filter: &QueryFilter,
395 mut fcn: impl FnMut(ShapeId, Pos, Vec3, f32, u64, i32, i32) -> f32,
396) -> TreeStats {
397 let mut tree_stats = TreeStats::default();
398
399 debug_assert!(!world.locked);
400 if world.locked {
401 return tree_stats;
402 }
403
404 debug_assert!(is_valid_position(origin));
405 debug_assert!(is_valid_vec3(translation));
406
407 let mut rec = crate::recording::query_capture::maybe_begin(world, filter);
408 if let Some(w) = rec.as_mut() {
409 w.write_cast_shape_header(
410 crate::recording::query_capture::world_id(world),
411 origin,
412 proxy,
413 translation,
414 filter,
415 );
416 }
417
418 let cast_input = ShapeCastInput {
419 proxy: *proxy,
420 translation,
421 max_fraction: 1.0,
422 can_encroach: false,
423 };
424
425 let mut fraction = 1.0f32;
426
427 let local_box = make_aabb(&proxy.points[..proxy.count as usize], proxy.radius);
429 let mut tree_input = BoxCastInput {
430 box_: offset_aabb(local_box, origin),
431 translation,
432 max_fraction: 1.0,
433 };
434
435 for i in 0..BODY_TYPE_COUNT {
436 let tree_result = world.broad_phase.trees[i].box_cast(
437 &tree_input,
438 filter.mask_bits,
439 false,
440 |box_input, _, user_data| {
441 let shape_id = user_data as i32;
442 let shape = &world.shapes[shape_id as usize];
443
444 if !should_query_collide(&shape.filter, filter) {
445 return box_input.max_fraction;
446 }
447
448 let mut local_input = cast_input;
451 local_input.max_fraction = box_input.max_fraction;
452
453 let body = &world.bodies[shape.body_id as usize];
454 let transform =
455 to_relative_transform(get_body_transform_quick(world, body), origin);
456
457 let output = shape_cast_shape(shape, transform, &local_input);
458
459 if output.hit {
460 let id = ShapeId {
461 index1: shape_id + 1,
462 world0: world.world_id,
463 generation: shape.generation,
464 };
465 let material_index =
466 clamp_int(output.material_index, 0, shape.material_count() - 1);
467 let user_material_id =
468 shape.shape_materials()[material_index as usize].user_material_id;
469
470 let point = offset_pos(origin, output.point);
471 let user_fraction = fcn(
472 id,
473 point,
474 output.normal,
475 output.fraction,
476 user_material_id,
477 output.triangle_index,
478 output.child_index,
479 );
480
481 if let Some(w) = rec.as_mut() {
482 w.append_cast_hit(
483 id,
484 point,
485 output.normal,
486 output.fraction,
487 user_material_id,
488 output.triangle_index,
489 output.child_index,
490 user_fraction,
491 );
492 }
493
494 if (0.0..=1.0).contains(&user_fraction) {
495 fraction = user_fraction;
496 }
497
498 return user_fraction;
499 }
500
501 box_input.max_fraction
502 },
503 );
504
505 tree_stats.node_visits += tree_result.node_visits;
506 tree_stats.leaf_visits += tree_result.leaf_visits;
507
508 if fraction == 0.0 {
509 break;
510 }
511
512 tree_input.max_fraction = fraction;
513 }
514
515 if let Some(mut w) = rec {
516 w.finish_counted();
517 w.append_tree_stats(tree_stats);
518 crate::recording::query_capture::commit(
519 world,
520 w,
521 crate::recording::ops::RecOp::QueryCastShape,
522 );
523 }
524
525 tree_stats
526}
527
528pub fn world_collide_mover(
532 world: &World,
533 origin: Pos,
534 mover: &Capsule,
535 filter: &QueryFilter,
536 mut fcn: impl FnMut(ShapeId, &[PlaneResult]) -> bool,
537) {
538 debug_assert!(!world.locked);
539 if world.locked {
540 return;
541 }
542
543 debug_assert!(is_valid_position(origin));
544
545 let mut rec = crate::recording::query_capture::maybe_begin(world, filter);
546 if let Some(w) = rec.as_mut() {
547 w.write_collide_mover_header(
548 crate::recording::query_capture::world_id(world),
549 origin,
550 *mover,
551 filter,
552 );
553 }
554
555 let r = Vec3 {
556 x: mover.radius,
557 y: mover.radius,
558 z: mover.radius,
559 };
560
561 let rel_box = Aabb {
563 lower_bound: sub(min(mover.center1, mover.center2), r),
564 upper_bound: add(max(mover.center1, mover.center2), r),
565 };
566 let aabb = offset_aabb(rel_box, origin);
567
568 for i in 0..BODY_TYPE_COUNT {
569 world.broad_phase.trees[i].query(aabb, filter.mask_bits, false, |_, user_data| {
570 let shape_id = user_data as i32;
571 let shape = &world.shapes[shape_id as usize];
572
573 if !should_query_collide(&shape.filter, filter) {
574 return true;
575 }
576
577 let body = &world.bodies[shape.body_id as usize];
579 let transform = to_relative_transform(get_body_transform_quick(world, body), origin);
580
581 let mut buffer = [PlaneResult::default(); 64];
582 let count = collide_mover(&mut buffer, shape, transform, mover);
583
584 if count > 0 {
585 let id = query_shape_id(world, shape);
586 let planes = &buffer[..count as usize];
587 let ret = fcn(id, planes);
588 if let Some(w) = rec.as_mut() {
589 w.append_plane_hit(id, planes, ret);
590 }
591 return ret;
592 }
593
594 true
595 });
596 }
597
598 if let Some(mut w) = rec {
599 w.finish_counted();
600 crate::recording::query_capture::commit(
601 world,
602 w,
603 crate::recording::ops::RecOp::QueryCollideMover,
604 );
605 }
606}
607
608pub fn world_cast_mover(
612 world: &World,
613 origin: Pos,
614 mover: &Capsule,
615 translation: Vec3,
616 filter: &QueryFilter,
617 mut filter_fcn: Option<&mut dyn FnMut(ShapeId) -> bool>,
618) -> f32 {
619 debug_assert!(is_valid_position(origin));
620 debug_assert!(is_valid_vec3(translation));
621
622 debug_assert!(!world.locked);
623 if world.locked {
624 return 1.0;
625 }
626
627 let mut rec = crate::recording::query_capture::maybe_begin(world, filter);
628 if let Some(w) = rec.as_mut() {
629 w.write_cast_mover_header(
630 crate::recording::query_capture::world_id(world),
631 origin,
632 *mover,
633 translation,
634 filter,
635 );
636 }
637
638 let cast_input = ShapeCastInput {
639 proxy: make_proxy(&[mover.center1, mover.center2], mover.radius),
640 translation,
641 max_fraction: 1.0,
642 can_encroach: mover.radius > 0.0,
643 };
644
645 let mut fraction = 1.0f32;
646
647 let centers = [mover.center1, mover.center2];
649 let mut tree_input = BoxCastInput {
650 box_: offset_aabb(make_aabb(¢ers, mover.radius), origin),
651 translation,
652 max_fraction: 1.0,
653 };
654
655 for i in 0..BODY_TYPE_COUNT {
656 world.broad_phase.trees[i].box_cast(
657 &tree_input,
658 filter.mask_bits,
659 false,
660 |box_input, _, user_data| {
661 let shape_id = user_data as i32;
662 let shape = &world.shapes[shape_id as usize];
663
664 if !should_query_collide(&shape.filter, filter) {
665 return fraction;
666 }
667
668 let id = ShapeId {
669 index1: shape_id + 1,
670 world0: world.world_id,
671 generation: shape.generation,
672 };
673
674 let should_collide = if let Some(ref mut fcn) = filter_fcn {
677 fcn(id)
678 } else {
679 true
680 };
681 if rec.is_some() || filter_fcn.is_some() {
682 if let Some(w) = rec.as_mut() {
683 w.append_overlap_hit(id, should_collide);
684 }
685 if !should_collide {
686 return fraction;
687 }
688 }
689
690 let mut local_input = cast_input;
692 local_input.max_fraction = box_input.max_fraction;
693
694 let body = &world.bodies[shape.body_id as usize];
696 let transform =
697 to_relative_transform(get_body_transform_quick(world, body), origin);
698
699 let output = shape_cast_shape(shape, transform, &local_input);
700 if output.fraction == 0.0 {
701 return fraction;
703 }
704
705 fraction = output.fraction;
706 output.fraction
707 },
708 );
709
710 if fraction == 0.0 {
711 break;
712 }
713
714 tree_input.max_fraction = fraction;
715 }
716
717 if let Some(mut w) = rec {
718 w.finish_counted();
719 w.append_f32(fraction);
720 crate::recording::query_capture::commit(
721 world,
722 w,
723 crate::recording::ops::RecOp::QueryCastMover,
724 );
725 }
726
727 fraction
728}