fix #108-residual (root cause): terrain drew DOUBLE-SIDED - port retail landPolysDraw eye-side gate as terrain backface cull
The cellar-ascent grass window was the UNDERSIDE of the z~94 grade sheet. Retail terrain is single-sided: ACRender::landPolysDraw (0x006b7040) draws each land triangle ONLY when the camera is on the POSITIVE (upper) side of its plane (Plane::which_side2 vs Render::FrameCurrent, zFightTerrainAdjust bias) - a below-grade eye gets NO terrain, so retail shows sky through the cellar door. We inherited WB's frame-global cull DISABLE (WB GameScene.cs:841 - an editor camera goes underground by design) and TerrainModernRenderer.Draw set no cull state of its own -> terrain rasterized both sides. From a below-grade eye every aperture sight-ray RISES, so the only 'terrain' it can see is the grade sheet's underside - which painted the exit-door aperture (the landscape slice's 2D NDC clip planes (nx,ny,0,dw) have no depth axis and cannot exclude between-eye-and-portal geometry) and slid off the door exactly as the eye crossed grade. Membership/viewer was exonerated by the harness in the previous commit. Fix: TerrainModernRenderer.Draw owns its cull state (the 7th self-contained-GL-state instance): Enable(CullFace) + CullFace(Back) + FrontFace(Ccw), set -> draw -> restore the frame-global CW + cull-off baseline. GL backface culling evaluates retail's per-triangle eye-side predicate at rasterization; no shader change. Pins: - LandblockMeshTests.Build_AllTriangles_WindCounterClockwiseInWorldXY: every emitted triangle CCW in world XY across both FSplitNESW split directions - the winding invariant culling depends on. - TerrainCullOrientationTests: under the production camera convention (LookAt up=+Z, Numerics perspective) an up-facing triangle winds CCW in window space from above (kept) and CW from below (culled) - guards FrontFace inversion, which would blank terrain from above. Oracle note: retail's through-portal clip has NO portal-face near plane (PView::GetClip / Render::set_view install edge planes only); nearer- than-portal exclusion comes from the eye-side cull + cell-level admission. No register row: this PORTS the retail mechanism, retiring an undocumented WB-heritage deviation. Gate pending: cellar climb (grass window gone) + outdoor sanity glance (terrain intact from above). Suites: App 263+1skip / Core 1443+2skip / UI 420 / Net 294. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
007af1391c
commit
96a425a9a5
4 changed files with 180 additions and 19 deletions
|
|
@ -283,6 +283,27 @@ public sealed unsafe class TerrainModernRenderer : IDisposable
|
|||
// when wired, else the no-clip fallback (count 0 = ungated terrain).
|
||||
BindClipUboBinding2();
|
||||
|
||||
// #108-residual: retail terrain is SINGLE-SIDED — ACRender::landPolysDraw
|
||||
// (0x006b7040) draws each land triangle ONLY when the camera is on the
|
||||
// POSITIVE (upper) side of its plane (Plane::which_side2 vs
|
||||
// Render::FrameCurrent, zFightTerrainAdjust bias). GL backface culling
|
||||
// evaluates the same per-triangle eye-side predicate at rasterization.
|
||||
// LandblockMesh emits every triangle CCW in world XY seen from above
|
||||
// (LandblockMeshTests winding pin), which the unified camera chain
|
||||
// (CreateLookAt up=+Z + Numerics perspective) maps to CCW window
|
||||
// winding from above / CW from below (TerrainCullOrientationTests) —
|
||||
// so FrontFace(Ccw)+Cull(Back) keeps the top side and culls the
|
||||
// underside. WB drew the whole world with culling DISABLED
|
||||
// frame-globally (WB GameScene.cs:841 — an editor camera goes
|
||||
// underground); inheriting that drew terrain DOUBLE-SIDED, and a
|
||||
// below-grade eye (cellar ascent) saw the UNDERSIDE of the grade
|
||||
// sheet through the exit-door aperture — the #108 grass window.
|
||||
// Self-contained state per feedback_render_self_contained_gl_state;
|
||||
// the frame-global CW + cull-off baseline is restored after the draw.
|
||||
_gl.Enable(EnableCap.CullFace);
|
||||
_gl.CullFace(TriangleFace.Back);
|
||||
_gl.FrontFace(FrontFaceDirection.Ccw);
|
||||
|
||||
_gl.BindVertexArray(_globalVao);
|
||||
_gl.MemoryBarrier(MemoryBarrierMask.CommandBarrierBit);
|
||||
_gl.MultiDrawElementsIndirect(
|
||||
|
|
@ -292,6 +313,9 @@ public sealed unsafe class TerrainModernRenderer : IDisposable
|
|||
(uint)sizeof(DrawElementsIndirectCommand));
|
||||
_gl.BindVertexArray(0);
|
||||
_gl.BindBuffer(GLEnum.DrawIndirectBuffer, 0);
|
||||
|
||||
_gl.FrontFace(FrontFaceDirection.CW);
|
||||
_gl.Disable(EnableCap.CullFace);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue