diff --git a/src/AcDream.App/Rendering/Wb/EnvCellRenderer.cs b/src/AcDream.App/Rendering/Wb/EnvCellRenderer.cs index ad3e4e1..3bf2071 100644 --- a/src/AcDream.App/Rendering/Wb/EnvCellRenderer.cs +++ b/src/AcDream.App/Rendering/Wb/EnvCellRenderer.cs @@ -815,21 +815,32 @@ public sealed unsafe class EnvCellRenderer : IDisposable _gl.BindVertexArray(0); _currentVao = 0; - // Phase A8 fix (2026-05-28 visual-gate-#3 follow-up): explicitly - // restore cull-back at exit. The Landblock→None override above - // can leave cull DISABLED if the last batch's CullMode was - // Landblock — which would leak into the subsequent dispatcher - // draws (IndoorPass building shells, then LiveDynamic chars + - // NPCs + doors), making them all render see-through (no - // back-face cull). The see-through-head symptom in the - // ACDREAM_A8_DISABLE_CULL=1 A/B test was caused exactly by - // this state leak. Re-enabling cull here restores the - // dispatcher's expected default and updates our static cache - // so the next Render call's first SetCullMode comparison is - // correct. - _gl.Enable(EnableCap.CullFace); - _gl.CullFace(TriangleFace.Back); - _currentCullMode = CullMode.CounterClockwise; + // Phase A8 (2026-05-28 visual-gate-#4 follow-up): NO cull-restore + // at exit. The Landblock→None override can leave cull DISABLED + // if the last batch was Landblock — and that's intentional: the + // subsequent `dispatcher.Draw(IndoorPass)` call in + // RenderInsideOutAcdream's Step 3 wants cull-off too, because + // AC's cottage-shell GfxObj parts (the wooden floor planks + + // wall slabs that the player walks on / through) have winding + // that gets back-face-culled by the dispatcher's default + // FrontFace=CCW. Letting cull stay off through IndoorPass + // renders both shell and cell mesh double-sided, so floors are + // visible from above (and inverted-front-facing wall slabs are + // visible from inside the room). Step 4's + // `gl.Enable(EnableCap.CullFace)` (line ~10768) + the cleanup + // block's enable (line ~10870) re-establish cull-back before + // LiveDynamic chars / NPCs / doors render — so those still + // look solid (no see-through head). The static `_currentVao` + // is reset because the next Render call's batch loop needs to + // re-issue BindVertexArray regardless; `_currentCullMode` is + // intentionally left at None so the cache matches actual GL + // state until the next Render call's per-batch SetCullMode + // either confirms or re-sets it. + // + // The retail-faithful long-term move is matching WB's + // glFrontFace(CW) globally (GameScene.cs:843) so cull-back + // selects the correct side for AC's polygon winding without + // double-sided rendering — deferred until a wider audit. // Update frame stats for probe emission at the call site. _lastFrameStats.CellsRendered = filter?.Count ?? snapshot.BatchedByCell.Count;