islaApocalypse/Client/README.md
beezm 3b5bc5e5d6 docs: water at rest across the Core/Client/Server READMEs (terrain-water task 13)
Core: BlockRegistry gains WATER, with the reason it is wire-safe (block IDs
are never serialized) AND the reason it is a registry identity only rather
than a voxel the terrain path writes. Corrects the now-false line "Nothing
at runtime consumes the water data yet" -- it does, as of this task.
ChunkData's new per-column water fields and the Constants tunables.

Client: how the water sheet is built and, more importantly, WHY it is a
second mesh instead of a block in the density field -- the trap here is
that writing water into a Marching-Cubes field fuses it into the terrain
instead of laying it on top, and that is not obvious until you have done
it. Notes translucency came free from the separate material.

Server: the per-column water rule, which section is the authority for what
(WBID presence, WSRF level, WBTB fallback), that an unknown body leaves the
column dry rather than guessing, and why depth for shading comes from
blueprint heights rather than the clamped rendered geometry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 02:33:06 -04:00

2.6 KiB
Raw Permalink Blame History

Client Module

The visual layer. Takes finished chunk data and turns it into Godot nodes. It calculates nothing — no densities, no terrain, no collision.

ChunkRenderer.cs

A MeshInstance3D created per chunk by the server, which then calls RenderChunk(data).

  • Mesh generation. Calls MarchingCubes.GenerateMesh(), passing the density field, the block IDs, and the per-column data the mesher needs to colour correctly: the true (unrounded) surface heights, the biome per column, and the road surface material per column.

  • Vertex colouring. Creates a StandardMaterial3D with VertexColorUseAsAlbedo = true, so Godot renders the per-vertex colours the mesher assigned rather than a texture. Colours fade between materials across a tunable band instead of switching at whole-metre steps.

  • World positioning. ChunkPosition × CHUNK_SIZE × VOXEL_SCALE on X and Z, y = 0 — chunks are placed by their footprint and carry their full height internally.

  • Backface rendering. CullMode is disabled, so the world is still visible from underneath or from inside terrain.

  • Water at rest (task 13). BuildWaterSurface adds a second MeshInstance3D as a child, a flat sheet at each cell's water level, from the per-column water data the server read out of the blueprint. Skipped entirely for chunks with no water.

    Why a separate mesh rather than a water block. The terrain is a Marching-Cubes iso-surface over the density field. Putting water into that field would not lay a sheet on top of the seabed — it would move the iso-surface, fusing the sea into the terrain as if it were solid ground. A second surface is the only way water can sit at its level independent of the ground beneath it.

    That also makes translucency nearly free: a distinct MeshInstance3D carries its own StandardMaterial3D, so alpha is one flag and Godot sorts transparent surfaces after opaque ones itself. Colour and alpha both ramp with depth (shallow teal and clearer → deep navy and near opaque), so the shelved coast stays readable.

    Water is flat and static — no waves, no displacement, no animation. Those are Phase C2+.

Notes

  • One material instance per chunk. Each renderer builds its own StandardMaterial3D. Since they are all identical, a single shared material would do — an easy win whenever performance work starts.
  • Chunk positioning is relative to this node's parent, so moving the World node in the scene moves the entire rendered world with it.

Not here yet

No textures or UV mapping (raw vertex colour only), no LOD, no player, no UI, no input handling.