file #131 (portal swirl gone through doorways) + #132 (candle flame vs aperture background) + the [outstage] capture probe
Two user reports from the #124 gate session, both axioms: - #131: "the portal swirl is missing, when I look out from inside a house. Appears when I walk out again." Mechanism frame: under an interior root an outdoor dynamic's particles draw ONLY via the landscape slice's Scene pass (#118 outside-stage routing; #121 excludes them from the last-pass particle callback) - if any link fails, the swirl draws nowhere exactly when indoors. Desk-exonerated already: filter key conventions uniform, the routing predicate correct, sphere from vertex bounds. - #132: "I have a candle ... when a wall is behind it it shows, but if I turn a bit and the opening through a house is behind it candle light disappears." Background-dependent => per-pixel depth/blend at the aperture region, not owner culling. Possible overlap with the #124 look-in sub-pass (new pre-clear content in those pixels) - the pre-77cef4c check is in the issue. Apparatus (env-gated, zero cost off): ACDREAM_PROBE_OUTSTAGE=1 -> [outstage] per-slice outside-stage routing + cone verdict per dynamic (print-on-change, RetailPViewRenderer) + [outstage-pt] slice Scene-particle id set + live attached-emitter match count (GameWindow). One capture standing inside looking at the portal pins which link breaks. 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
77cef4cd86
commit
eeb1c59ded
4 changed files with 127 additions and 0 deletions
|
|
@ -4581,6 +4581,67 @@ or distance.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## #131 — Portal swirl invisible when viewed from inside a building through the doorway
|
||||||
|
|
||||||
|
**Status:** OPEN
|
||||||
|
**Severity:** MEDIUM (portals are landmark objects; the through-door view is common)
|
||||||
|
**Filed:** 2026-06-12 (user report, #124 gate session)
|
||||||
|
**Component:** render — outside-stage dynamics' particles under interior roots (#118/#121 family)
|
||||||
|
|
||||||
|
**Symptom (user, axiom):** "the portal swirl is missing, when I look out
|
||||||
|
from inside a house. Appears when I walk out again."
|
||||||
|
|
||||||
|
**Mechanism frame:** under an interior root an outdoor dynamic routes to
|
||||||
|
the OUTSIDE stage (`_outsideStageDynamics`, #118) and its particles'
|
||||||
|
ONLY path is the landscape slice's Scene pass
|
||||||
|
(`_outdoorSceneParticleEntityIds`); the last-pass particle callback
|
||||||
|
deliberately excludes outside-stage entities (#121: "already drew in
|
||||||
|
the slice"). If any link fails (slice cone verdict, the id set, emitter
|
||||||
|
matching, draw order vs the slice's blend state), the swirl draws
|
||||||
|
NOWHERE exactly when indoors — and reappears outdoors where
|
||||||
|
DrawDynamicsLast + DrawDynamicsParticles take over. Matches the report
|
||||||
|
exactly.
|
||||||
|
|
||||||
|
**Desk-exonerated (2026-06-12):** key conventions are uniform
|
||||||
|
(`ParticleEntityKey` = ServerGuid-first at all three filter sites);
|
||||||
|
`DynamicDrawsInOutsideStage` routes outdoor dynamics correctly;
|
||||||
|
`EntitySphere` uses the vertex-derived bounds.
|
||||||
|
|
||||||
|
**Apparatus (shipped, env-gated):** `ACDREAM_PROBE_OUTSTAGE=1` —
|
||||||
|
`[outstage]` (per-slice routing + cone verdict per outside-stage
|
||||||
|
dynamic, print-on-change) + `[outstage-pt]` (slice Scene-particle id
|
||||||
|
set + live attached-emitter matched count). Capture: stand inside,
|
||||||
|
look at the portal through the door.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## #132 — Candle flame disappears when the through-opening background is behind it
|
||||||
|
|
||||||
|
**Status:** OPEN
|
||||||
|
**Severity:** LOW-MEDIUM
|
||||||
|
**Filed:** 2026-06-12 (user report, #124 gate session)
|
||||||
|
**Component:** render — cell-particle compositing vs aperture pixels
|
||||||
|
|
||||||
|
**Symptom (user, axiom):** "I have a candle, when I look at the candle
|
||||||
|
when a wall is behind it it shows, but if I turn a bit and the opening
|
||||||
|
through a house is behind it candle light disappears."
|
||||||
|
|
||||||
|
**Reading:** BACKGROUND-dependent disappearance — the candle (and its
|
||||||
|
owner static) stays in view; only what is behind it changes. That rules
|
||||||
|
out viewcone/owner culling (which keys on the candle's own position)
|
||||||
|
and points at per-pixel state in the aperture region: depth left by the
|
||||||
|
punch/seal/look-in machinery at those pixels, draw order of the cell
|
||||||
|
particle pass vs the aperture passes, or blend state. Candidate overlap
|
||||||
|
with the #124 look-in sub-pass (new pre-clear content in exactly those
|
||||||
|
pixels) — check whether the symptom predates `77cef4c` by looking at a
|
||||||
|
candle in front of a doorway WITHOUT a through-house view.
|
||||||
|
|
||||||
|
**Next:** repro at the spot + `ACDREAM_PROBE_OUTSTAGE` lines for the
|
||||||
|
same frame; then a depth-state walkthrough of the aperture pixels for
|
||||||
|
the cell-particle pass.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Recently closed
|
# Recently closed
|
||||||
|
|
||||||
## #113 — Phantom staircase: REOPENED 2026-06-11, folded into the HOLISTIC BUILDING-RENDER PORT
|
## #113 — Phantom staircase: REOPENED 2026-06-11, folded into the HOLISTIC BUILDING-RENDER PORT
|
||||||
|
|
|
||||||
|
|
@ -5094,6 +5094,9 @@ public sealed class GameWindow : IDisposable
|
||||||
private static uint ParticleEntityKey(AcDream.Core.World.WorldEntity entity)
|
private static uint ParticleEntityKey(AcDream.Core.World.WorldEntity entity)
|
||||||
=> entity.ServerGuid != 0 ? entity.ServerGuid : entity.Id;
|
=> entity.ServerGuid != 0 ? entity.ServerGuid : entity.Id;
|
||||||
|
|
||||||
|
// #131 [outstage-pt] probe state (throwaway — strip when #131 closes).
|
||||||
|
private string? _lastOutStagePtSig;
|
||||||
|
|
||||||
private static System.Numerics.Vector3 SkyPesAnchor(
|
private static System.Numerics.Vector3 SkyPesAnchor(
|
||||||
AcDream.Core.World.SkyObjectData obj,
|
AcDream.Core.World.SkyObjectData obj,
|
||||||
System.Numerics.Vector3 cameraWorldPos)
|
System.Numerics.Vector3 cameraWorldPos)
|
||||||
|
|
@ -9638,6 +9641,27 @@ public sealed class GameWindow : IDisposable
|
||||||
foreach (var entity in sliceCtx.OutdoorEntities)
|
foreach (var entity in sliceCtx.OutdoorEntities)
|
||||||
_outdoorSceneParticleEntityIds.Add(ParticleEntityKey(entity));
|
_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.
|
||||||
|
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeOutStageEnabled
|
||||||
|
&& _particleSystem is not null)
|
||||||
|
{
|
||||||
|
int matched = 0, attached = 0;
|
||||||
|
foreach (var (emitter, _) in _particleSystem.EnumerateLive())
|
||||||
|
{
|
||||||
|
if (emitter.AttachedObjectId == 0) continue;
|
||||||
|
attached++;
|
||||||
|
if (_outdoorSceneParticleEntityIds.Contains(emitter.AttachedObjectId)) matched++;
|
||||||
|
}
|
||||||
|
string ptSig = System.FormattableString.Invariant(
|
||||||
|
$"ids={_outdoorSceneParticleEntityIds.Count} attachedEmitters={attached} matched={matched}");
|
||||||
|
if (ptSig != _lastOutStagePtSig)
|
||||||
|
{
|
||||||
|
_lastOutStagePtSig = ptSig;
|
||||||
|
Console.WriteLine("[outstage-pt] " + ptSig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DisableClipDistances();
|
DisableClipDistances();
|
||||||
if (_outdoorSceneParticleEntityIds.Count > 0
|
if (_outdoorSceneParticleEntityIds.Count > 0
|
||||||
&& _particleSystem is not null
|
&& _particleSystem is not null
|
||||||
|
|
|
||||||
|
|
@ -397,6 +397,8 @@ public sealed class RetailPViewRenderer
|
||||||
if (viewcone.SphereVisibleInOutsideSlice(probeSliceIndex, c, r))
|
if (viewcone.SphereVisibleInOutsideSlice(probeSliceIndex, c, r))
|
||||||
_outdoorStaticScratch.Add(e);
|
_outdoorStaticScratch.Add(e);
|
||||||
}
|
}
|
||||||
|
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeOutStageEnabled)
|
||||||
|
EmitOutStageProbe(probeSliceIndex, viewcone);
|
||||||
probeSliceIndex++;
|
probeSliceIndex++;
|
||||||
ctx.DrawLandscapeSlice(new RetailPViewLandscapeSliceContext(slice, _outdoorStaticScratch));
|
ctx.DrawLandscapeSlice(new RetailPViewLandscapeSliceContext(slice, _outdoorStaticScratch));
|
||||||
}
|
}
|
||||||
|
|
@ -420,6 +422,32 @@ public sealed class RetailPViewRenderer
|
||||||
UseIndoorMembershipOnlyRouting();
|
UseIndoorMembershipOnlyRouting();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #131 [outstage] probe state (2026-06-12, throwaway): print-on-change —
|
||||||
|
// which outdoor dynamics were routed to the outside stage and which
|
||||||
|
// survived the slice viewcone. Strip with the probe when #131 closes.
|
||||||
|
private string? _lastOutStageSig;
|
||||||
|
|
||||||
|
private void EmitOutStageProbe(int sliceIndex, ViewconeCuller viewcone)
|
||||||
|
{
|
||||||
|
var sb = new System.Text.StringBuilder(192);
|
||||||
|
sb.Append("slice=").Append(sliceIndex)
|
||||||
|
.Append(" outStage=").Append(_outsideStageDynamics.Count).Append(" [");
|
||||||
|
for (int i = 0; i < _outsideStageDynamics.Count; i++)
|
||||||
|
{
|
||||||
|
var e = _outsideStageDynamics[i];
|
||||||
|
EntitySphere(e, out var c, out float r);
|
||||||
|
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}"));
|
||||||
|
}
|
||||||
|
sb.Append(']');
|
||||||
|
string sig = sb.ToString();
|
||||||
|
if (sig == _lastOutStageSig) return;
|
||||||
|
_lastOutStageSig = sig;
|
||||||
|
Console.WriteLine("[outstage] " + sig);
|
||||||
|
}
|
||||||
|
|
||||||
// §4 flap [clip-route] probe state (2026-06-10, throwaway): print-on-change signature +
|
// §4 flap [clip-route] probe state (2026-06-10, throwaway): print-on-change signature +
|
||||||
// monotonic sequence so held-flap vs healthy frames diff cleanly in one capture.
|
// monotonic sequence so held-flap vs healthy frames diff cleanly in one capture.
|
||||||
private string? _lastClipRouteSig;
|
private string? _lastClipRouteSig;
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,20 @@ public static class RenderingDiagnostics
|
||||||
public static bool ProbeViewerEnabled { get; set; } =
|
public static bool ProbeViewerEnabled { get; set; } =
|
||||||
Environment.GetEnvironmentVariable("ACDREAM_PROBE_VIEWER") == "1";
|
Environment.GetEnvironmentVariable("ACDREAM_PROBE_VIEWER") == "1";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// #131 (2026-06-12) outside-stage dynamics probe. When true, the renderer
|
||||||
|
/// emits one <c>[outstage]</c> line per CHANGE of the outside-stage
|
||||||
|
/// routing + per-slice cone verdict set under an interior root (which
|
||||||
|
/// outdoor dynamics were routed to the landscape slice, which survived the
|
||||||
|
/// slice viewcone), and GameWindow emits one <c>[outstage-pt]</c> line per
|
||||||
|
/// change of the slice Scene-particle id set + matched-emitter count.
|
||||||
|
/// Built for the portal-swirl-missing-through-doorway capture. Light:
|
||||||
|
/// silent while the set is stable. Initial state from
|
||||||
|
/// <c>ACDREAM_PROBE_OUTSTAGE=1</c>.
|
||||||
|
/// </summary>
|
||||||
|
public static bool ProbeOutStageEnabled { get; set; } =
|
||||||
|
Environment.GetEnvironmentVariable("ACDREAM_PROBE_OUTSTAGE") == "1";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Phase U.4c (2026-05-31) flap-convergence probe. When true, the portal
|
/// Phase U.4c (2026-05-31) flap-convergence probe. When true, the portal
|
||||||
/// visibility pass emits, EVERY frame the camera root is an indoor cell, a
|
/// visibility pass emits, EVERY frame the camera root is an indoor cell, a
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue