use crate::containment::ray_hits_triangle;
use axiolid_core::{Point3, Vec3};
use axiolid_mesh::TriMesh;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Hit {
pub triangle: usize,
pub t: f64,
pub point: Point3,
}
pub fn ray_cast(mesh: &TriMesh, origin: Point3, direction: Vec3) -> Option<Hit> {
if direction.length_squared() == 0.0 {
return None;
}
let mut nearest: Option<Hit> = None;
for index in 0..mesh.indices.len() / 3 {
let Some(triangle) = crate::clearance::triangle_at(mesh, index) else {
continue;
};
if !ray_hits_triangle(triangle, origin, direction)? {
continue;
}
let Some(t) = ray_parameter(triangle, origin, direction) else {
continue;
};
if t < 0.0 {
continue;
}
if nearest.is_none_or(|hit| t < hit.t) {
nearest = Some(Hit {
triangle: index,
t,
point: origin + direction * t,
});
}
}
nearest
}
fn ray_parameter(triangle: [Point3; 3], origin: Point3, direction: Vec3) -> Option<f64> {
let [a, b, c] = triangle;
let normal = (b - a).cross(c - a);
let denominator = normal.dot(direction);
if denominator == 0.0 {
return None;
}
let t = normal.dot(a - origin) / denominator;
t.is_finite().then_some(t)
}