fix(test): correct geometric pin test for door slab Z math
The Geometric_DoorSlabZRange_AbovePlayerSphereTop test was computing slabWorldZBottom as (entity.Z + partFrame.Z) — assuming the slab's local Z=0 was its bottom. Actually checking the dat shows the slab's PhysicsPolygons local AABB is min=(-0.954, -0.134, -1.236) max=(0.971, 0.127, 1.255) — the slab's local origin is at its GEOMETRIC CENTER, not the bottom. With partFrame.Z=1.275 lifting the origin, the slab world Z is actually [94.139, 96.630], not [95.375, 97.865]. Corrected test now computes both slabLocalZMin and slabLocalZMax from the polygon vertices and asserts the opposite (correct) geometric fact: the slab IS at sphere height — overlap from Z=94.139 to Z=95.20 (1.061 m of vertical overlap with the player's sphere). The slab is NOT a lintel that misses the sphere; it should collide. Test renamed: Geometric_DoorSlabZRange_AbovePlayerSphereTop → Geometric_DoorSlabAtSphereHeight_OverlapsInZ. Handoff doc 2026-05-25-door-bug-partial-fix-shipped.md updated with the corrected analysis. The "next investigation candidates" list now points toward cdb attach to retail as the highest-ROI option, since the BSP collision IS active at sphere height but production still shows asymmetric walkthrough behavior. The bug is in either the GetNearbyObjects coverage at primary-cell boundaries, the BSP polygon partial-overlap handling, or missing cell-BSP collision for cottage doorway walls. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c27fded61e
commit
85a164f4a8
2 changed files with 125 additions and 129 deletions
|
|
@ -106,90 +106,90 @@ The `[bsp-test]` probe fires 245 times for the door entity during the
|
|||
post-fix inside-out attempts — door IS being queried. The
|
||||
collision-detection mechanics produce the wrong response.
|
||||
|
||||
## What's next (separate bug — REFRAMED 2026-05-25 evening)
|
||||
## What's next (separate bug)
|
||||
|
||||
**Initial hypothesis was wrong.** Two new directional tests built
|
||||
post-fix (`Directional_OutsideIn_*`, `Directional_InsideOut_*`) BOTH
|
||||
PASS — the BSP collision response is symmetric at unit-test level.
|
||||
The asymmetric production bug must come from something the unit tests
|
||||
weren't capturing.
|
||||
**Investigation status (corrected 2026-05-25 late evening).** Two new
|
||||
directional tests + a geometric pin test all PASS:
|
||||
|
||||
A geometric pin test (`Geometric_DoorSlabZRange_AbovePlayerSphereTop`)
|
||||
reveals the real story:
|
||||
- `Directional_OutsideIn_SouthApproach_BlocksAtSlabSouthFace` PASSES.
|
||||
- `Directional_InsideOut_NorthApproach_BlocksAtSlabNorthFace` PASSES.
|
||||
- `Geometric_DoorSlabAtSphereHeight_OverlapsInZ` PASSES.
|
||||
|
||||
The geometric test reveals (correctly computed this time):
|
||||
|
||||
```
|
||||
Setup 0x020019FF (cottage door):
|
||||
CylSphere[0]: r=0.10, h=0.20, origin=(0, 0, 0.018)
|
||||
→ world Z [94.118, 94.318] when entity at Z=94.1
|
||||
Part 0 (GfxObj 0x010044B5, the BSP "slab"):
|
||||
placement frame [Default][0].Origin = (-0.006, 0.125, 1.275)
|
||||
→ BSP world Z [95.375, 97.865] when entity at Z=94.1
|
||||
Setup 0x020019FF (cottage door) PhysicsPolygons local AABB:
|
||||
min=(-0.954, -0.134, -1.236) max=(0.971, 0.127, 1.255)
|
||||
(slab origin at GEOMETRIC CENTER, not the bottom)
|
||||
|
||||
Player at floor Z=94:
|
||||
sphere height = 1.20, sphere top = 95.20
|
||||
partFrame[0].Origin = (-0.006, 0.125, 1.275) → lifts slab origin
|
||||
1.275 m above entity Z
|
||||
|
||||
BSP slab BOTTOM (95.375) is ABOVE sphere TOP (95.20) by 0.175 m.
|
||||
The slab NEVER collides with the player's body sphere.
|
||||
With entity at world (132.6, 17.1, 94.1) + 180° entity rotation:
|
||||
partWorldPos = (132.606, 16.975, 95.375)
|
||||
|
||||
Slab WORLD AABB:
|
||||
X: [131.635, 133.560] (1.925 m wide)
|
||||
Y: [16.848, 17.109] (0.261 m thick)
|
||||
Z: [94.139, 96.630] (2.491 m tall, bottom JUST above floor)
|
||||
|
||||
Player sphere at foot Z=94:
|
||||
Z: [94, 95.20]
|
||||
|
||||
Slab DOES overlap sphere in Z (overlap Z=[94.139, 95.20] = 1.061 m).
|
||||
```
|
||||
|
||||
The slab is a LINTEL (the door frame above the doorway), not a leaf.
|
||||
The door's only collision against a player at floor level is the
|
||||
0.10 m radius foot cylinder. Sphere radius 0.48 + cyl 0.10 = 0.58 m
|
||||
collision reach. Any sphere center > 0.58 m from cylinder center
|
||||
(132.6, 17.1) passes freely.
|
||||
**The slab IS at sphere height — it should collide.** Both directional
|
||||
tests prove BSP collision response is symmetric for sphere-to-slab
|
||||
approach. Yet production shows asymmetric inside-out walkthrough at
|
||||
off-center positions. The bug must be in one of:
|
||||
|
||||
The user-reported "inside-out walkthrough at ~50 cm off-center" is
|
||||
the sphere walking AROUND the cylinder, at X = 132.6 ± 0.6+ m where
|
||||
collision misses entirely. "Body partially intersects door" is the
|
||||
character model occupying the visual door's volume while the collision
|
||||
sphere passes beside the foot cylinder.
|
||||
1. **The portal-reachable cells from indoor cell 0x0150 still miss the
|
||||
door's shadow at certain sphere positions**, despite the
|
||||
AddAllOutsideCells fix. The user's walkthrough at X=133.655 (1.05 m
|
||||
east of door center) puts the sphere mostly east of slab X range
|
||||
[131.635, 133.560]. The sphere's WEST edge (X=133.175) is barely
|
||||
inside the slab. If GetNearbyObjects's outdoor radial sweep uses
|
||||
sphere center XY for cell lookup, it computes
|
||||
gridX = (int)(133.655 / 24) = 5 → cell 0xA9B40029. But AddAllOutsideCells
|
||||
only adds cells based on the sphere's PRIMARY position. The east-cell
|
||||
neighbor might not be added if the sphere is wholly within the primary
|
||||
cell's grid XY. Worth verifying.
|
||||
|
||||
The "outside-in works" case is also the foot cylinder doing the
|
||||
blocking — when the user approaches more centered or hits the cylinder
|
||||
head-on. The cell-visibility fix made the cylinder visible from indoor
|
||||
cells (it wasn't before), which is why outside-in went from "walks
|
||||
through" to "blocks" — but the cylinder is the actual collider.
|
||||
2. **The BSP polygon-level test for partial-overlap geometry.** Sphere
|
||||
half-east-of-slab, sphere south edge at slab north edge, moving +Y:
|
||||
sphere is on the verge of leaving the slab volume. BSPQuery's polygon
|
||||
intersection might consider this a "leaving collision" with no
|
||||
response, even though the sphere body still partially occupies the
|
||||
slab volume. Retail might handle this as "depenetration push" to
|
||||
resolve the overlap.
|
||||
|
||||
**This is a door-geometry interpretation question, NOT a BSP query bug.**
|
||||
Three candidate next-step investigations:
|
||||
3. **Cell BSP (cell 0x0150's PhysicsPolygons) is missing**. The doorway
|
||||
alcove cell has 4 physics polygons — likely walls + floor. If retail
|
||||
relies on the cell's walls to catch sphere-vs-doorway-side-wall
|
||||
collisions (in addition to the door slab), and we're not loading /
|
||||
testing the cell BSP correctly for the player's foot at sphere
|
||||
height, the side walls would miss.
|
||||
|
||||
1. **Retail-faithfulness audit on door collision.** Read retail's
|
||||
`CPhysicsObj::set_setup_for_door` (or similar) to determine what
|
||||
shapes retail loads for a closed cottage door. If retail uses the
|
||||
SAME setup data + finds the same shapes we do, the door geometry
|
||||
in the dat IS the spec. The "blocking" the user sees in retail
|
||||
might similarly be the foot cylinder + perhaps a different default
|
||||
sphere from setup.Radius/setup.Height that we're not registering.
|
||||
Three candidate investigations, ranked by ROI:
|
||||
|
||||
2. **Inspect door parts 1+2 (GfxObj 0x010044B6).** Per prior session's
|
||||
handoff they are visual-only (HasPhysics=false). Verify by direct
|
||||
dat read — maybe the PhysicsBSP is null but the cylinder/sphere
|
||||
list on the GfxObj itself has collision data we're missing.
|
||||
`DoorSetupGfxObjInspectionTests` already prints this; re-read.
|
||||
**A. cdb attach to retail** at a Holtburg cottage doorway. Break on
|
||||
`CTransition::FindObjCollisions` for the door entity. Inspect what
|
||||
shapes retail actually tests against. THIS IS DEFINITIVE — answers
|
||||
"what should we be doing differently" in 15-30 min. CLAUDE.md has the
|
||||
toolchain ready.
|
||||
|
||||
3. **The cottage cell BSP encloses the doorway.** Cell 0x0150
|
||||
(the doorway alcove) is bounded by cottage walls. The DOOR opens
|
||||
through a gap in those walls. When the door is closed, the gap
|
||||
is filled by the door geometry. Maybe retail's collision relies
|
||||
on the cottage walls (cell BSP) for the "doorway side walls" and
|
||||
the door's slab covers ONLY the opening's leaf area. The asymmetric
|
||||
block we see could be the cottage cell walls catching outside-in
|
||||
approach (cell BSP collision via FindEnvCollisions, before the
|
||||
door's shadow ever fires) but NOT catching inside-out at the same
|
||||
alcove geometry.
|
||||
**B. Reproduce inside-out walkthrough at unit-test speed.** Load real
|
||||
cell 0x0150 BSP into the harness (via CacheCellStruct from dat) +
|
||||
register door at faithful transform + replay captured tick 3262.
|
||||
If walkthrough reproduces at unit speed, can iterate on the fix in
|
||||
<500 ms.
|
||||
|
||||
The 50cm off-center approach probably exits the cottage walls' BSP
|
||||
through the doorway gap, walks past the foot cylinder (which is small),
|
||||
and never has anything to collide against in the world's collision
|
||||
representation. Retail might block this via a `setup.Radius=0.1414`
|
||||
cylinder we're missing (Setup.Radius isn't currently registered by
|
||||
`ShadowShapeBuilder.FromSetup` for entities WITH a CylSphere).
|
||||
**C. Audit GetNearbyObjects radial sweep + AddAllOutsideCells coverage**
|
||||
for east-neighbor cell when sphere XY is at primary cell boundary.
|
||||
|
||||
Apparatus to write next: a test that registers the door using
|
||||
ShadowShapeBuilder.FromSetup AND verifies setup.Radius is reflected
|
||||
in the shape list. If it isn't, that's a candidate fix — the door's
|
||||
0.14 m radius + 0.20 m height SETUP sphere is wider than the 0.10 m
|
||||
CylSphere and would catch the off-center approach.
|
||||
Recommendation: **A first** (cdb), then **B** to validate the fix at
|
||||
unit-test speed.
|
||||
|
||||
## Commits
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue