parry3d/partitioning/bvh/bvh_queries.rs
1use super::{Bvh, BvhNode};
2use crate::bounding_volume::{Aabb, BoundingVolume};
3use crate::math::Point;
4use crate::math::Real;
5use crate::query::PointProjection;
6use crate::query::{PointQuery, Ray};
7
8#[cfg(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32"))]
9pub(super) struct SimdInvRay {
10 // TODO: we need to use `glam` here instead of `wide` because `wide` is lacking
11 // operations for getting the min/max vector element.
12 // We can switch back to `wide` once it's supported.
13 pub origin: glam::Vec3A,
14 pub inv_dir: glam::Vec3A,
15}
16
17#[cfg(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32"))]
18impl From<Ray> for SimdInvRay {
19 fn from(ray: Ray) -> Self {
20 let inv_dir = ray.dir.map(|r| {
21 if r.abs() < Real::EPSILON {
22 r.signum() / Real::EPSILON
23 } else {
24 1.0 / r
25 }
26 });
27 Self {
28 origin: glam::Vec3A::from([ray.origin.x, ray.origin.y, ray.origin.z]),
29 inv_dir: glam::Vec3A::from([inv_dir.x, inv_dir.y, inv_dir.z]),
30 }
31 }
32}
33
34impl Bvh {
35 /// Iterates through all the leaves with an AABB intersecting the given `aabb`.
36 pub fn intersect_aabb<'a>(&'a self, aabb: &'a Aabb) -> impl Iterator<Item = u32> + 'a {
37 self.leaves(|node: &BvhNode| node.aabb().intersects(aabb))
38 }
39
40 /// Projects a point on this BVH using the provided leaf projection function.
41 ///
42 /// The `primitive_check` delegates the point-projection task to an external function that
43 /// is assumed to map a leaf index to an actual geometry to project on. The `Real` argument
44 /// given to that closure is the distance to the closest point found so far (or is equal to
45 /// `max_distance` if no projection was found so far).
46 pub fn project_point(
47 &self,
48 point: &Point<Real>,
49 max_distance: Real,
50 primitive_check: impl Fn(u32, Real) -> Option<PointProjection>,
51 ) -> Option<(u32, (Real, PointProjection))> {
52 self.find_best(
53 max_distance,
54 |node: &BvhNode, _| node.aabb().distance_to_local_point(point, true),
55 |primitive, _| {
56 let proj = primitive_check(primitive, max_distance)?;
57 Some((na::distance(&proj.point, point), proj))
58 },
59 )
60 }
61
62 /// Casts a ray on this BVH using the provided leaf ray-cast function.
63 ///
64 /// The `primitive_check` delegates the ray-casting task to an external function that
65 /// is assumed to map a leaf index to an actual geometry to cast a ray on. The `Real` argument
66 /// given to that closure is the distance to the closest ray hit found so far (or is equal to
67 /// `max_time_of_impact` if no projection was found so far).
68 #[cfg(not(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32")))]
69 pub fn cast_ray(
70 &self,
71 ray: &Ray,
72 max_time_of_impact: Real,
73 primitive_check: impl Fn(u32, Real) -> Option<Real>,
74 ) -> Option<(u32, Real)> {
75 self.find_best(
76 max_time_of_impact,
77 |node: &BvhNode, best_so_far| node.cast_ray(ray, best_so_far),
78 primitive_check,
79 )
80 }
81
82 /// Casts a ray on this BVH using the provided leaf ray-cast function.
83 ///
84 /// The `primitive_check` delegates the ray-casting task to an external function that
85 /// is assumed to map a leaf index to an actual geometry to cast a ray on. The `Real` argument
86 /// given to that closure is the distance to the closest ray hit found so far (or is equal to
87 /// `max_time_of_impact` if no projection was found so far).
88 #[cfg(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32"))]
89 pub fn cast_ray(
90 &self,
91 ray: &Ray,
92 max_time_of_impact: Real,
93 primitive_check: impl Fn(u32, Real) -> Option<Real>,
94 ) -> Option<(u32, Real)> {
95 // The commented code below relies on depth-first traversal instead of
96 // depth first. Interesting to compare both approaches (the best-first is
97 // expected to be consistently faster though).
98 //
99 // let simd_inv_ray = SimdInvRay::from(*ray);
100 // let mut best_primitive = u32::MAX;
101 // let mut best_toi = Real::MAX;
102 // self.traverse(|node: &BvhNode| {
103 // if node.cast_inv_ray_simd(&simd_inv_ray) >= best_toi {
104 // return TraversalAction::Prune;
105 // }
106 //
107 // if let Some(primitive) = node.leaf_data() {
108 // let toi = primitive_check(primitive as u32, best_toi);
109 // if toi < best_toi {
110 // best_primitive = primitive as u32;
111 // best_toi = toi;
112 // }
113 // }
114 //
115 // TraversalAction::Continue
116 // });
117 // (best_primitive, best_toi)
118
119 let simd_inv_ray = SimdInvRay::from(*ray);
120 self.find_best(
121 max_time_of_impact,
122 |node: &BvhNode, _best_so_far| node.cast_inv_ray_simd(&simd_inv_ray),
123 |primitive, best_so_far| primitive_check(primitive, best_so_far),
124 )
125 }
126}