using DatReaderWriter.Enums;
namespace AcDream.Core.Meshing;
///
/// Categorizes how a sub-mesh should be composited into the frame. Determined
/// from the Surface.Type flags on the AC dat surface that owns the sub-mesh.
///
public enum TranslucencyKind
{
/// Standard opaque. Depth write + test, no blend.
Opaque = 0,
///
/// Alpha-keyed (clip-map). Treated as opaque for sorting; the fragment
/// shader discards low-alpha fragments. Matches the current rendering of
/// doors, windows, and vegetation.
///
ClipMap = 1,
///
/// Standard alpha blend: src*a + dst*(1-a).
/// Depth-write off, depth-test on. Used for semi-transparent glass,
/// water decals, and flame alpha surfaces.
///
AlphaBlend = 2,
///
/// Additive blend: src*a + dst. Depth-write off, depth-test on.
/// Used for portal swirls, magical glows, and particle effects.
///
Additive = 3,
///
/// Inverted alpha blend: src*(1-a) + dst*a. Rare but present in
/// the AC dat files.
///
InvAlpha = 4,
}
public static class TranslucencyKindExtensions
{
// Translucent override comes FIRST, then the existing priority chain:
// 1. Translucent override — Translucent (0x10) AND (ClipMap OR opaque-base)
// → AlphaBlend (matches retail's blend forcing).
// 2. Additive — SurfaceType.Additive (0x10000)
// 3. InvAlpha — SurfaceType.InvAlpha (0x200)
// 4. AlphaBlend — SurfaceType.Alpha (0x100) OR SurfaceType.Translucent (0x10)
// 5. ClipMap — SurfaceType.Base1ClipMap (0x04)
// 6. Opaque — everything else
//
// The Translucent override matches retail's D3DPolyRender::SetSurface
// at 0x0059c4d0 (decomp lines 425083-425260). Verbatim from the
// Translucent branch at 425246:
//
// if ((curr_surface_type & 0x10) != 0) {
// if (skipChk != 0 || ebx == 0 || arg3 == 1) {
// edi_2 = BLEND_SRCALPHA; // src
// ebp = BLEND_INVSRCALPHA; // dst ← alpha-blend
// ebx = 1; arg1 = 1; arg3 = 0;
// }
// curr_alpha = _ftol2(translucency * 255);
// }
//
// Where `arg3 = 1` after the ClipMap branch and `ebx == 0` happens
// in Branch 2 when the surface would otherwise be opaque (no Additive,
// Alpha, or InvAlpha bits). So Translucent + ClipMap (e.g. cloud
// surface 0x08000023, Type=0x10114) renders ALPHA-BLEND in retail
// even though the Additive flag is also set; previously acdream's
// priority-Additive-first classification mis-routed it as additive.
// Empirically: this is the surface for cloud GfxObj 0x01004C35 in
// every Cloudy/Rainy DayGroup. Misclassifying it as additive made
// acdream's clouds barely-visible "brightness adders" rather than
// the dense alpha-blended sheets retail shows.
///
/// Maps a flags value to the correct
/// for the two-pass render split.
///
public static TranslucencyKind FromSurfaceType(SurfaceType type)
{
// Step 1: Translucent override — matches retail's branch at
// decomp line 425250 where (skipChk || ebx == 0 || arg3 == 1)
// forces (SrcAlpha, InvSrcAlpha) regardless of Additive.
bool isTranslucent = (type & SurfaceType.Translucent) != 0;
bool isClipMap = (type & SurfaceType.Base1ClipMap) != 0;
bool wouldBeOpaque =
(type & (SurfaceType.Additive
| SurfaceType.Alpha
| SurfaceType.InvAlpha)) == 0;
if (isTranslucent && (isClipMap || wouldBeOpaque))
return TranslucencyKind.AlphaBlend;
// Step 2..6: existing priority order for non-overridden surfaces.
if ((type & SurfaceType.Additive) != 0)
return TranslucencyKind.Additive;
if ((type & SurfaceType.InvAlpha) != 0)
return TranslucencyKind.InvAlpha;
if ((type & (SurfaceType.Alpha | SurfaceType.Translucent)) != 0)
return TranslucencyKind.AlphaBlend;
if ((type & SurfaceType.Base1ClipMap) != 0)
return TranslucencyKind.ClipMap;
return TranslucencyKind.Opaque;
}
///
/// Retail translucency is transparency: 0 = opaque, 1 = invisible.
/// CMaterial::SetTranslucencySimple at 0x005396f0 writes material alpha
/// as 1 - translucency.
///
public static float OpacityFromSurfaceTranslucency(SurfaceType type, float translucency)
{
if ((type & SurfaceType.Translucent) == 0)
return 1f;
return Math.Clamp(1f - translucency, 0f, 1f);
}
///
/// D3DPolyRender::SetSurface at 0x0059c882 disables fixed-function fog
/// alpha whenever the raw Additive surface bit is present, even when the
/// Translucent+ClipMap branch later forces alpha blending.
///
public static bool DisablesFixedFunctionFog(SurfaceType type)
=> (type & SurfaceType.Additive) != 0;
}