fix: brush-spread deposition + deposit-cap governor; retune erosion for a drainage hierarchy (terrain-water task 18)
Deposition now spreads over the same cone brush as carving, and the per-cell erosion ledger becomes a signed NET displacement ledger, so both caps are measured from the height the pass found. Brush-spreading alone cut the spike 15.49 -> 6.12 m at task-17 dials, but NOT at hierarchy dials (long paths carry far more sediment; a loaded droplet meeting a rise dumps min(rise, load) at once -> 25.5 m). So deposition also gets governor 4, ErosionDepositCap (default 6 m, <= 0 = unbounded), asserted on exit like the carve cap. Dial defaults retuned for a drainage HIERARCHY: 250k droplets x 384 steps at inertia 0.35 / evaporation 0.004, erode 0.12, carve cap 15 m. Diagnosis was that lifetime 48 let a droplet travel at most 48 px on a ~3000 px island radius, so paths could not overlap into trunks: the deepest task-17 features were 43x36 px patches. Now 177 channel systems of 200+ cells at the 3 m threshold, top one 204x279 px with 14 tributary tips; cells past 5 m up 8.6x (4,948 -> 42,385) while the fine rills are retained. Verified descending, not contour-locked: 0/155 components have drop/extent < 0.12 (median 1.13). EROS body version -> 2 (deposit cap inserted after the carve cap); v1 payloads are skipped whole by the existing rule. HydraulicErosion.VERSION now reads BlueprintFormat.EROS_VERSION instead of restating it — the local copy had already drifted and stamped a v2 body as v1, which readers decode with every float shifted by one field. Oracle green on both seeds (1280587109, 1512575962): 1_biomes/0_water md5-identical erosion-ON vs OFF, BIOM/WBID bitwise equal. Flood guard 0 new water px, 0 below-sea cells touched, crater zone untouched, island top unchanged, carve field isotropic to 1.4% across the folded 45deg period. Erosion pass 34 s at these dials. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
56ac17e670
commit
cc6a24d2b9
8 changed files with 105 additions and 59 deletions
|
|
@ -43,11 +43,13 @@ namespace IslaApocalypse.Core
|
|||
// differently shaped payload into plausible-looking nonsense.
|
||||
public const ushort TDTL_VERSION = 2;
|
||||
|
||||
// EROS body version (terrain-water task 17). 1 = the droplet hydraulic-
|
||||
// erosion params block: governors (count/lifetime/carve cap), sea clamp,
|
||||
// brush, strength constants, RNG seed offset, crater exclusion factor.
|
||||
// Same reader rule as TDTL: an unknown body version is skipped whole.
|
||||
public const ushort EROS_VERSION = 1;
|
||||
// EROS body version. 1 (task 17) = governors count/lifetime/carve cap, sea
|
||||
// clamp, brush, strength constants, RNG seed offset, crater exclusion factor;
|
||||
// the only v1 payloads in existence are in that task's batch tree. 2 (task 18,
|
||||
// current) inserts the DEPOSIT CAP governor after the carve cap — deposition is
|
||||
// now brush-spread and per-cell bounded. Same reader rule as TDTL: an unknown
|
||||
// body version is skipped whole rather than misread into plausible nonsense.
|
||||
public const ushort EROS_VERSION = 2;
|
||||
|
||||
// WSRF quantization: u16, 0 reserved as the no-water sentinel. A real level L
|
||||
// (raw blueprint height units) encodes as 1 + round(L × 32768), so a genuine
|
||||
|
|
|
|||
|
|
@ -170,7 +170,8 @@ namespace IslaApocalypse.Core
|
|||
writer.Write(e.Version); // u16
|
||||
writer.Write(e.DropletCount); writer.Write(e.Lifetime); // 2 × i32
|
||||
writer.Write(e.BrushRadius); writer.Write(e.SeedOffset); // 2 × i32
|
||||
writer.Write(e.CarveCapM); writer.Write(e.SeaMarginM); // 2 × f32
|
||||
writer.Write(e.CarveCapM); writer.Write(e.DepositCapM); // 2 × f32
|
||||
writer.Write(e.SeaMarginM); // f32
|
||||
writer.Write(e.Inertia); writer.Write(e.CapacityFactor); // 2 × f32
|
||||
writer.Write(e.MinSlopeM); // f32
|
||||
writer.Write(e.ErodeRate); writer.Write(e.DepositRate); // 2 × f32
|
||||
|
|
|
|||
|
|
@ -55,18 +55,28 @@ namespace IslaApocalypse.Core // Change this if your namespace is different
|
|||
// never touched at all, so the rendered coastline cannot move. The remaining
|
||||
// dials are the standard droplet-model strength constants; slopes/amounts
|
||||
// are in METRES (1 raw height unit = 251 m).
|
||||
// Task-18 defaults tune for a DRAINAGE HIERARCHY: long-lived, committed
|
||||
// droplets (lifetime 384 at inertia 0.35, evaporation 0.004) travel far
|
||||
// enough down a flank that their paths overlap and deepen shared low lines
|
||||
// into trunk channels, instead of dying as independent 48-px scratches;
|
||||
// the carve cap is raised to 15 m so trunks can separate from the fine
|
||||
// rills instead of both piling up against the same ceiling. A modest
|
||||
// erode rate keeps the total material moved in detailing range.
|
||||
// ErosionDepositCap is governor 4 (task 18): brush-spread deposition alone
|
||||
// does not bound a spike once droplets carry long-path loads.
|
||||
public static string Erosion = "off";
|
||||
public static int ErosionDropletCount = 400000;
|
||||
public static int ErosionDropletLifetime = 48;
|
||||
public static float ErosionCarveCap = 8.0f; // m per cell
|
||||
public static int ErosionDropletCount = 250000;
|
||||
public static int ErosionDropletLifetime = 384;
|
||||
public static float ErosionCarveCap = 15.0f; // m per cell
|
||||
public static float ErosionDepositCap = 6.0f; // m per cell; <= 0 = unbounded
|
||||
public static float ErosionSeaMargin = 0.5f; // m above sea, carve floor
|
||||
public static int ErosionBrushRadius = 2; // px
|
||||
public static float ErosionInertia = 0.05f;
|
||||
public static float ErosionInertia = 0.35f;
|
||||
public static float ErosionCapacity = 4.0f;
|
||||
public static float ErosionMinSlope = 0.01f; // m per px, capacity floor
|
||||
public static float ErosionErodeRate = 0.3f;
|
||||
public static float ErosionDepositRate = 0.3f;
|
||||
public static float ErosionEvaporation = 0.02f;
|
||||
public static float ErosionMinSlope = 0.02f; // m per px, capacity floor
|
||||
public static float ErosionErodeRate = 0.12f;
|
||||
public static float ErosionDepositRate = 0.15f;
|
||||
public static float ErosionEvaporation = 0.004f;
|
||||
public static float ErosionGravity = 4.0f;
|
||||
|
||||
// Island falloff shaping (task 11).
|
||||
|
|
@ -215,6 +225,7 @@ namespace IslaApocalypse.Core // Change this if your namespace is different
|
|||
if (data.ContainsKey("ErosionDropletCount")) ErosionDropletCount = (int)data["ErosionDropletCount"];
|
||||
if (data.ContainsKey("ErosionDropletLifetime")) ErosionDropletLifetime = (int)data["ErosionDropletLifetime"];
|
||||
if (data.ContainsKey("ErosionCarveCap")) ErosionCarveCap = (float)data["ErosionCarveCap"];
|
||||
if (data.ContainsKey("ErosionDepositCap")) ErosionDepositCap = (float)data["ErosionDepositCap"];
|
||||
if (data.ContainsKey("ErosionSeaMargin")) ErosionSeaMargin = (float)data["ErosionSeaMargin"];
|
||||
if (data.ContainsKey("ErosionBrushRadius")) ErosionBrushRadius = (int)data["ErosionBrushRadius"];
|
||||
if (data.ContainsKey("ErosionInertia")) ErosionInertia = (float)data["ErosionInertia"];
|
||||
|
|
@ -235,6 +246,8 @@ namespace IslaApocalypse.Core // Change this if your namespace is different
|
|||
ErosionCarveCap = Mathf.Clamp(ErosionCarveCap, 0f, 60f);
|
||||
if (rawCount != ErosionDropletCount || rawLife != ErosionDropletLifetime || rawCap != ErosionCarveCap)
|
||||
GD.PrintErr($"[ConfigManager] Erosion governor out of bounds — clamped: count {rawCount}->{ErosionDropletCount}, lifetime {rawLife}->{ErosionDropletLifetime}, cap {rawCap}->{ErosionCarveCap} m.");
|
||||
// Negative is meaningless; 0 is the documented "unbounded" escape hatch.
|
||||
ErosionDepositCap = Mathf.Clamp(ErosionDepositCap, 0f, 60f);
|
||||
ErosionSeaMargin = Mathf.Clamp(ErosionSeaMargin, 0f, 5f);
|
||||
ErosionBrushRadius = Mathf.Clamp(ErosionBrushRadius, 0, 8);
|
||||
ErosionInertia = Mathf.Clamp(ErosionInertia, 0f, 0.99f);
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ namespace IslaApocalypse.Core
|
|||
{
|
||||
public ushort Version;
|
||||
public int DropletCount, Lifetime, BrushRadius, SeedOffset;
|
||||
public float CarveCapM, SeaMarginM;
|
||||
public float CarveCapM, DepositCapM, SeaMarginM;
|
||||
public float Inertia, CapacityFactor, MinSlopeM;
|
||||
public float ErodeRate, DepositRate, Evaporation, Gravity;
|
||||
public float CraterExclFactor;
|
||||
|
|
@ -509,7 +509,8 @@ namespace IslaApocalypse.Core
|
|||
}
|
||||
e.DropletCount = reader.ReadInt32(); e.Lifetime = reader.ReadInt32();
|
||||
e.BrushRadius = reader.ReadInt32(); e.SeedOffset = reader.ReadInt32();
|
||||
e.CarveCapM = reader.ReadSingle(); e.SeaMarginM = reader.ReadSingle();
|
||||
e.CarveCapM = reader.ReadSingle(); e.DepositCapM = reader.ReadSingle();
|
||||
e.SeaMarginM = reader.ReadSingle();
|
||||
e.Inertia = reader.ReadSingle(); e.CapacityFactor = reader.ReadSingle();
|
||||
e.MinSlopeM = reader.ReadSingle();
|
||||
e.ErodeRate = reader.ReadSingle(); e.DepositRate = reader.ReadSingle();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue