use super::functions::*;
pub type ShapeEntry = (Box<dyn ShapeQuery>, [[f64; 4]; 4]);
pub struct ViewFrustum {
pub planes: [[f64; 4]; 6],
}
impl ViewFrustum {
pub fn new(planes: [[f64; 4]; 6]) -> Self {
Self { planes }
}
pub fn test_sphere(&self, center: [f64; 3], radius: f64) -> bool {
for plane in &self.planes {
let n = [plane[0], plane[1], plane[2]];
let d = plane[3];
let sd = dot(center, n) + d;
if sd < -radius {
return false;
}
}
true
}
pub fn test_aabb(&self, min: [f64; 3], max: [f64; 3]) -> bool {
for plane in &self.planes {
let n = [plane[0], plane[1], plane[2]];
let d = plane[3];
let p = [
if n[0] >= 0.0 { max[0] } else { min[0] },
if n[1] >= 0.0 { max[1] } else { min[1] },
if n[2] >= 0.0 { max[2] } else { min[2] },
];
if dot(p, n) + d < 0.0 {
return false;
}
}
true
}
}
pub struct AllPairsRayResult {
pub shape_index: usize,
pub toi: f64,
pub normal: [f64; 3],
pub point: [f64; 3],
}
pub struct SphereCastResult {
pub shape_index: usize,
pub toi: f64,
pub normal: [f64; 3],
}
pub struct ToiResult {
pub toi: f64,
pub normal: [f64; 3],
pub point: [f64; 3],
}
pub struct NearestShape {
pub index: usize,
pub distance: f64,
pub closest_point: [f64; 3],
}
pub struct CompoundQuery {
pub shapes: Vec<ShapeEntry>,
}
impl CompoundQuery {
pub fn new() -> Self {
CompoundQuery { shapes: Vec::new() }
}
pub fn add_shape(&mut self, shape: Box<dyn ShapeQuery>, transform: [[f64; 4]; 4]) {
self.shapes.push((shape, transform));
}
pub fn ray_cast_any(
&self,
ray_origin: [f64; 3],
ray_dir: [f64; 3],
max_toi: f64,
) -> Option<RayCastResult> {
let mut best: Option<RayCastResult> = None;
for (shape, transform) in &self.shapes {
let (local_origin, local_dir) = transform_ray(ray_origin, ray_dir, *transform);
let current_max = best.as_ref().map_or(max_toi, |b| b.toi);
if let Some(local_res) = shape.ray_cast(local_origin, local_dir, current_max) {
let world_point = transform_point(local_res.point, *transform);
let n = local_res.normal;
let t = transform;
let world_normal = normalize([
t[0][0] * n[0] + t[0][1] * n[1] + t[0][2] * n[2],
t[1][0] * n[0] + t[1][1] * n[1] + t[1][2] * n[2],
t[2][0] * n[0] + t[2][1] * n[1] + t[2][2] * n[2],
]);
let candidate = RayCastResult {
toi: local_res.toi,
normal: world_normal,
point: world_point,
};
let replace = match &best {
None => true,
Some(b) => candidate.toi < b.toi,
};
if replace {
best = Some(candidate);
}
}
}
best
}
}
pub enum QueryShapeRef<'a> {
Sphere(&'a QuerySphere),
Box(&'a QueryBox),
Capsule(&'a QueryCapsule),
}
pub struct RayCastResult {
pub toi: f64,
pub normal: [f64; 3],
pub point: [f64; 3],
}
pub struct QuerySphere {
pub center: [f64; 3],
pub radius: f64,
}
pub struct SegmentDistResult {
pub point_a: [f64; 3],
pub point_b: [f64; 3],
pub distance: f64,
}
pub struct PointQueryResult {
pub point: [f64; 3],
pub distance: f64,
pub inside: bool,
}
pub struct QueryCapsule {
pub p0: [f64; 3],
pub p1: [f64; 3],
pub radius: f64,
}
impl QueryCapsule {
pub(super) fn closest_point_on_segment(&self, point: [f64; 3]) -> ([f64; 3], f64) {
let ab = sub(self.p1, self.p0);
let ap = sub(point, self.p0);
let len_sq = dot(ab, ab);
if len_sq < 1e-30 {
return (self.p0, 0.0);
}
let t = clamp(dot(ap, ab) / len_sq, 0.0, 1.0);
let closest = add(self.p0, scale(ab, t));
(closest, t)
}
}
pub struct QueryBox {
pub center: [f64; 3],
pub half_extents: [f64; 3],
}