diff --git a/src/AcDream.App/Rendering/Walk/WalkEvents.cs b/src/AcDream.App/Rendering/Walk/WalkEvents.cs new file mode 100644 index 00000000..b3aa43c0 --- /dev/null +++ b/src/AcDream.App/Rendering/Walk/WalkEvents.cs @@ -0,0 +1,58 @@ +namespace AcDream.App.Rendering.Walk; + +/// +/// Campaign FW1 — the frame walk's observable event stream. These events +/// mirror the FW0 oracle-trace vocabulary one-to-one +/// (docs/research/2026-08-30-fw-walk-oracle/README.md): the conformance +/// contract is that RetailFrameWalk, fed the same world state and +/// camera pose as a capture, emits the identical sequence. Richer draw +/// commands (mesh/state/stage tuples for the FW2 ordered stream) layer on +/// later; this vocabulary stays the conformance surface. +/// +public enum WalkEventKind +{ + /// LScape::draw @0x00506330 ran (outdoor root, or drawn + /// through an interior frame's exit views). + Landscape, + + /// RenderDeviceD3D::DrawBuilding @0x0059f2a0 entered for + /// one building (identified by its position cell id). + Building, + + /// PView::DrawInside @0x005a5860 entered — the frame is + /// rooted at this interior cell. + DrawInside, + + /// PView::DrawCells @0x005a4840 entered with the flood's + /// cell list (far-to-near iteration happens inside; the event carries the + /// list in storage order, matching the oracle dump). + DrawCells, +} + +public readonly record struct WalkEvent( + WalkEventKind Kind, + uint CellId, + int OutsideViewCount, + IReadOnlyList Cells) +{ + public static WalkEvent Landscape() + => new(WalkEventKind.Landscape, 0, 0, Array.Empty()); + + public static WalkEvent Building(uint positionCellId) + => new(WalkEventKind.Building, positionCellId, 0, Array.Empty()); + + public static WalkEvent DrawInside(uint cellId) + => new(WalkEventKind.DrawInside, cellId, 0, Array.Empty()); + + public static WalkEvent DrawCells(int outsideViewCount, IReadOnlyList cells) + => new(WalkEventKind.DrawCells, 0, outsideViewCount, cells); +} + +/// +/// The sink a walk emits into. FW1 conformance tests collect events; FW2's +/// production sink will additionally receive the ordered draw stream. +/// +public interface IWalkEventSink +{ + void Emit(in WalkEvent walkEvent); +}