fix #132 (outdoor sibling): outdoor attached scene emitters move to the post-frame pass; sharpen the #131 probe
User gate on20d1730: the candle is FIXED indoors ("now the candle light is visible when I'm in the house when it is in front of the opening") and the OUTDOOR sibling surfaced exactly as AP-34 recorded ("when I go out it is not showing unless I turn so the angle doesn't put it in front of the opening"): under an OUTDOOR root the merged building interiors draw AFTER the landscape stage (DrawEnvCellShells), so a slice-drawn flame is overpainted by a punched aperture's interior behind it. Fix: outdoor roots SKIP the late-slice Scene-particle draw; attached outdoor-static scene emitters draw in the POST-FRAME pass alongside the T3 unattached pass, where depth is complete and flames composite correctly against interiors. The owner-id set carries over from the late slice (single full-screen slice outdoors); cell-pass and dynamics-pass emitters keep their own passes (their owners are never in the outdoor-static id set - no double-draw). Interior roots keep the late-slice draw (their stage ends with the clear + seal discipline). AP-34 row updated (the outdoor residual is now covered; the remaining residual is translucent MESH batches within stage draw calls). Portal swirl (#131): the user's "same results" on20d1730KILLS the look-in-erasure hypothesis for the portal - the mesh now draws after the look-ins and is still missing indoors. No further speculative fix; the [outstage] probe now prints each outside-stage dynamic's SourceGfxObjOrSetupId (portals have distinctive setups) and [outstage-pt] lists up to 12 distinct UNMATCHED attached emitter owner ids - the next capture identifies whether the portal entity reaches the through-door draw at all, and where its emitters point. Suites: App 259+1skip / Core 1439+2skip / UI 420 / Net 294 green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
20d17304d7
commit
87afbc0a42
4 changed files with 60 additions and 17 deletions
|
|
@ -5096,6 +5096,7 @@ public sealed class GameWindow : IDisposable
|
|||
|
||||
// #131 [outstage-pt] probe state (throwaway — strip when #131 closes).
|
||||
private string? _lastOutStagePtSig;
|
||||
private readonly HashSet<uint> _outStageUnmatchedScratch = new();
|
||||
|
||||
private static System.Numerics.Vector3 SkyPesAnchor(
|
||||
AcDream.Core.World.SkyObjectData obj,
|
||||
|
|
@ -7841,7 +7842,8 @@ public sealed class GameWindow : IDisposable
|
|||
renderSky,
|
||||
renderWeather: playerSeenOutside,
|
||||
kf,
|
||||
environOverrideActive),
|
||||
environOverrideActive,
|
||||
isOutdoorRoot: clipRoot.IsOutdoorNode),
|
||||
// T1: retail's depth discipline (PView::DrawCells, Ghidra 0x005a4840).
|
||||
// INTERIOR roots: one FULL depth clear between the outside stage and
|
||||
// the interior stage, then SEALS re-stamp every outside-leading
|
||||
|
|
@ -8002,20 +8004,26 @@ public sealed class GameWindow : IDisposable
|
|||
&& _particleSystem is not null && _particleRenderer is not null)
|
||||
{
|
||||
// T3 (BR-5): unattached emitters (campfires, ground effects —
|
||||
// AttachedObjectId == 0) under the OUTDOOR root. The unified
|
||||
// path's attached emitters draw via the landscape slice + the
|
||||
// per-cell callbacks; unattached ones had NO pass on
|
||||
// outdoor-node frames (the unattached-particles-dropped-
|
||||
// outdoors divergence, adjusted-confirmed). The outdoor root's
|
||||
// outside view is full-screen (cone pass-all); depth test
|
||||
// composites them against the world.
|
||||
// AttachedObjectId == 0) under the OUTDOOR root. The outdoor
|
||||
// root's outside view is full-screen (cone pass-all); depth
|
||||
// test composites them against the world.
|
||||
// #132 outdoor sibling: ATTACHED outdoor-static scene emitters
|
||||
// (lantern/candle flames) moved here too — drawn in the
|
||||
// landscape slice they were overpainted by merged building
|
||||
// interiors (drawn later) whenever a punched aperture sat
|
||||
// behind them. Post-frame, depth is complete and the flames
|
||||
// composite correctly. The owner-id set is the late slice's
|
||||
// (full-screen cone outdoors). Cell-pass and dynamics-pass
|
||||
// emitters keep their own passes (no double-draw: their owners
|
||||
// are never in the outdoor-static id set).
|
||||
sigSceneParticles = sigSceneParticles == "none" ? "unattached" : sigSceneParticles + "+unattached";
|
||||
_particleRenderer.Draw(
|
||||
_particleSystem,
|
||||
camera,
|
||||
camPos,
|
||||
AcDream.Core.Vfx.ParticleRenderPass.Scene,
|
||||
emitter => emitter.AttachedObjectId == 0);
|
||||
emitter => emitter.AttachedObjectId == 0
|
||||
|| _outdoorSceneParticleEntityIds.Contains(emitter.AttachedObjectId));
|
||||
}
|
||||
|
||||
// Bug A fix (post-#26 worktree, 2026-04-26): weather sky
|
||||
|
|
@ -9697,7 +9705,8 @@ public sealed class GameWindow : IDisposable
|
|||
bool renderSky,
|
||||
bool renderWeather,
|
||||
AcDream.Core.World.SkyKeyframe kf,
|
||||
bool environOverrideActive)
|
||||
bool environOverrideActive,
|
||||
bool isOutdoorRoot)
|
||||
{
|
||||
var slice = lateCtx.Slice;
|
||||
bool scissor = BeginDoorwayScissor(true, slice.NdcAabb);
|
||||
|
|
@ -9724,19 +9733,28 @@ public sealed class GameWindow : IDisposable
|
|||
_outdoorSceneParticleEntityIds.Add(ParticleEntityKey(entity));
|
||||
|
||||
// #131 [outstage-pt] probe: the slice Scene-particle id set + how many
|
||||
// live emitters the filter would actually match. Print-on-change.
|
||||
// live emitters the filter would actually match, plus the distinct
|
||||
// UNMATCHED attached owner ids (the portal-identification handle —
|
||||
// an emitter whose owner never lands in the set draws nowhere
|
||||
// indoors). Print-on-change.
|
||||
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeOutStageEnabled
|
||||
&& _particleSystem is not null)
|
||||
{
|
||||
int matched = 0, attached = 0, unattached = 0;
|
||||
_outStageUnmatchedScratch.Clear();
|
||||
foreach (var (emitter, _) in _particleSystem.EnumerateLive())
|
||||
{
|
||||
if (emitter.AttachedObjectId == 0) { unattached++; continue; }
|
||||
attached++;
|
||||
if (_outdoorSceneParticleEntityIds.Contains(emitter.AttachedObjectId)) matched++;
|
||||
else if (_outStageUnmatchedScratch.Count < 12)
|
||||
_outStageUnmatchedScratch.Add(emitter.AttachedObjectId);
|
||||
}
|
||||
var unm = new System.Text.StringBuilder(96);
|
||||
foreach (uint id in _outStageUnmatchedScratch)
|
||||
unm.Append(System.FormattableString.Invariant($" 0x{id:X8}"));
|
||||
string ptSig = System.FormattableString.Invariant(
|
||||
$"ids={_outdoorSceneParticleEntityIds.Count} attachedEmitters={attached} matched={matched} unattached={unattached}");
|
||||
$"ids={_outdoorSceneParticleEntityIds.Count} attachedEmitters={attached} matched={matched} unattached={unattached} unmatchedIds=[{unm}]");
|
||||
if (ptSig != _lastOutStagePtSig)
|
||||
{
|
||||
_lastOutStagePtSig = ptSig;
|
||||
|
|
@ -9744,7 +9762,17 @@ public sealed class GameWindow : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
if (_outdoorSceneParticleEntityIds.Count > 0
|
||||
// #132 outdoor sibling: under an OUTDOOR root the merged building
|
||||
// interiors draw AFTER this stage (DrawEnvCellShells) — a flame drawn
|
||||
// here is overpainted whenever a punched aperture sits behind it
|
||||
// (user-confirmed at the outdoor candle). Outdoor roots therefore
|
||||
// SKIP the slice Scene pass and draw attached scene particles in the
|
||||
// post-frame pass alongside the T3 unattached pass (the id set built
|
||||
// above carries over — the outdoor root has a single full-screen
|
||||
// slice). Interior roots draw here: the look-ins already ran and the
|
||||
// post-clear seal discipline owns the rest of the frame.
|
||||
if (!isOutdoorRoot
|
||||
&& _outdoorSceneParticleEntityIds.Count > 0
|
||||
&& _particleSystem is not null
|
||||
&& _particleRenderer is not null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -488,7 +488,7 @@ public sealed class RetailPViewRenderer
|
|||
bool pass = viewcone.SphereVisibleInOutsideSlice(sliceIndex, c, r);
|
||||
if (i > 0) sb.Append(' ');
|
||||
sb.Append(System.FormattableString.Invariant(
|
||||
$"0x{(e.ServerGuid != 0 ? e.ServerGuid : e.Id):X8}:{(pass ? "PASS" : "CULL")}:r={r:F1}"));
|
||||
$"0x{(e.ServerGuid != 0 ? e.ServerGuid : e.Id):X8}(s{e.SourceGfxObjOrSetupId:X8}):{(pass ? "PASS" : "CULL")}:r={r:F1}"));
|
||||
}
|
||||
sb.Append(']');
|
||||
string sig = sb.ToString();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue