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>
This commit is contained in:
Erik 2026-08-30 10:27:38 +02:00
parent 6caedf25f9
commit e8df809c40
2 changed files with 47 additions and 17 deletions

View file

@ -19,16 +19,28 @@ namespace AcDream.App.Tests.Rendering.Walk;
/// </summary>
public sealed class WalkTraceReplayContext : IWalkFrameContext, IRetailFrameWalkContext
{
private sealed class BasisRayCaster(
Vector3 right, Vector3 forward, Vector3 up,
float halfWidth, float halfHeight, float focal) : IWalkRayCaster
// 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)
{
// Screen origin top-left, y down (the xformStart convention).
float nx = (screenX - halfWidth) / halfWidth;
float ny = (halfHeight - screenY) / halfHeight;
return right * (nx / focal) + forward + up * (ny / focal);
float u = screenX * RetailXInvScale - RetailTx;
float w = screenY * RetailYInvScale - RetailTy;
return right * u + forward * RetailVdst - up * w;
}
}
@ -36,10 +48,7 @@ public sealed class WalkTraceReplayContext : IWalkFrameContext, IRetailFrameWalk
private readonly Matrix4x4 _viewProjection;
private readonly IWalkRayCaster _rays;
public WalkTraceReplayContext(
WalkOraclePose pose, Dictionary<uint, WalkCell> cells,
float viewportWidth = 800f, float viewportHeight = 600f,
float verticalFovRadians = 1.0f)
public WalkTraceReplayContext(WalkOraclePose pose, Dictionary<uint, WalkCell> cells)
{
_cells = cells;
WorldViewpoint = pose.Origin;
@ -48,16 +57,16 @@ public sealed class WalkTraceReplayContext : IWalkFrameContext, IRetailFrameWalk
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(
verticalFovRadians, viewportWidth / viewportHeight, 0.1f, 5000f);
fovY, RetailTx / RetailTy, 0.1f, 5000f);
_viewProjection = view * projection;
ViewportWidth = viewportWidth;
ViewportHeight = viewportHeight;
float focal = MathF.Tan(verticalFovRadians / 2f);
_rays = new BasisRayCaster(
right, forward, up, viewportWidth / 2f, viewportHeight / 2f, focal);
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);

View file

@ -0,0 +1,21 @@
.logopen C:\Users\erikn\AppData\Local\Temp\claude\C--Users-erikn-source-repos-acdream--claude-worktrees-peaceful-blackburn-5333f0\e3dbd387-d947-4f49-964d-d454fc9c7d83\scratchpad\fw1-proj-recon.log
.sympath C:\Users\erikn\source\repos\acdream\refs
.symopt+ 0x40
.reload /f acclient.exe
x acclient!Render::bw
x acclient!Render::bh
x acclient!Render::xinvscale
x acclient!Render::yinvscale
x acclient!Render::tx
x acclient!Render::ty
x acclient!Render::vdst
x acclient!Render::viewer_world_space
dd acclient!Render::bw L1
dd acclient!Render::bh L1
dd acclient!Render::xinvscale L1
dd acclient!Render::yinvscale L1
dd acclient!Render::tx L1
dd acclient!Render::ty L1
dd acclient!Render::vdst L1
.logclose
qd