fix(render): FW4 slice 1 - interior outside-view slices come from the walk
The FW3 visual gate's stairwell/grass transition flash (grass briefly covering floor openings at doorway crossings - the #119 family) was the FW3 dual path leaking: the walk decided WHETHER terrain draws while the old PortalVisibilityBuilder assembly decided WHERE (slice planes, count, scissor), and punch fans indexed the old slice array with walk view indices. The new ACDREAM_PROBE_WALK_ROOT apparatus pinned the boundary frames: fat/degenerate old-apparatus exit views splash terrain over interior pixels, the interior depth-clear preserves color, and cells absent from the walk's flood never repaint. Retail has ONE visibility structure and cannot produce this. ClipFrameAssembler.ReassembleOutsideViewFromWalk now materializes the walk's own outside_view (pixel screen points -> standard NDC -> the existing ClipPlaneSet machinery) into the assembly's outside-view block after Collect, ahead of the single PrepareClipFrame publication (moved below the walk block). The Landscape event carries the walk's active view count on the record's existing OutsideViewCount field (trace mapping compares kind only - zero oracle-fixture churn) and the driver fans exactly that many terrain slices; activeTerrainSliceCount is deleted end to end. Outdoor roots keep the assembler's single full-screen slice, asserted ==1. Hermetic 6,762/0 (4 new materializer tests pin the y-flip and plane-sign conventions), Walk lane 209/1, InstalledDat walk conformance 40/1. Seals/cell slices/look-in seeding stay on the old per-cell views for the rest of FW4 (identical dat polygons; only the visible set can differ). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
8f8b0b9c57
commit
37febd1fe6
11 changed files with 468 additions and 44 deletions
|
|
@ -100,6 +100,13 @@ public sealed class RetailPViewRenderer
|
|||
// cleared in finally.
|
||||
private Action? _walkPreClearDynamics;
|
||||
|
||||
// ACDREAM_PROBE_WALK_ROOT (FW3 visual-gate apparatus, throwaway): the
|
||||
// previous frame's root kind + a post-flip frame countdown so each
|
||||
// interior/outdoor transition dumps 8 frames of rooting facts.
|
||||
private bool? _probeWalkRootPrevOutdoor;
|
||||
private int _probeWalkRootFramesLeft;
|
||||
private ulong _probeWalkRootFrame;
|
||||
|
||||
// Campaign FW3.2b-2: the walk's production world-data registries
|
||||
// (published/retired by LandblockRenderPublisher) plus the per-frame
|
||||
// driver state. Null until the composition passes them; the static
|
||||
|
|
@ -189,8 +196,10 @@ public sealed class RetailPViewRenderer
|
|||
pvFrame,
|
||||
_clipAssemblyScratch);
|
||||
passes.AppendLookInClipFrames(_lookInFrames, clipAssembly);
|
||||
int terrainUploadCount = checked(1 + clipAssembly.OutsideViewSlices.Length * 2);
|
||||
passes.PrepareClipFrame(terrainUploadCount);
|
||||
// FW4 slice 1: PrepareClipFrame (the one clip-region publication)
|
||||
// moved BELOW the walk block — an interior-rooted walk frame
|
||||
// re-derives the outside-view slices from the walk's own views
|
||||
// first, so the appended slots join the same single publication.
|
||||
|
||||
// R1: draw EVERY visible cell (retail cell_draw_list), not only the cells the
|
||||
// assembler handed a clip-slot. This feeds the Prepare filter + entity partition,
|
||||
|
|
@ -288,15 +297,15 @@ public sealed class RetailPViewRenderer
|
|||
}
|
||||
}
|
||||
|
||||
int activeTerrainSliceCount = clipAssembly.OutsideViewSlices.Length;
|
||||
if (ctx.RootCell.IsOutdoorNode && activeTerrainSliceCount != 1)
|
||||
if (ctx.RootCell.IsOutdoorNode && clipAssembly.OutsideViewSlices.Length != 1)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"walk static cutover: an outdoor root's clip assembly produced "
|
||||
+ $"{activeTerrainSliceCount} outside-view slices, not the expected 1 — "
|
||||
+ "WalkFrameDriver's landscape turn assumes the outdoor root's default "
|
||||
+ "full-screen view (plan §FW3 item 2c's pinned assumption; assert "
|
||||
+ "rather than silently coercing to 1).");
|
||||
+ $"{clipAssembly.OutsideViewSlices.Length} outside-view slices, not the "
|
||||
+ "expected 1 — the outdoor root draws through the full-screen default "
|
||||
+ "view (retail set_default_view; the walk fans exactly its own 1), and "
|
||||
+ "the outdoor slice data still comes from the assembler (plan §FW3 item "
|
||||
+ "2c's pinned assumption; assert rather than silently coercing to 1).");
|
||||
}
|
||||
|
||||
_walkWorldData!.BeginFrame(
|
||||
|
|
@ -333,9 +342,60 @@ public sealed class RetailPViewRenderer
|
|||
|
||||
walkDriver.Collect(
|
||||
_frameWalk, ctx.ViewerCellId, walkCameraCell, walkLandscape, walkContext,
|
||||
ctx.ViewProjection, ctx.CameraWorldPosition, activeTerrainSliceCount);
|
||||
ctx.ViewProjection, ctx.CameraWorldPosition);
|
||||
|
||||
// FW4 slice 1: an interior root's terrain/sky/punch clip slices
|
||||
// come from THE WALK'S OWN outside_view — retail's one
|
||||
// visibility structure. See ClipFrameAssembler.
|
||||
// ReassembleOutsideViewFromWalk's doc comment for the boundary-
|
||||
// frame desync (the stairwell/grass flash) this retires. The
|
||||
// outdoor root keeps the assembler's single full-screen slice
|
||||
// (asserted ==1 above; identical content by construction).
|
||||
if (walkCameraCell is not null)
|
||||
{
|
||||
ClipFrameAssembler.ReassembleOutsideViewFromWalk(
|
||||
clipAssembly,
|
||||
_frameWalk.InteriorOutsideView,
|
||||
viewportWidth,
|
||||
viewportHeight);
|
||||
}
|
||||
|
||||
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeWalkRootEnabled)
|
||||
{
|
||||
_probeWalkRootFrame++;
|
||||
bool outdoorNow = ctx.RootCell.IsOutdoorNode;
|
||||
if (_probeWalkRootPrevOutdoor is bool prev && prev != outdoorNow)
|
||||
{
|
||||
_probeWalkRootFramesLeft = 8;
|
||||
Console.WriteLine(
|
||||
$"[walk-root] ---- FLIP {(prev ? "OUT->IN" : "IN->OUT")} at frame {_probeWalkRootFrame} ----");
|
||||
}
|
||||
_probeWalkRootPrevOutdoor = outdoorNow;
|
||||
if (_probeWalkRootFramesLeft > 0)
|
||||
{
|
||||
_probeWalkRootFramesLeft--;
|
||||
var cellsList = new List<uint>(walkDriver.VisitedCells);
|
||||
cellsList.Sort();
|
||||
string cells = string.Join(",", cellsList.ConvertAll(c => c.ToString("x8"))
|
||||
.GetRange(0, Math.Min(cellsList.Count, 10)));
|
||||
Console.WriteLine(
|
||||
$"[walk-root] f={_probeWalkRootFrame} out={(outdoorNow ? 1 : 0)} "
|
||||
+ $"viewer=0x{ctx.ViewerCellId:X8} root=0x{ctx.RootCell.CellId:X8} "
|
||||
+ $"res={ctx.CameraCellResolution} slices={clipAssembly.OutsideViewSlices.Length} "
|
||||
+ $"eye=({ctx.ViewerEyePos.X:F2},{ctx.ViewerEyePos.Y:F2},{ctx.ViewerEyePos.Z:F2}) "
|
||||
+ $"walkCells={walkDriver.VisitedCells.Count} bld={walkDriver.VisitedBuildings.Count} "
|
||||
+ $"cells=[{cells}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FW4 slice 1: the ONE clip-region publication, after any walk
|
||||
// reassembly so the walk-derived outside-view slots are included
|
||||
// (moved from directly after AssembleClipFrame; the count is
|
||||
// reservation metadata the RHI arm ignores).
|
||||
int terrainUploadCount = checked(1 + clipAssembly.OutsideViewSlices.Length * 2);
|
||||
passes.PrepareClipFrame(terrainUploadCount);
|
||||
|
||||
// #124: look-in cells need prepared shell batches + their statics routed
|
||||
// into partition.ByCell (consumed ONLY by DrawBuildingLookIns — the main
|
||||
// cell-object pass iterates pvFrame.OrderedVisibleCells, which never
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue