using System; using System.Numerics; namespace AcDream.App.Rendering; /// /// BR-2 (holistic building-render port): retail's invisible portal depth /// writes — the port of D3DPolyRender::DrawPortalPolyInternal /// (Ghidra 0x0059bc90, pc:424490). /// /// Campaign FW's ordered frame walk restores the positional discipline /// on which this primitive depends: farther landscape/static content is /// submitted before a building punch, the punch precedes that building's /// look-in cells, and nearer content repaints afterward. Consequently both /// sides now use retail's one-pass depth operation directly; the former /// stencil mark/bias approximation from #117 is intentionally gone. /// /// Retail projects a portal polygon, software-clips it against the /// installed portal view (polyClipFinish), and draws the survivor as a /// COLOR-INVISIBLE triangle fan with depth-test ALWAYS + depth-write ON: /// /// Seal (retail maxZ2=6, bit0 clear, data 0x00820e14): /// z = the polygon's true projected depth. Drawn on portals leading OUTSIDE /// (other_cell_id==0xFFFF) after the landscape pass — terrain seen /// through a doorway keeps its pixels because farther interior geometry /// z-fails inside the aperture (PView::DrawCells loop 1, Ghidra 0x005a4840, /// pc:432783-432786). /// Punch (retail maxZ1=7, bit0 set, data 0x00820e18): /// z forced to the far plane (0.99999988) — erases depth inside a building /// aperture so the interior cells drawn next land cleanly /// (ConstructView(CBldPortal) mode-1, pc:433827). BR-2 commit 2 wires this /// side. /// /// /// Where retail clips the polygon on the CPU against the view, we apply /// the SAME view region via gl_ClipDistance from the slice's clip-space /// half-planes (≤8, the validated output) — the /// depth write lands only inside the slice region, matching retail's clipped /// fan. /// /// The GL arm's inline program (rehomed clip planes, raw uniform /// locations) and this class's own dynamic VAO/VBO ring were deleted at /// Campaign V slice V11; the RHI arm compiles /// Rendering/Shaders/portal_depth.{vert,frag} from committed SPIR-V, /// and its per-frame fan vertices come from the frame ring instead. /// public sealed partial class PortalDepthMaskRenderer : IDisposable { private readonly ResourceCleanupGroup _resources; /// Shared by both arms: the largest fan accepts. private const int MaxFanVerts = 32; /// /// The GL arm's per-flight VAO/VBO ring this used to report on was deleted /// at Campaign V slice V11: the RHI arm draws every fan from a ring /// allocation that lives until its frame retires, so there is no /// persistent dynamic-buffer pool left to size. /// internal long DynamicBufferCapacityBytes => 0; /// /// Selects the GPU-fenced frame slot and resets its append cursor. Portal /// fans written during one frame occupy distinct ranges, so later portal /// slices never overwrite vertices still referenced by earlier draws. /// /// The GL arm's per-flight VAO/VBO ring this used to activate was /// deleted at Campaign V slice V11 — the RHI arm owns no per-flight ring /// at all, because every frame ring allocation is already distinct memory /// that lives until the frame retires — so all this edge carries now is /// the started latch. /// public void BeginFrame(int frameSlot) { ArgumentOutOfRangeException.ThrowIfNegative(frameSlot); _rhiFrameStarted = true; } /// /// /// Draw one portal polygon as an invisible depth write, clipped to the /// slice's clip-space half-planes. selects /// punch (true, retail maxZ1) vs seal (false, retail maxZ2 true depth). /// /// Seal (interior root): one pass, retail-verbatim — /// depth ALWAYS + true projected depth. It runs immediately after the /// gated full depth clear, so there is no nearer content to stomp. /// /// Punch (outdoor root / look-in): one pass, retail-verbatim /// — depth ALWAYS + far-Z. The ordered frame walk, not a visibility test /// at the aperture plane, supplies retail's painter-order safety. /// public void DrawDepthFan( ReadOnlySpan worldVerts, in Matrix4x4 viewProjection, ReadOnlySpan planes, bool forceFarZ) { if (worldVerts.Length < 3) return; DrawDepthFanRhi(worldVerts, in viewProjection, planes, forceFarZ); } public void Dispose() => DisposeRhiResources(); }