1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
use super::{Bvh, BvhNode};
use crate::bounding_volume::{Aabb, BoundingVolume};
use crate::math::Real;
use crate::math::Vector;
use crate::query::PointProjection;
use crate::query::{PointQuery, Ray};
use crate::shape::FeatureId;
#[cfg(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32"))]
pub(super) struct SimdInvRay {
// TODO: we need to use `glam` here instead of `wide` because `wide` is lacking
// operations for getting the min/max vector element.
// We can switch back to `wide` once it's supported.
pub origin: glamx::Vec3A,
pub inv_dir: glamx::Vec3A,
}
#[cfg(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32"))]
impl From<Ray> for SimdInvRay {
fn from(ray: Ray) -> Self {
let inv_dir = ray.dir.map(|r| {
if r.abs() < Real::EPSILON {
r.signum() / Real::EPSILON
} else {
1.0 / r
}
});
Self {
origin: glamx::Vec3A::from([ray.origin.x, ray.origin.y, ray.origin.z]),
inv_dir: glamx::Vec3A::from([inv_dir.x, inv_dir.y, inv_dir.z]),
}
}
}
impl Bvh {
/// Returns an iterator over all leaves whose AABBs intersect the given AABB.
///
/// This efficiently finds all objects in the scene that potentially overlap with a
/// query region. It's commonly used for:
/// - Finding objects in a specific area
/// - Broad-phase collision detection (find candidate pairs)
/// - Spatial queries (what's near this position?)
/// - Frustum culling (what's in the camera's view?)
///
/// The iterator returns leaf indices corresponding to objects that were added to the BVH.
///
/// # Arguments
///
/// * `aabb` - The axis-aligned bounding box to test for intersections
///
/// # Returns
///
/// An iterator yielding `u32` leaf indices for all leaves whose AABBs intersect the
/// query AABB. The iteration order is depth-first through the tree.
///
/// # Performance
///
/// - **Time**: O(log n) average case with good spatial locality
/// - **Worst case**: O(n) if the query AABB is very large
/// - Efficiently prunes entire subtrees that don't intersect
/// - Zero allocations
///
/// # Examples
///
/// ## Find objects in a region
///
/// ```
/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
/// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
/// use parry3d::bounding_volume::Aabb;
/// use parry3d::math::Vector;
///
/// // Create a scene with objects
/// let objects = vec![
/// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
/// Aabb::new(Vector::new(5.0, 0.0, 0.0), Vector::new(6.0, 1.0, 1.0)),
/// Aabb::new(Vector::new(10.0, 0.0, 0.0), Vector::new(11.0, 1.0, 1.0)),
/// ];
///
/// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &objects);
///
/// // Query: which objects are near the origin?
/// let query_region = Aabb::new(
/// Vector::new(-1.0, -1.0, -1.0),
/// Vector::new(2.0, 2.0, 2.0)
/// );
///
/// for leaf_id in bvh.intersect_aabb(&query_region) {
/// println!("Object {} is in the query region", leaf_id);
/// // You can now test the actual geometry of objects[leaf_id]
/// }
/// # }
/// ```
///
/// ## Broad-phase collision detection
///
/// ```
/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
/// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
/// use parry3d::bounding_volume::Aabb;
/// use parry3d::math::Vector;
///
/// let objects = vec![
/// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
/// Aabb::new(Vector::new(0.5, 0.0, 0.0), Vector::new(1.5, 1.0, 1.0)), // Overlaps first
/// Aabb::new(Vector::new(10.0, 0.0, 0.0), Vector::new(11.0, 1.0, 1.0)), // Far away
/// ];
///
/// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &objects);
///
/// // Find potential collision pairs
/// let mut collision_pairs = Vec::new();
///
/// for (i, aabb) in objects.iter().enumerate() {
/// for leaf_id in bvh.intersect_aabb(aabb) {
/// if leaf_id as usize > i { // Avoid duplicate pairs
/// collision_pairs.push((i as u32, leaf_id));
/// }
/// }
/// }
///
/// // collision_pairs now contains potential collision pairs
/// // Next step: narrow-phase test actual geometry
/// # }
/// ```
///
/// ## Frustum culling for rendering
///
/// ```
/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
/// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
/// use parry3d::bounding_volume::Aabb;
/// use parry3d::math::Vector;
///
/// let scene_objects = vec![
/// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
/// Aabb::new(Vector::new(2.0, 0.0, 0.0), Vector::new(3.0, 1.0, 1.0)),
/// Aabb::new(Vector::new(100.0, 0.0, 0.0), Vector::new(101.0, 1.0, 1.0)), // Far away
/// ];
///
/// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &scene_objects);
///
/// // Camera frustum as an AABB (simplified - real frustums are more complex)
/// let camera_frustum = Aabb::new(
/// Vector::new(-10.0, -10.0, -10.0),
/// Vector::new(10.0, 10.0, 10.0)
/// );
///
/// let mut visible_objects = Vec::new();
/// for leaf_id in bvh.intersect_aabb(&camera_frustum) {
/// visible_objects.push(leaf_id);
/// }
///
/// // Only render visible_objects - massive performance win!
/// assert_eq!(visible_objects.len(), 2); // Far object culled
/// # }
/// ```
///
/// ## Finding neighbors for an object
///
/// ```
/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
/// use parry3d::partitioning::{Bvh, BvhBuildStrategy};
/// use parry3d::bounding_volume::{Aabb, BoundingVolume};
/// use parry3d::math::Vector;
///
/// let objects = vec![
/// Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)),
/// Aabb::new(Vector::new(1.5, 0.0, 0.0), Vector::new(2.5, 1.0, 1.0)),
/// Aabb::new(Vector::new(100.0, 0.0, 0.0), Vector::new(101.0, 1.0, 1.0)),
/// ];
///
/// let bvh = Bvh::from_leaves(BvhBuildStrategy::default(), &objects);
///
/// // Expand an object's AABB to find nearby objects
/// let search_radius = 2.0;
/// let expanded = objects[0].loosened(search_radius);
///
/// let mut neighbors = Vec::new();
/// for leaf_id in bvh.intersect_aabb(&expanded) {
/// if leaf_id != 0 { // Don't include the object itself
/// neighbors.push(leaf_id);
/// }
/// }
///
/// // neighbors contains objects within search_radius of object 0
/// # }
/// ```
///
/// # Notes
///
/// - This only tests AABB-AABB intersection, which is conservative (may have false positives)
/// - For exact collision detection, test actual geometry after getting candidates
/// - The iterator is lazy - work is only done as you consume items
/// - Empty queries (no intersections) are very fast due to early pruning
///
/// # See Also
///
/// - [`cast_ray`](Self::cast_ray) - Ray casting queries
/// - [`project_point`](Self::project_point) - Find closest point
/// - [`traverse`](Self::traverse) - Custom traversal logic
/// - [`leaves`](Self::leaves) - General leaf iteration with predicate
pub fn intersect_aabb<'a>(&'a self, aabb: &'a Aabb) -> impl Iterator<Item = u32> + 'a {
self.leaves(|node: &BvhNode| node.aabb().intersects(aabb))
}
/// Projects a point on this BVH using the provided leaf projection function.
///
/// The `primitive_check` delegates the point-projection task to an external function that
/// is assumed to map a leaf index to an actual geometry to project on. The `Real` argument
/// given to that closure is the distance to the closest point found so far (or is equal to
/// `max_distance` if no projection was found so far).
pub fn project_point(
&self,
point: Vector,
max_distance: Real,
primitive_check: impl Fn(u32, Real) -> Option<PointProjection>,
) -> Option<(u32, (Real, PointProjection))> {
self.find_best(
max_distance,
|node: &BvhNode, _| node.aabb().distance_to_local_point(point, true),
|primitive, _| {
let proj = primitive_check(primitive, max_distance)?;
Some((proj.point.distance(point), proj))
},
)
}
/// Projects a point on this BVH using the provided leaf projection function.
///
/// Also returns the feature the point was projected on.
///
/// The `primitive_check` delegates the point-projection task to an external function that
/// is assumed to map a leaf index to an actual geometry to project on. The `Real` argument
/// given to that closure is the distance to the closest point found so far (or is equal to
/// `max_distance` if no projection was found so far).
pub fn project_point_and_get_feature(
&self,
point: Vector,
max_distance: Real,
primitive_check: impl Fn(u32, Real) -> Option<(PointProjection, FeatureId)>,
) -> Option<(u32, (Real, (PointProjection, FeatureId)))> {
self.find_best(
max_distance,
|node: &BvhNode, _| node.aabb().distance_to_local_point(point, true),
|primitive, _| {
let proj = primitive_check(primitive, max_distance)?;
Some((proj.0.point.distance(point), proj))
},
)
}
/// Casts a ray on this BVH using the provided leaf ray-cast function.
///
/// The `primitive_check` delegates the ray-casting task to an external function that
/// is assumed to map a leaf index to an actual geometry to cast a ray on. The `Real` argument
/// given to that closure is the distance to the closest ray hit found so far (or is equal to
/// `max_time_of_impact` if no projection was found so far).
#[cfg(not(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32")))]
pub fn cast_ray(
&self,
ray: &Ray,
max_time_of_impact: Real,
primitive_check: impl Fn(u32, Real) -> Option<Real>,
) -> Option<(u32, Real)> {
self.find_best(
max_time_of_impact,
|node: &BvhNode, best_so_far| node.cast_ray(ray, best_so_far),
primitive_check,
)
}
/// Casts a ray on this BVH using the provided leaf ray-cast function.
///
/// The `primitive_check` delegates the ray-casting task to an external function that
/// is assumed to map a leaf index to an actual geometry to cast a ray on. The `Real` argument
/// given to that closure is the distance to the closest ray hit found so far (or is equal to
/// `max_time_of_impact` if no projection was found so far).
#[cfg(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32"))]
pub fn cast_ray(
&self,
ray: &Ray,
max_time_of_impact: Real,
primitive_check: impl Fn(u32, Real) -> Option<Real>,
) -> Option<(u32, Real)> {
// The commented code below relies on depth-first traversal instead of
// depth first. Interesting to compare both approaches (the best-first is
// expected to be consistently faster though).
//
// let simd_inv_ray = SimdInvRay::from(*ray);
// let mut best_primitive = u32::MAX;
// let mut best_toi = Real::MAX;
// self.traverse(|node: &BvhNode| {
// if node.cast_inv_ray_simd(&simd_inv_ray) >= best_toi {
// return TraversalAction::Prune;
// }
//
// if let Some(primitive) = node.leaf_data() {
// let toi = primitive_check(primitive as u32, best_toi);
// if toi < best_toi {
// best_primitive = primitive as u32;
// best_toi = toi;
// }
// }
//
// TraversalAction::Continue
// });
// (best_primitive, best_toi)
let simd_inv_ray = SimdInvRay::from(*ray);
self.find_best(
max_time_of_impact,
|node: &BvhNode, _best_so_far| node.cast_inv_ray_simd(&simd_inv_ray),
|primitive, best_so_far| primitive_check(primitive, best_so_far),
)
}
}