BR-2 commit 2: far-Z PUNCH on building entry apertures (outdoor + look-in)

Mirror of commit 1's seal: the punch half of retail's invisible portal
depth writes (DrawPortalPolyInternal maxZ1, Ghidra 0x0059bc90;
ConstructView(CBldPortal) mode-1, pc:433827).

Generalized DrawRetailPViewExitPortalSeal -> DrawRetailPViewPortalDepthWrite
with retail's maxZ1/maxZ2 selector (forceFarZ):
- INTERIOR root: forceFarZ=false (SEAL, true depth) - unchanged from c1.
- OUTDOOR root + DrawPortal look-in: forceFarZ=true (PUNCH, far depth) -
  erase the terrain depth inside a flooded building's entry aperture so
  the interior shows THROUGH the doorway against the nearer front-ground.

Architectural note (divergence from retail ORDER, same RESULT): retail
draws the shell LAST (DrawBuilding Draw(part,1) punch -> draw interior ->
Draw(part,0) shell) so the shell closes everything outside the punch. Our
pipeline draws the building shell FIRST (it is an outdoor WorldEntity in
the landscape pass), so the outside-the-aperture wall occlusion is already
in the depth buffer when interiors draw - we need ONLY the punch for
in-aperture visibility, no shell reorder. The punch is confined to each
door polygon clipped to its slice (NOT a full clear), so it does not
reintroduce the 'cellar paints over everything' hazard that gated the old
outdoor ClearDepthSlice to null.

DrawExitPortalMasks is now wired on the outdoor-root DrawInside context and
the DrawPortal look-in context (both previously null -> no-op).

