feat(render) Campaign FW1: the landscape replay world + first outdoor diff

WalkLandscapeDatBuilder assembles the replay grid with the LIVE-dumped
retail landscape model (LScape mid_radius=25 -> 51x51; the resolution
pyramid observed on the capture client: side_cell_count 8 in the 3x3
core, 4 at ring 2, 2 at rings 3-4, 1 beyond; buildings attach only to
full-res blocks - matching the traces roster), retail z-slabs
(heightTable[max]+200 / [min]-1 per CLandBlock unpack @0052f297), and
camera-block-local coordinates. The replay context gains the building
half (placements, active-view install, building-polygon clip). The
street-outdoor conformance diff now reaches real adjudication: the walk
over-culls six traced buildings (jagged boundary - not a clean frustum
edge) and the look-in floods differ; the driving test is parked Skip
while the visibility map is instrumented.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-30 10:36:09 +02:00
parent e8df809c40
commit e3f0c6a1a1
5 changed files with 222 additions and 21 deletions

View file

@ -32,7 +32,14 @@ using AcDream.App.Rendering.Walk;
/// </summary>
public static class WalkWorldDatAdapter
{
/// <summary>One built building plus its landblock-local placement.</summary>
public sealed record BuildingEntry(
WalkBuilding Building, Matrix4x4 WorldTransform, Matrix4x4 InverseWorldTransform);
public static WalkCell? BuildCell(DatCollection dats, uint cellId)
=> BuildCell(dats, cellId, Vector3.Zero);
public static WalkCell? BuildCell(DatCollection dats, uint cellId, Vector3 blockOffset)
{
if (dats.Get<EnvCell>(cellId) is not EnvCell envCell)
return null;
@ -67,9 +74,9 @@ public static class WalkWorldDatAdapter
Matrix4x4 worldTransform =
Matrix4x4.CreateFromQuaternion(envCell.Position.Orientation)
* Matrix4x4.CreateTranslation(
envCell.Position.Origin.X,
envCell.Position.Origin.Y,
envCell.Position.Origin.Z);
envCell.Position.Origin.X + blockOffset.X,
envCell.Position.Origin.Y + blockOffset.Y,
envCell.Position.Origin.Z + blockOffset.Z);
Matrix4x4.Invert(worldTransform, out Matrix4x4 inverse);
return new WalkCell
{
@ -84,12 +91,17 @@ public static class WalkWorldDatAdapter
public static Dictionary<uint, WalkCell> BuildInteriorCells(
DatCollection dats, uint landblockId)
=> BuildInteriorCells(dats, landblockId, Vector3.Zero, null);
public static Dictionary<uint, WalkCell> BuildInteriorCells(
DatCollection dats, uint landblockId, Vector3 blockOffset,
Dictionary<uint, WalkCell>? into)
{
uint lbMask = landblockId & 0xFFFF0000u;
var cells = new Dictionary<uint, WalkCell>();
Dictionary<uint, WalkCell> cells = into ?? new Dictionary<uint, WalkCell>();
for (uint low = 0x0100; low <= 0xFFFD; low++)
{
WalkCell? cell = BuildCell(dats, lbMask | low);
WalkCell? cell = BuildCell(dats, lbMask | low, blockOffset);
if (cell is null) break; // interior cells are contiguous from 0x0100
cells[cell.CellId] = cell;
}
@ -97,11 +109,12 @@ public static class WalkWorldDatAdapter
}
/// <summary>Buildings of one landblock, with their walk event ids
/// (containing landcell), portal records, and drawing-BSP portal trees.</summary>
public static List<WalkBuilding> BuildBuildings(DatCollection dats, uint landblockId)
/// (containing landcell), portal records, drawing-BSP portal trees, and
/// landblock-local placements.</summary>
public static List<BuildingEntry> BuildBuildings(DatCollection dats, uint landblockId)
{
uint lbMask = landblockId & 0xFFFF0000u;
var result = new List<WalkBuilding>();
var result = new List<BuildingEntry>();
if (dats.Get<LandBlockInfo>(lbMask | 0xFFFEu) is not LandBlockInfo info
|| info.Buildings is null)
{
@ -138,12 +151,19 @@ public static class WalkWorldDatAdapter
if (dats.Get<GfxObj>(buildingInfo.ModelId) is GfxObj gfxObj)
bsp = ConvertDrawingBsp(gfxObj, gfxObj.DrawingBSP?.Root);
result.Add(new WalkBuilding
{
PositionCellId = positionCellId,
Portals = portals,
DrawingBsp = bsp,
});
Matrix4x4 worldTransform =
Matrix4x4.CreateFromQuaternion(buildingInfo.Frame.Orientation)
* Matrix4x4.CreateTranslation(origin);
Matrix4x4.Invert(worldTransform, out Matrix4x4 inverse);
result.Add(new BuildingEntry(
new WalkBuilding
{
PositionCellId = positionCellId,
Portals = portals,
DrawingBsp = bsp,
},
worldTransform,
inverse));
}
return result;
}