1use super::World;
19use crate::aabb::offset_aabb;
20use crate::body::get_body_transform_quick;
21use crate::collision::PlaneResult;
22use crate::collision::{Capsule, CastOutput, RayCastInput, ShapeCastInput};
23use crate::constants::linear_slop;
24use crate::distance::{shape_distance, DistanceInput, ShapeProxy, SimplexCache};
25use crate::dynamic_tree::{BoxCastInput, TreeStats};
26use crate::id::ShapeId;
27use crate::math_functions::{
28 is_normalized, is_valid_aabb, is_valid_position, is_valid_vec2, make_aabb, offset_pos, sub_pos,
29 to_relative_transform, to_vec2, Aabb, Pos, Vec2,
30};
31use crate::recording::{
32 rec_w_aabb, rec_w_bool, rec_w_capsule, rec_w_f32, rec_w_planeresult, rec_w_position,
33 rec_w_queryfilter, rec_w_rayresult, rec_w_shapeid, rec_w_shapeproxy, rec_w_vec2,
34 record_query_result, QueryRecorder, OP_QUERY_CAST_MOVER, OP_QUERY_CAST_RAY,
35 OP_QUERY_CAST_RAY_CLOSEST, OP_QUERY_CAST_SHAPE, OP_QUERY_COLLIDE_MOVER, OP_QUERY_OVERLAP_AABB,
36 OP_QUERY_OVERLAP_SHAPE,
37};
38use crate::shape::{
39 collide_mover, make_shape_distance_proxy, ray_cast_shape, shape_cast_shape,
40 should_query_collide, Shape,
41};
42use crate::types::{QueryFilter, BODY_TYPE_COUNT};
43
44fn query_shape_id(world: &World, shape: &Shape) -> ShapeId {
46 ShapeId {
47 index1: shape.id + 1,
48 world0: world.world_id,
49 generation: shape.generation,
50 }
51}
52
53pub fn world_overlap_aabb(
57 world: &mut World,
58 origin: Pos,
59 aabb: Aabb,
60 filter: QueryFilter,
61 mut fcn: impl FnMut(ShapeId) -> bool,
62) -> TreeStats {
63 let mut tree_stats = TreeStats::default();
64
65 debug_assert!(!world.locked);
66 if world.locked {
67 return tree_stats;
68 }
69
70 debug_assert!(is_valid_position(origin));
71 debug_assert!(is_valid_aabb(aabb));
72
73 let mut q = QueryRecorder::begin(world, |buf| {
74 rec_w_position(buf, origin);
75 rec_w_aabb(buf, aabb);
76 rec_w_queryfilter(buf, filter);
77 });
78
79 {
80 let world: &World = world;
81
82 let world_box = offset_aabb(aabb, origin);
85
86 for i in 0..BODY_TYPE_COUNT {
87 let tree_result =
88 world.broad_phase.trees[i].query(world_box, filter.mask_bits, |_, user_data| {
89 let shape_id = user_data as i32;
91 let shape = &world.shapes[shape_id as usize];
92
93 if !should_query_collide(shape.filter, filter) {
94 return true;
95 }
96
97 let id = ShapeId {
98 index1: shape_id + 1,
99 world0: world.world_id,
100 generation: shape.generation,
101 };
102 let ret = fcn(id);
103 if q.active() {
104 rec_w_shapeid(&mut q.buf, id);
106 rec_w_bool(&mut q.buf, ret);
107 q.hits += 1;
108 }
109 ret
110 });
111
112 tree_stats.node_visits += tree_result.node_visits;
113 tree_stats.leaf_visits += tree_result.leaf_visits;
114 }
115 }
116
117 q.commit(world, OP_QUERY_OVERLAP_AABB, Some(tree_stats));
118 tree_stats
119}
120
121pub fn world_overlap_shape(
125 world: &mut World,
126 origin: Pos,
127 proxy: &ShapeProxy,
128 filter: QueryFilter,
129 mut fcn: impl FnMut(ShapeId) -> bool,
130) -> TreeStats {
131 let mut tree_stats = TreeStats::default();
132
133 debug_assert!(!world.locked);
134 if world.locked {
135 return tree_stats;
136 }
137
138 debug_assert!(is_valid_position(origin));
139
140 let mut q = QueryRecorder::begin(world, |buf| {
141 rec_w_position(buf, origin);
142 rec_w_shapeproxy(buf, proxy);
143 rec_w_queryfilter(buf, filter);
144 });
145
146 {
147 let world: &World = world;
148
149 let aabb = offset_aabb(
152 make_aabb(&proxy.points[..proxy.count as usize], proxy.radius),
153 origin,
154 );
155
156 for i in 0..BODY_TYPE_COUNT {
157 let tree_result =
158 world.broad_phase.trees[i].query(aabb, filter.mask_bits, |_, user_data| {
159 let shape_id = user_data as i32;
161 let shape = &world.shapes[shape_id as usize];
162
163 if !should_query_collide(shape.filter, filter) {
164 return true;
165 }
166
167 let body = &world.bodies[shape.body_id as usize];
170 let transform =
171 to_relative_transform(get_body_transform_quick(world, body), origin);
172
173 let input = DistanceInput {
174 proxy_a: *proxy,
175 proxy_b: make_shape_distance_proxy(shape),
176 transform,
177 use_radii: true,
178 };
179
180 let mut cache = SimplexCache::default();
181 let output = shape_distance(&input, &mut cache, None);
182
183 let tolerance = 0.1 * linear_slop();
184 if output.distance > tolerance {
185 return true;
186 }
187
188 let id = query_shape_id(world, shape);
189 let ret = fcn(id);
190 if q.active() {
191 rec_w_shapeid(&mut q.buf, id);
192 rec_w_bool(&mut q.buf, ret);
193 q.hits += 1;
194 }
195 ret
196 });
197
198 tree_stats.node_visits += tree_result.node_visits;
199 tree_stats.leaf_visits += tree_result.leaf_visits;
200 }
201 }
202
203 q.commit(world, OP_QUERY_OVERLAP_SHAPE, Some(tree_stats));
204 tree_stats
205}
206
207fn cast_ray_impl(
211 world: &World,
212 origin: Pos,
213 translation: Vec2,
214 filter: QueryFilter,
215 mut fcn: impl FnMut(ShapeId, Pos, Vec2, f32) -> f32,
216) -> TreeStats {
217 let mut tree_stats = TreeStats::default();
218
219 let mut input = RayCastInput {
223 origin: to_vec2(origin),
224 translation,
225 max_fraction: 1.0,
226 };
227
228 let mut fraction = 1.0f32;
229
230 for i in 0..BODY_TYPE_COUNT {
231 let tree_result = world.broad_phase.trees[i].ray_cast(
232 &input,
233 filter.mask_bits,
234 |tree_input, _, user_data| {
235 let shape_id = user_data as i32;
237 let shape = &world.shapes[shape_id as usize];
238
239 if !should_query_collide(shape.filter, filter) {
240 return tree_input.max_fraction;
241 }
242
243 let body = &world.bodies[shape.body_id as usize];
244 let xf = get_body_transform_quick(world, body);
245
246 let base = xf.p;
251 let transform = to_relative_transform(xf, base);
252 let mut local_input = *tree_input;
253 local_input.origin = sub_pos(origin, base);
254 let output: CastOutput = ray_cast_shape(&local_input, shape, transform);
255
256 if output.hit {
257 let id = query_shape_id(world, shape);
258 let point = offset_pos(base, output.point);
259 let user_fraction = fcn(id, point, output.normal, output.fraction);
260
261 if (0.0..=1.0).contains(&user_fraction) {
263 fraction = user_fraction;
264 }
265
266 return user_fraction;
267 }
268
269 tree_input.max_fraction
270 },
271 );
272 tree_stats.node_visits += tree_result.node_visits;
273 tree_stats.leaf_visits += tree_result.leaf_visits;
274
275 if fraction == 0.0 {
276 break;
277 }
278
279 input.max_fraction = fraction;
280 }
281
282 tree_stats
283}
284
285pub fn world_cast_ray(
291 world: &mut World,
292 origin: Pos,
293 translation: Vec2,
294 filter: QueryFilter,
295 mut fcn: impl FnMut(ShapeId, Pos, Vec2, f32) -> f32,
296) -> TreeStats {
297 debug_assert!(!world.locked);
298 if world.locked {
299 return TreeStats::default();
300 }
301
302 debug_assert!(is_valid_position(origin));
303 debug_assert!(is_valid_vec2(translation));
304
305 let mut q = QueryRecorder::begin(world, |buf| {
306 rec_w_position(buf, origin);
307 rec_w_vec2(buf, translation);
308 rec_w_queryfilter(buf, filter);
309 });
310
311 let tree_stats = cast_ray_impl(
312 world,
313 origin,
314 translation,
315 filter,
316 |id, point, normal, fraction| {
317 let ret = fcn(id, point, normal, fraction);
318 if q.active() {
319 rec_w_shapeid(&mut q.buf, id);
321 rec_w_position(&mut q.buf, point);
322 rec_w_vec2(&mut q.buf, normal);
323 rec_w_f32(&mut q.buf, fraction);
324 rec_w_f32(&mut q.buf, ret);
325 q.hits += 1;
326 }
327 ret
328 },
329 );
330
331 q.commit(world, OP_QUERY_CAST_RAY, Some(tree_stats));
332 tree_stats
333}
334
335pub fn world_cast_ray_closest(
339 world: &mut World,
340 origin: Pos,
341 translation: Vec2,
342 filter: QueryFilter,
343) -> crate::types::RayResult {
344 let mut result = crate::types::RayResult::default();
345
346 debug_assert!(!world.locked);
347 if world.locked {
348 return result;
349 }
350
351 debug_assert!(is_valid_position(origin));
352 debug_assert!(is_valid_vec2(translation));
353
354 let stats = cast_ray_impl(
357 world,
358 origin,
359 translation,
360 filter,
361 |id, point, normal, fraction| {
362 if fraction == 0.0 {
364 return -1.0;
365 }
366
367 result.shape_id = id;
368 result.point = point;
369 result.normal = normal;
370 result.fraction = fraction;
371 result.hit = true;
372 fraction
373 },
374 );
375
376 result.node_visits = stats.node_visits;
377 result.leaf_visits = stats.leaf_visits;
378
379 record_query_result(
380 world,
381 OP_QUERY_CAST_RAY_CLOSEST,
382 |buf| {
383 rec_w_position(buf, origin);
384 rec_w_vec2(buf, translation);
385 rec_w_queryfilter(buf, filter);
386 },
387 |buf| rec_w_rayresult(buf, &result),
388 );
389
390 result
391}
392
393pub fn world_cast_shape(
397 world: &mut World,
398 origin: Pos,
399 proxy: &ShapeProxy,
400 translation: Vec2,
401 filter: QueryFilter,
402 mut fcn: impl FnMut(ShapeId, Pos, Vec2, f32) -> f32,
403) -> TreeStats {
404 let mut tree_stats = TreeStats::default();
405
406 debug_assert!(!world.locked);
407 if world.locked {
408 return tree_stats;
409 }
410
411 debug_assert!(is_valid_position(origin));
412 debug_assert!(is_valid_vec2(translation));
413
414 let mut q = QueryRecorder::begin(world, |buf| {
415 rec_w_position(buf, origin);
416 rec_w_shapeproxy(buf, proxy);
417 rec_w_vec2(buf, translation);
418 rec_w_queryfilter(buf, filter);
419 });
420
421 {
422 let world: &World = world;
423
424 let cast_input = ShapeCastInput {
427 proxy: *proxy,
428 translation,
429 max_fraction: 1.0,
430 can_encroach: false,
431 };
432
433 let mut fraction = 1.0f32;
434
435 let local_box = make_aabb(&proxy.points[..proxy.count as usize], proxy.radius);
441 let box_ = offset_aabb(local_box, origin);
442 let mut tree_input = BoxCastInput {
443 box_,
444 translation,
445 max_fraction: 1.0,
446 };
447
448 for i in 0..BODY_TYPE_COUNT {
449 let tree_result = world.broad_phase.trees[i].box_cast(
450 &tree_input,
451 filter.mask_bits,
452 |box_input, _, user_data| {
453 let shape_id = user_data as i32;
455 let shape = &world.shapes[shape_id as usize];
456
457 if !should_query_collide(shape.filter, filter) {
458 return box_input.max_fraction;
459 }
460
461 let mut local_input = cast_input;
465 local_input.max_fraction = box_input.max_fraction;
466
467 let body = &world.bodies[shape.body_id as usize];
468 let transform = get_body_transform_quick(world, body);
469 let local_transform = to_relative_transform(transform, origin);
470
471 let output = shape_cast_shape(&local_input, shape, local_transform);
472
473 if output.hit {
474 let id = query_shape_id(world, shape);
475 let point = offset_pos(origin, output.point);
476 let user_fraction = fcn(id, point, output.normal, output.fraction);
477 if q.active() {
478 rec_w_shapeid(&mut q.buf, id);
479 rec_w_position(&mut q.buf, point);
480 rec_w_vec2(&mut q.buf, output.normal);
481 rec_w_f32(&mut q.buf, output.fraction);
482 rec_w_f32(&mut q.buf, user_fraction);
483 q.hits += 1;
484 }
485
486 if (0.0..=1.0).contains(&user_fraction) {
488 fraction = user_fraction;
489 }
490
491 return user_fraction;
492 }
493
494 box_input.max_fraction
495 },
496 );
497 tree_stats.node_visits += tree_result.node_visits;
498 tree_stats.leaf_visits += tree_result.leaf_visits;
499
500 if fraction == 0.0 {
501 break;
502 }
503
504 tree_input.max_fraction = fraction;
505 }
506 }
507
508 q.commit(world, OP_QUERY_CAST_SHAPE, Some(tree_stats));
509 tree_stats
510}
511
512pub fn world_cast_mover(
517 world: &mut World,
518 origin: Pos,
519 mover: &Capsule,
520 translation: Vec2,
521 filter: QueryFilter,
522) -> f32 {
523 debug_assert!(is_valid_position(origin));
524 debug_assert!(is_valid_vec2(translation));
525 debug_assert!(mover.radius > 2.0 * linear_slop());
526
527 debug_assert!(!world.locked);
528 if world.locked {
529 return 1.0;
530 }
531
532 let mut fraction = 1.0f32;
533
534 {
535 let world: &World = world;
536
537 let mut cast_input = ShapeCastInput::default();
540 cast_input.proxy.points[0] = mover.center1;
541 cast_input.proxy.points[1] = mover.center2;
542 cast_input.proxy.count = 2;
543 cast_input.proxy.radius = mover.radius;
544 cast_input.translation = translation;
545 cast_input.max_fraction = 1.0;
546 cast_input.can_encroach = true;
547
548 let centers = [mover.center1, mover.center2];
551 let box_ = offset_aabb(make_aabb(¢ers, mover.radius), origin);
552 let mut tree_input = BoxCastInput {
553 box_,
554 translation,
555 max_fraction: 1.0,
556 };
557
558 for i in 0..BODY_TYPE_COUNT {
559 world.broad_phase.trees[i].box_cast(
560 &tree_input,
561 filter.mask_bits,
562 |box_input, _, user_data| {
563 let shape_id = user_data as i32;
565 let shape = &world.shapes[shape_id as usize];
566
567 if !should_query_collide(shape.filter, filter) {
568 return fraction;
569 }
570
571 let mut local_input = cast_input;
574 local_input.max_fraction = box_input.max_fraction;
575
576 let body = &world.bodies[shape.body_id as usize];
577 let transform =
578 to_relative_transform(get_body_transform_quick(world, body), origin);
579
580 let output = shape_cast_shape(&local_input, shape, transform);
581 if output.fraction == 0.0 {
582 return fraction;
584 }
585
586 fraction = output.fraction;
587 output.fraction
588 },
589 );
590
591 if fraction == 0.0 {
592 break;
593 }
594
595 tree_input.max_fraction = fraction;
596 }
597 }
598
599 record_query_result(
600 world,
601 OP_QUERY_CAST_MOVER,
602 |buf| {
603 rec_w_position(buf, origin);
604 rec_w_capsule(buf, *mover);
605 rec_w_vec2(buf, translation);
606 rec_w_queryfilter(buf, filter);
607 },
608 |buf| rec_w_f32(buf, fraction),
609 );
610
611 fraction
612}
613
614pub fn world_collide_mover(
619 world: &mut World,
620 origin: Pos,
621 mover: &Capsule,
622 filter: QueryFilter,
623 mut fcn: impl FnMut(ShapeId, &PlaneResult) -> bool,
624) {
625 debug_assert!(!world.locked);
626 if world.locked {
627 return;
628 }
629
630 debug_assert!(is_valid_position(origin));
631
632 let mut q = QueryRecorder::begin(world, |buf| {
633 rec_w_position(buf, origin);
634 rec_w_capsule(buf, *mover);
635 rec_w_queryfilter(buf, filter);
636 });
637
638 {
639 let world: &World = world;
640
641 let r = Vec2 {
642 x: mover.radius,
643 y: mover.radius,
644 };
645
646 let rel_box = Aabb {
649 lower_bound: crate::math_functions::sub(
650 crate::math_functions::min(mover.center1, mover.center2),
651 r,
652 ),
653 upper_bound: crate::math_functions::add(
654 crate::math_functions::max(mover.center1, mover.center2),
655 r,
656 ),
657 };
658 let aabb = offset_aabb(rel_box, origin);
659
660 for i in 0..BODY_TYPE_COUNT {
661 world.broad_phase.trees[i].query(aabb, filter.mask_bits, |_, user_data| {
662 let shape_id = user_data as i32;
664 let shape = &world.shapes[shape_id as usize];
665
666 if !should_query_collide(shape.filter, filter) {
667 return true;
668 }
669
670 let body = &world.bodies[shape.body_id as usize];
673 let transform =
674 to_relative_transform(get_body_transform_quick(world, body), origin);
675
676 let result = collide_mover(mover, shape, transform);
677
678 if result.hit && is_normalized(result.plane.normal) {
680 let id = query_shape_id(world, shape);
681 let ret = fcn(id, &result);
682 if q.active() {
683 rec_w_shapeid(&mut q.buf, id);
685 rec_w_planeresult(&mut q.buf, &result);
686 rec_w_bool(&mut q.buf, ret);
687 q.hits += 1;
688 }
689 return ret;
690 }
691
692 true
693 });
694 }
695 }
696
697 q.commit(world, OP_QUERY_COLLIDE_MOVER, None);
699}