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