fix: detail must yield to the crater carve (terrain-water task 10)
Batch 10_detail_edge measured 532 new below-sea pixels on seed 1158286446 and 44 on 72563200 -- exported terrain under the sea scalar that the water grid (classify-driven, and correctly identical) calls dry. Located: every one of them strictly inside the crater's physical carve radius, 201-323 px out of 640. Mechanism, confirmed against the data: detail moves a column's PRE-carve height, and the carve is Lerp(curvedH, target, t). At t ~ 0.45-0.62 a pre-carve drop of up to 8.4 m (relief plus the band-shift the warp implies) still passes 0.02-3.53 m through, which is enough to push a column sitting 0-3.45 m above the sea inside the bowl under it. The crater carve is supposed to be the FINAL authority on its own terrain, so detail now yields to it: CraterDetailWeight is 0 inside 0.80 x CraterRadius (exactly the carve's own radius) and feathers to full by 1.05 x, scaling both the edge shift and the relief skin. Inside the carve, B is bit-identical to A by construction, so the count is zero rather than small. The reverted incision pass carried the same exclusion for the same reason -- the principle outlived the pass that motivated it. Re-running the 6 B_detail generations; A_off and the continuity run are untouched by a detail-only change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
af1a6d912b
commit
59f49ac8a2
2 changed files with 34 additions and 5 deletions
|
|
@ -519,6 +519,7 @@ public partial class MapGenerator : TextureRect
|
|||
float raw = _heightMap[x, y];
|
||||
float classifyH = raw;
|
||||
float curvedH;
|
||||
float distToCrater = new Vector2(x, y).DistanceTo(_impactCenter);
|
||||
if (_curveOn)
|
||||
{
|
||||
// v4: per-column shelf modulation — anchors and strength from the
|
||||
|
|
@ -528,14 +529,20 @@ public partial class MapGenerator : TextureRect
|
|||
float plateauLo = HeightCurve.PLATEAU_BASE + _plateauNoise.GetNoise2D(x, y) * HeightCurve.PLATEAU_AMP;
|
||||
float shelfSpan = HeightCurve.ShelfSpan((_strengthNoise.GetNoise2D(x, y) + 1f) * 0.5f);
|
||||
|
||||
float edgeShift = _detailOn ? _edgeNoise.GetNoise2D(x, y) * _edgeAmpRaw : 0f;
|
||||
// Detail yields to the crater: zero inside the carve, feathered just
|
||||
// outside it, so the carve stays the final authority on its terrain.
|
||||
float wCrater = _detailOn
|
||||
? TerrainDetailPass.CraterDetailWeight(distToCrater, _impactRadius)
|
||||
: 0f;
|
||||
|
||||
float edgeShift = _detailOn ? _edgeNoise.GetNoise2D(x, y) * _edgeAmpRaw * wCrater : 0f;
|
||||
curvedH = HeightCurve.Apply(raw, _hMaxSeed, benchLo, shelfSpan, plateauLo, shelfSpan, _curveKnots, edgeShift);
|
||||
|
||||
if (_detailOn)
|
||||
if (_detailOn && wCrater > 0f)
|
||||
{
|
||||
float wShelf = TerrainDetailPass.ShelfWeight(raw, _curveKnots, edgeShift);
|
||||
if (wShelf > 0f)
|
||||
curvedH += _reliefNoise.GetNoise2D(x, y) * reliefAmpRaw * wShelf;
|
||||
curvedH += _reliefNoise.GetNoise2D(x, y) * reliefAmpRaw * wShelf * wCrater;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -546,8 +553,6 @@ public partial class MapGenerator : TextureRect
|
|||
// --- 5. CARVE THE CRATER (The Flooded Bay & Landbridge Fix!) ---
|
||||
// Both maps are carved from LOCALS and written once, so the curve-off
|
||||
// aliasing (classify and height are the same array) cannot double-carve.
|
||||
float distToCrater = new Vector2(x, y).DistanceTo(_impactCenter);
|
||||
|
||||
// We only carve the physical hole at 80% of the radius to guarantee a landbridge!
|
||||
if (distToCrater < physicalCraterRadius)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -52,6 +52,30 @@ public static class TerrainDetailPass
|
|||
// ordering follows from it.
|
||||
public const float EDGE_SAFETY_FRACTION = 2f / 3f;
|
||||
|
||||
// The crater carve is the final authority on its own terrain. Detail is masked
|
||||
// out inside the physical carve radius (0.80 × CraterRadius — exactly where the
|
||||
// carve applies) and feathers to full by 1.05 ×. Without this, detail moves a
|
||||
// column's PRE-carve height, the carve's Lerp passes a fraction of that through,
|
||||
// and columns sitting a metre or two above the sea inside the bowl get pushed
|
||||
// under it — 532 px on seed 1158286446 in the first batch, terrain below the sea
|
||||
// scalar that the (classify-driven, and correctly unchanged) water grid calls dry.
|
||||
public const float CRATER_DETAIL_EXCL_FACTOR = 0.80f;
|
||||
public const float CRATER_DETAIL_FEATHER_FACTOR = 1.05f;
|
||||
|
||||
/// <summary>
|
||||
/// Detail weight from distance to the impact centre: 0 inside the carve, 1 well
|
||||
/// outside it, linear between. <paramref name="craterRadius"/> is the configured
|
||||
/// CraterRadius (the carve itself uses 0.80 × of it).
|
||||
/// </summary>
|
||||
public static float CraterDetailWeight(float distToCrater, float craterRadius)
|
||||
{
|
||||
float excl = craterRadius * CRATER_DETAIL_EXCL_FACTOR;
|
||||
if (distToCrater <= excl) return 0f;
|
||||
float feather = craterRadius * CRATER_DETAIL_FEATHER_FACTOR;
|
||||
if (distToCrater >= feather) return 1f;
|
||||
return (distToCrater - excl) / (feather - excl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The largest per-column knot shift this preset allows: bounded by the band
|
||||
/// squeeze above, which also keeps the knot set strictly ordered
|
||||
|
|
|
|||
Loading…
Reference in a new issue