ncollide3d/query/ray/
ray_triangle.rs

1use na::{self, RealField, Vector3};
2
3use crate::math::{Isometry, Point};
4use crate::query::{Ray, RayCast, RayIntersection};
5use crate::shape::{FeatureId, Triangle};
6
7impl<N: RealField + Copy> RayCast<N> for Triangle<N> {
8    #[inline]
9    fn toi_and_normal_with_ray(
10        &self,
11        m: &Isometry<N>,
12        ray: &Ray<N>,
13        max_toi: N,
14        _: bool,
15    ) -> Option<RayIntersection<N>> {
16        let ls_ray = ray.inverse_transform_by(m);
17        let mut inter = ray_intersection_with_triangle(&self.a, &self.b, &self.c, &ls_ray)?.0;
18
19        if inter.toi <= max_toi {
20            inter.normal = m * inter.normal;
21            Some(inter)
22        } else {
23            None
24        }
25    }
26}
27
28/// Computes the intersection between a triangle and a ray.
29///
30/// If an intersection is found, the time of impact, the normal and the barycentric coordinates of
31/// the intersection point are returned.
32pub fn ray_intersection_with_triangle<N: RealField + Copy>(
33    a: &Point<N>,
34    b: &Point<N>,
35    c: &Point<N>,
36    ray: &Ray<N>,
37) -> Option<(RayIntersection<N>, Vector3<N>)> {
38    let ab = *b - *a;
39    let ac = *c - *a;
40
41    // normal
42    let n = ab.cross(&ac);
43    let d = n.dot(&ray.dir);
44
45    // the normal and the ray direction are parallel
46    if d.is_zero() {
47        return None;
48    }
49
50    let ap = ray.origin - *a;
51    let t = ap.dot(&n);
52
53    // the ray does not intersect the plane defined by the triangle
54    if (t < na::zero() && d < na::zero()) || (t > na::zero() && d > na::zero()) {
55        return None;
56    }
57
58    let fid = if d < N::zero() { 0 } else { 1 };
59
60    let d = d.abs();
61
62    //
63    // intersection: compute barycentric coordinates
64    //
65    let e = -ray.dir.cross(&ap);
66
67    let mut v;
68    let mut w;
69    let toi;
70    let normal;
71
72    if t < na::zero() {
73        v = -ac.dot(&e);
74
75        if v < na::zero() || v > d {
76            return None;
77        }
78
79        w = ab.dot(&e);
80
81        if w < na::zero() || v + w > d {
82            return None;
83        }
84
85        let invd = na::one::<N>() / d;
86        toi = -t * invd;
87        normal = -n.normalize();
88        v = v * invd;
89        w = w * invd;
90    } else {
91        v = ac.dot(&e);
92
93        if v < na::zero() || v > d {
94            return None;
95        }
96
97        w = -ab.dot(&e);
98
99        if w < na::zero() || v + w > d {
100            return None;
101        }
102
103        let invd = na::one::<N>() / d;
104        toi = t * invd;
105        normal = n.normalize();
106        v = v * invd;
107        w = w * invd;
108    }
109
110    Some((
111        RayIntersection::new(toi, normal, FeatureId::Face(fid)),
112        Vector3::new(-v - w + na::one(), v, w),
113    ))
114}