# Handoff - M1.5 Indoor Render / Retail PView Replacement Attempts - 2026-06-06 This is a **stop-and-handoff** note for the next agent. It records what was tried, what changed on disk, what the user still sees, and what evidence should drive the next step. The user explicitly stopped this thread after repeated visual regressions. Do **not** continue the same patching loop. Treat all current uncommitted render work as suspect until re-audited against named retail. ## Worktree And Rules - Worktree: `C:\Users\erikn\source\repos\acdream\.claude\worktrees\thirsty-goldberg-51bb9b` - Branch: `claude/thirsty-goldberg-51bb9b` - Starting HEAD called out by the user: `8116d10` - Do **not** branch or create a new worktree. - Do **not** push without asking. - Never run `git stash` or `git gc`. - PowerShell on Windows. - Launch logs are UTF-16. - Build before launching. - Use `apply_patch` for manual edits. - Do not revert dirty changes unless the user explicitly asks. Current child handoff thread created before this file: - Child thread id: `019e9d5c-bb34-7fe3-85cc-6b9065b4e882` - It was forked same-directory, not a new worktree. - A follow-up prompt was sent there with the immediate evidence and constraints. ## User-Visible State At Stop The latest user report, after the most recent relaunch: - Transition flaps still happen between outdoor/indoor, room/room, and cellar. - Ground floor became transparent instead of sealed. - Cellar remains broken. - Prior screenshots showed grey or black background filling cell openings. - Prior screenshots showed indoor walls losing texture or drawing as clear/background color. - Prior screenshots showed character cut in half on the cellar stairs. - User explicitly says we are back to old bugs and nothing feels solid. Important: **do not claim any current code is fixed**. Build/tests passed for some pieces, but visual acceptance failed. ## Current Dirty State `git status --short --branch` showed these tracked files modified: - `src/AcDream.App/Rendering/ClipFrameAssembler.cs` - `src/AcDream.App/Rendering/ClipPlaneSet.cs` - `src/AcDream.App/Rendering/GameWindow.cs` - `src/AcDream.App/Rendering/InteriorEntityPartition.cs` - `src/AcDream.App/Rendering/InteriorRenderer.cs` - `src/AcDream.App/Rendering/ParticleRenderer.cs` - `src/AcDream.App/Rendering/PortalView.cs` - `src/AcDream.App/Rendering/PortalVisibilityBuilder.cs` - `src/AcDream.App/Rendering/Wb/ObjectMeshManager.cs` - `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs` - `src/AcDream.Core/Rendering/RenderingDiagnostics.cs` - `src/AcDream.Core/World/WorldEntity.cs` - `tests/AcDream.App.Tests/Rendering/ClipFrameAssemblerTests.cs` - `tests/AcDream.App.Tests/Rendering/ClipPlaneSetTests.cs` - `tests/AcDream.App.Tests/Rendering/InteriorEntityPartitionTests.cs` - `tests/AcDream.App.Tests/Rendering/PortalVisibilityBuilderTests.cs` - `tests/AcDream.App.Tests/Rendering/Wb/WbDrawDispatcherClipSlotTests.cs` - `tests/AcDream.Core.Tests/Rendering/RenderingDiagnosticsTests.cs` - `tools/TextureDump/Program.cs` Important untracked files include: - `src/AcDream.App/Rendering/RetailPViewRenderer.cs` - `docs/research/2026-06-05-retail-pview-indoor-render-pseudocode.md` - many probe logs and local scripts/images, including `launch-flap-shell-capture-relaunch.log`, `launch-pview-watermark-probe.log`, `a8-current-room-cellar-audit.txt`, `texture-current-room-surfaces.txt`, `analyze_*.py`, `retail-*-trace.log`, and several screenshots. Diff size before this handoff file: - 19 tracked files changed. - About 1593 insertions and 773 deletions. ## Validation That Passed But Did Not Prove Visual Correctness After the last attempted PortalVisibilityBuilder patch: ```powershell dotnet test tests\AcDream.App.Tests\AcDream.App.Tests.csproj -c Debug --filter "FullyQualifiedName~PortalVisibilityBuilderTests|FullyQualifiedName~PortalProjectionTests" ``` Passed: 29/29. ```powershell dotnet build -c Debug --no-restore ``` Succeeded, with 9 known warnings. ```powershell dotnet test tests\AcDream.App.Tests\AcDream.App.Tests.csproj -c Debug --no-build ``` Passed: 196/196. These results only prove the pure/tested slices compile and pass. They did **not** solve the live render. ## Retail PView Reference Already Written New pseudocode note exists: - `docs/research/2026-06-05-retail-pview-indoor-render-pseudocode.md` It summarizes: - `SmartBox::RenderNormalMode @ 0x00453aa0` - `RenderDeviceD3D::DrawInside @ 0x0059f0d0` - `PView::DrawInside @ 0x005a5860` - `PView::ConstructView @ 0x005a57b0` - `PView::DrawCells @ 0x005a4840` - `RenderDeviceD3D::DrawObjCellForDummies @ 0x005a0760` Core retail model from that note: - Outdoor: `LScape::draw`, then portal/interior peering through PView portal paths. - Indoor: `DrawInside(viewer_cell)`. - `PView::ConstructView` builds `cell_draw_list`, per-cell `portal_view`, and `outside_view`. - `PView::DrawCells` draws outside landscape through `outside_view`, then reverse `cell_draw_list` exit masks, reverse shells, reverse object lists. - No global indoor terrain/entity/particle pass should bypass PView membership. ## Retail Functions That Still Matter Re-read named retail before more code: - `PView::AddViewToPortals @ 0x005a52d0` - `PView::ConstructView @ 0x005a57b0` - `PView::ClipPortals @ 0x005a5520` - `PView::FixCellList @ 0x005a5250` - `PView::AdjustCellView @ 0x005a5770` - `PView::OtherPortalClip @ 0x005a5400` - `PView::GetClip` around `0x005a4320` - `SmartBox::RenderNormalMode @ 0x00453aa0` - `SmartBox::update_viewer @ 0x00453ce0` - `RenderDeviceD3D::DrawObjCellForDummies @ 0x005a0760` The critical retail detail not faithfully settled yet: - Retail tracks `view_count` and `update_count`. - When a cell view grows after the cell was already processed, retail calls `FixCellList` / `AdjustCellView`. - Current acdream code only approximates this. It may not match draw-list ordering or downstream propagation. ## What We Tried ### 1. Treated symptoms as separate render leaks The session started with symptoms that looked separate: - dynamic objects and particles visible through ground when looking out from inside; - outside ground texture covering cellar entrance when looking in; - grey flaps when crossing cell boundaries; - missing cellar floor / grey cellar; - transparent or textureless interior walls. The user correctly pushed back that these are probably one render-pipeline failure: indoor/outdoor, cells, shells, terrain, objects, particles, and doors must all agree on one visible-cell graph. ### 2. Gated dynamic objects and particles by ownership Attempt: - `WorldEntity.ParentCellId` was populated for player/spawns/teleports/motion updates. - `InteriorEntityPartition` was changed so live dynamic entities with an indoor `ParentCellId` go into their cell bucket instead of a global live-dynamic overlay. - `WbDrawDispatcher.ResolveEntitySlot` was changed so `ServerGuid != 0` no longer always means "draw unclipped indoors". - Particles were moved toward PView-scoped / owner-scoped behavior instead of a global indoor scene pass. Effect: - User reported this stopped much of the obvious dynamic-object/particle bleeding when looking out. - It did **not** fix grey/background transition flaps. - It did **not** fix cellar/floor/walls. Current risk: - This direction is probably correct, but the exact routing must be audited. A later attempt also cleared clip routing to avoid character/shell cutting, so "PView membership" and "GPU clip slot routing" are currently mixed/confused. ### 3. Added/used a `RetailPViewRenderer` Attempt: - Added `src/AcDream.App/Rendering/RetailPViewRenderer.cs`. - Moved part of indoor draw orchestration into `RetailPViewRenderer.DrawInside`. - Added `DrawPortal` for outdoor-looking-in through `PortalVisibilityBuilder.BuildFromExterior`. - The renderer currently does: - `PortalVisibilityBuilder.Build` - `ClipFrameAssembler.Assemble` - `_envCells.PrepareRenderBatches(filter: drawableCells)` - `InteriorEntityPartition.Partition` - landscape through outside slices - exit masks - EnvCell shells - object buckets Effect: - This is not a full retail replacement yet. - User repeatedly saw unchanged or worse symptoms. - FPS was reported drastically down after one iteration. - Subsequent attempts produced missing textures / white or grey wall panels. Current risk: - `RetailPViewRenderer` is not truly verbatim retail. It keeps modern infrastructure and approximates PView with GPU clip slots and callbacks. - The user asked "have you ported retail verbatim?" and the honest answer remains no. - `GameWindow` still has a lot of orchestration, diagnostics, and render routing around this. It is not a small caller yet. ### 4. Reworked `ClipFrameAssembler` from one clip per cell to per-slice clip slots Attempt: - `ClipFrameAssembler` was rewritten toward per-polygon/slice output: - `CellIdToViewSlices` - `OutsideViewSlices` - per-slice `ClipViewSlice` - `TerrainClipMode` for outside-view landscape - The goal was to represent retail `portal_view` slices more closely. Effect: - The code is plausible as a draw assist, but it is not retail membership. - User saw regressions including black covers during transitions. Current risk: - The next agent must ensure `ClipFrameAssembler` never decides PView membership. - It should be draw-assist only. - Several failures looked like GPU clip slots cutting shells or characters at door/stair boundaries. ### 5. Disabled clip routing for shells/entities to stop character/stair cutting Attempt: - `RetailPViewRenderer.UseIndoorMembershipOnlyRouting` clears `_envCells.SetClipRouting(null)` and `_entities.ClearClipRouting()`. - Comment says retail portal views decide eligibility, but feeding those 2D views into GL clip distances slices characters and shells at stair/door boundaries. Effect: - This was a reaction to user screenshots where the character was cut in half on stairs. - It may reduce character slicing. - It may also mean shells/objects are currently only membership-gated, not portal-view clipped. Current risk: - This is not a settled retail copy. It is an emergency compromise. - Retail does use per-view setup (`CEnvCell::setup_view`) around shell/object drawing. We need to know whether our GL clip-plane model is simply the wrong mechanism for that setup. ### 6. Tried EnvCell / DAT polygon side handling changes Attempt: - `ObjectMeshManager` changed CellStruct polygon side handling: - DAT `CullMode` interpreted as retail `CPolygon::sides_type`. - `0 = pos` - `1 = pos twice with reversed winding` - `2 = pos + neg surface` - `NoPos` / `NoNeg` still suppress faces. - Added explicit normal inversion / winding reversal logic. Effect: - User saw missing textures/white/grey interior panels after some launches. - The attempt did not fix the cellar or transition flaps. Current risk: - This may be correct retail interpretation or may be partially wrong. - Audit with DAT dumps and retail/ACME references before keeping. - `a8-current-room-cellar-audit.txt` and `texture-current-room-surfaces.txt` may contain useful surface/cell evidence. ### 7. Tried outside-looking-in via `BuildFromExterior` Attempt: - `PortalVisibilityBuilder.BuildFromExterior` seeds interior cell views through outside-facing exit portals. - `RetailPViewRenderer.DrawPortal` calls it from outdoor branch. - Tests were added: - seeds interior cell through outside portal; - does not seed when camera is on interior side; - traverses deeper interior portals; - max seed distance skips distant exit portal. Effect: - User initially reported walls became visible looking in from outside, but ground/cellar entrance composition stayed wrong. - Later launches regressed to transparent/grey panels and missing textures. Current risk: - This is probably needed, but the exterior portal path is not proven retail-faithful. - `BuildFromExterior` may now have duplicated-looking test diff context; inspect file carefully. ### 8. Tried broad "no hybrid" render routing in `GameWindow` Attempt: - `GameWindow` was changed so indoor path should call `RetailPViewRenderer.DrawInside`. - Outdoor path should draw world and call `DrawPortal`. - Global indoor terrain/entity/particle passes were reduced or bypassed. - New render signature diagnostics log: - `branch` - `root` - `viewerRoot` - `playerRoot` - `viewerCell` - `playerCell` - `gate` - `terrain` - `skyGate` - `zclear` - `sceneParticles` - `outSlices` - `outPolys` - `ids` - `draw` - object partition counts Effect: - User explicitly asked whether the hybrid was totally gone. - It is not safe to answer "yes" without auditing `GameWindow`. - Symptoms persisted, so either the routing is still hybrid or the PView graph/draw setup is wrong enough that "no hybrid" alone does not solve it. Current risk: - `GameWindow.cs` has a very large diff, around 1000 lines touched. - Next agent should not blindly keep it. - Audit all remaining global passes while `clipRoot != null`. ### 9. Tried PView `update_count`-style reprocessing Attempt in the last aborted step: - `PortalView.CellView.Add` now returns `bool` and deduplicates near-identical polygons. - `PortalVisibilityBuilder.Build` replaced `seen` with: - `queued` - `drawListed` - `processedViewCounts` - A cell can be requeued when its view grows. - Each processing pass clips portals against only newly added view polygons. - Similar logic was added to `BuildFromExterior`. - Added tests: - `Build_CollapsedInteriorPortalNearEyeBeyondHalfMeter_FloodsNeighbour` - `Build_ViewGrowthAfterDoneCell_PropagatesNewSlicesToExit` Effect: - Focused tests passed. - Live probe after patch still showed `outPolys` toggling near root `0xA9B40172`. - User then reported transition flaps still there and now ground floor transparent. Current risk: - This patch is **unaccepted** and may be wrong. - It approximates retail `update_count`, but does not necessarily implement `FixCellList`, `AdjustCellPlace`, or retail draw-list ordering correctly. ### 10. Widened eye-standing-in-portal fallback Attempt: - `EyeStandingPerpDist` widened from `0.5f` to `1.75f`. - Motivation: live cellar capture had `0174 -> 0175` traversable with `D=-1.41` but `ProjectToNdc` returned zero vertices. - The fallback still requires the perpendicular projection to land inside the portal opening. Effect: - It made one unit test pass for the cellar-style collapsed portal. - It did not solve live transitions. Current risk: - This may be a bandaid, not retail. - It should be validated against `OtherPortalClip` / `GetClip` in named retail before keeping. ### 11. Tried reciprocal clip fallback for eye-in-opening Attempt: - Before `ApplyReciprocalClip`, code clones `clippedRegion` when `eyeInsideOpening`. - If reciprocal clipping empties the region, it restores the pre-reciprocal region. Effect: - Tests passed. - Live visual did not. Current risk: - This may over-include. - It is not proven retail-faithful. ## Critical Evidence From Logs ### Cellar startup: root 0174 only sees itself From `launch-flap-shell-capture-relaunch.log`: ```text [flap] root=0xA9B40174 eye=(154.50,4.99,92.25) localEye=(7.43,2.51,-1.77) | p0->0x0175 D=-1.41 TRV proj=0 clip=-1 || outPolys=0 vis=1 [flap-cam] root=0xA9B40174 viewerCell=0xA9B40174 playerCell=0xA9B40174 ... terrain=Skip outVisible=False [render-sig] frame=49 branch=RetailPViewInside root=0xA9B40174 ... terrain=Skip/skip sky=n zclear=n sceneParticles=none outSlices=0 outPolys=0 ids=[0xA9B40174] draw=[0xA9B40174] ``` Meaning: - The player/viewer/root are in cellar cell `0174`. - The only visible portal to stair connector `0175` is traversable. - Projection produces zero vertices. - The PView flood stops at the cellar. - Only the cellar draws; stair/main-floor cells are not in the visible set. This is a direct candidate cause for missing floor/grey composition. ### Root 0172: outside view toggles on/off From `launch-pview-watermark-probe.log`: ```text frame=3625 root=0xA9B40172 p0->0x0173 D=... TRV proj=4 clip=4 p1->0x016F D=5.28 TRV proj=0 clip=-1 outPolys=1 vis=6 ids include 0xA9B40170 terrain=Skip/draw sky=Y zclear=Y frame=3626 root=0xA9B40172 p0->0x0173 D=... TRV proj=5 clip=5 p1->0x016F D=5.39 TRV proj=0 clip=-1 outPolys=0 vis=5 ids missing 0xA9B40170 terrain=Skip/skip sky=n zclear=n frame=3647 root=0xA9B40172 outPolys=1 ids include 0xA9B40170 terrain draw frame=3648/3649 root=0xA9B40172 outPolys=0 ids missing 0xA9B40170 terrain skip ``` Meaning: - The same root cell can alternate between seeing outside and not seeing outside. - `0x016F` is involved in the root flap line but projects to zero. - Sometimes `0x0170` becomes reachable and outside terrain/sky/depth clear run; sometimes it disappears. - The visible-cell list and outside-view list are not stable. Open question: - Is `0x016F` an outdoor/land cell, an env cell lookup miss, or a portal that retail handles differently? - Is the toggling caused by projection/clip degeneracy, wrong portal reciprocal handling, update-count propagation, or camera/viewer-cell root? ### Earlier known evidence: root 0171 vs player 0174 contradiction From the older `2026-06-05-shell-sealing-cellar-floor-handoff.md`: ```text [flap-cam] root=0xA9B40171 viewerCell=0xA9B40171 playerCell=0xA9B40174 ... [flap] root=0xA9B40171 ... p1->0173 proj=0 ... ``` Meaning: - Earlier, camera/root was the room while player was cellar. - The flood did not seal the player's cell. - Later, after branch/viewer changes, there are also frames where root/player/viewer are all `0174` but the flood still fails on `0174 -> 0175`. This means the problem is probably not only "wrong root"; it also includes projection/portal traversal/flood propagation or mesh-shell handling. ## What Not To Retry Blindly Do not simply: - switch the root to player cell as a workaround; - widen `EyeStandingPerpDist` further; - globally draw all indoor shells; - globally draw terrain/entities/particles while inside; - turn off all clipping and hope depth sorts it; - keep adding `if cellar` or Holtburg-cottage-specific handling; - claim "no hybrid" without auditing all `GameWindow` indoor/outdoor passes; - equate unit-test pass with visual correctness. The user has explicitly asked for retail smoothness, not a new patch stack. ## Likely Root Problem Space The next fix probably lives in one of these, but evidence must decide: 1. **PView graph construction is not retail-faithful.** - Missing or wrong `update_count` / `FixCellList` / `AdjustCellView`. - Wrong draw-list ordering when a processed cell receives new views. - Downstream portal propagation incomplete. 2. **Portal projection/clip behavior differs from retail.** - `0174 -> 0175` traversable but `proj=0`. - `0172 -> 016F` traversable but `proj=0`. - `OtherPortalClip` / `GetClip` may not match retail. 3. **Outdoor/exit portal classification is wrong.** - `OtherCellId=0xFFFF` is treated as exit/outside, but `0x016F` may be another kind of outside/land portal or missing env cell. - OutsideView may be created through a downstream path that acdream sometimes drops. 4. **Renderer draw setup is still hybrid or ordered wrong.** - `GameWindow` may still draw or skip global passes inconsistently. - Sky/terrain/depth clear decisions are visibly flapping with `outside_view`. 5. **EnvCell shell mesh/surface handling is wrong.** - Missing/transparent/white walls and floors may be mesh/surface/cull-side regressions. - Audit `ObjectMeshManager` side handling against retail and DAT dumps. 6. **GPU clip slots are being used as membership or hard clipping when retail uses view setup differently.** - Character cut in half on stairs strongly suggests hard clip-plane use on avatars/shells is wrong or applied at wrong pass. ## Suggested Next Procedure 1. Stop patching. Inspect the dirty diff first. 2. Read `docs/research/2026-06-05-retail-pview-indoor-render-pseudocode.md`. 3. Re-read named retail functions listed above. 4. Parse `launch-flap-shell-capture-relaunch.log` and `launch-pview-watermark-probe.log` around the cited frames. 5. Add better probes if needed: - cell id; - portal index; - other cell id; - portal flags; - other portal id; - traversable decision; - standing distance; - projection vertex count; - clip vertex count; - reciprocal clip result; - outside-view add/skip reason; - cell view count / processed count / update count; - queue/requeue reason; - draw-list insertion/reorder. 6. Decide from evidence whether `0174 -> 0175` and `0172 -> 016F` fail because of projection, reciprocal clip, cell lookup/classification, or update propagation. 7. Patch only the retail mismatch. 8. Build/test before launch. 9. Launch with probes once. 10. Then launch clean for FPS/visual feel. 11. Do not call it done until the user visually confirms retail smoothness. ## Launch Command Use PowerShell: ```powershell Get-Process -Name AcDream.App -ErrorAction SilentlyContinue | 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" $env:ACDREAM_PROBE_FLAP = "1" $env:ACDREAM_PROBE_SHELL = "1" $env:ACDREAM_PROBE_VIS = "1" dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 | Tee-Object -FilePath "launch-next-pview.log" ``` For clean visual/FPS run, remove the probe env vars. ## Minimal Prompt For Next Agent ```text Continue acdream M1.5 indoor render in SAME worktree: C:\Users\erikn\source\repos\acdream\.claude\worktrees\thirsty-goldberg-51bb9b branch claude/thirsty-goldberg-51bb9b. Do NOT branch/worktree. Do NOT push. NEVER stash/gc. The current dirty render code is not visually accepted. The user stopped the prior agent after repeated regressions. Read docs/research/2026-06-06-retail-pview-renderer-replacement-attempts-handoff.md first. Current symptoms: transition flaps still happen indoor/outdoor, room/room, cellar; ground floor is now transparent; cellar broken; prior runs showed grey/black clear color, missing wall textures, and character cut on stairs. Do not patch first. Audit dirty diff, read named retail PView, parse launch-flap-shell-capture-relaunch.log and launch-pview-watermark-probe.log. Determine exactly why: 1) cellar root 0174 fails to traverse 0174 -> 0175 when proj=0; 2) root 0172 toggles outside_view/0170 reachability while 0172 -> 016F has proj=0; 3) shell/object/terrain/depth-clear decisions disagree. Patch only the retail mismatch. Build/test before relaunch. Do not claim success before user visual confirmation. ```