feat(render): Phase A8 — wire stencil pipeline into render frame

Replaces the pre-A8 "terrain-always + depth-clear-when-inside" pattern
with WB's stencil-aware ordering when cameraInsideCell:

  1. Upload portal triangle mesh from VisibleCellIds → LoadedCell.
  2. Draw indoor entities (EntitySet.IndoorOnly) — stencil OFF.
  3. Mark portal stencil + punch far depth (MarkAndPunch).
  4. Draw terrain — stencil-gated to portal silhouettes.
  5. Draw outdoor entities (EntitySet.OutdoorOnly) — stencil-gated.
  6. DisableStencil before particles/weather/UI.

Outdoor path unchanged (EntitySet.All, no stencil work).

Adds CellVisibility.TryGetCell(uint) for the VisibleCellIds → LoadedCell
materialization. Removes the now-redundant DepthBufferBit Clear that
was the old approximation.

Retail oracle: PView::DrawCells at
acclient_2013_pseudo_c.txt:432709 ("outside_view.view_count > 0" gate).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-26 08:29:45 +02:00
parent dcf69a1feb
commit 41c2e67cd8
2 changed files with 114 additions and 24 deletions

View file

@ -344,6 +344,17 @@ public sealed class CellVisibility
local.Z <= cell.LocalBoundsMax.Z + PointInCellEpsilon;
}
/// <summary>
/// Looks up a loaded cell by full 32-bit cell id, or returns null if
/// not loaded. Used by the Phase A8 stencil pipeline to materialize
/// <see cref="VisibilityResult.VisibleCellIds"/> back into
/// <see cref="LoadedCell"/> instances for portal mesh extraction.
/// </summary>
public LoadedCell? TryGetCell(uint cellId)
{
return _cellLookup.TryGetValue(cellId, out var cell) ? cell : null;
}
/// <summary>
/// Brute-force scan of every loaded cell to test whether
/// <paramref name="worldPoint"/> is inside any of them. Does not touch