Session-end documentation for the issue #100 ship and the visibility- culling investigation handoff for the next session. Three documents land together: - docs/superpowers/plans/2026-05-25-issue-100-terrain-cutout.md (the 3-task plan that drove this session'sf48c74a/a64e6f2/84e3b72— never committed by Tasks 1-2) - docs/research/2026-05-25-issue-100-terrain-cutout-handoff.md (the predecessor session's smoking-gun research that drove the #100 fix — never committed by the prior session) - docs/research/2026-05-25-issue-100-shipped-and-culling-handoff.md (THIS session's handoff: what shipped, what visual-verification surfaced, the issue family map for #78 + #95 + the new cellar- stairs finding, root-cause hypothesis, retail anchors, WB references, do-not-retry list, and pickup prompt for the next session's investigation + plan + implementation) Plus two updates to existing files: - CLAUDE.md — adds a ship paragraph for #100 to the M1.5 progress block. References the new handoff doc as the next-session pickup point. - docs/ISSUES.md #78 — broadens scope from "outdoor stabs visible through floor" to "outdoor stabs + terrain mesh visible inside EnvCells". Adds the 2026-05-25 cellar-stairs evidence (per user direction: not filed as new issue; treated as evidence reinforcing #78's hypothesis #2). Promotes hypothesis #2 to "high confidence as of 2026-05-25" and adds the retail anchor (acclient_2013_pseudo_c.txt:311397 CEnvCell::find_visible_child_cell). Acceptance criteria broadened to include the cellar-stairs case. Next session: pickup prompt at the bottom of the new handoff doc drives a /investigate → writing-plans → subagent-driven-development pass on indoor-cell visibility culling — the work that closes #78 + cellar-stairs together, and possibly #95 if the infrastructure overlaps. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
313 lines
20 KiB
Markdown
313 lines
20 KiB
Markdown
# Issue #100 shipped + indoor-cell culling investigation handoff
|
||
|
||
**Date:** 2026-05-25 PM
|
||
**Status:** Issue #100 SHIPPED (visually verified for primary acceptance). Visual verification surfaced a NEW finding in the same family as issue #78 — outdoor terrain mesh visible inside cottage cellars at certain camera angles. Next session: deep investigation + plan + port retail's indoor-cell visibility culling to close the family.
|
||
**Branch:** `claude/strange-albattani-3fc83c` (worktree)
|
||
**Predecessor handoff:** [docs/research/2026-05-25-issue-100-terrain-cutout-handoff.md](2026-05-25-issue-100-terrain-cutout-handoff.md) (the prior session's smoking-gun research that drove the #100 fix).
|
||
|
||
---
|
||
|
||
## TL;DR
|
||
|
||
**Shipped this session (3 commits):**
|
||
- `f48c74a` — Task 1: terrain shader Z nudge (retail `zFightTerrainAdjust = 0.00999999978`)
|
||
- `a64e6f2` — Task 2: removed ~50 LOC of `hiddenTerrainCells` / `BuildingTerrainCells` plumbing across 7 source files + 2 test files, closed #100 in ISSUES.md
|
||
- `84e3b72` — docs: stabilized Task 2's SHA reference in ISSUES.md (follow-up commit, not amend)
|
||
|
||
**Visual verification result (Holtburg, live ACE):**
|
||
- ✅ **Primary acceptance:** transparent rectangles around houses are GONE. Ground reads as continuous cobblestone / grass around every cottage observed. Issue #100's user-visible symptom is closed.
|
||
- ❌ **New finding:** standing inside a cottage cellar with the camera positioned such that the cottage walls don't fully occlude the view, the outdoor terrain mesh renders as a sharp-edged grass rectangle over the cellar stairs and floor. **Clears when the camera moves closer** (camera position changes such that cottage geometry properly occludes). **Gameplay unaffected** — player can walk down/up the stairs normally.
|
||
|
||
**Root cause hypothesis for the new finding (HIGH CONFIDENCE):** indoor-cell visibility culling is not gating outdoor terrain rendering. The outdoor terrain mesh is now (correctly per retail) rendered everywhere on the 192 m landblock — including in 3D regions occupied by indoor `CEnvCell` volumes. When the camera is in an indoor cell, the outdoor terrain mesh should be EXCLUDED from the draw set unless an outdoor cell is reachable via portal LOS from the camera's cell. acdream does not currently perform this culling.
|
||
|
||
**This is the same root cause as filed issue #78** ("Outdoor stabs/buildings visible through the rendered floor" at the inn), just with outdoor *terrain* affected instead of outdoor *stabs*. #78 was filed 2026-05-19 with the hypothesis "Outdoor stabs aren't being culled when the player is inside an EnvCell — this is the Phase 1 Task 3 deferred work ('Cull outdoor stabs when indoors via VisibleCellIds')." We never returned to it.
|
||
|
||
---
|
||
|
||
## The visibility-culling issue family
|
||
|
||
Three filed/observed issues likely share infrastructure:
|
||
|
||
| ID | Symptom | Domain |
|
||
|---|---|---|
|
||
| **#78** (OPEN) | Inside Holtburg Inn, outdoor stabs/buildings visible THROUGH the floor and walls | Outdoor stabs not culled when camera in indoor cell |
|
||
| **Cellar-stairs** (NEW, observed 2026-05-25 PM) | Inside cottage cellar, outdoor terrain mesh visible covering stair geometry at certain camera angles | Outdoor terrain not culled when camera in indoor cell |
|
||
| **#95** (OPEN) | Entering dungeon via portal, `visibleCells` per cell jumps from ~4-7 to **135-145**, including cells from other landblocks; see-through walls, other-dungeon geometry visible | Indoor→indoor portal-graph traversal blowup (over-inclusion) |
|
||
|
||
#78 and the cellar-stairs finding are the **same bug** (outdoor geometry not culled when camera is in an indoor cell) with different geometry classes affected. **They should close together.**
|
||
|
||
#95 is a sibling — same visibility-culling SUBSYSTEM but different specific failure (indoor→indoor over-inclusion via unrooted portal recursion). It might or might not close as a side effect of the #78/cellar-stairs fix; the next session should determine if the infrastructure overlaps enough to fix both, or whether #95 needs its own work.
|
||
|
||
Additional adjacent issues (probably NOT same root cause but worth noting):
|
||
- **#79, #80, #81, #93, #94** — indoor lighting bugs. Filed under A7 (M1.5 lighting fidelity). Some may share visibility plumbing (e.g., if lights from outdoor entities leak into indoor cells, that's a visibility issue).
|
||
|
||
---
|
||
|
||
## Why I'm confident this is culling, not Z-fighting
|
||
|
||
Three signals, ordered by weight:
|
||
|
||
1. **Patch geometry is too large.** A Z-precision Z-fight at coplanar 1 cm separation would manifest as a thin ~0.3 m strip on the topmost stair tread (Z=94). The observed patch is sharp-edged rectangular geometry the size of a terrain cell footprint (likely 24 m × 24 m in landblock-local space), covering multiple stair steps and floor area. That's a polygon, not a precision artifact.
|
||
|
||
2. **"Clears when closer" matches geometric occlusion, not depth precision.** If 1 cm depth-buffer precision were failing, closer camera distance would PASS more cleanly (precision tightens). The user reports the patch clears as they approach the stairs — consistent with cottage walls + stair treads now occluding the terrain in screen space. At 2-5 m camera distance and 24-bit depth buffer, the 1 cm nudge has sub-millimeter resolving power; precision is not the bottleneck.
|
||
|
||
3. **Exact match for #78's hypothesis #2 mechanism.** #78 ("outdoor stabs visible through cell walls") was filed 2026-05-19 with hypothesis: outdoor stabs aren't culled when player is in an EnvCell; WB has a `RenderInsideOut` stencil pipeline that acdream never invokes. The cellar-stairs case is the same mechanism applied to outdoor terrain mesh.
|
||
|
||
**One test that could falsify culling-as-cause:** stand at the spot showing the artifact, look at the grass patch, rotate the camera slowly without moving the character. If the patch FLICKERS / shimmers as you turn, that's Z-fight (depth precision unstable across angles). If the patch stays geometrically stable (its polygon edges move predictably with the camera, but it doesn't flicker), that's culling. The screenshot suggested polygon-stable edges — consistent with culling — but rotating the camera is the definitive test, and the next session should do this in the first 60 seconds of visual checking before planning the fix.
|
||
|
||
---
|
||
|
||
## Existing apparatus the next session can use
|
||
|
||
### acdream's current visibility code
|
||
|
||
**[`src/AcDream.App/Rendering/CellVisibility.cs`](../../src/AcDream.App/Rendering/CellVisibility.cs)** — portal-based interior cell visibility system ported from ACME's `EnvCellManager.cs`. Exposes:
|
||
- `FindCameraCell(...)` — resolves which EnvCell the camera is in.
|
||
- `PointInCell(...)` — point-in-cell test with `PointInCellEpsilon = 0.01f`.
|
||
- `GetVisibleCells(...)` — returns `VisibleCellIds` set for the camera's current cell, via portal-chain traversal.
|
||
- `CellSwitchGraceFrameCount = 3` — anti-flicker grace period for cell transitions.
|
||
|
||
**[`src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs`](../../src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs)** — per-entity draw filter. Per ISSUES.md #78 line 165, "the dispatcher already filters by `entity.ParentCellId ∈ visibleCellIds` but outdoor stabs have `ParentCellId == null` so they always pass." This is the gate we need to extend.
|
||
|
||
**[`src/AcDream.App/Rendering/TerrainModernRenderer.cs`](../../src/AcDream.App/Rendering/TerrainModernRenderer.cs)** — terrain dispatcher. Currently renders ALL loaded landblocks unconditionally. Needs to learn about indoor-camera-state to optionally skip outdoor-cell terrain cells.
|
||
|
||
### Probes available
|
||
|
||
From CLAUDE.md "Diagnostic env vars":
|
||
- `ACDREAM_PROBE_CELL=1` — one `[cell-transit]` line per `PlayerMovementController.CellId` change. Useful for verifying when the camera is in an indoor vs outdoor cell.
|
||
- `ACDREAM_PROBE_RESOLVE=1` — full physics resolver trace.
|
||
- Runtime-toggleable via the DebugPanel "Diagnostics" section.
|
||
|
||
No existing probe instruments the rendering visibility decision — the next session might add one (`ACDREAM_PROBE_VIS=1` that logs the camera's resolved cell + `VisibleCellIds` set per N frames).
|
||
|
||
### Retail oracle anchors
|
||
|
||
```
|
||
docs/research/named-retail/acclient_2013_pseudo_c.txt:311397
|
||
CEnvCell::find_visible_child_cell (address 0x0052dc50)
|
||
|
||
docs/research/named-retail/acclient_2013_pseudo_c.txt:280028
|
||
call site: eax_6 = CEnvCell::find_visible_child_cell(eax_5, &__return, arg5);
|
||
```
|
||
|
||
Grep further for `find_visible`, `visibility`, `cull`, `RenderDeviceD3D::DrawBlock`, `ACRender::draw`, etc. The retail render loop's visibility chain — pre-frame walk-down from the camera's cell through portal-visible neighbours — is the target to port.
|
||
|
||
### WorldBuilder reference
|
||
|
||
```
|
||
references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs
|
||
references/WorldBuilder/Chorizite.OpenGLSDLBackend/GameScene.cs
|
||
```
|
||
|
||
WB has a `RenderInsideOut` mechanism in these files. Per #78's hypothesis, "acdream never invokes" this pipeline. The next session should determine whether to (a) invoke WB's existing code from our render path, (b) port the algorithm to acdream's namespaces, or (c) write a retail-faithful port from the named-retail decomp directly. CLAUDE.md's WB inventory policy applies — read [docs/architecture/worldbuilder-inventory.md](../architecture/worldbuilder-inventory.md) before deciding.
|
||
|
||
---
|
||
|
||
## Do-not-retry list for the next session
|
||
|
||
1. **Don't try to roll back the #100 fix.** The transparent-rectangle bug was a universal symptom on every Holtburg house. The cellar-stairs artifact is conditional and camera-angle-dependent. Reverting #100 trades a worse bug for a less-bad one.
|
||
|
||
2. **Don't try to solve the cellar-stairs case by lowering the terrain Z further** (e.g., bumping the shader nudge from 0.01 to 0.1 or 1.0). The visible terrain is rendered at its correct Z; the issue is that it's visible AT ALL inside the indoor cell. Bigger nudge doesn't help and would break coplanar-floor disambiguation elsewhere.
|
||
|
||
3. **Don't try to solve it by hiding terrain cells based on the building footprint again.** That was issue #100's bug — cell-level hiding is too coarse (cottage ~12 m × 12 m in a 24 m × 24 m cell). The right granularity is per-camera-state visibility, not per-cell mesh modification.
|
||
|
||
4. **Don't try to fix this with depth tricks** (disable depth-write for terrain, etc.) — those break elsewhere and aren't retail-faithful.
|
||
|
||
5. **Don't conflate this with #82** (some slope terrain lit incorrectly). #82 is about per-vertex normal calculation; the cellar-stairs artifact is about which polygons render at all, not how they're shaded.
|
||
|
||
6. **Don't try to land a 1-line fix for this.** Indoor-cell visibility culling is a real system to port. Single-line patches at the symptom site (e.g., "if camera in cellar, skip terrain") would close cellar-stairs but not #78 — and would be the kind of workaround CLAUDE.md prohibits. Per the project rule, fix the root cause: port the visibility computation properly.
|
||
|
||
7. **Don't trust the WB `RenderInsideOut` code blindly.** WB's editor view has known visibility quirks (per the predecessor handoff: "WB has a known Z-fighting issue in the editor view that nobody noticed because it's editor-only"). Cross-reference WB against retail before adopting.
|
||
|
||
---
|
||
|
||
## Open questions for the next session to answer
|
||
|
||
1. **Is the cellar-stairs artifact 100% culling, or partly Z-precision?** The first verification step is the camera-rotation test described above (rotate without moving — flicker = Z-fight, stable = culling). Until this is confirmed, the diagnosis remains "high confidence" but not certain.
|
||
|
||
2. **Does the #78 + cellar-stairs fix also close #95?** The two are in the same family but #95's specific failure (over-inclusion of indoor cells via portal recursion) might need a separate cap-traversal-depth fix. The next session should map the shared infrastructure before committing to a combined-or-split plan.
|
||
|
||
3. **What's the right Phase identifier?** M1.5 doesn't have a "visibility" sub-phase yet. A6 is physics; A7 is lighting. Visibility might warrant its own A-letter (A8?) or be slotted under whichever existing structure makes sense. Discuss with user at the start of the next session before naming the work.
|
||
|
||
4. **Should the cellar-stairs case be documented in #78** as additional evidence, or filed as a separate issue tied to #78? Per user direction (2026-05-25 PM session-end): don't file a new issue; treat as evidence for #78. The next session's investigation should formalize this — possibly by editing #78 to broaden its description to "outdoor geometry (stabs + terrain) visible inside EnvCells."
|
||
|
||
---
|
||
|
||
## Pickup prompt for the next session
|
||
|
||
```
|
||
Indoor-cell visibility culling — port retail's mechanism to close
|
||
issue #78 (outdoor stabs visible through inn floor) and the new
|
||
cellar-stairs visual artifact discovered while visual-verifying
|
||
the #100 fix on 2026-05-25.
|
||
|
||
Read first (in this order):
|
||
1. docs/research/2026-05-25-issue-100-shipped-and-culling-handoff.md
|
||
(this doc — full session handoff with the family map, root-cause
|
||
hypothesis, retail anchors, WB references, do-not-retry list)
|
||
2. docs/ISSUES.md #78 (the filed issue; same root cause as the
|
||
cellar-stairs finding)
|
||
3. docs/ISSUES.md #95 (sibling visibility issue; verify whether
|
||
it closes as a side effect)
|
||
4. CLAUDE.md — search "currently working toward" to refresh state
|
||
|
||
State both altitudes:
|
||
Currently working toward: M1.5 — Indoor world feels right
|
||
Current phase: TBD (visibility culling; new sub-phase to name
|
||
with the user at session start — possibly A8 if A6=physics,
|
||
A7=lighting follow this naming, OR fits under an existing
|
||
A6 sub-phase)
|
||
|
||
## Session flow (three phases, in order)
|
||
|
||
### Phase 1 — Investigate (use the /investigate skill)
|
||
|
||
Independently verify the hypothesis and locate the retail mechanism.
|
||
Specifically:
|
||
|
||
a. Run the camera-rotation falsification test on the cellar-stairs
|
||
artifact. Stand in a Holtburg cottage cellar at a position where
|
||
the grass overlay is visible, rotate the camera slowly without
|
||
moving. If the patch stays geometrically stable (polygon edges
|
||
move predictably), confirms culling. If it flickers / shimmers,
|
||
pivot the diagnosis to Z-precision.
|
||
|
||
b. Grep named-retail for the visibility chain. Anchors to start
|
||
from:
|
||
acclient_2013_pseudo_c.txt:311397 — CEnvCell::find_visible_child_cell
|
||
acclient_2013_pseudo_c.txt:280028 — call site
|
||
Find: RenderDeviceD3D::DrawBlock (around line 430027 per the
|
||
#100 predecessor handoff), the visibility computation that
|
||
precedes it, and how it gates outdoor-cell rendering when the
|
||
camera is in an indoor cell.
|
||
|
||
c. Read WorldBuilder's visibility implementation:
|
||
references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs
|
||
references/WorldBuilder/Chorizite.OpenGLSDLBackend/GameScene.cs
|
||
Specifically the RenderInsideOut stencil pipeline that #78
|
||
flags as "acdream never invokes." Decide whether to adopt
|
||
wholesale, port to our namespaces, or write fresh from
|
||
retail.
|
||
|
||
d. Read acdream's existing visibility code:
|
||
src/AcDream.App/Rendering/CellVisibility.cs
|
||
src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs
|
||
src/AcDream.App/Rendering/TerrainModernRenderer.cs
|
||
Understand the current per-entity gate (filters by
|
||
entity.ParentCellId ∈ visibleCellIds, but outdoor stabs
|
||
have null ParentCellId so they always pass — that's the bug).
|
||
|
||
e. Determine whether #95's symptom (visibleCells exploding to
|
||
135-145 at network hubs) closes as a side effect or needs
|
||
its own work. Read scen5 acdream.log if it's still in the
|
||
research tree.
|
||
|
||
Output of Phase 1: a short report — either "culling is confirmed
|
||
and here's the retail anchor / WB code / acdream extension point"
|
||
or "diagnosis pivot needed, here's the new shape." Plus a
|
||
fix-shape sketch. Get user approval before Phase 2.
|
||
|
||
### Phase 2 — Plan (use the superpowers:writing-plans skill)
|
||
|
||
Draft the implementation plan. The shape depends on Phase 1
|
||
findings, but likely 4-6 tasks:
|
||
|
||
- Task 1: Build the diagnostic probe (ACDREAM_PROBE_VIS=1 logging
|
||
camera cell + VisibleCellIds + which entities/cells get
|
||
rendered) — apparatus first, per CLAUDE.md's "apparatus for
|
||
physics bugs" memory note generalized to rendering.
|
||
- Task 2: Extend the WbDrawDispatcher per-entity gate to skip
|
||
outdoor entities (ParentCellId == null) when the camera's
|
||
current cell is indoor AND no outdoor cell is in VisibleCellIds.
|
||
- Task 3: Extend the TerrainModernRenderer to skip outdoor
|
||
landblocks under the same condition (or to skip individual
|
||
cells if the granularity matters — let the retail decomp
|
||
decide).
|
||
- Task 4: (Possibly) Port the portal-LOS chain that decides
|
||
which outdoor cells ARE visible from inside an indoor cell
|
||
via doors/windows — so transitions through doorways don't
|
||
abruptly cull and re-add geometry. Read retail's clip-plane
|
||
portal test for this.
|
||
- Task 5: (Possibly) Address #95's traversal-depth cap if
|
||
Phase 1 confirms it's not closed by the #78 fix.
|
||
- Task 6: Visual verification — at Holtburg cottages (cellar
|
||
stairs no longer show terrain), Holtburg Inn (outdoor stabs
|
||
no longer visible through walls), and a portal-entry dungeon
|
||
(visibleCells stays in a sane range if #95 is in scope).
|
||
|
||
### Phase 3 — Implement (use superpowers:subagent-driven-development)
|
||
|
||
Same pattern as the #100 session: fresh subagent per task,
|
||
two-stage review per task (spec + code quality), final review
|
||
across all commits, visual verification by user as the
|
||
acceptance test.
|
||
|
||
## Constraints
|
||
|
||
Per CLAUDE.md "no workarounds" rule — fix the root cause, do not
|
||
patch symptom sites. Visibility culling is a real system, not a
|
||
one-line gate.
|
||
|
||
Read the do-not-retry list in this handoff doc (7 items) before
|
||
starting Phase 2.
|
||
|
||
Visual verification is the acceptance test. The fix must close
|
||
the cellar-stairs artifact AND #78's "outdoor stabs through floor"
|
||
AND not regress #100's transparent-rectangle resolution. Be
|
||
honest about partial results.
|
||
|
||
## Reference repo hierarchy reminder
|
||
|
||
Per CLAUDE.md "Reference repos: cross-check the relevant ones" —
|
||
for visibility/culling work, the relevant references are:
|
||
- Retail decomp (docs/research/named-retail/) — primary oracle
|
||
- WorldBuilder VisibilityManager + GameScene — implementation reference
|
||
- ACE has minimal coverage here (it's server-side; client visibility
|
||
is not its concern)
|
||
- holtburger is TUI, no rendering visibility
|
||
- AC2D has fixed-function rendering — limited modern relevance
|
||
|
||
Cross-reference retail + WB. If they diverge, retail wins.
|
||
|
||
## What success looks like
|
||
|
||
After this work lands:
|
||
- Standing in a Holtburg cottage cellar at the exact spot of the
|
||
2026-05-25 screenshot artifact, no grass overlay on stairs from
|
||
ANY camera angle.
|
||
- Standing inside Holtburg Inn, no outdoor stabs visible through
|
||
floor or walls.
|
||
- Entering a dungeon via the Town Network portal, visibleCells
|
||
per cell stays in the ~4-15 range (if #95 in scope).
|
||
- No regression on issue #100 (no transparent rectangles around
|
||
houses).
|
||
- dotnet build green; dotnet test failures within the documented
|
||
14-23 flaky window.
|
||
```
|
||
|
||
---
|
||
|
||
## CLAUDE.md update (post-handoff)
|
||
|
||
Pending. The CLAUDE.md ship paragraph for #100 was deferred to "after visual verification confirms" — visual verification PARTIALLY confirmed (primary acceptance met, secondary artifact in same family as existing #78). The next session can either:
|
||
- Add a brief CLAUDE.md ship entry now mentioning #100 closed + cellar-stairs finding linked to #78
|
||
- Skip until #78 / cellar-stairs lands, then add a combined paragraph
|
||
|
||
Recommendation: add it now (issue #100 is genuinely closed by its own criteria). The cellar-stairs work is a NEW investigation, not a continuation of #100.
|
||
|
||
---
|
||
|
||
## Files state at session end
|
||
|
||
```
|
||
Branch: claude/strange-albattani-3fc83c
|
||
HEAD: 84e3b72 docs: #100 — stabilize Task 2 SHA reference in ISSUES.md
|
||
Parent: a64e6f2 refactor: #100 — remove hiddenTerrainCells / BuildingTerrainCells plumbing
|
||
Grandparent: f48c74a fix(render): #100 — render terrain 1 cm below physical Z (retail zFightTerrainAdjust)
|
||
Before #100: 2fc312e docs: #101 — fix fabricated content in Recently closed entry
|
||
|
||
Working tree: clean
|
||
Untracked: pre-flight-test-baseline.log, issue100-verify-launch.log (logs, can be deleted/gitignored)
|
||
```
|
||
|
||
Both log files are session-scoped; the next session can either delete them or ignore them. They aren't committed.
|