feat(#188): fading-wall + sliding-door translucency; hold open past animation settle
Lands the fading-secret-door feature and fixes the door "flip-back" that surfaced while testing it. #188 — fading-wall doors (e.g. "Pedestal Weak Spot") fade their wall part out via TransparentPartHook instead of swinging: - TranslucencyHookSink consumes TransparentPartHook -> TranslucencyFadeManager (per-(entity,part) linear translucency ramp; holds at End frame). - WbDrawDispatcher: new per-instance alpha SSBO (binding 7); ClassifyBatches takes opacityMultiplier (1 - translucency, per CMaterial::SetTranslucencySimple 0x005396f0) forcing AlphaBlend; fully-invisible parts skipped. - mesh_modern.vert/.frag: binding-7 InstanceAlphaBuf -> vOpacityMultiplier -> FragColor.a *= vOpacityMultiplier. - Register AP-89: the fade multiplies sampled texture alpha, not a separate D3D9 material alpha channel (observably identical for texture-alpha==1 surfaces). Door flip-back fix (affected BOTH #188 fading walls AND #187 sliding doors): a door/wall that finished opening holds a single unchanging frame, so the uncommitted IsEntityCurrentlyMoving cache-bypass narrowing dropped it onto the Tier-1 static cache -- which only remembers the REST pose + opacity 1.0 -- snapping it visually shut/opaque while physics stayed open. Reverted that narrowing: every Sequencer entity stays on the per-frame path (live pose + live fade opacity), the known-good pre-optimization behavior. The per-frame CPU cost that narrowing chased was a Debug-build artifact -- Release is GPU-bound (~200 fps in Sawato, measured), so the unconditional add is free where it matters. Left a code comment barring re-introduction. Tests: full Core suite green (2649 passed, 2 skipped). Live visual gate PASSED -- both fading-wall and sliding doors hold open. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
e2e285b855
commit
3284dd0aed
12 changed files with 625 additions and 14 deletions
|
|
@ -0,0 +1,70 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.Rendering;
|
||||
using DatReaderWriter.Types;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.Core.Tests.Rendering;
|
||||
|
||||
public sealed class TranslucencyHookSinkTests
|
||||
{
|
||||
[Fact]
|
||||
public void TransparentPartHook_ForwardsFieldsVerbatimToManager()
|
||||
{
|
||||
var mgr = new TranslucencyFadeManager();
|
||||
var sink = new TranslucencyHookSink(mgr);
|
||||
|
||||
var hook = new TransparentPartHook { PartIndex = 3, Start = 0.1f, End = 0.9f, Time = 0.5f };
|
||||
sink.OnHook(entityId: 42, entityWorldPosition: Vector3.Zero, hook: hook);
|
||||
|
||||
Assert.True(mgr.TryGetCurrentValue(42, 3, out float value));
|
||||
Assert.Equal(0.1f, value); // t=0 value, readable immediately after the hook fires
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TransparentPartHook_InstantTime_CommitsEndImmediately()
|
||||
{
|
||||
var mgr = new TranslucencyFadeManager();
|
||||
var sink = new TranslucencyHookSink(mgr);
|
||||
|
||||
var hook = new TransparentPartHook { PartIndex = 0, Start = 0f, End = 1f, Time = 0f };
|
||||
sink.OnHook(entityId: 1, entityWorldPosition: Vector3.Zero, hook: hook);
|
||||
|
||||
Assert.True(mgr.TryGetCurrentValue(1, 0, out float value));
|
||||
Assert.Equal(1f, value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EtherealHook_IsIgnored()
|
||||
{
|
||||
var mgr = new TranslucencyFadeManager();
|
||||
var sink = new TranslucencyHookSink(mgr);
|
||||
|
||||
sink.OnHook(entityId: 1, entityWorldPosition: Vector3.Zero, hook: new EtherealHook { Ethereal = true });
|
||||
|
||||
Assert.False(mgr.TryGetCurrentValue(1, 0, out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WholeObjectTransparentHook_IsIgnored()
|
||||
{
|
||||
var mgr = new TranslucencyFadeManager();
|
||||
var sink = new TranslucencyHookSink(mgr);
|
||||
|
||||
// #188 scope: the whole-object variant is deliberately NOT ported this pass.
|
||||
sink.OnHook(entityId: 1, entityWorldPosition: Vector3.Zero,
|
||||
hook: new TransparentHook { Start = 0f, End = 1f, Time = 1f });
|
||||
|
||||
Assert.False(mgr.TryGetCurrentValue(1, 0, out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnrelatedHook_IsIgnored()
|
||||
{
|
||||
var mgr = new TranslucencyFadeManager();
|
||||
var sink = new TranslucencyHookSink(mgr);
|
||||
|
||||
sink.OnHook(entityId: 1, entityWorldPosition: Vector3.Zero, hook: new SoundTableHook());
|
||||
|
||||
Assert.False(mgr.TryGetCurrentValue(1, 0, out _));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue