feat(render): Phase A8.F — IndoorCellStencilPipeline.MarkAndPunchNdc (clipped-region stencil)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
270c21f263
commit
d12892be90
1 changed files with 74 additions and 0 deletions
|
|
@ -264,6 +264,80 @@ public sealed unsafe class IndoorCellStencilPipeline : IDisposable
|
|||
_gl.Disable(EnableCap.StencilTest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Phase A8.F: mark a pre-projected NDC clip region into stencil bit 1 and far-depth-punch it.
|
||||
/// Replaces the flat world-space exit-portal path (PortalMeshBuilder.BuildTriangles) with the
|
||||
/// recursively-clipped region from PortalVisibilityBuilder.OutsideView. Polygons are triangulated
|
||||
/// (fan) and uploaded as NDC verts (z=0) drawn with an identity view-projection (already clip-space).
|
||||
/// GL state on exit matches MarkAndPunch: stencil disabled, color+depth on, depth=Less.
|
||||
/// </summary>
|
||||
public void MarkAndPunchNdc(System.Collections.Generic.IReadOnlyList<ViewPolygon> region)
|
||||
{
|
||||
// Triangulate the region (fan per convex polygon) into NDC Vector3 (z=0).
|
||||
int triVerts = 0;
|
||||
foreach (var p in region) if (!p.IsEmpty) triVerts += (p.Vertices.Length - 2) * 3;
|
||||
if (triVerts == 0) { _lastVertexCount = 0; return; }
|
||||
|
||||
var verts = new Vector3[triVerts];
|
||||
int idx = 0;
|
||||
foreach (var p in region)
|
||||
{
|
||||
if (p.IsEmpty) continue;
|
||||
var v0 = new Vector3(p.Vertices[0], 0f);
|
||||
for (int i = 1; i < p.Vertices.Length - 1; i++)
|
||||
{
|
||||
verts[idx++] = v0;
|
||||
verts[idx++] = new Vector3(p.Vertices[i], 0f);
|
||||
verts[idx++] = new Vector3(p.Vertices[i + 1], 0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (triVerts > _vboCapacityVerts) AllocateVbo(Math.Max(triVerts * 2, 1024));
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, _vbo);
|
||||
fixed (Vector3* p = verts)
|
||||
_gl.BufferSubData(BufferTargetARB.ArrayBuffer, 0, (nuint)(triVerts * sizeof(Vector3)), p);
|
||||
_gl.BindBuffer(BufferTargetARB.ArrayBuffer, 0);
|
||||
_lastVertexCount = triVerts;
|
||||
|
||||
// Same GL state machine as MarkAndPunch, but identity VP (verts are already NDC).
|
||||
var identity = Matrix4x4.Identity;
|
||||
|
||||
_gl.Enable(EnableCap.StencilTest);
|
||||
_gl.Enable(EnableCap.DepthTest);
|
||||
_gl.ClearStencil(0);
|
||||
_gl.Clear(ClearBufferMask.StencilBufferBit);
|
||||
|
||||
// Step 1: mark bit 1.
|
||||
_gl.ColorMask(false, false, false, false);
|
||||
_gl.DepthMask(false);
|
||||
_gl.DepthFunc(DepthFunction.Always);
|
||||
_gl.Disable(EnableCap.CullFace);
|
||||
_gl.StencilFunc(StencilFunction.Always, 1, 0xFFu);
|
||||
_gl.StencilMask(0x01u);
|
||||
_gl.StencilOp(StencilOp.Keep, StencilOp.Keep, StencilOp.Replace);
|
||||
_shader.Use();
|
||||
_gl.UniformMatrix4(_uViewProjectionLoc, 1, false, (float*)&identity);
|
||||
_gl.Uniform1(_uWriteFarDepthLoc, 0);
|
||||
_gl.BindVertexArray(_vao);
|
||||
_gl.DrawArrays(PrimitiveType.Triangles, 0, (uint)triVerts);
|
||||
|
||||
// Step 2: far-depth punch where bit 1 is set.
|
||||
_gl.DepthMask(true);
|
||||
_gl.DepthFunc(DepthFunction.Always);
|
||||
_gl.StencilFunc(StencilFunction.Equal, 1, 0x01u);
|
||||
_gl.StencilMask(0x00u);
|
||||
_gl.StencilOp(StencilOp.Keep, StencilOp.Keep, StencilOp.Keep);
|
||||
_gl.Uniform1(_uWriteFarDepthLoc, 1);
|
||||
_gl.DrawArrays(PrimitiveType.Triangles, 0, (uint)triVerts);
|
||||
_gl.BindVertexArray(0);
|
||||
|
||||
// Clean state for the indoor-entities pass (matches MarkAndPunch exit).
|
||||
_gl.Enable(EnableCap.CullFace);
|
||||
_gl.ColorMask(true, true, true, true);
|
||||
_gl.DepthFunc(DepthFunction.Less);
|
||||
_gl.Disable(EnableCap.StencilTest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Step 4 of WB's RenderInsideOut: enable stencil read-only with
|
||||
/// ref=1, so subsequent terrain + outdoor entity draws are gated
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue