fix(render): Phase W Stage 4 — scissor sky/weather mesh in Scissor mode (adversarial-review fix)
Opus adversarial review caught a real gap: the sky/weather MESH bled full-screen indoors in TerrainClipMode.Scissor (a multi-exit interior, or an OutsideView with >8 edges). The assembler only sets the binding=2 clip-plane UBO in Planes mode; in Scissor mode it leaves count==0, so sky.vert's gl_ClipDistance writes all +1 (no clip) and the mesh draws — which had NO scissor wrapper, only the no-op planes — covered the whole screen. The terrain and particle passes were already scissored; the sky/weather mesh was the one unguarded path. Fix: scissor the WHOLE sky pre-scene + weather post-scene blocks (mesh + particles) to the OutsideView AABB when indoors. In Planes mode the scissor is a harmless over-include (the per-vertex clip planes are tighter and do the exact doorway clip); in Scissor mode it is the sole confinement, mirroring the terrain Scissor path; outdoors it is skipped (full-screen, bit-identical). Also hoisted the scissor-disable out of the particle null-check (cleaner, leak-free on the no-particle path) and corrected a stale 'weather does not write gl_ClipDistance' comment at the world-bracket close. The single-convex-doorway case (Holtburg cottage) was already correct (Planes mode); this seals the multi-opening case. Build 0/0; App tests 171/171. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4bc99fc6fd
commit
b595cfbb9f
1 changed files with 33 additions and 21 deletions
|
|
@ -7391,11 +7391,21 @@ public sealed class GameWindow : IDisposable
|
|||
System.Numerics.Vector4 skyDoorwayNdc = clipAssembly?.OutsideViewNdcAabb ?? default;
|
||||
if (drawSkyThisFrame)
|
||||
{
|
||||
// Sky MESH: enable the 8 clip planes around RenderSky so sky.vert clips it to the
|
||||
// OutsideView (binding=2 UBO: count>0 indoor → confined to the doorway; count==0
|
||||
// outdoor → all distances +1 → full-screen, bit-identical to pre-Stage-4). Re-bind
|
||||
// binding=2 (UBO namespace) defensively — SkyRenderer does not own it, and we must not
|
||||
// inherit whatever was last bound (memory: render-self-contained-gl-state).
|
||||
// Scissor the WHOLE sky pre-scene block (mesh + particles) to the doorway AABB when
|
||||
// indoors. The sky MESH is precisely clipped by sky.vert's gl_ClipDistance in PLANES
|
||||
// mode (the scissor is then a harmless over-include — the planes are tighter); but in
|
||||
// SCISSOR mode the OutsideView exceeded the convex-plane budget so the assembler left
|
||||
// the binding=2 UBO at count 0 (no planes) — there the scissor is the ONLY confinement,
|
||||
// exactly mirroring the terrain Scissor path. Without this, a multi-exit interior would
|
||||
// bleed full-screen sky/rain (sky.vert with count 0 writes all +1 = no clip). The
|
||||
// SkyPreScene particles (particle.vert, no gl_ClipDistance) rely on the scissor in BOTH
|
||||
// modes. Outdoors (skyDoorwayClip=false) → no scissor → full-screen, bit-identical.
|
||||
bool skySc = BeginDoorwayScissor(skyDoorwayClip, skyDoorwayNdc);
|
||||
|
||||
// Sky MESH: re-bind binding=2 (the OutsideView UBO) defensively — SkyRenderer does not
|
||||
// own it and we must not inherit whatever was last bound (memory:
|
||||
// render-self-contained-gl-state) — then enable the 8 clip planes so sky.vert clips
|
||||
// precisely in Planes mode (count>0). count==0 (outdoor / Scissor-mode) → all +1.
|
||||
_gl.BindBufferBase(BufferTargetARB.UniformBuffer,
|
||||
ClipFrame.TerrainClipUboBinding, _clipFrame.TerrainUbo);
|
||||
for (int _cp = 0; _cp < ClipFrame.MaxPlanes; _cp++)
|
||||
|
|
@ -7405,15 +7415,12 @@ public sealed class GameWindow : IDisposable
|
|||
for (int _cp = 0; _cp < ClipFrame.MaxPlanes; _cp++)
|
||||
_gl.Disable(EnableCap.ClipDistance0 + _cp);
|
||||
|
||||
// SkyPreScene particles (particle.vert, no gl_ClipDistance) → scissor to the doorway
|
||||
// bbox indoors. Outdoors (skyDoorwayClip=false) draws full-screen.
|
||||
// SkyPreScene particles (particle.vert, no gl_ClipDistance) — confined by the scissor.
|
||||
if (_particleSystem is not null && _particleRenderer is not null)
|
||||
{
|
||||
bool sc = BeginDoorwayScissor(skyDoorwayClip, skyDoorwayNdc);
|
||||
_particleRenderer.Draw(_particleSystem, camera, camPos,
|
||||
AcDream.Core.Vfx.ParticleRenderPass.SkyPreScene);
|
||||
if (sc) _gl.Disable(EnableCap.ScissorTest);
|
||||
}
|
||||
|
||||
if (skySc) _gl.Disable(EnableCap.ScissorTest);
|
||||
}
|
||||
|
||||
// K-fix1 (2026-04-26): suppress terrain + entity rendering while live mode is configured
|
||||
|
|
@ -7546,10 +7553,11 @@ public sealed class GameWindow : IDisposable
|
|||
if (clipAssembly is not null && envCellShellFilter is not null)
|
||||
_envCellRenderer?.Render(AcDream.App.Rendering.Wb.WbRenderPass.Transparent, envCellShellFilter);
|
||||
|
||||
// Phase U.3: close the world-geometry clip bracket opened above. From
|
||||
// here down (particles, weather, debug lines, UI) the vertex shaders do
|
||||
// NOT write gl_ClipDistance, so the planes must be OFF to avoid the
|
||||
// undefined-behavior clip.
|
||||
// Phase U.3: close the world-geometry clip bracket opened above. From here down the
|
||||
// scene particles, debug lines, and UI use shaders that do NOT write gl_ClipDistance, so
|
||||
// the planes must be OFF to avoid the undefined-behavior clip. (The weather/rain pass
|
||||
// below DOES use sky.vert — it re-enables the planes in its OWN local bracket; the sky
|
||||
// pre-scene pass above already did the same. Both are scissored to the doorway too.)
|
||||
for (int _cp = 0; _cp < ClipFrame.MaxPlanes; _cp++)
|
||||
_gl.Disable(EnableCap.ClipDistance0 + _cp);
|
||||
|
||||
|
|
@ -7574,6 +7582,13 @@ public sealed class GameWindow : IDisposable
|
|||
// outdoors). Suppressed in sealed dungeons / interiors with no exit portal in view.
|
||||
if (drawSkyThisFrame)
|
||||
{
|
||||
// Scissor the WHOLE weather post-scene block (rain mesh + particles) to the doorway
|
||||
// AABB when indoors — symmetric with the sky pre-scene block. The rain cylinder MESH
|
||||
// is precisely clipped by sky.vert in Planes mode (scissor a harmless over-include);
|
||||
// in Scissor mode (UBO count 0, no planes) the scissor is the ONLY confinement — else
|
||||
// the 815m rain cylinder bleeds full-screen indoors. Outdoors → no scissor → unchanged.
|
||||
bool wxSc = BeginDoorwayScissor(skyDoorwayClip, skyDoorwayNdc);
|
||||
|
||||
// Weather MESH (rain cylinder): re-bind binding=2 (the OutsideView UBO) defensively,
|
||||
// enable the 8 clip planes around RenderWeather, disable after. count==0 outdoors ⇒
|
||||
// full-screen rain, unchanged.
|
||||
|
|
@ -7586,15 +7601,12 @@ public sealed class GameWindow : IDisposable
|
|||
for (int _cp = 0; _cp < ClipFrame.MaxPlanes; _cp++)
|
||||
_gl.Disable(EnableCap.ClipDistance0 + _cp);
|
||||
|
||||
// SkyPostScene particles (particle.vert, no gl_ClipDistance) → scissor to the doorway
|
||||
// bbox indoors, full-screen outdoors.
|
||||
// SkyPostScene particles (particle.vert, no gl_ClipDistance) — confined by the scissor.
|
||||
if (_particleSystem is not null && _particleRenderer is not null)
|
||||
{
|
||||
bool sc = BeginDoorwayScissor(skyDoorwayClip, skyDoorwayNdc);
|
||||
_particleRenderer.Draw(_particleSystem, camera, camPos,
|
||||
AcDream.Core.Vfx.ParticleRenderPass.SkyPostScene);
|
||||
if (sc) _gl.Disable(EnableCap.ScissorTest);
|
||||
}
|
||||
|
||||
if (wxSc) _gl.Disable(EnableCap.ScissorTest);
|
||||
}
|
||||
|
||||
// Debug: draw collision shapes as wireframe cylinders around the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue