fix(meshing): Translucent flag overrides Additive blend per retail SetSurface
acdream's TranslucencyKindExtensions.FromSurfaceType picked Additive
first (priority order). Retail's D3DPolyRender::SetSurface at
0x0059c4d0 (decomp 425083+) has a different resolution: when the
Translucent flag (0x10) is set AND either Base1ClipMap (0x04) is set
OR the surface would otherwise be opaque (no Additive/Alpha/InvAlpha),
the blend is *forced* to (SrcAlpha, InvSrcAlpha) — i.e. standard
alpha-blend, not additive. Verbatim from decomp lines 425246-425260:
if ((curr_surface_type & 0x10) != 0) {
if (skipChk != 0 || ebx == 0 || arg3 == 1) {
edi_2 = BLEND_SRCALPHA; // src
ebp = BLEND_INVSRCALPHA; // dst ← alpha-blend
}
curr_alpha = _ftol2(translucency * 255);
}
Where `arg3 == 1` is set after the Base1ClipMap branch and `ebx == 0`
is the opaque-base case in Branch 2.
Concrete impact: Dereth's inner cloud sheet GfxObj 0x01004C35 uses
surface 0x08000023 with Type=0x10114 (B1ClipMap|Translucent|Alpha|
Additive). Retail renders it alpha-blend; acdream was rendering it
additive. Additive on a dark cloud texture only brightens the
background — sun shines through unchanged — which doesn't match
retail's denser cloud appearance.
Rain surface 0x080000C5 (Type=0x10112 = B1Image|Translucent|Alpha|
Additive, NO ClipMap) hits Branch 1 → Additive, ClipMap branch is
skipped, the Translucent override doesn't fire (arg3 stays 0) → stays
Additive. Visual rain rendering is unchanged.
User reported no visible difference at the verification launch; the
remaining cloud-density gap likely lives in the PES particle layer
(issue #28). Keeping this fix because the classification is now
decomp-correct regardless of immediate visual impact — issue #29
documents the residual gap.
1227 tests pass.
This commit is contained in:
parent
034a684f02
commit
375065ba94
1 changed files with 44 additions and 10 deletions
|
|
@ -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.
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="SurfaceType"/> flags value to the correct
|
||||
|
|
@ -58,6 +79,19 @@ public static class TranslucencyKindExtensions
|
|||
/// </summary>
|
||||
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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue