195 lines
5.5 KiB
GDScript
195 lines
5.5 KiB
GDScript
class_name TerrainChunkEdgeProfile
|
|
extends RefCounted
|
|
|
|
var edge := TerrainChunkTopology.Edge.NORTH
|
|
var points := PackedVector2Array()
|
|
|
|
|
|
func _init(
|
|
profile_edge := TerrainChunkTopology.Edge.NORTH,
|
|
profile_points := PackedVector2Array(),
|
|
) -> void:
|
|
edge = profile_edge
|
|
points = profile_points
|
|
|
|
|
|
func matches(other: TerrainChunkEdgeProfile, tolerance: float) -> bool:
|
|
if other == null:
|
|
return false
|
|
var first_surface := _upper_surface_points()
|
|
var second_surface := other._upper_surface_points()
|
|
return _surfaces_match(first_surface, second_surface, tolerance)
|
|
|
|
|
|
## Compares only the visible raised portion of two edge profiles. Authored
|
|
## deep-water cliff pieces continue below ordinary ground level; when their
|
|
## lateral end meets an inland cliff, that lower extension is buried by the
|
|
## neighboring base terrain and must not create a false seam mismatch.
|
|
func matches_above_height(
|
|
other: TerrainChunkEdgeProfile,
|
|
minimum_height: float,
|
|
tolerance: float,
|
|
) -> bool:
|
|
if other == null:
|
|
return false
|
|
var first_surface := _points_above_height(
|
|
_upper_surface_points(),
|
|
minimum_height,
|
|
tolerance,
|
|
)
|
|
var second_surface := _points_above_height(
|
|
other._upper_surface_points(),
|
|
minimum_height,
|
|
tolerance,
|
|
)
|
|
return _surfaces_match(first_surface, second_surface, tolerance)
|
|
|
|
|
|
## Compares the visible terrain seam at and above a shared waterline while
|
|
## deliberately ignoring authored underwater wall depth. Terrain chunks may
|
|
## extend different distances below the water without creating a visible gap,
|
|
## but their banks still need to meet exactly at the surface.
|
|
func matches_at_or_above_height(
|
|
other: TerrainChunkEdgeProfile,
|
|
minimum_height: float,
|
|
tolerance: float,
|
|
) -> bool:
|
|
if other == null:
|
|
return false
|
|
var first_surface := _points_at_or_above_height(
|
|
_upper_surface_points(),
|
|
minimum_height,
|
|
tolerance,
|
|
)
|
|
var second_surface := _points_at_or_above_height(
|
|
other._upper_surface_points(),
|
|
minimum_height,
|
|
tolerance,
|
|
)
|
|
if first_surface.is_empty() or second_surface.is_empty():
|
|
return first_surface.is_empty() and second_surface.is_empty()
|
|
return _surfaces_match(first_surface, second_surface, tolerance)
|
|
|
|
|
|
func _surfaces_match(
|
|
first_surface: PackedVector2Array,
|
|
second_surface: PackedVector2Array,
|
|
tolerance: float,
|
|
) -> bool:
|
|
if first_surface.size() < 2 or second_surface.size() < 2:
|
|
return false
|
|
# Matching authoring control points can shift slightly along a steep rounded
|
|
# bank while describing the same seam. Accept the pair directly when every
|
|
# corresponding point remains inside the requested two-axis tolerance.
|
|
if first_surface.size() == second_surface.size():
|
|
var control_points_match := true
|
|
for index: int in first_surface.size():
|
|
if (
|
|
absf(first_surface[index].x - second_surface[index].x)
|
|
> tolerance
|
|
or absf(first_surface[index].y - second_surface[index].y)
|
|
> tolerance
|
|
):
|
|
control_points_match = false
|
|
break
|
|
if control_points_match:
|
|
return true
|
|
if (
|
|
absf(first_surface[0].x - second_surface[0].x) > tolerance
|
|
or absf(
|
|
first_surface[first_surface.size() - 1].x
|
|
- second_surface[second_surface.size() - 1].x
|
|
) > tolerance
|
|
):
|
|
return false
|
|
|
|
var sample_tangents := PackedFloat32Array()
|
|
for point: Vector2 in first_surface:
|
|
_append_unique_tangent(sample_tangents, point.x, tolerance)
|
|
for point: Vector2 in second_surface:
|
|
_append_unique_tangent(sample_tangents, point.x, tolerance)
|
|
sample_tangents.sort()
|
|
for tangent: float in sample_tangents:
|
|
var first_height := _sample_height(first_surface, tangent)
|
|
var second_height := _sample_height(second_surface, tangent)
|
|
if absf(first_height - second_height) > tolerance:
|
|
return false
|
|
return true
|
|
|
|
|
|
func _points_above_height(
|
|
surface: PackedVector2Array,
|
|
minimum_height: float,
|
|
tolerance: float,
|
|
) -> PackedVector2Array:
|
|
var result := PackedVector2Array()
|
|
for point: Vector2 in surface:
|
|
if point.y > minimum_height + tolerance:
|
|
result.append(point)
|
|
return result
|
|
|
|
|
|
func _points_at_or_above_height(
|
|
surface: PackedVector2Array,
|
|
minimum_height: float,
|
|
tolerance: float,
|
|
) -> PackedVector2Array:
|
|
var result := PackedVector2Array()
|
|
for point: Vector2 in surface:
|
|
if point.y >= minimum_height - tolerance:
|
|
result.append(point)
|
|
return result
|
|
|
|
|
|
func _upper_surface_points() -> PackedVector2Array:
|
|
var result := PackedVector2Array()
|
|
for point: Vector2 in points:
|
|
if (
|
|
not result.is_empty()
|
|
and is_equal_approx(result[result.size() - 1].x, point.x)
|
|
):
|
|
var previous := result[result.size() - 1]
|
|
previous.y = maxf(previous.y, point.y)
|
|
result[result.size() - 1] = previous
|
|
continue
|
|
result.append(point)
|
|
return result
|
|
|
|
|
|
func _append_unique_tangent(
|
|
tangents: PackedFloat32Array,
|
|
tangent: float,
|
|
tolerance: float,
|
|
) -> void:
|
|
for existing: float in tangents:
|
|
if absf(existing - tangent) <= tolerance:
|
|
return
|
|
tangents.append(tangent)
|
|
|
|
|
|
func _sample_height(surface: PackedVector2Array, tangent: float) -> float:
|
|
if tangent <= surface[0].x:
|
|
return surface[0].y
|
|
for index: int in range(1, surface.size()):
|
|
var right := surface[index]
|
|
if tangent > right.x:
|
|
continue
|
|
var left := surface[index - 1]
|
|
var span := right.x - left.x
|
|
if is_zero_approx(span):
|
|
return maxf(left.y, right.y)
|
|
return lerpf(left.y, right.y, (tangent - left.x) / span)
|
|
return surface[surface.size() - 1].y
|
|
|
|
|
|
func signature(quantization: float) -> String:
|
|
var tokens := PackedStringArray()
|
|
for point: Vector2 in points:
|
|
tokens.append(
|
|
"%d:%d"
|
|
% [
|
|
roundi(point.x / quantization),
|
|
roundi(point.y / quantization),
|
|
]
|
|
)
|
|
return ",".join(tokens)
|