docs(phase): Indoor walking Phase 2 — Portal-based cell tracking shipped
Closes ISSUES.md #87 + #85 + the remaining wall-pass-through portion of #84 (fully closes #84). Portal-graph cell traversal replaces Phase D's AABB containment. Walking through doors promotes/demotes CellId correctly via portal traversal; walls block from inside indoor cells; indoor walkable plane is synthesized from the cell's floor poly so the resolver tracks walkability correctly during indoor movement. Files two new issues: #88 (indoor static objects vibrate — pre-existing, spotted during Phase 2 testing) and #89 (BSPQuery.SphereIntersectsCellBsp — follow-up to make CheckBuildingTransit retail-faithful; currently uses radius-less PointInsideCellBsp as a documented approximation). ISSUES.md: #87, #85, #84 moved to DONE. #88 + #89 filed. Roadmap: Indoor walking Phase 2 added to shipped table. CLAUDE.md: recent-phase paragraph updated to reflect Phase 2 shipped. New handoff: docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
eb0f772f0f
commit
a9c74d153a
4 changed files with 391 additions and 88 deletions
49
CLAUDE.md
49
CLAUDE.md
|
|
@ -776,35 +776,32 @@ acdream's plan lives in two files committed to the repo:
|
||||||
acceptance criteria. Do not drift from the spec without explicit user
|
acceptance criteria. Do not drift from the spec without explicit user
|
||||||
approval.
|
approval.
|
||||||
|
|
||||||
**Indoor walking Phase 1 — BSP cluster (Cluster A) partially shipped
|
**Indoor walking Phase 2 — Portal-based cell tracking shipped
|
||||||
2026-05-19.** Seven commits across five phases:
|
2026-05-19.** Six commits:
|
||||||
- `18a2e28` — implementation plan
|
- `1969c55` — CellBSP + Portals wired into CellPhysics (`PortalInfo` struct, `VisibleCellIds`)
|
||||||
- `27d7de1` — Phase A: `[indoor-bsp]` probe + `ProbeIndoorBspEnabled` toggle
|
- `aad6976` — `CellTransit.FindCellList` + `FindTransitCellsSphere` + `AddAllOutsideCells`; `ResolveCellId` rename
|
||||||
- `3764867` — Phase B: `CellBspRayOccluder` in `WorldPicker.Pick` (**closes #86**)
|
- `069534a` — `BuildingPhysics` + `CheckBuildingTransit` for outdoor→indoor entry via `BldPortalInfo`
|
||||||
- `4e308d5` — Phase B follow-up: screen-rect cell-occlusion tests
|
- `702b30a` — code-review polish (DRY cell-id derivation, `PortalFlags.ExactMatch` enum, docs)
|
||||||
- `c19d6fb` — Phase D: AABB containment for indoor CellId promotion + L.2e bare-low-byte fix (partial #84 fix)
|
- `3ffe1e4` — critical fix: pass foot-sphere center (`GlobalSphere[0].Origin`) not `CheckPos` to `ResolveCellId`
|
||||||
- `fda6af7` — Phase E first commit: `[cell-cache]` probe
|
- `eb0f772` — `TryFindIndoorWalkablePlane` synthesizes indoor walkable plane from cell floor poly
|
||||||
- `1f11ba9` — Phase E second commit: extended `[cell-cache]` with AABB + bsphere + poly counts
|
|
||||||
|
|
||||||
**#86** (click selection penetrates walls) — **CLOSED.** `WorldPicker.Pick`
|
**#86** (click selection penetrates walls) — **CLOSED** (Phase 1 Cluster A).
|
||||||
consults `CellBspRayOccluder.NearestWallT`; entities behind walls are filtered.
|
**#84** (blocked by air indoors) — **FULLY CLOSED.** Spawn-in-building variant
|
||||||
**#84** (blocked by air indoors) — **PARTIAL.** The "spawn-in-building stuck
|
closed by Phase 1 (Phase D AABB containment). Wall-block-from-inside variant
|
||||||
above floor" variant is resolved (Phase D promotes CellId to the indoor cell
|
closed by Phase 2 (portal-graph traversal).
|
||||||
on spawn-in). The remaining "walls don't block from inside during normal
|
**#85** (pass through walls outside→in) — **CLOSED** by Phase 2.
|
||||||
walking" symptom is the same root cause as #85 — AABB containment is too
|
`CheckBuildingTransit` promotes CellId via the building-shell portal graph
|
||||||
tight for threshold/doorway cells (Z range ~0.2 m, player stands at ~0.46 m)
|
on outdoor→indoor entry; indoor-BSP collision fires from both sides.
|
||||||
to keep CellId promoted. Both are tracked under new issue **#87**.
|
**#87** (indoor portal-based cell tracking) — **CLOSED** by Phase 2.
|
||||||
**#85** (pass through walls outside→in) — **OPEN.** Root cause confirmed as
|
**#88** (indoor static objects vibrate) — **FILED** (pre-existing, Medium).
|
||||||
same as #84 remaining symptom — CellId drifts back to outdoor cell, indoor
|
**#89** (port `BSPQuery.SphereIntersectsCellBsp`) — **FILED** (Low, documented
|
||||||
BSP never fires. See #87.
|
approximation in `CheckBuildingTransit`).
|
||||||
**#87** (indoor portal-based cell tracking) — **FILED.** Retail-faithful fix
|
Diagnostic infrastructure: `[indoor-bsp]`, `[cell-cache]`, `[cell-transit]`,
|
||||||
via `CObjMaint::HandleObjectEnterCell` + `CEnvCell` portal connectivity.
|
`[check-bldg]` probes all stay in place.
|
||||||
Diagnostic infrastructure from Cluster A (`[indoor-bsp]` + `[cell-cache]`
|
Handoff: [`docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md`](docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md).
|
||||||
probes, both runtime-toggleable) stays in place as scaffolding for the
|
Phase 1 handoff: [`docs/research/2026-05-19-cluster-a-shipped-handoff.md`](docs/research/2026-05-19-cluster-a-shipped-handoff.md).
|
||||||
follow-up phase. Handoff: [`docs/research/2026-05-19-cluster-a-shipped-handoff.md`](docs/research/2026-05-19-cluster-a-shipped-handoff.md).
|
|
||||||
|
|
||||||
**Next phase is Claude's choice** per work-order autonomy. Candidates:
|
**Next phase is Claude's choice** per work-order autonomy. Candidates:
|
||||||
indoor portal-based cell tracking (#87, completes the indoor walking story);
|
|
||||||
M2 critical path (F.2 / F.3 / F.5a / L.1c / L.1b — kill-a-drudge demo);
|
M2 critical path (F.2 / F.3 / F.5a / L.1c / L.1b — kill-a-drudge demo);
|
||||||
or the pre-existing "next phase candidates" list below.
|
or the pre-existing "next phase candidates" list below.
|
||||||
|
|
||||||
|
|
|
||||||
145
docs/ISSUES.md
145
docs/ISSUES.md
|
|
@ -237,9 +237,10 @@ to the second floor without getting stuck.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## #84 — Blocked by air indoors
|
## #84 — [DONE 2026-05-19] Blocked by air indoors
|
||||||
|
|
||||||
**Status:** OPEN (partial fix 2026-05-19)
|
**Status:** DONE
|
||||||
|
**Closed:** 2026-05-19
|
||||||
**Severity:** HIGH (blocks indoor navigation)
|
**Severity:** HIGH (blocks indoor navigation)
|
||||||
**Filed:** 2026-05-19
|
**Filed:** 2026-05-19
|
||||||
**Component:** physics, collision
|
**Component:** physics, collision
|
||||||
|
|
@ -274,58 +275,68 @@ enables the `FindEnvCollisions` indoor-BSP branch. This resolved the
|
||||||
"spawn in building and be stuck above the floor" variant of #84 —
|
"spawn in building and be stuck above the floor" variant of #84 —
|
||||||
player's CellId now promotes to the interior cell on spawn-in, the floor
|
player's CellId now promotes to the interior cell on spawn-in, the floor
|
||||||
is walkable, and the player can move freely. The "invisible air obstacle"
|
is walkable, and the player can move freely. The "invisible air obstacle"
|
||||||
symptom for rooms the player walks INTO from outside is now superseded by
|
symptom for rooms the player walks INTO from outside was tracked under #87
|
||||||
the root cause in #87 (AABB containment is too tight for threshold/
|
and required portal-based cell tracking.
|
||||||
doorway cells to keep CellId promoted during normal walking). That
|
|
||||||
remaining symptom will be resolved by the portal-based cell tracking
|
**Resolution (2026-05-19 full · `1969c55, aad6976, 069534a, 702b30a, 3ffe1e4, eb0f772`):**
|
||||||
fix.
|
Indoor walking Phase 2 replaced AABB containment with portal-graph cell traversal
|
||||||
|
(`CellTransit.FindCellList` + `CheckBuildingTransit`). CellId now promotes to indoor
|
||||||
|
cells via portals and remains promoted during normal walking through doorways. Indoor
|
||||||
|
cell-BSP collision fires consistently. Indoor walkable plane synthesized from floor
|
||||||
|
poly (`TryFindIndoorWalkablePlane`) so the resolver tracks walkability correctly when
|
||||||
|
the player is standing on an indoor floor. User visually verified at Holtburg cottage:
|
||||||
|
walls block from inside, multi-room navigation works, walking outdoors through a door
|
||||||
|
works. Issue fully closed.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## #85 — Pass through walls from outside→in
|
## #85 — [DONE 2026-05-19 · 1969c55, aad6976, 069534a, 702b30a, 3ffe1e4, eb0f772] Pass through walls from outside→in
|
||||||
|
|
||||||
**Status:** OPEN
|
**Status:** DONE
|
||||||
**Severity:** HIGH (gameplay-breaking)
|
**Closed:** 2026-05-19
|
||||||
|
**Commits:** `1969c55, aad6976, 069534a, 702b30a, 3ffe1e4, eb0f772`
|
||||||
**Filed:** 2026-05-19
|
**Filed:** 2026-05-19
|
||||||
**Component:** physics, collision
|
**Component:** physics, collision
|
||||||
|
|
||||||
**Description:** Approaching a building from the outside, the player
|
**Resolution (2026-05-19 · Indoor walking Phase 2):** The root cause (CellId never promoted
|
||||||
|
to the indoor cell during outdoor→indoor walking) was resolved by portal-graph cell
|
||||||
|
traversal in `CellTransit.CheckBuildingTransit`. Once `CellId` promotes to the indoor
|
||||||
|
cell, the indoor-BSP collision branch in `FindEnvCollisions` fires for approaches from
|
||||||
|
both inside and outside. User visually verified walls block from outside (player must
|
||||||
|
use the door portal to enter). See #87 and handoff:
|
||||||
|
[`docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md`](2026-05-19-indoor-walking-phase2-shipped-handoff.md).
|
||||||
|
|
||||||
|
**Original description:** Approaching a building from the outside, the player
|
||||||
can walk THROUGH walls into the interior — one-directional wall
|
can walk THROUGH walls into the interior — one-directional wall
|
||||||
collision. From the inside trying to exit, the wall does block.
|
collision. From the inside trying to exit, the wall does block.
|
||||||
|
|
||||||
**Root cause / status:** Cell BSP polygons likely have one-sided
|
The root cause was pinned (Cluster A 2026-05-19) as the same failure as
|
||||||
normals (front-facing only). Approach from the inside hits the front;
|
#84's remaining symptom — `CellId` wasn't promoted to the indoor cell
|
||||||
approach from the outside hits the back which BSP traversal treats as
|
during normal outdoor→indoor walking because AABB containment was too
|
||||||
"behind the plane" → no collision. Retail handles this via two-sided
|
tight for threshold/doorway cells. Without CellId in the indoor cell,
|
||||||
collision polys or per-poly back-face handling.
|
the indoor-BSP collision branch in `FindEnvCollisions` never fired
|
||||||
|
regardless of approach direction.
|
||||||
**Files:**
|
|
||||||
- `src/AcDream.Core/Physics/BSPQuery.cs`
|
|
||||||
- `src/AcDream.Core/Physics/TransitionTypes.cs` (`FindObjCollisions` cell
|
|
||||||
branch).
|
|
||||||
|
|
||||||
**Acceptance:** Walking into an inn wall from outside collides; player
|
|
||||||
must enter via the door portal.
|
|
||||||
|
|
||||||
**Status update (2026-05-19):** The root cause is now pinned as the
|
|
||||||
same failure as #84's remaining symptom — `CellId` isn't promoted to
|
|
||||||
the indoor cell during normal outdoor→indoor walking because AABB
|
|
||||||
containment is too tight for threshold/doorway cells. Without CellId
|
|
||||||
in the indoor cell, the indoor-BSP collision branch in
|
|
||||||
`FindEnvCollisions` never fires regardless of approach direction.
|
|
||||||
See new issue #87 (portal-based indoor cell tracking) for the
|
|
||||||
retail-faithful fix.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## #87 — Indoor cell tracking uses AABB containment instead of portal traversal
|
## #87 — [DONE 2026-05-19 · 1969c55, aad6976, 069534a, 702b30a, 3ffe1e4, eb0f772] Indoor cell tracking uses AABB containment instead of portal traversal
|
||||||
|
|
||||||
**Status:** OPEN
|
**Status:** DONE
|
||||||
**Severity:** HIGH
|
**Closed:** 2026-05-19
|
||||||
|
**Commits:** `1969c55, aad6976, 069534a, 702b30a, 3ffe1e4, eb0f772`
|
||||||
**Filed:** 2026-05-19
|
**Filed:** 2026-05-19
|
||||||
**Component:** physics
|
**Component:** physics
|
||||||
|
|
||||||
**Description:** `PhysicsDataCache.TryFindContainingCell` promotes the
|
**Resolution (2026-05-19 · Indoor walking Phase 2):** Portal-graph cell traversal
|
||||||
|
(`CellTransit.FindCellList` + `CheckBuildingTransit`) replaced the AABB containment
|
||||||
|
shortcut. Player CellId now correctly promotes to indoor cells via portals;
|
||||||
|
indoor cell-BSP collision branch fires consistently; walls block from inside.
|
||||||
|
Outdoor→indoor entry via `BuildingPhysics` + `BldPortalInfo` (`CheckBuildingTransit`)
|
||||||
|
wires the building-shell portal graph. Indoor walkable plane synthesized from the
|
||||||
|
cell's floor poly so the resolver tracks walkability during indoor movement (`TryFindIndoorWalkablePlane`).
|
||||||
|
See handoff: [`docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md`](2026-05-19-indoor-walking-phase2-shipped-handoff.md).
|
||||||
|
|
||||||
|
**Original description:** `PhysicsDataCache.TryFindContainingCell` promotes the
|
||||||
player's `CellId` to an indoor EnvCell when their world position falls
|
player's `CellId` to an indoor EnvCell when their world position falls
|
||||||
inside any cached cell's local AABB. This is too tight to keep `CellId`
|
inside any cached cell's local AABB. This is too tight to keep `CellId`
|
||||||
promoted to an indoor cell during normal walking. Threshold/doorway cells
|
promoted to an indoor cell during normal walking. Threshold/doorway cells
|
||||||
|
|
@ -338,34 +349,44 @@ physics is unreliable. The retail fix is portal-based cell traversal —
|
||||||
when the player crosses a cell portal boundary, the cell ownership
|
when the player crosses a cell portal boundary, the cell ownership
|
||||||
propagates through portal connectivity data in `CEnvCell`.
|
propagates through portal connectivity data in `CEnvCell`.
|
||||||
|
|
||||||
**Evidence:** `launch-cluster-a-cache-diag3.log` (Cluster A Phase E
|
---
|
||||||
capture). Cell `0xA9B40143` (real room) has
|
|
||||||
`physicsPolyCount=14 bspTotalLeafPolys=14 bspUnmatchedIds=0
|
## #88 — Indoor static objects vibrate (bookshelves, open furnaces)
|
||||||
aabbMin=(-11.60,-1.60,0.00) aabbMax=(-6.20,7.60,2.80)` — geometry is
|
|
||||||
complete and the AABB spans 2.8 m height, which works. Cell `0xA9B40146`
|
**Status:** OPEN
|
||||||
(threshold/doorway) has `physicsPolyCount=4
|
**Severity:** MEDIUM (visual jitter; doesn't block gameplay)
|
||||||
aabbMin=(-11.60,2.80,-0.20) aabbMax=(-10.00,7.60,0.00)` — Z range is
|
**Filed:** 2026-05-19
|
||||||
only 0.2 m; a standing player is always outside it. Only 6 `[indoor-bsp]`
|
**Component:** rendering, animation
|
||||||
lines fired across an entire indoor walking session (all during mid-jump
|
|
||||||
frames when the player was briefly inside the room AABB at jump height).
|
**Description:** Static objects inside cells (bookshelves, open furnaces, possibly other interior props) show per-frame transform jitter / vibration. Pre-existing (user noticed before Phase 2 shipped). Likely candidates:
|
||||||
|
|
||||||
|
1. `EntityScriptActivator.OnCreate/OnRemove` firing repeatedly as the player's CellId promotes/demotes near cell boundaries (less likely after Phase 2's portal-based tracking — but worth investigating).
|
||||||
|
2. Per-part transforms for cell-static `WorldEntity` instances getting recomputed each frame with floating-point drift.
|
||||||
|
3. Particle-emitter offsets accumulating instead of resetting.
|
||||||
|
|
||||||
|
**Files to investigate:**
|
||||||
|
- `src/AcDream.App/Rendering/Vfx/EntityScriptActivator.cs` — OnCreate/OnRemove call patterns
|
||||||
|
- `src/AcDream.App/Rendering/GpuWorldState.cs` — entity transform updates per frame
|
||||||
|
- `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs` — per-batch transform composition
|
||||||
|
|
||||||
|
**Acceptance:** Indoor static objects render stable (no per-frame jitter).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## #89 — Port BSPQuery.SphereIntersectsCellBsp for retail-faithful CheckBuildingTransit
|
||||||
|
|
||||||
|
**Status:** OPEN
|
||||||
|
**Severity:** LOW (Phase 2 ships with a documented approximation)
|
||||||
|
**Filed:** 2026-05-19
|
||||||
|
**Component:** physics
|
||||||
|
|
||||||
|
**Description:** Retail's `CEnvCell::check_building_transit` uses `CCellStruct::sphere_intersects_cell` — a radius-aware sphere-vs-BSP test that returns Inside/Crossing/Outside. Phase 2's `CellTransit.CheckBuildingTransit` uses `BSPQuery.PointInsideCellBsp` (radius-less, tests only the sphere CENTER). Practical effect: outdoor→indoor entry fires ~sphereRadius (~0.48m) deeper into the doorway than retail. The sphereRadius parameter is plumbed through but currently unused.
|
||||||
|
|
||||||
**Files:**
|
**Files:**
|
||||||
- `src/AcDream.Core/Physics/PhysicsDataCache.cs` (`TryFindContainingCell`,
|
- `src/AcDream.Core/Physics/CellTransit.cs::CheckBuildingTransit` (line ~162)
|
||||||
approximately line 261)
|
- `src/AcDream.Core/Physics/BSPQuery.cs::PointInsideCellBsp` (line ~940) — existing point test to model the new sphere variant after
|
||||||
- `src/AcDream.Core/Physics/PhysicsEngine.cs` (`ResolveOutdoorCellId`,
|
|
||||||
approximately line 238)
|
|
||||||
- `src/AcDream.Core/Physics/TransitionTypes.cs` (`FindEnvCollisions` cell
|
|
||||||
branch, approximately line 1188)
|
|
||||||
|
|
||||||
**Retail reference:** PDB symbols `CObjMaint::HandleObjectEnterCell` and
|
**Acceptance:** `CellTransit.CheckBuildingTransit` calls a new `BSPQuery.SphereIntersectsCellBsp(node, sphereCenter, sphereRadius)` that returns `Inside`/`Crossing`/`Outside`. Entry timing matches retail visually at the Holtburg cottage door.
|
||||||
`CEnvCell` portal data. See `docs/research/named-retail/acclient.h` lines
|
|
||||||
31715-31726 for `CCellStructure` shape; `acclient_2013_pseudo_c.txt` for
|
|
||||||
the implementations.
|
|
||||||
|
|
||||||
**Acceptance:** Player walking from outside the Holtburg cottage into the
|
|
||||||
interior crosses portals and `CellId` updates accordingly; walls block
|
|
||||||
from both inside and outside; the `[indoor-bsp]` probe fires consistently
|
|
||||||
during indoor walking (not just during mid-jump frames).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@
|
||||||
| Indoor lighting + rendering — Phase 2 (fix) | Three-component diagnostic-driven fix for missing-floor bug. Component 1: `WbMeshAdapter` captures the `Task<ObjectMeshData?>` from `PrepareMeshDataAsync` and attaches a `ContinueWith` for EnvCell ids — surfaces faulted-task exceptions + clean-null returns. Component 2: replaced `NullLogger<ObjectMeshManager>` with a Console-backed `ConsoleErrorLogger<T>` so WB's intentional `_logger.LogError(ex, ...)` at the swallow site at `ObjectMeshManager.cs:589` writes `[wb-error]` lines. **Root cause definitively identified in one capture: `ArgumentOutOfRangeException` from `DatReaderWriter.Setup.Unpack` at WB's `PrepareEnvCellMeshData` line 1223 — `TryGet<Setup>(stab.Id, ...)` was called blindly on every `envCell.StaticObjects` id without checking the Setup-prefix bit. GfxObj-typed stabs (0x01xxxxxx) caused mid-deserialization throws, bubbling up to PrepareMeshData's outer catch which silently returned null. Entire cell upload failed, room mesh never reached `_renderData`.** Component 3 fix: one-line type-check guard `(stab.Id & 0xFF000000u) == 0x02000000u && _dats.Portal.TryGet<Setup>(stab.Id, out var stabSetup)`. Committed to WB submodule on branch `acdream-fix-floor-rendering` at SHA `34460c4` — needs submodule pointer advance at merge time. **Verification: 0 [wb-error] (was 385), 0 NULL_RESULT (was 55), Holtburg 123/123 cells complete (was 97/123). User visually confirmed floors render in Holtburg Inn.** Surfaced 9 pre-existing indoor bugs (see-through floor, indoor collision, stairs, walls, click-thru, indoor lighting artifacts, atmospheric-lighting-on-stabs, slope terrain lighting) — all filed in `docs/ISSUES.md` for follow-up phases. Cause report: [`docs/research/2026-05-19-indoor-cell-rendering-cause.md`](../research/2026-05-19-indoor-cell-rendering-cause.md). Verification: [`docs/research/2026-05-19-indoor-cell-rendering-verification.md`](../research/2026-05-19-indoor-cell-rendering-verification.md). Plan: [`docs/superpowers/plans/2026-05-19-phase2-indoor-cell-rendering-fix.md`](../superpowers/plans/2026-05-19-phase2-indoor-cell-rendering-fix.md). | Live ✓ |
|
| Indoor lighting + rendering — Phase 2 (fix) | Three-component diagnostic-driven fix for missing-floor bug. Component 1: `WbMeshAdapter` captures the `Task<ObjectMeshData?>` from `PrepareMeshDataAsync` and attaches a `ContinueWith` for EnvCell ids — surfaces faulted-task exceptions + clean-null returns. Component 2: replaced `NullLogger<ObjectMeshManager>` with a Console-backed `ConsoleErrorLogger<T>` so WB's intentional `_logger.LogError(ex, ...)` at the swallow site at `ObjectMeshManager.cs:589` writes `[wb-error]` lines. **Root cause definitively identified in one capture: `ArgumentOutOfRangeException` from `DatReaderWriter.Setup.Unpack` at WB's `PrepareEnvCellMeshData` line 1223 — `TryGet<Setup>(stab.Id, ...)` was called blindly on every `envCell.StaticObjects` id without checking the Setup-prefix bit. GfxObj-typed stabs (0x01xxxxxx) caused mid-deserialization throws, bubbling up to PrepareMeshData's outer catch which silently returned null. Entire cell upload failed, room mesh never reached `_renderData`.** Component 3 fix: one-line type-check guard `(stab.Id & 0xFF000000u) == 0x02000000u && _dats.Portal.TryGet<Setup>(stab.Id, out var stabSetup)`. Committed to WB submodule on branch `acdream-fix-floor-rendering` at SHA `34460c4` — needs submodule pointer advance at merge time. **Verification: 0 [wb-error] (was 385), 0 NULL_RESULT (was 55), Holtburg 123/123 cells complete (was 97/123). User visually confirmed floors render in Holtburg Inn.** Surfaced 9 pre-existing indoor bugs (see-through floor, indoor collision, stairs, walls, click-thru, indoor lighting artifacts, atmospheric-lighting-on-stabs, slope terrain lighting) — all filed in `docs/ISSUES.md` for follow-up phases. Cause report: [`docs/research/2026-05-19-indoor-cell-rendering-cause.md`](../research/2026-05-19-indoor-cell-rendering-cause.md). Verification: [`docs/research/2026-05-19-indoor-cell-rendering-verification.md`](../research/2026-05-19-indoor-cell-rendering-verification.md). Plan: [`docs/superpowers/plans/2026-05-19-phase2-indoor-cell-rendering-fix.md`](../superpowers/plans/2026-05-19-phase2-indoor-cell-rendering-fix.md). | Live ✓ |
|
||||||
| C.1.5b | Per-part PES transforms + dat-hydrated entity DefaultScript dispatch. Closes issue #56. Shipped 2026-05-12 across 5 commits (`1e3c33b` docs+plan, `f3bc15e` SetupPartTransforms helper, `11521f4` ParticleHookSink applies `CreateParticleHook.PartIndex`, `5ca5827` activator refactor + GameWindow resolver lambda, `8735c39` GpuWorldState 4 new fire-sites). **Slice A** — new [`SetupPartTransforms.Compute(setup)`](../../src/AcDream.Core/Meshing/SetupPartTransforms.cs) walks `PlacementFrames[Resting]` → `[Default]` → first-available (mirrors `SetupMesh.Flatten` priority) and returns `Matrix4x4` per part; new `ParticleHookSink.SetEntityPartTransforms(entityId, partTransforms)` mirrors the existing `_rotationByEntity` pattern; `SpawnFromHook` now transforms hook offset through `partTransforms[partIndex]` before applying entity rotation. **Slice B** — activator's `ServerGuid==0` guard relaxed: keys by `entity.ServerGuid` when non-zero, else `entity.Id` (collision-free with server guids in the `0x40xxxxxx` interior / `0x80xxxxxx` scenery / `0xC0xxxxxx` ranges). Resolver delegate refactored to return `ScriptActivationInfo(ScriptId, PartTransforms)` so one dat lookup yields both pieces. `GpuWorldState` fires the activator from 4 new sites: `AddLandblock` + `AddEntitiesToExistingLandblock` (Far→Near promotion) for OnCreate, `RemoveLandblock` + `RemoveEntitiesFromLandblock` (Near→Far demotion) for OnRemove. ServerGuid==0 filter on AddLandblock avoids double-firing pending-bucket merges. **Reality discovery folded into spec §3**: EnvCell `StaticObjects` are already hydrated as `WorldEntity` instances by `GameWindow.BuildInteriorEntitiesForStreaming` (with stable `entity.Id` in `0x40xxxxxx`) — no synthetic-ID scheme or separate walker class needed (handoff §4 Q1/Q2 mooted). **Visual verification 2026-05-12**: Holtburg Town network portal swirl distributes across the arch (no ground-burial), Inn fireplace flames render over the firebox, cottage chimney smoke columns render, spell-cast animation-hook particles all match retail. 18 new + 4 updated tests, all Vfx/Meshing/Streaming/Activator green. Spec: [`docs/superpowers/specs/2026-05-13-phase-c1.5b-design.md`](../superpowers/specs/2026-05-13-phase-c1.5b-design.md). Plan: [`docs/superpowers/plans/2026-05-13-phase-c1.5b.md`](../superpowers/plans/2026-05-13-phase-c1.5b.md). | Live ✓ |
|
| C.1.5b | Per-part PES transforms + dat-hydrated entity DefaultScript dispatch. Closes issue #56. Shipped 2026-05-12 across 5 commits (`1e3c33b` docs+plan, `f3bc15e` SetupPartTransforms helper, `11521f4` ParticleHookSink applies `CreateParticleHook.PartIndex`, `5ca5827` activator refactor + GameWindow resolver lambda, `8735c39` GpuWorldState 4 new fire-sites). **Slice A** — new [`SetupPartTransforms.Compute(setup)`](../../src/AcDream.Core/Meshing/SetupPartTransforms.cs) walks `PlacementFrames[Resting]` → `[Default]` → first-available (mirrors `SetupMesh.Flatten` priority) and returns `Matrix4x4` per part; new `ParticleHookSink.SetEntityPartTransforms(entityId, partTransforms)` mirrors the existing `_rotationByEntity` pattern; `SpawnFromHook` now transforms hook offset through `partTransforms[partIndex]` before applying entity rotation. **Slice B** — activator's `ServerGuid==0` guard relaxed: keys by `entity.ServerGuid` when non-zero, else `entity.Id` (collision-free with server guids in the `0x40xxxxxx` interior / `0x80xxxxxx` scenery / `0xC0xxxxxx` ranges). Resolver delegate refactored to return `ScriptActivationInfo(ScriptId, PartTransforms)` so one dat lookup yields both pieces. `GpuWorldState` fires the activator from 4 new sites: `AddLandblock` + `AddEntitiesToExistingLandblock` (Far→Near promotion) for OnCreate, `RemoveLandblock` + `RemoveEntitiesFromLandblock` (Near→Far demotion) for OnRemove. ServerGuid==0 filter on AddLandblock avoids double-firing pending-bucket merges. **Reality discovery folded into spec §3**: EnvCell `StaticObjects` are already hydrated as `WorldEntity` instances by `GameWindow.BuildInteriorEntitiesForStreaming` (with stable `entity.Id` in `0x40xxxxxx`) — no synthetic-ID scheme or separate walker class needed (handoff §4 Q1/Q2 mooted). **Visual verification 2026-05-12**: Holtburg Town network portal swirl distributes across the arch (no ground-burial), Inn fireplace flames render over the firebox, cottage chimney smoke columns render, spell-cast animation-hook particles all match retail. 18 new + 4 updated tests, all Vfx/Meshing/Streaming/Activator green. Spec: [`docs/superpowers/specs/2026-05-13-phase-c1.5b-design.md`](../superpowers/specs/2026-05-13-phase-c1.5b-design.md). Plan: [`docs/superpowers/plans/2026-05-13-phase-c1.5b.md`](../superpowers/plans/2026-05-13-phase-c1.5b.md). | Live ✓ |
|
||||||
| Indoor walking Phase 1 — BSP cluster (partial) | 2026-05-19. Probe + WorldPicker cell-BSP occlusion (#86 closed) + CellId promotion via AABB containment (partial #84 fix). Seven commits across 5 phases: `18a2e28` plan, `27d7de1` Phase A `[indoor-bsp]` probe + toggle, `3764867` Phase B CellBspRayOccluder in WorldPicker, `4e308d5` Phase B screen-rect tests, `c19d6fb` Phase D AABB containment + L.2e bare-low-byte fix, `fda6af7` Phase E `[cell-cache]` diagnostic, `1f11ba9` Phase E extended AABB/bsphere/poly-count fields. **#86 closed** (picker occlusion). **#84 partially closed** (spawn-in-building stuck-above-floor resolved; threshold/doorway walls remain open under #87). **#85 open** (wall pass-through root cause confirmed as same as #84 remaining symptom — CellId doesn't stay promoted during outdoor→indoor walking). **#87 filed** (portal-based indoor cell tracking — retail-faithful follow-up). `[indoor-bsp]` + `[cell-cache]` probes stay in place as scaffolding for the follow-up phase. Handoff: [`docs/research/2026-05-19-cluster-a-shipped-handoff.md`](../research/2026-05-19-cluster-a-shipped-handoff.md). Plan: [`docs/superpowers/plans/2026-05-19-indoor-walking-phase1-bsp-cluster.md`](../superpowers/plans/2026-05-19-indoor-walking-phase1-bsp-cluster.md). | Tests ✓ |
|
| Indoor walking Phase 1 — BSP cluster (partial) | 2026-05-19. Probe + WorldPicker cell-BSP occlusion (#86 closed) + CellId promotion via AABB containment (partial #84 fix). Seven commits across 5 phases: `18a2e28` plan, `27d7de1` Phase A `[indoor-bsp]` probe + toggle, `3764867` Phase B CellBspRayOccluder in WorldPicker, `4e308d5` Phase B screen-rect tests, `c19d6fb` Phase D AABB containment + L.2e bare-low-byte fix, `fda6af7` Phase E `[cell-cache]` diagnostic, `1f11ba9` Phase E extended AABB/bsphere/poly-count fields. **#86 closed** (picker occlusion). **#84 partially closed** (spawn-in-building stuck-above-floor resolved; threshold/doorway walls remain open under #87). **#85 open** (wall pass-through root cause confirmed as same as #84 remaining symptom — CellId doesn't stay promoted during outdoor→indoor walking). **#87 filed** (portal-based indoor cell tracking — retail-faithful follow-up). `[indoor-bsp]` + `[cell-cache]` probes stay in place as scaffolding for the follow-up phase. Handoff: [`docs/research/2026-05-19-cluster-a-shipped-handoff.md`](../research/2026-05-19-cluster-a-shipped-handoff.md). Plan: [`docs/superpowers/plans/2026-05-19-indoor-walking-phase1-bsp-cluster.md`](../superpowers/plans/2026-05-19-indoor-walking-phase1-bsp-cluster.md). | Tests ✓ |
|
||||||
|
| Indoor walking Phase 2 — Portal-based cell tracking | 2026-05-19. Portal-graph traversal replaces Phase D's AABB containment. Six commits: `1969c55` CellBSP+Portals wired into CellPhysics; `aad6976` CellTransit.FindCellList + FindTransitCellsSphere + AddAllOutsideCells + ResolveCellId rename; `069534a` BuildingPhysics + CheckBuildingTransit for outdoor→indoor entry; `702b30a` code-review polish; `3ffe1e4` pass foot-sphere center to ResolveCellId (critical fix — was passing CheckPos instead of GlobalSphere[0].Origin, causing PointInsideCellBsp to return false at floor level); `eb0f772` TryFindIndoorWalkablePlane synthesizes walkable plane from cell floor poly so the resolver doesn't fall through to outdoor SampleTerrainWalkable. **Closes #87, #85, and the wall-pass-through portion of #84 (fully closes #84).** Files #88 (indoor static object vibration — pre-existing) and #89 (BSPQuery.SphereIntersectsCellBsp — approximation in CheckBuildingTransit). `[cell-transit]`, `[indoor-bsp]`, `[check-bldg]`, `[cell-cache]` probes stay in place. Handoff: [`docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md`](../research/2026-05-19-indoor-walking-phase2-shipped-handoff.md). | Live ✓ |
|
||||||
|
|
||||||
Plus polish that doesn't get its own phase number:
|
Plus polish that doesn't get its own phase number:
|
||||||
- FlyCamera default speed lowered + Shift-to-boost
|
- FlyCamera default speed lowered + Shift-to-boost
|
||||||
|
|
|
||||||
|
|
@ -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