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
|
|
@ -7,6 +7,47 @@ namespace AcDream.Core.Tests.Textures;
|
|||
|
||||
public class SurfaceDecoderTests
|
||||
{
|
||||
[Fact]
|
||||
public void ApplyAuthoredTranslucency_ScalesAlphaOnly_InPlace()
|
||||
{
|
||||
// Runtime analogue of the shared-atlas bake (MeshExtractor's
|
||||
// alphaScale = 1 - Surface.Translucency): the composite decode paths
|
||||
// apply this so an override-carrying item keeps its authored
|
||||
// translucency instead of painting alpha=1 and erasing the particles
|
||||
// composited behind it.
|
||||
var texture = new DecodedTexture(
|
||||
Rgba8: [10, 20, 30, 200, 40, 50, 60, 100],
|
||||
Width: 2,
|
||||
Height: 1);
|
||||
|
||||
var result = SurfaceDecoder.ApplyAuthoredTranslucency(texture, 0.5f);
|
||||
|
||||
Assert.Same(texture, result);
|
||||
Assert.Equal(new byte[] { 10, 20, 30, 100, 40, 50, 60, 50 }, result.Rgba8);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyAuthoredTranslucency_FullTranslucency_ZeroesAlpha()
|
||||
{
|
||||
var texture = new DecodedTexture(Rgba8: [255, 255, 255, 255], Width: 1, Height: 1);
|
||||
|
||||
var result = SurfaceDecoder.ApplyAuthoredTranslucency(texture, 1f);
|
||||
|
||||
Assert.Equal(0, result.Rgba8[3]);
|
||||
Assert.Equal(255, result.Rgba8[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyAuthoredTranslucency_ZeroOrNegative_IsANoOp()
|
||||
{
|
||||
var texture = new DecodedTexture(Rgba8: [1, 2, 3, 4], Width: 1, Height: 1);
|
||||
|
||||
Assert.Same(texture, SurfaceDecoder.ApplyAuthoredTranslucency(texture, 0f));
|
||||
Assert.Equal(4, texture.Rgba8[3]);
|
||||
Assert.Same(texture, SurfaceDecoder.ApplyAuthoredTranslucency(texture, -0.25f));
|
||||
Assert.Equal(4, texture.Rgba8[3]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Decode_A8R8G8B8_ConvertsToRgba8()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue