fix #130: doorway-slice scissor cut the aperture's top/right pixel row
The user's "thin strip of background color along the TOP outer edge of a
doorway, looking out from inside" is the landscape-slice scissor box, not
the W=0 clip port.
Mechanism (pinned headlessly, Issue130DoorwayStripTests, 147 eye/gaze
combos at the real Holtburg A9B4 0x0170 exit door):
- BeginDoorwayScissor converted the slice NDC AABB to pixels as
Floor(origin) + Ceiling(size). The far edge floor(min)+ceil(max-min)
lands up to ONE PIXEL SHORT of the true top/right edge at unlucky
fractional alignments (captured: top edge y=0.7938 @1080p -> row 968
cut; right edge column 1296 @1920 cut).
- The scissor brackets the ENTIRE landscape slice (sky, terrain, outdoor
statics, weather). The exit-portal SEAL stamps the full raw aperture at
true depth and the shell wall ends at the aperture edge, so the cut row
never receives any color write -> clear color, flickering with eye
movement as the fractional alignment shifts.
- This violated AD-17's own invariant (over-inclusion is safe,
UNDER-inclusion is the bug class). No register change: the fix restores
the row's documented doctrine.
Lead 1 (987313a W=0 clip port regression) REFUTED by the same harness:
the CPU polygon pipeline (ProjectToClip -> ClipToRegion merges ->
ClipPlaneSet planes) is sub-pixel exact against the raw aperture
projection (worst 0.54 px, 0.00 px aligned). For an all-in-front doorway
polygon the port is bit-identical to the old 1e-4 path by construction.
The EyeInsidePortalOpening rescue stays deleted.
Fix: conservative outer bound floor(min)/ceil(max) extracted to
NdcScissorRect.ToPixels (GL-free; containment property proven in the
header comment); BeginDoorwayScissor delegates.
Pins:
- NdcScissorRectTests: center-inside containment across 251 fractional
alignments x 2 framebuffer sizes + both captured regression cases.
- Issue130DoorwayStripTests: production flood + assembler at the real
exit door; asserts the scissor never cuts a plane-admitted fragment
(worstScissorGap 0.00 px post-fix, was 10.8 px capped) and the CPU
pipeline stays sub-pixel exact (canary 1.2 px).
Suites: App 252+1skip / Core 1439+2skip / UI 420 / Net 294 green.
Awaiting the user visual gate at a cottage doorway.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
0cb97aa594
commit
6c4b6d64d9
5 changed files with 494 additions and 40 deletions
|
|
@ -9954,26 +9954,18 @@ public sealed class GameWindow : IDisposable
|
|||
|
||||
// Phase W Stage 4: set a glScissor to an NDC AABB (the doorway / OutsideView region) in
|
||||
// framebuffer pixels and enable the scissor test; returns true iff applied (the caller then
|
||||
// disables EnableCap.ScissorTest after its draw/clear). Mirrors the terrain Scissor-mode
|
||||
// NDC→pixel conversion (one source for the box math). Used to confine the sky/weather particle
|
||||
// passes (particle.vert has no gl_ClipDistance) and the conditional doorway depth-only Z-clear
|
||||
// to the doorway opening. Returns false (no scissor) when not applied (outdoor / no window).
|
||||
// disables EnableCap.ScissorTest after its draw/clear). Used to bracket the landscape slice
|
||||
// (sky, terrain, statics, weather — particle.vert has no gl_ClipDistance). Returns false
|
||||
// (no scissor) when not applied (outdoor / no window). The box is the CONSERVATIVE outer
|
||||
// bound (NdcScissorRect): the previous Floor(origin)+Ceiling(size) form cut up to one pixel
|
||||
// off the TOP/RIGHT edges at unlucky alignments — the #130 doorway top-edge background strip.
|
||||
private bool BeginDoorwayScissor(bool apply, System.Numerics.Vector4 ndcAabb)
|
||||
{
|
||||
if (!apply || _window is null) return false;
|
||||
var fb = _window.FramebufferSize;
|
||||
// NDC [-1,1] → window pixels. Clamp so a doorway opening that extends past a screen edge
|
||||
// still yields a valid box (same clamp the terrain Scissor path uses).
|
||||
float nx0 = System.Math.Clamp(ndcAabb.X, -1f, 1f);
|
||||
float ny0 = System.Math.Clamp(ndcAabb.Y, -1f, 1f);
|
||||
float nx1 = System.Math.Clamp(ndcAabb.Z, -1f, 1f);
|
||||
float ny1 = System.Math.Clamp(ndcAabb.W, -1f, 1f);
|
||||
int px = (int)System.MathF.Floor((nx0 * 0.5f + 0.5f) * fb.X);
|
||||
int py = (int)System.MathF.Floor((ny0 * 0.5f + 0.5f) * fb.Y);
|
||||
int pw = (int)System.MathF.Ceiling((nx1 - nx0) * 0.5f * fb.X);
|
||||
int ph = (int)System.MathF.Ceiling((ny1 - ny0) * 0.5f * fb.Y);
|
||||
var box = NdcScissorRect.ToPixels(ndcAabb, fb.X, fb.Y);
|
||||
_gl!.Enable(EnableCap.ScissorTest);
|
||||
_gl.Scissor(px, py, (uint)System.Math.Max(1, pw), (uint)System.Math.Max(1, ph));
|
||||
_gl.Scissor(box.X, box.Y, (uint)box.Width, (uint)box.Height);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue