fix(render): keep authored surface translucency on composite textures
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
The user reported wielded items subtly hiding particle effects, as if a translucent texture were missing. Root cause verified in source: the DAT authors a per-surface Translucency float, and the shared-atlas extraction honors it by baking (1 - Translucency) into the texture alpha (MeshExtractor). But a surface with an appearance override - ObjDesc subpalettes or texture changes, which wielded loot typically carries - routes through the per-instance composite paths instead (WbDrawDispatcher.ResolveTexture -> TextureCache GetOrUploadWithPaletteOverrideBindless / GetOrUploadWithOrigTextureOverrideBindless -> DecodeFromDats), and the textured decode there never saw the authored value: only the Base1Solid branch passed it (SurfaceDecoder.DecodeSolidColor); DecodeRenderSurface has no translucency input at all. Consequence: the part still classified translucent, still sorted in the RetailAlphaQueue, still drew with depth writes off - but with texture alpha = 1 it overwrote everything already composited behind it. Particles behind the part vanished; particles in front survived. The same GfxObj without overrides (atlas path) rendered correctly, which is why the loss was so selective and subtle. Fix: SurfaceDecoder.ApplyAuthoredTranslucency mirrors the atlas bake (in-place alpha scale, caller-owned buffers, Magenta sentinel guarded), and DecodeFromDats applies it behind an opt-in flag set by exactly the two world composite paths. The sky path stays unbaked (its shader applies the authored opacity separately - baking would double-apply, the AP-89 compounding class) and particle sheets stay unbaked (emitter-driven alpha, no authored-translucency consumer). Composite cache keys already include the surface id, so the baked alpha is cache-coherent. This closes an unregistered divergence (no register row existed; the fix restores parity with the shipped atlas mechanism, so none is added). Investigation evidence: equipped children and world objects share the same classification chain (ClassifyPackedBatches/GroupKey), so the gap was override-driven, not attachment-driven - a dropped item with the same ObjDesc was equally affected. Core SurfaceDecoder tests 22/22 (3 new); App Release suite 3,968 / 3 skips. Visual gate: a wielded item with authored-translucent parts must let its particle effects show through. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
bfba0ecf7f
commit
16ed6e7c5c
3 changed files with 94 additions and 4 deletions
|
|
@ -495,7 +495,8 @@ public sealed class TextureCache
|
|||
DecodedTexture decoded = DecodeFromDats(
|
||||
surfaceId,
|
||||
origTextureOverride: overrideOrigTextureId,
|
||||
paletteOverride: null);
|
||||
paletteOverride: null,
|
||||
bakeAuthoredTranslucency: true);
|
||||
return composites.TryAddAndAcquire(ownerLocalId, key, decoded, out BindlessTextureLocation added)
|
||||
? added
|
||||
: default;
|
||||
|
|
@ -534,7 +535,8 @@ public sealed class TextureCache
|
|||
DecodedTexture decoded = DecodeFromDats(
|
||||
surfaceId,
|
||||
origTextureOverride: overrideOrigTextureId,
|
||||
paletteOverride: paletteOverride);
|
||||
paletteOverride: paletteOverride,
|
||||
bakeAuthoredTranslucency: true);
|
||||
return composites.TryAddAndAcquire(ownerLocalId, key, decoded, out BindlessTextureLocation added)
|
||||
? added
|
||||
: default;
|
||||
|
|
@ -806,7 +808,21 @@ public sealed class TextureCache
|
|||
Console.WriteLine($"[N6-DUMP] Surface histogram written to {outPath} ({seen.Count} textures, {totalBytes} bytes)");
|
||||
}
|
||||
|
||||
private DecodedTexture DecodeFromDats(uint surfaceId, uint? origTextureOverride, PaletteOverride? paletteOverride)
|
||||
/// <param name="bakeAuthoredTranslucency">
|
||||
/// Apply the surface's authored <c>Translucency</c> to the decoded alpha, the same
|
||||
/// bake the shared-atlas extraction performs. TRUE for the world composite paths
|
||||
/// (palette / original-texture overrides) — without it an override-carrying item's
|
||||
/// translucent part paints alpha=1: it still sorts as see-through in the alpha
|
||||
/// queue but erases the particles composited behind it. FALSE for the sky (its
|
||||
/// shader applies the authored opacity separately — baking would double-apply)
|
||||
/// and for particle sheets (emitter-driven alpha, no authored-translucency
|
||||
/// consumer today).
|
||||
/// </param>
|
||||
private DecodedTexture DecodeFromDats(
|
||||
uint surfaceId,
|
||||
uint? origTextureOverride,
|
||||
PaletteOverride? paletteOverride,
|
||||
bool bakeAuthoredTranslucency = false)
|
||||
{
|
||||
var surface = _dats.Get<Surface>(surfaceId);
|
||||
if (surface is null)
|
||||
|
|
@ -863,7 +879,20 @@ public sealed class TextureCache
|
|||
bool isClipMap = surface.Type.HasFlag(SurfaceType.Base1ClipMap);
|
||||
bool isAdditive = surface.Type.HasFlag(SurfaceType.Additive);
|
||||
|
||||
return SurfaceDecoder.DecodeRenderSurface(rs, effectivePalette, isClipMap, isAdditive);
|
||||
DecodedTexture decoded =
|
||||
SurfaceDecoder.DecodeRenderSurface(rs, effectivePalette, isClipMap, isAdditive);
|
||||
|
||||
// The decoders return the shared Magenta sentinel on failure; it must never
|
||||
// be scaled in place. Fresh decodes are caller-owned, so the in-place bake
|
||||
// is safe.
|
||||
if (bakeAuthoredTranslucency
|
||||
&& surface.Translucency > 0.0f
|
||||
&& !ReferenceEquals(decoded, DecodedTexture.Magenta))
|
||||
{
|
||||
decoded = SurfaceDecoder.ApplyAuthoredTranslucency(decoded, surface.Translucency);
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue