1use crate::bounding_volume::SimdAabb;
2use crate::math::{Real, SimdBool, SimdReal, SIMD_WIDTH};
3use crate::partitioning::{SimdBestFirstVisitStatus, SimdBestFirstVisitor};
4use crate::query::{Ray, RayCast, RayIntersection, SimdRay};
5use crate::shape::{Compound, FeatureId, Polyline, TriMesh, TypedSimdCompositeShape};
6use simba::simd::{SimdBool as _, SimdPartialOrd, SimdValue};
7
8impl RayCast for TriMesh {
9 #[inline]
10 fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: Real, solid: bool) -> Option<Real> {
11 let mut visitor =
12 RayCompositeShapeToiBestFirstVisitor::new(self, ray, max_time_of_impact, solid);
13
14 self.qbvh()
15 .traverse_best_first(&mut visitor)
16 .map(|res| res.1 .1)
17 }
18
19 #[inline]
20 fn cast_local_ray_and_get_normal(
21 &self,
22 ray: &Ray,
23 max_time_of_impact: Real,
24 solid: bool,
25 ) -> Option<RayIntersection> {
26 let mut visitor = RayCompositeShapeToiAndNormalBestFirstVisitor::new(
27 self,
28 ray,
29 max_time_of_impact,
30 solid,
31 );
32
33 self.qbvh()
34 .traverse_best_first(&mut visitor)
35 .map(|(_, (best, mut res))| {
36 if res.feature == FeatureId::Face(1) {
39 res.feature = FeatureId::Face(best + self.indices().len() as u32)
40 } else {
41 res.feature = FeatureId::Face(best);
42 }
43 res
44 })
45 }
46}
47
48impl RayCast for Polyline {
49 #[inline]
50 fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: Real, solid: bool) -> Option<Real> {
51 let mut visitor =
52 RayCompositeShapeToiBestFirstVisitor::new(self, ray, max_time_of_impact, solid);
53
54 self.qbvh()
55 .traverse_best_first(&mut visitor)
56 .map(|res| res.1 .1)
57 }
58
59 #[inline]
60 fn cast_local_ray_and_get_normal(
61 &self,
62 ray: &Ray,
63 max_time_of_impact: Real,
64 solid: bool,
65 ) -> Option<RayIntersection> {
66 let mut visitor = RayCompositeShapeToiAndNormalBestFirstVisitor::new(
67 self,
68 ray,
69 max_time_of_impact,
70 solid,
71 );
72
73 self.qbvh()
74 .traverse_best_first(&mut visitor)
75 .map(|(_, (_, res))| res)
76 }
77}
78
79impl RayCast for Compound {
80 #[inline]
81 fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: Real, solid: bool) -> Option<Real> {
82 let mut visitor =
83 RayCompositeShapeToiBestFirstVisitor::new(self, ray, max_time_of_impact, solid);
84
85 self.qbvh()
86 .traverse_best_first(&mut visitor)
87 .map(|res| res.1 .1)
88 }
89
90 #[inline]
91 fn cast_local_ray_and_get_normal(
92 &self,
93 ray: &Ray,
94 max_time_of_impact: Real,
95 solid: bool,
96 ) -> Option<RayIntersection> {
97 let mut visitor = RayCompositeShapeToiAndNormalBestFirstVisitor::new(
98 self,
99 ray,
100 max_time_of_impact,
101 solid,
102 );
103
104 self.qbvh()
105 .traverse_best_first(&mut visitor)
106 .map(|(_, (_, res))| res)
107 }
108}
109
110pub struct RayCompositeShapeToiBestFirstVisitor<'a, S> {
115 shape: &'a S,
116 ray: &'a Ray,
117 simd_ray: SimdRay,
118 max_time_of_impact: Real,
119 solid: bool,
120}
121
122impl<'a, S> RayCompositeShapeToiBestFirstVisitor<'a, S> {
123 pub fn new(shape: &'a S, ray: &'a Ray, max_time_of_impact: Real, solid: bool) -> Self {
125 Self {
126 shape,
127 ray,
128 simd_ray: SimdRay::splat(*ray),
129 max_time_of_impact,
130 solid,
131 }
132 }
133}
134
135impl<S> SimdBestFirstVisitor<S::PartId, SimdAabb> for RayCompositeShapeToiBestFirstVisitor<'_, S>
136where
137 S: TypedSimdCompositeShape,
138{
139 type Result = (S::PartId, Real);
140
141 #[inline]
142 fn visit(
143 &mut self,
144 best: Real,
145 aabb: &SimdAabb,
146 data: Option<[Option<&S::PartId>; SIMD_WIDTH]>,
147 ) -> SimdBestFirstVisitStatus<Self::Result> {
148 let (hit, time_of_impact) =
149 aabb.cast_local_ray(&self.simd_ray, SimdReal::splat(self.max_time_of_impact));
150
151 if let Some(data) = data {
152 let mut weights = [0.0; SIMD_WIDTH];
153 let mut mask = [false; SIMD_WIDTH];
154 let mut results = [None; SIMD_WIDTH];
155
156 let better_toi = time_of_impact.simd_lt(SimdReal::splat(best));
157 let bitmask = (hit & better_toi).bitmask();
158
159 for ii in 0..SIMD_WIDTH {
160 if (bitmask & (1 << ii)) != 0 && data[ii].is_some() {
161 let part_id = *data[ii].unwrap();
162 self.shape
163 .map_typed_part_at(part_id, |part_pos, part_shape, _| {
164 let time_of_impact = if let Some(part_pos) = part_pos {
165 part_shape.cast_ray(
166 part_pos,
167 self.ray,
168 self.max_time_of_impact,
169 self.solid,
170 )
171 } else {
172 part_shape.cast_local_ray(
173 self.ray,
174 self.max_time_of_impact,
175 self.solid,
176 )
177 };
178 if let Some(time_of_impact) = time_of_impact {
179 results[ii] = Some((part_id, time_of_impact));
180 mask[ii] = true;
181 weights[ii] = time_of_impact;
182 }
183 })
184 }
185 }
186
187 SimdBestFirstVisitStatus::MaybeContinue {
188 weights: SimdReal::from(weights),
189 mask: SimdBool::from(mask),
190 results,
191 }
192 } else {
193 SimdBestFirstVisitStatus::MaybeContinue {
194 weights: time_of_impact,
195 mask: hit,
196 results: [None; SIMD_WIDTH],
197 }
198 }
199 }
200}
201
202pub struct RayCompositeShapeToiAndNormalBestFirstVisitor<'a, S> {
204 shape: &'a S,
205 ray: &'a Ray,
206 simd_ray: SimdRay,
207 max_time_of_impact: Real,
208 solid: bool,
209}
210
211impl<'a, S> RayCompositeShapeToiAndNormalBestFirstVisitor<'a, S> {
212 pub fn new(shape: &'a S, ray: &'a Ray, max_time_of_impact: Real, solid: bool) -> Self {
214 Self {
215 shape,
216 ray,
217 simd_ray: SimdRay::splat(*ray),
218 max_time_of_impact,
219 solid,
220 }
221 }
222}
223
224impl<S> SimdBestFirstVisitor<S::PartId, SimdAabb>
225 for RayCompositeShapeToiAndNormalBestFirstVisitor<'_, S>
226where
227 S: TypedSimdCompositeShape,
228{
229 type Result = (S::PartId, RayIntersection);
230
231 #[inline]
232 fn visit(
233 &mut self,
234 best: Real,
235 aabb: &SimdAabb,
236 data: Option<[Option<&S::PartId>; SIMD_WIDTH]>,
237 ) -> SimdBestFirstVisitStatus<Self::Result> {
238 let (hit, time_of_impact) =
239 aabb.cast_local_ray(&self.simd_ray, SimdReal::splat(self.max_time_of_impact));
240
241 if let Some(data) = data {
242 let mut weights = [0.0; SIMD_WIDTH];
243 let mut mask = [false; SIMD_WIDTH];
244 let mut results = [None; SIMD_WIDTH];
245
246 let better_toi = time_of_impact.simd_lt(SimdReal::splat(best));
247 let bitmask = (hit & better_toi).bitmask();
248
249 for ii in 0..SIMD_WIDTH {
250 if (bitmask & (1 << ii)) != 0 && data[ii].is_some() {
251 self.shape
252 .map_typed_part_at(*data[ii].unwrap(), |part_pos, part_shape, _| {
253 let result = if let Some(part_pos) = part_pos {
254 part_shape.cast_ray_and_get_normal(
255 part_pos,
256 self.ray,
257 self.max_time_of_impact,
258 self.solid,
259 )
260 } else {
261 part_shape.cast_local_ray_and_get_normal(
262 self.ray,
263 self.max_time_of_impact,
264 self.solid,
265 )
266 };
267
268 if let Some(result) = result {
269 results[ii] = Some((*data[ii].unwrap(), result));
270 mask[ii] = true;
271 weights[ii] = result.time_of_impact;
272 }
273 });
274 }
275 }
276
277 SimdBestFirstVisitStatus::MaybeContinue {
278 weights: SimdReal::from(weights),
279 mask: SimdBool::from(mask),
280 results,
281 }
282 } else {
283 SimdBestFirstVisitStatus::MaybeContinue {
284 weights: time_of_impact,
285 mask: hit,
286 results: [None; SIMD_WIDTH],
287 }
288 }
289 }
290}