Both documents retained for historical reference. The new full-WB-port design + plan (2026-05-26-phase-a8-wb-full-port-design.md + plan,ea60d1f+651e7e2) replace them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1148 lines
53 KiB
Markdown
1148 lines
53 KiB
Markdown
# Phase A8 — Render-Frame Restructure to WB-Faithful Order Implementation Plan
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Goal:** Replace the R3.5 v2 "frankenstein" render frame (initial terrain + depth-clear-if-inside + stencil pipeline, three workarounds stacked) with WorldBuilder's `RenderInsideOut` order verbatim. Close R4 Issue B (sky doesn't render through windows). Match the proven reference. Pre-restructure falsification spike (RR0) determines whether R4 Issues A + C are pre-existing on `main` (out of A8 scope) or A8-caused (re-brainstorm before continuing).
|
||
|
||
**Architecture:** Skip initial sky + terrain at the start of the render frame when the camera is inside a cell; delete the depth-clear-if-inside block entirely; inside the indoor branch insert a stencil-gated `_skyRenderer.RenderSky` step (DepthMask off) between `EnableOutdoorPass` and the terrain re-draw so windows show real sky; unify the two-flag asymmetry (`cameraInsideCell` lenient + `cameraReallyInside` strict) into a single strict `cameraInside` flag computed via `PointInCell`. Grace mechanism in `CellVisibility` stays alive for non-render consumers.
|
||
|
||
**Tech Stack:** C# .NET 10, Silk.NET (OpenGL 4.3 + GL_ARB_bindless_texture + GL_ARB_shader_draw_parameters), xUnit.
|
||
|
||
**Predecessor context (REQUIRED reading before starting):**
|
||
- [docs/superpowers/specs/2026-05-26-phase-a8-restructure-design.md](../specs/2026-05-26-phase-a8-restructure-design.md) — the approved design this plan implements
|
||
- [docs/research/2026-05-26-a8-r3.5-restructure-handoff.md](../../research/2026-05-26-a8-r3.5-restructure-handoff.md) — full handoff covering the R3.5 v1+v2 saga and architectural mismatch
|
||
- [references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs:73-239](../../../references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs) — the proven WB reference; the restructure must match this verbatim through Step 4. Step 5 (3-stencil-bit cross-building) stays deferred.
|
||
|
||
**Infrastructure preserved (consume as-is — these are already shipped + tested):**
|
||
- `src/AcDream.App/Rendering/IndoorCellStencilPipeline.cs` — `UploadPortalMesh` / `MarkAndPunch` / `EnableOutdoorPass` / `DisableStencil`
|
||
- `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs` — `EntitySet.{IndoorPass, OutdoorScenery, LiveDynamic, All}` partition (R2)
|
||
- `src/AcDream.Core/World/WorldEntity.cs` — `IsBuildingShell` flag (R1)
|
||
- `src/AcDream.App/Rendering/CellVisibility.cs` — `PointInCell` static, `ComputeVisibility`, `FindCameraCell` with grace mechanism
|
||
|
||
**HEAD at start of session:** `2bfeafd` (R3.5 v2)
|
||
**Clean revert points:** `60f07bc` (R3 baseline) or further back to `55f26f2` (R2)
|
||
|
||
---
|
||
|
||
## File Structure
|
||
|
||
| File | What changes | Why |
|
||
|---|---|---|
|
||
| `docs/research/2026-05-26-a8-rr0-falsification-findings.md` | NEW doc capturing RR0 outcome on three branches | Decision evidence for whether A+C are A8-caused or pre-existing |
|
||
| `src/AcDream.App/Rendering/GameWindow.cs` | Render-frame block, lines ~7011–7260: gate-flag rename + skip-sky+terrain when inside + delete-depth-clear + add stencil-gated sky inside indoor branch | The restructure itself |
|
||
| `src/AcDream.App/Rendering/SkyRenderer.cs` | READ ONLY in RR3; possibly a small wrapper invocation in `GameWindow.cs` if dirty | Pre-flight verify stencil state contract |
|
||
| `docs/ISSUES.md` | Move #78 to "Recently closed"; file new follow-ups (cross-cell-portal, cellar Z-fight from outside, and A/C if RR0 confirms pre-existing on main) | Ship docs |
|
||
| `CLAUDE.md` | Update A8 paragraph: `PAUSED` → `SHIPPED 2026-05-2X` with the restructure commit list | Roadmap discipline |
|
||
|
||
**Decomposition rationale:** the restructure is localized to one render-frame block in one file. The other files are docs. Six tasks because of the mandatory pre-spike (RR0) and the SkyRenderer verification (RR3); the core restructure is one commit (RR2).
|
||
|
||
---
|
||
|
||
## Task RR0: Falsification spike — are Issues A + C pre-existing or A8-caused?
|
||
|
||
**Goal:** Determine whether R4 Issues A ("objects through ground + walls missing on exit") and C ("transparent floor showing cellar on entry") reproduce on `main` (no A8 work) and on `60f07bc` (R3 baseline, before R3.5 patches). This is the decision gate for whether the restructure as designed is sufficient or whether scope must expand.
|
||
|
||
**Files:**
|
||
- Create: `docs/research/2026-05-26-a8-rr0-falsification-findings.md`
|
||
|
||
**Method:** three side-by-side launches. Human visual observation per scenario. Findings doc records what was seen on each branch and the decision outcome.
|
||
|
||
- [ ] **RR0-S1: Launch HEAD (2bfeafd) and observe Issues A + C**
|
||
|
||
Build + launch in the foreground (the human watches the client window):
|
||
|
||
```powershell
|
||
$proc = Get-Process -Name AcDream.App -ErrorAction SilentlyContinue
|
||
if ($proc) {
|
||
$proc.CloseMainWindow() | Out-Null
|
||
if (-not $proc.WaitForExit(5000)) { $proc | Stop-Process -Force }
|
||
}
|
||
Start-Sleep -Seconds 3
|
||
|
||
$env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call"
|
||
$env:ACDREAM_LIVE = "1"
|
||
$env:ACDREAM_TEST_HOST = "127.0.0.1"
|
||
$env:ACDREAM_TEST_PORT = "9000"
|
||
$env:ACDREAM_TEST_USER = "testaccount"
|
||
$env:ACDREAM_TEST_PASS = "testpassword"
|
||
|
||
dotnet build src\AcDream.App\AcDream.App.csproj -c Debug --nologo
|
||
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 |
|
||
Tee-Object -FilePath "launch-a8-rr0-head.log"
|
||
```
|
||
|
||
Human walks `+Acdream` through these scenarios:
|
||
1. Stand outside Holtburg cottage. Walk into cottage. Watch the entry transition for ~3 seconds. Observe: does the cottage floor go transparent showing cellar below, with "wrong texture" flicker?
|
||
2. Stand inside cottage. Walk back outside. Watch the exit transition for ~3 seconds. Observe: do objects appear visible through the ground? Do walls of other buildings (adjacent cottages) appear missing?
|
||
|
||
Record per-scenario:
|
||
- Issue C (entry transparent floor): YES / NO / INTERMITTENT
|
||
- Issue A (exit through-ground): YES / NO / INTERMITTENT
|
||
|
||
Graceful close (use `$proc.CloseMainWindow()` pattern).
|
||
|
||
- [ ] **RR0-S2: Test against R3 baseline by checking out 60f07bc's GameWindow.cs only**
|
||
|
||
`60f07bc` is the R3 baseline (before R3.5 v1+v2 patches). It includes R1+R2 infrastructure (which we want), but the render-frame block is pre-patch. Single-file checkout keeps all other R1+R2 code intact:
|
||
|
||
```bash
|
||
git checkout 60f07bc -- src/AcDream.App/Rendering/GameWindow.cs
|
||
dotnet build src\AcDream.App\AcDream.App.csproj -c Debug --nologo
|
||
```
|
||
|
||
Expected: build green (R1+R2 deps unchanged between 60f07bc and HEAD).
|
||
|
||
Launch + repro the same two scenarios from S1, logging to `launch-a8-rr0-r3.log`:
|
||
|
||
```powershell
|
||
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 |
|
||
Tee-Object -FilePath "launch-a8-rr0-r3.log"
|
||
```
|
||
|
||
Record per-scenario: Issue C, Issue A outcomes.
|
||
|
||
Graceful close. Restore HEAD's `GameWindow.cs`:
|
||
|
||
```bash
|
||
git checkout HEAD -- src/AcDream.App/Rendering/GameWindow.cs
|
||
dotnet build src\AcDream.App\AcDream.App.csproj -c Debug --nologo
|
||
```
|
||
|
||
Expected: build green; working tree clean.
|
||
|
||
- [ ] **RR0-S3: Test against main using a side worktree**
|
||
|
||
Main has no A8 work at all. Need a separate worktree to test it without disturbing the current worktree:
|
||
|
||
```bash
|
||
git fetch origin main
|
||
git worktree add ../tmp-main-baseline main
|
||
```
|
||
|
||
Expected: new worktree created at `../tmp-main-baseline` checked out to `main`.
|
||
|
||
Build + launch from the side worktree:
|
||
|
||
```powershell
|
||
Push-Location ..\tmp-main-baseline
|
||
|
||
$proc = Get-Process -Name AcDream.App -ErrorAction SilentlyContinue
|
||
if ($proc) {
|
||
$proc.CloseMainWindow() | Out-Null
|
||
if (-not $proc.WaitForExit(5000)) { $proc | Stop-Process -Force }
|
||
}
|
||
Start-Sleep -Seconds 3
|
||
|
||
dotnet build src\AcDream.App\AcDream.App.csproj -c Debug --nologo
|
||
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 |
|
||
Tee-Object -FilePath "launch-a8-rr0-main.log"
|
||
|
||
Pop-Location
|
||
```
|
||
|
||
Record per-scenario: Issue C, Issue A outcomes.
|
||
|
||
Graceful close. Remove the side worktree:
|
||
|
||
```bash
|
||
git worktree remove ../tmp-main-baseline
|
||
```
|
||
|
||
- [ ] **RR0-S4: Write findings doc and commit**
|
||
|
||
Create `docs/research/2026-05-26-a8-rr0-falsification-findings.md` with this template (fill in the actual observations from S1–S3):
|
||
|
||
```markdown
|
||
# A8 RR0 falsification — are Issues A and C pre-existing or A8-caused?
|
||
|
||
**Date:** 2026-05-26
|
||
**Method:** three-branch launch + visual repro at Holtburg cottage entry / exit transitions.
|
||
|
||
## Observations
|
||
|
||
| Branch | Commit | Issue A (exit through-ground) | Issue C (entry transparent floor) |
|
||
|---|---|---|---|
|
||
| HEAD | `2bfeafd` (R3.5 v2) | [YES / NO / INTERMITTENT] | [YES / NO / INTERMITTENT] |
|
||
| R3 baseline | `60f07bc` | [YES / NO / INTERMITTENT] | [YES / NO / INTERMITTENT] |
|
||
| main | [current main SHA] | [YES / NO / INTERMITTENT] | [YES / NO / INTERMITTENT] |
|
||
|
||
## Decision
|
||
|
||
Based on the table:
|
||
|
||
- [ ] **Outcome 1:** All three reproduce → A + C are pre-existing on main. Restructure proceeds RR1→RR5 unchanged. RR5 files A + C as new ISSUES.md entries.
|
||
|
||
- [ ] **Outcome 2:** Only R3 + HEAD reproduce → A and/or C caused by R3 work specifically. **PAUSE** the plan. Re-brainstorm via `superpowers:brainstorming` to address them; update the design doc; resume.
|
||
|
||
- [ ] **Outcome 3:** Only HEAD reproduces → R3.5 v1/v2 patches caused them. Revert in RR1 clears them. Restructure proceeds; RR4 confirms cleanup.
|
||
|
||
Selected: [outcome #]
|
||
|
||
## Logs
|
||
|
||
- `launch-a8-rr0-head.log`
|
||
- `launch-a8-rr0-r3.log`
|
||
- `launch-a8-rr0-main.log`
|
||
```
|
||
|
||
Commit the findings:
|
||
|
||
```bash
|
||
git add docs/research/2026-05-26-a8-rr0-falsification-findings.md
|
||
git commit -m "$(cat <<'EOF'
|
||
docs(research): Phase A8 RR0 — falsification findings (A + C pre-existing?)
|
||
|
||
Side-by-side launch on HEAD (2bfeafd) / R3 baseline (60f07bc) / main.
|
||
Visual repro at Holtburg cottage entry + exit transitions. Records
|
||
whether Issues A and C are A8-caused or pre-existing.
|
||
|
||
Decision gate for whether the restructure as designed is sufficient
|
||
or whether RR2 scope must expand.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
- [ ] **RR0-S5: Decision gate — proceed or pause**
|
||
|
||
If selected outcome is 1 or 3 → proceed to RR1.
|
||
|
||
If selected outcome is 2 → **STOP**. Re-invoke `superpowers:brainstorming` to expand the design. Do NOT proceed to RR1.
|
||
|
||
---
|
||
|
||
## Task RR1: Revert R3.5 v1 + v2 as two new commits
|
||
|
||
**Goal:** Clean diff against R3 baseline for the restructure that follows. The v1+v2 patches are subsumed by RR2 (the stencil branch will use `cameraInside`; depth-clear gets deleted entirely).
|
||
|
||
**Files:**
|
||
- Modify (via `git revert`): `src/AcDream.App/Rendering/GameWindow.cs`
|
||
|
||
- [ ] **RR1-S1: Revert R3.5 v2 (depth-clear gate)**
|
||
|
||
```bash
|
||
git revert 2bfeafd --no-edit
|
||
```
|
||
|
||
Expected: one new commit lands. `git log -1` shows `Revert "fix(render): Phase A8 R3.5 v2 — gate depth-clear on cameraReallyInside too"`.
|
||
|
||
- [ ] **RR1-S2: Revert R3.5 v1 (stencil branch gate)**
|
||
|
||
```bash
|
||
git revert 38d5374 --no-edit
|
||
```
|
||
|
||
Expected: one new commit lands. `git log -1` shows `Revert "fix(render): Phase A8 R3.5 — gate stencil branch on PointInCell containment"`.
|
||
|
||
- [ ] **RR1-S3: Verify the working tree matches the R3 baseline render frame**
|
||
|
||
```bash
|
||
git diff 60f07bc HEAD -- src/AcDream.App/Rendering/GameWindow.cs
|
||
```
|
||
|
||
Expected: empty diff. The render-frame block at HEAD now matches `60f07bc`'s GameWindow.cs verbatim.
|
||
|
||
If the diff is NOT empty, investigate — likely a non-A8 commit landed between 60f07bc and 2bfeafd that touched GameWindow.cs unrelated to R3.5 (uncommon but possible).
|
||
|
||
- [ ] **RR1-S4: Build + test green**
|
||
|
||
```bash
|
||
dotnet build -c Debug --nologo
|
||
dotnet test --nologo
|
||
```
|
||
|
||
Expected: build green; test failures within the documented 14–23 flaky window (PhysicsResolveCapture/Diagnostics static-leak issues, pre-existing).
|
||
|
||
---
|
||
|
||
## Task RR2: Restructure render frame to WB-faithful order
|
||
|
||
**Goal:** Re-wire `GameWindow.cs`'s render-frame block to match `VisibilityManager.RenderInsideOut` Steps 1–4 verbatim, unify the two-flag asymmetry, and add the stencil-gated sky step.
|
||
|
||
**Files:**
|
||
- Modify: `src/AcDream.App/Rendering/GameWindow.cs` — render-frame block, lines ~7011–7260
|
||
|
||
The exact line numbers below are approximations from the worktree state at the time this plan was written (HEAD = `2bfeafd`). After RR1 reverts the line numbers shift; locate each edit by the exact `old_string` content, not by line number.
|
||
|
||
- [ ] **RR2-S1: Rename `cameraInsideCell` → `cameraInside` with strict semantics**
|
||
|
||
In `src/AcDream.App/Rendering/GameWindow.cs`, find this line (currently ~7012):
|
||
|
||
```csharp
|
||
bool cameraInsideCell = visibility?.CameraCell is not null;
|
||
```
|
||
|
||
Replace with:
|
||
|
||
```csharp
|
||
// Phase A8 restructure (2026-05-26): SINGLE source of truth for
|
||
// "rendering as if camera is inside a cell." Strict PointInCell
|
||
// check, no grace. Drives sky, terrain, stencil branch, weather,
|
||
// sky-PES debug. `playerInsideCell` (line below) STAYS unchanged
|
||
// — it tracks player position, not camera, for lighting.
|
||
//
|
||
// The grace mechanism in CellVisibility.FindCameraCell stays
|
||
// alive; non-render consumers (IsInsideAnyCell, etc.) may still
|
||
// benefit. Only the render-frame consumers use this strict flag.
|
||
//
|
||
// See: docs/superpowers/specs/2026-05-26-phase-a8-restructure-design.md
|
||
bool cameraInside = visibility?.CameraCell is not null
|
||
&& CellVisibility.PointInCell(camPos, visibility.CameraCell);
|
||
```
|
||
|
||
Note: the `—` em-dash in the comment above is just for the doc renderer; in the actual file write em-dashes directly (or hyphens; not load-bearing).
|
||
|
||
- [ ] **RR2-S2: Update sky-PES debug call to use cameraInside**
|
||
|
||
Find this line (currently ~7032):
|
||
|
||
```csharp
|
||
if (_options.EnableSkyPesDebug)
|
||
UpdateSkyPes((float)WorldTime.DayFraction, _activeDayGroup, camPos, cameraInsideCell);
|
||
```
|
||
|
||
Replace with:
|
||
|
||
```csharp
|
||
if (_options.EnableSkyPesDebug)
|
||
UpdateSkyPes((float)WorldTime.DayFraction, _activeDayGroup, camPos, cameraInside);
|
||
```
|
||
|
||
- [ ] **RR2-S3: Update sky pre-scene gate to use cameraInside**
|
||
|
||
Find this line (currently ~7090):
|
||
|
||
```csharp
|
||
if (!cameraInsideCell)
|
||
{
|
||
_skyRenderer?.RenderSky(camera, camPos, (float)WorldTime.DayFraction,
|
||
_activeDayGroup, kf, environOverrideActive);
|
||
if (_particleSystem is not null && _particleRenderer is not null)
|
||
_particleRenderer.Draw(_particleSystem, camera, camPos,
|
||
AcDream.Core.Vfx.ParticleRenderPass.SkyPreScene);
|
||
}
|
||
```
|
||
|
||
Replace with:
|
||
|
||
```csharp
|
||
if (!cameraInside)
|
||
{
|
||
_skyRenderer?.RenderSky(camera, camPos, (float)WorldTime.DayFraction,
|
||
_activeDayGroup, kf, environOverrideActive);
|
||
if (_particleSystem is not null && _particleRenderer is not null)
|
||
_particleRenderer.Draw(_particleSystem, camera, camPos,
|
||
AcDream.Core.Vfx.ParticleRenderPass.SkyPreScene);
|
||
}
|
||
```
|
||
|
||
- [ ] **RR2-S4: Gate initial terrain draw on `!cameraInside`**
|
||
|
||
Find this block (currently ~7111–7123):
|
||
|
||
```csharp
|
||
// Phase N.5b: wrap Draw in CPU stopwatch for [TERRAIN-DIAG] rollup
|
||
// (gated on ACDREAM_WB_DIAG=1, same env var as [WB-DIAG]). Stopwatch
|
||
// is cheap; only the periodic Console.WriteLine is gated.
|
||
_terrainCpuStopwatch.Restart();
|
||
_terrain?.Draw(camera, frustum, neverCullLandblockId: playerLb);
|
||
_terrainCpuStopwatch.Stop();
|
||
```
|
||
|
||
Replace with:
|
||
|
||
```csharp
|
||
// Phase N.5b: wrap Draw in CPU stopwatch for [TERRAIN-DIAG] rollup
|
||
// (gated on ACDREAM_WB_DIAG=1, same env var as [WB-DIAG]). Stopwatch
|
||
// is cheap; only the periodic Console.WriteLine is gated.
|
||
//
|
||
// Phase A8 restructure: skip initial terrain when cameraInside.
|
||
// WB renders terrain ONLY at the stencil-gated step inside the
|
||
// indoor branch (see line ~7215). Drawing terrain unconditionally
|
||
// here would require a depth-clear-if-inside workaround (deleted
|
||
// by this restructure).
|
||
_terrainCpuStopwatch.Restart();
|
||
if (!cameraInside)
|
||
_terrain?.Draw(camera, frustum, neverCullLandblockId: playerLb);
|
||
_terrainCpuStopwatch.Stop();
|
||
```
|
||
|
||
The diag stopwatch + sample buffer + MaybeFlushTerrainDiag below stay unchanged — they record sub-microsecond when skipped, which is correct.
|
||
|
||
- [ ] **RR2-S5: Delete the R3.5 long comment block + cameraReallyInside declaration + depth-clear-if-inside**
|
||
|
||
Find this entire block (currently ~7125–7155):
|
||
|
||
```csharp
|
||
// Phase A8 R3.5 — transition-flicker fix. `cameraInsideCell`
|
||
// stays true for ~3 grace frames after the camera physically
|
||
// exits a cell (see CellVisibility._cellSwitchGraceFrames).
|
||
// The grace mechanism prevents sky/lighting flicker at the
|
||
// doorway threshold, but the render-frame mechanisms that
|
||
// touch depth (depth-clear AND the stencil pipeline) MUST be
|
||
// gated on the stricter PointInCell containment so they don't
|
||
// fire during grace frames when the camera is actually outside.
|
||
//
|
||
// cameraInsideCell — lenient, grace-aware → sky, lighting
|
||
// cameraReallyInside — PointInCell, no grace → depth-clear,
|
||
// stencil pipeline branch
|
||
//
|
||
// R3.5 v1 only gated the stencil branch on `cameraReallyInside`;
|
||
// depth-clear stayed on `cameraInsideCell`. Result: during grace
|
||
// frames the depth-clear ran but the outdoor branch ran (because
|
||
// !cameraReallyInside), so terrain depth was destroyed AND
|
||
// everything below terrain (cellars, basement geometry) won the
|
||
// depth test in the outdoor pass → "objects visible through
|
||
// ground." R3.5 v2 unifies the depth-related gates on
|
||
// `cameraReallyInside` so terrain depth is preserved during
|
||
// grace, eliminating the through-ground artifact.
|
||
bool cameraReallyInside = visibility?.CameraCell is not null
|
||
&& CellVisibility.PointInCell(camPos, visibility.CameraCell);
|
||
|
||
// Conditional depth clear: when camera is ACTUALLY inside a cell
|
||
// volume (not just in the grace window), clear depth (not color)
|
||
// so interior geometry writes fresh Z values on top of the
|
||
// terrain color buffer. Matches ACME GameScene.cs pattern.
|
||
if (cameraReallyInside)
|
||
_gl!.Clear(ClearBufferMask.DepthBufferBit);
|
||
```
|
||
|
||
Delete the entire block. No replacement — the depth-clear was a workaround for the now-skipped initial terrain. RR2-S4 (terrain gated on `!cameraInside`) replaces both functions.
|
||
|
||
Note: after RR1 reverts have run, the `cameraReallyInside` declaration is GONE (it was added by R3.5 v1). The remaining lines you need to delete in this step are smaller — just the depth-clear-if-inside block. The full block above is what HEAD looks like BEFORE RR1 reverts; after RR1 the matching `old_string` shrinks to roughly:
|
||
|
||
```csharp
|
||
// Conditional depth clear: when camera is ACTUALLY inside a cell
|
||
// volume (not just in the grace window), clear depth (not color)
|
||
// so interior geometry writes fresh Z values on top of the
|
||
// terrain color buffer. Matches ACME GameScene.cs pattern.
|
||
if (cameraInsideCell)
|
||
_gl!.Clear(ClearBufferMask.DepthBufferBit);
|
||
```
|
||
|
||
(Note: post-RR1, the variable name is `cameraInsideCell` again — but RR2-S1 already renamed it to `cameraInside`. So at this step's execution time, the gate is `cameraInside`. Locate by the `_gl!.Clear(ClearBufferMask.DepthBufferBit)` line and the comment above it; delete the whole 4-line block.)
|
||
|
||
- [ ] **RR2-S6: Modify the indoor branch — switch condition to cameraInside, prepare for stencil-gated sky insertion**
|
||
|
||
Find the indoor branch condition (currently ~7174):
|
||
|
||
```csharp
|
||
// The `visibility?.CameraCell is not null` repeat is for the
|
||
// compiler's null-flow analysis: `cameraReallyInside` already
|
||
// implies it, but flow doesn't propagate through a separate
|
||
// local. Restating it inside the if condition lets the branch
|
||
// body use the un-`?`d form without null-forgiving.
|
||
if (cameraReallyInside && _indoorStencilPipeline is not null
|
||
&& visibility?.CameraCell is not null)
|
||
{
|
||
```
|
||
|
||
Replace with:
|
||
|
||
```csharp
|
||
// The `visibility?.CameraCell is not null` repeat is for the
|
||
// compiler's null-flow analysis: `cameraInside` already implies
|
||
// it, but flow doesn't propagate through a separate local.
|
||
// Restating it inside the if condition lets the branch body use
|
||
// the un-`?`d form without null-forgiving.
|
||
if (cameraInside && _indoorStencilPipeline is not null
|
||
&& visibility?.CameraCell is not null)
|
||
{
|
||
```
|
||
|
||
(Post-RR1, the variable is `cameraInsideCell` and is then renamed by RR2-S1 to `cameraInside`. Locate this step's edit by the `_indoorStencilPipeline is not null` text — that's stable across the renames.)
|
||
|
||
- [ ] **RR2-S7: Insert stencil-gated sky step between EnableOutdoorPass and the terrain re-draw**
|
||
|
||
Find this block (currently ~7209–7215):
|
||
|
||
```csharp
|
||
// 5. Stencil-gated outdoor pass.
|
||
_indoorStencilPipeline.EnableOutdoorPass();
|
||
|
||
// 5a. Re-draw terrain — at portal-silhouette pixels only,
|
||
// terrain Z (with the f48c74a -0.01 nudge) wins over the
|
||
// punched 1.0 depth. Color writes through window.
|
||
_terrain?.Draw(camera, frustum, neverCullLandblockId: playerLb);
|
||
```
|
||
|
||
Replace with:
|
||
|
||
```csharp
|
||
// 5. Stencil-gated outdoor pass.
|
||
_indoorStencilPipeline.EnableOutdoorPass();
|
||
|
||
// 5a. Stencil-gated SKY. DepthMask OFF so sky color writes
|
||
// through the punched depth=1.0 but depth STAYS at the
|
||
// punch value, letting the next step's terrain re-draw
|
||
// win the depth test wherever closer terrain exists.
|
||
// EnableOutdoorPass left stencil at Equal(1, 0x01) so
|
||
// sky color only writes where bit 1 is set (portal
|
||
// silhouettes). This is acdream's enhancement over WB
|
||
// (which shows fog clear color through windows); see
|
||
// design doc §Q2 + brainstorm outcomes.
|
||
//
|
||
// PRE-FLIGHT (RR3): SkyRenderer.RenderSky must NOT
|
||
// toggle stencil state internally. Verified in commit
|
||
// <RR3 SHA — fill in after RR3 ships>.
|
||
_gl!.DepthMask(false);
|
||
_skyRenderer?.RenderSky(camera, camPos, (float)WorldTime.DayFraction,
|
||
_activeDayGroup, kf, environOverrideActive);
|
||
_gl!.DepthMask(true);
|
||
|
||
// 5b. Re-draw terrain — at portal-silhouette pixels only,
|
||
// terrain Z (with the f48c74a -0.01 nudge) wins over the
|
||
// punched 1.0 depth. Color writes through window,
|
||
// overwriting the sky's color writes where terrain is
|
||
// closer (near-field landscape through the window).
|
||
_terrain?.Draw(camera, frustum, neverCullLandblockId: playerLb);
|
||
```
|
||
|
||
The step numbering in the comments shifts: what was "5a" terrain re-draw is now "5b" with sky as "5a." Outdoor scenery below stays as "5c" (was "5b"). Update consistently below:
|
||
|
||
Find the OutdoorScenery comment immediately following:
|
||
|
||
```csharp
|
||
// 5b. Outdoor scenery — same stencil gating.
|
||
_wbDrawDispatcher!.Draw(camera, _worldState.LandblockEntries, frustum,
|
||
neverCullLandblockId: playerLb,
|
||
visibleCellIds: visibility.VisibleCellIds,
|
||
animatedEntityIds: animatedIds,
|
||
set: AcDream.App.Rendering.Wb.WbDrawDispatcher.EntitySet.OutdoorScenery);
|
||
```
|
||
|
||
Replace the comment marker:
|
||
|
||
```csharp
|
||
// 5c. Outdoor scenery — same stencil gating.
|
||
_wbDrawDispatcher!.Draw(camera, _worldState.LandblockEntries, frustum,
|
||
neverCullLandblockId: playerLb,
|
||
visibleCellIds: visibility.VisibleCellIds,
|
||
animatedEntityIds: animatedIds,
|
||
set: AcDream.App.Rendering.Wb.WbDrawDispatcher.EntitySet.OutdoorScenery);
|
||
```
|
||
|
||
- [ ] **RR2-S8: Update animated-id-set comment to reference cameraInside**
|
||
|
||
Find this comment block (currently ~7157–7160):
|
||
|
||
```csharp
|
||
// L-fix1 (2026-04-28): animated-entity id set. Required by both
|
||
// the cameraReallyInside branch (to route them to LiveDynamic
|
||
// pass) and the outdoor path (where it preserves visibility
|
||
// across landblock frustum culling).
|
||
```
|
||
|
||
Replace with:
|
||
|
||
```csharp
|
||
// L-fix1 (2026-04-28): animated-entity id set. Required by both
|
||
// the cameraInside branch (to route them to LiveDynamic pass)
|
||
// and the outdoor path (where it preserves visibility across
|
||
// landblock frustum culling).
|
||
```
|
||
|
||
(Post-RR1 the comment may already say `cameraInsideCell` instead of `cameraReallyInside`. Adjust the `old_string` to match whatever's actually there.)
|
||
|
||
- [ ] **RR2-S9: Update weather post-scene gate**
|
||
|
||
Find this block (currently ~7252–7267):
|
||
|
||
```csharp
|
||
// Bug A fix (post-#26 worktree, 2026-04-26): weather sky
|
||
// meshes (Properties & 0x04, e.g. the 815m-tall rain
|
||
// cylinder 0x01004C42/0x01004C44) render AFTER the scene so
|
||
// the additive rain streaks overlay terrain and entities
|
||
// instead of being painted over by them. This is the second
|
||
// half of retail's LScape::draw split — GameSky::Draw(1)
|
||
// fires after the DrawBlock loop. Same indoor gate as the
|
||
// sky pass: weather is suppressed inside cells.
|
||
if (!cameraInsideCell)
|
||
{
|
||
_skyRenderer?.RenderWeather(camera, camPos, (float)WorldTime.DayFraction,
|
||
_activeDayGroup, kf, environOverrideActive);
|
||
if (_particleSystem is not null && _particleRenderer is not null)
|
||
_particleRenderer.Draw(_particleSystem, camera, camPos,
|
||
AcDream.Core.Vfx.ParticleRenderPass.SkyPostScene);
|
||
}
|
||
```
|
||
|
||
Replace the gate line only:
|
||
|
||
```csharp
|
||
if (!cameraInside)
|
||
{
|
||
_skyRenderer?.RenderWeather(camera, camPos, (float)WorldTime.DayFraction,
|
||
_activeDayGroup, kf, environOverrideActive);
|
||
if (_particleSystem is not null && _particleRenderer is not null)
|
||
_particleRenderer.Draw(_particleSystem, camera, camPos,
|
||
AcDream.Core.Vfx.ParticleRenderPass.SkyPostScene);
|
||
}
|
||
```
|
||
|
||
(Comment block stays. Only the `if (!cameraInsideCell)` → `if (!cameraInside)` line.)
|
||
|
||
- [ ] **RR2-S10: Build to verify all edits compile**
|
||
|
||
```bash
|
||
dotnet build -c Debug --nologo
|
||
```
|
||
|
||
Expected: green, 0 errors, 0 warnings. If the compiler reports an undefined `cameraInsideCell` anywhere, find that remaining reference and rename it to `cameraInside`.
|
||
|
||
- [ ] **RR2-S11: Run full test suite**
|
||
|
||
```bash
|
||
dotnet test --nologo
|
||
```
|
||
|
||
Expected: failures within the documented 14–23 flaky window only (PhysicsResolveCapture/Diagnostics static-leak from unrelated tests, pre-existing).
|
||
|
||
If a test fails with `cameraInsideCell` in its name or assertion message, that's a stale reference — investigate and update.
|
||
|
||
- [ ] **RR2-S12: Commit**
|
||
|
||
```bash
|
||
git add src/AcDream.App/Rendering/GameWindow.cs
|
||
git commit -m "$(cat <<'EOF'
|
||
feat(render): Phase A8 RR2 — restructure render frame to WB-faithful order
|
||
|
||
Replaces the R3.5 v1+v2 frankenstein (terrain twice + depth-clear
|
||
workaround + two-flag asymmetry) with WB's RenderInsideOut order
|
||
verbatim:
|
||
|
||
Outdoor frame (cameraInside == false): unchanged from pre-A8.
|
||
sky -> terrain -> Draw(All) -> weather
|
||
|
||
Indoor frame (cameraInside == true):
|
||
1. skip initial sky
|
||
2. skip initial terrain
|
||
3. NO depth-clear (deleted)
|
||
4. MarkAndPunch (camera-cell exit portals)
|
||
5. IndoorPass (cell mesh + statics + building shells)
|
||
6. EnableOutdoorPass
|
||
7. stencil-gated sky (DepthMask off; closes R4 Issue B)
|
||
8. stencil-gated terrain re-draw
|
||
9. stencil-gated OutdoorScenery
|
||
10. DisableStencil
|
||
11. LiveDynamic
|
||
12. skip weather
|
||
|
||
Unifies the two-flag asymmetry into a single strict `cameraInside`
|
||
flag computed via PointInCell. Grace mechanism in CellVisibility
|
||
stays alive for non-render consumers.
|
||
|
||
Closes the architectural mismatch flagged in the 2026-05-26 R3.5
|
||
restructure handoff. R1 (#78 fix) + R2 (taxonomy partition) + R3
|
||
(stencil pipeline) shipped earlier; this commit replaces R3.5's
|
||
patches.
|
||
|
||
Design: docs/superpowers/specs/2026-05-26-phase-a8-restructure-design.md
|
||
Plan: docs/superpowers/plans/2026-05-26-phase-a8-restructure.md
|
||
WB reference: references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs:73-160
|
||
|
||
RR3 verifies the SkyRenderer-stencil-contract precondition for the
|
||
stencil-gated sky step. RR4 visual-verifies. RR5 ships docs.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
## Task RR3: Verify SkyRenderer doesn't toggle stencil state
|
||
|
||
**Goal:** Confirm the stencil-gated sky step's precondition: `_skyRenderer.RenderSky` does NOT touch `EnableCap.StencilTest`, `StencilFunc`, `StencilOp`, or `StencilMask` between EnableOutdoorPass's setup and the sky fragment writes. If it does, our gate is lost.
|
||
|
||
**Files:**
|
||
- Read: `src/AcDream.App/Rendering/SkyRenderer.cs`
|
||
- Possibly modify: `src/AcDream.App/Rendering/GameWindow.cs` (only if SkyRenderer is dirty)
|
||
|
||
- [ ] **RR3-S1: Grep SkyRenderer for stencil-state calls**
|
||
|
||
```bash
|
||
grep -n "Stencil\|stencil" src/AcDream.App/Rendering/SkyRenderer.cs
|
||
```
|
||
|
||
Possible outcomes:
|
||
- **No matches at all** → SkyRenderer is clean. Proceed to RR3-S2 (note-only commit).
|
||
- **Matches found** → inspect each. If they are inside conditional code paths that aren't reachable from our `RenderSky` call site (e.g., dead code, debug-only), still clean. If they touch live state during a normal `RenderSky` call, dirty — proceed to RR3-S3 (wrapper required).
|
||
|
||
Most likely outcome: no matches. SkyRenderer was written for outdoor rendering with no stencil concerns.
|
||
|
||
Also grep for any other GL state SkyRenderer may touch that could affect our step (DepthFunc, ColorMask):
|
||
|
||
```bash
|
||
grep -n "DepthFunc\|ColorMask\|DepthMask" src/AcDream.App/Rendering/SkyRenderer.cs
|
||
```
|
||
|
||
If SkyRenderer changes `DepthMask` internally and restores, our outer `DepthMask(false)` + `DepthMask(true)` brackets may be redundant but safe. If SkyRenderer permanently changes `DepthMask` without restoring, we have a different problem (also worth knowing).
|
||
|
||
- [ ] **RR3-S2: If SkyRenderer is clean, commit a verification note**
|
||
|
||
If RR3-S1 found no stencil-state writes, update the comment in `GameWindow.cs` at the stencil-gated sky step to record the verification. Find this comment (added in RR2-S7):
|
||
|
||
```csharp
|
||
// PRE-FLIGHT (RR3): SkyRenderer.RenderSky must NOT
|
||
// toggle stencil state internally. Verified in commit
|
||
// <RR3 SHA — fill in after RR3 ships>.
|
||
```
|
||
|
||
Replace `<RR3 SHA — fill in after RR3 ships>` with this commit's SHA (you'll know it once you run `git commit`; first commit with the placeholder, then `git commit --amend` after seeing the SHA, OR write the comment with the line-range reference to SkyRenderer.cs instead of a SHA reference). Preferred form (line-range, more durable than a SHA):
|
||
|
||
```csharp
|
||
// PRE-FLIGHT (RR3): SkyRenderer.RenderSky must NOT
|
||
// toggle stencil state internally. Verified by reading
|
||
// src/AcDream.App/Rendering/SkyRenderer.cs at commit
|
||
// time — no Stencil*/EnableCap.StencilTest calls in
|
||
// the file.
|
||
```
|
||
|
||
Commit:
|
||
|
||
```bash
|
||
git add src/AcDream.App/Rendering/GameWindow.cs
|
||
git commit -m "$(cat <<'EOF'
|
||
chore(render): Phase A8 RR3 — verify SkyRenderer doesn't touch stencil
|
||
|
||
Pre-flight precondition for the RR2 stencil-gated sky step: confirms
|
||
SkyRenderer.RenderSky doesn't enable/disable stencil test, change
|
||
stencil function/op/mask. Verified by grep over
|
||
src/AcDream.App/Rendering/SkyRenderer.cs — no matches.
|
||
|
||
Comment at the stencil-gated sky call site updated to record the
|
||
verification + reference the verified source.
|
||
|
||
No behavior change.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
- [ ] **RR3-S3: If SkyRenderer is dirty, add a state save/restore wrapper**
|
||
|
||
This step only runs if RR3-S1 found stencil writes inside `SkyRenderer.RenderSky` that would interfere.
|
||
|
||
In `GameWindow.cs`, replace the stencil-gated sky block (RR2-S7's insertion) with a state-saving variant:
|
||
|
||
```csharp
|
||
// 5a. Stencil-gated SKY with state save/restore wrapper.
|
||
// SkyRenderer.RenderSky touches stencil state (see
|
||
// RR3-S1 finding); save before, restore after.
|
||
_gl!.GetInteger(GLEnum.StencilFunc, out int savedStencilFunc);
|
||
_gl!.GetInteger(GLEnum.StencilRef, out int savedStencilRef);
|
||
_gl!.GetInteger(GLEnum.StencilValueMask, out int savedStencilValueMask);
|
||
_gl!.GetInteger(GLEnum.StencilWritemask, out int savedStencilWritemask);
|
||
bool savedStencilTest = _gl!.IsEnabled(EnableCap.StencilTest);
|
||
|
||
_gl!.DepthMask(false);
|
||
_skyRenderer?.RenderSky(camera, camPos, (float)WorldTime.DayFraction,
|
||
_activeDayGroup, kf, environOverrideActive);
|
||
_gl!.DepthMask(true);
|
||
|
||
// Restore stencil state to what EnableOutdoorPass left us with.
|
||
if (savedStencilTest) _gl!.Enable(EnableCap.StencilTest);
|
||
else _gl!.Disable(EnableCap.StencilTest);
|
||
_gl!.StencilFunc((StencilFunction)savedStencilFunc, savedStencilRef, (uint)savedStencilValueMask);
|
||
_gl!.StencilMask((uint)savedStencilWritemask);
|
||
```
|
||
|
||
Commit:
|
||
|
||
```bash
|
||
git add src/AcDream.App/Rendering/GameWindow.cs
|
||
git commit -m "$(cat <<'EOF'
|
||
fix(render): Phase A8 RR3 — wrap stencil-gated sky with state save/restore
|
||
|
||
SkyRenderer.RenderSky internally touches stencil state (verified in
|
||
RR3-S1). The stencil-gated sky step inside the A8 indoor branch needs
|
||
the EnableOutdoorPass stencil setup intact for the subsequent terrain
|
||
re-draw and OutdoorScenery passes. Wrap RenderSky with glGetInteger
|
||
save + restore so the outer stencil context survives.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
## Task RR4: Visual verification matrix
|
||
|
||
**Goal:** Confirm the restructure delivers the design's promises across the four building types + both transitions + sky-through-windows. Human runs the scenarios; Claude collects logs; per-scenario PASS/FAIL recorded against the acceptance criteria.
|
||
|
||
**Files:**
|
||
- Create: `docs/research/2026-05-26-a8-rr4-visual-verification.md` — outcome doc
|
||
|
||
- [ ] **RR4-S1: Build for verification**
|
||
|
||
```bash
|
||
dotnet build src\AcDream.App\AcDream.App.csproj -c Debug --nologo
|
||
```
|
||
|
||
Expected: green.
|
||
|
||
- [ ] **RR4-S2: Pre-launch — close any running client; launch fresh**
|
||
|
||
```powershell
|
||
$proc = Get-Process -Name AcDream.App -ErrorAction SilentlyContinue
|
||
if ($proc) {
|
||
$proc.CloseMainWindow() | Out-Null
|
||
if (-not $proc.WaitForExit(5000)) { $proc | Stop-Process -Force }
|
||
}
|
||
Start-Sleep -Seconds 3
|
||
|
||
$env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call"
|
||
$env:ACDREAM_LIVE = "1"
|
||
$env:ACDREAM_TEST_HOST = "127.0.0.1"
|
||
$env:ACDREAM_TEST_PORT = "9000"
|
||
$env:ACDREAM_TEST_USER = "testaccount"
|
||
$env:ACDREAM_TEST_PASS = "testpassword"
|
||
|
||
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 |
|
||
Tee-Object -FilePath "launch-a8-rr4-verify.log"
|
||
```
|
||
|
||
Background run (so the tool returns; human keeps the client window foreground).
|
||
|
||
- [ ] **RR4-S3: Scenario A — Holtburg cottage interior**
|
||
|
||
Human walks `+Acdream` into a Holtburg cottage near the network portal. Stand in the middle of the room. Look at each wall in turn. Look out a window.
|
||
|
||
**Acceptance:**
|
||
- All walls SOLID — no see-through to outdoor terrain
|
||
- Outdoor terrain visible ONLY through windows / open doors
|
||
- **SKY visible through windows** (the R4 Issue B closure)
|
||
- Player character body visible (no head-backwards, no missing limbs)
|
||
- No "transparent rectangles around buildings" regression (#100 stays closed)
|
||
|
||
Record per criterion: PASS / FAIL with notes.
|
||
|
||
- [ ] **RR4-S4: Scenario B — Holtburg cottage cellar**
|
||
|
||
Walk to a cottage cellar (descend stairs).
|
||
|
||
**Acceptance:**
|
||
- Cellar walls + floor + ceiling SOLID
|
||
- Cellar stairs SOLID from inside view — no grass/terrain overlay through the stair geometry
|
||
- (Out-to-in stair-grass artifact is NOT A8 scope; will be filed as #103 by RR5 if observed)
|
||
|
||
Record per criterion: PASS / FAIL.
|
||
|
||
- [ ] **RR4-S5: Scenario C — Holtburg Inn (multi-room cell mesh)**
|
||
|
||
Walk into the Holtburg inn. Move through its rooms.
|
||
|
||
**Acceptance:**
|
||
- All inn walls SOLID
|
||
- Adjacent rooms not visible through walls (no "I can see the door of the next room")
|
||
- Furniture (cell statics) visible and properly positioned
|
||
- Sky visible through inn windows
|
||
|
||
Record per criterion: PASS / FAIL.
|
||
|
||
- [ ] **RR4-S6: Scenario D — A dungeon (portal-entry indoor world)**
|
||
|
||
Pick any reachable dungeon (network portal to a known dungeon).
|
||
|
||
**Acceptance:**
|
||
- Corridor walls SOLID
|
||
- Adjacent corridors not visible through walls
|
||
- Lighting reads as indoor (sun zeroed, indoor ambient applied)
|
||
- No outdoor stab/terrain leak
|
||
- (Dungeons typically have no exit portals; no sky-through-window expected. If a dungeon HAS exit portals, sky should show through them.)
|
||
|
||
Record per criterion: PASS / FAIL.
|
||
|
||
- [ ] **RR4-S7: Transition scenarios — exit and entry**
|
||
|
||
Walk through an entry/exit transition at the cottage door several times.
|
||
|
||
**Exit transition (indoor → outdoor):**
|
||
- Clean transition; no through-ground objects; no missing walls
|
||
- (If A reproduces AND RR0 confirmed pre-existing on main, this is documented limitation; PASS with note)
|
||
|
||
**Entry transition (outdoor → indoor):**
|
||
- Clean transition; no floor transparent showing cellar
|
||
- (If C reproduces AND RR0 confirmed pre-existing on main, documented limitation; PASS with note)
|
||
|
||
Record per scenario: PASS / FAIL with notes.
|
||
|
||
- [ ] **RR4-S8: Graceful close + write findings doc**
|
||
|
||
```powershell
|
||
$proc = Get-Process -Name AcDream.App -ErrorAction SilentlyContinue
|
||
if ($proc) {
|
||
$proc.CloseMainWindow() | Out-Null
|
||
if (-not $proc.WaitForExit(5000)) { $proc | Stop-Process -Force }
|
||
}
|
||
```
|
||
|
||
Create `docs/research/2026-05-26-a8-rr4-visual-verification.md` capturing each scenario's PASS/FAIL outcome with observations.
|
||
|
||
Template:
|
||
|
||
```markdown
|
||
# A8 RR4 visual verification — render-frame restructure outcome
|
||
|
||
**Date:** 2026-05-26
|
||
**HEAD:** [restructure-commit SHA from RR2]
|
||
**Build:** green
|
||
|
||
## Scenario outcomes
|
||
|
||
| Scenario | Acceptance | Outcome | Notes |
|
||
|---|---|---|---|
|
||
| A: cottage interior | walls solid + sky through windows | PASS / FAIL | |
|
||
| B: cottage cellar | walls solid + stairs solid from inside | PASS / FAIL | |
|
||
| C: inn (multi-room) | walls solid + sky through windows | PASS / FAIL | |
|
||
| D: dungeon | walls solid + indoor lighting | PASS / FAIL | |
|
||
| Exit transition | clean | PASS / FAIL / DOCUMENTED PRE-EXISTING | |
|
||
| Entry transition | clean | PASS / FAIL / DOCUMENTED PRE-EXISTING | |
|
||
|
||
## Gate decision
|
||
|
||
- [ ] All four building-type scenarios PASS → proceed to RR5
|
||
- [ ] Any building-type scenario FAILED → STOP, invoke /investigate
|
||
- [ ] Transition scenarios FAILED but RR0 confirmed pre-existing → file as new ISSUES.md entries in RR5
|
||
- [ ] Transition scenarios FAILED and RR0 did NOT confirm pre-existing → STOP, re-brainstorm
|
||
```
|
||
|
||
Commit the findings:
|
||
|
||
```bash
|
||
git add docs/research/2026-05-26-a8-rr4-visual-verification.md
|
||
git commit -m "$(cat <<'EOF'
|
||
docs(research): Phase A8 RR4 — visual verification outcome
|
||
|
||
Cottage / cellar / inn / dungeon + exit/entry transition + sky-through-
|
||
windows scenarios per the design's acceptance matrix. Gate decision
|
||
recorded for RR5 ship-or-stop.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
- [ ] **RR4-S9: Gate decision**
|
||
|
||
Per the findings:
|
||
- All building-type scenarios PASS AND transition scenarios are clean OR documented-pre-existing → proceed to RR5.
|
||
- Any building-type scenario FAILED → STOP. Open `/investigate` to triage. Do NOT ship RR5.
|
||
- Transition scenarios failed and NOT pre-existing → STOP. Re-brainstorm scope expansion.
|
||
|
||
---
|
||
|
||
## Task RR5: Ship docs (close #78, file follow-ups, update CLAUDE.md)
|
||
|
||
**Goal:** Move #78 to closed, file new ISSUES for known limitations + any RR0-confirmed pre-existing transition issues, update CLAUDE.md's A8 paragraph from "PAUSED" → "SHIPPED."
|
||
|
||
**Files:**
|
||
- Modify: `docs/ISSUES.md`
|
||
- Modify: `CLAUDE.md`
|
||
|
||
- [ ] **RR5-S1: Read ISSUES.md to confirm next available ID**
|
||
|
||
```bash
|
||
grep -n "^## #1[0-9][0-9]" docs/ISSUES.md
|
||
```
|
||
|
||
Expected: shows the highest #1xx ID currently filed. Tentative next available was #102 at plan-write time (highest filed = #101). Verify and adjust.
|
||
|
||
- [ ] **RR5-S2: Move #78 to Recently closed**
|
||
|
||
Find #78's current entry in `docs/ISSUES.md` (OPEN). Move it to the "Recently closed" section with format:
|
||
|
||
```markdown
|
||
**#78 — Outdoor stabs/buildings visible through the rendered floor** — CLOSED 2026-05-26 by Phase A8 (R1+R2+R3+RR1+RR2 across commits ed72704 → [RR2 SHA]). Restructure ports WB's RenderInsideOut stencil pipeline with a corrected entity taxonomy (WorldEntity.IsBuildingShell). Visual-verified at cottage interior, cellar, inn, and dungeon. Sky visible through windows (Issue B closure).
|
||
```
|
||
|
||
- [ ] **RR5-S3: File #102 — Cross-cell-portal far-side visibility (Step 5 deferral)**
|
||
|
||
Add this OPEN issue (use the actual next available ID confirmed in RR5-S1):
|
||
|
||
```markdown
|
||
## #102 — Far-side portal visibility through walls (WB Step 5 deferral)
|
||
|
||
**Status:** OPEN (low priority; first ship of A8 deferred this).
|
||
|
||
**Description:** When standing inside a multi-room building, looking at a wall between rooms, portals on the FAR side of the room (e.g. a doorway opening to outdoors on the other side of the wall) may have their silhouette stencil-marked by Phase A8. This lets outdoor terrain leak through the wall at that silhouette. The first-ship approximation in A8 RR2 stencil-marks ONLY the camera's own cell's exit portals (not BFS-extended VisibleCellIds), which AVOIDS the leak in most cases but loses cross-cell-portal visibility.
|
||
|
||
**Acceptance:** Inside Holtburg Inn looking at the wall between two rooms, no visible terrain or scenery shows through. WB Step 5's 3-stencil-bit cross-building pipeline is the reference fix (see references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/VisibilityManager.cs:156-232).
|
||
|
||
**Files:**
|
||
- src/AcDream.App/Rendering/IndoorCellStencilPipeline.cs — currently single-bit stencil; would extend to bits 1+2.
|
||
- src/AcDream.App/Rendering/GameWindow.cs — render frame would gain a per-far-portal pass.
|
||
```
|
||
|
||
- [ ] **RR5-S4: File #103 — Outdoor-to-indoor cellar terrain Z-fight (out-to-in artifact)**
|
||
|
||
Add this OPEN issue:
|
||
|
||
```markdown
|
||
## #103 — Outdoor-to-indoor cellar terrain Z-fight (out-to-in artifact)
|
||
|
||
**Status:** OPEN (low priority; pre-existing).
|
||
|
||
**Description:** Looking from OUTSIDE a cottage cellar at the stair geometry from above, grass/terrain may overlap the stair triangles. Pre-existing; not addressed by A8 (no stencil work runs when the camera is outside). #100's 1cm terrain Z nudge is insufficient because cellar geometry sits multiple meters below terrain Z — depth-precision artifacts persist at oblique angles.
|
||
|
||
**Acceptance:** From outside a cottage, looking at the cellar entrance, stair geometry reads as solid stone (no grass overlay) regardless of camera angle.
|
||
|
||
**Files:** likely a deeper terrain-occlusion mechanism (per-cell terrain mask, or proper outdoor portal culling) — beyond the scope of A8.
|
||
```
|
||
|
||
- [ ] **RR5-S5: File R4 Issue A as #104 IF RR0 confirmed pre-existing on main**
|
||
|
||
Read `docs/research/2026-05-26-a8-rr0-falsification-findings.md` written in RR0-S4. If outcome was "all three reproduce" (Issue A pre-existing on main), file:
|
||
|
||
```markdown
|
||
## #104 — Exit transition: objects visible through ground (pre-existing)
|
||
|
||
**Status:** OPEN (medium priority; pre-existing on main, confirmed by A8 RR0 falsification).
|
||
|
||
**Description:** Briefly after the camera exits a cell (transitioning indoor → outdoor), objects below terrain Z (cellar geometry, basement entities) may render visible through the ground for 1–3 frames. Falsified against `main` in [a8-rr0-falsification-findings.md](../research/2026-05-26-a8-rr0-falsification-findings.md) — reproduces on `main` without any A8 work. Likely root cause: terrain Z + grace-frame interaction with entity depth tests at certain camera angles.
|
||
|
||
**Acceptance:** Exit a cottage; no objects visible through the ground at any camera angle during the transition. Steady-state outdoor view: terrain occludes below-ground entities cleanly.
|
||
|
||
**Files:**
|
||
- Likely terrain shader (terrain_modern.vert) or the cell-grace mechanism (`CellVisibility.FindCameraCell`).
|
||
- May need a "true outside" gate that ignores grace for depth-test correctness.
|
||
```
|
||
|
||
(If RR0 did NOT confirm pre-existing, skip this step. The expected handling was already decided in RR0-S5.)
|
||
|
||
- [ ] **RR5-S6: File R4 Issue C as #105 IF RR0 confirmed pre-existing on main**
|
||
|
||
Same condition as RR5-S5. If RR0 confirmed Issue C pre-existing:
|
||
|
||
```markdown
|
||
## #105 — Entry transition: floor transparent showing cellar (pre-existing)
|
||
|
||
**Status:** OPEN (medium priority; pre-existing on main, confirmed by A8 RR0 falsification).
|
||
|
||
**Description:** Briefly during the camera entry into a cottage (outdoor → indoor transition), the cottage floor may go transparent showing the cellar geometry below, with the cellar ceiling's texture flickering at the floor pixels. Falsified against `main` in [a8-rr0-falsification-findings.md](../research/2026-05-26-a8-rr0-falsification-findings.md) — reproduces on `main` without any A8 work. Likely root cause: Z-fight between cottage floor mesh and cellar ceiling mesh at adjacent world Z values (both with +0.02 EnvCell render lift).
|
||
|
||
**Acceptance:** Enter a cottage; floor reads as solid floor texture throughout the transition. No glimpse of cellar geometry through the floor.
|
||
|
||
**Files:**
|
||
- The +0.02m EnvCell render lift might need per-cell adjustment.
|
||
- Alternative: depth-fight-resolving via polygon offset on indoor cell meshes.
|
||
```
|
||
|
||
- [ ] **RR5-S7: Update CLAUDE.md A8 paragraph**
|
||
|
||
Find the current A8 paragraph in `CLAUDE.md` (begins with "**A8.P3 —" or "**Phase A8 —"; locate by grep). Find the "PAUSED" / "R3.5" / "restructure" mentions. Replace with a SHIPPED summary:
|
||
|
||
```markdown
|
||
**Phase A8 — Indoor-cell visibility culling — SHIPPED 2026-05-26.** Closes
|
||
issue #78. Six tasks across the restructure (after R1+R2+R3 shipped earlier
|
||
in the same week and R3.5 v1+v2 paused for restructure):
|
||
- RR0: falsification spike — confirmed R4 Issues A + C [pre-existing on main / A8-specific] (per `docs/research/2026-05-26-a8-rr0-falsification-findings.md`).
|
||
- RR1: reverted R3.5 v1 + v2 patches (`38d5374`, `2bfeafd`) as two new revert commits.
|
||
- RR2: restructured render frame to WB-faithful RenderInsideOut order. Skipped initial sky+terrain when inside; deleted depth-clear-if-inside; added stencil-gated sky inside the indoor branch (closes R4 Issue B). Unified the two-flag asymmetry into a single strict `cameraInside` flag via PointInCell.
|
||
- RR3: verified `SkyRenderer.RenderSky` doesn't toggle stencil state internally — stencil-gated sky step's precondition holds.
|
||
- RR4: visual-verified at Holtburg cottage interior + cellar + Inn + dungeon, plus exit/entry transitions and sky-through-windows.
|
||
- RR5 (this ship-docs commit).
|
||
|
||
Five deferred follow-ups remain open:
|
||
- #102 — Cross-cell-portal far-side visibility (WB Step 5).
|
||
- #103 — Cellar terrain Z-fight from outside (pre-existing).
|
||
- #104 — Exit transition through-ground (filed only if RR0 confirmed pre-existing).
|
||
- #105 — Entry transition transparent floor (filed only if RR0 confirmed pre-existing).
|
||
- Grace-mechanism cleanup (Q4 third option from brainstorm).
|
||
|
||
Full design: [docs/superpowers/specs/2026-05-26-phase-a8-restructure-design.md](docs/superpowers/specs/2026-05-26-phase-a8-restructure-design.md).
|
||
Plan: [docs/superpowers/plans/2026-05-26-phase-a8-restructure.md](docs/superpowers/plans/2026-05-26-phase-a8-restructure.md).
|
||
Restructure handoff (historical): [docs/research/2026-05-26-a8-r3.5-restructure-handoff.md](docs/research/2026-05-26-a8-r3.5-restructure-handoff.md).
|
||
```
|
||
|
||
(Customize the "Five deferred follow-ups" list based on which of #104 + #105 were actually filed — if RR0 confirmed neither pre-existing, both come off the list; if just A, drop #105; etc.)
|
||
|
||
Also find the "Currently working toward" line + the A8 references in the Milestone Discipline section. Update them to reflect ship status (e.g. "M1.5 — Indoor world feels right — partially advanced; A8 shipped 2026-05-26; remaining indoor work continues at [next phase]").
|
||
|
||
- [ ] **RR5-S8: Commit ship docs**
|
||
|
||
```bash
|
||
git add docs/ISSUES.md CLAUDE.md
|
||
git commit -m "$(cat <<'EOF'
|
||
ship(render): Phase A8 — indoor-cell visibility culling SHIPPED
|
||
|
||
Closes #78 (outdoor stabs/terrain visible through indoor walls). Files
|
||
#102 (cross-cell-portal Step 5 deferral), #103 (cellar Z-fight from
|
||
outside, pre-existing), and [#104/#105 if RR0 confirmed Issues A/C
|
||
pre-existing on main].
|
||
|
||
Visual-verified at Holtburg cottage interior + cellar, Holtburg Inn,
|
||
and dungeon. Sky visible through windows. Exit/entry transitions
|
||
[clean / documented pre-existing per RR0 findings].
|
||
|
||
Architecture: WB RenderInsideOut order verbatim. Entity taxonomy
|
||
partition (WorldEntity.IsBuildingShell from R1) drives
|
||
WbDrawDispatcher.EntitySet (R2) into IndoorPass / OutdoorScenery /
|
||
LiveDynamic. Stencil-gated sky inside the indoor branch is acdream's
|
||
enhancement over WB's reference.
|
||
|
||
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
## Self-review
|
||
|
||
**Spec coverage check:**
|
||
|
||
- [x] Q1 (initial-terrain conditional) → RR2-S4 gates terrain on `!cameraInside`.
|
||
- [x] Q2 (sky through windows) → RR2-S7 inserts stencil-gated sky step.
|
||
- [x] Q3 (Issue C investigation) → RR5-S6 files as #105 if RR0 confirmed pre-existing.
|
||
- [x] Q4 (gate unification) → RR2-S1 introduces single `cameraInside`; RR2-S2/S3/S6/S8/S9 propagate.
|
||
- [x] Q5 (R3.5 revert) → RR1-S1/S2 revert.
|
||
- [x] RR0 spike (pre-restructure falsification) → entire Task RR0 + decision gate RR0-S5.
|
||
- [x] SkyRenderer precondition check → Task RR3.
|
||
- [x] Visual verification matrix (cottage/cellar/inn/dungeon + transitions + sky-through-windows) → RR4 Scenarios A–D + S7 transitions.
|
||
- [x] Close #78 + file new issues → RR5-S2 through RR5-S6.
|
||
|
||
**Placeholder scan:**
|
||
|
||
- No "TBD" or "implement later" — every step has actual code or actual commands.
|
||
- Two intentional placeholder substitutions: `[RR2 SHA]` in the #78 closure entry (filled in at RR5 execution time) and `[a SHA / pre-existing / not pre-existing]` strings in the CLAUDE.md update template (filled in based on RR0 outcome). These are NOT spec failures — they're explicit "fill in at execution time based on observed state."
|
||
- "Customize" appears once in RR5-S7 for the deferred-follow-ups list — accompanied by a clear conditional that the executor fills in.
|
||
|
||
**Type consistency:**
|
||
|
||
- `cameraInside` (single source of truth) used consistently across RR2-S1/S2/S3/S6/S8/S9; never spelled `cameraInsideCell` or `cameraReallyInside` in any post-RR1 step.
|
||
- `_indoorStencilPipeline` is the field name referenced everywhere (matches R3's already-shipped code).
|
||
- `EntitySet.IndoorPass` / `OutdoorScenery` / `LiveDynamic` enum values match R2's already-shipped code.
|
||
- `IndoorCellStencilPipeline` method names — `UploadPortalMesh`, `MarkAndPunch`, `EnableOutdoorPass`, `DisableStencil` — match the dormant infrastructure shipped in Tasks 1–6.
|
||
|
||
**Task granularity:**
|
||
|
||
- RR0: 5 steps, each ~5–15 minutes (most are launch-and-observe). Visual repro takes the longest.
|
||
- RR1: 4 steps, each <2 minutes.
|
||
- RR2: 12 steps, each 2–5 minutes (mechanical edits with exact code shown).
|
||
- RR3: 3 steps (S2 OR S3 depending on S1 outcome), each 5–10 minutes.
|
||
- RR4: 9 steps, each 3–10 minutes (mostly observation).
|
||
- RR5: 8 steps, each 5–10 minutes (mostly edits to two markdown files).
|
||
|
||
Total estimated time: ~2.5–3 hours assuming RR0 doesn't trigger scope expansion.
|
||
|
||
**Outcome contingencies:**
|
||
|
||
- RR0 outcome 2 (A8-caused) → STOP at RR0-S5. Re-brainstorm.
|
||
- RR3-S1 dirty → divert to RR3-S3 wrapper.
|
||
- RR4 building-type FAILED → STOP at RR4-S9. Open `/investigate`.
|
||
- RR4 transition FAILED + not pre-existing → STOP, re-brainstorm.
|
||
|
||
All gates explicit; no dead-ends.
|
||
|
||
---
|
||
|
||
**SUPERSEDED 2026-05-26 (PM) by [2026-05-26-phase-a8-wb-full-port.md](2026-05-26-phase-a8-wb-full-port.md).**
|
||
|
||
This plan implemented the "WB-faithful restructure" design which RR0 evidence invalidated. The new plan implements the full WorldBuilder RenderInsideOut + RenderOutsideIn port. RR0 was completed and its findings doc is retained.
|
||
|
||
This document is retained for historical reference.
|