CNA-BUG-069: Ray::Intersects(BoundingBox) and Ray::Intersects(BoundingSphere) differ from XNA 4.0 on degenerate and boundary rays
Evidence basis: source-verified at the pinned commit; executed for this entry (the Evidence section names exactly what was run); 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 zero-direction ray inside a box returns no hit (XNA: 0), axis parallelism uses a 2^-24 threshold where XNA uses 1e-6, and a ray starting exactly on a sphere and pointing outward returns no hit (XNA: 0, because XNA's inside test is <=).
- Identifier
CNA-BUG-069- Category
- Bug
- Subsystem
- Math & geometry
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Severity
- Low (a triage suggestion, not a project priority)
- Evidence basis
- Reproduced: executed for this entry (the Evidence section names exactly what was run)
- Tests touching this area
- Yes: see Current tests
- Affected contract
- Microsoft::Xna::Framework::Ray::Intersects(BoundingBox) and Ray::Intersects(BoundingSphere) (and the out-parameter overloads)
Expected behaviour
XNA 4.0 (IL of BoundingBox.Intersects(ref Ray, out float?)): start with num = 0, treat an axis as parallel when Math.Abs(dir) < 1E-06, otherwise multiply by 1f / dir; a ray whose three axes are all parallel and whose origin is inside returns 0. Ray.Intersects(ref BoundingSphere, out float?) returns 0 when the squared centre distance is <= the squared radius.
Actual behaviour at TARGET
Ray.cpp uses FNA's slab algorithm: MathHelper::WithinEpsilon(dir, 0) (machine-epsilon threshold), division by the direction component, and an optional tMin that is never set when all three axes are parallel, so the zero-direction inside case returns std::nullopt. The sphere overload returns 0 only for differenceLengthSquared < sphereRadiusSquared; on the surface it falls through, and an outward direction gives a negative dot product and no hit.
Source locations
modules/math/src/Ray.cpp— Ray::Intersects(BoundingBox) slab test and Ray::Intersects(const BoundingSphere&, std::optional<float>&)modules/math/src/MathHelper.cpp— MathHelper::WithinEpsilon (MachineEpsilonFloat threshold)modules/math/tests/Microsoft/Xna/Framework/RayTests.cpp— existing ray tests
Evidence
Checked by reading the named sources at 009d40f5; the audit's own executed comparison follows below. XNA behaviour from the genuine XNA 4.0 IL (constants 1E-06 and 0, comparison opcodes).
Independent re-verification: Reproduced by the audit at 009d40f5. The TARGET math sources were compiled with g++ -O0 and compared with float32 models of the decompiled XNA 4.0 IL. A zero-direction ray at a box centre returned nullopt (XNA 0); a ray starting on a unit sphere and pointing outward returned nullopt (XNA 0); a ray from (-5,0,0) with direction (5e-7,0,0) toward a unit box returned t=8e6 (XNA: no hit, because XNA treats a component below 1e-6 as parallel). On 2,000,000 random rays hit and miss outcomes never disagreed, but about 2% of box results differed by 1 ulp and about 1% of sphere results differed, by up to 0.2% for near-tangent rays, because the operations are ordered differently. The sharp-runtime types came from a sibling checkout that TARGET does not pin.
Focused reproduction
// Illustrative; the audit's executed probe is described under Evidence.
BoundingBox box(Vector3(-1, -1, -1), Vector3(1, 1, 1));
Ray still(Vector3::Zero, Vector3::Zero);
auto a = still.Intersects(box); // XNA: 0 CNA: nullopt
BoundingSphere s(Vector3::Zero, 1.0f);
Ray out(Vector3(1, 0, 0), Vector3(1, 0, 0));
auto b = out.Intersects(s); // XNA: 0 CNA: nullopt
Current tests
RayTests.cpp covers ordinary hits and misses, not these boundary cases.
Regression test
RayTests cases for a zero-direction ray inside a box, a direction component of 5e-7 on one axis, and an outward ray starting on a sphere surface, each asserting XNA's answer.
Blast radius
Picking and collision code that starts rays on or inside volumes; normal rays are unaffected in hit-or-miss outcome (their distances can differ from XNA's in the last bit).
Workaround
Nudge ray origins off surfaces, or test containment before intersecting.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Internals
- Math internals: geometry
- Deep dives
- Bounding volumes: containment matrix
- Known issues
- Bug index