using System.Numerics; using AcDream.App.Rendering.Walk; using AcDream.Core.Vfx; namespace AcDream.App.Rendering; /// Campaign FW3.2b-2 (step 1): the walk driver's leaf draws over /// this executor's renderers. Same leaf mechanics as the packed slice /// methods — the cutover changes ORDER only. internal sealed partial class RetailPViewPassExecutor { /// The walk's single sky turn: retail draws GameSky once /// inside LScape::draw, clipped by the active views; looping /// today's per-slice scissor + terrain-clip block reproduces today's /// pixels while the driver sees ONE turn. internal void DrawWalkSky( RetailPViewFrameInput frame, ClipFrameAssembly clipAssembly) { if (!frame.RenderSky) return; ReadOnlySpan slices = clipAssembly.OutsideViewSlices; for (int sliceIndex = 0; sliceIndex < slices.Length; sliceIndex++) { ClipViewSlice slice = slices[sliceIndex]; bool scissor = BeginDoorwayScissor(slice.NdcAabb); _surface.BindTerrainClip(); EnableClipDistances(); _sky?.RenderSky( frame.Camera, frame.CameraWorldPosition, frame.DayFraction, frame.ActiveDayGroup, frame.SkyKeyframe, frame.EnvironOverrideActive); DisableClipDistances(); if (_particles is not null && _particleRenderer is not null) { _particleRenderer.Draw( frame.Camera, frame.CameraWorldPosition, ParticleRenderPass.SkyPreScene); } if (scissor) _surface.EndScissor(); } } /// The walk's per-slice terrain turn — the terrain block of /// DrawLandscapeSlice (scissor + terrain clip + slice planes + /// NDC AABB), without the sky/entity halves the walk owns separately. internal void DrawWalkTerrainSlice( RetailPViewFrameInput frame, ClipFrameAssembly clipAssembly, int sliceIndex) { ReadOnlySpan slices = clipAssembly.OutsideViewSlices; if ((uint)sliceIndex >= (uint)slices.Length) { throw new ArgumentOutOfRangeException( nameof(sliceIndex), sliceIndex, $"walk terrain turn: slice {sliceIndex} of {slices.Length}"); } ClipViewSlice slice = slices[sliceIndex]; bool scissor = BeginDoorwayScissor(slice.NdcAabb); _surface.BindTerrainClip(); EnableClipDistances(); _terrainDiagnostics.Begin(); _terrain?.Draw( frame.Camera, frame.Frustum, neverCullLandblockId: frame.PlayerLandblockId, clipPlanes: slice.Planes, ndcClipAabb: slice.NdcAabb); _terrainDiagnostics.Complete(); DisableClipDistances(); if (scissor) _surface.EndScissor(); } /// The walk's punch-fan turn — DrawPortalPolyInternal /// @0x0059bc90's far-Z punch through PortalDepthMaskRenderer, /// clipped by the pinned view's slice planes (retail /// building_view @0x0059f3bf). FW3.3: fans draw at the dat /// aperture verbatim (the ShellDrawLiftZ retirement). internal void DrawWalkPunchFan( RetailPViewFrameInput frame, ClipFrameAssembly clipAssembly, WalkPolygon worldPolygon, int activeViewIndex) { if (_portalDepthMask is null) return; Vector3[] vertices = worldPolygon.Vertices; if (vertices.Length < 3) return; ReadOnlySpan slices = clipAssembly.OutsideViewSlices; ReadOnlySpan planes = (uint)activeViewIndex < (uint)slices.Length ? slices[activeViewIndex].Planes : default; Span world = stackalloc Vector3[32]; int count = Math.Min(vertices.Length, world.Length); for (int vertex = 0; vertex < count; vertex++) world[vertex] = vertices[vertex]; _portalDepthMask.DrawDepthFan( world[..count], frame.ViewProjection, planes, forceFarZ: true); } } /// /// Campaign FW stage FW3.2b-2 (step 1, additive): the production /// over this executor's leaf renderers. /// Constructed per frame by the FW3.2b-2 rooting (plan §FW3 "FW3.2b-2 — /// the production rooting"); nothing invokes it until the static cutover /// flips. Each member maps a turn onto the /// SAME leaf calls the packed path uses today, so the cutover changes /// ORDER, never the leaf mechanics: /// /// /// → the sky block of DrawLandscapeSlice, /// looped over the active slices internally (retail draws GameSky once /// inside LScape::draw clipped by the active views; the per-slice /// scissor+clip here reproduces today's pixels while the driver still sees /// ONE sky turn). /// → the terrain block of /// DrawLandscapeSlice (scissor + terrain clip + slice planes). /// EnvCellRenderer opaque + /// transparent-ordered for ONE cell (retail DrawEnvCell /// @0x0059f170 draws per cell at its flood turn). /// PortalDepthMaskRenderer.DrawDepthFan /// with forceFarZ, clipped by the pinned view's slice planes /// (retail building_view @0x0059f3bf); FW3.3 draws fans at the dat /// aperture verbatim (the ShellDrawLiftZ retirement). /// / → /// caller-supplied actions (the renderer owns the pass scope and the /// root-flood seal iteration; the adapter only provides the turns). /// FlushLandscapeAlphaFartherThan /// (the DrawBuilding barrier; retail's flush-all /// FlushAlphaList(0f) @0x0059f30b is the FW4 adjudication /// candidate recorded on the driver's interface). /// /// internal sealed class WalkProductionLeafRenderer : IWalkFrameLeafRenderer { private readonly RetailPViewPassExecutor _passes; private readonly RetailPViewFrameInput _frame; private readonly ClipFrameAssembly _clipAssembly; private readonly Action _clearInteriorDepth; private readonly Action _drawExitSeals; private readonly HashSet _singleCellScratch = new(); private readonly List _singleCellListScratch = new(); internal WalkProductionLeafRenderer( RetailPViewPassExecutor passes, RetailPViewFrameInput frame, ClipFrameAssembly clipAssembly, Action clearInteriorDepth, Action drawExitSeals) { _passes = passes ?? throw new ArgumentNullException(nameof(passes)); _frame = frame ?? throw new ArgumentNullException(nameof(frame)); _clipAssembly = clipAssembly; _clearInteriorDepth = clearInteriorDepth ?? throw new ArgumentNullException(nameof(clearInteriorDepth)); _drawExitSeals = drawExitSeals ?? throw new ArgumentNullException(nameof(drawExitSeals)); } public void DrawSky() => _passes.DrawWalkSky(_frame, _clipAssembly); public void DrawTerrainSlice(int sliceIndex) => _passes.DrawWalkTerrainSlice(_frame, _clipAssembly, sliceIndex); public void DrawCellShell(uint cellId) { _singleCellScratch.Clear(); _singleCellScratch.Add(cellId); _passes.DrawOpaqueCellShells(_singleCellScratch); if (_passes.CellHasTransparentShell(cellId)) { _singleCellListScratch.Clear(); _singleCellListScratch.Add(cellId); _passes.DrawTransparentCellShellsOrdered(_singleCellListScratch); } } public void ClearInteriorDepth() => _clearInteriorDepth(); public void DrawExitSeals() => _drawExitSeals(); public void DrawPunchFan(WalkPolygon worldPolygon, int activeViewIndex) => _passes.DrawWalkPunchFan(_frame, _clipAssembly, worldPolygon, activeViewIndex); public void AlphaBarrier(float viewerDistance) => _passes.FlushLandscapeAlphaFartherThan(viewerDistance); }