Skip to content

Commit 8edbb77

Browse files
authored
Optimize and fix sphere-OBB intersection logic (#23865)
# Objective - Optimize the Sphere and OOB intersection logic. - Fix a bug when the sphere and OBB shared the exact same center. ## Solution - The previous implementation calculated `v / d` (a normalized vector). If the distance `d` was zero (in a situation when centers are identical), this resulted in a division by zero. The new approach avoids normalization by refactoring the comparison. - Original: $d < r_1 + r_2$ where $r_2 =$ `relative_radius`$\Bigl(\frac{\mathbf{v}}{d}\Bigr)$ - New: $d^2 \leq r \cdot d + r_{\rm unscaled}$ where $r_{\rm unscaled} =$ `relative_radius`$(\mathbf{v})$ ## Testing - Added new test cases covering identical centers, edge contacts, and zero-extent volumes. These tests fail on main but pass with this PR. - Verified performance via the newly added camera benchmarks in #23863 ``` intersects_obb/sphere_intersects_obb time: [5.3677 ns 5.3932 ns 5.4227 ns] change: [−19.462% −18.945% −18.426%] (p = 0.00 < 0.05) Performance has improved. ```
1 parent e9bfe1e commit 8edbb77

1 file changed

Lines changed: 78 additions & 3 deletions

File tree

crates/bevy_camera/src/primitives.rs

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,10 @@ impl Sphere {
218218
pub fn intersects_obb(&self, aabb: &Aabb, world_from_local: &Affine3A) -> bool {
219219
let aabb_center_world = world_from_local.transform_point3a(aabb.center);
220220
let v = aabb_center_world - self.center;
221-
let d = v.length();
222-
let relative_radius = aabb.relative_radius(&(v / d), &world_from_local.matrix3);
223-
d < self.radius + relative_radius
221+
let d_sq = v.length_squared();
222+
let d = d_sq.sqrt();
223+
let relative_radius_unscaled = aabb.relative_radius(&v, &world_from_local.matrix3);
224+
d_sq <= self.radius * d + relative_radius_unscaled
224225
}
225226
}
226227

@@ -608,6 +609,80 @@ mod tests {
608609
assert!(frustum.intersects_sphere(&sphere, true));
609610
}
610611

612+
#[test]
613+
fn sphere_intersects_obb_identical_center() {
614+
let sphere_at_origin = Sphere {
615+
center: Vec3A::ZERO,
616+
radius: 1.0,
617+
};
618+
let aabb_at_origin = Aabb {
619+
center: Vec3A::ZERO,
620+
half_extents: Vec3A::splat(0.5),
621+
};
622+
assert!(
623+
sphere_at_origin.intersects_obb(&aabb_at_origin, &Affine3A::IDENTITY),
624+
"Should intersect when centers are exactly identical"
625+
);
626+
}
627+
628+
#[test]
629+
fn sphere_intersects_obb_at_edge() {
630+
// Zero-radius sphere (a point) exactly on the edge of an OBB
631+
let point_sphere = Sphere {
632+
center: Vec3A::new(1.0, 0.0, 0.0),
633+
radius: 0.0,
634+
};
635+
let aabb = Aabb {
636+
center: Vec3A::ZERO,
637+
half_extents: Vec3A::X, // Width of 1, height/depth 0
638+
};
639+
assert!(
640+
point_sphere.intersects_obb(&aabb, &Affine3A::IDENTITY),
641+
"Zero radius sphere (point) on the boundary should count as an intersection"
642+
);
643+
}
644+
645+
#[test]
646+
fn sphere_intersects_obb_zero_extents_inside() {
647+
// OBB with zero extents (a point) inside a sphere
648+
let sphere = Sphere {
649+
center: Vec3A::ZERO,
650+
radius: 10.0,
651+
};
652+
let point_aabb = Aabb {
653+
center: Vec3A::new(1.0, 1.0, 1.0),
654+
half_extents: Vec3A::ZERO,
655+
};
656+
assert!(
657+
sphere.intersects_obb(&point_aabb, &Affine3A::IDENTITY),
658+
"Sphere should intersect an AABB with zero extents if the point is inside"
659+
);
660+
}
661+
662+
#[test]
663+
fn sphere_intersects_obb_rotated_zeros() {
664+
// Rotated zero-extent OBB
665+
let sphere = Sphere {
666+
center: Vec3A::new(5.0, 0.0, 0.0),
667+
radius: 1.0,
668+
};
669+
let point_aabb = Aabb {
670+
center: Vec3A::ZERO,
671+
half_extents: Vec3A::ZERO,
672+
};
673+
674+
// Rotate and translate the "point" OBB so it sits inside the sphere
675+
let transform = Affine3A::from_rotation_translation(
676+
Quat::from_rotation_y(PI),
677+
Vec3::new(5.0, 0.0, 0.0),
678+
);
679+
680+
assert!(
681+
sphere.intersects_obb(&point_aabb, &transform),
682+
"Should intersect rotated point OBB"
683+
);
684+
}
685+
611686
#[test]
612687
fn aabb_enclosing() {
613688
assert_eq!(Aabb::enclosing([] as [Vec3; 0]), None);

0 commit comments

Comments
 (0)