feat(render) Campaign FW1: pin the camera basis; outdoor adjudication state

The moving-fixture sweep pinned the pose decode (quat storage w,x,y,z;
facing = rotated +X; the consistent triple right=-rot(Y), fwd=rot(X),
up=rot(Z) - mean 20.8 deg vs motion, alternatives >=52). With it the
street-outdoor BLD roster and order match retail EXACTLY except one
ring-1 boundary pair (aab50002 extra / a9b3003c missing). Look-in
punches fire at the wrong buildings under BOTH GfxObj plane-winding
conventions (retail punched only 001a and 0022 into the a9b4016x
cells) - the next adjudication targets the PortalRef.PortalIndex join
and the BuildingPortal side-flag decode via a per-building dump. The
driving diff test carries the state in its Skip note; the flip toggle
stays for the next arm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-30 10:40:22 +02:00
parent e3f0c6a1a1
commit faba9be635
3 changed files with 25 additions and 6 deletions

View file

@ -53,9 +53,14 @@ public sealed class WalkTraceReplayContext : IWalkFrameContext, IRetailFrameWalk
_cells = cells;
WorldViewpoint = pose.Origin;
var rotation = new Quaternion(pose.Q1, pose.Q2, pose.Q3, pose.Q0);
Vector3 forward = Vector3.Transform(Vector3.UnitY, rotation);
// Convention pinned against the moving fixtures (2026-08-30 sweep:
// storage w,x,y,z; the facing direction is the rotated +X axis —
// mean 20.8° vs motion headings, every alternative ≥52°). The
// consistent right-handed triple is then right = rot(+Y),
// forward = rot(+X), up = rot(+Z).
Vector3 forward = Vector3.Transform(Vector3.UnitX, rotation);
Vector3 up = Vector3.Transform(Vector3.UnitZ, rotation);
Vector3 right = Vector3.Transform(Vector3.UnitX, rotation);
Vector3 right = -Vector3.Transform(Vector3.UnitY, rotation);
// The exact retail frustum: tan(halfFovY) = ty/vdst, aspect = tx/ty.
float fovY = 2f * MathF.Atan(RetailTy / RetailVdst);