feat(tools): polyline Canyon and Escarpment primitives with outline noise
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
520a62863b
commit
aae9bcd301
2 files changed
+156
-1
No files matched your search
@@ -7,6 +7,8 @@ const TerrainHeightField = preload("res://game/domain/sites/terrain_height_field
|
||||
|
||||
## A primitive returning this from material_at leaves the slope rule in charge.
|
||||
const NO_MATERIAL := -1
|
||||
## Outline noise wavelength of about 20 m: visible irregularity, no cell-scale jitter.
|
||||
const EDGE_NOISE_FREQUENCY := 0.05
|
||||
|
||||
|
||||
class Primitive extends RefCounted:
|
||||
@@ -36,6 +38,52 @@ class Primitive extends RefCounted:
|
||||
var along := clampf((point - start).dot(segment) / length, 0.0, length)
|
||||
return smoothstep(0.0, taper_m, along) * smoothstep(0.0, taper_m, length - along)
|
||||
|
||||
static func path_length(path: PackedVector2Array) -> float:
|
||||
var total := 0.0
|
||||
for index in range(1, path.size()):
|
||||
total += path[index - 1].distance_to(path[index])
|
||||
return total
|
||||
|
||||
## Nearest point of a polyline: x = distance to the path, y = arc length of the nearest
|
||||
## point from the first vertex, z = +1 when the point lies to the right of the travel
|
||||
## direction (south of an eastbound path, with +X east and +Z south), -1 to the left.
|
||||
static func path_sample(point: Vector2, path: PackedVector2Array) -> Vector3:
|
||||
if path.size() == 1:
|
||||
return Vector3(point.distance_to(path[0]), 0.0, 1.0)
|
||||
var best := Vector3(INF, 0.0, 1.0)
|
||||
var arc_start := 0.0
|
||||
for index in range(1, path.size()):
|
||||
var start := path[index - 1]
|
||||
var end := path[index]
|
||||
var segment := end - start
|
||||
var length := segment.length()
|
||||
var t := 0.0
|
||||
if length > 0.0:
|
||||
t = clampf((point - start).dot(segment) / (length * length), 0.0, 1.0)
|
||||
var distance := point.distance_to(start + segment * t)
|
||||
if distance < best.x:
|
||||
var cross := segment.x * (point.y - start.y) - segment.y * (point.x - start.x)
|
||||
best = Vector3(distance, arc_start + t * length, 1.0 if cross >= 0.0 else -1.0)
|
||||
arc_start += length
|
||||
return best
|
||||
|
||||
## Deterministic low-frequency noise that roughens an outline; null when amplitude is 0.
|
||||
static func edge_noise(seed: int, amplitude_m: float) -> FastNoiseLite:
|
||||
if amplitude_m <= 0.0:
|
||||
return null
|
||||
var noise := FastNoiseLite.new()
|
||||
noise.noise_type = FastNoiseLite.TYPE_SIMPLEX_SMOOTH
|
||||
noise.seed = seed
|
||||
noise.frequency = EDGE_NOISE_FREQUENCY
|
||||
noise.fractal_type = FastNoiseLite.FRACTAL_FBM
|
||||
noise.fractal_octaves = 3
|
||||
return noise
|
||||
|
||||
static func edge_offset(noise: FastNoiseLite, amplitude_m: float, x_m: float, z_m: float) -> float:
|
||||
if noise == null:
|
||||
return 0.0
|
||||
return noise.get_noise_2d(x_m, z_m) * amplitude_m
|
||||
|
||||
|
||||
class SlopedPlane extends Primitive:
|
||||
var depth_m: float
|
||||
@@ -119,6 +167,82 @@ class Groove extends Primitive:
|
||||
return -depth_m * inside * end_taper(point, start, end, half_width_m)
|
||||
|
||||
|
||||
## Flat-floored trench along a polyline. The floor sits depth_m below the surroundings
|
||||
## wherever the (noise-perturbed) distance to the path is under half_width_m minus
|
||||
## wall_softness_m; the walls climb over 2 * wall_softness_m; the trench closes over one
|
||||
## half-width at each end.
|
||||
class Canyon extends Primitive:
|
||||
var path: PackedVector2Array
|
||||
var half_width_m: float
|
||||
var depth_m: float
|
||||
var wall_softness_m: float
|
||||
var edge_amplitude_m: float
|
||||
var _noise: FastNoiseLite
|
||||
var _length_m: float
|
||||
|
||||
func _init(
|
||||
p_path: PackedVector2Array,
|
||||
p_half_width_m: float,
|
||||
p_depth_m: float,
|
||||
p_wall_softness_m: float,
|
||||
p_edge_seed: int,
|
||||
p_edge_amplitude_m: float
|
||||
) -> void:
|
||||
path = p_path
|
||||
half_width_m = p_half_width_m
|
||||
depth_m = p_depth_m
|
||||
wall_softness_m = p_wall_softness_m
|
||||
edge_amplitude_m = p_edge_amplitude_m
|
||||
_noise = edge_noise(p_edge_seed, p_edge_amplitude_m)
|
||||
_length_m = path_length(p_path)
|
||||
|
||||
func elevation_delta(x_m: float, z_m: float) -> float:
|
||||
var sample := path_sample(Vector2(x_m, z_m), path)
|
||||
var distance := sample.x + edge_offset(_noise, edge_amplitude_m, x_m, z_m)
|
||||
var inside := 1.0 - smoothstep(
|
||||
half_width_m - wall_softness_m,
|
||||
half_width_m + wall_softness_m,
|
||||
distance
|
||||
)
|
||||
var taper := smoothstep(0.0, half_width_m, sample.y) * smoothstep(0.0, half_width_m, _length_m - sample.y)
|
||||
return -depth_m * inside * taper
|
||||
|
||||
|
||||
## Step along a polyline: the terrain drops by drop_m on the right of the travel direction
|
||||
## (south of an eastbound path), over 2 * softness_m, then keeps falling at slope_beyond.
|
||||
## The crest line is roughened by outline noise. Extend the path past the field edges:
|
||||
## beyond its ends the contour wraps around the end vertex.
|
||||
class Escarpment extends Primitive:
|
||||
var path: PackedVector2Array
|
||||
var drop_m: float
|
||||
var softness_m: float
|
||||
var slope_beyond: float
|
||||
var edge_amplitude_m: float
|
||||
var _noise: FastNoiseLite
|
||||
|
||||
func _init(
|
||||
p_path: PackedVector2Array,
|
||||
p_drop_m: float,
|
||||
p_softness_m: float,
|
||||
p_slope_beyond: float,
|
||||
p_edge_seed: int,
|
||||
p_edge_amplitude_m: float
|
||||
) -> void:
|
||||
path = p_path
|
||||
drop_m = p_drop_m
|
||||
softness_m = p_softness_m
|
||||
slope_beyond = p_slope_beyond
|
||||
edge_amplitude_m = p_edge_amplitude_m
|
||||
_noise = edge_noise(p_edge_seed, p_edge_amplitude_m)
|
||||
|
||||
func elevation_delta(x_m: float, z_m: float) -> float:
|
||||
var sample := path_sample(Vector2(x_m, z_m), path)
|
||||
var signed_distance := sample.x * sample.z + edge_offset(_noise, edge_amplitude_m, x_m, z_m)
|
||||
var step := drop_m * smoothstep(-softness_m, softness_m, signed_distance)
|
||||
var slope := maxf(signed_distance - softness_m, 0.0) * slope_beyond
|
||||
return -(step + slope)
|
||||
|
||||
|
||||
class Step extends Primitive:
|
||||
var point: Vector2
|
||||
var normal: Vector2
|
||||
|
||||
Reference in new issue
Block a user