fix(render): drawn-once look-in dynamics + farther-than-building alpha drains
Two retail-parity corrections on top of 684380d4, user-gated together
(no regressions; door/creature-through-opening rendering verified live
at Holtburg; cathedral waterfall and look-north casts pass):
1. Drawn-once look-in dynamics. Retail marks every drawn non-player
part for the frame (DrawMeshInternal @0x0059F360 GetDrawnThisFrame), so
an object draws once, with its cell. acdream drew a look-in cell's
dynamics twice under an outdoor root - once correctly with the look-in,
then again in dynamics-last after the boundary alpha drain, where the
second draw overpainted nearer flames. Both the accepted path
(_lookInCellIds) and the frame product (BuildDynamicLastRoute) now
exclude dynamics whose parent cell drew as a look-in.
2. Pre/inter-building barriers drain only content FARTHER than the
building they precede (FlushLandscapeAlphaFartherThan +
RetailAlphaQueue.FlushFartherThan + conservative anchor-origin
threshold). Retail's far-to-near walk guarantees a building's
FlushAlphaList(0f) @0x0059F2A0 has only farther content queued; a
nearer emitter composites at a later flush. AP-236 documents the
remaining barrier-order divergence.
The #132 candle-before-door overdraw is NOT yet fixed by these steps
and stays open: the current suspect is that houses without a
constructed look-in still draw their interior-parented door in
dynamics-last after the outdoor candle's boundary drain (see the
2026-08-29 ledger for the retail flush-after-objects hypothesis).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
684380d421
commit
fc30285fd7
3 changed files with 84 additions and 13 deletions
|
|
@ -1128,6 +1128,12 @@ internal sealed class RenderScenePViewFrameBuilder
|
|||
private RenderProjectionRecord[] _cell = [];
|
||||
private RenderProjectionRecord[] _dirty = [];
|
||||
private RenderProjectionRecord[] _survivors = [];
|
||||
|
||||
// Cells drawn as building look-ins this frame — the DynamicLast route
|
||||
// honors retail's drawn-once contract (DrawMeshInternal @0x0059F360
|
||||
// marks every non-player part): an object whose cell drew with a look-in
|
||||
// must not enter the final dynamics route again.
|
||||
private readonly HashSet<uint> _lookInCellScratch = new();
|
||||
private RenderProjectionRecord[] _cellRoute = [];
|
||||
private readonly Dictionary<RenderProjectionId, int>
|
||||
_outdoorPositions = [];
|
||||
|
|
@ -1528,6 +1534,16 @@ internal sealed class RenderScenePViewFrameBuilder
|
|||
RenderFrameWriter writer,
|
||||
in RenderScenePViewBuildInput input)
|
||||
{
|
||||
_lookInCellScratch.Clear();
|
||||
for (int frameIndex = 0;
|
||||
frameIndex < input.LookInFrames.Count;
|
||||
frameIndex++)
|
||||
{
|
||||
PortalVisibilityFrame frame = input.LookInFrames[frameIndex];
|
||||
for (int i = 0; i < frame.OrderedVisibleCells.Count; i++)
|
||||
_lookInCellScratch.Add(frame.OrderedVisibleCells[i]);
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
EnsureCapacity(ref _survivors, _dynamicCount);
|
||||
for (int i = 0; i < _dynamicCount; i++)
|
||||
|
|
@ -1539,6 +1555,14 @@ internal sealed class RenderScenePViewFrameBuilder
|
|||
if (!input.RootIsOutdoor && !indoor)
|
||||
continue;
|
||||
|
||||
// Drawn-once (retail DrawMeshInternal @0x0059F360): a dynamic
|
||||
// whose cell drew as a building look-in already rendered with
|
||||
// that cell in the landscape stage; re-entering the final route
|
||||
// would draw it after the boundary alpha drain and overpaint
|
||||
// nearer flames (the Holtburg candle-behind-door class).
|
||||
if (indoor && _lookInCellScratch.Contains(parentCellId!.Value))
|
||||
continue;
|
||||
|
||||
Sphere(in record, out Vector3 center, out float radius);
|
||||
bool visible = indoor
|
||||
? input.Viewcone.SphereVisibleInCell(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue