fix #131: unattached emitters had NO particle pass under interior roots
The user's capture run + a code read pinned it in one step: every particle pass under an interior root is id-filtered (the landscape slice's Scene pass, the per-cell pass, and the dynamics pass all require AttachedObjectId != 0 plus owner-set membership). An UNATTACHED emitter - AttachedObjectId == 0: portal swirls, campfires, ground effects anchored at a position - drew NOWHERE when the viewer root was interior. The outdoor root has the dedicated T3 pass for exactly this class (its own comment records that "unattached ones had NO pass on outdoor-node frames"); the identical hole on interior-root frames was never plugged. Walking out flips to the outdoor root and the T3 pass picks the swirl up - "appears when I walk out again", verbatim. The [outstage] capture corroborated the rest of the chain healthy under the interior root: outside-stage routing correct, cone PASS for the portal-family dynamics, 57 attached emitters matched and drawn through the doorway. Only the unattached class was orphaned. Fix: RetailPViewDrawContext.DrawUnattachedSceneParticles - invoked ONCE per interior-root frame at the END of the landscape stage: - pre-clear, because drawn after the depth clear + seals an outdoor emitter beyond the door plane z-fails against the seal's door-plane stamp; - after the #124 look-in sub-pass, so swirls blend over far-building interiors; - once per frame, not per slice - alpha particles must not double-draw (the #121 lesson); - mutually exclusive with the outdoor T3 pass by root kind (interior invokes this; outdoor keeps T3). Residual (documented in the issue): unattached INDOOR emitters now draw pre-clear and get overpainted by the room's shells - the same invisibility they had before this fix; the proper per-emitter cell classification is a future port. [outstage-pt] probe extended with the unattached emitter count (the probe's blind spot was exactly where the bug hid). Suites: App 259+1skip / Core 1439+2skip / UI 420 / Net 294 green. Awaiting the user gate: the swirl through the doorway. #132 (candle flame vs through-opening background) remains open - different mechanism, background-dependent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
eeb1c59ded
commit
1d3f9a8c97
3 changed files with 70 additions and 24 deletions
|
|
@ -7852,6 +7852,21 @@ public sealed class GameWindow : IDisposable
|
|||
DrawLookInPortalPunch = sliceCtx =>
|
||||
DrawRetailPViewPortalDepthWrite(sliceCtx, envCellViewProj,
|
||||
forceFarZ: true),
|
||||
// #131: unattached emitters under an interior root — the
|
||||
// landscape-stage pass (the outdoor T3 pass below is gated
|
||||
// IsOutdoorNode, so the two never both run).
|
||||
DrawUnattachedSceneParticles = () =>
|
||||
{
|
||||
if (_particleSystem is null || _particleRenderer is null)
|
||||
return;
|
||||
DisableClipDistances();
|
||||
_particleRenderer.Draw(
|
||||
_particleSystem,
|
||||
camera,
|
||||
camPos,
|
||||
AcDream.Core.Vfx.ParticleRenderPass.Scene,
|
||||
emitter => emitter.AttachedObjectId == 0);
|
||||
},
|
||||
DrawCellParticles = sliceCtx =>
|
||||
DrawRetailPViewCellParticles(sliceCtx, camera, camPos),
|
||||
DrawDynamicsParticles = survivors =>
|
||||
|
|
@ -9646,15 +9661,15 @@ public sealed class GameWindow : IDisposable
|
|||
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeOutStageEnabled
|
||||
&& _particleSystem is not null)
|
||||
{
|
||||
int matched = 0, attached = 0;
|
||||
int matched = 0, attached = 0, unattached = 0;
|
||||
foreach (var (emitter, _) in _particleSystem.EnumerateLive())
|
||||
{
|
||||
if (emitter.AttachedObjectId == 0) continue;
|
||||
if (emitter.AttachedObjectId == 0) { unattached++; continue; }
|
||||
attached++;
|
||||
if (_outdoorSceneParticleEntityIds.Contains(emitter.AttachedObjectId)) matched++;
|
||||
}
|
||||
string ptSig = System.FormattableString.Invariant(
|
||||
$"ids={_outdoorSceneParticleEntityIds.Count} attachedEmitters={attached} matched={matched}");
|
||||
$"ids={_outdoorSceneParticleEntityIds.Count} attachedEmitters={attached} matched={matched} unattached={unattached}");
|
||||
if (ptSig != _lastOutStagePtSig)
|
||||
{
|
||||
_lastOutStagePtSig = ptSig;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue