use crate::physics::sim::body::Body;
use crate::physics::sim::collide::Pose;
use crate::physics::sim::query::gjk::{self, Support};
pub(crate) fn overlapping(a: &Body, b: &Body) -> bool {
let (Some(shape_a), Some(shape_b)) = (a.convex(), b.convex()) else {
return false;
};
shapes_overlap(
&Support::new(shape_a, pose_of(a)),
&Support::new(shape_b, pose_of(b)),
)
}
pub(crate) fn shapes_overlap(a: &Support, b: &Support) -> bool {
let separation = gjk::separation(a, b);
separation.is_entangled() || separation.gap < 0.0
}
fn pose_of(body: &Body) -> Pose {
Pose {
position: body.position,
rotation: body.orientation,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::physics::sim::math::{Quat, Vec3, vec3};
use crate::physics::{ColliderShape, LayerMask};
const UNIT: ColliderShape = ColliderShape::Cuboid {
half_extents: [1.0, 1.0, 1.0],
};
fn at(shape: ColliderShape, position: Vec3) -> Body {
Body::fixed(shape, position, Quat::IDENTITY, 0.0, LayerMask::ALL)
}
#[test]
fn boxes_sharing_space_overlap_and_boxes_beside_each_other_do_not() {
let region = at(UNIT, Vec3::ZERO);
assert!(overlapping(®ion, &at(UNIT, vec3(1.5, 0.0, 0.0))));
assert!(overlapping(®ion, &at(UNIT, Vec3::ZERO)));
assert!(!overlapping(®ion, &at(UNIT, vec3(2.5, 0.0, 0.0))));
}
#[test]
fn a_ball_is_measured_by_its_surface() {
let region = at(UNIT, Vec3::ZERO);
let ball = ColliderShape::Ball { radius: 0.5 };
assert!(overlapping(®ion, &at(ball, vec3(1.4, 0.0, 0.0))));
assert!(!overlapping(®ion, &at(ball, vec3(1.6, 0.0, 0.0))));
}
#[test]
fn a_capsule_crossing_a_corner_is_found() {
let region = at(UNIT, Vec3::ZERO);
let capsule = ColliderShape::Capsule {
half_height: 1.0,
radius: 0.25,
};
assert!(overlapping(®ion, &at(capsule, vec3(1.1, 1.5, 0.0))));
assert!(!overlapping(®ion, &at(capsule, vec3(1.5, 2.5, 0.0))));
}
#[test]
fn a_turned_region_is_measured_where_it_actually_is() {
let slab = ColliderShape::Cuboid {
half_extents: [2.0, 0.2, 2.0],
};
let mut region = at(slab, Vec3::ZERO);
let probe = at(ColliderShape::Ball { radius: 0.1 }, vec3(0.0, 1.5, 0.0));
assert!(!overlapping(®ion, &probe));
region.orientation = Quat::from_euler_deg([0.0, 0.0, 90.0]);
assert!(overlapping(®ion, &probe), "the slab now stands upright");
}
#[test]
fn terrain_never_overlaps_a_region() {
use crate::physics::sim::aabb::Aabb;
let terrain = Body::terrain(0, Aabb::EMPTY, Vec3::ZERO, 1.0, LayerMask::ALL);
assert!(!overlapping(&at(UNIT, Vec3::ZERO), &terrain));
assert!(!overlapping(&terrain, &at(UNIT, Vec3::ZERO)));
}
}