acdream/src/AcDream.App/Rendering/PrivatePresentationRenderer.cs
Erik 7eaa68a5f4 feat(ui): port retail creature appraisal presentation
Render assessed creatures through the shared private viewport with retail heading, bounding-box camera, and light. Build the exact authored nine-row stat list and resolve creature names from the retail EnumMapper while keeping remaining font/sequencer adaptations explicit.

Co-authored-by: Codex <codex@openai.com>
2026-07-23 12:55:24 +02:00

172 lines
5.8 KiB
C#

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);
}
/// <summary>
/// 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.
/// </summary>
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);
}
}
/// <summary>
/// Ordered private creature-view presentation. Retail renders each visible
/// <c>CreatureMode</c> before the retained 2-D tree that displays its texture.
/// </summary>
internal sealed class PrivateEntityViewportFrameGroup :
IPrivateEntityViewportFrame
{
private readonly IPrivateEntityViewportFrame[] _frames;
public PrivateEntityViewportFrameGroup(
params IPrivateEntityViewportFrame?[] frames)
{
_frames = frames.Where(static frame => frame is not null)
.Cast<IPrivateEntityViewportFrame>()
.ToArray();
}
public void Render()
{
foreach (IPrivateEntityViewportFrame frame in _frames)
frame.Render();
}
}
/// <summary>Typed portal viewport adapter over the canonical teleport owner.</summary>
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);
}
/// <summary>
/// One retained-gameplay-UI tick/cursor/draw transaction. This is the explicit
/// boundary where the render graph reaches the retained UI composition owner;
/// <see cref="RetailUiRuntime"/> 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.
/// </summary>
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));
}
}
/// <summary>Optional complete-frame capture adapter.</summary>
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);
}