use num::Float;
use na::{Vector3, Identity, Transform, Rotate};
use na;
use geometry::algorithms::johnson_simplex::JohnsonSimplex;
use entities::shape::Triangle;
use ray::{Ray, RayCast, RayIntersection, implicit_toi_and_normal_with_ray};
use math::{Point, Vector};
use utils;
impl
RayCast
for Triangle
where P: Point,
M: Transform
+ Rotate {
#[inline]
fn toi_and_normal_with_ray(&self, m: &M, ray: &Ray, solid: bool) -> Option> {
let ls_ray = Ray::new(m.inverse_transform(&ray.origin), m.inverse_rotate(&ray.dir));
let res = if na::dimension::() == 3 {
triangle_ray_intersection(self.a(), self.b(), self.c(), &ls_ray).map(|(r, _)| r)
}
else {
implicit_toi_and_normal_with_ray(&Identity::new(), self,
&mut JohnsonSimplex::
::new_w_tls(), &ls_ray, solid)
};
res.map(|mut r| { r.normal = m.rotate(&r.normal); r })
}
}
/// Computes the intersection between a triangle and a ray.
///
/// If an intersection is found, the time of impact, the normal and the barycentric coordinates of
/// the intersection point are returned.
pub fn triangle_ray_intersection
(a: &P, b: &P, c: &P, ray: &Ray
)
-> Option<(RayIntersection, Vector3<::Scalar>)>
where P: Point {
let ab = *b - *a;
let ac = *c - *a;
// normal
let n = utils::cross3(&ab, &ac);
let d = na::dot(&n, &ray.dir);
// the normal and the ray direction are parallel
if na::is_zero(&d) {
return None;
}
let ap = ray.origin - *a;
let t = na::dot(&ap, &n);
// the ray does not intersect the plane defined by the triangle
if (t < na::zero() && d < na::zero()) ||
(t > na::zero() && d > na::zero()) {
return None;
}
let d = d.abs();
//
// intersection: compute barycentric coordinates
//
let e = -utils::cross3(&ray.dir, &ap);
let mut v;
let mut w;
let toi;
let normal;
if t < na::zero() {
v = -na::dot(&ac, &e);
if v < na::zero() || v > d {
return None;
}
w = na::dot(&ab, &e);
if w < na::zero() || v + w > d {
return None;
}
let invd = na::one::<::Scalar>() / d;
toi = -t * invd;
normal = -na::normalize(&n);
v = v * invd;
w = w * invd;
}
else {
v = na::dot(&ac, &e);
if v < na::zero() || v > d {
return None;
}
w = -na::dot(&ab, &e);
if w < na::zero() || v + w > d {
return None;
}
let invd = na::one::<::Scalar>() / d;
toi = t * invd;
normal = na::normalize(&n);
v = v * invd;
w = w * invd;
}
Some((RayIntersection::new(toi, normal), Vector3::new(-v - w + na::one(), v, w)))
}