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:
parent
e3f0c6a1a1
commit
faba9be635
3 changed files with 25 additions and 6 deletions
|
|
@ -32,9 +32,14 @@ public sealed class WalkTraceConformanceTests
|
||||||
return new DatCollection(datDir!, DatAccessType.Read);
|
return new DatCollection(datDir!, DatAccessType.Read);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Skip = "FW1 adjudication in progress: the walk over-culls six traced "
|
[Fact(Skip = "FW1 outdoor adjudication (2026-08-30 state): with the pinned "
|
||||||
+ "buildings (incl. block a9b3) and the look-in floods differ — see the "
|
+ "camera basis the BLD roster+order match retail EXACTLY except one "
|
||||||
+ "campaign memory. Re-enable as the outdoor gate once adjudicated.")]
|
+ "ring-1 boundary pair (aab50002 extra / a9b3003c missing). The "
|
||||||
|
+ "look-in punches fire at the wrong buildings under BOTH GfxObj "
|
||||||
|
+ "plane-winding conventions (retail: only 001a and 0022, cells "
|
||||||
|
+ "a9b4016x) — adjudicate the PortalRef.PortalIndex join and the "
|
||||||
|
+ "BuildingPortal side-flag decode with a per-building dump next. "
|
||||||
|
+ "Re-enable as the outdoor gate when adjudicated.")]
|
||||||
public void Street_outdoor_first_frame_diff()
|
public void Street_outdoor_first_frame_diff()
|
||||||
{
|
{
|
||||||
// The first landscape-involving conformance case: diff-first (the
|
// The first landscape-involving conformance case: diff-first (the
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,14 @@ public sealed class WalkTraceReplayContext : IWalkFrameContext, IRetailFrameWalk
|
||||||
_cells = cells;
|
_cells = cells;
|
||||||
WorldViewpoint = pose.Origin;
|
WorldViewpoint = pose.Origin;
|
||||||
var rotation = new Quaternion(pose.Q1, pose.Q2, pose.Q3, pose.Q0);
|
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 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.
|
// The exact retail frustum: tan(halfFovY) = ty/vdst, aspect = tx/ty.
|
||||||
float fovY = 2f * MathF.Atan(RetailTy / RetailVdst);
|
float fovY = 2f * MathF.Atan(RetailTy / RetailVdst);
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,12 @@ public static class WalkWorldDatAdapter
|
||||||
: null);
|
: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Adjudication toggle (2026-08-30): the GfxObj portal-poly
|
||||||
|
/// plane winding vs the cell convention is under test — the street
|
||||||
|
/// fixture shows retail punching far fewer look-ins than the first-3
|
||||||
|
/// cross convention admits.</summary>
|
||||||
|
public static bool FlipGfxPolygonPlanes;
|
||||||
|
|
||||||
private static WalkPolygon? BuildGfxPolygon(GfxObj gfxObj, ushort polygonId)
|
private static WalkPolygon? BuildGfxPolygon(GfxObj gfxObj, ushort polygonId)
|
||||||
{
|
{
|
||||||
if (!gfxObj.Polygons.TryGetValue(polygonId, out Polygon? poly)
|
if (!gfxObj.Polygons.TryGetValue(polygonId, out Polygon? poly)
|
||||||
|
|
@ -218,11 +224,14 @@ public static class WalkWorldDatAdapter
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return BuildPolygonFromVertices(
|
WalkPolygon? built = BuildPolygonFromVertices(
|
||||||
poly.VertexIds,
|
poly.VertexIds,
|
||||||
id => gfxObj.VertexArray.Vertices.TryGetValue((ushort)id, out SWVertex? v)
|
id => gfxObj.VertexArray.Vertices.TryGetValue((ushort)id, out SWVertex? v)
|
||||||
? new Vector3(v.Origin.X, v.Origin.Y, v.Origin.Z)
|
? new Vector3(v.Origin.X, v.Origin.Y, v.Origin.Z)
|
||||||
: null);
|
: null);
|
||||||
|
if (built is not null && FlipGfxPolygonPlanes)
|
||||||
|
built.Plane = new WalkPlane(-built.Plane.Normal, -built.Plane.D);
|
||||||
|
return built;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static WalkPolygon? BuildPolygonFromVertices(
|
private static WalkPolygon? BuildPolygonFromVertices(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue