feat: shelf-edge variation -- the red-line scalloping (terrain-water task 10)
PASS B, replacing the reverted incision. The developer's sketch asked for
the shelf/riser BOUNDARY to be organic, not for water: notches, coves and
small peninsulas where a flat shelf meets its riser, instead of the clean
oval contour the curve produces.
Mechanism (the task's preferred "boundary warp", in its most
monotonic-safe form): every shelf/riser boundary is the contour where the
raw height crosses K3, K4 or K5, so the boundary is warped by SLIDING
THOSE THREE KNOTS per column -- edgeShift = simplex(resolvedSeed + 7507,
12 per island width) x ShelfEdgeVariation. The contours then wander in and
out of the terrain instead of tracing an iso-height line. Noise-warped by
construction, so there is no grid direction for an artifact to line up on
-- the failure mode of the thing this replaces.
Why slide knots rather than perturb a weight or the input height:
monotonicity becomes structural instead of conditional. The curve is
strictly monotonic for ANY ordered knot set, so no derivative bound, no
amplitude-vs-feather-width tuning, no way for a dial to invert a column.
MaxEdgeShift keeps the set ordered (half the smallest margin to a fixed
knot = 12.3 m of input height for the v5 knots); the config dial is
clamped to it, loudly. AssertMonotonic now sweeps 24 corners -- the 8
modulation extremes x {-max, 0, +max} shift -- and checks the bound first.
K1, K2 and K6 never move, which buys the guarantees exactly rather than
statistically: below K2 and above K6 a warped column is bit-identical to
an unwarped one, so the red ceiling still floors every shelf edge (storm
ladder safe), the 420 m cap still caps, and the toe and summit spikes are
untouched. The block slides rigidly, so the bench and mid-riser keep their
exact widths -- shelf interiors stay flat, riser interiors keep their
profile, and only the foothill riser and plateau stretch to absorb it.
The micro-relief mask takes the same shift, so pass A's skin follows the
shelf wherever pass B moved its edge.
Dial ShelfEdgeVariation (default 5 m of INPUT height -- a boundary
displacement, not an elevation change) under the existing TerrainDetail
gate; both passes stay one judged unit. TDTL v2 body records relief and
edge amp/frequency/seed-offset plus the applied clamp bound; writer,
parser and harness follow. No blueprint was written between the revert
and this commit, so v2 only ever means relief + edge warp on disk.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3b6d166458
commit
492b56a87c
7 changed files with 182 additions and 69 deletions
|
|
@ -8,16 +8,19 @@ using Godot;
|
|||
/// default 3 m) weighted by shelf-ness, so the compressed shelves get their
|
||||
/// rolling texture back while risers and peaks stay untouched.
|
||||
///
|
||||
/// Output-height only; the classify map never sees it.
|
||||
/// PASS B — shelf-edge variation: a per-column shift of the shelf/riser KNOT
|
||||
/// BLOCK (K3/K4/K5) by a low-frequency noise field, so the boundary where a
|
||||
/// shelf meets its riser wanders in and out instead of tracing a clean height
|
||||
/// contour — organic notches, coves and peninsulas at the shelf edge.
|
||||
///
|
||||
/// NOTE — what this file deliberately no longer contains: the task-10 draft's D8
|
||||
/// steepest-descent flow routing, accumulation and drainage incision. It shipped,
|
||||
/// and it produced the canonical grid artifact — thousands of straight,
|
||||
/// disconnected, pooling scratches running along the eight D8 neighbour
|
||||
/// directions, because per-cell steepest descent on a regular grid can only ever
|
||||
/// route along those eight headings. It is reverted whole. Rivers and erosion are
|
||||
/// Phase C work: a hydraulic-erosion pass over FINAL terrain, not a routing carve
|
||||
/// on a grid mid-pipeline.
|
||||
/// Both are output-height only; the classify map never sees either of them.
|
||||
///
|
||||
/// NOTE — what this file deliberately does NOT contain: the task-10 draft's D8
|
||||
/// flow routing / accumulation / drainage incision. It shipped, produced the
|
||||
/// canonical grid artifact (thousands of straight, disconnected, pooling
|
||||
/// scratches along the D8 neighbour directions) and was reverted whole. Rivers
|
||||
/// and erosion are Phase C work — a hydraulic-erosion pass over FINAL terrain,
|
||||
/// not a per-cell steepest-descent carve on a grid.
|
||||
/// </summary>
|
||||
public static class TerrainDetailPass
|
||||
{
|
||||
|
|
@ -29,14 +32,38 @@ public static class TerrainDetailPass
|
|||
public const float RELIEF_FREQ_ISLANDS = 40f; // ~40 undulations per island width (~200 m features)
|
||||
public const int RELIEF_SEED_OFFSET = 7409;
|
||||
|
||||
// Pass B — shelf-edge variation. The amplitude is stated in metres of INPUT
|
||||
// height (raw × 251): it is how far, in raw-height terms, a shelf boundary
|
||||
// contour is displaced — not an output elevation change. Lateral wander on
|
||||
// the map is that displacement divided by the local raw gradient.
|
||||
public const float EDGE_AMP_DEFAULT_M = 5f; // config dial: ShelfEdgeVariation (metres of input height)
|
||||
public const float EDGE_FREQ_ISLANDS = 12f; // ~12 undulations per island width — a handful of notches
|
||||
public const int EDGE_SEED_OFFSET = 7507; // per shelf perimeter, not hundreds of teeth
|
||||
public const float EDGE_SAFETY_FRACTION = 0.5f; // shift ≤ half the smallest margin to a fixed knot
|
||||
|
||||
/// <summary>
|
||||
/// The largest per-column knot shift this preset can take while keeping the
|
||||
/// knot set strictly ordered (K2 < K3+d, K5+d < K6) with margin to spare.
|
||||
/// K1/K2/K6 never move, so the toe, the orange/red bands and the summit spike
|
||||
/// are bit-identical whatever the warp does — which is what makes the
|
||||
/// red-ceiling and peak-height guarantees exact rather than statistical.
|
||||
/// </summary>
|
||||
public static float MaxEdgeShift(CurveKnots k)
|
||||
{
|
||||
return EDGE_SAFETY_FRACTION * Mathf.Min(k.K3 - k.K2, k.K6 - k.K5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shelf-ness weight from the RAW input height: 1 mid-shelf, feathering to 0
|
||||
/// through the risers (feather extends 30 % of the band half-width past each
|
||||
/// shelf edge). Covers both shelves.
|
||||
/// shelf edge). Covers both shelves. <paramref name="edgeShift"/> is the same
|
||||
/// per-column warp the curve is evaluated with, so the micro-relief skin
|
||||
/// follows the shelf wherever pass B has moved its boundary.
|
||||
/// </summary>
|
||||
public static float ShelfWeight(float raw, CurveKnots k)
|
||||
public static float ShelfWeight(float raw, CurveKnots k, float edgeShift)
|
||||
{
|
||||
return Mathf.Max(BandBump(raw, k.K3, k.K4), BandBump(raw, k.K5, k.K6));
|
||||
return Mathf.Max(BandBump(raw, k.K3 + edgeShift, k.K4 + edgeShift),
|
||||
BandBump(raw, k.K5 + edgeShift, k.K6));
|
||||
}
|
||||
|
||||
private static float BandBump(float h, float lo, float hi)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue