fix(render): FW4 slice 6 - interior roots draw landscape unclipped

Five ordering repairs later the cathedral falls still bled, and the
final probe round proved why no ordering fix could ever land it: the
falls now submit pre-clear with depth test LESS - and STILL pass,
because the depth they test against is EMPTY. DrawWalkTerrainSlice
scissored + hardware-clipped the terrain to the walk''s exit-view
polygons, and at the #456 cathedral seam those authored portal polys
are thin BANDS far narrower than the real opening: most of the vista
never received terrain color or depth that frame (the visible scene
there was stale color), so unclipped alpha painted straight across.

Retail''s rule, per the PV campaign''s proven model and LScape::draw:
landscape content is view-CULLED, never view-CLIPPED - terrain blocks
draw whole, once per landscape turn (the views feed only CheckBlocks''
block visibility), and aperture exactness comes from the depth clear +
exit seals + interior repaint that follow in the walk''s own order.
This slice ports that: the driver fans exactly ONE terrain turn
(correcting slice 1''s per-view fan), and an interior root''s terrain
and sky draw unclipped/unscissored; the outdoor root keeps its single
full-screen slice (equivalent by construction). The walk''s views
still own the punch fans and dynamics apertures.

Hermetic 6,762/0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-30 20:06:23 +02:00
parent 0d6cd5c06e
commit bb9212c70d
2 changed files with 59 additions and 8 deletions

View file

@ -18,6 +18,32 @@ internal sealed partial class RetailPViewPassExecutor
{ {
if (!frame.RenderSky) if (!frame.RenderSky)
return; return;
// FW4 slice 6: an INTERIOR root draws the sky ONCE, unclipped —
// the same LScape::draw rule as the terrain turn below (view-culled
// landscape content is never view-clipped; the clear + seals +
// interior repaint own aperture exactness). The former per-slice
// scissor left stale sky color wherever the authored exit-portal
// bands were narrower than the real opening.
if (!frame.RootCell.IsOutdoorNode)
{
_sky?.RenderSky(
frame.Camera,
frame.CameraWorldPosition,
frame.DayFraction,
frame.ActiveDayGroup,
frame.SkyKeyframe,
frame.EnvironOverrideActive);
if (_particles is not null && _particleRenderer is not null)
{
_particleRenderer.Draw(
frame.Camera,
frame.CameraWorldPosition,
ParticleRenderPass.SkyPreScene);
}
return;
}
ReadOnlySpan<ClipViewSlice> slices = clipAssembly.OutsideViewSlices; ReadOnlySpan<ClipViewSlice> slices = clipAssembly.OutsideViewSlices;
for (int sliceIndex = 0; sliceIndex < slices.Length; sliceIndex++) for (int sliceIndex = 0; sliceIndex < slices.Length; sliceIndex++)
{ {
@ -58,6 +84,31 @@ internal sealed partial class RetailPViewPassExecutor
nameof(sliceIndex), sliceIndex, nameof(sliceIndex), sliceIndex,
$"walk terrain turn: slice {sliceIndex} of {slices.Length}"); $"walk terrain turn: slice {sliceIndex} of {slices.Length}");
} }
// FW4 slice 6: an INTERIOR root draws the terrain UNCLIPPED —
// retail's LScape::draw draws whole blocks (view-culled, never
// view-clipped; "retail never clips ordinary meshes per view"),
// and aperture exactness comes from the depth clear + exit seals +
// interior repaint that follow in the walk's own order. The former
// per-slice scissor+clip under-painted terrain COLOR AND DEPTH
// wherever the authored exit-portal polygons are narrower than the
// real opening (the #456 cathedral seam bands) — leaving stale
// color and empty depth that unclipped alpha (the falls) then
// painted straight across. The outdoor root keeps its single
// full-screen slice (equivalent to unclipped by construction).
if (!frame.RootCell.IsOutdoorNode)
{
_terrainDiagnostics.Begin();
_terrain?.Draw(
frame.Camera,
frame.Frustum,
neverCullLandblockId: frame.PlayerLandblockId,
clipPlanes: default,
ndcClipAabb: new Vector4(-1f, -1f, 1f, 1f));
_terrainDiagnostics.Complete();
return;
}
ClipViewSlice slice = slices[sliceIndex]; ClipViewSlice slice = slices[sliceIndex];
bool scissor = BeginDoorwayScissor(slice.NdcAabb); bool scissor = BeginDoorwayScissor(slice.NdcAabb);
_surface.BindTerrainClip(); _surface.BindTerrainClip();

View file

@ -744,14 +744,14 @@ internal sealed class WalkFrameDriver : IWalkEventSink
MarkIfGrown(); MarkIfGrown();
_events.Add(WalkFrameEvent.Sky()); _events.Add(WalkFrameEvent.Sky());
_skyDrawnThisFrame = true; _skyDrawnThisFrame = true;
// FW4 slice 1: the fan count is the WALK'S OWN active view count // FW4 slice 6 (correcting slice 1's per-view fan): retail's
// (carried on the Landscape event) — retail LScape::draw runs once // LScape::draw draws the terrain blocks ONCE per landscape turn —
// per view the walk installed. The old clip apparatus's slice count // the active views feed only the block-level visibility union
// no longer participates; the renderer re-derives the actual slice // (CheckBlocks); terrain cells are ordinary meshes and retail never
// clip data from the same walk views before replay, so index i here // clips those per view (pixel exactness = the depth clear + seals +
// and OutsideViewSlices[i] there are the same view by construction. // interior repaint afterward). One terrain turn, always; the walk's
for (int slice = 0; slice < activeViewCount; slice++) // views still own the punch fans and dynamics apertures.
_events.Add(WalkFrameEvent.TerrainSlice(slice)); _events.Add(WalkFrameEvent.TerrainSlice(0));
} }
private void HandleDrawCellsTurn(IReadOnlyList<uint> cells) private void HandleDrawCellsTurn(IReadOnlyList<uint> cells)