Skip to main content

box3d_rust/compound/
query.rs

1//! Compound AABB query and character-mover collision.
2//!
3//! SPDX-FileCopyrightText: 2025 Erin Catto
4//! SPDX-License-Identifier: MIT
5
6use super::types::{get_compound_child, ChildGeometry, CompoundData};
7use crate::dynamic_tree::DEFAULT_MASK_BITS;
8use crate::geometry::{collide_mover_and_capsule, collide_mover_and_sphere, Capsule, PlaneResult};
9use crate::hull::collide_mover_and_hull;
10use crate::math_functions::{
11    add, inv_transform_point, max, min, rotate_vector, sub, transform_point, Aabb, Vec3,
12};
13use crate::mesh::collide_mover_and_mesh;
14
15/// Query an AABB against compound children. (b3QueryCompound)
16///
17/// The callback receives `(compound, child_index)` and returns `false` to stop.
18pub fn query_compound(
19    compound: &CompoundData,
20    aabb: Aabb,
21    mut fcn: impl FnMut(&CompoundData, i32) -> bool,
22) {
23    compound
24        .tree
25        .query(aabb, DEFAULT_MASK_BITS, false, |_proxy_id, user_data| {
26            fcn(compound, user_data as i32)
27        });
28}
29
30/// Collide a capsule mover against a compound. (b3CollideMoverAndCompound)
31pub fn collide_mover_and_compound(
32    planes: &mut [PlaneResult],
33    shape: &CompoundData,
34    mover: &Capsule,
35) -> i32 {
36    let capacity = planes.len() as i32;
37    if capacity == 0 {
38        return 0;
39    }
40
41    let mut plane_count = 0i32;
42
43    let mut aabb = Aabb {
44        lower_bound: min(mover.center1, mover.center2),
45        upper_bound: max(mover.center1, mover.center2),
46    };
47    let r = Vec3 {
48        x: mover.radius,
49        y: mover.radius,
50        z: mover.radius,
51    };
52    aabb.lower_bound = sub(aabb.lower_bound, r);
53    aabb.upper_bound = add(aabb.upper_bound, r);
54
55    shape
56        .tree
57        .query(aabb, !0u64, false, |_proxy_id, user_data| {
58            let child_index = user_data as i32;
59            let child = get_compound_child(shape, child_index);
60
61            let local_mover = Capsule {
62                center1: inv_transform_point(child.transform, mover.center1),
63                center2: inv_transform_point(child.transform, mover.center2),
64                radius: mover.radius,
65            };
66
67            let remaining = capacity - plane_count;
68            debug_assert!(remaining > 0);
69            let slot = &mut planes[plane_count as usize..];
70
71            let added = match child.geometry {
72                ChildGeometry::Capsule(ref capsule) => {
73                    collide_mover_and_capsule(&mut slot[0], capsule, &local_mover)
74                }
75                ChildGeometry::Hull(hull) => {
76                    collide_mover_and_hull(&mut slot[0], hull, &local_mover)
77                }
78                ChildGeometry::Mesh(ref mesh) => collide_mover_and_mesh(slot, mesh, &local_mover),
79                ChildGeometry::Sphere(ref sphere) => {
80                    collide_mover_and_sphere(&mut slot[0], sphere, &local_mover)
81                }
82            };
83
84            for i in 0..added {
85                let p = &mut planes[(plane_count + i) as usize];
86                p.plane.normal = rotate_vector(child.transform.q, p.plane.normal);
87                p.point = transform_point(child.transform, p.point);
88            }
89
90            plane_count += added;
91            plane_count < capacity
92        });
93
94    plane_count
95}