using System.Numerics; using AcDream.App.Rendering.Scene; 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 { private IReadOnlySet? _walkTerrainInViewLandcells; internal void SetWalkTerrainInViewLandcells(IReadOnlySet? cells) => _walkTerrainInViewLandcells = cells; /// 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; // FW4 slice 6: an INTERIOR root draws the sky ONCE, unclipped — // the same LScape::draw rule as the terrain turn below (view-culled // landscape content is never view-clipped; the clear + seals + // interior repaint own aperture exactness). The former per-slice // scissor left stale sky color wherever the authored exit-portal // bands were narrower than the real opening. if (!frame.RootCell.IsOutdoorNode) { _sky?.RenderSky( frame.Camera, frame.CameraWorldPosition, frame.DayFraction, frame.ActiveDayGroup, frame.SkyKeyframe, frame.EnvironOverrideActive); if (_particles is not null && _particleRenderer is not null) { _particleRenderer.Draw( frame.Camera, frame.CameraWorldPosition, ParticleRenderPass.SkyPreScene); } 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}"); } // FW4 slice 6: an INTERIOR root draws the terrain UNCLIPPED — // retail's LScape::draw draws whole blocks (view-culled, never // view-clipped; "retail never clips ordinary meshes per view"), // and aperture exactness comes from the depth clear + exit seals + // interior repaint that follow in the walk's own order. The former // per-slice scissor+clip under-painted terrain COLOR AND DEPTH // wherever the authored exit-portal polygons are narrower than the // real opening (the #456 cathedral seam bands) — leaving stale // color and empty depth that unclipped alpha (the falls) then // painted straight across. The outdoor root keeps its single // full-screen slice (equivalent to unclipped by construction). if (!frame.RootCell.IsOutdoorNode) { _terrainDiagnostics.Begin(); _terrain?.Draw( frame.Camera, frame.Frustum, neverCullLandblockId: frame.PlayerLandblockId, clipPlanes: default, ndcClipAabb: new Vector4(-1f, -1f, 1f, 1f), inViewLandcells: _walkTerrainInViewLandcells); _terrainDiagnostics.Complete(); return; } 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, inViewLandcells: _walkTerrainInViewLandcells); _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). /// FlushLandscapeAlpha, retail's /// flush-all FlushAlphaList(0f) @0x0059f30b. /// /// internal sealed class WalkProductionLeafRenderer : IWalkFrameLeafRenderer { private RetailPViewPassExecutor _passes = null!; private RetailPViewFrameInput _frame = null!; private ClipFrameAssembly _clipAssembly = null!; private Action _clearInteriorDepth = null!; private Action _drawExitSeals = null!; private readonly HashSet _singleCellScratch = new(); private readonly List _singleCellListScratch = new(); internal WalkProductionLeafRenderer( RetailPViewPassExecutor passes, RetailPViewFrameInput frame, ClipFrameAssembly clipAssembly, Action clearInteriorDepth, Action drawExitSeals) => Reset(passes, frame, clipAssembly, clearInteriorDepth, drawExitSeals); /// /// FW6 allocation closeout: bind the retained leaf and its retained /// one-cell collections to the current frame. Replay is synchronous, so /// no frame may outlive these references. /// internal void Reset( 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 ?? throw new ArgumentNullException(nameof(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) { // Output-gated binary discriminator for the moving wall-textured // triangles at the 0xF4180107/0112 floating stairs. Missing walls are // intentional while enabled: this identifies the visual carrier and // must not become a production admission rule. if (AcDream.Core.Rendering.RenderingDiagnostics .ProbeCathedralSkipFloatingStairCellShells && cellId is 0xF4180107u or 0xF4180112u) { return; } // Retail DrawEnvCell @0x0059F170 submits the prepared EnvCell mesh // whole after stamping it drawn-this-frame. obj_view_set prepares // legacy polygon/object tests, but the use_built_mesh branch calls // D3DPolyRender::DrawMesh directly; it does not slice the shell mesh // once per portal polygon. _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 DrawStaticParticles(IReadOnlySet ownerIds) => _passes.DrawLandscapeStaticParticles( _frame, new RetailPViewLandscapeStaticParticleContext(ownerIds)); public void DrawCellParticles(uint cellId, IReadOnlySet ownerIds) { _passes.DrawCellParticles( _frame, new RetailPViewCellSliceContext(cellId, default, ownerIds)); } public void DrawExitSeals() => _drawExitSeals(); public void DrawPunchFan(WalkPolygon worldPolygon, int activeViewIndex) => _passes.DrawWalkPunchFan(_frame, _clipAssembly, worldPolygon, activeViewIndex); public void AlphaBarrier() => _passes.FlushLandscapeAlpha(); }