Commit graph

107 commits

Author SHA1 Message Date
Erik
4763b973da fix(terrain): use real LandHeightTable from Region dat
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>
2026-04-10 19:09:27 +02:00
Erik
1375780e14 feat(app): render static meshes from Holtburg LandBlockInfo 2026-04-10 18:32:09 +02:00
Erik
cefc689ba8 feat(app): add TextureCache for Surface→GL texture handle caching 2026-04-10 18:03:58 +02:00
Erik
6a100ef6e7 refactor(app): harden shutdown per final review
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>
2026-04-10 16:52:19 +02:00
Erik
87c45c70ac feat(app): render landblock with height-ramp shader + orbit camera
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>
2026-04-10 16:44:08 +02:00
Erik
8356fe65a0 feat(app): load landblock from dats and upload mesh to GPU
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>
2026-04-10 16:42:13 +02:00
Erik
6d18e0bd38 feat(app): Silk.NET window smoke — clear to navy
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>
2026-04-10 16:39:40 +02:00