diff --git a/src/AcDream.App/Rendering/RetailPViewRenderer.cs b/src/AcDream.App/Rendering/RetailPViewRenderer.cs index 33b01447..d17bddbf 100644 --- a/src/AcDream.App/Rendering/RetailPViewRenderer.cs +++ b/src/AcDream.App/Rendering/RetailPViewRenderer.cs @@ -644,10 +644,6 @@ public sealed class RetailPViewRenderer // closure invokes it via _walkPreClearDynamics. For an // OUTDOOR root there is no clear (retail has none) and the // phase runs after the driver, matching the old order. - RenderFrameView capturedView = frameView; - IRenderFrameEntityPassExecutor? capturedPasses = frameEntityPasses; - InteriorEntityPartition.Result? capturedPartition = partition; - ViewconeCuller capturedViewcone = viewcone; Walk.WalkFrameDriver capturedDriver = walkDriver!; _walkPreClearDynamics = () => { @@ -660,11 +656,7 @@ public sealed class RetailPViewRenderer ctx, passes, clipAssembly, - capturedPartition, - capturedViewcone, - capturedDriver, - capturedPasses, - in capturedView); + capturedDriver); }; try { @@ -676,11 +668,7 @@ public sealed class RetailPViewRenderer ctx, passes, clipAssembly, - partition, - viewcone, - walkDriver!, - frameEntityPasses, - in frameView); + walkDriver!); } } finally @@ -738,14 +726,22 @@ public sealed class RetailPViewRenderer in frameView); } - DrawDynamicsLast( - ctx, - passes, - partition, - viewcone, - ctx.RootCell.IsOutdoorNode, - frameEntityPasses, - in frameView); + // Campaign FW4: the retail walk now owns live objects at their + // exact DrawObjCell turns (outdoor cells, look-ins, and the root + // flood). The packed DynamicLast route is a legacy duplicate and + // must not run on a walk frame. Standalone/fake executors retain + // the old path only until the FW4 shell-deletion slice removes it. + if (!walkActive) + { + DrawDynamicsLast( + ctx, + passes, + partition, + viewcone, + ctx.RootCell.IsOutdoorNode, + frameEntityPasses, + in frameView); + } // OUTDOOR root: the LScape-boundary alpha drain deferred from the // landscape stage runs HERE, after punches, interior shells, cell @@ -1441,21 +1437,15 @@ public sealed class RetailPViewRenderer /// Campaign FW3.2b-2: the DYNAMICS-only remainder of the old /// + - /// split — the walk now draws every STATIC route (see - /// ); this method keeps ONLY what - /// stays on the OLD visibility pipeline per the plan's dual-compute - /// split: outdoor-cell unattached particles, the late per-slice outside- - /// dynamics/weather loop, and the late particle union submission. Look-in - /// dynamics and their cell particles are now walk-turn-owned. + /// split. The walk now draws every mesh and every attached emitter at its + /// own turn. This tail therefore carries only the two landscape services + /// that are not scene projections: ownerless outdoor-cell emitters and + /// weather. It deliberately submits no packed entity route. private void DrawLandscapeDynamicsPhase( RetailPViewFrameInput ctx, IRetailPViewPassExecutor passes, ClipFrameAssembly clipAssembly, - InteriorEntityPartition.Result? partition, - ViewconeCuller viewcone, - Walk.WalkFrameDriver walkDriver, - IRenderFrameEntityPassExecutor? frameEntityPasses, - in RenderFrameView frameView) + Walk.WalkFrameDriver walkDriver) { if (clipAssembly.OutsideViewSlices.Length == 0) return; @@ -1471,73 +1461,19 @@ public sealed class RetailPViewRenderer // per-cell DrawCells turn, before the enclosing building shell. ProbeBuildingLookInFrames(ctx, passes, walkDriver); - // LATE phase (per slice): outside-stage dynamics' meshes + weather — - // unchanged from DrawLandscapeThroughOutsideView's own late loop. - _staticParticleUnionScratch.Clear(); - int probeSliceIndex = 0; + // GameSky's weather pass still runs through each active landscape + // view so doorway scissor/clip state matches the old executor. The + // dynamics collection is intentionally empty: the walk already drew + // those records at OnLandscapeCellTurn. foreach (var slice in clipAssembly.OutsideViewSlices) { passes.SetTerrainClip(slice.Planes); passes.ClearClipRouting(); - - _outdoorStaticScratch.Clear(); - _lateParticleOwnerScratch.Clear(); - foreach (var e in _outsideStageDynamics) - { - EntitySphere(e, out var c, out float r); - if (viewcone.SphereVisibleInOutsideSlice(probeSliceIndex, c, r)) - { - _outdoorStaticScratch.Add(e); - if (!InteriorEntityPartition.IsIndoorCellId(e.ParentCellId)) - _lateParticleOwnerScratch.Add(e.Id); - } - } - if (frameEntityPasses is not null) - { - RenderFrameRouteOwnerSelector.Union( - _lateParticleOwnerScratch, - in frameView, - RenderFrameCandidateRoute.LandscapeOutsideDynamic, - probeSliceIndex, - 0); - } - _candidateObserver?.ObservePViewBucket( - CurrentRenderPViewRoute.LandscapeOutsideDynamic, - probeSliceIndex, - 0, - _outdoorStaticScratch); - RenderFrameEntityDrawRequest? entityDraw = - frameEntityPasses is null - ? null - : new RenderFrameEntityDrawRequest( - frameView, - RenderFrameCandidateRoute.LandscapeOutsideDynamic, - probeSliceIndex, - 0, - ctx.PlayerLandblockId ?? 0); - probeSliceIndex++; - _staticParticleUnionScratch.UnionWith(_lateParticleOwnerScratch); passes.DrawLandscapeSliceLate( ctx, - new RetailPViewLandscapeLateSliceContext(slice, _outdoorStaticScratch) - { - EntityDraw = entityDraw, - }); - } - - // Late-particle union submission — DynamicLast owners excluded, same - // as DrawLandscapeThroughOutsideView's own final submission. - if (frameEntityPasses is not null) - { - RenderFrameRouteOwnerSelector.ExceptRoute( - _staticParticleUnionScratch, in frameView, RenderFrameCandidateRoute.DynamicLast); - } - if (_staticParticleUnionScratch.Count > 0) - { - passes.DrawLandscapeStaticParticles( - ctx, - new RetailPViewLandscapeStaticParticleContext(_staticParticleUnionScratch)); - _staticParticleUnionScratch.Clear(); + new RetailPViewLandscapeLateSliceContext( + slice, + Array.Empty())); } passes.UseIndoorMembershipOnlyRouting();