diff --git a/engine/Age.Engine.Tests/AnimInterpolatorTests.cs b/engine/Age.Engine.Tests/AnimInterpolatorTests.cs
index b91aa21..779d29a 100644
--- a/engine/Age.Engine.Tests/AnimInterpolatorTests.cs
+++ b/engine/Age.Engine.Tests/AnimInterpolatorTests.cs
@@ -43,15 +43,17 @@ public class AnimInterpolatorTests
}
[Fact]
- public void ColorAnim_PingPongsAlphaTowardTarget()
+ public void ColorAnim_PingPongsTintStrength_ObjectStaysOpaque()
{
var g = VisibleObj(0x100);
- // base = opaque white (no static color); target = alpha 0 (fade out) => pulses opaque<->transparent
- g.SetColorAnim(0x100, period: 1000, target: GfxState.PackColor(0x00, 0x000000));
+ // base = no tint (strength 0); target = full-strength (alpha 0xff) red glow => strength pulses 0<->255
+ g.SetColorAnim(0x100, period: 1000, target: GfxState.PackColor(0xFF, 0xFF0000));
g.SnapshotVisibleObjects(0); // seeds start=0
- var opaque = g.SnapshotVisibleObjects(0).Single();
- Assert.Equal(255, opaque.Alpha); // t=0 -> base opaque
+ var baseFrame = g.SnapshotVisibleObjects(0).Single();
+ Assert.Equal(255, baseFrame.Alpha); // object opacity ALWAYS opaque (never the color alpha)
+ Assert.Equal(0, baseFrame.TintStrength); // t=0 -> no tint
var peak = g.SnapshotVisibleObjects(500).Single();
- Assert.Equal(0, peak.Alpha); // t=1 -> target alpha 0
+ Assert.Equal(255, peak.Alpha); // still opaque
+ Assert.Equal(255, peak.TintStrength); // t=1 -> full tint strength (the glow peak)
}
}
diff --git a/engine/Age.Engine.Tests/RenderObjectBlendTests.cs b/engine/Age.Engine.Tests/RenderObjectBlendTests.cs
index 78f00d1..9b22b1c 100644
--- a/engine/Age.Engine.Tests/RenderObjectBlendTests.cs
+++ b/engine/Age.Engine.Tests/RenderObjectBlendTests.cs
@@ -14,25 +14,33 @@ public class RenderObjectBlendTests
}
[Fact]
- public void ObjectWithoutColor_ResolvesOpaqueWhiteTint()
+ public void ObjectWithoutColor_ResolvesOpaqueNoTint()
{
var g = WithVisibleObject(0x100, resId: 5, colorKey: -1);
var ro = g.SnapshotVisibleObjects().Single();
- Assert.Equal(255, ro.Alpha);
+ Assert.Equal(255, ro.Alpha); // opaque
+ Assert.Equal(0, ro.TintStrength); // no tint
Assert.Equal(0xFFFFFF, ro.Tint);
Assert.Equal(BlendKind.Opaque, ro.Blend);
}
[Fact]
- public void DrawColor_0x203_SetsAlphaTintAndBlend()
+ public void DrawColor_0x203_IsTintStrength_ObjectStaysOpaque()
{
+ // Op 0x203/0x202 alpha is TINT-BLEND STRENGTH, not object opacity (evidence: a CG drawn with
+ // (alpha=0, color=white) must stay fully OPAQUE + untinted, not vanish). Root cause of the grey-BG.
var g = WithVisibleObject(0x100, resId: 5, colorKey: -1);
- // emulate op 0x203: pack (alpha=0x80, color=0x102030) and mark HasColor
- g.SetObjectColor(0x100, GfxState.PackColor(0x80, 0x102030));
+ g.SetObjectColor(0x100, GfxState.PackColor(0x00, 0xFFFFFF)); // "no tint" — the grey-BG case
var ro = g.SnapshotVisibleObjects().Single();
- Assert.Equal(0x80, ro.Alpha);
- Assert.Equal(0x102030, ro.Tint);
- Assert.Equal(BlendKind.Alpha, ro.Blend);
+ Assert.Equal(255, ro.Alpha); // OBJECT STAYS OPAQUE (was wrongly 0 -> invisible)
+ Assert.Equal(0, ro.TintStrength); // zero tint strength
+
+ g.SetObjectColor(0x100, GfxState.PackColor(0x80, 0x102030)); // half-strength tint toward 0x102030
+ var ro2 = g.SnapshotVisibleObjects().Single();
+ Assert.Equal(255, ro2.Alpha); // still opaque
+ Assert.Equal(0x80, ro2.TintStrength); // strength from the alpha byte
+ Assert.Equal(0x102030, ro2.Tint);
+ Assert.Equal(BlendKind.Alpha, ro2.Blend);
}
[Fact]
diff --git a/engine/Age.Engine/Model/GfxState.cs b/engine/Age.Engine/Model/GfxState.cs
index 7556b7d..b3988ee 100644
--- a/engine/Age.Engine/Model/GfxState.cs
+++ b/engine/Age.Engine/Model/GfxState.cs
@@ -14,9 +14,13 @@ public readonly record struct AnimState(bool Enabled, bool Normalized, long TX,
/// (= the engine's z-order) each frame. Built by ; the surface
/// resId/colorkey are resolved from the object's live source slot at snapshot time (see docs/engine-re.md,
/// "The full gfx render model").
+/// is the object's OPACITY (0-255). is how
+/// strongly (RGB) is blended into the texel (0=keep texel, 255=full tint) — this is the
+/// op 0x202/0x203/0x232 "alpha" byte, which is a tint strength, NOT opacity (conflating them made opaque CGs
+/// vanish — the grey-background bug).
public readonly record struct RenderObject(long Handle, long SurfaceResId, long ColorKey,
int SrcX, int SrcY, int W, int H, int DstX, int DstY,
- AnimState Anim, int Alpha, long Tint, BlendKind Blend);
+ AnimState Anim, int Alpha, long Tint, int TintStrength, BlendKind Blend);
/// 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
@@ -236,20 +240,21 @@ public sealed class GfxState
if (!o.Visible) continue;
var (resId, ck) = _surfaces.TryGetValue(o.SourceSlot, out var s) ? s : (0L, 0L);
- // ---- color: slice-A static base, then op-0x232 ping-pong toward the target ----
- int alpha = 255; long tint = 0xFFFFFF; var blend = BlendKind.Opaque;
+ // ---- color: object stays OPAQUE; the op-0x202/0x203 alpha is a TINT STRENGTH (0=keep texel,
+ // 255=full tint), NOT opacity. 0x232 ping-pongs the strength (+tint) toward the target (glow). ----
+ int alpha = 255; long tint = 0xFFFFFF; int strength = 0; var blend = BlendKind.Opaque;
if (o.HasColor)
{
var (a, r, g, b) = BlendMath.UnpackArgb(o.Color);
- alpha = a; tint = ((long)r << 16) | ((long)g << 8) | (long)b; blend = BlendKind.Alpha;
+ strength = a; tint = ((long)r << 16) | ((long)g << 8) | (long)b; blend = BlendKind.Alpha;
}
if (o.ColorAnim)
{
if (o.ColorStart < 0) o.ColorStart = nowMs;
double t = PingPongWeight(nowMs, o.ColorStart, o.ColorPeriod);
var (ta, tr, tg, tb) = BlendMath.UnpackArgb(o.ColorTarget);
- var (ba, br, bg, bb) = (alpha, (int)((tint >> 16) & 0xff), (int)((tint >> 8) & 0xff), (int)(tint & 0xff));
- alpha = (int)(ba + (ta - ba) * t);
+ var (br, bg, bb) = ((int)((tint >> 16) & 0xff), (int)((tint >> 8) & 0xff), (int)(tint & 0xff));
+ strength = (int)(strength + (ta - strength) * t);
tint = ((long)(br + (tr - br) * t) << 16) | ((long)(bg + (tg - bg) * t) << 8) | (long)(bb + (tb - bb) * t);
blend = BlendKind.Alpha;
}
@@ -278,7 +283,7 @@ public sealed class GfxState
new AnimState(o.AnimEnabled, o.AnimNormalized,
o.AnimTarget.X, o.AnimTarget.Y, o.AnimTarget.Z,
o.AnimDurationTicks, o.AnimGeneration),
- alpha, tint, blend));
+ alpha, tint, strength, blend));
}
return list;
}