rev 3 of the curve redesign. The developer's verdict on the 01 baseline was that the
LOWLANDS ARE GOOD; the fault is the terracing above them. So this adds a second curve
mode that preserves the low plain bit-for-bit and replaces everything above the flood
line with one smooth monotone climb.
Core/ContinuousCurve — piecewise, and the pieces have different loyalties:
- at/below sea: identity, as ever.
- above sea to K2: DELEGATES to HeightCurve's own toe+red branches. Not "equivalent" —
the same code path, so the same floats. Oracle (d) holds it to that.
- above the ceiling: a Fritsch-Carlson (PCHIP) monotone spline to the 420 m cap,
C1-joined to the red band's exit slope. Monotone by construction for any ordered
control points, which retires the 24-corner sweep; a 10k strict-increase sample runs
per seed anyway, because "cannot fail" is worth a millisecond.
- Build() REFUSES rather than degrades: a ceiling near the old bench, a drama that folds
the summit under its own onset, control-point secants that are not strictly increasing
(the no-magnet rule, enforced rather than hoped for).
Only BENCH_*/PLATEAU_* are dropped. SEA/ORANGE_CEIL/RED_CEIL survive because they are
the storm-ladder FLOOD TIERS and they live inside the preserved lowland; PEAK_CAP and
the per-seed spikeMax normalization survive as the summit.
Shelf detail is forced off in continuous mode: the flat benches it de-slabbed no longer
exist, and painting noise on the climb now would pre-judge what erosion should carve.
Oracle, all hard checks passing:
- (a1) curve off is bit-identical to Phase 1's dump.
- (a2) staircase mode is bit-identical to TASK 01's dump — the control is provably the
control, not a re-derivation. (CurveBaselineTool is pinned to Staircase so the config
default moving to Continuous cannot drift it.)
- (d) lowlands bit-identical to the staircase over 3.6M cells, every continuous variant,
both seeds. The lifted_WRONG bookend fails it on 1.6M cells, as intended.
- (f) sea identity per CELL, not per count, including 67M cells at 8192.
The finding, measured and recorded in the batch scratch: the massif SHRANK. Land above
100 m goes 14.9% -> 4.8%, above 220 m 4.5% -> 0.6%. A feather sweep to the practical
floor recovers ~1.3 points, so this is structural, not a tuning miss: the staircase's
highland area was an artifact of the bench and plateau acting as magnets, and a curve
with no magnets preserves the raw distribution's bottom-heavy shape. "No terraces" and
"the same land up high" are not both available from curve work alone.
Exploration batch, not convergence. A tuning pass follows once a direction is picked.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCWNaDZPfTiAy3meGNGgqt
Presentation only. No generation file is touched — Topography, TerrainNoise,
IslandFalloff, Pass1Result, TerrainGenConfig and GenerationScale are all unchanged.
Batch naming, fixed in code. The prefix is the AUTHORING TASK number, not a running
counter: 04_review means "the batch task 04 authored", not "the fifth batch". It had
already drifted — tasks 02 and 03 produced 00_smoke through 05_trophy_10240 across
two tasks, so no folder name said which task made what. Now the task number is an
explicit argument to ToolingPaths.BatchRoot(taskNumber, descriptor), which composes
the prefix itself and REFUSES a descriptor that carries its own. All three tools
take it as ISLA_TASK. Verified: ISLA_BATCH=05_foo is refused with a stated reason
and exit 2, and creates no folder.
Also fixed: an exception out of _Ready does not stop Godot — it logs and the process
sits there with no main loop, so a misconfigured run HUNG rather than failing. A
hang looks like slow work, which is worse than a crash. The batch tools now catch,
print what was refused, and exit non-zero.
Grayscale mode: normalize a field to its own [min,max]. This is the one place
per-image normalization is correct — everywhere else anchors are fixed so images
compare, but here the point is to see one field at full contrast. The range is
printed and indexed so a shade reads back to a height. You cannot judge noise
through a palette: a ramp bends the distribution, a hillshade adds shape the data
does not have.
The wide Costa-Rica palette, and the reframing the developer asked for: the pretty
map is the WIDE GRADIENT RENDERED FLAT, and relief is no longer the hero. On raw
pass-1 noise a hillshade has nothing coherent to shade, so it renders fine fractal
bumpiness as fuzz that actively hides the elevation the colour is showing. Strong
hillshade is retained as diagnostic_relief and labelled a dev view — its bumpiness
is exaggerated slope, not extra terrain. Subtle relief is kept for comparison, with
the honest note that it still fuzzes until Phase 2 carves coherent landforms.
Legend: a colour bar with ticks drawn from the palette's own ramp, so it cannot
drift from the map beside it. Ticks are RELATIVE height and the image says NOT
METRES — the conversion is Phase 2's, and labelling it "m" would invent a fact in
the artefact a reader most trusts. Text comes from a 5x7 bitmap font written for
this, specifically so a legend does not drag in the SubViewport capture path.
Presentation only. Nothing here touches Topography, the noise, or any generation
constant; the renderer is handed a height field LOADED from a .f32 dump and has no
way to produce one, so a look change provably cannot move the terrain.
Hillshade: Horn 3x3, cell size 1, edges clamped (never wrapped — the Trench border
is a wall, not a seam). ZExaggeration is required rather than optional: measured
median land slope is 0.22 degrees unexaggerated, so the island shades as a flat
plane. Chosen from a measured slope table — zex 75 gives 16/28/37 deg at land
median/p90/p99, which reads as natural relief. It is a look dial; raw units are not
metres and no code may read it as if they were.
Palette: stops placed on the MEASURED height distribution, not spread linearly.
Land is bottom-heavy (median 0.456, p99 1.113, max 1.415 over 8.6M land columns
across four seeds), so a linear ramp would spend 99% of its range on 1% of the land
and the map would read as green with a few white dots. Bathymetry is compressed by
d/(d+0.35) so the shallow shelf gets the range and the featureless abyss flattens.
Anchors are absolute and fixed across the batch — a map coloured on its own min/max
cannot be compared with its neighbour, and comparison is the point of a batch.
Blend: the naive tint x hillshade darkens everything by 29% before any slope is
involved, because flat ground shades to sin(altitude). So shade is normalized by
its flat-ground value first — flat terrain keeps its true tint and only SLOPE moves
the colour — then shadows multiply while highlights screen toward white. That
asymmetry is the difference between a colour ramp and a map you would frame.
Relief fades to zero with depth below sea. Not only taste: the deep floor is the
Trench, a synthetic wall whose gradient is ~1.5x the p99 land gradient, so shading
it faithfully draws a bright rim around the map and lights the abyss with noise
mottle. The fade puts relief on the near-shore shelf where the bathymetry is real.
Three named looks (atlas / relief / dusk), gated like any other change and kept
deliberately few — iteration fatigue on a subjective gate is a real failure mode.
Each pair isolates one question.
HeightMapRenderer.cs is retired: its raw I/O moved to HeightField.cs and its ramp
to ReliefPalette.cs, so there is one palette everywhere and the generator's own
quick-look is coloured identically to a beauty render.
No biome words, no classification, no water. The 0.15 line is a colour boundary.