Merge branch 'claude/competent-robinson-dec1f4' — Indoor walking Phase 1 + Phase 2
Cluster A (Indoor walking Phase 1 — BSP cluster): - WorldPicker cell-BSP occlusion → #86 closed - CellId promotion via AABB containment (partial Phase D fix) - Diagnostic infrastructure: [indoor-bsp], [cell-cache] probes Indoor walking Phase 2 (Portal-based cell tracking): - CellBSP + Portals wired into CellPhysics - CellTransit static class: FindTransitCellsSphere + AddAllOutsideCells + FindCellList - ResolveCellId rename + sphereRadius plumbing - BuildingPhysics + CheckBuildingTransit (outdoor→indoor entry) - Foot-sphere center fix (made portal tracking actually work in production) - Indoor walkable-plane synthesis (closes the falling-stuck bug) Closes ISSUES.md #84, #85, #86, #87. Files new issues #88 (indoor object vibration) + #89 (port SphereIntersectsCellBsp). Spec: docs/superpowers/specs/2026-05-19-indoor-portal-cell-tracking-design.md Handoff: docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md
This commit is contained in:
commit
1af49b710e
34 changed files with 6618 additions and 79 deletions
256
docs/research/2026-05-19-cluster-a-shipped-handoff.md
Normal file
256
docs/research/2026-05-19-cluster-a-shipped-handoff.md
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
# Indoor walking Phase 1 — BSP cluster (Cluster A) — handoff (2026-05-19)
|
||||
|
||||
**Date:** 2026-05-19.
|
||||
**Branch:** `claude/competent-robinson-dec1f4` (commits land here; merge to main handled by controller).
|
||||
**Predecessor:** Indoor lighting + rendering Phase 2 (fix) — floors now render in Holtburg Inn. Nine pre-existing indoor bugs surfaced the moment floors were visible; this cluster addresses the collision/interaction subset (#84, #85, #86) and adds diagnostic infrastructure for the follow-up portal-traversal phase.
|
||||
**Plan:** [`docs/superpowers/plans/2026-05-19-indoor-walking-phase1-bsp-cluster.md`](../superpowers/plans/2026-05-19-indoor-walking-phase1-bsp-cluster.md).
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
Cluster A shipped **partially**. Three of the five planned phases (A, B, D)
|
||||
produced real behavior changes; two (C — obstacle audit — and E — cell-cache
|
||||
diagnostics) are diagnostic/research phases. The cluster's investigation
|
||||
confirmed that the wall-collision failures (#84, #85) all root in one cause:
|
||||
the player's `CellId` is never promoted to an indoor cell during normal
|
||||
walking, so the indoor-BSP collision branch in `TransitionTypes.FindEnvCollisions`
|
||||
never fires. Phase D implemented an AABB-containment shortcut that resolves
|
||||
the specific "spawn inside a building and be stuck above the floor" case but
|
||||
proved too tight to keep `CellId` promoted through threshold/doorway cells
|
||||
during normal outdoor→indoor entry.
|
||||
|
||||
**#86** (click selection penetrates walls) is **fully closed** — a clean,
|
||||
self-contained fix in `WorldPicker`.
|
||||
|
||||
**#84** is **partially closed** — the spawn-in-building symptom is gone; the
|
||||
remaining wall-collision symptom during normal walking is tracked under the
|
||||
new **#87**.
|
||||
|
||||
**#85** remains **open**; its root cause is confirmed identical to #84's
|
||||
remaining symptom and is also tracked under #87.
|
||||
|
||||
**#87** (indoor portal-based cell tracking) is **filed** and ready for the
|
||||
follow-up phase.
|
||||
|
||||
---
|
||||
|
||||
## Commits
|
||||
|
||||
| # | SHA | Subject | Phase |
|
||||
|---|---|---|---|
|
||||
| 1 | `18a2e28` | `docs(plan): implementation plan written` | Plan doc |
|
||||
| 2 | `27d7de1` | `feat(physics): Cluster A — indoor BSP collision probe` | Phase A |
|
||||
| 3 | `3764867` | `fix(picker): Cluster A #86 — cell-BSP ray occlusion in WorldPicker` | Phase B |
|
||||
| 4 | `4e308d5` | `test(picker): Cluster A #86 — screen-rect cell-occlusion tests` | Phase B follow-up |
|
||||
| 5 | `c19d6fb` | `fix(physics): Cluster A #84 + #85 — indoor cell tracking` | Phase D |
|
||||
| 6 | `fda6af7` | `feat(physics): Cluster A — cell-cache diagnostic` | Phase E (1st) |
|
||||
| 7 | `1f11ba9` | `feat(diag): Cluster A — extend [cell-cache] with AABB + bsphere + recursive poly count` | Phase E (2nd) |
|
||||
|
||||
**Build:** clean on all commits.
|
||||
**Tests:** `dotnet test` shows the same 8 pre-existing failures in
|
||||
`AcDream.Core.Tests` (MotionInterpreter / BSPStepUp / etc., unchanged across
|
||||
the entire cluster). All targeted test projects green. Phase B follow-up
|
||||
adds screen-rect occlusion tests; Phase D adds `RegisterCellStructForTest`
|
||||
helper used by caller-side tests.
|
||||
|
||||
---
|
||||
|
||||
## What shipped
|
||||
|
||||
### Phase A — `[indoor-bsp]` probe
|
||||
|
||||
New `PhysicsDiagnostics.ProbeIndoorBspEnabled` toggle (env var
|
||||
`ACDREAM_PROBE_INDOOR_BSP` + DebugPanel checkbox under
|
||||
`ACDREAM_DEVTOOLS=1`). When enabled, logs one `[indoor-bsp]` line each time
|
||||
`TransitionTypes.FindEnvCollisions` takes the indoor-cell branch —
|
||||
i.e., when `CellId` is an EnvCell id and the BSP contains physics polys. The
|
||||
probe serves as a presence detector: if `[indoor-bsp]` never fires during
|
||||
indoor walking, the BSP is not being consulted at all.
|
||||
|
||||
### Phase B — WorldPicker cell-BSP ray occlusion (closes #86)
|
||||
|
||||
New `CellBspRayOccluder` class (in `src/AcDream.App/Rendering/`) computes
|
||||
`NearestWallT`: the smallest ray parameter at which the pick ray intersects
|
||||
any cached EnvCell BSP polygon. Both `WorldPicker.Pick` overloads now accept
|
||||
an optional `cellOccluder` callback and filter out any hit candidate whose
|
||||
ray T exceeds `NearestWallT`. The occluder is wired from `GameWindow` using
|
||||
the `PhysicsDataCache` cell structs that Phase D also extends.
|
||||
|
||||
Before Phase B: clicking through a wall from the outside selected NPCs/items
|
||||
inside the building — `WorldPicker.BuildRay + Pick` (Phase B.4b) tested only
|
||||
entity AABBs and scenery BSPs, not EnvCell BSP geometry.
|
||||
|
||||
After Phase B: entities behind the nearest wall from the camera's perspective
|
||||
are filtered out of the candidate set. Screen-rect unit tests verify the
|
||||
filter across hit/miss/occlusion scenarios.
|
||||
|
||||
### Phase D — AABB containment for indoor CellId (partial #84 fix)
|
||||
|
||||
`PhysicsEngine.ResolveOutdoorCellId` is extended with an indoor
|
||||
cell-containment scan. After resolving the outdoor cell, the method checks
|
||||
whether the player's world position falls inside any cached `CellPhysics`
|
||||
AABB; if so, `CellId` is promoted to that EnvCell. This enables the
|
||||
`FindEnvCollisions` indoor-BSP branch.
|
||||
|
||||
New `PhysicsDataCache.TryFindContainingCell(worldPos)` does the AABB scan.
|
||||
New `CellPhysics.WorldAabb` caches the cell-local AABB in world space on
|
||||
first call (transforms the BSP bounding sphere's local AABB by the cell
|
||||
origin). New `RegisterCellStructForTest` helper allows unit test callers to
|
||||
populate the cache directly.
|
||||
|
||||
Also fixes the L.2e bare-low-byte preservation bug: `ResolveOutdoorCellId`
|
||||
was silently truncating the player CellId to the low 16 bits; the fix
|
||||
preserves the full 32-bit value.
|
||||
|
||||
**What this solved:** player spawning inside a building (e.g., logging in
|
||||
from a position inside Holtburg cottage) no longer sees `walkable=False` for
|
||||
hundreds of resolves with world Z=94.000. Phase D promotes CellId to the
|
||||
indoor cell, the floor's BSP polys are found, the player can move.
|
||||
|
||||
**What this did NOT solve:** the `[indoor-bsp]` probe fires only 6 times
|
||||
during an entire indoor walking session (all mid-jump, when the body happens
|
||||
to be at a height that falls inside a room AABB). During normal walking on
|
||||
the floor, the player's world Z is at the AABB floor level or lower —
|
||||
outside the AABB for threshold/doorway cells that have only a 0.2 m Z range.
|
||||
See Phase E evidence below.
|
||||
|
||||
### Phase E — Cell-cache diagnostic infrastructure
|
||||
|
||||
Two commits add `[cell-cache]` log output (env var
|
||||
`ACDREAM_PROBE_CELL_CACHE`, also DebugPanel). For each EnvCell in the
|
||||
physics cache, the probe logs:
|
||||
|
||||
```
|
||||
[cell-cache] id=0xA9B40143 physicsPolyCount=14 bspTotalLeafPolys=14
|
||||
bspUnmatchedIds=0 aabbMin=(-11.60,-1.60,0.00) aabbMax=(-6.20,7.60,2.80)
|
||||
bspOrigin=(0.00,0.00,0.00) bspRadius=9.97
|
||||
```
|
||||
|
||||
The extended second commit adds `bspTotalLeafPolys`, `bspUnmatchedIds`,
|
||||
`bspOrigin`, and `bspRadius` fields to give a complete picture of cell
|
||||
geometry from the physics cache perspective. This infrastructure stays in
|
||||
place as scaffolding for the portal-traversal phase.
|
||||
|
||||
---
|
||||
|
||||
## Issue status after Cluster A
|
||||
|
||||
| Issue | Status | Notes |
|
||||
|---|---|---|
|
||||
| #84 Blocked by air indoors | OPEN (partial) | Spawn-in-building variant resolved by Phase D. Threshold/doorway wall-blocking remains open under #87. |
|
||||
| #85 Pass through walls outside→in | OPEN | Root cause confirmed as same as #84 remaining symptom. See #87. |
|
||||
| #86 Click selection penetrates walls | **CLOSED** | Phase B. `WorldPicker.Pick` + `CellBspRayOccluder`. |
|
||||
| #87 Indoor portal-based cell tracking | OPEN (new) | Filed 2026-05-19. Retail-faithful fix via `CObjMaint::HandleObjectEnterCell`. |
|
||||
|
||||
---
|
||||
|
||||
## Probe evidence — log file findings
|
||||
|
||||
### `launch-cluster-a-capture.log`
|
||||
|
||||
Initial probe run with `ACDREAM_PROBE_INDOOR_BSP=1`. Result: **zero
|
||||
`[indoor-bsp]` lines** during outdoor walking and during approach to the
|
||||
Holtburg cottage doorway. This was the first confirmation that the indoor-BSP
|
||||
branch was entirely gated out. The player's CellId remained an outdoor cell
|
||||
for all movement.
|
||||
|
||||
### `launch-cluster-a-verify.log`
|
||||
|
||||
Post-Phase-D run. Observed `[indoor-bsp]` lines **only during jump frames**
|
||||
(6 total). When the player jumped inside the cottage, the body briefly rose
|
||||
to a height inside the room AABB, CellId promoted to `0xA9B40143`, and the
|
||||
indoor-BSP branch fired. On landing, the body returned to floor level, fell
|
||||
outside the AABB, and CellId reverted to the outdoor cell. Confirmed that
|
||||
AABB containment works for the room cell when the player is mid-air, but
|
||||
fails at floor level.
|
||||
|
||||
### `launch-cluster-a-cache-diag2.log`
|
||||
|
||||
First `[cell-cache]` probe run (Phase E first commit). Showed all cached
|
||||
cells with their physics poly counts and local AABBs. Confirmed 14 physics
|
||||
polys in cell `0xA9B40143` (the room), indicating BSP geometry is present
|
||||
and complete. Identified cell `0xA9B40146` as a 4-poly threshold cell.
|
||||
|
||||
### `launch-cluster-a-cache-diag3.log`
|
||||
|
||||
Extended `[cell-cache]` probe run (Phase E second commit). Full data:
|
||||
|
||||
```
|
||||
[cell-cache] id=0xA9B40143 physicsPolyCount=14 bspTotalLeafPolys=14
|
||||
bspUnmatchedIds=0 aabbMin=(-11.60,-1.60,0.00) aabbMax=(-6.20,7.60,2.80)
|
||||
bspOrigin=(0.00,0.00,0.00) bspRadius=9.97
|
||||
```
|
||||
Room cell: 2.80 m AABB height — works for mid-air player.
|
||||
|
||||
```
|
||||
[cell-cache] id=0xA9B40146 physicsPolyCount=4
|
||||
aabbMin=(-11.60,2.80,-0.20) aabbMax=(-10.00,7.60,0.00)
|
||||
bspRadius=2.3
|
||||
```
|
||||
Threshold/doorway cell: 0.20 m AABB Z range (from -0.20 to 0.00). A standing
|
||||
player at local Z=0.46 m is outside this AABB. **This is why AABB containment
|
||||
fails for normal walking through doorways.**
|
||||
|
||||
Key conclusion: the geometry is correct and complete (14/14 polys match between
|
||||
physics cache and BSP leaf count). The problem is purely in the cell-ownership
|
||||
tracking mechanism, not the collision data itself.
|
||||
|
||||
---
|
||||
|
||||
## Diagnostic infrastructure remaining in place
|
||||
|
||||
Both probes stay committed and wired. They serve as scaffolding for the
|
||||
portal-traversal follow-up phase:
|
||||
|
||||
- **`ACDREAM_PROBE_INDOOR_BSP=1`** / DebugPanel "Indoor BSP probe": logs one
|
||||
`[indoor-bsp]` line each time `FindEnvCollisions` takes the indoor-cell
|
||||
branch. After portal traversal is implemented, this probe should fire
|
||||
consistently whenever the player is indoors.
|
||||
|
||||
- **`ACDREAM_PROBE_CELL_CACHE=1`** / DebugPanel "Cell cache probe": dumps all
|
||||
cached EnvCell physics data (poly counts, BSP bounding sphere, AABB,
|
||||
unmatched ID count). Useful for verifying that cell structs load correctly
|
||||
and that portal connectivity data is present.
|
||||
|
||||
Both are gated behind `PhysicsDiagnostics` static class (existing pattern
|
||||
from L.2a).
|
||||
|
||||
---
|
||||
|
||||
## Follow-up items for the portal-traversal phase
|
||||
|
||||
**1. Implement portal-based indoor cell tracking (issue #87).**
|
||||
Replace `PhysicsDataCache.TryFindContainingCell` AABB containment with retail's
|
||||
`CObjMaint::HandleObjectEnterCell` portal traversal. When the player crosses
|
||||
a cell portal boundary, `CellId` propagates through `CEnvCell` portal
|
||||
connectivity data. PDB symbols in `docs/research/named-retail/acclient_2013_pseudo_c.txt`
|
||||
and struct definitions in `docs/research/named-retail/acclient.h` lines
|
||||
31715-31726 (`CCellStructure` shape). The retail reference implementation
|
||||
is the right oracle — do not guess at the traversal algorithm.
|
||||
|
||||
**2. Audit-trail note: add retail PDB symbol citations to `TryFindContainingCell`.**
|
||||
The current implementation in `src/AcDream.Core/Physics/PhysicsDataCache.cs`
|
||||
~line 261 is documented as a shortcut. The follow-up phase should add
|
||||
the PDB symbol citation (e.g., `// retail: CObjMaint::HandleObjectEnterCell
|
||||
// docs/research/named-retail/acclient_2013_pseudo_c.txt:XXXXX`)
|
||||
per the Phase D code-review I1 note, so future readers know this is intentionally
|
||||
replacing an interim implementation.
|
||||
|
||||
**3. Consider renaming `ResolveOutdoorCellId` → `ResolveCellId`.**
|
||||
The method now handles both outdoor and indoor cell resolution. The rename
|
||||
is low-risk (one call site in `PhysicsEngine.cs`) and would reduce the
|
||||
cognitive overhead for the next phase's author. Noted as a Phase D code-review
|
||||
M2 suggestion — do it in the same commit as the portal-traversal implementation
|
||||
to keep the rename and the semantic change together.
|
||||
|
||||
---
|
||||
|
||||
## State at handoff
|
||||
|
||||
- **Branch:** `claude/competent-robinson-dec1f4`, 7 commits of implementation/test/diagnostic work.
|
||||
- **Build state:** `dotnet build -c Debug` clean.
|
||||
- **Tests:** 8 pre-existing failures unchanged (MotionInterpreter / BSPStepUp baseline). All new tests green.
|
||||
- **Issues:** #86 CLOSED; #84 PARTIAL; #85 OPEN; #87 OPEN (new).
|
||||
- **Diagnostic probes:** `[indoor-bsp]` + `[cell-cache]` active and wired.
|
||||
- **Next:** portal-based indoor cell tracking (#87) or M2 critical path — Claude's choice per work-order autonomy.
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
# Indoor walking Phase 2 — Portal-based cell tracking — handoff (2026-05-19)
|
||||
|
||||
**Date:** 2026-05-19.
|
||||
**Branch:** `claude/competent-robinson-dec1f4` (commits land here; merge to main handled by controller).
|
||||
**Predecessor:** Indoor walking Phase 1 — BSP cluster (Cluster A). Partially shipped 2026-05-19; closed #86 cleanly, filed #87 for the portal-traversal root cause. Diagnostic infrastructure (`[indoor-bsp]` + `[cell-cache]` probes) remained as scaffolding. Handoff: [`docs/research/2026-05-19-cluster-a-shipped-handoff.md`](2026-05-19-cluster-a-shipped-handoff.md).
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
Phase 2 fully closes the indoor-walking story. Six commits replace Phase D's
|
||||
AABB-containment shortcut with retail-faithful portal-graph cell traversal.
|
||||
`CellId` now promotes to indoor cells via portals and remains promoted through
|
||||
doorways, thresholds, and multi-room navigation. Indoor cell-BSP collision fires
|
||||
consistently. A critical fix in commit 5 passes the foot-sphere center (not the
|
||||
entity reference point) to `ResolveCellId`, which was the production failure that
|
||||
made PointInsideCellBsp return false at floor level. Commit 6 adds
|
||||
`TryFindIndoorWalkablePlane` so the walkability resolver doesn't fall through to
|
||||
outdoor terrain when the player is inside.
|
||||
|
||||
**Visual verification at Holtburg cottage (2026-05-19, user testing live ACE):**
|
||||
- Walls block from inside — player cannot walk through cottage walls.
|
||||
- Multi-room navigation via doorways works — `[cell-transit]` log shows `0xA9B40145 → 0x143 → 0x144 → 0x13F` chains.
|
||||
- Walking back outdoors through a door works (post-walkable fix in commit 6).
|
||||
- Cell tracking is robust through multiple indoor sessions.
|
||||
|
||||
---
|
||||
|
||||
## Commits
|
||||
|
||||
| # | SHA | Subject |
|
||||
|---|---|---|
|
||||
| 1 | `1969c55` | `feat(physics): Phase 2 — wire CellBSP + Portals into CellPhysics` |
|
||||
| 2 | `aad6976` | `feat(physics): Phase 2 — port CellTransit + wire into ResolveCellId` |
|
||||
| 3 | `069534a` | `feat(physics): Phase 2 — BuildingPhysics + CheckBuildingTransit` |
|
||||
| 4 | `702b30a` | `refactor(physics): Phase 2 — code-review polish on BuildingPhysics commit` |
|
||||
| 5 | `3ffe1e4` | `fix(physics): Phase 2 — pass foot-sphere center to ResolveCellId` |
|
||||
| 6 | `eb0f772` | `fix(physics): Phase 2 — synthesize indoor walkable plane from cell floor` |
|
||||
|
||||
**Build:** clean on all commits.
|
||||
**Tests:** `dotnet test` shows the same 8 pre-existing failures in
|
||||
`AcDream.Core.Tests` (MotionInterpreter / BSPStepUp / etc., unchanged). All
|
||||
new Phase 2 tests and the walkable-plane tests green.
|
||||
|
||||
---
|
||||
|
||||
## What shipped
|
||||
|
||||
### Commit 1 — CellBSP + Portals wired into CellPhysics
|
||||
|
||||
New `PortalInfo` struct holds `PortalId`, `PortalPolygonIndex`, `PortalFlags`,
|
||||
and `OtherCellId`. `CellPhysics` extended with:
|
||||
- `CellBSP` — a third BSP tree (alongside `PhysicsBSP` and the render BSP) used
|
||||
for point-in-cell tests. Retail: `CCellStructure::cell_bsp`.
|
||||
- `Portals` — `IReadOnlyList<PortalInfo>` built from `envCell.CellPortals`.
|
||||
- `PortalPolygons` — the visible polygons that portals reference (`cellStruct.Polygons`,
|
||||
not `PhysicsPolygons`; portals reference the visible-geometry polygon list).
|
||||
- `VisibleCellIds` — cells visible from this cell (used by `AddAllOutsideCells`).
|
||||
|
||||
Phase D's `LocalAabbMin/Max` + `TryFindContainingCell` are deleted — they are now
|
||||
superseded by the portal traversal in `CellTransit`.
|
||||
|
||||
### Commit 2 — CellTransit + ResolveCellId
|
||||
|
||||
New `CellTransit` static class implements the retail portal-neighbour walk.
|
||||
Three public entry points:
|
||||
|
||||
- **`FindTransitCellsSphere(sphereCenter, sphereRadius, startCell, cache)`** —
|
||||
walks portal connectivity from `startCell` outward. For each portal, tests
|
||||
whether the sphere overlaps the portal polygon (using `PointInsideCellBsp` on
|
||||
the sphere center as an approximation — see issue #89 for the retail-faithful
|
||||
sphere variant). Recurses into neighbour cells up to a depth limit.
|
||||
|
||||
- **`AddAllOutsideCells(sphereCenter, blockId, cache, results)`** — for the
|
||||
outdoor path: populates a 24m grid of outdoor cell ids around the sphere center
|
||||
using `TerrainSurface.ComputeOutdoorCellId`. Mirrors retail's `add_all_outside_cells`.
|
||||
|
||||
- **`FindCellList(sp, startCell, cache)`** — top-level driver. Determines whether
|
||||
`startCell` is an indoor (EnvCell) or outdoor cell and dispatches accordingly.
|
||||
Returns a list of candidate cell ids.
|
||||
|
||||
`PhysicsEngine.ResolveOutdoorCellId` renamed to `ResolveCellId` (accepts
|
||||
`sphereRadius` parameter). Body splits on indoor vs outdoor:
|
||||
- **Indoor:** delegates to `FindCellList` and picks the candidate cell where
|
||||
`PointInsideCellBsp` returns true for the sphere center.
|
||||
- **Outdoor:** existing terrain-grid loop (`AddAllOutsideCells`).
|
||||
|
||||
`BSPQuery.PointInsideCellBsp` retyped from `PhysicsBSPNode?` to `CellBSPNode?`
|
||||
(dead code retype — no behavior change). Phase D's test file deleted.
|
||||
|
||||
### Commit 3 — BuildingPhysics + CheckBuildingTransit
|
||||
|
||||
Outdoor→indoor entry path via building-shell portal graph. New `BuildingPhysics`
|
||||
class caches per-building portal data (`BldPortalInfo` structs with `PortalId`,
|
||||
`OtherCellId`, `CellBSP`). `PhysicsDataCache` gains `_buildings` cache keyed by
|
||||
building entity id. `GameWindow` iterates `lbInfo.Buildings` at landblock load and
|
||||
populates the cache.
|
||||
|
||||
`CellTransit.CheckBuildingTransit(sphereCenter, sphereRadius, blockId, physicsCache)`
|
||||
ports retail's outdoor→indoor portal-graph entry:
|
||||
1. For each building in the landblock's physics cache, test whether the sphere
|
||||
center is inside the building's shell cell BSP (`PointInsideCellBsp`).
|
||||
2. If inside, walk the building's portal graph to find the indoor EnvCell that
|
||||
contains the sphere center.
|
||||
3. Returns the EnvCell id (or 0 if no match).
|
||||
|
||||
`PhysicsEngine.ResolveCellId`'s outdoor branch hooks `CheckBuildingTransit` after
|
||||
the terrain-grid loop, so outdoor→indoor transition is detected during normal walking.
|
||||
|
||||
### Commit 4 — Code-review polish
|
||||
|
||||
Five items addressed from reviewer:
|
||||
1. DRY cell-id derivation via existing `TerrainSurface.ComputeOutdoorCellId`
|
||||
(removed inline duplicate in `CheckBuildingTransit`).
|
||||
2. Named `PortalFlags.ExactMatch` enum instead of raw `0x01` literal.
|
||||
3. Comment clarity on `ExactMatch` reserved field.
|
||||
4. Doc comment on `CheckBuildingTransit` calling out the sphere-vs-point
|
||||
divergence from retail's `sphere_intersects_cell` (see issue #89).
|
||||
5. Rename misleading test method name.
|
||||
|
||||
### Commit 5 — Critical fix: foot-sphere center to ResolveCellId
|
||||
|
||||
**This was the production bug that prevented Phase 2 from working until the last run.**
|
||||
|
||||
`ResolveCellId` was being called with `sp.CheckPos` (the entity's reference point
|
||||
at feet level, world Z = terrain Z after the +0.02f bump) instead of
|
||||
`sp.GlobalSphere[0].Origin` (the foot sphere CENTER, approximately +0.48m above terrain).
|
||||
|
||||
Combined with the +0.02f Z-bump applied to cell origins in `PhysicsDataCache`, the
|
||||
test point landed at cell-local Z = -0.02 m — just below the cell's floor — and
|
||||
`PointInsideCellBsp` returned false for every cell. CellId never promoted to indoor
|
||||
cells during normal walking despite `FindCellList` correctly finding the right
|
||||
candidate cells.
|
||||
|
||||
Passing the foot-sphere center (which sits 0.48m above the floor, well inside any
|
||||
room cell) made portal-based cell tracking actually work in production.
|
||||
|
||||
Also adds the `[check-bldg]` diagnostic line (logged when `CheckBuildingTransit`
|
||||
returns a non-zero indoor cell id).
|
||||
|
||||
### Commit 6 — TryFindIndoorWalkablePlane
|
||||
|
||||
**Root cause of the post-Phase-2 falling-stuck bug.**
|
||||
|
||||
When indoor cell-BSP returned OK (no wall collision), the code fell through to
|
||||
outdoor `SampleTerrainWalkable` + `ValidateWalkable`. Outdoor terrain Z is below
|
||||
the indoor floor (due to the +0.02f Z-bump), so `ValidateWalkable` computed the
|
||||
player as floating well above terrain → not walkable → player stuck in the falling
|
||||
animation when blocked by an indoor wall.
|
||||
|
||||
New `TryFindIndoorWalkablePlane(worldPos, cellPhysics)`: finds the floor polygon
|
||||
directly under the player's world position by testing `worldPos` against each
|
||||
physics polygon's plane normal (upward-facing = floor) and building a `ContactPlane`
|
||||
from it. Called from the indoor branch of `ResolveWithTransition` before the outdoor
|
||||
terrain fallback. Returns true when a floor poly is found; the resolver uses the
|
||||
synthesized plane for walkability.
|
||||
|
||||
---
|
||||
|
||||
## Issue status after Phase 2
|
||||
|
||||
| Issue | Status | Notes |
|
||||
|---|---|---|
|
||||
| #84 Blocked by air indoors | **FULLY CLOSED** | Spawn-in-building variant: Phase D (Cluster A). Wall-block-from-inside + falling-stuck variants: Phase 2 commits 2, 5, 6. |
|
||||
| #85 Pass through walls outside→in | **CLOSED** | `CheckBuildingTransit` + portal traversal. CellId promotes to indoor on outdoor→indoor entry. |
|
||||
| #86 Click selection penetrates walls | CLOSED (Phase 1) | `WorldPicker.Pick` + `CellBspRayOccluder`. |
|
||||
| #87 Indoor portal-based cell tracking | **CLOSED** | `CellTransit.FindCellList` + `FindTransitCellsSphere` + `AddAllOutsideCells`. Portal-graph traversal replaces AABB containment. |
|
||||
| #88 Indoor static objects vibrate | OPEN (new) | Pre-existing visual jitter on bookshelves/furnaces. Filed 2026-05-19. Medium severity. |
|
||||
| #89 Port BSPQuery.SphereIntersectsCellBsp | OPEN (new) | `CheckBuildingTransit` uses `PointInsideCellBsp` (radius-less approximation) instead of retail's `sphere_intersects_cell`. Filed 2026-05-19. Low severity. |
|
||||
|
||||
---
|
||||
|
||||
## Probe evidence — log file findings
|
||||
|
||||
### `launch-phase2-verify3.log`
|
||||
|
||||
First run that showed indoor cell-transits firing. `[cell-transit]` output
|
||||
confirmed the portal traversal was finding indoor cells. `[indoor-bsp]` probe
|
||||
fired consistently during indoor walking (not just during mid-jump frames as in
|
||||
Cluster A). This log is the first evidence that `CellTransit.FindCellList` was
|
||||
working correctly for room interiors, though outdoor→indoor entry was not yet
|
||||
exercised.
|
||||
|
||||
### `launch-phase2-verify4.log`
|
||||
|
||||
Multi-room navigation run. `[cell-transit]` log shows
|
||||
`0xA9B40145 → 0x143 → 0x144 → 0x13F` chains as the player walked between
|
||||
rooms in the Holtburg cottage via doorways. Confirmed the `FindTransitCellsSphere`
|
||||
recursive portal walk was promoting CellId correctly through threshold cells.
|
||||
Walls blocked from inside in all rooms tested.
|
||||
|
||||
### `launch-phase2-verify5.log`
|
||||
|
||||
Walkable bug evidence run. After the outdoor→indoor transition was wired
|
||||
(`CheckBuildingTransit`), the player could walk into the cottage from outside,
|
||||
but colliding with an indoor wall produced a falling-stuck state (the `[indoor-bsp]`
|
||||
probe fired for the wall collision, but `ValidateWalkable` returned false because
|
||||
it was sampling outdoor terrain Z). This log captured the falling-stuck symptom
|
||||
and the `SampleTerrainWalkable` fallthrough trace, motivating commit 6.
|
||||
|
||||
### `launch-phase2-verify6.log`
|
||||
|
||||
Post-walkable-fix verification run. After `TryFindIndoorWalkablePlane` was added:
|
||||
- Outdoor→indoor entry works (player walks through doorway, CellId promotes).
|
||||
- Indoor wall collision works (walls block, player doesn't pass through).
|
||||
- Walking back outdoors through the door works (CellId demotes to outdoor cell).
|
||||
- No falling-stuck state observed. User confirmed all three behaviors.
|
||||
|
||||
---
|
||||
|
||||
## Diagnostic infrastructure remaining in place
|
||||
|
||||
All four probes stay committed and wired. They serve as production diagnostics
|
||||
and as debugging aids for follow-up issues:
|
||||
|
||||
- **`ACDREAM_PROBE_INDOOR_BSP=1`** / DebugPanel "Indoor BSP probe": logs one
|
||||
`[indoor-bsp]` line each time `FindEnvCollisions` takes the indoor-cell branch.
|
||||
After Phase 2, this fires consistently whenever the player is indoors. Useful
|
||||
for confirming the indoor-BSP path is active.
|
||||
|
||||
- **`ACDREAM_PROBE_CELL_CACHE=1`** / DebugPanel "Cell cache probe": dumps all
|
||||
cached EnvCell physics data (poly counts, BSP bounding sphere, AABB, unmatched
|
||||
ID count, portal count). Useful for verifying cell struct loads and portal
|
||||
connectivity.
|
||||
|
||||
- **`ACDREAM_PROBE_CELL=1`** (existing L.2a slice 1): one `[cell-transit]` line
|
||||
per `PlayerMovementController.CellId` change (old → new cell, world position,
|
||||
reason tag). Essential for tracing indoor promotion/demotion sequences.
|
||||
|
||||
- **`[check-bldg]`** (commit 5): logged by `ResolveCellId` when
|
||||
`CheckBuildingTransit` returns a non-zero indoor cell id. Fires once per
|
||||
outdoor→indoor transition detection.
|
||||
|
||||
All gated behind `PhysicsDiagnostics` static class (existing pattern from L.2a).
|
||||
|
||||
---
|
||||
|
||||
## Visual verification outcomes
|
||||
|
||||
**2026-05-19, user testing live against local ACE at Holtburg.**
|
||||
|
||||
| Scenario | Result |
|
||||
|---|---|
|
||||
| Walk into cottage wall from inside | Blocked ✓ |
|
||||
| Walk between rooms via doorway | CellId transitions logged, multi-room navigation works ✓ |
|
||||
| Walk from outside into cottage through door | Outdoor→indoor entry promoted CellId; indoor BSP collision active ✓ |
|
||||
| Walk back outside through door | CellId demoted to outdoor cell; outdoor physics resumed ✓ |
|
||||
| No falling-stuck after post-walkable fix | Confirmed ✓ |
|
||||
| Robust across multiple indoor sessions | Confirmed ✓ |
|
||||
|
||||
---
|
||||
|
||||
## Known follow-ups
|
||||
|
||||
**#88 — Indoor static objects vibrate (bookshelves, open furnaces).** Pre-existing
|
||||
visual jitter spotted before Phase 2 shipped. Medium severity. Candidates: repeated
|
||||
`EntityScriptActivator.OnCreate/OnRemove` near cell boundaries, per-part transform
|
||||
drift, or particle-emitter offset accumulation. Investigate in a follow-up session.
|
||||
|
||||
**#89 — Port `BSPQuery.SphereIntersectsCellBsp`.** `CellTransit.CheckBuildingTransit`
|
||||
currently uses `PointInsideCellBsp` (tests sphere CENTER only). Retail's
|
||||
`CEnvCell::check_building_transit` uses `CCellStruct::sphere_intersects_cell`
|
||||
(radius-aware, returns Inside/Crossing/Outside). Practical effect: entry fires
|
||||
~0.48m deeper into the doorway than retail. Low severity — visually acceptable.
|
||||
The `sphereRadius` parameter is already plumbed through for when this is ported.
|
||||
|
||||
**#80 — Indoor darkness (camera on 2nd floor goes very dark).** Still open.
|
||||
Not in Phase 2's scope. Lighting / ambient-occlusion issue that predates indoor
|
||||
rendering Phase 2.
|
||||
|
||||
---
|
||||
|
||||
## State at handoff
|
||||
|
||||
- **Branch:** `claude/competent-robinson-dec1f4`, 6 commits of Phase 2 work
|
||||
(plus 7 from Phase 1 / Cluster A on the same branch).
|
||||
- **Build state:** `dotnet build -c Debug` clean.
|
||||
- **Tests:** 8 pre-existing failures unchanged (MotionInterpreter / BSPStepUp
|
||||
baseline). All targeted test projects green.
|
||||
- **Issues:** #84, #85, #87 CLOSED. #86 CLOSED (Phase 1). #88, #89 OPEN (new).
|
||||
- **Diagnostic probes:** `[indoor-bsp]`, `[cell-cache]`, `[cell-transit]`,
|
||||
`[check-bldg]` all active and wired.
|
||||
- **Next:** M2 critical path (F.2 / F.3 / F.5a / L.1c / L.1b — kill-a-drudge
|
||||
demo) or other candidates per work-order autonomy in CLAUDE.md.
|
||||
Loading…
Add table
Add a link
Reference in a new issue