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> |
||
|---|---|---|
| .. | ||
| Scripts | ||
| README.md | ||
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
StandardMaterial3DwithVertexColorUseAsAlbedo = 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_SCALEon X and Z,y = 0— chunks are placed by their footprint and carry their full height internally. -
Backface rendering.
CullModeis disabled, so the world is still visible from underneath or from inside terrain. -
Water at rest (task 13).
BuildWaterSurfaceadds a secondMeshInstance3Das 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
MeshInstance3Dcarries its ownStandardMaterial3D, 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
Worldnode 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.