acdream/src/AcDream.App/Rendering/Wb/EnvCellSceneryInstance.cs
Erik fc68d6d01f feat(render): Phase A8 Wave 1 — WB scaffolding extraction + stencil low-level method
Five tasks shipped together (interdependent at build time):

Task 1: WbRenderPass enum — verbatim port of WB RenderPass.cs:1-22
Task 2: WbFrustum + WbBoundingBox + FrustumTestResult — verbatim port
  of WB Frustum.cs (98 LOC) with namespace + BoundingBox-type adaptations.
  +7 unit tests.
Task 3: EnvCellSceneryInstance + EnvCellLandblock — verbatim port of WB
  SceneryInstance.cs:1-161, renamed scope-narrow. Dropped editor-only
  fields (DisqualificationReason, ParticleEmitters, IsQueuedForUpload,
  InstanceBufferOffset, InstanceCount, MdiCommands, IsTransformOnlyUpdate)
  + InstanceId narrowed uint (we don't use ObjectId's editor methods).
  +5 unit tests.
Task 4: EnvCellVisibilitySnapshot — direct port of WB VisibilitySnapshot
  narrowed to BatchedByCell + VisibleLandblocks only.
Task 7: IndoorCellStencilPipeline.RenderBuildingStencilMask — new
  low-level WB-faithful entry mirroring PortalRenderManager:471-484.
  No surrounding GL state setup (caller's responsibility). Probe fields
  LastStencilVertexCount / LastStencilWasFarPunch / LastStencilBuildingId
  for the [stencil] probe emitter in Task 9.

Build green, 18 tests pass (7 new Frustum + 5 new SceneryInstance + 6
existing stencil pipeline). Ready for Wave 2 (EnvCellRenderer port).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 14:46:07 +02:00

146 lines
5.3 KiB
C#

// Ported from references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/SceneryInstance.cs
// Phase A8 extraction (2026-05-28). Verbatim port; adaptations:
// - SceneryInstance -> EnvCellSceneryInstance (scope-narrow to env-cell rendering)
// - ObjectLandblock -> EnvCellLandblock
// - Namespace: AcDream.App.Rendering.Wb
// - BoundingBox -> WbBoundingBox (defined in WbFrustum.cs)
// - DisqualificationReason + SceneryDisqualificationReason dropped (editor-only)
// - IsQueuedForUpload, IsTransformOnlyUpdate, ParticleEmitters, InstanceBufferOffset,
// InstanceCount, MdiCommands dropped (editor-only / shared-MDI)
using System.Collections.Generic;
using System.Numerics;
namespace AcDream.App.Rendering.Wb;
/// <summary>
/// Lightweight data for a single placed env-cell scenery object.
/// Source: references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/SceneryInstance.cs (lines 11-56)
/// </summary>
public struct EnvCellSceneryInstance
{
/// <summary>GfxObj or Setup ID from DAT.</summary>
public ulong ObjectId;
/// <summary>Unique instance index within the landblock (WB used a 128-bit editor ID; we use uint).</summary>
public uint InstanceId;
/// <summary>True for multi-part Setup objects, false for simple GfxObj.</summary>
public bool IsSetup;
/// <summary>True if this instance is a building.</summary>
public bool IsBuilding;
/// <summary>True if this is an interior cell connected directly to the landblock.</summary>
public bool IsEntryCell;
/// <summary>World-space position.</summary>
public Vector3 WorldPosition;
/// <summary>Local-space position (relative to landblock origin).</summary>
public Vector3 LocalPosition;
/// <summary>Rotation quaternion.</summary>
public Quaternion Rotation;
/// <summary>The current cell ID this instance is in (used for previewing moves between cells).</summary>
public uint CurrentPreviewCellId;
/// <summary>Scale (typically uniform).</summary>
public Vector3 Scale;
/// <summary>Pre-computed world transform matrix.</summary>
public Matrix4x4 Transform;
/// <summary>Local-space bounding box.</summary>
public WbBoundingBox LocalBoundingBox;
/// <summary>World-space bounding box.</summary>
public WbBoundingBox BoundingBox;
/// <summary>Rendering flags for this instance.</summary>
public uint Flags;
}
/// <summary>
/// Holds all EnvCell instances for a single landblock, ready for rendering.
/// Shared by both scenery and static object render managers.
/// Source: references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/SceneryInstance.cs (lines 62-160)
/// </summary>
public class EnvCellLandblock
{
/// <summary>Grid X coordinate of this landblock.</summary>
public int GridX { get; set; }
/// <summary>Grid Y coordinate of this landblock.</summary>
public int GridY { get; set; }
public object Lock { get; } = new();
public List<EnvCellSceneryInstance> Instances { get; set; } = new();
/// <summary>
/// Grouped bounding boxes for each EnvCell in this landblock.
/// Key: CellID, Value: Composite bounding box of the cell and all its static objects.
/// </summary>
public Dictionary<uint, WbBoundingBox> EnvCellBounds { get; set; } = new();
/// <summary>
/// Set of EnvCell IDs in this landblock that have the SeenOutside flag.
/// </summary>
public HashSet<uint> SeenOutsideCells { get; set; } = new();
public List<EnvCellSceneryInstance>? PendingInstances { get; set; }
/// <summary>
/// Grouped bounding boxes for each EnvCell in this landblock (pending upload).
/// </summary>
public Dictionary<uint, WbBoundingBox>? PendingEnvCellBounds { get; set; }
/// <summary>
/// Set of EnvCell IDs in this landblock that have the SeenOutside flag (pending upload).
/// </summary>
public HashSet<uint>? PendingSeenOutsideCells { get; set; }
/// <summary>
/// Grouped transforms for each GfxObj part for static objects, for efficient instanced rendering.
/// Key: GfxObjId, Value: List of transforms
/// </summary>
public Dictionary<ulong, List<InstanceData>> StaticPartGroups { get; set; } = new();
/// <summary>
/// Grouped transforms for each GfxObj part for buildings, for efficient instanced rendering.
/// Key: GfxObjId, Value: List of transforms
/// </summary>
public Dictionary<ulong, List<InstanceData>> BuildingPartGroups { get; set; } = new();
/// <summary>
/// World-space bounding box of this landblock.
/// </summary>
public WbBoundingBox BoundingBox { get; set; }
/// <summary>
/// Total bounding box covering all EnvCells in this landblock.
/// </summary>
public WbBoundingBox TotalEnvCellBounds { get; set; }
/// <summary>
/// Total bounding box covering all EnvCells in this landblock (pending upload).
/// </summary>
public WbBoundingBox PendingTotalEnvCellBounds { get; set; }
/// <summary>
/// Whether instances (positions/bounding boxes) have been generated.
/// </summary>
public bool InstancesReady { get; set; }
/// <summary>
/// Whether mesh data for all instances has been prepared (CPU-side).
/// </summary>
public bool MeshDataReady { get; set; }
/// <summary>
/// Whether GPU resources have been uploaded.
/// </summary>
public bool GpuReady { get; set; }
}