using System.Numerics; using AcDream.App.Diagnostics; using AcDream.App.Streaming; using AcDream.App.UI; using Silk.NET.Input; namespace AcDream.App.Rendering; internal interface IPrivatePortalViewport { void Draw(int width, int height); } internal interface IPrivateEntityViewportFrame { void Render(); } internal interface IRetainedGameplayUiFrame { void Render(double deltaSeconds, int width, int height); } internal interface IPrivateFrameScreenshot { bool CapturePending(int width, int height); } /// /// Draws the private and 2-D presentation layers after the world alpha scope /// has closed: portal CreatureMode, paperdoll FBO, retained gameplay UI, /// developer UI, then the complete-frame screenshot. /// internal sealed class PrivatePresentationRenderer : IPrivatePresentationFramePhase { private readonly IPrivatePortalViewport _portal; private readonly IRenderFrameFoundationSource _foundation; private readonly IPrivateEntityViewportFrame? _entityViewports; private readonly IRetainedGameplayUiFrame? _gameplayUi; private readonly IDevToolsFrameLifecycle? _devTools; private readonly IPrivateFrameScreenshot? _screenshots; public PrivatePresentationRenderer( IPrivatePortalViewport portal, IRenderFrameFoundationSource foundation, IPrivateEntityViewportFrame? entityViewports, IRetainedGameplayUiFrame? gameplayUi, IDevToolsFrameLifecycle? devTools, IPrivateFrameScreenshot? screenshots) { _portal = portal ?? throw new ArgumentNullException(nameof(portal)); _foundation = foundation ?? throw new ArgumentNullException(nameof(foundation)); _entityViewports = entityViewports; _gameplayUi = gameplayUi; _devTools = devTools; _screenshots = screenshots; } public PrivatePresentationFrameOutcome Render( RenderFrameInput input, WorldRenderFrameOutcome world) { _ = world; // Foundation is the immutable result of this frame's clear phase. // Snapshot it before drawing so a portal transition raised by a // private layer cannot relabel the viewport that was just presented. bool portalViewportVisible = _foundation.Foundation.PortalViewportVisible; _portal.Draw(input.ViewportWidth, input.ViewportHeight); _entityViewports?.Render(); _gameplayUi?.Render( input.DeltaSeconds, input.ViewportWidth, input.ViewportHeight); _devTools?.Render( input.DeltaSeconds, input.ViewportWidth, input.ViewportHeight); bool screenshotCaptured = _screenshots?.CapturePending( input.ViewportWidth, input.ViewportHeight) == true; return new PrivatePresentationFrameOutcome( portalViewportVisible, screenshotCaptured); } } /// /// Ordered private creature-view presentation. Retail renders each visible /// CreatureMode before the retained 2-D tree that displays its texture. /// internal sealed class PrivateEntityViewportFrameGroup : IPrivateEntityViewportFrame { private readonly IPrivateEntityViewportFrame[] _frames; public PrivateEntityViewportFrameGroup( params IPrivateEntityViewportFrame?[] frames) { _frames = frames.Where(static frame => frame is not null) .Cast() .ToArray(); } public void Render() { foreach (IPrivateEntityViewportFrame frame in _frames) frame.Render(); } } /// Typed portal viewport adapter over the canonical teleport owner. internal sealed class LocalPlayerPortalViewport : IPrivatePortalViewport { private readonly LocalPlayerTeleportController _teleport; private readonly CameraController _camera; public LocalPlayerPortalViewport( LocalPlayerTeleportController teleport, CameraController camera) { _teleport = teleport ?? throw new ArgumentNullException(nameof(teleport)); _camera = camera ?? throw new ArgumentNullException(nameof(camera)); } public void Draw(int width, int height) => _teleport.DrawPortalViewport(width, height, _camera.Active.Projection); } /// /// One retained-gameplay-UI tick/cursor/draw transaction. This is the explicit /// boundary where the render graph reaches the retained UI composition owner; /// intentionally retains its documented state and /// command bindings. The distinction between direct frame ownership and /// recursive shared-owner reachability is recorded in /// docs/architecture/code-structure.md. /// internal sealed class RetainedGameplayUiFrame : IRetainedGameplayUiFrame { private readonly RetailUiRuntime _runtime; private readonly IInputContext? _input; public RetainedGameplayUiFrame( RetailUiRuntime runtime, IInputContext? input) { _runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); _input = input; } public void Render(double deltaSeconds, int width, int height) { _runtime.Tick(deltaSeconds); if (_input is not null) _runtime.UpdateCursor(_input.Mice); _runtime.Draw(new Vector2(width, height)); } } /// Optional complete-frame capture adapter. internal sealed class PrivateFrameScreenshot : IPrivateFrameScreenshot { private readonly FrameScreenshotController _screenshots; public PrivateFrameScreenshot(FrameScreenshotController screenshots) => _screenshots = screenshots ?? throw new ArgumentNullException(nameof(screenshots)); public bool CapturePending(int width, int height) => _screenshots.CapturePending(width, height); }