Suites: build green, App 226 green, Core 1398 + 4 pre-existing #99-era.
NEEDS VISUAL GATE (batched with BR-3): outdoor interiors-through-doorways
must not bleed; cellar grass-sweep (#108) gone; tower stairs near+far.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-11 08:15:52 +02:00
parent 6d4cac2418
commit 4ac547f6eb

View file

@ -7656,9 +7656,12 @@ public sealed class GameWindow : IDisposable
_gl.DepthMask(true); // depth clears honor glDepthMask (c4df241 lesson) _gl.DepthMask(true); // depth clears honor glDepthMask (c4df241 lesson)
_gl.Clear(ClearBufferMask.DepthBufferBit); _gl.Clear(ClearBufferMask.DepthBufferBit);
}, },
DrawExitPortalMasks = clipRoot.IsOutdoorNode // BR-2: interior roots SEAL exit doors at true depth (#108);
? null // outdoor roots PUNCH building entry apertures to far-Z so
: sliceCtx => DrawRetailPViewExitPortalSeal(sliceCtx, envCellViewProj), // flooded interiors show through doorways from outside.
DrawExitPortalMasks = sliceCtx =>
DrawRetailPViewPortalDepthWrite(sliceCtx, envCellViewProj,
forceFarZ: clipRoot.IsOutdoorNode),
DrawCellParticles = sliceCtx => DrawCellParticles = sliceCtx =>
DrawRetailPViewCellParticles(sliceCtx, camera, camPos), DrawRetailPViewCellParticles(sliceCtx, camera, camPos),
EmitDiagnostics = result => EmitDiagnostics = result =>
@ -7804,6 +7807,11 @@ public sealed class GameWindow : IDisposable
MaxSeedDistance = 48f, MaxSeedDistance = 48f,
LandblockEntries = _worldState.LandblockEntries, LandblockEntries = _worldState.LandblockEntries,
SetTerrainClipUbo = uboId => _terrain?.SetClipUbo(uboId), SetTerrainClipUbo = uboId => _terrain?.SetClipUbo(uboId),
// BR-2: outside-looking-in — PUNCH building entry apertures
// to far-Z so the flooded interior shows through the doorway.
DrawExitPortalMasks = sliceCtx =>
DrawRetailPViewPortalDepthWrite(sliceCtx, envCellViewProj,
forceFarZ: true),
}); });
if (portalResult is not null) if (portalResult is not null)
@ -9559,18 +9567,29 @@ public sealed class GameWindow : IDisposable
DisableClipDistances(); DisableClipDistances();
} }
// BR-2 seal: re-stamp the TRUE depth of every outside-leading portal of this // BR-2: retail's invisible portal depth writes on every outside-leading
// cell, clipped to the slice's view region — retail PView::DrawCells loop 1 // portal (other_cell_id==0xFFFF) of this cell, clipped to the slice's view
// (Ghidra 0x005a4840, pc:432783-432786): after the landscape draws through // region — D3DPolyRender::DrawPortalPolyInternal (Ghidra 0x0059bc90),
// the outside views and the depth buffer is cleared, every portal with // dispatched by PView::DrawCells (Ghidra 0x005a4840). The forceFarZ flag is
// other_cell_id==0xFFFF gets DrawPortalPolyInternal(poly, false) — an // retail's maxZ1(true)/maxZ2(false) selector:
// invisible depth write at the portal plane, so interior geometry FARTHER //
// than the doorway z-fails inside the aperture and the terrain seen through // • INTERIOR root (forceFarZ=false → SEAL, true depth): after the full
// it keeps its pixels (#108). Wiring only — the draw lives in // depth clear, stamp the door plane so interior geometry beyond the door
// PortalDepthMaskRenderer. // z-fails inside the aperture and the terrain drawn through the outside
private void DrawRetailPViewExitPortalSeal( // view keeps its pixels (#108).
// • OUTDOOR root / look-in (forceFarZ=true → PUNCH, far depth): after the
// landscape + shell drew, erase the terrain depth inside the building's
// entry aperture so the flooded interior shows THROUGH the doorway
// against the nearer front-ground. Our pipeline draws the shell FIRST
// (as an outdoor entity in the landscape pass), so — unlike retail's
// shell-LAST order — we get the outside-the-aperture wall occlusion for
// free and need only the punch for in-aperture visibility (no reorder).
//
// Wiring only — the draw lives in PortalDepthMaskRenderer.
private void DrawRetailPViewPortalDepthWrite(
AcDream.App.Rendering.RetailPViewCellSliceContext sliceCtx, AcDream.App.Rendering.RetailPViewCellSliceContext sliceCtx,
System.Numerics.Matrix4x4 viewProjection) System.Numerics.Matrix4x4 viewProjection,
bool forceFarZ)
{ {
if (_portalDepthMask is null) if (_portalDepthMask is null)
return; return;
@ -9581,7 +9600,7 @@ public sealed class GameWindow : IDisposable
for (int i = 0; i < cell.Portals.Count; i++) for (int i = 0; i < cell.Portals.Count; i++)
{ {
if (cell.Portals[i].OtherCellId != 0xFFFF) if (cell.Portals[i].OtherCellId != 0xFFFF)
continue; // seals apply to portals leading OUTSIDE only continue; // depth writes apply to portals leading OUTSIDE only
if (i >= cell.PortalPolygons.Count) if (i >= cell.PortalPolygons.Count)
break; break;
var localVerts = cell.PortalPolygons[i]; var localVerts = cell.PortalPolygons[i];
@ -9592,7 +9611,7 @@ public sealed class GameWindow : IDisposable
for (int v = 0; v < n; v++) for (int v = 0; v < n; v++)
world[v] = System.Numerics.Vector3.Transform(localVerts[v], cell.WorldTransform); world[v] = System.Numerics.Vector3.Transform(localVerts[v], cell.WorldTransform);
_portalDepthMask.DrawDepthFan(world[..n], viewProjection, sliceCtx.Slice.Planes, forceFarZ: false); _portalDepthMask.DrawDepthFan(world[..n], viewProjection, sliceCtx.Slice.Planes, forceFarZ);
} }
} }