refactor: #100 — remove hiddenTerrainCells / BuildingTerrainCells plumbing

Retired in favour of Task 1's retail-faithful terrain shader Z nudge.
Pure removal — ~50 LOC of dead surface area across:

  - src/AcDream.Core/Terrain/LandblockMesh.cs (drop parameter +
    cell-collapse block)
  - src/AcDream.Core/World/LoadedLandblock.cs (drop field)
  - src/AcDream.Core/World/LandblockLoader.cs (drop method + call)
  - src/AcDream.App/Rendering/GameWindow.cs (3 sites)
  - src/AcDream.App/Streaming/GpuWorldState.cs (6 ctor sites)
  - src/AcDream.App/Streaming/LandblockStreamer.cs (1 ctor site)
  - tests/AcDream.Core.Tests/World/LandblockLoaderTests.cs (drop test)
  - tests/AcDream.Core.Tests/Terrain/LandblockMeshTests.cs (drop test)

No retail anchor — the deleted mechanism never had one; this commit
rolls our code back to the actual retail behaviour established in
the prior commit's shader nudge.

ISSUES.md #100 moved to Recently closed.

Cross-ref:
  docs/research/2026-05-25-issue-100-terrain-cutout-handoff.md
  docs/superpowers/plans/2026-05-25-issue-100-terrain-cutout.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-25 21:34:06 +02:00
parent f48c74aa8b
commit a64e6f20da
9 changed files with 52 additions and 165 deletions

View file

@ -1806,7 +1806,7 @@ public sealed class GameWindow : IDisposable
// _heightTable and _blendCtx are read-only after initialization.
// lb.Heightmap is the pre-loaded LandBlock; no dat read needed here.
return AcDream.Core.Terrain.LandblockMesh.Build(
lb.Heightmap, lbX, lbY, _heightTable, _blendCtx, _surfaceCache, lb.BuildingTerrainCells);
lb.Heightmap, lbX, lbY, _heightTable, _blendCtx, _surfaceCache);
});
_streamer.Start();
@ -5145,8 +5145,7 @@ public sealed class GameWindow : IDisposable
return new AcDream.Core.World.LoadedLandblock(
baseLoaded.LandblockId,
baseLoaded.Heightmap,
merged,
baseLoaded.BuildingTerrainCells);
merged);
}
/// <summary>
@ -8803,7 +8802,7 @@ public sealed class GameWindow : IDisposable
uint lbX = (id >> 24) & 0xFFu;
uint lbY = (id >> 16) & 0xFFu;
return AcDream.Core.Terrain.LandblockMesh.Build(
lb.Heightmap, lbX, lbY, _heightTable, _blendCtx, _surfaceCache, lb.BuildingTerrainCells);
lb.Heightmap, lbX, lbY, _heightTable, _blendCtx, _surfaceCache);
});
_streamer.Start();

View file

@ -176,8 +176,7 @@ public sealed class GpuWorldState
landblock = new LoadedLandblock(
landblock.LandblockId,
landblock.Heightmap,
merged,
landblock.BuildingTerrainCells);
merged);
_pendingByLandblock.Remove(landblock.LandblockId);
}
@ -239,8 +238,7 @@ public sealed class GpuWorldState
_loaded[kvp.Key] = new LoadedLandblock(
kvp.Value.LandblockId,
kvp.Value.Heightmap,
newList,
kvp.Value.BuildingTerrainCells);
newList);
// Add to new (via AppendLiveEntity which handles pending)
AppendLiveEntity(newCanonicalLb, entity);
@ -341,7 +339,7 @@ public sealed class GpuWorldState
foreach (var e in lb.Entities)
if (e.ServerGuid != serverGuid) newList.Add(e);
_loaded[kvp.Key] = new LoadedLandblock(lb.LandblockId, lb.Heightmap, newList, lb.BuildingTerrainCells);
_loaded[kvp.Key] = new LoadedLandblock(lb.LandblockId, lb.Heightmap, newList);
rebuiltLoaded = true;
}
@ -398,8 +396,7 @@ public sealed class GpuWorldState
_loaded[canonicalLandblockId] = new LoadedLandblock(
lb.LandblockId,
lb.Heightmap,
newEntities,
lb.BuildingTerrainCells);
newEntities);
RebuildFlatView();
return;
}
@ -463,8 +460,7 @@ public sealed class GpuWorldState
_loaded[canonical] = new LoadedLandblock(
lb.LandblockId,
lb.Heightmap,
System.Array.Empty<WorldEntity>(),
lb.BuildingTerrainCells);
System.Array.Empty<WorldEntity>());
_pendingByLandblock.Remove(canonical);
RebuildFlatView();
}
@ -500,7 +496,7 @@ public sealed class GpuWorldState
var merged = new List<WorldEntity>(lb.Entities.Count + entities.Count);
merged.AddRange(lb.Entities);
merged.AddRange(entities);
_loaded[canonical] = new LoadedLandblock(lb.LandblockId, lb.Heightmap, merged, lb.BuildingTerrainCells);
_loaded[canonical] = new LoadedLandblock(lb.LandblockId, lb.Heightmap, merged);
if (_wbSpawnAdapter is not null)
_wbSpawnAdapter.OnLandblockLoaded(_loaded[canonical]);

