diff --git a/docs/ISSUES.md b/docs/ISSUES.md index 120229f6..ae486384 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -3701,14 +3701,25 @@ Unverified. The likely culprits, ranked by suspected probability: --- -## #108 — Cellar↔main-floor transition: terrain (grass) sweeps across the upstairs door opening — [DONE 2026-06-11 · T5 gate] +## #108 — Cellar↔main-floor transition: terrain (grass) sweeps across the upstairs door opening — [REOPENED 2026-06-11 · narrowed residual] -**Status:** DONE — user-confirmed GONE at the T5 comprehensive gate -("3. Check" — cellar descent + ascent with the upstairs doorway watched -during the transition). Closed by the holistic-port stack: the T1 retail -frame order + T2 flood fidelity + the BR-7 membership/collision work -(the punch that previously masked-then-exposed it was rebuilt as part of -the discipline). No isolated fix commit — the discipline retired the class. +**Status:** REOPENED (narrowed) — the broad symptom is GONE (T5 + +re-gate #2: "Yes, but…"), but a residual remains in ONE window: during +the cellar ASCENT, while the eye is still below ground level, the +upstairs exit-door opening is covered with grass — "like the ground +level rose to the top of the door … as soon as my head pops up it falls +back to ground level" (user, re-gate 2026-06-11). The original +BR-2-era diagnosis stands: grass-sweep frames render through the +OUTDOOR root (membership/viewer-cell flips outdoor mid-cellar), and the +#117 depth-gated punch then correctly refuses to punch the aperture +where terrain depth is NEARER than the door fan (eye below grade ⇒ the +visible front-facing terrain can sit between the eye and the door in +depth). The punch must STAY depth-gated (DO-NOT-RETRY) — the fix is on +the membership/viewer side (why is the root outdoor while the eye is in +the cellar stairwell below grade?). Apparatus shape: a vertical +cellar-ascent variant of the #118 exit-walk harness (drive the eye up +the stair path; log root resolution + the punch's mark-pass outcome per +step). Prior history below. **Severity:** MEDIUM **Component:** ~~render / indoor PView~~ → **physics / membership** (cellar-transition root flip) @@ -3756,7 +3767,13 @@ stable now; this is a draw-order/depth oscillation localized to the door surface ## #112 — A9B3 hill cottage: containment gap inside the house demotes to outdoor with no re-promotion (transparent interior while walking) -**Status:** OPEN +**Status:** OPEN — user re-confirmed at the 2026-06-11 re-gate: "that +hill house also sometimes when entering, all walls turn transparent. +Only that house." NOTE: the #120 reciprocal ping-pong fired at exactly +A9B3 `0103↔010F` during that session — the runaway duplicate views are a +plausible alternate mechanism for the transparent frames. Re-check this +symptom AFTER the #120 fix (`dede7e4`) before resuming the membership +investigation. **Severity:** MEDIUM-HIGH (any house with interior containment gaps; user-observed "sometimes transparent" while walking around inside) **Filed:** 2026-06-10 (late — user exploration after #111 closed) @@ -3910,7 +3927,7 @@ ad hoc — the DO-NOT-RETRY table's slide entries (physics digest) apply. --- -## #117 — Aperture-shaped see-through: doors/interiors visible through terrain hills and through nearer buildings +## #117 — Aperture-shaped see-through: doors/interiors visible through terrain hills and through nearer buildings — [DONE 2026-06-11 · 478c549, user re-gate "Yes solved"] **Status:** OPEN **Severity:** HIGH (the most visible remaining render artifact post-port) @@ -3935,9 +3952,11 @@ AFTER the punch. Compare against the T1 (`579c8b0`) punch pass wiring. --- -## #118 — Character clipped + disappears for a moment when exiting houses +## #118 — Character clipped + disappears for a moment when exiting houses — [DONE 2026-06-11 · 5a80a2e, user re-gate "Yes solved"] -**Status:** FIXED 2026-06-11 — pending visual re-gate +**Status:** DONE — user-confirmed at the 2026-06-11 re-gate ("Yes +solved"), including the outdoor-NPC-through-doorway companion symptom +("Yes fixed"). **Root cause (pinned by the exit-walk harness, `HouseExitWalkReplayTests`):** NOT the cone stack — candidates 1–3 all exonerated (cone-level walk passes @@ -4046,15 +4065,43 @@ location) — then the cell set + flood can be replayed headlessly like #118. The extraneous water barrel remains a separate static-inclusion question (which cell owns it; is it admitted by a view it shouldn't be). +**User split (re-gate 2026-06-11) — THREE distinct artifacts in the +area:** +1. The PHANTOM walkable-but-invisible stairs (the #113 family) is still + present and now reads as located at the HILL COTTAGE — "the stairs + half embedded into the outside wall." (#113's reopened + drawing-BSP-orphan investigation owns this.) +2. A tower CLOSE TO the hill cottage has the MISSING stairs + the + extraneous water barrel in its middle — this entry (#119) proper. +3. The hill house sometimes turns ALL walls transparent when entering — + tracked under #112; note the #120 ping-pong fired at exactly A9B3 + 0103↔010F, so re-check after the #120 fix (`dede7e4`). + --- ## #120 — [pv-ERROR] in-place propagation tripwire: convergence invariant broken at depth 128 (cottage interior cells) -**Status:** OPEN +**Status:** FIXED 2026-06-11 (`dede7e4`) — pending re-gate (watch for +zero `[pv-ERROR]` lines in the next launch log) **Severity:** HIGH (self-detected invariant break in the new flood growth) **Filed:** 2026-06-11 (T5 launch log; fired during normal cottage play) **Component:** render — PortalVisibilityBuilder in-place growth (T2/BR-4) +**RESOLVED (2026-06-11):** the armed tripwire self-attributed on the +re-gate launch — a pure TWO-CELL reciprocal ping-pong (`0xA9B4015C ↔ +0x0162` and `0xA9B30103 ↔ 0x010F`, 64 laps each). Mechanism: eye within +PortalSideEpsilon (±1 cm) of the portal plane → in-plane counts interior +for BOTH cells → views lap A→B→A; near-edge-on aperture re-clips wobble +beyond the 1e-3 dedup grid → every lap keys "new". The prior sweeps +couldn't reproduce because they only loaded the corner building — both +firing pairs are outside it. `Issue120ReciprocalPingPongTests` loads the +full landblock and reproduces deterministically (tripwire firings + +65-polygon CellView piles). Fix: `CellView.Add` rejects polygons +CONTAINED in an already-stored polygon (a round-trip re-emission is a +subset of its originator in exact math) — union growth is strictly +area-increasing, the lap dies at iteration 1. Corner-flood completeness +pins stay green. PortalSideEpsilon untouched (DO-NOT-RETRY). + **Evidence:** `[pv-ERROR] in-place propagation tripwire at depth 128 on cell=0xA9B40175 / 0xA9B40174 / 0xA9B40162 — convergence invariant broken, investigate` (3+ firings in the T5 session, exactly the cottage interior @@ -4085,6 +4132,99 @@ Revisit on the next firing (the #117/#118 re-gate launch will carry it). --- +## #121 — All world portals invisible (portal swirl VFX gone everywhere) + +**Status:** FIXED 2026-06-11 — pending re-gate +**Severity:** HIGH (user: "all portals that were previously showing at +various places are now gone") +**Filed:** 2026-06-11 (re-gate launch) +**Component:** render — particle pass routing under the pview path + +**Root cause (by read):** dynamics' ATTACHED emitters (portal swirls on +server-spawned portal entities, creature effects) fell through EVERY +particle filter under the unified pview path: the landscape slice's +filter carries outdoor STATICS (+ the #118 outside-stage dynamics), the +per-cell callback carries cell STATICS, and T4 deleted the old +`clipRoot==null` global pass from normal frames. T5 never checked +portals (not on the checklist) — the gap dates to the T3/T4 one-gate +work, surfaced at this re-gate. **Fix:** a dynamics-owner particle pass +— `DrawDynamicsLast` hands its cone-surviving dynamics (minus +outside-stage entities, whose emitters already drew in the landscape +slice) to a new `DrawDynamicsParticles` callback; GameWindow draws +Scene-pass emitters filtered to those owner ids (mirror of +`DrawRetailPViewCellParticles`). Retail shape: emitters draw with their +owner object. + +--- + +## #122 — Windows oscillate between background and the correct outside view when entering houses + +**Status:** OPEN +**Severity:** MEDIUM +**Filed:** 2026-06-11 (re-gate; user: "the oscillating between +background world and the right outside view is now back on some windows +when entering houses") +**Component:** render — window exit-portal region at the root flip + +The #109 oscillation family, now localized to WINDOWS during house +ENTRY (the outdoor→interior root flip). Candidate mechanisms: +(a) the #120 reciprocal ping-pong polluting clip volumes near the +portal plane during the crossing — the firing sites were exactly +cottage cells during entry; RE-CHECK after `dede7e4` before +investigating; (b) the seal/punch handoff on windows across the root +flip (forceFarZ keys on `clipRoot.IsOutdoorNode`, flipping the window +aperture between punch and seal semantics frame-to-frame at the +threshold). + +--- + +## #123 — Buildings transiently disappear when running close past them + +**Status:** OPEN +**Severity:** MEDIUM +**Filed:** 2026-06-11 (re-gate; user: "when I pass by close by +buildings, sometimes the building disappears as I run by") +**Component:** render — outdoor root, close-range building draw + +Whole-building transient vanish at close range under the outdoor root. +Suspects (unverified): the per-building frustum pre-gate on +`Building.PortalBounds` (T2 draw-driven flood gather) interacting with +close-range AABB degeneracy; dispatcher frustum cull with a stale +entity AABB; or the #117 stencil punch marking a near-full-screen +aperture fan at grazing range while the building's own flood is gated +off (far-Z holes → sky/fog where the shell should be). Needs evidence +first: reproduce with `ACDREAM_PROBE_VIS`/`[outdoor-node]` + a capture +of which draw list the building's shell left. + +--- + +## #124 — Looking out through an opening: far buildings with openings show missing/transparent back walls + +**Status:** OPEN +**Severity:** MEDIUM +**Filed:** 2026-06-11 (re-gate; pre-existing — "still have that issue") +**Component:** render — per-building look-in floods under INTERIOR roots + +From inside a building, looking out through a door/window at ANOTHER +building that has an opening: the far building's back walls are +missing/transparent (see the world through it). **Lead (by read):** the +per-building look-in floods (`MergeNearbyBuildingFloods`) run ONLY for +outdoor roots — `RetailPViewDrawContext.NearbyBuildingCells` is +documented "Null for interior roots." So under an interior root the far +building's INTERIOR never floods: through its window you see the shell +only, and a shell has no interior back-wall faces → transparent. +Retail runs the building look-in inside `LScape::draw` (DrawBlock → +DrawPortal → ConstructView(CBldPortal)), which executes for ANY root +whose outside view is non-empty — including interior roots looking out +a doorway. Fix shape: provide the nearby-building gather + per-building +floods for interior roots too, with look-in apertures getting PUNCH +semantics (the `forceFarZ` selector currently keys on +`clipRoot.IsOutdoorNode`, which under-punches this case). Needs its own +focused pass — touches the gather, the merge, and the depth-mask +selector. + +--- + # Recently closed ## #113 — Phantom staircase: REOPENED 2026-06-11, folded into the HOLISTIC BUILDING-RENDER PORT diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 596c40b7..9881d15b 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -7729,6 +7729,8 @@ public sealed class GameWindow : IDisposable forceFarZ: clipRoot.IsOutdoorNode), DrawCellParticles = sliceCtx => DrawRetailPViewCellParticles(sliceCtx, camera, camPos), + DrawDynamicsParticles = survivors => + DrawRetailPViewDynamicsParticles(survivors, camera, camPos), EmitDiagnostics = result => EmitRetailPViewDiagnostics( result, @@ -9630,6 +9632,43 @@ public sealed class GameWindow : IDisposable DisableClipDistances(); } + // #121: the dynamics-owner particle pass — Scene-pass emitters attached to + // the frame's cone-surviving dynamics (portal swirls on server-spawned + // portal entities, creature effects). Retail draws emitters with their + // owner object; before this pass, dynamics' emitters fell through every + // pview particle filter (landscape slice = outdoor statics + #118 + // outside-stage dynamics; cell callback = cell statics) once T4 deleted + // the clipRoot==null global pass from normal frames — every world portal + // went invisible. Mirror of DrawRetailPViewCellParticles with the + // survivors' ids as the filter. + private readonly HashSet _dynamicsSceneParticleEntityIds = new(); + + private void DrawRetailPViewDynamicsParticles( + IReadOnlyList survivors, + ICamera camera, + System.Numerics.Vector3 camPos) + { + if (_particleSystem is null || _particleRenderer is null || survivors.Count == 0) + return; + + _dynamicsSceneParticleEntityIds.Clear(); + foreach (var entity in survivors) + _dynamicsSceneParticleEntityIds.Add(ParticleEntityKey(entity)); + + if (_dynamicsSceneParticleEntityIds.Count == 0) + return; + + DisableClipDistances(); + _particleRenderer.Draw( + _particleSystem, + camera, + camPos, + AcDream.Core.Vfx.ParticleRenderPass.Scene, + emitter => emitter.AttachedObjectId != 0 + && _dynamicsSceneParticleEntityIds.Contains(emitter.AttachedObjectId)); + DisableClipDistances(); + } + private void EmitRetailPViewDiagnostics( AcDream.App.Rendering.RetailPViewFrameResult result, LoadedCell clipRoot, diff --git a/src/AcDream.App/Rendering/RetailPViewRenderer.cs b/src/AcDream.App/Rendering/RetailPViewRenderer.cs index 83d3831f..f42941be 100644 --- a/src/AcDream.App/Rendering/RetailPViewRenderer.cs +++ b/src/AcDream.App/Rendering/RetailPViewRenderer.cs @@ -445,6 +445,26 @@ public sealed class RetailPViewRenderer UseIndoorMembershipOnlyRouting(); DrawEntityBucket(ctx, _dynamicsScratch, visibleCellIds: null); + + // #121: dynamics' attached emitters (portal swirls, creature effects) + // gate through the SAME cone-surviving owner set as their meshes — + // retail draws emitters with the owner object. Before this callback, + // dynamics' emitters fell through EVERY particle filter under the pview + // path (the landscape slice carries outdoor statics + #118 outside- + // stage dynamics; the cell callback carries cell statics; T4 deleted + // the old clipRoot==null global pass from normal frames) — all world + // portals went invisible. Outside-stage dynamics are excluded here: + // their emitters already drew in the landscape slice (alpha-blended + // particles must not double-draw, unlike the depth-idempotent meshes). + if (ctx.DrawDynamicsParticles is not null) + { + _dynamicsParticleScratch.Clear(); + foreach (var e in _dynamicsScratch) + if (!_outsideStageDynamics.Contains(e)) + _dynamicsParticleScratch.Add(e); + if (_dynamicsParticleScratch.Count > 0) + ctx.DrawDynamicsParticles(_dynamicsParticleScratch); + } } private void DrawCellObjectLists( @@ -509,6 +529,9 @@ public sealed class RetailPViewRenderer // #118: dynamics assigned to the OUTSIDE stage this frame (interior roots // only) — outdoor-classified + exit-portal straddlers. Cleared per frame. private readonly List _outsideStageDynamics = new(); + // #121: cone-surviving dynamics whose emitters draw in the dynamics + // particle pass (survivors minus outside-stage). Cleared per use. + private readonly List _dynamicsParticleScratch = new(); /// /// #118 stage assignment for a dynamic under an INTERIOR root: does it draw @@ -650,6 +673,11 @@ public interface IRetailPViewCellDrawContext : IRetailPViewCellDrawCallbacks public FrustumPlanes? Frustum { get; } public uint? PlayerLandblockId { get; } public HashSet? AnimatedEntityIds { get; } + + /// #121: draw the Scene-pass emitters attached to the frame's + /// cone-surviving dynamics (portal swirls, creature effects). Invoked once + /// per frame after the last entity pass with the survivor list. + public Action>? DrawDynamicsParticles { get; } } public sealed class RetailPViewDrawContext : IRetailPViewCellDrawContext @@ -683,6 +711,7 @@ public sealed class RetailPViewDrawContext : IRetailPViewCellDrawContext public Action? ClearDepthForInterior { get; init; } public Action? DrawExitPortalMasks { get; init; } public Action? DrawCellParticles { get; init; } + public Action>? DrawDynamicsParticles { get; init; } public Action? EmitDiagnostics { get; init; } }