feat(render): Phase 3 (Step B) — single render path rooted at the viewer cell (cutover flip)
The CUTOVER FLIP that kills the indoor FLAP. Replace the two-branch gate (clipRoot = playerIndoorGate && viewerRoot ? viewerRoot : null) with clipRoot = viewerRoot ?? _outdoorNode, so EVERY frame routes through the one RetailPViewRenderer.DrawInside path rooted at the viewer cell — interior EnvCell when the eye is indoors, the synthetic outdoor node when outdoors. There is no inside/outside branch to toggle as the 3rd-person eye crosses the doorway, so the flap (textures battling at every transition) dies by construction. Matches retail SmartBox::RenderNormalMode -> DrawInside(viewer_cell) (0x453aa0 -> 0x5a5860). clipRoot is null only pre-spawn/login (viewerCellId==0 -> _outdoorNode null), so the outdoor LScape block still runs as the safety path and login keeps its live sky. playerIndoorGate stays computed for the [render-sig] probe. Preserve the LiveDynamic entity draw (server entities with no resolved ParentCellId — the transient unpositioned case) for the outdoor-node root: the old outdoor branch drew it; DrawInside does not, so re-issue it after DrawInside to keep the spec section 10 byte-identical-outdoor guarantee (no live entity blinks out). The old outdoor else block + DrawPortal/BuildFromExterior are now dead when clipRoot is non-null but are LEFT IN PLACE for the user visual gate (handoff section 4 Step D deletes them only after the user confirms). App 216/0, build green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5379f6ecd3
commit
445e861163
1 changed files with 27 additions and 1 deletions
|
|
@ -7384,7 +7384,16 @@ public sealed class GameWindow : IDisposable
|
||||||
bool playerIndoorGate = AcDream.Core.Rendering.RenderingDiagnostics.ShouldRenderIndoor(
|
bool playerIndoorGate = AcDream.Core.Rendering.RenderingDiagnostics.ShouldRenderIndoor(
|
||||||
playerCellId,
|
playerCellId,
|
||||||
playerRoot is not null);
|
playerRoot is not null);
|
||||||
var clipRoot = playerIndoorGate && viewerRoot is not null ? viewerRoot : null;
|
// Render unification (outdoor-as-cell, 2026-06-07 cutover): ONE render path rooted at the
|
||||||
|
// VIEWER cell. Eye indoors -> its interior EnvCell (viewerRoot); eye outdoors -> the
|
||||||
|
// synthetic outdoor node (_outdoorNode, built above from nearby building entrances). The
|
||||||
|
// result is null ONLY when neither exists (pre-spawn / login / legacy non-chase camera) ->
|
||||||
|
// the outdoor LScape block below still runs as the safety path (and login still shows the
|
||||||
|
// live sky). There is no inside/outside branch to TOGGLE as the chase eye crosses the
|
||||||
|
// doorway boundary, so the indoor FLAP dies by construction. playerIndoorGate stays
|
||||||
|
// computed for the [render-sig] probe but no longer selects the path (handoff
|
||||||
|
// docs/research/2026-06-07-render-unification-cutover-flip-handoff.md section 4 Step B).
|
||||||
|
var clipRoot = viewerRoot ?? _outdoorNode;
|
||||||
string renderBranch = clipRoot is null
|
string renderBranch = clipRoot is null
|
||||||
? "OutdoorRoot"
|
? "OutdoorRoot"
|
||||||
: "RetailPViewInside";
|
: "RetailPViewInside";
|
||||||
|
|
@ -7553,6 +7562,23 @@ public sealed class GameWindow : IDisposable
|
||||||
: sigSceneParticles;
|
: sigSceneParticles;
|
||||||
sigOutdoorSceneryDrawn = pviewResult.Partition.Outdoor.Count > 0
|
sigOutdoorSceneryDrawn = pviewResult.Partition.Outdoor.Count > 0
|
||||||
&& pviewResult.ClipAssembly.OutsideViewSlices.Length > 0;
|
&& pviewResult.ClipAssembly.OutsideViewSlices.Length > 0;
|
||||||
|
|
||||||
|
// Render unification: DrawInside draws the Outdoor bucket (through the landscape
|
||||||
|
// slice) and the per-cell ByCell buckets, but NOT LiveDynamic — server entities with
|
||||||
|
// no resolved ParentCellId (the transient just-spawned / unpositioned case the old
|
||||||
|
// outdoor branch drew at the bottom of its block). Preserve that draw for the
|
||||||
|
// outdoor-node root so no live entity blinks out outdoors (spec section 10 regression
|
||||||
|
// guard). DrawInside's tail clears entity clip routing and disables clip distances, so
|
||||||
|
// visibleCellIds:null draws them unclipped — identical to the old outdoor path.
|
||||||
|
if (ReferenceEquals(clipRoot, _outdoorNode)
|
||||||
|
&& _interiorRenderer is not null
|
||||||
|
&& pviewResult.Partition.LiveDynamic.Count > 0)
|
||||||
|
{
|
||||||
|
_interiorRenderer.DrawEntityBucket(
|
||||||
|
camera, frustum, playerLb, animatedIds,
|
||||||
|
pviewResult.Partition.LiveDynamic, visibleCellIds: null);
|
||||||
|
sigLiveDynamicDrawnCount = pviewResult.Partition.LiveDynamic.Count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue