fix(physics): #90 — sphere-overlap cell stickiness at doorway threshold
ResolveCellId's indoor-seed fall-through was point-only: when the indoor BSP push-back moved the foot-sphere CENTER a few cm outside the indoor CellBSP volume, the resolver flipped CellId back to outdoor. Next tick re-promoted via CheckBuildingTransit. The ping-pong caused most ticks to be classified outdoor, bypassing indoor BSP wall checks entirely and producing the user-reported "walls walk through everywhere in the inn" symptom. Fix: port retail's BSPTREE::sphere_intersects_cell_bsp (acclient_2013_pseudo_c.txt:323267 → BSPNODE variant at :325546) as BSPQuery.SphereIntersectsCellBsp(node, center, radius). Replace the point-only check at PhysicsEngine.ResolveCellId:285 with the radius- aware overlap test. Player stays classified indoor as long as ANY part of the foot-sphere still overlaps the indoor cell volume; only flips to outdoor when the sphere is FULLY outside. Retail uses a 0.01 m epsilon on the radius (acclient :325551); ported verbatim. 8 new unit tests cover null/leaf/inside/on-plane/straddling/ fully-outside/tangent-boundary cases plus a regression-anchor test that proves the old PointInsideCellBsp would have returned false for the same straddling input. 1147 + 8 baseline maintained (was 1139 + 8 before #90 fix). Closes #90. A4 multi-cell iteration (shipped earlier today) should now actually exercise in production since the player can stably remain in indoor cells. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1534990102
commit
4ca35966f8
3 changed files with 172 additions and 3 deletions
|
|
@ -281,12 +281,26 @@ public sealed class PhysicsEngine
|
|||
if (indoorCell?.CellBSP?.Root is null)
|
||||
return indoorResult; // Can't verify (no CellBSP); trust FindCellList.
|
||||
|
||||
// Issue #90 fix (2026-05-20): use SPHERE-overlap instead of POINT-in
|
||||
// for the indoor verification. The previous point-only check caused
|
||||
// a per-frame ping-pong at the inn doorway: indoor BSP push-back
|
||||
// moved the sphere CENTER a few cm outside the indoor CellBSP
|
||||
// volume → point-only check returned false → fell through to outdoor
|
||||
// → next tick re-promoted to indoor → wall hit → push-back →
|
||||
// outdoor → repeat. Net visual behavior: "walls walk through"
|
||||
// because outdoor ticks bypass indoor BSP entirely. With sphere-
|
||||
// overlap, the player stays classified indoor as long as ANY part
|
||||
// of the foot sphere still overlaps the indoor cell volume.
|
||||
//
|
||||
// Retail oracle: CCellStruct::sphere_intersects_cell at
|
||||
// acclient_2013_pseudo_c.txt:317666 →
|
||||
// BSPTREE::sphere_intersects_cell_bsp at :323267.
|
||||
var localCenter = Vector3.Transform(worldPos, indoorCell.InverseWorldTransform);
|
||||
if (BSPQuery.PointInsideCellBsp(indoorCell.CellBSP.Root, localCenter))
|
||||
if (BSPQuery.SphereIntersectsCellBsp(indoorCell.CellBSP.Root, localCenter, sphereRadius))
|
||||
return indoorResult;
|
||||
|
||||
// Fall through to outdoor resolution: player has left the indoor
|
||||
// portal-connected graph entirely.
|
||||
// Fall through to outdoor resolution: player has FULLY left the
|
||||
// indoor portal-connected graph (sphere no longer overlaps).
|
||||
}
|
||||
|
||||
// Outdoor seed: use terrain grid to compute the prefixed cell id.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue