using AcDream.App.Rendering;
using AcDream.Core.Meshing;
namespace AcDream.App.Tests.Rendering;
///
/// S4-c2: 's port of retail
/// D3DPolyRender::DrawMesh @0x0059d4a0's branch table (spec §4).
///
public sealed class RetailAlphaMeshRouterTests
{
///
/// Hand-traced literal cases against the Ghidra-verified decomp — each
/// one differentiates exactly one row boundary, with the mutation each
/// guards against spelled out. delayMask is fixed at the real default
/// (0x0E) throughout, matching the contract's fixed axis.
///
public static IEnumerable RowBoundaryCases()
{
// Row 1a: sky wins outright even over a mask that would otherwise
// hit row 3. Mutation: dropping the sky check entirely returns
// Append(Alpha) instead.
yield return new object[]
{
true, false, false, (byte)0x02, false,
(int)RetailAlphaMeshAction.Immediate, (int)RetailAlphaList.Alpha, false,
};
// Row 1c: detailSurfaceActive wins outright even with multipass=true
// and a clip-bearing mask that would otherwise hit row 2. Mutation:
// dropping the detail check returns AppendClipAndImmediate instead.
yield return new object[]
{
false, true, true, (byte)0x08, false,
(int)RetailAlphaMeshAction.Immediate, (int)RetailAlphaList.Alpha, false,
};
// Row 2a: multipass + clip bit (exact 0x08) -> CLIP,
// overrideClipmap=true, ALSO immediate. Mutation: an `==` (exact
// mask match) instead of a bit test would still pass THIS case...
yield return new object[]
{
false, false, true, (byte)0x08, false,
(int)RetailAlphaMeshAction.AppendClipAndImmediate, (int)RetailAlphaList.Clip, true,
};
// ...but Row 2b (0x09 = clip bit + stipple bit) exposes an `==`
// mutation: it would fall through to row 3 (Append, no immediate
// pass) instead of also matching row 2's bit test.
yield return new object[]
{
false, false, true, (byte)0x09, false,
(int)RetailAlphaMeshAction.AppendClipAndImmediate, (int)RetailAlphaList.Clip, true,
};
// Row 2c: multipass=true but mask has NO clip bit (0x02) -> must
// fall through to row 3, not row 2. Mutation: a row-2 test that
// ignores the clip-bit requirement (fires on multipass alone) would
// return AppendClipAndImmediate here instead of plain Append(Alpha).
yield return new object[]
{
false, false, true, (byte)0x02, false,
(int)RetailAlphaMeshAction.Append, (int)RetailAlphaList.Alpha, false,
};
// Row 3a: alpha-family mask -> ALPHA, no immediate pass. Mutation:
// swapping the CLIP/ALPHA selection (always ALPHA regardless of bit
// 0x08) would still pass this one — paired with 3b below.
yield return new object[]
{
false, false, false, (byte)0x02, false,
(int)RetailAlphaMeshAction.Append, (int)RetailAlphaList.Alpha, false,
};
// Row 3b: clip-map mask -> CLIP (bit 0x08 selects the list even
// without multipass). Mutation: always routing row 3 to ALPHA
// regardless of the clip bit fails THIS case (expects Clip).
yield return new object[]
{
false, false, false, (byte)0x08, false,
(int)RetailAlphaMeshAction.Append, (int)RetailAlphaList.Clip, false,
};
// Row 3c: translucent-only mask (0x04, no clip bit) -> ALPHA.
yield return new object[]
{
false, false, false, (byte)0x04, false,
(int)RetailAlphaMeshAction.Append, (int)RetailAlphaList.Alpha, false,
};
// Row-3-inert-stipple: mask 0x01 (stipple bit ONLY, no alpha/clip/
// translucent bit) does NOT intersect the fixed default delay mask
// 0x0E (bit 0 is absent from 0x0E) — falls through to row 4.
// Mutation: including bit 0 in the effective delay test (treating
// 0x0E as if it were 0x0F) would make this case ALSO match row 3
// (Append) instead of falling to row 4's material-fallback decision.
yield return new object[]
{
false, false, false, (byte)0x01, true,
(int)RetailAlphaMeshAction.Append, (int)RetailAlphaList.Alpha, false,
};
// Row 4a: plain mask (0x00), material has_alpha true -> ALPHA
// fallback. Mutation: requiring the TRANSLUCENT bit (0x04) to
// ALREADY be set on the mask itself (rather than just checking the
// DELAY mask's own bit 0x04, which is fixed on regardless of the
// subset's mask) would make this case fall to row 5 (Immediate)
// instead.
yield return new object[]
{
false, false, false, (byte)0x00, true,
(int)RetailAlphaMeshAction.Append, (int)RetailAlphaList.Alpha, false,
};
// Row 4 vs 5: plain mask, material has_alpha FALSE -> row 4's own
// condition fails -> row 5 Immediate. Mutation: dropping the
// materialHasAlpha check from row 4 (treating it as "always append
// when mask is plain") would return Append here instead.
yield return new object[]
{
false, false, false, (byte)0x00, false,
(int)RetailAlphaMeshAction.Immediate, (int)RetailAlphaList.Alpha, false,
};
}
///
/// /
/// are boxed as (not the internal enum types)
/// because [Theory] methods must be and a
/// public method cannot expose an parameter
/// type — /
/// stay internal (this assembly's whole alpha-queue surface is internal).
///
[Theory]
[MemberData(nameof(RowBoundaryCases))]
public void Route_MatchesTheHandTracedDecompBoundary(
bool sky, bool detail, bool multipass, byte mask, bool hasAlpha,
int expectedActionRaw, int expectedListRaw,
bool expectedOverrideClipmap)
{
var expectedAction = (RetailAlphaMeshAction)expectedActionRaw;
var expectedList = (RetailAlphaList)expectedListRaw;
RetailAlphaMeshDecision decision = RetailAlphaMeshRouter.Route(
sky, RetailAlphaMeshRouter.DefaultDelayMask, detail, multipass, mask, hasAlpha);
Assert.Equal(expectedAction, decision.Action);
if (expectedAction != RetailAlphaMeshAction.Immediate)
{
Assert.Equal(expectedList, decision.List);
Assert.Equal(expectedOverrideClipmap, decision.OverrideClipmap);
}
}
///
/// S4-c2 fix round 1 (A7): this was documented as an "INDEPENDENTLY-
/// shaped reference derivation (a switch-driven truth table, not a copy
/// of 's own if-chain)" — false;
/// below is the SAME five-row
/// if-chain shape with different local variable names, not a switch or
/// lookup table. It is renamed and redocumented honestly rather than
/// rewritten into a literal 160-row table (spec §4's five rows over ten
/// masks × sky × detail × multipass × hasAlpha) — the per-row
/// Theory above already carries one
/// mutation text per row boundary; this brute-force sweep's real value
/// is catching a copy-paste/off-by-one divergence BETWEEN the two
/// restatements (a typo in one that the other doesn't share), not an
/// independent verification of the spec itself. The claim that "each of
/// the five rows' conditions was flipped in turn and this test failed
/// every time" is deleted — that hand-verification was never re-run and
/// is not reproduced in this round's evidence.
///
[Fact]
public void Route_MatchesRestatedBranchTableAcrossEveryCell()
{
byte[] masks = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09];
bool[] bothBools = [false, true];
int mismatches = 0;
var firstMismatch = "";
foreach (byte mask in masks)
foreach (bool sky in bothBools)
foreach (bool detail in bothBools)
foreach (bool multipass in bothBools)
foreach (bool hasAlpha in bothBools)
{
RetailAlphaMeshDecision actual = RetailAlphaMeshRouter.Route(
sky, RetailAlphaMeshRouter.DefaultDelayMask, detail, multipass, mask, hasAlpha);
RetailAlphaMeshDecision expected = RestatedBranchTableRoute(
sky, RetailAlphaMeshRouter.DefaultDelayMask, detail, multipass, mask, hasAlpha);
bool matches = actual.Action == expected.Action
&& (actual.Action == RetailAlphaMeshAction.Immediate
|| (actual.List == expected.List
&& actual.OverrideClipmap == expected.OverrideClipmap));
if (!matches && mismatches++ == 0)
{
firstMismatch =
$"sky={sky} detail={detail} multipass={multipass} mask=0x{mask:x2} "
+ $"hasAlpha={hasAlpha}: expected {expected}, got {actual}";
}
}
Assert.True(mismatches == 0, $"{mismatches} mismatches; first: {firstMismatch}");
}
/// Re-derivation of spec §4's table in independently-named
/// local variables (NOT a switch/lookup shape — see this Fact's own doc
/// comment above, A7).
private static RetailAlphaMeshDecision RestatedBranchTableRoute(
bool sky, byte delayMask, bool detail, bool multipass, byte mask, bool hasAlpha)
{
bool row1 = sky || delayMask == 0 || detail;
if (row1)
return new RetailAlphaMeshDecision(RetailAlphaMeshAction.Immediate, default, false);
bool clipBit = (mask & 0b1000) == 0b1000;
bool row2 = multipass switch
{
true => clipBit,
false => false,
};
if (row2)
return new RetailAlphaMeshDecision(RetailAlphaMeshAction.AppendClipAndImmediate, RetailAlphaList.Clip, true);
int intersect = delayMask & mask;
bool row3 = intersect != 0;
if (row3)
{
RetailAlphaList list = clipBit ? RetailAlphaList.Clip : RetailAlphaList.Alpha;
return new RetailAlphaMeshDecision(RetailAlphaMeshAction.Append, list, false);
}
bool delayHasTranslucentBit = (delayMask & 0b0100) == 0b0100;
bool row4 = delayHasTranslucentBit && hasAlpha;
if (row4)
return new RetailAlphaMeshDecision(RetailAlphaMeshAction.Append, RetailAlphaList.Alpha, false);
return new RetailAlphaMeshDecision(RetailAlphaMeshAction.Immediate, default, false);
}
///
/// 's
/// complete mapping. Ordinary Wb callers pre-filter opaque/ClipMap and
/// retain AP-239's earlier subset-mask reconstruction distinction. Mesh
/// particles, however, genuinely call this method without that ordinary-
/// Wb pre-filter, including for opaque-classified batches.
///
[Theory]
[InlineData(TranslucencyKind.AlphaBlend, RetailAlphaMeshRouter.MaskAlphaFamily)]
[InlineData(TranslucencyKind.Additive, RetailAlphaMeshRouter.MaskAlphaFamily)]
[InlineData(TranslucencyKind.InvAlpha, RetailAlphaMeshRouter.MaskAlphaFamily)]
[InlineData(TranslucencyKind.ClipMap, RetailAlphaMeshRouter.MaskClipMap)]
[InlineData(TranslucencyKind.Opaque, (byte)0)]
public void MaskFromTranslucencyKind_MapsEveryKind(TranslucencyKind kind, byte expectedMask)
=> Assert.Equal(expectedMask, RetailAlphaMeshRouter.MaskFromTranslucencyKind(kind));
/// Spec §2's priority chain: alpha-family bits win outright over
/// ClipMap and Translucent even when both are also present. Mutation
/// check: checking ClipMap before the alpha-family union would return
/// 0x08 here instead of 0x02.
[Fact]
public void ConstructSubsetMask_AlphaFamilyWinsOverClipMapAndTranslucent()
{
byte mask = RetailAlphaMeshRouter.ConstructSubsetMask(
hasAlphaFamilyBit: true, hasClipMapBit: true, hasTranslucentBit: true,
hasPositiveStippling: false);
Assert.Equal(RetailAlphaMeshRouter.MaskAlphaFamily, mask);
}
/// ClipMap wins over Translucent when no alpha-family bit is
/// present. Mutation check: checking Translucent before ClipMap would
/// return 0x04 here instead of 0x08.
[Fact]
public void ConstructSubsetMask_ClipMapWinsOverTranslucentWithoutAlphaFamily()
{
byte mask = RetailAlphaMeshRouter.ConstructSubsetMask(
hasAlphaFamilyBit: false, hasClipMapBit: true, hasTranslucentBit: true,
hasPositiveStippling: false);
Assert.Equal(RetailAlphaMeshRouter.MaskClipMap, mask);
}
/// Positive stippling ORs bit 0x01 onto whichever base mask
/// applies. Mutation check: forgetting the OR (or using an exclusive
/// select) would leave the base mask value (0x04) instead of 0x05.
[Fact]
public void ConstructSubsetMask_PositiveStipplingOrsIntoTheBaseMask()
{
byte mask = RetailAlphaMeshRouter.ConstructSubsetMask(
hasAlphaFamilyBit: false, hasClipMapBit: false, hasTranslucentBit: true,
hasPositiveStippling: true);
Assert.Equal((byte)(RetailAlphaMeshRouter.MaskTranslucent | RetailAlphaMeshRouter.MaskPositiveStipple), mask);
}
}