The blueprint has carried water since task 03 -- WBID per-pixel body id, WBTB body table, WSRF per-pixel surface level. Nothing ever drew it. Now the runtime does. No new water data: the blueprint is the authority on where water is and at what level, and this only reads it. WHY WATER IS ITS OWN MESH, not a block in the terrain field. The terrain is a Marching-Cubes iso-surface over ChunkData.Densities. Writing 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 under it. So ChunkRenderer builds a water MeshInstance3D as a child of the chunk's terrain mesh. TRANSPARENCY IS SHIPPED, NOT DEFERRED (Step 2.4's cheap branch). Because water is a distinct MeshInstance3D it carries its own StandardMaterial3D, so alpha is one flag and Godot sorts transparent surfaces after opaque ones by itself. Zero mesher changes. Alpha runs 0.62 shallow -> 0.97 deep, so the shelved coast stays readable and open ocean closes up. WHICH DATA DROVE IT: WSRF for the level (per-pixel, quantised to 1/32768 raw ~ 7.7 mm, far under the 1 m voxel, and no table lookup), WBID for presence (it is the water stage's own classification output -- the same set the biome grid's Ocean/Lake pixels form by construction), WBTB as the fallback when a column is flagged wet but carries the WSRF no-water sentinel. A body the table does not know leaves the column DRY rather than guessing a level. Details worth keeping: - Water Y uses Constants.HEIGHT_SCALE, the SAME mapping as the terrain surface, named so the sheet and the seabed cannot drift apart if the vertical band is ever retuned. - Depth for shading comes from BLUEPRINT heights, not rendered geometry: the terrain render clamps its floor at Y=2, so every deep-ocean column would otherwise read as one flat ~36 m and the gradient would die exactly where the ocean gets interesting. - A cell is drawn if ANY corner is wet, flat at the highest wet level. Drawing onto a partly-dry cell is deliberate -- it carries the sheet under the shoreline where the opaque terrain hides it. Only-fully-wet cells retreat the waterline a metre and leave a dry gap around every coast and lake. - Non-metallic, roughness 0.35: the scene environment is minimal, and a metallic surface reads near-black when there is nothing to reflect. BlockRegistry gains WATER (id 11). Wire-safe: block IDs are never serialized -- the blueprint's section table stores heights, biome ordinals and water data, never block IDs, and chunks are not persisted yet. MEASURED on seed 1825907253: 241,415 water columns across 454 of 4096 chunks; surface Y 37.6..37.6 m (flat, = 0.15 x 251 -- the flat sea model, rendered); depth to 32.3 m, matching an independent read of the blueprint exactly. Both an ocean and a lake fall in the default view. NO REGRESSION to the 2D pipeline, verified section by section: a regeneration of the working seed is byte-identical to task 12's run in TCRV, TDTL, HGTS, BIOM, WBID, WBTB, WSRF, TOWN and all four road sections; only PRMS differs, and only in its write timestamp. All four snapshot PNGs md5-identical. No storms, no waves, no animation, no flow -- water at rest. Those are C2+. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
72 lines
3.3 KiB
C#
72 lines
3.3 KiB
C#
using Godot;
|
|
using System.Collections.Generic;
|
|
|
|
namespace IslaApocalypse.Core
|
|
{
|
|
public static class BlockRegistry
|
|
{
|
|
// A dictionary holding all the block types in the game, searchable by their byte ID
|
|
public static readonly Dictionary<byte, BlockData> Blocks = new Dictionary<byte, BlockData>();
|
|
|
|
// Hardcoded IDs for easy reference in code
|
|
public const byte AIR = 0;
|
|
public const byte BEDROCK = 1;
|
|
public const byte STONE = 2;
|
|
public const byte DIRT = 3;
|
|
public const byte SAND = 4;
|
|
public const byte GRASS_TROPICAL = 5;
|
|
public const byte GRASS_JUNGLE = 6;
|
|
public const byte GRASS_PARADISE = 7;
|
|
public const byte SNOW = 8;
|
|
public const byte WASTELAND_DIRT = 9;
|
|
public const byte ASPHALT = 10; // For our roads!
|
|
|
|
// Water (terrain-water task 13). Wire-safe to add: block IDs are NEVER
|
|
// serialized — the blueprint's section table (BLUEPRINT_FORMAT.md) stores
|
|
// heights, biome ordinals and water data, never block IDs, and chunks are
|
|
// not persisted yet. So this ID is a runtime-only value and appending to
|
|
// the table cannot invalidate a .dat.
|
|
//
|
|
// NOTE this block is not placed into ChunkData.BlockIDs by the terrain
|
|
// path. Terrain is a Marching-Cubes iso-surface over a density field, and
|
|
// writing WATER into that field would FUSE the water into the terrain
|
|
// surface rather than lay a sheet on top of it. Water is its own mesh
|
|
// (ChunkRenderer). The entry exists so water has a real identity in the
|
|
// registry — a name and a colour — for the renderer and for whatever
|
|
// later needs to ask "what is this".
|
|
public const byte WATER = 11;
|
|
|
|
// This static constructor runs automatically the first time the registry is accessed
|
|
static BlockRegistry()
|
|
{
|
|
// ID 0 is ALWAYS Air in voxel engines.
|
|
Blocks.Add(AIR, new BlockData(AIR, "Air", false, Colors.Transparent));
|
|
|
|
// The Foundation Blocks
|
|
Blocks.Add(BEDROCK, new BlockData(BEDROCK, "Bedrock", true, new Color(0.1f, 0.1f, 0.1f)));
|
|
Blocks.Add(STONE, new BlockData(STONE, "Stone", true, new Color(0.5f, 0.5f, 0.5f)));
|
|
Blocks.Add(DIRT, new BlockData(DIRT, "Dirt", true, new Color(0.4f, 0.3f, 0.2f)));
|
|
Blocks.Add(SAND, new BlockData(SAND, "Sand", true, new Color(0.8f, 0.7f, 0.4f)));
|
|
|
|
// The Biome Surface Blocks
|
|
Blocks.Add(GRASS_TROPICAL, new BlockData(GRASS_TROPICAL, "Tropical Grass", true, new Color(0.2f, 0.6f, 0.2f)));
|
|
Blocks.Add(GRASS_JUNGLE, new BlockData(GRASS_JUNGLE, "Jungle Grass", true, new Color(0.1f, 0.4f, 0.1f)));
|
|
Blocks.Add(GRASS_PARADISE, new BlockData(GRASS_PARADISE, "Paradise Grass", true, new Color(0.4f, 0.8f, 0.4f)));
|
|
Blocks.Add(SNOW, new BlockData(SNOW, "Snow", true, new Color(0.9f, 0.9f, 0.9f)));
|
|
Blocks.Add(WASTELAND_DIRT, new BlockData(WASTELAND_DIRT, "Wasteland Dirt", true, new Color(0.5f, 0.2f, 0.5f)));
|
|
|
|
// Infrastructure
|
|
Blocks.Add(ASPHALT, new BlockData(ASPHALT, "Asphalt", true, new Color(0.15f, 0.15f, 0.15f)));
|
|
|
|
// Water — not solid (you can move through it), shallow-water colour as the
|
|
// registry's representative value; the renderer shades by depth from there.
|
|
Blocks.Add(WATER, new BlockData(WATER, "Water", false, new Color(0.24f, 0.55f, 0.62f)));
|
|
}
|
|
|
|
public static BlockData GetBlock(byte id)
|
|
{
|
|
if (Blocks.TryGetValue(id, out BlockData data)) return data;
|
|
return Blocks[AIR]; // Safe fallback so the game doesn't crash if an ID is missing
|
|
}
|
|
}
|
|
}
|