diff --git a/Core/Scripts/BLUEPRINT_FORMAT.md b/Core/Scripts/BLUEPRINT_FORMAT.md index 76ded96..c701a0f 100644 --- a/Core/Scripts/BLUEPRINT_FORMAT.md +++ b/Core/Scripts/BLUEPRINT_FORMAT.md @@ -57,7 +57,7 @@ Every section: `[u32 tag][u64 payload-length in bytes][payload]`. | `TOWN` / `0x4E574F54` | `i32 count`, then per town: `f32 X` · `f32 Y` · `u8 tier` · `u8 isHighwayNode` (0/1) | Tier ordinals from `Enums.cs::TownTier`, append-only, range-checked on read. The highway-node flag is what the generator's road topology was built from (v1 dropped it); carried and exposed on the parsed blueprint, consumed by nothing server-side yet. | | `TCRV` / `0x56524354` | 50 B base: `u16 curveVersion` · `f32 knot1..knot4` · `f32 spikeMax` · `f32 sea` · `f32 orangeCeil` · `f32 redCeil` · `f32 benchLo` · `f32 benchHi` · `f32 peakCap` · `f32 tailSlope`. **When `curveVersion ≥ 4`, a 36 B modulation extension follows:** `f32 benchAmp` · `f32 plateauAmp` · `f32 shelfSpanMin` · `f32 shelfSpanMax` · `f32 elevFreqIslands` · `f32 strengthFreqIslands` · `i32 benchSeedOffset` · `i32 plateauSeedOffset` · `i32 strengthSeedOffset`. **When `curveVersion ≥ 5`, a 9 B preset extension follows:** `u8 presetId` (1 = compact, 2 = balanced) · `f32 k5` · `f32 k6` — with the four base knot slots this makes the effective curve unambiguous from the record alone (record total 95 B; the length-framed section + version byte keep every layout change safe) | Optional — present iff the height-redistribution curve shaped this blueprint's `HGTS` (config `TerrainCurve: "v3"`); absent = raw legacy profile. **The `u16 curveVersion` selects the field semantics and the unserialized anchor set:** v1 — knots t1..t4, spikeMax = pooled calibration max, benchLo/Hi = the 50 m plateau's lo/hi. v2 — as v1 but spikeMax = the seed's effective raw pre-curve maximum (per-seed spike normalizer). v3 — knots = K1..K4 of six (K5 = 0.930304, K6 = 1.050720 are version constants, not serialized), spikeMax per-seed, benchLo/benchHi = the fixed 100 m / 220 m shelves. **v4 (current)** — as v3, but benchLo/benchHi are the shelf **BASE** anchors and the extension record carries the spatial-modulation parameters: shelf elevations vary ±benchAmp/±plateauAmp and shelf strength blends the output span across `[shelfSpanMin, shelfSpanMax]`, via Simplex fields seeded `resolvedWorldSeed + seedOffset` at `freq/MapSize` (frequencies stated in undulations per island width). Because spikeMax is per-seed (v2+), blueprints are not reproducible from curve constants alone — that is why it is recorded. **Metadata only:** exported heights are already curved; nothing re-applies the map. Calibration provenance: `HeightCurve.cs` header + the task-05/06/07 reports. | | `TDTL` / `0x4C544454` | 30 B: `u16 detailVersion` · `f32 reliefAmpM` · `f32 reliefFreqIslands` · `i32 reliefSeedOffset` · `f32 edgeAmpM` · `f32 edgeFreqIslands` · `i32 edgeSeedOffset` · `f32 edgeMaxShiftM` | Optional — present iff the terrain detail passes shaped this blueprint's `HGTS` (config `TerrainDetail: "v1"`, requires the curve). **`detailVersion` selects the body layout and a reader that does not recognise it SKIPS the section** (leaving detail metadata null) rather than misreading a differently shaped payload — the one section whose body is versioned rather than extended, because v1's layout was retired rather than grown. **v1 (retired, never shipped)** — shelf micro-relief + D8 drainage incision, 38 B; the incision produced grid-aligned artifacts and was reverted whole, so the only v1 payloads that exist are in that batch's own tree. **v2 (current)** — shelf micro-relief (±`reliefAmpM` output metres, shelf-ness weighted) + shelf-edge variation: a per-column shift of the curve's shelf/riser knot block K3/K4/K5, drawn from a Simplex field seeded `resolvedWorldSeed + edgeSeedOffset` at `edgeFreqIslands/MapSize`, amplitude ±`edgeAmpM` **metres of INPUT height** — a displacement of the shelf boundary contour, not an elevation change. `edgeAmpM` is recorded **as applied**, after the clamp to `edgeMaxShiftM` (the preset's band-squeeze bound), so the record always describes the terrain rather than the request. **Metadata only** — heights are already detailed. Machinery: `Tools/Scripts/TerrainDetailPass.cs`. | -| `EROS` / `0x534F5245` | 58 B: `u16 erosionVersion` · `i32 dropletCount` · `i32 lifetime` · `i32 brushRadius` · `i32 seedOffset` · `f32 carveCapM` · `f32 seaMarginM` · `f32 inertia` · `f32 capacityFactor` · `f32 minSlopeM` · `f32 erodeRate` · `f32 depositRate` · `f32 evaporation` · `f32 gravity` · `f32 craterExclFactor` | Optional — present iff the droplet hydraulic-erosion pass shaped this blueprint's `HGTS` (config `Erosion: "v1"`, terrain-water task 17). **`erosionVersion` selects the body layout; an unrecognised version is SKIPPED whole** (erosion metadata left null), same rule as `TDTL`. **v1 (current)** — the carve-and-deposit droplet model: `dropletCount`/`lifetime`/`carveCapM` are the three hard governors (total droplets, max steps each, max cumulative erosion depth per cell in metres); `seaMarginM` is the flood-guard clamp (no cell carved below sea + margin; below-sea cells untouched entirely, so the rendered coastline cannot move); droplets are deterministic from `resolvedWorldSeed + seedOffset` (PCG32); no cell within `craterExclFactor × CraterRadius` of the impact centre is modified. Strength constants (`inertia`, `capacityFactor`, `minSlopeM`, `erodeRate`, `depositRate`, `evaporation`, `gravity`) are the standard droplet-model dials; slopes/amounts in metres (1 raw = 251 m). All values recorded **as applied** (post config clamping). **Metadata only** — heights are already eroded, and the classify-side sections (`BIOM`/`WBID`/…) never saw the pass by design. Machinery: `Tools/Scripts/HydraulicErosion.cs`. | +| `EROS` / `0x534F5245` | 62 B: `u16 erosionVersion` · `i32 dropletCount` · `i32 lifetime` · `i32 brushRadius` · `i32 seedOffset` · `f32 carveCapM` · `f32 depositCapM` · `f32 seaMarginM` · `f32 inertia` · `f32 capacityFactor` · `f32 minSlopeM` · `f32 erodeRate` · `f32 depositRate` · `f32 evaporation` · `f32 gravity` · `f32 craterExclFactor` | Optional — present iff the droplet hydraulic-erosion pass shaped this blueprint's `HGTS` (config `Erosion: "v1"`, terrain-water tasks 17–18). **`erosionVersion` selects the body layout; an unrecognised version is SKIPPED whole** (erosion metadata left null), same rule as `TDTL`. **v1 (task 17, superseded)** — 58 B, no `depositCapM`; deposition was bilinear over 4 cells and unbounded, which built isolated cones (measured 15.5 m). The only v1 payloads in existence are in that task's batch tree. **v2 (task 18, current)** — deposition is spread over the same cone brush as carving and bounded per cell by `depositCapM` (`<= 0` = unbounded, the reference model). The FOUR governors are `dropletCount`/`lifetime`/`carveCapM`/`depositCapM`; both caps are enforced against one per-cell NET displacement ledger (positive = carved below the height the pass found, negative = built up above it) and asserted on exit. `seaMarginM` is the flood-guard clamp — no cell is carved below sea + margin, and below-sea cells are untouched in BOTH directions, so the rendered coastline cannot move. Droplets are deterministic from `resolvedWorldSeed + seedOffset` (PCG32); no cell within `craterExclFactor × CraterRadius` of the impact centre is modified. Remaining fields are the droplet-model strength dials; slopes/amounts in metres (1 raw = 251 m). All values recorded **as applied** (post config clamping). **Metadata only** — heights are already eroded, and the classify-side sections (`BIOM`/`WBID`/…) never saw the pass by design. Machinery: `Tools/Scripts/HydraulicErosion.cs`. | | `WBID` / `0x44494257` | `MapSize²` × `u16` water-body id, same pixel order as `HGTS` | Optional (absent = no water data, e.g. a legacy re-encode). `0` = no water, `1` = **the** ocean body, `2..N` = lakes. Ids assigned in deterministic scan order (X outer / Y inner, first-encountered pixel), lakes labeled with the **same 4-connectivity as `CalculateTrueOcean`**. Membership is exactly the generator's water classification — the biome grid's Ocean/Lake pixels and this grid's nonzero pixels are the same set **by construction** (shared predicates). Length must equal `2·MapSize²`. | | `WBTB` / `0x42544257` | `i32 count`, then per body (20 B): `u16 id` · `u8 type` (0 ocean, 1 lake) · `u8 salinity` (0 fresh, 1 salt) · `f32 surfaceLevel` · `i32 pixelCount` · `f32 centroidX` · `f32 centroidY` | Optional, paired with `WBID`. **`surfaceLevel` is a documented TRANSITIONAL rule:** one flat level per body — `GetSeaLevel` at the body's pixel centroid (ocean: at the map centre) under the still-live latitude field; superseded by the flat-scalar sea model (minted, lands with the coast change set). The field's per-pixel slope is deliberately NOT baked into any section. **Salinity is a provisional default** (ocean salt, lake fresh) — a placeholder for the future fresh/salt irrigation mechanic, not a mechanic. | | `WSRF` / `0x46525357` | `MapSize²` × `u16` quantized water-surface elevation, same pixel order | Optional, paired with `WBID`. `0` is the reserved **no-water sentinel**; a real level `L` (raw height units) encodes as `1 + round(L × 32768)` so it can never encode to 0; decode `(q − 1)/32768` (`BlueprintFormat.EncodeWaterLevel`/`DecodeWaterLevel`). Covers `[0 … ~1.99997]` raw at `1/32768` raw ≈ **7.7 mm** of world height (1 raw = 251 m) — far finer than the 1 m voxel. Nonzero exactly where `WBID` is nonzero; the value is the pixel's body level. | @@ -123,14 +123,18 @@ body). follow the curved terrain. When on, `TCRV` records the effective parameters including the per-seed `spikeMax`. - **`Erosion`** (`"v1"` | `"off"`, default `"off"` — opt-in until the developer's gate approves - it) — the task-17 droplet hydraulic-erosion pass: carve-and-deposit drainage detailing of - `HGTS` after the detail passes and before the crater carve. Governor dials - (`ErosionDropletCount` / `ErosionDropletLifetime` / `ErosionCarveCap`, defaults 400 000 / 48 / - 8 m; bounds clamped loudly at load) plus the sea-clamp margin and strength constants — the - full dial list and semantics live in the `EROS` row above and `ConfigManager.cs`. Output-height - only: the classify path reads pre-erosion heights, so `BIOM` and every water section stay - bit-identical with erosion on or off, and the sea clamp keeps even the RENDERED coastline - fixed. When on, `EROS` records the parameters as applied. + it) — the droplet hydraulic-erosion pass (tasks 17–18): carve-and-deposit drainage detailing of + `HGTS` after the detail passes and before the crater carve. FOUR governor dials + (`ErosionDropletCount` / `ErosionDropletLifetime` / `ErosionCarveCap` / `ErosionDepositCap`, + defaults 250 000 / 384 / 15 m / 6 m; bounds clamped loudly at load) plus the sea-clamp margin + and strength constants — the full dial list and semantics live in the `EROS` row above and + `ConfigManager.cs`. The task-18 defaults tune for a drainage HIERARCHY: droplets live long + enough (384 steps at inertia 0.35) for their paths to overlap and deepen shared low lines into + trunk channels rather than dying as short independent scratches, and the raised carve cap lets + those trunks separate from the fine rills instead of both piling against one ceiling. + Output-height only: the classify path reads pre-erosion heights, so `BIOM` and every water + section stay bit-identical with erosion on or off, and the sea clamp keeps even the RENDERED + coastline fixed. When on, `EROS` records the parameters as applied. - **`CoastProfile`** (`"wide"` | `"steep"`, default `"wide"`) — the submarine shelf. The height curve is identity at and below sea level, so it never reshaped the seabed; `"wide"` compresses diff --git a/Tools/Scripts/README.md b/Tools/Scripts/README.md index df8ed79..b6e3a60 100644 --- a/Tools/Scripts/README.md +++ b/Tools/Scripts/README.md @@ -43,22 +43,33 @@ Generates the entire 2D blueprint. Roughly in order: bit-identical to an unwarped one outside the shelf/riser stack. Drops the `0_height` hillshade snapshot (hypsometric bands × NW hillshade) in both modes. - **Erosion (task 17, Phase C0)** — with `Erosion: "v1"` (default **"off"**, opt-in until the + **Erosion (tasks 17–18, Phase C0)** — with `Erosion: "v1"` (default **"off"**, opt-in until the developer's gate approves it) a droplet-based hydraulic erosion pass (`HydraulicErosion.cs`, standalone numeric, deterministic from the resolved seed) details the curved+detailed render map after the detail passes and before the crater carve: droplets walk downhill with inertia, - eroding steep fast stretches over a small brush and depositing where the ground flattens — - organic, sinuous drainage, not grid hatching. Three hard governors bound it - (`ErosionDropletCount`/`ErosionDropletLifetime`/`ErosionCarveCap` — the cap is per-cell, in - metres, ledger-enforced), a sea clamp forbids carving below sea + margin and leaves below-sea - cells untouched entirely (the rendered coastline cannot move — asserted every generation), and - nothing within 1.2× the crater radius is modified. Output-height only: biome/water + eroding steep fast stretches and depositing where the ground flattens — both spread over the + same cone brush, so neither carving nor dumping can spike a single cell. **Four** hard + governors bound it (`ErosionDropletCount`/`ErosionDropletLifetime`/`ErosionCarveCap`/ + `ErosionDepositCap`; both caps are per-cell metres against one net-displacement ledger and are + asserted on exit), a sea clamp forbids carving below sea + margin and leaves below-sea cells + untouched in both directions (the rendered coastline cannot move — asserted every generation), + and nothing within 1.2× the crater radius is modified. Output-height only: biome/water classification reads the retained pre-erosion map, so `1_biomes`/`0_water` are bit-identical - with erosion on or off. **No rivers yet** — the carved channels are the designated future river - routes (Phase C0b promotes them). The predecessor D8 drainage-incision pass was written and - reverted in task 10 — per-cell steepest descent on a regular grid can only route along eight - headings, and at map scale that reads as straight hatching, not drainage; the droplet model is - the working replacement. + with erosion on or off. + The task-18 defaults tune for a drainage **hierarchy** — fine rills everywhere feeding a set of + clearly deeper convergent channels — by letting droplets live long enough (384 steps at inertia + 0.35, low evaporation) that their paths overlap and deepen shared low lines, and by raising the + carve cap to 15 m so trunks separate from rills instead of both piling against one ceiling. + Measured on seed 1280587109: ~1.9 M cells carved past 0.5 m, 42 k past 5 m, 1.4 k past 10 m, + and 177 connected channel systems of 200+ cells at the 3 m threshold. Erosion concentrates + ~9× on the curve's shelf risers, because that is where sustained slope exists; the flat + shelves and lowlands are barely touched, so **the lowland continuation of a trunk is not + erosion's to cut** — that is river promotion's job. **No rivers yet** — the carved channels are + the designated future river routes (Phase C0b promotes them by flow accumulation). + The predecessor D8 drainage-incision pass was written and reverted in task 10 — per-cell + steepest descent on a regular grid can only route along eight headings, and at map scale that + reads as straight hatching, not drainage; the droplet model is the working replacement (its + carve field measures isotropic to within 1.5 % across the folded 45° grid period). 3. **Sea level and water** — sea level per the configured model (`SeaLevelModel`: `"flat"` scalar — the default, `SeaLevelValue` 0.15 — or the legacy `"field"` latitude Lerp); flood fill separates true ocean from inland lakes; a mainland fill guarantees one contiguous landmass.