acdream/tests/AcDream.App.Tests/Rendering/Walk/WalkTraceReplay.cs
Erik e8df809c40 feat(render) Campaign FW1: pin the exact retail projection in the replay context
Live-dumped from the capture client (Render::bw/bh 1024x720,
xinvscale/yinvscale 0.00025, tx 0.127875, ty 0.089875, vdst 0.1330767):
the replay context now uses retail unproject formula verbatim for the
ray caster and the exact frustum (fovY = 2*atan(ty/vdst), aspect =
tx/ty) for projection - no guessed camera constants remain.
foundry-deep conformance stays green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 10:27:38 +02:00

119 lines
5.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Numerics;
using AcDream.App.Rendering.Walk;
namespace AcDream.App.Tests.Rendering.Walk;
/// <summary>
/// The FW1 conformance replay harness: reconstructs the camera state from a
/// pose-stamped oracle frame and drives the ported walk over
/// adapter-built world data. Convention notes (adjudicate against the
/// fixtures, loudly, on any mismatch):
/// <list type="bullet">
/// <item>The dumped quaternion is retail Frame storage order w,x,y,z
/// (q0=w) — unit-norm verified on the captures.</item>
/// <item>Retail's frame axes: +Y forward, +Z up (the camera looks along
/// the rotated +Y).</item>
/// <item>Pose origin is landblock-local, the same space the adapter's
/// cell transforms produce.</item>
/// </list>
/// </summary>
public sealed class WalkTraceReplayContext : IWalkFrameContext, IRetailFrameWalkContext
{
// Retail projection globals, dumped live from the capture client
// (recon 2026-08-30 evening: Render::bw/bh/xinvscale/yinvscale/tx/ty/vdst).
public const float RetailViewportWidth = 1024f;
public const float RetailViewportHeight = 720f;
public const float RetailXInvScale = 0.00025f;
public const float RetailYInvScale = 0.00025f;
public const float RetailTx = 0.127875f;
public const float RetailTy = 0.089875f;
public const float RetailVdst = 0.1330766976f;
private sealed class RetailRayCaster(
Vector3 right, Vector3 forward, Vector3 up) : IWalkRayCaster
{
// Retail's unproject (copy_view's ray path; equal to
// ScreenToViewTransform for these globals):
// u = sx·xinvscale tx; w = sy·yinvscale ty
// ray = Xaxis·u + Yaxis·vdst Zaxis·w
public Vector3 RayThrough(float screenX, float screenY)
{
float u = screenX * RetailXInvScale - RetailTx;
float w = screenY * RetailYInvScale - RetailTy;
return right * u + forward * RetailVdst - up * w;
}
}
private readonly Dictionary<uint, WalkCell> _cells;
private readonly Matrix4x4 _viewProjection;
private readonly IWalkRayCaster _rays;
public WalkTraceReplayContext(WalkOraclePose pose, Dictionary<uint, WalkCell> cells)
{
_cells = cells;
WorldViewpoint = pose.Origin;
var rotation = new Quaternion(pose.Q1, pose.Q2, pose.Q3, pose.Q0);
Vector3 forward = Vector3.Transform(Vector3.UnitY, rotation);
Vector3 up = Vector3.Transform(Vector3.UnitZ, rotation);
Vector3 right = Vector3.Transform(Vector3.UnitX, rotation);
// The exact retail frustum: tan(halfFovY) = ty/vdst, aspect = tx/ty.
float fovY = 2f * MathF.Atan(RetailTy / RetailVdst);
Matrix4x4 view = Matrix4x4.CreateLookAt(pose.Origin, pose.Origin + forward, up);
Matrix4x4 projection = Matrix4x4.CreatePerspectiveFieldOfView(
fovY, RetailTx / RetailTy, 0.1f, 5000f);
_viewProjection = view * projection;
ViewportWidth = RetailViewportWidth;
ViewportHeight = RetailViewportHeight;
_rays = new RetailRayCaster(right, forward, up);
// The retail CY near plane: N = forward, d = dot(eye, forward) znear.
CyPlane = new WalkPlane(forward, -Vector3.Dot(pose.Origin, forward) - 0.1f);
}
public Vector3 ViewpointIn(WalkCell cell)
=> Vector3.Transform(WorldViewpoint, cell.InverseWorldTransform);
public Matrix4x4 ObjectToClip(WalkCell cell)
=> cell.WorldTransform * _viewProjection;
public WalkCell? GetVisible(uint cellId) => _cells.GetValueOrDefault(cellId);
public IWalkRayCaster Rays => _rays;
public Vector3 WorldViewpoint { get; }
public float ViewportWidth { get; }
public float ViewportHeight { get; }
public WalkPlane CyPlane { get; }
public IWalkFrameContext CellContext => this;
public void SetActiveView(WalkPortalView views, int index) { }
public Vector3 ViewpointInBuilding(WalkBuilding building) => WorldViewpoint;
public int ClipBuildingPolygon(
WalkBuilding building, WalkPolygon polygon, int side, Span<WalkScreenPoint> output)
=> 0; // building look-ins join the conformance surface with the landscape fixtures
// ---- signatures for comparing walk output to oracle frames ----
public static string Signature(IEnumerable<WalkEvent> events)
=> string.Join("|", events.Select(e => e.Kind switch
{
WalkEventKind.Landscape => "LS",
WalkEventKind.Building => $"BLD:{e.CellId:x8}",
WalkEventKind.DrawInside => $"DI:{e.CellId:x8}",
WalkEventKind.DrawCells =>
$"DC:ov={e.OutsideViewCount}:{string.Join(',', e.Cells.Select(c => c.ToString("x8")))}",
_ => "?",
}));
public static string Signature(WalkOracleFrame frame)
=> string.Join("|", frame.Events.Select(e => e.Kind switch
{
WalkOracleEventKind.Landscape => "LS",
WalkOracleEventKind.Building => $"BLD:{e.CellId!.Value:x8}",
WalkOracleEventKind.DrawInside => $"DI:{e.CellId!.Value:x8}",
WalkOracleEventKind.DrawCells =>
$"DC:ov={e.OutsideViewCount}:{string.Join(',', e.Cells.Select(c => c.ToString("x8")))}",
_ => "?",
}));
}