feat(gfx): surface anim channel to the compositor via RenderObject.AnimState

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-07-07 23:39:18 -04:00
parent 0495628b99
commit bcb95f3bb5
2 changed files with 38 additions and 2 deletions

View File

@@ -92,4 +92,28 @@ public class GfxAnimationTests
Assert.Equal(400, vm.Gfx.AnimClockDurationTicks); Assert.Equal(400, vm.Gfx.AnimClockDurationTicks);
Assert.Equal(1, vm.Gfx.AnimClockGeneration); Assert.Equal(1, vm.Gfx.AnimClockGeneration);
} }
[Fact]
public void SnapshotCarriesAnimStateForVisibleObject()
{
var t = T();
// make object 0xA visible via set/draw-texture, then anim-start it toward (800,600,0) over 30 ticks.
var scene = ScriptAssembler.Assemble(t, "ANIM", new List<(int, Operand[])>
{
MovGI(1, 0xA), MovGI(2, 4), MovGI(7, 0x25), MovGI(3, 800), MovGI(4, 600), MovGI(5, 0), MovGI(6, 0),
(0x1f9, new[] { G(7), G(2), I(0) }), // set-texture resId 0x25 -> slot 4
(0x1fb, new[] { G(1), G(2), I(0), I(0), G(3), G(4), G(5), G(6) }), // draw-texture: object 0xA visible
MovGI(8, 30),
(0x234, new[] { G(1), G(8), G(3), G(4), G(5) }), // anim-start(0xA, dur=30, (800,600,0))
Exit(),
}, System.Array.Empty<string>());
var vm = new VirtualMachine(scene, t, new RecordingHost());
vm.Run();
var vis = vm.Gfx.SnapshotVisibleObjects();
Assert.Single(vis);
Assert.True(vis[0].Anim.Enabled);
Assert.Equal((800L, 600L, 0L), (vis[0].Anim.TX, vis[0].Anim.TY, vis[0].Anim.TZ));
Assert.Equal(30, vis[0].Anim.DurationTicks);
Assert.Equal(1, vis[0].Anim.Generation);
}
} }

View File

@@ -2,12 +2,21 @@ using System.Linq;
namespace Age.Engine.Model; namespace Age.Engine.Model;
/// <summary>Per-object animation channel snapshot for the compositor (cluster 0x21c-0x243). Enabled = the
/// object has an active anim channel; (TX,TY,TZ) = the transform target it animates toward; Normalized = the
/// 0x21e ~percent variant; DurationTicks = the object's own duration (anim-start op2). The GLOBAL clock timebase
/// (duration + generation) is read separately off <see cref="GfxState.AnimClockDurationTicks"/>. Generation bumps
/// on each anim-start — the compositor re-triggers its wall-clock tween when it changes.</summary>
public readonly record struct AnimState(bool Enabled, bool Normalized, long TX, long TY, long TZ,
long DurationTicks, long Generation);
/// <summary>A renderable view of one visible gfx object — the host composites these in ascending-handle order /// <summary>A renderable view of one visible gfx object — the host composites these in ascending-handle order
/// (= the engine's z-order) each frame. Built by <see cref="GfxState.SnapshotVisibleObjects"/>; the surface /// (= the engine's z-order) each frame. Built by <see cref="GfxState.SnapshotVisibleObjects"/>; the surface
/// resId/colorkey are resolved from the object's live source slot at snapshot time (see docs/engine-re.md, /// resId/colorkey are resolved from the object's live source slot at snapshot time (see docs/engine-re.md,
/// "The full gfx render model").</summary> /// "The full gfx render model").</summary>
public readonly record struct RenderObject(long Handle, long SurfaceResId, long ColorKey, public readonly record struct RenderObject(long Handle, long SurfaceResId, long ColorKey,
int SrcX, int SrcY, int W, int H, int DstX, int DstY); int SrcX, int SrcY, int W, int H, int DstX, int DstY,
AnimState Anim);
/// <summary>Host-agnostic model of the AGE native gfx command-buffer (reversed in /// <summary>Host-agnostic model of the AGE native gfx command-buffer (reversed in
/// docs/engine-re.md, gfx op-contract table). One registry maps an object handle to a GfxObject — the /// docs/engine-re.md, gfx op-contract table). One registry maps an object handle to a GfxObject — the
@@ -171,7 +180,10 @@ public sealed class GfxState
if (!o.Visible) continue; if (!o.Visible) continue;
var (resId, ck) = _surfaces.TryGetValue(o.SourceSlot, out var s) ? s : (0L, 0L); var (resId, ck) = _surfaces.TryGetValue(o.SourceSlot, out var s) ? s : (0L, 0L);
list.Add(new RenderObject(kv.Key, resId, ck, o.SrcRect.X, o.SrcRect.Y, o.SrcRect.W, o.SrcRect.H, list.Add(new RenderObject(kv.Key, resId, ck, o.SrcRect.X, o.SrcRect.Y, o.SrcRect.W, o.SrcRect.H,
(int)o.V24.X, (int)o.V24.Y)); (int)o.V24.X, (int)o.V24.Y,
new AnimState(o.AnimEnabled, o.AnimNormalized,
o.AnimTarget.X, o.AnimTarget.Y, o.AnimTarget.Z,
o.AnimDurationTicks, o.AnimGeneration)));
} }
return list; return list;
} }