revert: the D8 drainage incision, whole (terrain-water task 10)

The task-10 draft's PASS B shipped and produced the canonical grid
artifact: thousands of straight, disconnected, pooling scratches. Per-
cell steepest descent on a regular grid can only route along its eight
neighbour headings, so at map scale the "channels" read as hatching,
not drainage. Reverted entirely rather than tuned -- no K, p or mask
setting fixes a directional basis. Rivers and erosion move to Phase C
as a hydraulic-erosion pass over FINAL terrain.

Removed: FlowAccumulation / IncisionWeight / EdgeBlend and the INC_*,
SEA_CLAMP, SHELF_INC_WEIGHT and CRATER_* constants; RunIncisionPass and
the pass-2b call; the six incision fields from TDTL (writer, parser,
harness). KEPT, untouched and developer-approved: pass A, the shelf
micro-relief skin, and its ShelfReliefAmp dial.

The crater carve folds back into the single pass-2 loop it came from,
carving two LOCALS written once each. That is what the code did before
the draft split it out, and it makes the aliased double-carve that the
split introduced (fix 1b98fb5) structurally impossible rather than
merely fixed -- there is no longer a read-modify-write to get wrong.

TDTL bodies are now versioned (BlueprintFormat.TDTL_VERSION = 2) and the
parser skips a body version it does not know instead of misreading the
longer v1 layout into plausible nonsense. The only v1 payloads that
exist are in the reverted batch's own tree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stewart Howe 2026-08-08 20:30:44 -04:00
parent e3f7b508e7
commit 3b6d166458
7 changed files with 56 additions and 224 deletions

View file

@ -35,6 +35,13 @@ namespace IslaApocalypse.Core
public const uint TAG_TERRAIN_CURVE = 0x56524354; // "TCRV"
public const uint TAG_TERRAIN_DETAIL = 0x4C544454; // "TDTL"
// TDTL body version. 1 = shelf micro-relief + D8 drainage incision (the
// task-10 draft; the incision was reverted, and the only v1 payloads in
// existence are in that batch's tree). 2 = the current body. A reader that
// meets an unrecognised body version skips it rather than misreading a
// differently shaped payload into plausible-looking nonsense.
public const ushort TDTL_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
// level can never encode to 0. Decodes back via (q − 1) / 32768. Covers

View file

@ -155,12 +155,9 @@ namespace IslaApocalypse.Core
private static void WriteTerrainDetail(BinaryWriter writer, TerrainDetailInfo d)
{
writer.Write(d.Version);
writer.Write(d.ReliefAmpM); writer.Write(d.ReliefFreqIslands);
writer.Write(d.IncK); writer.Write(d.IncP); writer.Write(d.IncCapM);
writer.Write(d.SeaClampRaw); writer.Write(d.CraterExclFactor);
writer.Write(d.ShelfIncWeight);
writer.Write(d.ReliefSeedOffset);
writer.Write(d.Version); // u16
writer.Write(d.ReliefAmpM); writer.Write(d.ReliefFreqIslands); // 2 × f32
writer.Write(d.ReliefSeedOffset); // i32
}
private static void WriteWaterBodyIds(BinaryWriter writer, WorldBlueprint bp)

View file

@ -33,10 +33,9 @@ namespace IslaApocalypse.Core // Change this if your namespace is different
// are retired. Default: v5.
public static string TerrainCurve = "v5";
// Terrain detail passes (task 10): "v1" = shelf micro-relief + drainage
// incision as one judged unit (requires the curve; no-op when it is off);
// "off" disables both. ShelfReliefAmp is the micro-relief amplitude in
// metres. Defaults: v1, 3 m.
// Terrain detail passes (task 10): "v1" = shelf micro-relief (requires the
// curve; no-op when it is off), "off" disables it. ShelfReliefAmp is the
// micro-relief amplitude in metres. Defaults: v1, 3 m.
public static string TerrainDetail = "v1";
public static float ShelfReliefAmp = 3.0f;

View file

@ -89,15 +89,15 @@ namespace IslaApocalypse.Core
/// <summary>
/// The terrain detail passes that shaped this blueprint's HGTS (v2 TDTL section,
/// terrain-water task 10): shelf micro-relief + drainage incision parameters.
/// Null when detail was off. Metadata only — heights are already detailed.
/// terrain-water task 10): the shelf micro-relief parameters. Null when detail
/// was off. Metadata only — heights are already detailed. Body version 1 (the
/// reverted relief + D8-incision layout) is longer and differently shaped; the
/// parser refuses it rather than misreading it.
/// </summary>
public class TerrainDetailInfo
{
public ushort Version;
public float ReliefAmpM, ReliefFreqIslands;
public float IncK, IncP, IncCapM;
public float SeaClampRaw, CraterExclFactor, ShelfIncWeight;
public int ReliefSeedOffset;
}
@ -254,7 +254,7 @@ namespace IslaApocalypse.Core
else if (tag == BlueprintFormat.TAG_WATER_SURFACE) sectionOk = ParseWaterGrid(reader, blueprint, payloadLength, isSurface: true);
else if (tag == BlueprintFormat.TAG_WATER_BODY_TABLE) sectionOk = ParseWaterBodyTable(reader, blueprint);
else if (tag == BlueprintFormat.TAG_TERRAIN_CURVE) sectionOk = ParseTerrainCurve(reader, blueprint);
else if (tag == BlueprintFormat.TAG_TERRAIN_DETAIL) sectionOk = ParseTerrainDetail(reader, blueprint);
else if (tag == BlueprintFormat.TAG_TERRAIN_DETAIL) sectionOk = ParseTerrainDetail(reader, blueprint, payloadLength);
else
{
// The property the redesign exists to buy: future sections (water,
@ -448,14 +448,19 @@ namespace IslaApocalypse.Core
return true;
}
private static bool ParseTerrainDetail(BinaryReader reader, WorldBlueprint blueprint)
private static bool ParseTerrainDetail(BinaryReader reader, WorldBlueprint blueprint, ulong payloadLength)
{
var d = new TerrainDetailInfo();
d.Version = reader.ReadUInt16();
if (d.Version != BlueprintFormat.TDTL_VERSION)
{
// Skip the body and leave TerrainDetail null. The heights are still
// whatever they are — we simply refuse to describe them wrongly.
GD.PrintErr($"[MapDataParser] ⚠ TDTL body version {d.Version} is not the current {BlueprintFormat.TDTL_VERSION} — section skipped, detail metadata unavailable.");
reader.BaseStream.Seek((long)payloadLength - 2L, SeekOrigin.Current);
return true;
}
d.ReliefAmpM = reader.ReadSingle(); d.ReliefFreqIslands = reader.ReadSingle();
d.IncK = reader.ReadSingle(); d.IncP = reader.ReadSingle(); d.IncCapM = reader.ReadSingle();
d.SeaClampRaw = reader.ReadSingle(); d.CraterExclFactor = reader.ReadSingle();
d.ShelfIncWeight = reader.ReadSingle();
d.ReliefSeedOffset = reader.ReadInt32();
blueprint.TerrainDetail = d;
return true;