Phase 1 simplified per-vertex height as byte * 2.0f, but AC stores
heights as byte indices into a 256-entry non-linear float lookup
(Region.LandDefs.LandHeightTable). Static object placements in
LandBlockInfo use the real table, so terrain rendered with the
simplified scale left buildings floating or buried.
LandblockMesh.Build now takes an explicit float[] heightTable so
the core code stays testable without a DatCollection. GameWindow
loads Region id 0x13000000 once at startup and passes its
LandDefs.LandHeightTable into every landblock mesh build. The
Phase 1 tests use an identity table (i * 2f for i in 0..255) so
their expectations remain unchanged.
Addresses the 'buildings buried and floating' issue the user
observed after the Phase 2a visual checkpoint.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Addresses final code review of phase-1 branch (Important I-1, I-3):
- Move plugin Enable() loop inside the same try block as GameWindow.Run,
and wrap each Enable() in per-plugin try/catch mirroring the Disable
loop. Previously, a plugin Enable() throwing would skip the finally
block entirely: plugins that had already enabled would never get
disabled, Serilog would never flush, and the exception would escape
ungracefully. Now Enable failures are logged and contained, and
shutdown always runs.
- Add a comment at the Get<LandBlock> call in GameWindow.OnLoad explaining
why TryGet was avoided (the [MaybeNullWhen(false)] nullable-generic
analysis trips TreatWarningsAsErrors).
I-2 (camera aspect doesn't update on window resize) deferred to Phase 2.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a 2-stage GLSL shader (vertex + fragment), a Shader helper that
compiles/links and exposes SetMatrix4 for uniforms, and an OrbitCamera
with yaw/pitch/distance and a 192-unit-centered target for a single
landblock. TerrainRenderer now takes a Shader and issues an actual
DrawElements call with uView + uProjection uniforms. GameWindow owns
the Shader and Camera, routes mouse drag to camera yaw/pitch, and
scroll wheel to camera distance.
The fragment shader maps world Z to a green-brown-white ramp so
lowlands read green, midlands brown, and peaks white — no textures
yet, but enough to visually confirm the terrain shape.
Shaders are copied to the output dir via a <None Update> item group.
Smoke verified against real dats: process stays alive with no GL
errors, no shader compile/link failures, and no exception trail.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GameWindow now owns a DatCollection + TerrainRenderer. On load it
opens the dat directory passed as argv[0] (or ACDREAM_DAT_DIR), finds
Holtburg (landblock 0xA9B4FFFF) by default with a fallback to the
first landblock in the cell b-tree, builds the CPU mesh from
LandblockMesh.Build, and uploads VBO+EBO+VAO with a 3f/3f/2f attribute
layout. No draw call yet — shader and matrix uniforms land in Task 9.
Enabled AllowUnsafeBlocks on the App csproj so the fixed-buffer upload
in TerrainRenderer compiles. Uses dats.Get<LandBlock>(id) instead of
TryGet(..., out T) to sidestep the [MaybeNullWhen(false)] analysis that
TreatWarningsAsErrors was flagging.
Smoke verified against the real retail dats: prints
"loaded landblock 0xA9B4FFFF" and the window stays alive with no GL
errors or exceptions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Minimal Silk.NET window wiring: 1280x720, OpenGL 4.3 core profile,
VSync, dark navy clear color, Escape to close. No rendering beyond
the clear call — terrain and shader land in Tasks 8 and 9.
Manual smoke: process starts, stays alive past GL context creation,
produces no stderr, no uncaught exceptions. Actual visual check
will happen end-to-end after Task 10.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>