New per-landblock data model for WB-style per-building cell scoping:
Building — BuildingId, EnvCellIds, ExitPortalPolygons,
occlusion-query state (Step 5 lifecycle)
BuildingRegistry — two-way indexed (by cellId + by buildingId);
single source of truth per landblock
BuildingLoader — static factory from LandBlockInfo.Buildings;
walks interior portals to expand cell sets;
collects exit portal polygons in world space
10 new unit tests cover data invariants + registry indexing + loader
mapping per the algorithm resolved in RR2 findings.
LoadedCell.BuildingId stamping wired in RR4. Render-time consumption
arrives in RR7 (Steps 1-4) + RR9 (Step 5) + RR11 (RenderOutsideIn).
Design: docs/superpowers/specs/2026-05-26-phase-a8-wb-full-port-design.md
Spike: docs/research/2026-05-26-a8-buildings-data-shape.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
57 lines
2.9 KiB
C#
57 lines
2.9 KiB
C#
using System.Collections.Generic;
|
|
using System.Numerics;
|
|
|
|
namespace AcDream.App.Rendering.Wb;
|
|
|
|
/// <summary>
|
|
/// Phase A8 (2026-05-26): a logical building — one or more EnvCells linked
|
|
/// via the dat-level <c>LandBlockInfo.Buildings</c> entry. Building shells (cottage
|
|
/// walls, inn walls — <c>IsBuildingShell=true</c> entities) render unconditionally
|
|
/// when the camera is inside this building's cells. The exit portal polygons
|
|
/// are stencil-marked so outdoor visibility leaks through portal silhouettes
|
|
/// only.
|
|
///
|
|
/// <para>Step 5 (cross-building visibility via 3-stencil-bit pipeline) uses
|
|
/// the occlusion-query state to skip rendering when the building's portals
|
|
/// weren't visible last frame.</para>
|
|
///
|
|
/// <para>WB reference: <c>WorldBuilder.Shared/Services/PortalService.cs</c>
|
|
/// (<c>BuildingPortalGroup</c>) and <c>PortalRenderManager.cs</c> step-5 lifecycle.
|
|
/// Retail reference: <c>docs/research/named-retail/acclient.h:32035</c>
|
|
/// (<c>BuildInfo</c>) + <c>32094</c> (<c>CBldPortal</c>).</para>
|
|
/// </summary>
|
|
public sealed class Building
|
|
{
|
|
/// <summary>Unique within a landblock; allocated sequentially by <see cref="BuildingLoader"/>
|
|
/// starting at 1 (0 is reserved for "no building" semantics on <c>LoadedCell</c>).</summary>
|
|
public required uint BuildingId { get; init; }
|
|
|
|
/// <summary>The EnvCells this building owns. Includes all cells reachable
|
|
/// from the building's entry portals via interior portals (no exit portals).
|
|
/// Populated by <see cref="BuildingLoader.Build"/> via BFS over
|
|
/// <see cref="AcDream.App.Rendering.LoadedCell.Portals"/>.</summary>
|
|
public required HashSet<uint> EnvCellIds { get; init; }
|
|
|
|
/// <summary>Exit portal polygons in world space (each polygon is a triangle
|
|
/// fan from vertex 0). Stencil-marked + far-depth-punched at Steps 1+2 of
|
|
/// WB's RenderInsideOut pipeline (RR7). Collected during
|
|
/// <see cref="BuildingLoader.Build"/> Step C by transforming cell-local portal
|
|
/// polygon vertices via <see cref="AcDream.App.Rendering.LoadedCell.WorldTransform"/>.</summary>
|
|
public required IReadOnlyList<Vector3[]> ExitPortalPolygons { get; init; }
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Step 5 occlusion-query state (mutable, per-frame, RR9 scope).
|
|
// -------------------------------------------------------------------------
|
|
|
|
/// <summary>GL query object handle; lazily created on first use by the
|
|
/// Step 5 occlusion-query pass (RR9). 0 = not yet created.</summary>
|
|
public uint QueryId;
|
|
|
|
/// <summary>True after the first <c>BeginQuery</c> call; controls whether the
|
|
/// read-back path is safe to execute on the next frame.</summary>
|
|
public bool QueryStarted;
|
|
|
|
/// <summary>Previous-frame query result. When false, the building's interior
|
|
/// render is skipped (Step 5 early-out in RR9 + RR11).</summary>
|
|
public bool WasVisible;
|
|
}
|