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}