#121: dynamics-owner particle pass - world portals visible again; re-gate ledger in ISSUES
Fix: 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 filter carries outdoor statics
(+ the #118 outside-stage dynamics), the per-cell callback carries cell
statics, and T4 deleted the clipRoot==null global pass from normal
frames. T5 never checked portals; the user's re-gate caught it ("all
portals that were previously showing are now gone"). DrawDynamicsLast
now hands its cone-surviving dynamics (minus outside-stage entities,
whose emitters already drew in the landscape slice - alpha particles
must not double-draw) to a new DrawDynamicsParticles callback;
GameWindow draws Scene-pass emitters filtered to those owner ids,
mirroring DrawRetailPViewCellParticles. Retail shape: emitters draw
with their owner object.
Re-gate ledger (user verdicts are axioms):
- #117 CLOSED ("Yes solved"), #118 CLOSED ("Yes solved" + NPC-through-
door "Yes fixed").
- #108 REOPENED narrowed: cellar-ascent eye-below-grade window only
(grass covers the exit door until the head pops over ground level);
fix belongs on the membership/viewer side - the depth-gated punch
stays (DO-NOT-RETRY).
- #119 user split: phantom walkable stairs at the hill cottage (#113
family), tower missing stairs + barrel (#119 proper), hill-house
transparent-on-entry (#112 - re-check after the #120 fix; the
ping-pong fired at exactly A9B3 0103/010F).
- #120 FIXED pending re-gate (dede7e4).
- NEW #122 window oscillation on entry (re-check after #120 first),
NEW #123 buildings transiently disappear running close past,
NEW #124 far-building back walls missing through openings (lead:
per-building look-in floods run only for outdoor roots -
NearbyBuildingCells is null for interior roots; retail runs the
look-in inside LScape::draw for ANY root).
Suites: App 236, Core 1419+2skip, UI 420, Net 294.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
dede7e491c
commit
c4464739d2
3 changed files with 220 additions and 12 deletions
|
|
@ -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<uint> _dynamicsSceneParticleEntityIds = new();
|
||||
|
||||
private void DrawRetailPViewDynamicsParticles(
|
||||
IReadOnlyList<AcDream.Core.World.WorldEntity> 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,
|
||||
|
|
|
|||
|
|
@ -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<WorldEntity> _outsideStageDynamics = new();
|
||||
// #121: cone-surviving dynamics whose emitters draw in the dynamics
|
||||
// particle pass (survivors minus outside-stage). Cleared per use.
|
||||
private readonly List<WorldEntity> _dynamicsParticleScratch = new();
|
||||
|
||||
/// <summary>
|
||||
/// #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<uint>? AnimatedEntityIds { get; }
|
||||
|
||||
/// <summary>#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.</summary>
|
||||
public Action<IReadOnlyList<WorldEntity>>? DrawDynamicsParticles { get; }
|
||||
}
|
||||
|
||||
public sealed class RetailPViewDrawContext : IRetailPViewCellDrawContext
|
||||
|
|
@ -683,6 +711,7 @@ public sealed class RetailPViewDrawContext : IRetailPViewCellDrawContext
|
|||
public Action? ClearDepthForInterior { get; init; }
|
||||
public Action<RetailPViewCellSliceContext>? DrawExitPortalMasks { get; init; }
|
||||
public Action<RetailPViewCellSliceContext>? DrawCellParticles { get; init; }
|
||||
public Action<IReadOnlyList<WorldEntity>>? DrawDynamicsParticles { get; init; }
|
||||
public Action<RetailPViewFrameResult>? EmitDiagnostics { get; init; }
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue