extends SceneTree const JajceWatercourse := preload("res://world/jajce/JajceWatercourse.gd") var failures: Array[String] = [] func _initialize() -> void: call_deferred("_run") func _run() -> void: var camera := Camera3D.new() camera.current = true root.add_child(camera) var world: Node3D = load("res://world/jajce/JajceWorld.tscn").instantiate() root.add_child(world) await process_frame for _frame in 4: await physics_frame var terrain := world.get_node("TerrainRoot/Terrain3D") as Terrain3D _check(terrain != null, "Jajce watercourse validation requires Terrain3D") if terrain == null: _finish() return var shelf_z := -36.0 var shelf_center_x := JajceWatercourse.upper_stream_center_x(shelf_z) var shelf_half_width := JajceWatercourse.upper_stream_half_width(shelf_z) var shelf_stream_height := _terrain_height(terrain, shelf_center_x, shelf_z) var shelf_bank_height := _terrain_height( terrain, shelf_center_x + shelf_half_width + 4.0, shelf_z ) var plunge_height := _terrain_height( terrain, JajceWatercourse.PLUNGE_POOL_CENTER.x, JajceWatercourse.PLUNGE_POOL_CENTER.y ) _check(shelf_stream_height >= 10.8, "Upper stream should remain on the raised waterfall shelf") _check( shelf_bank_height - shelf_stream_height >= 0.45, "Upper stream should be visibly carved below its shelf bank" ) _check( shelf_stream_height - plunge_height >= 10.0, "Waterfall shelf should create a readable drop into the plunge basin" ) var downstream_samples := PackedFloat32Array([-10.0, 12.0, 34.0]) for world_z in downstream_samples: var center_x := JajceWatercourse.downstream_center_x(world_z) var half_width := JajceWatercourse.downstream_half_width(world_z) var channel_height := _terrain_height(terrain, center_x, world_z) var left_bank_height := _terrain_height(terrain, center_x - half_width - 5.0, world_z) var right_bank_height := _terrain_height(terrain, center_x + half_width + 5.0, world_z) _check( channel_height <= JajceWatercourse.downstream_bed_height(world_z) + 0.08, "Downstream bed should follow its authored profile at z=%.1f" % world_z ) _check( maxf(left_bank_height, right_bank_height) - channel_height >= 0.45, "Downstream channel should read below a natural bank at z=%.1f" % world_z ) var river_surface := world.get_node("WaterRoot/RiverSurface") as Node3D _check_ribbon_tracks_terrain( terrain, river_surface.get_node("RiverRibbon") as MeshInstance3D, "Downstream water" ) _check_ribbon_tracks_terrain( terrain, river_surface.get_node("UpperRiverRibbon") as MeshInstance3D, "Upper-stream water" ) print( ( "[TEST] Jajce watercourse samples | shelf=%.2f bank=%.2f pool=%.2f" % [shelf_stream_height, shelf_bank_height, plunge_height] ) ) _finish() func _terrain_height(terrain: Terrain3D, world_x: float, world_z: float) -> float: return terrain.data.get_height(Vector3(world_x, 0.0, world_z)) func _check_ribbon_tracks_terrain( terrain: Terrain3D, ribbon: MeshInstance3D, label: String ) -> void: _check(ribbon != null and ribbon.mesh is ArrayMesh, "%s ribbon should be generated" % label) if ribbon == null or not ribbon.mesh is ArrayMesh: return var arrays := ribbon.mesh.surface_get_arrays(0) var vertices: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX] _check(not vertices.is_empty(), "%s ribbon should contain vertices" % label) for vertex_index in range(0, vertices.size(), maxi(1, vertices.size() / 12)): var world_vertex := ribbon.to_global(vertices[vertex_index]) var terrain_height := terrain.data.get_height(world_vertex) if is_nan(terrain_height): failures.append("%s ribbon sampled outside Terrain3D" % label) return if absf(world_vertex.y - terrain_height - JajceWatercourse.WATER_SURFACE_OFFSET) > 0.03: failures.append("%s ribbon should hug the carved terrain" % label) return func _check(condition: bool, message: String) -> void: if not condition: failures.append(message) func _finish() -> void: if failures.is_empty(): print("[TEST] Jajce watercourse terrain passed") quit(0) return for failure in failures: push_error("[TEST] " + failure) quit(1)