fix(render): Phase A8 R3.5 v2 — gate depth-clear on cameraReallyInside too
R3.5 v1 only gated the stencil branch on `cameraReallyInside`; the depth-clear-if-inside at line ~7129 stayed on `cameraInsideCell`. During grace frames after exit: cameraInsideCell = true (grace, holds previous cell for 3 frames) cameraReallyInside = false (PointInCell on camera pos returns false) So depth-clear FIRED (writing depth = 1.0 globally) but the OUTDOOR branch ran (single Draw(All) on every entity). With depth cleared, terrain's depth = 1.0 — every entity below terrain (cellar geometry, basement GfxObjs, anything at world Z < terrain Z) won the depth test and rendered THROUGH the ground. User reported: "stand outside or pass outside → flicker where objects are visible through ground and walls of other buildings are missing." v2 fix: unify depth-related gates on `cameraReallyInside`. During grace frames depth-clear is now ALSO skipped; terrain depth survives; the outdoor pass renders normally with proper terrain occlusion. Sky / lighting / particles continue to use `cameraInsideCell` for smooth grace-aware transitions. The two-flag split is now explicit: cameraInsideCell → sky, lighting (smooth, grace-aware) cameraReallyInside → depth-clear, stencil branch (strict, no grace) Closes the persistent transition flicker observed in R4 visual verification after v1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
38d537491f
commit
2bfeafd358
1 changed files with 33 additions and 25 deletions
|
|
@ -7122,17 +7122,42 @@ public sealed class GameWindow : IDisposable
|
||||||
_terrainCpuSampleCursor = (_terrainCpuSampleCursor + 1) % _terrainCpuSamples.Length;
|
_terrainCpuSampleCursor = (_terrainCpuSampleCursor + 1) % _terrainCpuSamples.Length;
|
||||||
MaybeFlushTerrainDiag();
|
MaybeFlushTerrainDiag();
|
||||||
|
|
||||||
// Conditional depth clear: when camera is inside a building, clear
|
// Phase A8 R3.5 — transition-flicker fix. `cameraInsideCell`
|
||||||
// depth (not color) so interior geometry writes fresh Z values on top
|
// stays true for ~3 grace frames after the camera physically
|
||||||
// of the terrain color buffer. Exit portals show outdoor terrain color
|
// exits a cell (see CellVisibility._cellSwitchGraceFrames).
|
||||||
// because we kept the color buffer. Matching ACME GameScene.cs pattern.
|
// The grace mechanism prevents sky/lighting flicker at the
|
||||||
if (cameraInsideCell)
|
// doorway threshold, but the render-frame mechanisms that
|
||||||
|
// touch depth (depth-clear AND the stencil pipeline) MUST be
|
||||||
|
// gated on the stricter PointInCell containment so they don't
|
||||||
|
// fire during grace frames when the camera is actually outside.
|
||||||
|
//
|
||||||
|
// cameraInsideCell — lenient, grace-aware → sky, lighting
|
||||||
|
// cameraReallyInside — PointInCell, no grace → depth-clear,
|
||||||
|
// stencil pipeline branch
|
||||||
|
//
|
||||||
|
// R3.5 v1 only gated the stencil branch on `cameraReallyInside`;
|
||||||
|
// depth-clear stayed on `cameraInsideCell`. Result: during grace
|
||||||
|
// frames the depth-clear ran but the outdoor branch ran (because
|
||||||
|
// !cameraReallyInside), so terrain depth was destroyed AND
|
||||||
|
// everything below terrain (cellars, basement geometry) won the
|
||||||
|
// depth test in the outdoor pass → "objects visible through
|
||||||
|
// ground." R3.5 v2 unifies the depth-related gates on
|
||||||
|
// `cameraReallyInside` so terrain depth is preserved during
|
||||||
|
// grace, eliminating the through-ground artifact.
|
||||||
|
bool cameraReallyInside = visibility?.CameraCell is not null
|
||||||
|
&& CellVisibility.PointInCell(camPos, visibility.CameraCell);
|
||||||
|
|
||||||
|
// Conditional depth clear: when camera is ACTUALLY inside a cell
|
||||||
|
// volume (not just in the grace window), clear depth (not color)
|
||||||
|
// so interior geometry writes fresh Z values on top of the
|
||||||
|
// terrain color buffer. Matches ACME GameScene.cs pattern.
|
||||||
|
if (cameraReallyInside)
|
||||||
_gl!.Clear(ClearBufferMask.DepthBufferBit);
|
_gl!.Clear(ClearBufferMask.DepthBufferBit);
|
||||||
|
|
||||||
// L-fix1 (2026-04-28): animated-entity id set. Required by both
|
// L-fix1 (2026-04-28): animated-entity id set. Required by both
|
||||||
// the cameraInsideCell branch (to route them to LiveDynamic pass)
|
// the cameraReallyInside branch (to route them to LiveDynamic
|
||||||
// and the outdoor path (where it preserves visibility across
|
// pass) and the outdoor path (where it preserves visibility
|
||||||
// landblock frustum culling).
|
// across landblock frustum culling).
|
||||||
HashSet<uint>? animatedIds = null;
|
HashSet<uint>? animatedIds = null;
|
||||||
if (_animatedEntities.Count > 0)
|
if (_animatedEntities.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -7141,23 +7166,6 @@ public sealed class GameWindow : IDisposable
|
||||||
animatedIds.Add(k);
|
animatedIds.Add(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase A8 R3.5 — transition-flicker fix. `cameraInsideCell`
|
|
||||||
// stays true for ~3 grace frames after the camera physically
|
|
||||||
// exits a cell (see CellVisibility._cellSwitchGraceFrames).
|
|
||||||
// The grace mechanism prevents lighting/sky flicker at the
|
|
||||||
// doorway threshold, but if the stencil pipeline runs during
|
|
||||||
// grace frames it marks/punches at the OLD cell's portal
|
|
||||||
// silhouettes — which are now behind/beside the camera — and
|
|
||||||
// the subsequent IndoorPass + stencil-gated outdoor produce
|
|
||||||
// a brief frame of "walls disappear + buildings under ground"
|
|
||||||
// garbage. Gate the stencil branch on the stricter
|
|
||||||
// `PointInCell` containment check so the pipeline only runs
|
|
||||||
// when the camera is ACTUALLY inside its cell volume; sky /
|
|
||||||
// lighting / depth-clear continue to use `cameraInsideCell`
|
|
||||||
// for their smoother grace-aware behavior.
|
|
||||||
bool cameraReallyInside = visibility?.CameraCell is not null
|
|
||||||
&& CellVisibility.PointInCell(camPos, visibility.CameraCell);
|
|
||||||
|
|
||||||
// The `visibility?.CameraCell is not null` repeat is for the
|
// The `visibility?.CameraCell is not null` repeat is for the
|
||||||
// compiler's null-flow analysis: `cameraReallyInside` already
|
// compiler's null-flow analysis: `cameraReallyInside` already
|
||||||
// implies it, but flow doesn't propagate through a separate
|
// implies it, but flow doesn't propagate through a separate
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue