parry3d/query/ray/
ray_heightfield.rs1use crate::math::Real;
2#[cfg(feature = "dim2")]
3use crate::query;
4use crate::query::{Ray, RayCast, RayIntersection};
5#[cfg(feature = "dim2")]
6use crate::shape::FeatureId;
7use crate::shape::HeightField;
8
9#[cfg(feature = "dim2")]
10impl RayCast for HeightField {
11 #[inline]
12 fn cast_local_ray_and_get_normal(
13 &self,
14 ray: &Ray,
15 max_time_of_impact: Real,
16 _: bool,
17 ) -> Option<RayIntersection> {
18 let aabb = self.local_aabb();
19 let (min_t, mut max_t) = aabb.clip_ray_parameters(ray)?;
20
21 if min_t > max_time_of_impact {
22 return None;
23 }
24
25 max_t = max_t.min(max_time_of_impact);
26
27 let clip_ray_a = ray.point_at(min_t);
28
29 let mut curr = self.cell_at_point(&clip_ray_a).unwrap_or_else(|| {
31 if ray.origin.x > 0.0 {
32 self.num_cells() - 1
33 } else {
34 0_usize
35 }
36 });
37
38 if let Some(seg) = self.segment_at(curr) {
42 let (s, t) = query::details::closest_points_line_line_parameters(
43 &ray.origin,
44 &ray.dir,
45 &seg.a,
46 &seg.scaled_direction(),
47 );
48 if s >= 0.0 && t >= 0.0 && t <= 1.0 {
49 let n = seg.normal().unwrap().into_inner();
51 let fid = if n.dot(&ray.dir) > 0.0 {
52 curr + self.num_cells()
54 } else {
55 curr
57 };
58
59 return Some(RayIntersection::new(s, n, FeatureId::Face(fid as u32)));
60 }
61 }
62
63 if ray.dir.x == 0.0 {
67 return None;
68 }
69
70 let right = ray.dir.x > 0.0;
71 let cell_width = self.cell_width();
72 let start_x = self.start_x();
73
74 while (right && curr < self.num_cells()) || (!right && curr > 0) {
75 let curr_param;
76
77 if right {
78 curr += 1;
79 curr_param = (cell_width * na::convert::<f64, Real>(curr as f64) + start_x
80 - ray.origin.x)
81 / ray.dir.x;
82 } else {
83 curr_param =
84 (ray.origin.x - cell_width * na::convert::<f64, Real>(curr as f64) - start_x)
85 / ray.dir.x;
86 curr -= 1;
87 }
88
89 if curr_param >= max_t {
90 return None;
92 }
93
94 if let Some(seg) = self.segment_at(curr) {
95 let (s, t) = query::details::closest_points_line_line_parameters(
97 &ray.origin,
98 &ray.dir,
99 &seg.a,
100 &seg.scaled_direction(),
101 );
102
103 if t >= 0.0 && t <= 1.0 && s <= max_time_of_impact {
104 let n = seg.normal().unwrap().into_inner();
105 let fid = if n.dot(&ray.dir) > 0.0 {
106 curr + self.num_cells()
108 } else {
109 curr
111 };
112 return Some(RayIntersection::new(s, n, FeatureId::Face(fid as u32)));
113 }
114 }
115 }
116
117 None
118 }
119}
120
121#[cfg(feature = "dim3")]
122impl RayCast for HeightField {
123 #[inline]
124 fn cast_local_ray_and_get_normal(
125 &self,
126 ray: &Ray,
127 max_time_of_impact: Real,
128 solid: bool,
129 ) -> Option<RayIntersection> {
130 use num_traits::Bounded;
131
132 let aabb = self.local_aabb();
133 let (min_t, mut max_t) = aabb.clip_ray_parameters(ray)?;
134 max_t = max_t.min(max_time_of_impact);
135 let clip_ray_a = ray.point_at(min_t);
136 let mut cell = match self.cell_at_point(&clip_ray_a) {
137 Some(cell) => cell,
138 None => {
140 let i = if ray.origin.z > 0.0 {
141 self.nrows() - 1
142 } else {
143 0
144 };
145
146 let j = if ray.origin.x > 0.0 {
147 self.ncols() - 1
148 } else {
149 0
150 };
151
152 (i, j)
153 }
154 };
155
156 loop {
157 let tris = self.triangles_at(cell.0, cell.1);
158 let inter1 = tris
159 .0
160 .and_then(|tri| tri.cast_local_ray_and_get_normal(ray, max_time_of_impact, solid));
161 let inter2 = tris
162 .1
163 .and_then(|tri| tri.cast_local_ray_and_get_normal(ray, max_time_of_impact, solid));
164
165 match (inter1, inter2) {
166 (Some(mut inter1), Some(mut inter2)) => {
167 if inter1.time_of_impact < inter2.time_of_impact {
168 inter1.feature =
169 self.convert_triangle_feature_id(cell.0, cell.1, true, inter1.feature);
170 return Some(inter1);
171 } else {
172 inter2.feature =
173 self.convert_triangle_feature_id(cell.0, cell.1, false, inter2.feature);
174 return Some(inter2);
175 }
176 }
177 (Some(mut inter), None) => {
178 inter.feature =
179 self.convert_triangle_feature_id(cell.0, cell.1, true, inter.feature);
180 return Some(inter);
181 }
182 (None, Some(mut inter)) => {
183 inter.feature =
184 self.convert_triangle_feature_id(cell.0, cell.1, false, inter.feature);
185 return Some(inter);
186 }
187 (None, None) => {}
188 }
189
190 let (toi_x, right) = if ray.dir.x > 0.0 {
194 let x = self.x_at(cell.1 + 1);
195 ((x - ray.origin.x) / ray.dir.x, true)
196 } else if ray.dir.x < 0.0 {
197 let x = self.x_at(cell.1);
198 ((x - ray.origin.x) / ray.dir.x, false)
199 } else {
200 (Real::max_value(), false)
201 };
202
203 let (toi_z, down) = if ray.dir.z > 0.0 {
204 let z = self.z_at(cell.0 + 1);
205 ((z - ray.origin.z) / ray.dir.z, true)
206 } else if ray.dir.z < 0.0 {
207 let z = self.z_at(cell.0);
208 ((z - ray.origin.z) / ray.dir.z, false)
209 } else {
210 (Real::max_value(), false)
211 };
212
213 if toi_x > max_t && toi_z > max_t {
214 break;
215 }
216
217 if toi_x >= 0.0 && toi_x < toi_z {
218 if right {
219 cell.1 += 1
220 } else if cell.1 > 0 {
221 cell.1 -= 1
222 } else {
223 break;
224 }
225 } else if toi_z >= 0.0 {
226 if down {
227 cell.0 += 1
228 } else if cell.0 > 0 {
229 cell.0 -= 1
230 } else {
231 break;
232 }
233 } else {
234 break;
235 }
236
237 if cell.0 >= self.nrows() || cell.1 >= self.ncols() {
238 break;
239 }
240 }
241
242 None
243 }
244}