feat(sites): strata on the Banc du Vigilant drop-off and multi-scale seabed relief

Strata now take a world rectangle, so the 4 m ledges cover the whole break
and talus south of z = 118 m without touching la Saignée or the landmarks.
Roughness gains octave and fractal-type parameters; a SlopeRoughness
modifier roughens rock faces only. Both sites are re-baked with hummocks
from 250 m down to 5 m. The secondary item, the first debris piece and one
safe-route waypoint move a few metres to keep their contracts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
OlivierandClaude Fable 5.1 committed 2026-10-06 22:50:21 +02:00
1 parent 4af014288b
commit f22a5e69aa
21 files changed
+247 -43

No files matched your search

+54 -12
View File
@@ -410,18 +410,28 @@ class Ripples extends Primitive:
return amplitude_m * sin(phase) * falloff
## Seeded fractal noise added everywhere. frequency is 1 / wavelength of the largest
## octave; each further octave halves the wavelength and the amplitude, so a low
## frequency with five octaves gives hummocks, swales and ripple-scale detail at once.
## FRACTAL_RIDGED makes sharp crests (rock), FRACTAL_FBM rolling ground (sediment).
class Roughness extends Primitive:
var amplitude_m: float
var _noise: FastNoiseLite
func _init(p_seed: int, p_frequency: float, p_amplitude_m: float) -> void:
func _init(
p_seed: int,
p_frequency: float,
p_amplitude_m: float,
p_octaves: int = 3,
p_fractal_type: FastNoiseLite.FractalType = FastNoiseLite.FRACTAL_FBM
) -> void:
amplitude_m = p_amplitude_m
_noise = FastNoiseLite.new()
_noise.noise_type = FastNoiseLite.TYPE_SIMPLEX_SMOOTH
_noise.seed = p_seed
_noise.frequency = p_frequency
_noise.fractal_type = FastNoiseLite.FRACTAL_FBM
_noise.fractal_octaves = 3
_noise.fractal_type = p_fractal_type
_noise.fractal_octaves = p_octaves
func elevation_delta(x_m: float, z_m: float) -> float:
return _noise.get_noise_2d(x_m, z_m) * amplitude_m
@@ -437,12 +447,13 @@ class Modifier extends RefCounted:
## Quantises steep ground into horizontal ledges layer_height_m apart: the stratified
## walls of a canyon or cliff. Flat floors and summits are below minimum_slope_degrees and
## stay untouched; the effect is full from full_slope_degrees. sharpness 0 leaves the
## field unchanged, 1 cuts hard steps. radius_m 0 applies everywhere.
## field unchanged, 1 cuts hard steps. region_m is a world-space (x, z) rectangle with a
## hard edge; an empty rectangle applies everywhere. Ledges need two to three cells of
## wall per layer to be resolved: at 1.5 m per cell, 4 m layers on a 50 degree wall.
class Strata extends Modifier:
var layer_height_m: float
var sharpness: float
var centre: Vector2
var radius_m: float
var region_m: Rect2
var _minimum_tangent: float
var _full_tangent: float
@@ -451,21 +462,19 @@ class Strata extends Modifier:
p_sharpness: float,
p_minimum_slope_degrees: float,
p_full_slope_degrees: float,
p_centre: Vector2,
p_radius_m: float
p_region_m: Rect2
) -> void:
assert(p_layer_height_m > 0.0, "strata need a positive layer height")
layer_height_m = p_layer_height_m
sharpness = clampf(p_sharpness, 0.0, 0.999)
centre = p_centre
radius_m = p_radius_m
region_m = p_region_m
_minimum_tangent = tan(deg_to_rad(p_minimum_slope_degrees))
_full_tangent = tan(deg_to_rad(p_full_slope_degrees))
func modify(x_m: float, z_m: float, elevation_m: float, gradient: Vector2) -> float:
if region_m.has_area() and not region_m.has_point(Vector2(x_m, z_m)):
return elevation_m
var weight := smoothstep(_minimum_tangent, _full_tangent, gradient.length())
if radius_m > 0.0:
weight *= 1.0 - smoothstep(radius_m * 0.8, radius_m, Vector2(x_m, z_m).distance_to(centre))
if weight <= 0.0:
return elevation_m
var layer := floorf(elevation_m / layer_height_m)
@@ -474,6 +483,39 @@ class Strata extends Modifier:
return lerpf(elevation_m, (layer + sharpened) * layer_height_m, weight)
## Seeded noise weighted by the local slope: rock faces and ridge flanks get crumbly
## while flat sand stays smooth. Zero below minimum_slope_degrees, full amplitude from
## full_slope_degrees.
class SlopeRoughness extends Modifier:
var amplitude_m: float
var _noise: FastNoiseLite
var _minimum_tangent: float
var _full_tangent: float
func _init(
p_seed: int,
p_frequency: float,
p_amplitude_m: float,
p_minimum_slope_degrees: float,
p_full_slope_degrees: float
) -> void:
amplitude_m = p_amplitude_m
_noise = FastNoiseLite.new()
_noise.noise_type = FastNoiseLite.TYPE_SIMPLEX_SMOOTH
_noise.seed = p_seed
_noise.frequency = p_frequency
_noise.fractal_type = FastNoiseLite.FRACTAL_RIDGED
_noise.fractal_octaves = 3
_minimum_tangent = tan(deg_to_rad(p_minimum_slope_degrees))
_full_tangent = tan(deg_to_rad(p_full_slope_degrees))
func modify(x_m: float, z_m: float, elevation_m: float, gradient: Vector2) -> float:
var weight := smoothstep(_minimum_tangent, _full_tangent, gradient.length())
if weight <= 0.0:
return elevation_m
return elevation_m + _noise.get_noise_2d(x_m, z_m) * amplitude_m * weight
class MaterialPatch extends RefCounted:
var centre: Vector2
var radius_m: float