View file

@ -231,8 +231,7 @@ public sealed class LandblockStreamer : IDisposable
lb = new LoadedLandblock(
lb.LandblockId,
lb.Heightmap,
System.Array.Empty<AcDream.Core.World.WorldEntity>(),
lb.BuildingTerrainCells);
System.Array.Empty<AcDream.Core.World.WorldEntity>());
}
_outbox.Writer.TryWrite(new LandblockStreamResult.Loaded(
load.LandblockId, tier, lb, mesh));

View file

@ -40,15 +40,13 @@ public static class LandblockMesh
/// <param name="heightTable">Region.LandDefs.LandHeightTable — 256 float heights.</param>
/// <param name="ctx">TerrainAtlas-derived blending inputs.</param>
/// <param name="surfaceCache">Shared SurfaceInfo cache keyed by palette code.</param>
/// <param name="hiddenTerrainCells">Optional cell indices (cy * 8 + cx) to draw as zero-area triangles.</param>
public static LandblockMeshData Build(
LandBlock block,
uint landblockX,
uint landblockY,
float[] heightTable,
TerrainBlendingContext ctx,
System.Collections.Generic.IDictionary<uint, SurfaceInfo> surfaceCache,
System.Collections.Generic.IReadOnlySet<int>? hiddenTerrainCells = null)
System.Collections.Generic.IDictionary<uint, SurfaceInfo> surfaceCache)
{
ArgumentNullException.ThrowIfNull(block);
ArgumentNullException.ThrowIfNull(heightTable);
@ -168,21 +166,9 @@ public static class LandblockMesh
}
}
// Indices are trivial 0..383 since we don't deduplicate verts. When
// a building owns an outdoor terrain cell, keep the fixed 384-index
// contract but collapse its two triangles so the building/stair mesh
// can visually own the hole.
// Indices are trivial 0..383 since we don't deduplicate verts.
for (uint i = 0; i < VerticesPerLandblock; i++)
{
int cellIdx = (int)i / VerticesPerCell;
if (hiddenTerrainCells is not null && hiddenTerrainCells.Contains(cellIdx))
{
indices[i] = (uint)(cellIdx * VerticesPerCell);
continue;
}
indices[i] = i;
}
return new LandblockMeshData(vertices, indices);
}

View file

@ -23,30 +23,8 @@ public static class LandblockLoader
var entities = info is null
? Array.Empty<WorldEntity>()
: BuildEntitiesFromInfo(info, landblockId);
var buildingTerrainCells = info is null
? null
: BuildBuildingTerrainCells(info);
return new LoadedLandblock(landblockId, block, entities, buildingTerrainCells);
}
/// <summary>
/// Map LandBlockInfo.Buildings to 8x8 terrain mesh cells (cy * 8 + cx).
/// Retail attaches each CBuildingObj to its outside landcell during
/// CLandBlock::init_buildings; keep this signal separate from stabs so
/// ordinary static props do not punch holes in terrain.
/// </summary>
public static IReadOnlySet<int> BuildBuildingTerrainCells(LandBlockInfo info)
{
var result = new HashSet<int>();
foreach (var building in info.Buildings)
{
int cx = Math.Clamp((int)(building.Frame.Origin.X / 24f), 0, 7);
int cy = Math.Clamp((int)(building.Frame.Origin.Y / 24f), 0, 7);
result.Add(cy * 8 + cx);
}
return result;
return new LoadedLandblock(landblockId, block, entities);
}
/// <summary>

View file

@ -5,5 +5,4 @@ namespace AcDream.Core.World;
public sealed record LoadedLandblock(
uint LandblockId,
LandBlock Heightmap,
IReadOnlyList<WorldEntity> Entities,
IReadOnlySet<int>? BuildingTerrainCells = null);
IReadOnlyList<WorldEntity> Entities);