Improve generated river terrain

This commit is contained in:
Alexander Sellite 2026-08-24 20:21:49 -04:00
parent 8456093f8c
commit e6f46dcad9
21 changed files with 586 additions and 18 deletions

View file

@ -247,6 +247,7 @@ func _validate_generated_region(
assert(_placement_count(placements, "chunk_0034") == 1)
assert(_placement_count(placements, "chunk_0035") == 1)
assert(_placement_count(placements, "chunk_0036") == 1)
assert(_placement_count(placements, "chunk_0038") == 2)
assert(
_placement_count(placements, "chunk_0014")
== (2 if has_secondary_elevation else 0)
@ -285,10 +286,31 @@ func _validate_generated_region(
var river_edge_count := (
_placement_count(placements, "chunk_0024")
+ _placement_count(placements, "chunk_0025")
+ _placement_count(placements, "chunk_0038")
)
assert(river_coordinate_count >= 12)
assert(generator._river_feature_placements.size() == river_coordinate_count)
assert(river_edge_count == river_coordinate_count - 8)
var stone_bridge_specs: Array[Dictionary] = []
for spec: Dictionary in generator._river_feature_placements:
if spec.get("id", &"") == &"chunk_0038":
stone_bridge_specs.append(spec)
assert(stone_bridge_specs.size() == 2)
var first_bridge_coordinate := stone_bridge_specs[0].get(
"coordinate",
Vector2i(-1, -1),
) as Vector2i
var second_bridge_coordinate := stone_bridge_specs[1].get(
"coordinate",
Vector2i(-1, -1),
) as Vector2i
var bridge_delta := second_bridge_coordinate - first_bridge_coordinate
assert(absi(bridge_delta.x) + absi(bridge_delta.y) == 1)
assert(posmod(
int(stone_bridge_specs[1].get("turns", -1))
- int(stone_bridge_specs[0].get("turns", -1)),
4,
) == 2)
var river_source_coordinates := generator._river_source_coordinates(
generator._river_feature_origin,
generator._river_feature_footprint_size,
@ -446,6 +468,20 @@ func _validate_generated_region(
assert(fresh.surface_size.x <= 10.0 and fresh.surface_size.y <= 10.0)
assert(not fresh.visual_surface_enabled)
assert(not (fresh.get_node("VisualWater") as MeshInstance3D).visible)
var generated_bed := fresh.get_node_or_null(
"GeneratedBed",
) as MeshInstance3D
assert(generated_bed != null and generated_bed.mesh != null)
assert(
is_equal_approx(
generated_bed.global_position.y,
GeneratedWorldRegion.GENERATED_FRESH_WATER_BED_HEIGHT,
)
)
assert(
generated_bed.material_override != null
and generated_bed.material_override.resource_name == "dirt"
)
var fresh_recovery := (
fresh.get_node("RecoveryRegion") as PlayerWaterTrigger
)
@ -486,6 +522,16 @@ func _validate_generated_region(
)
var decorations := region.get_node("Decorations") as Node3D
var grass_prop_surface_triangles := region.get_spawn_surface_triangles(
[&"grass_lite"],
-INF,
0.35,
)
var sand_prop_surface_triangles := region.get_spawn_surface_triangles(
[&"sand"],
-INF,
0.35,
)
var anchors := region.get_node(
"GatherableAnchors/ReachableTreeTrunks"
) as GatherableAnchorSet3D
@ -540,6 +586,8 @@ func _validate_generated_region(
region,
child as Node3D,
definition,
grass_prop_surface_triangles,
sand_prop_surface_triangles,
)
if prop_id == &"prop_palm":
var cluster_id := int(
@ -1063,6 +1111,8 @@ func _validate_decoration_transform(
region: GeneratedWorldRegion,
prop: Node3D,
definition: TerrainPropDefinition,
grass_surface_triangles: Array[PackedVector3Array],
sand_surface_triangles: Array[PackedVector3Array],
) -> void:
assert(prop != null)
assert(prop.scale.is_equal_approx(Vector3.ONE))
@ -1142,6 +1192,43 @@ func _validate_decoration_transform(
],
],
)
var required_surface_triangles: Array[PackedVector3Array] = (
sand_surface_triangles
if definition.procedural_group == &"sand_tree"
else grass_surface_triangles
)
var required_surface_height := _triangle_surface_height_at(
prop.global_position,
required_surface_triangles,
)
assert(
required_surface_height > -INF
and absf(required_surface_height - prop.global_position.y) <= 0.01,
"%s (%s) is not rooted on its required terrain material at %s."
% [prop.name, definition.stable_id, prop.global_position],
)
func _triangle_surface_height_at(
position: Vector3,
triangles: Array[PackedVector3Array],
) -> float:
var highest := -INF
var segment_start := position + Vector3.UP * 100.0
var segment_end := position + Vector3.DOWN * 100.0
for triangle: PackedVector3Array in triangles:
if triangle.size() != 3:
continue
var hit: Variant = Geometry3D.segment_intersects_triangle(
segment_start,
segment_end,
triangle[0],
triangle[1],
triangle[2],
)
if hit is Vector3:
highest = maxf(highest, (hit as Vector3).y)
return highest
func _validate_tree_gatherable_anchors(