T2 slice 1 (BR-4): multi-view UNION merge + retail 1-px vertex merge (the fixpoint floor)
(a) MergeBuildingFrame now UNIONS a building flood's views into cells already present in the frame (retail Render::copy_view APPENDS every clipped portal polygon as a new view_poly, Ghidra 0x0054dfc0 - a cell visible through two apertures holds two views). The old first-wins 'ContainsKey -> continue' dropped the second aperture's views: the multiview-loss-first-wins divergence, a named #109 suspect. (b) ClipToRegion output now runs retail's post-divide vertex merge: consecutive vertices closer than ~1 pixel collapse (copy_view's |dx|<=1 && |dy|<=1 screen-unit merge), polygons that collapse below 3 distinct verts return empty (retail's '<3 survivors -> count 0'). This is the flood's PHYSICAL fixpoint floor - re-clipping can only insert sub-pixel slivers, which the merge removes, so accumulated views converge instead of drifting. Unit note: builder has no viewport, so 1 px is expressed as NDC at reference 1080p (0.00185); coarser at higher res, which only strengthens convergence. This is the prerequisite for removing the MaxReprocessPerCell=16 drift cap (T2 slice 2) and the EyeInsidePortalOpening rescue. Gates: all 10 flood conformance tests green (CornerSweep monotone pin included); App 226 green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
579c8b06bc
commit
cf8a2c379b
2 changed files with 75 additions and 8 deletions
|
|
@ -144,17 +144,33 @@ public sealed class RetailPViewRenderer
|
|||
}
|
||||
}
|
||||
|
||||
// Append a per-building flood's cells + views into the frame. Each building cell belongs to exactly
|
||||
// one building, so there is no cross-building overlap; ContainsKey is a safety dedup. OutsideView is
|
||||
// NOT merged — the outdoor root already seeds full-screen terrain, and ConstructViewBuilding
|
||||
// (BuildFromExterior) leaves OutsideView empty (it stops at exit portals once inside the building).
|
||||
// T2 (BR-4): merge a per-building flood's cells + views into the frame as a
|
||||
// UNION. Retail accumulates EVERY clipped portal polygon as a new view_poly
|
||||
// on the cell (Render::copy_view appends + view_count++, Ghidra 0x0054dfc0;
|
||||
// a cell visible through two apertures holds two views, all consumed
|
||||
// downstream). The old first-wins (`ContainsKey -> continue`) dropped the
|
||||
// second building flood's views whenever a cell was already in the frame —
|
||||
// the multiview-loss-first-wins divergence (a named #109 suspect: per-frame
|
||||
// winner flips between apertures). CellView.Add dedups exact/collinear
|
||||
// re-emissions (the dac8f6a CanonicalKey), so unioning is convergent.
|
||||
// OutsideView is NOT merged — the outdoor root already seeds full-screen
|
||||
// terrain, and ConstructViewBuilding (BuildFromExterior) leaves OutsideView
|
||||
// empty (it stops at exit portals once inside the building).
|
||||
private static void MergeBuildingFrame(PortalVisibilityFrame target, PortalVisibilityFrame src)
|
||||
{
|
||||
foreach (uint cellId in src.OrderedVisibleCells)
|
||||
{
|
||||
if (target.CellViews.ContainsKey(cellId))
|
||||
if (!src.CellViews.TryGetValue(cellId, out var srcView))
|
||||
continue;
|
||||
target.CellViews[cellId] = src.CellViews[cellId];
|
||||
|
||||
if (target.CellViews.TryGetValue(cellId, out var existing))
|
||||
{
|
||||
foreach (var p in srcView.Polygons)
|
||||
existing.Add(p);
|
||||
continue;
|
||||
}
|
||||
|
||||
target.CellViews[cellId] = srcView;
|
||||
target.OrderedVisibleCells.Add(cellId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue