feat(render) Campaign FW3.3: ShellDrawLiftZ is RETIRED - cells draw at the dat origin
Retail draws cell geometry at the dat EnvCell origin verbatim; the 0.02 m lift was our z-fight stand-in (register row AP-32, deleted in this commit). With the walk owning retail draw ORDER under WorldDepthContract Less (first-drawn-wins: DrawBlock terrain-then- objects per cell, DrawCells shells-then-contents), the coplanar tie-breaks the lift compensated for are now resolved the way retail resolves them. Deleted at every site: the PortalVisibilityBuilder const + the drawLiftZ Build parameter and its lifted exit-portal projection branch (gate and drawn geometry now share ONE space); the seal/punch fan lifts (DrawPortalDepthWrite + the walk's DrawWalkPunchFan); the LandblockBuildFactory drawn-cell-transform lift (render and physics share the one verbatim transform). The #130 proof flipped exactly as its own doc predicted: UnliftedGate_LeavesTheStripAtTheDrawnTopEdge is deleted (its premise - gate space != drawn space - no longer exists), and the renamed ExitDoorTopEdge_GateCoversTheDrawnApertureWithinPixelTolerance sweep (147 eye/gaze combos at the Holtburg corner door) passes with both in the same unlifted space (worst plane gap <= 1.2 px, scissor <= 0.15 px - unchanged tolerances). Ten more replay-test call sites swept to the new Build signature. Suites: full Release build 0 warnings; hermetic 6,750/0; the 21 affected InstalledDat replay tests green; Walk conformance 40/1 untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
1955a0d5f1
commit
05240d2cab
12 changed files with 55 additions and 196 deletions
|
|
@ -342,31 +342,26 @@ public static class PortalVisibilityBuilder
|
|||
Console.WriteLine($"[pv-ERROR] chain tail(24):{tail}");
|
||||
}
|
||||
|
||||
/// <summary>The +Z world lift applied to DRAWN cell shells (z-fighting vs
|
||||
/// terrain; applied in GameWindow's cell registration). The visibility
|
||||
/// graph stays in PHYSICS (unlifted) space — feeding the lift into portal
|
||||
/// planes broke horizontal-portal side tests (#119-residual, f35cb8b).
|
||||
/// Draw-space consumers of portal polygons (the OutsideView color gate
|
||||
/// here, the seal/punch depth fans in GameWindow) must apply this lift so
|
||||
/// they meet the drawn shell's aperture edge — the unlifted gate left a
|
||||
/// 2 cm background strip under the drawn lintel (#130).</summary>
|
||||
public const float ShellDrawLiftZ = 0.02f;
|
||||
// Campaign FW3.3 (2026-08-30): ShellDrawLiftZ (the 0.02 m drawn-shell
|
||||
// lift, register row AP-32) is RETIRED. Retail draws cell geometry at
|
||||
// the dat EnvCell origin verbatim; the coplanar tie-breaks the lift
|
||||
// stood in for are now owned by the walk's retail draw ORDER under
|
||||
// WorldDepthContract's Less (first-drawn-wins — RenderDeviceD3D::
|
||||
// DrawBlock terrain-then-objects per cell, PView::DrawCells shells-then-
|
||||
// contents). With the lift gone, the visibility gate and the drawn
|
||||
// geometry share ONE space, so the #130 strip mechanism (gate space ≠
|
||||
// drawn space) no longer exists.
|
||||
|
||||
/// <param name="lookup">Resolve a full cell id to its LoadedCell, or null if not loaded.</param>
|
||||
/// <param name="buildingMembership">Optional: true if a cell id is in the camera building's cell
|
||||
/// set. When provided, a neighbour OUTSIDE the set routes to CrossBuildingViews instead of
|
||||
/// continuing the in-building BFS. Pass null to treat all reachable cells as in-building.</param>
|
||||
/// <param name="drawLiftZ">World +Z applied ONLY to the exit-portal projection feeding
|
||||
/// <see cref="PortalVisibilityFrame.OutsideView"/> (a draw-space region; see
|
||||
/// <see cref="ShellDrawLiftZ"/>). Flood admission, side tests, and CellViews are unaffected.
|
||||
/// Production passes <see cref="ShellDrawLiftZ"/>; tests replaying visibility semantics pass 0.</param>
|
||||
public static PortalVisibilityFrame Build(
|
||||
LoadedCell cameraCell,
|
||||
Vector3 cameraPos,
|
||||
Func<uint, LoadedCell?> lookup,
|
||||
Matrix4x4 viewProj,
|
||||
Func<uint, bool>? buildingMembership = null,
|
||||
float drawLiftZ = 0f,
|
||||
PortalVisibilityFrame? reuseFrame = null)
|
||||
{
|
||||
var frame = reuseFrame ?? new PortalVisibilityFrame();
|
||||
|
|
@ -544,38 +539,13 @@ public static class PortalVisibilityBuilder
|
|||
|
||||
if (portal.OtherCellId == 0xFFFF)
|
||||
{
|
||||
// Exit portal -> outdoors visible through this (clipped) opening.
|
||||
// OutsideView gates DRAWN color (terrain/sky/scissor), and the
|
||||
// shell that rasterizes this aperture draws +drawLiftZ above
|
||||
// the physics transform — project the region in the SAME
|
||||
// lifted space or terrain stops a lift-height short of the
|
||||
// drawn lintel (#130 strip). Flood semantics keep the
|
||||
// unlifted clippedRegion path above.
|
||||
int outsideCount;
|
||||
if (drawLiftZ == 0f)
|
||||
{
|
||||
AddRegion(frame.OutsideView, clippedRegion);
|
||||
outsideCount = clippedRegion.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
using PortalVisibilityFrame.ClipRegionScratchLease outsideRegionLease =
|
||||
frame.RentClipRegionScratch();
|
||||
List<ViewPolygon> outsideRegion = outsideRegionLease.Region;
|
||||
ClipPortalAgainstView(
|
||||
frame,
|
||||
poly,
|
||||
cell.WorldTransform * Matrix4x4.CreateTranslation(0f, 0f, drawLiftZ),
|
||||
viewProj,
|
||||
currentView.Polygons,
|
||||
processedCount,
|
||||
endCount - processedCount,
|
||||
outsideRegion,
|
||||
out _);
|
||||
AddRegion(frame.OutsideView, outsideRegion);
|
||||
outsideCount = outsideRegion.Count;
|
||||
}
|
||||
trace?.Add($"portal cell=0x{cell.CellId:X8} p{i}->EXIT addOutside={outsideCount} clipVerts={clipVerts}");
|
||||
// Exit portal -> outdoors visible through this (clipped)
|
||||
// opening. FW3.3: shells draw at the dat origin (the
|
||||
// ShellDrawLiftZ retirement), so the gate and the drawn
|
||||
// aperture share one space — the unlifted clippedRegion
|
||||
// IS the draw-space region.
|
||||
AddRegion(frame.OutsideView, clippedRegion);
|
||||
trace?.Add($"portal cell=0x{cell.CellId:X8} p{i}->EXIT addOutside={clippedRegion.Count} clipVerts={clipVerts}");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,9 +78,8 @@ internal sealed partial class RetailPViewPassExecutor
|
|||
/// <summary>The walk's punch-fan turn — <c>DrawPortalPolyInternal</c>
|
||||
/// @0x0059bc90's far-Z punch through <c>PortalDepthMaskRenderer</c>,
|
||||
/// clipped by the pinned view's slice planes (retail
|
||||
/// <c>building_view</c> @0x0059f3bf). The +<c>ShellDrawLiftZ</c>
|
||||
/// matches today's <c>DrawPortalDepthWrite</c> and retires with
|
||||
/// FW3.3.</summary>
|
||||
/// <c>building_view</c> @0x0059f3bf). FW3.3: fans draw at the dat
|
||||
/// aperture verbatim (the ShellDrawLiftZ retirement).</summary>
|
||||
internal void DrawWalkPunchFan(
|
||||
RetailPViewFrameInput frame,
|
||||
ClipFrameAssembly clipAssembly,
|
||||
|
|
@ -100,10 +99,7 @@ internal sealed partial class RetailPViewPassExecutor
|
|||
Span<Vector3> world = stackalloc Vector3[32];
|
||||
int count = Math.Min(vertices.Length, world.Length);
|
||||
for (int vertex = 0; vertex < count; vertex++)
|
||||
{
|
||||
world[vertex] = vertices[vertex];
|
||||
world[vertex].Z += PortalVisibilityBuilder.ShellDrawLiftZ;
|
||||
}
|
||||
_portalDepthMask.DrawDepthFan(
|
||||
world[..count],
|
||||
frame.ViewProjection,
|
||||
|
|
@ -134,9 +130,8 @@ internal sealed partial class RetailPViewPassExecutor
|
|||
/// @0x0059f170 draws per cell at its flood turn).</item>
|
||||
/// <item><see cref="DrawPunchFan"/> → <c>PortalDepthMaskRenderer.DrawDepthFan</c>
|
||||
/// with <c>forceFarZ</c>, clipped by the pinned view's slice planes
|
||||
/// (retail <c>building_view</c> @0x0059f3bf). The +<c>ShellDrawLiftZ</c>
|
||||
/// on the fan verts matches today's <c>DrawPortalDepthWrite</c> and
|
||||
/// retires with FW3.3.</item>
|
||||
/// (retail <c>building_view</c> @0x0059f3bf); FW3.3 draws fans at the dat
|
||||
/// aperture verbatim (the ShellDrawLiftZ retirement).</item>
|
||||
/// <item><see cref="ClearInteriorDepth"/>/<see cref="DrawExitSeals"/> →
|
||||
/// caller-supplied actions (the renderer owns the pass scope and the
|
||||
/// root-flood seal iteration; the adapter only provides the turns).</item>
|
||||
|
|
|
|||
|
|
@ -711,10 +711,11 @@ internal sealed partial class RetailPViewPassExecutor :
|
|||
int count = Math.Min(localVertices.Length, world.Length);
|
||||
for (int vertex = 0; vertex < count; vertex++)
|
||||
{
|
||||
// FW3.3: fans draw at the dat aperture verbatim (the
|
||||
// ShellDrawLiftZ retirement — shells draw unlifted too).
|
||||
world[vertex] = Vector3.Transform(
|
||||
localVertices[vertex],
|
||||
cell.WorldTransform);
|
||||
world[vertex].Z += PortalVisibilityBuilder.ShellDrawLiftZ;
|
||||
}
|
||||
|
||||
_diagnostics.EmitSeamMask(
|
||||
|
|
|
|||
|
|
@ -148,7 +148,6 @@ public sealed class RetailPViewRenderer
|
|||
ctx.Cells.Find,
|
||||
ctx.ViewProjection,
|
||||
buildingMembership: null,
|
||||
drawLiftZ: PortalVisibilityBuilder.ShellDrawLiftZ,
|
||||
reuseFrame: _mainPortalFrameScratch);
|
||||
|
||||
// R-A2: outdoor root — flood each nearby building SEPARATELY from its own entrance and merge
|
||||
|
|
|
|||
|
|
@ -353,12 +353,10 @@ public sealed class EnvCellLandblockBuildBuilder
|
|||
// two consumers decode the same dat fields for different needs)
|
||||
// so it stays a direct, auditable port of the FW1 test adapter
|
||||
// rather than reshaping LoadedCell's own list layout around it.
|
||||
// cellTransform/inverse here are UNLIFTED (this method's
|
||||
// cellOrigin/cellTransform params are the caller's
|
||||
// physicsCellOrigin/physicsCellTransform — see
|
||||
// LandblockBuildFactory.BuildInteriorEntitiesForStreaming, which
|
||||
// keeps the +0.02 m ShellDrawLiftZ out of this transform) —
|
||||
// exactly what the walk wants.
|
||||
// cellTransform/inverse are the dat EnvCell origin verbatim —
|
||||
// since FW3.3 retired ShellDrawLiftZ, render and physics share
|
||||
// this one transform (retail draws at the origin; the walk's
|
||||
// retail draw order owns the coplanar tie-breaks).
|
||||
Walk = WalkCellFactory.FromParsed(envCellId, envCell, cellStruct, cellTransform, inverse),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -532,21 +532,20 @@ public sealed class LandblockBuildFactory
|
|||
// Static objects inside the cell continue to flow through the dispatcher
|
||||
// as WorldEntity records below — they have real GfxObj MeshRefs that work
|
||||
// fine; EnvCellRenderer receives only the completed shell transaction.
|
||||
// Transforms — needed by the portal-visibility cell (unlifted) AND the
|
||||
// Transforms — needed by the portal-visibility cell AND the
|
||||
// render/physics path. Computed for EVERY cell with a valid cellStruct,
|
||||
// not just drawable ones. Keep the small render lift out of physics; retail
|
||||
// BSP contact planes use the EnvCell origin verbatim. The lift constant is
|
||||
// shared with every draw-space consumer of portal polygons (OutsideView
|
||||
// gate, seal/punch fans) — PortalVisibilityBuilder.ShellDrawLiftZ (#130).
|
||||
// not just drawable ones. Campaign FW3.3 (2026-08-30): the +0.02 m
|
||||
// ShellDrawLiftZ render lift is RETIRED (register row AP-32 deleted) —
|
||||
// retail draws cell geometry at the dat EnvCell origin verbatim, and the
|
||||
// coplanar tie-breaks the lift stood in for are owned by the walk's
|
||||
// retail draw order under depth Less. Render and physics now share the
|
||||
// one verbatim transform.
|
||||
var physicsCellOrigin = envCell.Position.Origin + lbOffset;
|
||||
var cellOrigin = physicsCellOrigin + new System.Numerics.Vector3(
|
||||
0f, 0f, AcDream.App.Rendering.PortalVisibilityBuilder.ShellDrawLiftZ);
|
||||
var cellOrigin = physicsCellOrigin;
|
||||
var cellTransform =
|
||||
System.Numerics.Matrix4x4.CreateFromQuaternion(envCell.Position.Orientation) *
|
||||
System.Numerics.Matrix4x4.CreateTranslation(cellOrigin);
|
||||
var physicsCellTransform =
|
||||
System.Numerics.Matrix4x4.CreateFromQuaternion(envCell.Position.Orientation) *
|
||||
System.Numerics.Matrix4x4.CreateTranslation(physicsCellOrigin);
|
||||
var physicsCellTransform = cellTransform;
|
||||
|
||||
// PORTAL VISIBILITY: register EVERY cell with a valid cellStruct, regardless
|
||||
// of whether CellMesh.Build produced drawable sub-meshes. A portals-only
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue