Ports MapGenerator.GenerateTopography's pass-1 loop (reference ~:553-619 at tag
pre-rewrite-reference / ab78883) into Tools/. Working code and its tuned constants
carried over verbatim, read from source rather than re-derived from a design
summary (D-050). Six elements, in the reference's execution order — the order is
load-bearing: the sinker lands before the Pow (superlinear), the Trench after it
(a raw additive wall), preTrenchFalloff captured between them.
The fractal config is now PINNED. The reference set only NoiseType/Seed/Frequency,
so the island's entire fractal character was Godot 4.7.1 constructor defaults that
nothing recorded. Measured on 4.7.2 (NoiseDefaultsProbe): Fbm / 5 / 0.5 / 2.0 / 0.0
— identical to the 4.7.1 values, so pinning reproduces the look with no delta. The
terrain no longer depends on an engine default, and the probe makes a future drift
visible instead of silent.
Two deliberate departures from the reference's literals, both flagged in-code:
- the latitude noise offset is expressed in map widths, not the reference's raw
+1000 px, so a resize no longer samples a different slice of the noise field
(chat1/00 §4.2). Pinned to 1000/8192 so the 8K realization is unchanged.
- the `if (temperature < 0.65f)` spine gate is dropped as the provable no-op it
is — southernFade already reaches exactly 0 at 0.65. Bit-identical.
The reference's "temperature" is ported as a latitude field and deliberately NOT
named temperature: it is stage-1-local input to terrain geometry, and climate is
stage 3 (D-049 §2, D-056). The ±0.1 wobble is kept — it is what stops the spine's
southern terminus being a ruler-straight line.
Deferred to Phase 2 with the seam open: coast shelf and offshore islets (below-sea,
judged once water renders). Pass1Result exposes PreTrenchFalloff at their exact
consumption point, plus HMaxSeed for the seed-dependent curve.
Render is a direct Godot.Image, not the SubViewport capture path — that machinery
exists to composite vector overlays and is why --headless hangs; Phase 1 has none.
Hypsometric above 0.15, bathymetric below, fixed ramp anchors so seeds and ablation
rungs are comparable. No biome words anywhere.
Verified: land fraction 51.5% identical at 1024, 2048 and 10240 — the scaling
discipline holds across a 10x resize. Full-size 10240 pass: 36.3 s.
No curve, erosion, rivers, water, crater, biomes, roads, or mesher.
88 lines
3.6 KiB
C#
88 lines
3.6 KiB
C#
namespace IslaApocalypse.Tools
|
|
{
|
|
/// <summary>
|
|
/// Everything pass 1 produces. → <see cref="Topography"/>.
|
|
///
|
|
/// ⭐ THREE OF THESE FIELDS ARE THE PHASE-2 SEAM. The reference computed them in pass 1 and
|
|
/// consumed them in pass 2; exposing them now is what keeps the deferred ports clean, and it
|
|
/// costs nothing to carry.
|
|
/// </summary>
|
|
public sealed class Pass1Result
|
|
{
|
|
/// <summary>Map side in columns.</summary>
|
|
public readonly int MapSize;
|
|
|
|
/// <summary>The resolved seed this was generated with. Always positive, always recorded.</summary>
|
|
public readonly int Seed;
|
|
|
|
/// <summary>
|
|
/// The raw pass-1 height field, <c>[x, y]</c>. Pre-curve, pre-erosion, pre-crater.
|
|
/// The reference's <c>finalH</c> (~:619) written to <c>_heightMap</c> (~:666).
|
|
/// </summary>
|
|
public readonly float[,] Height;
|
|
|
|
/// <summary>
|
|
/// ⭐ PHASE-2 SEAM — the falloff value captured BEFORE the <c>Pow(·, 2.5f)</c> and BEFORE the
|
|
/// Trench wall (reference ~:591, <c>preTrenchFalloff</c>).
|
|
///
|
|
/// It is the "how far past the island body are we" number, uncontaminated by the power's
|
|
/// curvature and by the Trench's additive ramp. The DEFERRED offshore-islet layer reads it
|
|
/// as its distance mask (<c>OffshoreZoneWeight</c>), which is the only reason it is captured
|
|
/// mid-computation rather than recomputed.
|
|
///
|
|
/// ⚠ A port that "tidies" the falloff chain into one expression destroys this capture point,
|
|
/// and the loss would not show up until Phase 2's offshore mask moved. It is exposed here so
|
|
/// that cannot happen quietly.
|
|
/// </summary>
|
|
public readonly float[,] PreTrenchFalloff;
|
|
|
|
/// <summary>
|
|
/// ⭐ PHASE-2 SEAM — the wobbled latitude scalar, <c>[x, y]</c>.
|
|
///
|
|
/// ⚠ THIS IS NOT A CLIMATE TEMPERATURE MAP. See <see cref="Topography"/> for the full note.
|
|
/// It is a stage-1-local latitude field that terrain GEOMETRY reads. Phase 2 needs it if the
|
|
/// latitude sea-level model is ever switched on (<c>SeaLevelModel = "field"</c>); under the
|
|
/// shipped flat model it has no consumer beyond the spine.
|
|
/// </summary>
|
|
public readonly float[,] LatitudeField;
|
|
|
|
/// <summary>
|
|
/// ⭐ PHASE-2 SEAM — the maximum height over the whole map (reference <c>_hMaxSeed</c>, ~:665).
|
|
///
|
|
/// The reference's v2+ redistribution curve is SEED-DEPENDENT: its spike maps
|
|
/// <c>[t4, hMaxSeed]</c> onto the peak band, so pass 2 cannot start until pass 1 has scanned
|
|
/// every pixel. That is precisely why the pass-1/pass-2 boundary is a hard one and not an
|
|
/// interleave. Carried here so Phase 2 inherits it rather than rediscovering the dependency.
|
|
/// </summary>
|
|
public readonly float HMaxSeed;
|
|
|
|
/// <summary>The minimum height. Not a reference field — carried for the renderer's ramp and the report.</summary>
|
|
public readonly float HMinSeed;
|
|
|
|
/// <summary>Wall-clock milliseconds the pass took.</summary>
|
|
public readonly ulong ElapsedMs;
|
|
|
|
public Pass1Result(int mapSize, int seed, float[,] height, float[,] preTrenchFalloff,
|
|
float[,] latitudeField, float hMaxSeed, float hMinSeed, ulong elapsedMs)
|
|
{
|
|
MapSize = mapSize;
|
|
Seed = seed;
|
|
Height = height;
|
|
PreTrenchFalloff = preTrenchFalloff;
|
|
LatitudeField = latitudeField;
|
|
HMaxSeed = hMaxSeed;
|
|
HMinSeed = hMinSeed;
|
|
ElapsedMs = elapsedMs;
|
|
}
|
|
|
|
/// <summary>Fraction of the map at or above the sea threshold. A cheap shape sanity number.</summary>
|
|
public float LandFraction(float seaLevel)
|
|
{
|
|
long land = 0;
|
|
for (int x = 0; x < MapSize; x++)
|
|
for (int y = 0; y < MapSize; y++)
|
|
if (Height[x, y] >= seaLevel) land++;
|
|
return land / (float)((long)MapSize * MapSize);
|
|
}
|
|
}
|
|
}
|