From f2893667000438cbb4d32ed0b3c5862ad90434d0 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 30 Aug 2026 10:19:45 +0200 Subject: [PATCH] test(render) Campaign FW1: pose-aware oracle trace parser WalkOracleTrace decodes the P lines of the pose-stamped capture round (raw IEEE-754 dwords from Render::viewer_pos @0x0081ef00: camera cell, world origin, the four Frame quaternion components in storage order) into WalkOraclePose on each frame. Poseless FW0 fixtures parse as before. Co-Authored-By: Claude Fable 5 --- .../Rendering/Walk/WalkOracleTrace.cs | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkOracleTrace.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkOracleTrace.cs index d4af658a..11d30e08 100644 --- a/tests/AcDream.App.Tests/Rendering/Walk/WalkOracleTrace.cs +++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkOracleTrace.cs @@ -22,6 +22,7 @@ public static class WalkOracleTrace var frames = new List(); List? current = null; int currentNumber = 0; + WalkOraclePose? currentPose = null; foreach (string line in lines) { @@ -29,15 +30,39 @@ public static class WalkOracleTrace if (frameMatch.Success) { if (current is not null) - frames.Add(new WalkOracleFrame(currentNumber, current)); + frames.Add(new WalkOracleFrame(currentNumber, current, currentPose)); currentNumber = int.Parse( frameMatch.Groups[1].Value, CultureInfo.InvariantCulture); current = new List(); + currentPose = null; continue; } if (current is null) continue; + Match poseMatch = PosePattern.Match(line); + if (poseMatch.Success) + { + // P — raw IEEE-754 dwords + // dumped from Render::viewer_pos @0x0081ef00 at the frame marker. + uint[] raw = new uint[8]; + for (int i = 0; i < 8; i++) + raw[i] = uint.Parse( + poseMatch.Groups[i + 1].Value, + NumberStyles.HexNumber, CultureInfo.InvariantCulture); + currentPose = new WalkOraclePose( + raw[0], + new System.Numerics.Vector3( + BitConverter.Int32BitsToSingle((int)raw[1]), + BitConverter.Int32BitsToSingle((int)raw[2]), + BitConverter.Int32BitsToSingle((int)raw[3])), + BitConverter.Int32BitsToSingle((int)raw[4]), + BitConverter.Int32BitsToSingle((int)raw[5]), + BitConverter.Int32BitsToSingle((int)raw[6]), + BitConverter.Int32BitsToSingle((int)raw[7])); + continue; + } + if (line == "LS") { current.Add(WalkOracleEvent.Landscape()); @@ -102,13 +127,32 @@ public static class WalkOracleTrace => uint.Parse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture); private static readonly Regex FramePattern = new(@"^F (\d+)\s*$", RegexOptions.Compiled); + private static readonly Regex PosePattern = new( + @"^P ([0-9a-f]{8}) ([0-9a-f]{8}) ([0-9a-f]{8}) ([0-9a-f]{8}) ([0-9a-f]{8}) ([0-9a-f]{8}) ([0-9a-f]{8}) ([0-9a-f]{8})\s*$", + RegexOptions.Compiled); private static readonly Regex BuildingPattern = new(@"^BLD ([0-9a-f]{8})\s*$", RegexOptions.Compiled); private static readonly Regex DrawInsidePattern = new(@"^DI ([0-9a-f]{8})\s*$", RegexOptions.Compiled); private static readonly Regex DrawCellsPattern = new( @"^DC pv=[0-9a-f]{8} ov=(\d+) n=(\d+):((?: [0-9a-f]{8})*)\s*$", RegexOptions.Compiled); } -public sealed record WalkOracleFrame(int Number, IReadOnlyList Events) +/// The camera pose dumped at the frame marker (raw dwords from +/// Render::viewer_pos @0x0081ef00): the camera's cell id, world +/// origin, and the Frame quaternion's four raw components (q0..q3 in +/// storage order — axis convention resolved by the replay runner). +/// Present only in the pose-stamped capture round. +public sealed record WalkOraclePose( + uint CellId, + System.Numerics.Vector3 Origin, + float Q0, + float Q1, + float Q2, + float Q3); + +public sealed record WalkOracleFrame( + int Number, + IReadOnlyList Events, + WalkOraclePose? Pose = null) { /// The frame's root: the first DrawInside cell, or null when outdoor-rooted. public uint? InteriorRootCell