acdream/tests/AcDream.App.Tests/Rendering/Walk/WalkProjectionDiagnosticTests.cs

123 lines
4.6 KiB
C#

using System.Globalization;
using System.Numerics;
using AcDream.App.Rendering.Walk;
namespace AcDream.App.Tests.Rendering.Walk;
public sealed class WalkReplayProjectionTests
{
private const string OhRoot =
"docs/research/2026-09-01-overhaul/oh-capture";
[Fact]
public void Captured_matrix_screen_ray_screen_round_trips_without_legacy_half_pixel()
{
WalkOracleFrame captured = Assert.Single(WalkOracleTrace.Load(
OhRoot,
"cathedral-stair-arch-e229.combined-r1"));
WalkOraclePose pose = Assert.IsType<WalkOraclePose>(captured.ReplayPose);
var context = new WalkTraceReplayContext(
pose,
new Dictionary<uint, WalkCell>());
Vector2[] points =
[
new(512f, 360f),
new(0f, 0f),
new(1024f, 0f),
new(1024f, 720f),
new(0f, 720f),
new(127.25f, 83.75f),
new(817.5f, 611.125f),
];
foreach (Vector2 point in points)
{
Vector3 ray = context.Rays.RayThrough(point.X, point.Y);
Vector2 roundTrip = context.ProjectWorldToScreen(
pose.Origin + ray * 100f);
float error = Vector2.Distance(point, roundTrip);
Assert.True(
error <= 0.001f,
string.Create(
CultureInfo.InvariantCulture,
$"matrix round-trip error {error:R} at {point}"));
}
Vector3 legacyCenterRay = LegacyRayThrough(pose, 512f, 360f);
Vector2 legacyCenter = context.ProjectWorldToScreen(
pose.Origin + legacyCenterRay * 100f);
Assert.InRange(MathF.Abs(legacyCenter.X - 512f), 0.45f, 0.55f);
Assert.InRange(MathF.Abs(legacyCenter.Y - 360f), 0.45f, 0.55f);
}
[Fact]
public void Configured_projection_is_bound_to_captured_newmethod_matrix_words()
{
string[] lines = File.ReadAllLines(FixturePath(
"cathedral-stair-arch-e229.projection-mode"));
int newMethod = Array.FindIndex(lines, line => line.Trim() == "NEWMETHOD");
Assert.True(newMethod >= 0);
Assert.Matches(@"\s00000001\s*$", lines[newMethod + 1]);
int heading = Array.FindIndex(lines, line => line.Trim() == "VIEW_TO_CLIP");
Assert.True(heading >= 0);
uint[] captured = lines.Skip(heading + 1)
.Take(4)
.SelectMany(ParseDumpRow)
.ToArray();
Matrix4x4 configured = WalkTraceReplayContext.CapturedViewToClip();
uint[] actual =
[
Bits(configured.M11), Bits(configured.M12), Bits(configured.M13), Bits(configured.M14),
Bits(configured.M21), Bits(configured.M22), Bits(configured.M23), Bits(configured.M24),
Bits(configured.M31), Bits(configured.M32), Bits(configured.M33), Bits(configured.M34),
Bits(configured.M41), Bits(configured.M42), Bits(configured.M43), Bits(configured.M44),
];
Assert.Equal(captured, actual);
}
private static Vector3 LegacyRayThrough(
WalkOraclePose pose,
float screenX,
float screenY)
{
var rotation = new Quaternion(pose.Q1, pose.Q2, pose.Q3, pose.Q0);
Vector3 right = Vector3.Transform(Vector3.UnitX, rotation);
Vector3 forward = Vector3.Transform(Vector3.UnitY, rotation);
Vector3 up = Vector3.Transform(Vector3.UnitZ, rotation);
float u = screenX * 0.00025f - 0.127875f;
float w = screenY * 0.00025f - 0.089875f;
return right * u + forward * 0.1330766976f - up * w;
}
private static IEnumerable<uint> ParseDumpRow(string line)
{
string[] fields = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
Assert.Equal(5, fields.Length);
return fields.Skip(1).Select(value => uint.Parse(
value,
NumberStyles.HexNumber,
CultureInfo.InvariantCulture));
}
private static uint Bits(float value) =>
BitConverter.SingleToUInt32Bits(value);
private static string FixturePath(string fixtureName) => Path.Combine(
FindRepositoryRoot(),
Path.Combine(OhRoot.Split('/')),
fixtureName + ".log");
private static string FindRepositoryRoot()
{
DirectoryInfo? directory = new(AppContext.BaseDirectory);
while (directory is not null)
{
if (File.Exists(Path.Combine(directory.FullName, "AcDream.slnx")))
return directory.FullName;
directory = directory.Parent;
}
throw new InvalidOperationException("AcDream.slnx not found.");
}
}