namespace IslaApocalypse.Tools
{
///
/// The presentation dials — everything about how a heightmap is SHOWN, and nothing about how it
/// is MADE. → `Design - Rendering - Roughness Is Presentation.md`.
///
/// ⚠ Config-gated for the same reason every shaping change is: a look decision should be an A/B
/// pair, not a memory of last week's render, and a rejected look should cost a flipped default
/// rather than a reverted commit. → `Design - Tooling - Iteration and Batching.md`.
///
/// ⚠ Kept DELIBERATELY SMALL. This is a taste gate, and iteration fatigue on a subjective gate
/// is a real failure mode — a handful of variants the eye can actually judge beats a wall of
/// near-duplicates. Three named looks, chosen so each pair isolates one question.
///
public sealed class LookConfig
{
/// Short label, used in output filenames.
public string Name = "atlas";
/// Which hypsometric palette. → .
public ReliefPalette.Kind Palette = ReliefPalette.Kind.Atlas;
///
/// Vertical exaggeration for the relief normal. ⚠ A LOOK DIAL, NOT A PHYSICAL CLAIM — the
/// raw height units are not metres. See for the measured slope
/// table this was chosen from.
///
public float ZExaggeration = 75f;
/// Compass degrees clockwise from north. 315° (NW) is the cartographic standard.
public float LightAzimuth = 315f;
/// Degrees above the horizon. 45° standard; lower = longer, more dramatic shadows.
public float LightAltitude = 45f;
///
/// How much the relief moves the tint. 0 = flat hypsometric tint, no shading at all;
/// 1 = full relief. Above ~0.85 the tint starts washing out and the map reads as a
/// greyscale DEM wearing colour.
///
public float HillshadeStrength = 0.55f;
///
/// Relief multiplier BELOW sea level.
///
/// ⚠ Deliberately much weaker than on land, and the reason is the deferral: the seabed is
/// RAW — no coast shelf, no islets, both Phase-2 items — so it is steep and noisy in a way
/// the finished terrain will not be. Shading it at full strength drags the eye to the one
/// part of the map that is knowingly unfinished. Cartographic bathymetry is conventionally
/// shown flat or lightly shaded anyway, so the convention and the deferral agree.
///
public float SeaHillshadeFactor = 0.35f;
///
/// How much of the lit side is allowed to lift toward white. Highlights SCREEN rather than
/// multiply, so sunlit slopes brighten without blowing out to paper.
///
public float HighlightGain = 0.5f;
/// The colour boundary. NOT a water surface — no water is modelled (Phase 2).
public float SeaLevel = 0.15f;
///
/// The three looks offered to the taste gate. Each pair isolates one question:
/// atlas vs relief → how strong should the relief be? (same palette and light)
/// atlas vs dusk → which palette, and how low a sun? (comparable relief)
///
public static LookConfig[] Variants() => new[]
{
// Classic physical-atlas plate: tint-forward, relief present but polite.
new LookConfig
{
Name = "atlas", Palette = ReliefPalette.Kind.Atlas,
ZExaggeration = 75f, LightAzimuth = 315f, LightAltitude = 45f,
HillshadeStrength = 0.55f,
},
// Shape-forward: the same palette and light, harder relief. Isolates the relief dial.
new LookConfig
{
Name = "relief", Palette = ReliefPalette.Kind.Atlas,
ZExaggeration = 120f, LightAzimuth = 315f, LightAltitude = 45f,
HillshadeStrength = 0.80f,
},
// Warmer palette, lower sun, longer shadows. Isolates palette + light.
new LookConfig
{
Name = "dusk", Palette = ReliefPalette.Kind.Dusk,
ZExaggeration = 100f, LightAzimuth = 300f, LightAltitude = 35f,
HillshadeStrength = 0.70f,
},
};
public override string ToString() =>
$"{Name}: palette={Palette} zex={ZExaggeration:F0} light={LightAzimuth:F0}°/{LightAltitude:F0}° " +
$"strength={HillshadeStrength:F2} sea×{SeaHillshadeFactor:F2}";
}
}