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.
93 lines
4.7 KiB
C#
93 lines
4.7 KiB
C#
using Godot;
|
|
using IslaApocalypse.Core;
|
|
|
|
namespace IslaApocalypse.Tools
|
|
{
|
|
/// <summary>
|
|
/// ⭐⭐ THE CROWN JEWEL — the terrain noise configuration, ported and PINNED.
|
|
/// → D-050 ("port, don't re-derive"), `Design - Rewrite - Extraction Manifest.md`.
|
|
///
|
|
/// ═══ WHAT THE REFERENCE ACTUALLY SET, AND WHAT IT DID NOT ═══
|
|
///
|
|
/// The reference (MapGenerator._Ready ~:141-144) set exactly THREE properties:
|
|
///
|
|
/// _noise = new FastNoiseLite();
|
|
/// _noise.Seed = ConfigManager.WorldSeed == 0 ? (int)GD.Randi() : ConfigManager.WorldSeed;
|
|
/// _noise.NoiseType = FastNoiseLite.NoiseTypeEnum.Simplex;
|
|
/// _noise.Frequency = 0.004f / scaleFactor;
|
|
///
|
|
/// That is all four lines. There is no fifth. Fractal type, octaves, gain, lacunarity and
|
|
/// weighted-strength were NEVER SET anywhere in that codebase — so the island's entire fractal
|
|
/// character was Godot 4.7.1's `FastNoiseLite` CONSTRUCTOR DEFAULTS, and nothing recorded what
|
|
/// they were. (chat1/00 §2.1, §4.1.)
|
|
///
|
|
/// ═══ ⚠⚠ WHY EVERY VALUE IS PINNED EXPLICITLY BELOW ═══
|
|
///
|
|
/// Because a default is not a decision, and this project no longer rides one. The terrain must
|
|
/// not change when the engine version changes, and it must not change when this code is ported
|
|
/// off Godot's FastNoiseLite to a C++ or hand-rolled implementation — which D-049 explicitly
|
|
/// anticipates ("C++-ready seams... port path-by-path when settled and hot"). An unpinned
|
|
/// default would move the island silently, and nothing in the source would say what moved.
|
|
///
|
|
/// Measured on Godot 4.7.2.stable.mono (Tools/Scenes/NoiseDefaultsProbe.tscn):
|
|
/// FractalType Fbm · Octaves 5 · Gain 0.5 · Lacunarity 2.0 · WeightedStrength 0.0
|
|
/// These MATCH the Godot 4 documented defaults the reference rode on 4.7.1 — so pinning them
|
|
/// reproduces the reference look exactly, with no delta to reconcile.
|
|
///
|
|
/// ⚠ Note NoiseType is the one place the reference did NOT ride the default: the constructor
|
|
/// default is SimplexSmooth, and the reference explicitly chose Simplex. Different noise.
|
|
///
|
|
/// Run NoiseDefaultsProbe after any engine upgrade. It cannot change the terrain — this file
|
|
/// pins it — but it makes a drifting default visible instead of silent.
|
|
/// </summary>
|
|
public static class TerrainNoise
|
|
{
|
|
// ---- the pinned fractal character (was 4.7.1 constructor defaults) ----
|
|
public const FastNoiseLite.NoiseTypeEnum PinnedNoiseType = FastNoiseLite.NoiseTypeEnum.Simplex;
|
|
public const FastNoiseLite.FractalTypeEnum PinnedFractalType = FastNoiseLite.FractalTypeEnum.Fbm;
|
|
public const int PinnedOctaves = 5;
|
|
public const float PinnedGain = 0.5f;
|
|
public const float PinnedLacunarity = 2.0f;
|
|
public const float PinnedWeightedStrength = 0.0f;
|
|
|
|
/// <summary>
|
|
/// The base frequency, stated at the 1024 baseline. The reference wrote
|
|
/// <c>0.004f / scaleFactor</c>; here the division is <see cref="GenerationScale.NoiseFrequency"/>'s
|
|
/// job, so the literal never reaches a call site. → `Design - Tooling - Scaling Discipline.md`.
|
|
/// </summary>
|
|
public const float BaselineFrequency = 0.004f;
|
|
|
|
/// <summary>
|
|
/// Build the one terrain noise field. ⚠ ONE INSTANCE, deliberately.
|
|
///
|
|
/// The reference sampled the latitude wobble, the coastline edge roughness and the base
|
|
/// height from the SAME `_noise` object — three correlated slices of one field, at three
|
|
/// different coordinate transforms. That correlation is part of the look, so the port keeps
|
|
/// one instance rather than "tidying" it into three seeded fields.
|
|
/// </summary>
|
|
public static FastNoiseLite Create(int seed, GenerationScale scale)
|
|
{
|
|
var noise = new FastNoiseLite();
|
|
|
|
// --- ported verbatim from the reference ---
|
|
noise.Seed = seed;
|
|
noise.NoiseType = PinnedNoiseType;
|
|
noise.Frequency = scale.NoiseFrequency(BaselineFrequency); // = 0.004f / scaleFactor
|
|
|
|
// --- PINNED: the reference left these to the engine. We do not. ---
|
|
noise.FractalType = PinnedFractalType;
|
|
noise.FractalOctaves = PinnedOctaves;
|
|
noise.FractalGain = PinnedGain;
|
|
noise.FractalLacunarity = PinnedLacunarity;
|
|
noise.FractalWeightedStrength = PinnedWeightedStrength;
|
|
|
|
return noise;
|
|
}
|
|
|
|
/// <summary>The pinned configuration as one line, for a run header.</summary>
|
|
public static string Describe(int seed, GenerationScale scale) =>
|
|
$"Simplex · Fbm · octaves {PinnedOctaves} · gain {PinnedGain} · lacunarity {PinnedLacunarity} · " +
|
|
$"weighted {PinnedWeightedStrength} · freq {scale.NoiseFrequency(BaselineFrequency):G6} " +
|
|
$"(= {BaselineFrequency} / {scale.ScaleFactor:F3}) · seed {seed}";
|
|
}
|
|
}
|