acdream/docs/research/2026-05-25-issue-100-shipped-and-culling-handoff.md
Erik 4cbfbf98af docs: #100 ship + indoor-cell culling investigation handoff
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's f48c74a / 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>
2026-05-25 22:17:51 +02:00

20 KiB
Raw Blame History

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 (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 — 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 — 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 — 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 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.