diff --git a/src/AcDream.App/Rendering/RetailPViewRenderer.cs b/src/AcDream.App/Rendering/RetailPViewRenderer.cs index f61a4f69..4c95260d 100644 --- a/src/AcDream.App/Rendering/RetailPViewRenderer.cs +++ b/src/AcDream.App/Rendering/RetailPViewRenderer.cs @@ -93,7 +93,16 @@ public sealed class RetailPViewRenderer DrawLandscapeThroughOutsideView(ctx, clipAssembly, partition); UseIndoorMembershipOnlyRouting(); DrawExitPortalMasks(ctx, pvFrame, clipAssembly, drawableCells); - DrawEnvCellShells(ctx, pvFrame, clipAssembly, drawableCells); + // #113 fix scope (#114): GL-clip the shells only for the OUTDOOR root — + // the case the flood replay validated (tight, stable door-aperture + // regions) and the one that produced the phantom staircase. The first + // user gate (2026-06-11) showed INDOOR clip regions are not yet + // draw-quality (chopped stairs / vanishing inner walls at exits / + // see-through to neighbour rooms at the meeting hall) — indoor roots + // stay unclipped (yesterday's user-accepted state) until #114 brings + // the indoor regions to retail's pixel-exact crop. + DrawEnvCellShells(ctx, pvFrame, clipAssembly, drawableCells, + clipShells: ctx.RootCell.IsOutdoorNode); DrawCellObjectLists(ctx, pvFrame, clipAssembly, drawableCells, partition); return result; @@ -193,7 +202,9 @@ public sealed class RetailPViewRenderer ctx.EmitDiagnostics?.Invoke(result); DrawExitPortalMasks(ctx, pvFrame, clipAssembly, drawableCells); - DrawEnvCellShells(ctx, pvFrame, clipAssembly, drawableCells); + // DrawPortal is the from-outside look-in path — same validated outdoor + // regime as the outdoor root (see #114 scope note in DrawInside). + DrawEnvCellShells(ctx, pvFrame, clipAssembly, drawableCells, clipShells: true); DrawCellObjectLists(ctx, pvFrame, clipAssembly, drawableCells, partition); RestoreNoClip(ctx.SetTerrainClipUbo); @@ -335,7 +346,8 @@ public sealed class RetailPViewRenderer IRetailPViewCellDrawCallbacks ctx, PortalVisibilityFrame pvFrame, ClipFrameAssembly clipAssembly, - HashSet drawableCells) // param kept this task; removed in Task 4 + HashSet drawableCells, // param kept this task; removed in Task 4 + bool clipShells) { // Retail DrawCells Loop 2: every visible cell's shell, reverse cell_draw_list // (far→near), per portal_view slice. No drawableCells filter — a cell without a @@ -358,8 +370,14 @@ public sealed class RetailPViewRenderer // Characters/statics stay unclipped (DrawCellObjectLists): retail's mesh path is // viewcone-check + BoundingType handling, and hard-clipping slices characters at // doorways (the original UseIndoorMembershipOnlyRouting observation). - for (int i = 0; i < ClipFrame.MaxPlanes; i++) - _gl.Enable(Silk.NET.OpenGL.EnableCap.ClipDistance0 + i); + // + // clipShells (#114 scope, 2026-06-11): true only for outdoor-eye roots. + // The first user gate showed indoor clip regions are not draw-quality + // yet (chopped stairs / vanishing walls at exits) — indoor roots draw + // unclipped until #114 lands pixel-exact indoor regions. + if (clipShells) + for (int i = 0; i < ClipFrame.MaxPlanes; i++) + _gl.Enable(Silk.NET.OpenGL.EnableCap.ClipDistance0 + i); foreach (var entry in IndoorDrawPlan.ShellPass(pvFrame)) { @@ -375,8 +393,9 @@ public sealed class RetailPViewRenderer } } - for (int i = 0; i < ClipFrame.MaxPlanes; i++) - _gl.Disable(Silk.NET.OpenGL.EnableCap.ClipDistance0 + i); + if (clipShells) + for (int i = 0; i < ClipFrame.MaxPlanes; i++) + _gl.Disable(Silk.NET.OpenGL.EnableCap.ClipDistance0 + i); } private void DrawCellObjectLists(