feat(D.2b): Slice 2 — wire paperdoll doll RTT pass + re-dress into GameWindow

Drives the doll 3-D pass in a pre-UI hook (after the world passes, before
_uiHost.Draw), gated on inventory-open AND doll-view (the viewport widget's
Visible, set by the Slots toggle). RefreshPaperdollDoll clones the live
player entity (_entitiesByServerGuid[player]) into a DollEntityBuilder doll
with COPIED MeshRefs (frozen pose); re-dress is triggered by a dirty flag
set in OnLiveAppearanceUpdated (0xF625) — the C# analog of RedressCreature.

Correctness fix: the doll is passed in animatedEntityIds so the dispatcher
BYPASSES the Tier-1 classification cache (WbDrawDispatcher.cs:1142). Without
it, a re-dress (a new WorldEntity with the same fixed DollRenderId) would
serve the previous doll's cached batches and the new gear wouldn't appear.

Renderer disposed in the GameWindow teardown. Build + full App suite green
(594). Static doll (no idle animation yet — next slice). Visual gate next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-23 09:22:45 +02:00
parent 3cdecb536b
commit 8b0365e7a9
3 changed files with 103 additions and 2 deletions

View file

@ -49,6 +49,12 @@ public sealed unsafe class PaperdollViewportRenderer : IUiViewportRenderer, IDis
// neverCullLandblockId the entity is never culled. 0 is unused by live streaming on the doll path.
private const uint DollLandblockId = 0u;
// The doll's render id, passed as "animated" so the dispatcher bypasses the Tier-1 classification
// cache (WbDrawDispatcher.cs:1142) and re-classifies from the current MeshRefs every frame. This is
// what makes a re-dress (a new WorldEntity built with the same DollRenderId) actually take effect,
// AND what lets the idle animation (later slice, TickAnimations rebuilds MeshRefs) show.
private static readonly HashSet<uint> _dollAnimatedIds = new() { DollEntityBuilder.DollRenderId };
public PaperdollViewportRenderer(GL gl, WbDrawDispatcher dispatcher, SceneLightingUboBinding lightUbo)
{
_gl = gl ?? throw new ArgumentNullException(nameof(gl));
@ -105,7 +111,7 @@ public sealed unsafe class PaperdollViewportRenderer : IUiViewportRenderer, IDis
frustum: null,
neverCullLandblockId: DollLandblockId,
visibleCellIds: null,
animatedEntityIds: null);
animatedEntityIds: _dollAnimatedIds); // doll treated as animated ⇒ cache-bypass (re-dress correctness)
return _colorTex;
}