CNA-BUG-070: BoundingSphere::Contains(Vector3) and Contains(BoundingSphere) return different ContainmentType values from XNA 4.0
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
A point exactly on the surface is Intersects in CNA (XNA: Disjoint), and Contains(BoundingSphere) tests d^2 <= (R - r)^2, losing the sign of R - r, so a small sphere reports that it Contains a larger concentric sphere (XNA: Intersects).
- Identifier
CNA-BUG-070- Category
- Bug
- Subsystem
- Math & geometry
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Severity
- Medium (a triage suggestion, not a project priority)
- Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- Microsoft::Xna::Framework::BoundingSphere::Contains(Vector3), Contains(BoundingSphere) and their out-parameter overloads
Expected behaviour
XNA 4.0 (IL): Contains(Vector3) returns Contains when DistanceSquared(point, Center) < Radius*Radius and Disjoint otherwise - never Intersects. Contains(BoundingSphere) computes d = Distance(Center, sphere.Center); Disjoint if R + r < d, Contains if R - r >= d, otherwise Intersects.
Actual behaviour at TARGET
BoundingSphere.cpp returns Intersects for sqDistance == sqRadius in the point overload, where XNA 4.0 answers Disjoint (it returns Contains only for DistanceSquared < Radius*Radius). The sphere overload's Contains test is sqDistance <= (Radius - sphere.Radius) * (Radius - sphere.Radius): squaring discards the sign, so a radius-1 sphere asked about a concentric radius-5 sphere answers Contains. BoundingSphere::Contains(BoundingBox) and Contains(const BoundingFrustum&) use the point overload only as a “corner is not Disjoint” test, which counts a corner lying exactly on the surface as inside; XNA's own corner test (outside only when the squared distance exceeds Radius*Radius) does the same, so the surface difference does not change those two overloads' answers.
Source locations
modules/math/src/BoundingSphere.cpp— BoundingSphere::Contains(Vector3), Contains(BoundingSphere) and Contains(BoundingBox)modules/math/tests/Microsoft/Xna/Framework/BoundingSphereTests.cpp— ContainsPointOnBoundaryIsIntersects pins the CNA answer
Evidence
Checked by reading the named sources at 009d40f5; nothing was built or executed for this entry. XNA behaviour from the genuine XNA 4.0 IL (ContainmentType Disjoint=0, Contains=1, Intersects=2). The sphere overload's formula came from FNA.
Focused reproduction
// Illustrative; not compiled or run for this entry.
BoundingSphere small(Vector3::Zero, 1.0f), big(Vector3::Zero, 5.0f);
auto a = small.Contains(big); // XNA: Intersects CNA: Contains
auto b = small.Contains(Vector3(1, 0, 0)); // XNA: Disjoint CNA: Intersects
Current tests
BoundingSphereTests.cpp pins the non-XNA surface answer in ContainsPointOnBoundaryIsIntersects; no test checks a larger argument sphere.
Regression test
Change ContainsPointOnBoundaryIsIntersects to expect Disjoint, and add a test that a sphere does not Contain a larger concentric sphere.
Blast radius
Culling, trigger volumes and spatial partitioning that nest spheres; the enclosing-sphere case can make a container claim ownership of a larger object.
Workaround
Compare radii before calling Contains(BoundingSphere).
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Internals
- Math internals: bounding sphere
- Deep dives
- Bounding volumes: sphere
- Known issues
- Bug index