Ports the reference's v5 height curve and shelf-detail passes onto Phase 1's shape and re-calibrates them against this repo's actual pass-1 distribution. This is the BASELINE the reshape gets judged against, not the reshape. Core (engine-free, D-060): - WorldScale — THE vertical yardstick. One metres/raw number (251), replacing the prototype's three duplicate M_PER_UNIT constants and ~20 bare literals. The chunk-height coupling it had there is recorded as a DEFERRED vault decision, not inherited. RawFromMetres divides, matching the reference bit-for-bit. - HeightCurve — the 7 bands, the frozen corner-fix blends, the per-seed spike normalization, the 24-corner monotonicity sweep that throws and refuses. Identity at and below sea, which everything downstream rests on. - CurveKnots / CurveAnchors — input knots (measured percentiles) and output anchors (storm ladder) split apart and both made parameters, so the anchors are A/B-able without editing source. The reference's shipped knots are kept beside the measured ones as the fidelity yardstick. - TerrainDetailPass — micro-relief skin plus the shelf-edge KNOT warp (which slides K3/K4/K5, not height — that is what keeps monotonicity structural). The crater exclusion is ported and inert until the carve lands. Tools: - Shaping — pass 2a, producing the two height fields. classify is bit-for-bit the raw pass-1 field; render is curved and detailed. Aliased when the curve is off, as the reference did. Pass1Result is left immutable so the oracle can compare. - LandHistogram — the calibration engine AND the diagnostic. The reference shipped six knot literals and threw the measuring instrument away; this rebuilds it. - ShapingOracle + CurveBaselineTool — four automatic checks before anything is looked at, and the batch that runs them. Measured, not assumed: - Knots re-measured over a 6-seed / 12.8M-sample pool. They differ from the reference's by at most 5.6 m of world height, against a 44.7 m per-seed spread — the pass-1 port is faithful. - Oracle all pass, including pass 1 bit-identical to Phase 1's own .f32 dump. - Band shares land on 60/13/10/5/8/3/1 to 0.00 pp. - Knots hold across map size: the 8K delta (5.8 m) sits inside seed noise. The finding the histograms deliver: 83% of land ends below 100 m and 96% below 220 m, with the median column at 13 m. That is the share targets doing exactly what they say, not a bug — and it is the developer's call, which is why nothing here reshapes it and the palette was deliberately left mis-fitted rather than recalibrated to disguise it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DCWNaDZPfTiAy3meGNGgqt
80 lines
4.1 KiB
C#
80 lines
4.1 KiB
C#
namespace IslaApocalypse.Core
|
||
{
|
||
/// <summary>
|
||
/// ⭐ THE WORLD'S VERTICAL YARDSTICK — the single metres↔raw-height conversion for the rewrite.
|
||
///
|
||
/// ═══ THE RULE ═══
|
||
///
|
||
/// THERE IS EXACTLY ONE METRES-PER-RAW-UNIT NUMBER, AND IT LIVES HERE.
|
||
/// No literal <c>251f</c> anywhere. No second <c>M_PER_UNIT</c>. Ever.
|
||
///
|
||
/// ═══ ⚠⚠ WHY THIS TYPE EXISTS — THE PROTOTYPE'S SCATTER ═══
|
||
///
|
||
/// The reference had this number in ONE derived place and then wrote it out by hand everywhere:
|
||
///
|
||
/// • <c>Core/Constants.cs</c>: <c>HEIGHT_SCALE = CHUNK_HEIGHT - 5</c> — the only DERIVED
|
||
/// definition, and the only one the runtime (<c>ServerChunkManager</c>) actually used.
|
||
/// • THREE independent hardcoded copies: <c>HydraulicErosion.M_PER_UNIT = 251f</c>,
|
||
/// <c>DrainageAnalysis.M_PER_UNIT = 251f</c>, <c>RiverCarvePass.M_PER_UNIT = 251f</c>.
|
||
/// • ~20 bare <c>251f</c> literals across <c>HeightCurve</c> and <c>MapGenerator</c>.
|
||
///
|
||
/// So the GENERATOR never used the derived constant at all. Retuning the chunk height would have
|
||
/// moved the runtime's yardstick and left every generated constant behind — silently, because a
|
||
/// literal does not throw. (chat2/00 report § C.7.)
|
||
///
|
||
/// ═══ ⚠⚠ WHERE 251 CAME FROM, AND WHAT IS STILL UNDECIDED ═══
|
||
///
|
||
/// In the prototype this equalled <c>CHUNK_HEIGHT - 5</c> = 256 - 5: a VOXEL-COLUMN BUDGET the
|
||
/// MESHER owned — the renderable height band, not a fact about the world. Every "420 m peak",
|
||
/// "220 m plateau" and "±3 m relief skin" in the generator was therefore denominated in a unit
|
||
/// defined by a rendering constant.
|
||
///
|
||
/// > ### ⚑ HERE IT IS A STANDALONE WORLD CONSTANT.
|
||
/// > **Whether it stays coupled to a future chunk height is a DEFERRED DESIGN DECISION for the
|
||
/// > vault, and this port does not settle it.** The value 251 is carried because the curve's
|
||
/// > anchors were tuned against it and D-050 says port, don't re-derive — not because a
|
||
/// > 256-voxel chunk has been decided on. If the vault later rules that the world's vertical
|
||
/// > scale is its own number, only this file changes.
|
||
///
|
||
/// The vault currently records only "roughly 251 m per raw height unit"
|
||
/// (`Design - Water - Storm Ladder.md`) and does not record the chunk-height derivation at all.
|
||
/// That gap is flagged for graduation, not fixed here — only master writes the vault.
|
||
/// </summary>
|
||
public static class WorldScale
|
||
{
|
||
/// <summary>
|
||
/// Metres of world height per raw height unit.
|
||
///
|
||
/// ⚠ Ported value, not a re-derivation: the redistribution curve's storm-ladder anchors
|
||
/// (420 m cap, 220 m plateau, 100 m bench) were calibrated against exactly this number, so
|
||
/// changing it reshapes the island. → D-050.
|
||
/// </summary>
|
||
public const float MetresPerRawUnit = 251f;
|
||
|
||
/// <summary>
|
||
/// The inverse, for callers that genuinely want a multiplier.
|
||
///
|
||
/// ⚠⚠ NOT INTERCHANGEABLE WITH <see cref="RawFromMetres"/>. In float32,
|
||
/// <c>420f * (1f/251f)</c> and <c>420f / 251f</c> are DIFFERENT NUMBERS — they differ in the
|
||
/// last bits. The reference wrote the division (<c>420f / 251f</c>), so every anchor this
|
||
/// repo derives must divide too, or the port is off by an ulp at every knot and no oracle
|
||
/// can prove fidelity. Use <see cref="RawFromMetres"/> unless you specifically need the
|
||
/// reciprocal.
|
||
/// </summary>
|
||
public const float RawUnitsPerMetre = 1f / MetresPerRawUnit;
|
||
|
||
/// <summary>
|
||
/// Metres → raw height units. **Divides**, bit-for-bit as the reference wrote it
|
||
/// (<c>420f / 251f</c>) — see the warning on <see cref="RawUnitsPerMetre"/>.
|
||
/// </summary>
|
||
public static float RawFromMetres(float metres) => metres / MetresPerRawUnit;
|
||
|
||
/// <summary>Raw height units → metres. The reference's <c>× 251f</c>.</summary>
|
||
public static float MetresFromRaw(float raw) => raw * MetresPerRawUnit;
|
||
|
||
/// <summary>One line for a run header. Print it; a yardstick worth having is worth stating.</summary>
|
||
public static string Describe() =>
|
||
$"1 raw height unit = {MetresPerRawUnit:F0} m (single source: Core/WorldScale; " +
|
||
"chunk-height coupling is a DEFERRED vault decision)";
|
||
}
|
||
}
|