diff --git a/src/AcDream.Core/Meshing/TranslucencyKind.cs b/src/AcDream.Core/Meshing/TranslucencyKind.cs index 9d0ab7b..07aaa29 100644 --- a/src/AcDream.Core/Meshing/TranslucencyKind.cs +++ b/src/AcDream.Core/Meshing/TranslucencyKind.cs @@ -40,17 +40,38 @@ public enum TranslucencyKind public static class TranslucencyKindExtensions { - // Priority order (highest wins): - // 1. Additive — SurfaceType.Additive (0x10000) - // 2. InvAlpha — SurfaceType.InvAlpha (0x200) - // 3. AlphaBlend — SurfaceType.Alpha (0x100) OR SurfaceType.Translucent (0x10) - // 4. ClipMap — SurfaceType.Base1ClipMap (0x04) - // 5. Opaque — everything else + // 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 // - // Note: ACViewer groups Base1ClipMap with the alpha-draw bucket (AlphaSurfaceTypes), - // but acdream keeps its existing alpha-discard approach for clip-map surfaces - // (they render opaque with per-fragment discard) and introduces a separate - // translucent pass only for the genuinely blended surface types. + // 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 @@ -58,6 +79,19 @@ public static class TranslucencyKindExtensions /// 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;