parry3d/query/ray/
ray_triangle.rs1use crate::math::Real;
2#[cfg(feature = "dim2")]
3use crate::math::Vector;
4#[cfg(feature = "dim3")]
5use crate::math::Vector;
6use crate::query::{Ray, RayCast, RayIntersection};
7use crate::shape::{FeatureId, Triangle};
8
9impl RayCast for Triangle {
10 #[inline]
11 #[cfg(feature = "dim2")]
12 fn cast_local_ray_and_get_normal(
13 &self,
14 ray: &Ray,
15 max_time_of_impact: Real,
16 solid: bool,
17 ) -> Option<RayIntersection> {
18 let edges = self.edges();
19
20 if solid {
21 let perp_sign1 = edges[0]
23 .scaled_direction()
24 .perp_dot(ray.origin - edges[0].a)
25 > 0.0;
26 let perp_sign2 = edges[1]
27 .scaled_direction()
28 .perp_dot(ray.origin - edges[1].a)
29 > 0.0;
30 let perp_sign3 = edges[2]
31 .scaled_direction()
32 .perp_dot(ray.origin - edges[2].a)
33 > 0.0;
34
35 if perp_sign1 == perp_sign2 && perp_sign1 == perp_sign3 {
36 return Some(RayIntersection::new(0.0, Vector::Y, FeatureId::Face(0)));
37 }
38 }
39
40 let mut best = None;
41 let mut smallest_toi = Real::MAX;
42
43 for edge in &edges {
44 if let Some(inter) = edge.cast_local_ray_and_get_normal(ray, max_time_of_impact, solid)
45 {
46 if inter.time_of_impact < smallest_toi {
47 smallest_toi = inter.time_of_impact;
48 best = Some(inter);
49 }
50 }
51 }
52
53 best
54 }
55
56 #[inline]
57 #[cfg(feature = "dim3")]
58 fn cast_local_ray_and_get_normal(
59 &self,
60 ray: &Ray,
61 max_time_of_impact: Real,
62 _: bool,
63 ) -> Option<RayIntersection> {
64 let inter = local_ray_intersection_with_triangle(self.a, self.b, self.c, ray)?.0;
65
66 if inter.time_of_impact <= max_time_of_impact {
67 Some(inter)
68 } else {
69 None
70 }
71 }
72}
73
74#[cfg(feature = "dim3")]
79pub fn local_ray_intersection_with_triangle(
80 a: Vector,
81 b: Vector,
82 c: Vector,
83 ray: &Ray,
84) -> Option<(RayIntersection, Vector)> {
85 let ab = b - a;
86 let ac = c - a;
87
88 let n = ab.cross(ac);
90 let d = n.dot(ray.dir);
91
92 if d == 0.0 {
94 return None;
95 }
96
97 let ap = ray.origin - a;
98 let t = ap.dot(n);
99
100 if (t < 0.0 && d < 0.0) || (t > 0.0 && d > 0.0) {
102 return None;
103 }
104
105 let fid = if d < 0.0 { 0 } else { 1 };
106
107 let d = d.abs();
108
109 let e = -ray.dir.cross(ap);
113
114 let mut v;
115 let mut w;
116 let time_of_impact;
117 let normal;
118
119 if t < 0.0 {
120 v = -ac.dot(e);
121
122 if v < 0.0 || v > d {
123 return None;
124 }
125
126 w = ab.dot(e);
127
128 if w < 0.0 || v + w > d {
129 return None;
130 }
131
132 let invd = 1.0 / d;
133 time_of_impact = -t * invd;
134 normal = -n.normalize();
135 v *= invd;
136 w *= invd;
137 } else {
138 v = ac.dot(e);
139
140 if v < 0.0 || v > d {
141 return None;
142 }
143
144 w = -ab.dot(e);
145
146 if w < 0.0 || v + w > d {
147 return None;
148 }
149
150 let invd = 1.0 / d;
151 time_of_impact = t * invd;
152 normal = n.normalize();
153 v *= invd;
154 w *= invd;
155 }
156
157 Some((
158 RayIntersection::new(time_of_impact, normal, FeatureId::Face(fid)),
159 Vector::new(-v - w + 1.0, v, w),
160 ))
161}