feat(render) Campaign FW3.1: production walk world data behind the seam
The retail frame walk's world model now materializes from production landblock-build owners through the legal IDatReaderWriter seam, with zero frame wiring (FW3.2 roots the frame): - WalkCellFactory: WalkCell built in the SAME pass as LoadedCell (EnvCellLandblockBuild.BuildVisibilityCell) from the raw portal Flags/polygons/planes/stab lists already parsed there; stored as LoadedCell.Walk, committed atomically with the cell. The fixture-pinned decodes (inverse-0x2 portal side, 0xFFFF->0xFFFFFFFF exit widening) live here. - WalkBuildingFactory + WalkBuildingRegistry: the production WalkBuilding build (drawing BSP with PORT nodes, degrade ladder, portal sides/stab lists, sort center, model frame) from the SAME LandBlockInfo the streaming build already fetches, under the factory's existing DAT lock - closing the gap where BuildingLoader drops every walk field at load. - WalkLandscapeAssembler: the retail 51x51 viewer-centred grid (mid_radius 25) fed incrementally from landblock publish/retire; per-block z-slab (heightTable[max]+200 / [min]-1) computed worker-side in LandblockBuildFactory from the heights already in hand. O(1) SetViewer on same-block frames. - WalkProductionFrameContext: the walk's frame contexts over CellVisibility + WalkBuildingRegistry with a generic inverse-view-projection ray caster (rays feed cross products only - scale-free) and the znear=0.1 CY plane. - Publication: LandblockRenderPublisher owns both walk registries, publishing in the same AdvanceCompleteOne step as BuildingRegistry and retiring in RemoveBuildingRegistry - same commit, same retirement, no new ticket stage. Conformance: ALL TEN oracle fixtures replay identically through the PRODUCTION builders (WalkProductionWorldConformanceTests) - same signatures as the test adapter, first run. Known gap documented for FW3.2: far-tier landblocks carry no EnvCell transaction, so their z-slab never reaches the assembler. Suites: full Release build 0 warnings; Walk lane 186/1 skip; hermetic 6,738/0 (+24); RuntimeDatAccessArchitectureTests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
b95850defe
commit
b10ad662b0
14 changed files with 1711 additions and 2 deletions
75
src/AcDream.App/Rendering/Walk/WalkBuildingRegistry.cs
Normal file
75
src/AcDream.App/Rendering/Walk/WalkBuildingRegistry.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace AcDream.App.Rendering.Walk;
|
||||
|
||||
/// <summary>
|
||||
/// Campaign FW3.1 — the walk's per-landblock <see cref="WalkBuilding"/>
|
||||
/// registry: the production sibling of
|
||||
/// <see cref="AcDream.App.Rendering.Wb.BuildingRegistry"/>. Publish/retire
|
||||
/// mirror that registry's landblock lifecycle exactly — both are committed
|
||||
/// together in <c>LandblockRenderPublisher.AdvanceCompleteOne</c>'s
|
||||
/// <c>BuildingRegistryCommitted</c> step and retired together from
|
||||
/// <c>LandblockRenderPublisher.RemoveBuildingRegistry</c> — because a walk
|
||||
/// building and its BFS-derived <c>Wb.Building</c> counterpart come from the
|
||||
/// SAME <c>BuildingInfo</c> array at the SAME landblock commit.
|
||||
///
|
||||
/// The reverse index (<see cref="TryGetEntry"/>) exists because
|
||||
/// <see cref="WalkProductionFrameContext"/> resolves a building's placement
|
||||
/// by REFERENCE during the portal pass — <c>ViewpointInBuilding</c>,
|
||||
/// <c>ViewerDistanceTo</c>, and <c>ClipBuildingPolygon</c> are called many
|
||||
/// times per building per frame once FW3.2 wires the walk into the render
|
||||
/// loop, so this must be O(1), not a per-landblock scan.
|
||||
/// </summary>
|
||||
public sealed class WalkBuildingRegistry
|
||||
{
|
||||
private readonly Dictionary<uint, IReadOnlyList<WalkBuildingFactory.Entry>> _byLandblock = new();
|
||||
private readonly Dictionary<WalkBuilding, WalkBuildingFactory.Entry> _byBuilding = new();
|
||||
|
||||
/// <summary>Atomically replaces one landblock's complete building set.
|
||||
/// Mirrors <c>Wb.BuildingRegistry</c>'s "no partial landblock" commit
|
||||
/// discipline — <paramref name="entries"/> is the immutable, fully-built
|
||||
/// list carried by the streaming worker's
|
||||
/// <c>Wb.EnvCellLandblockBuild.WalkBuildings</c>.</summary>
|
||||
public void Publish(uint landblockId, IReadOnlyList<WalkBuildingFactory.Entry> entries)
|
||||
{
|
||||
uint key = landblockId & 0xFFFF0000u;
|
||||
if (_byLandblock.TryGetValue(key, out IReadOnlyList<WalkBuildingFactory.Entry>? previous))
|
||||
{
|
||||
foreach (WalkBuildingFactory.Entry entry in previous)
|
||||
_byBuilding.Remove(entry.Building);
|
||||
}
|
||||
_byLandblock[key] = entries;
|
||||
foreach (WalkBuildingFactory.Entry entry in entries)
|
||||
_byBuilding[entry.Building] = entry;
|
||||
}
|
||||
|
||||
/// <summary>Removes every building of one landblock. Safe to call on a
|
||||
/// landblock that never published (no-op).</summary>
|
||||
public void Retire(uint landblockId)
|
||||
{
|
||||
uint key = landblockId & 0xFFFF0000u;
|
||||
if (_byLandblock.Remove(key, out IReadOnlyList<WalkBuildingFactory.Entry>? previous))
|
||||
{
|
||||
foreach (WalkBuildingFactory.Entry entry in previous)
|
||||
_byBuilding.Remove(entry.Building);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The buildings of one landblock, or empty when none are
|
||||
/// published (unloaded, far-tier, or no <c>BuildingInfo</c> entries).</summary>
|
||||
public IReadOnlyList<WalkBuildingFactory.Entry> GetBuildings(uint landblockId) =>
|
||||
_byLandblock.TryGetValue(landblockId & 0xFFFF0000u, out IReadOnlyList<WalkBuildingFactory.Entry>? list)
|
||||
? list
|
||||
: Array.Empty<WalkBuildingFactory.Entry>();
|
||||
|
||||
/// <summary>O(1) reverse lookup: this building's committed placement.
|
||||
/// False means the building is not (or no longer) committed — the walk
|
||||
/// must treat that as a hard desync, never a silent skip (the FW
|
||||
/// fail-loud rule).</summary>
|
||||
public bool TryGetEntry(
|
||||
WalkBuilding building, [MaybeNullWhen(false)] out WalkBuildingFactory.Entry entry) =>
|
||||
_byBuilding.TryGetValue(building, out entry);
|
||||
|
||||
/// <summary>Number of landblocks with committed buildings (diagnostics).</summary>
|
||||
public int LandblockCount => _byLandblock.Count;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue