fix(physics): traverse prepared indoor portal topology

This commit is contained in:
Erik 2026-07-27 00:02:44 +02:00
parent b12d94047c
commit 4d095be286
9 changed files with 400 additions and 5 deletions

View file

@ -103,6 +103,33 @@ Copy this block when adding a new issue:
---
## #246 — Prepared indoor cells stopped traversing their portals
**Status:** DONE — 2026-07-26; connected house/dungeon gate passed
**Severity:** HIGH
**Filed:** 2026-07-26
**Component:** physics / cell membership / portal visibility
**Description:** Houses and dungeons could collide with empty space, expose
the background through walls, retain the wrong room, and fail on stairs after
the parsed collision graph was removed.
**Root cause / status:** The prepared package retained exact portal topology,
planes, and BSPs, but `CellTransit.FindTransitCellsSphere` still returned
immediately when the old parsed portal-polygon dictionary was absent. It now
consumes the prepared topology's direct polygon index, preserving retail's
portal traversal and failing loudly on corrupt topology. Synthetic graph-free
tests and the exact connected Holtburg cottage camera path pass, along with
1,778 physics tests / 1 skip.
**Research:**
[`research/2026-07-26-prepared-indoor-transit-regression.md`](research/2026-07-26-prepared-indoor-transit-regression.md).
**Acceptance:** PASSED — the user confirmed house/dungeon collision, doorway
entry, stairs, and interior rendering work in the corrected connected build.
---
## #245 — Distant Use stayed busy without turning or walking
**Status:** DONE — 2026-07-25; connected gate passed

View file

@ -168,6 +168,16 @@ This remains one `DatCollection`, one preparation algorithm, and one package
mmap. Evidence:
`docs/research/2026-07-25-slice-i7-closeout.md`.
**Prepared indoor-transit consumer correction (2026-07-26).** The package
already retained exact portal planes through
`FlatEnvCellTopology.PolygonIndex` plus the aliased CellStruct portal-polygon
table. The production `CellTransit` consumer now reads that prepared
relationship directly; it no longer treats the intentionally absent parsed
`CellPhysics.PortalPolygons` dictionary as “this cell has no portals.” No
package schema, bake, DAT reader, collision formula, or render portal graph
changed. Evidence:
`docs/research/2026-07-26-prepared-indoor-transit-regression.md`.
**Retail VFX hook compatibility seam (2026-07-14).** Chorizite.DatReaderWriter
2.1.7 models `CreateBlockingParticleHook` as the common hook header only, while
retail inherits the complete `CreateParticleHook` payload. The narrow readers in

View file

@ -343,6 +343,15 @@ GPU/frame-time and ordinary-process memory comparison remains a physical-
display measurement, not a Slice-I correctness blocker. Evidence:
[`../research/2026-07-25-slice-i7-closeout.md`](../research/2026-07-25-slice-i7-closeout.md).
**Corrective gate 2026-07-26.** Later connected house/dungeon testing exposed
one I7 consumer omission: `CEnvCell::find_transit_cells` still required the
deleted parsed portal-polygon dictionary, even though the prepared topology
and polygon table retained the exact same plane. The production consumer now
uses that direct prepared index. Synthetic graph-free tests and the captured
Holtburg cottage camera path prove identical graph/prepared results; no
package rebake or parsed-graph restoration is needed. Evidence:
[`../research/2026-07-26-prepared-indoor-transit-regression.md`](../research/2026-07-26-prepared-indoor-transit-regression.md).
## 4. Commit order
Each unit is independently buildable and reversible:

View file

@ -0,0 +1,90 @@
# Prepared indoor transit regression
**Date:** 2026-07-26
**Scope:** Slice-I prepared collision consumer correction
**Behavioral boundary:** No collision formula, portal rule, or renderer
behavior changed. This restores the existing retail traversal over the
production flat representation.
## Symptom and connected evidence
After the Slice-I parsed-graph removal, entering houses or dungeons could:
- collide with empty space at a doorway or room boundary;
- retain the wrong indoor cell on stairs or upper floors;
- expose the world background through the interior portal view.
The connected capture at
`.test-out/indoor-visibility-20260726-220007/stdout.log` placed the player in
Holtburg cottage vestibule `0xA9B40150`, with the chase-camera eye outside the
doorway in outdoor landcell `0xA9B40029`. The original DAT graph advanced the
camera transition from `0xA9B40150` to `0xA9B40029`. The prepared production
path retained `0xA9B40150`, causing collision and portal rendering to share
the same wrong viewer-cell root.
## Retail oracle
Named retail:
- `CEnvCell::find_transit_cells @ 0x0052C820`
- `CObjCell::find_cell_list`
- `CEnvCell::find_visible_child_cell`
Consolidated pseudocode:
[`acclient_indoor_transitions_pseudocode.md`](acclient_indoor_transitions_pseudocode.md).
For each `CCellPortal`, retail reads that portal's polygon plane from the
owning `CCellStruct`. An exterior portal admits outdoor cells when any path
sphere straddles the plane. An interior portal tests the loaded neighbor's
cell-containment BSP, using the plane only as an unloaded-cell hint.
## Root cause
The prepared package was complete:
- `FlatEnvCellTopology.Portals[i].PolygonIndex` retained the direct portal
polygon index;
- `FlatCellStructureCollisionAsset.PortalPolygons` retained the exact plane;
- prepared containment and physics BSPs matched the original DAT graph.
`CellTransit.FindTransitCellsSphere`, however, still began with:
```text
if currentCell.PortalPolygons is null: return
```
`PortalPolygons` is the old parsed-DAT dictionary and is intentionally null in
production after Slice I7. Therefore every prepared indoor cell skipped its
entire portal loop. The I4/I5 differential referee covered BSP queries and
complete resolver results, but did not exercise portal-plane lookup after the
parsed graph had been removed, allowing the representation-boundary defect to
escape.
## Correction
`CellTransit` now resolves the exact same portal plane from:
1. the parsed dictionary in explicit graph-oracle fixtures; or
2. the prepared topology's direct polygon index and flat polygon table in
production.
Prepared topology/list mismatches and invalid polygon indices fail loudly as
corrupt publication rather than silently disabling indoor traversal. Portal
iteration order, comparisons, sphere transforms, neighbor containment, and
outside admission remain unchanged.
## Automated evidence
- Synthetic prepared exterior-portal test with no parsed polygon dictionary.
- Synthetic prepared loaded-neighbor test with no parsed containment tree.
- Exact connected Holtburg cottage camera path:
graph and prepared paths both end in `0xA9B40029` at identical position.
- Installed-DAT graph/flat coverage expanded across the Holtburg cottage and
inn vestibules, connectors, stairwell, upper floor, and inn shell.
- Physics-focused Release tests: 1,778 passed / 1 skipped.
- Interaction/projection focused App tests: 127 passed.
- Client-object ordering tests: 82 passed.
- Complete Release build: passed with zero warnings.
No divergence-register row is required: the correction removes an accidental
production divergence and introduces no replacement behavior.