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", false ) _check_ribbon_tracks_terrain( terrain, river_surface.get_node("UpperRiverRibbon") as MeshInstance3D, "Upper-stream water", true ) 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, is_upper: bool ) -> 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] var uvs: PackedVector2Array = arrays[Mesh.ARRAY_TEX_UV] _check(not vertices.is_empty(), "%s ribbon should contain vertices" % label) if vertices.is_empty() or uvs.size() != vertices.size(): failures.append("%s ribbon requires a complete coordinate profile" % label) return var start_z := ( JajceWatercourse.UPPER_STREAM_START_Z if is_upper else JajceWatercourse.DOWNSTREAM_START_Z ) var end_z := ( JajceWatercourse.UPPER_STREAM_END_Z if is_upper else JajceWatercourse.DOWNSTREAM_END_Z ) _check( ( absf(ribbon.to_global(vertices[0]).z - start_z) < 0.03 and absf(ribbon.to_global(vertices[-1]).z - end_z) < 0.03 ), "%s ribbon should span its entire authored stream length" % label ) var pool := ribbon.get_parent().get_node("PlungePool") as MeshInstance3D var pool_mesh := pool.mesh as PlaneMesh var pool_radii := pool_mesh.size * Vector2(pool.scale.x, pool.scale.z) * 0.5 var min_depth := INF var max_depth := -INF for vertex_index in vertices.size(): var world_vertex := ribbon.to_global(vertices[vertex_index]) var world_z := lerpf(start_z, end_z, uvs[vertex_index].y) var center_x := ( JajceWatercourse.upper_stream_center_x(world_z) if is_upper else JajceWatercourse.downstream_center_x(world_z) ) var half_width := ( JajceWatercourse.upper_stream_half_width(world_z) if is_upper else JajceWatercourse.downstream_half_width(world_z) ) if not is_upper: var pool_progress := (world_z - pool.global_position.z) / pool_radii.y if absf(pool_progress) < 1.0: half_width = maxf( half_width, pool_radii.x * sqrt(1.0 - pool_progress * pool_progress) ) var center_height := _terrain_height(terrain, center_x, world_z) var terrain_height := terrain.data.get_height(world_vertex) if ( not world_vertex.is_finite() or not is_finite(terrain_height) or not is_finite(center_height) ): failures.append("%s ribbon sampled outside Terrain3D" % label) return if absf(world_vertex.y - center_height - JajceWatercourse.WATER_SURFACE_OFFSET) > 0.03: failures.append("%s cross-section should follow its actual carved centerline" % label) return if ( absf(world_vertex.z - world_z) > 0.03 or absf(world_vertex.x - center_x) > half_width + 0.03 ): failures.append( "%s ribbon should stay inside the authored stream/pool footprint" % label ) return var depth := world_vertex.y - terrain_height min_depth = minf(min_depth, depth) max_depth = maxf(max_depth, depth) if depth < -0.03 or depth > JajceWatercourse.WATER_SURFACE_OFFSET + 0.03: failures.append("%s surface should remain shallow and above the channel bed" % label) return var across := uvs[vertex_index].x if is_zero_approx(across) or is_equal_approx(across, 1.0): var edge_distance := absf(world_vertex.x - center_x) if absf(edge_distance - half_width) > 0.03 and absf(depth) > 0.03: failures.append( "%s edges must reach the authored extent or the actual shoreline" % label ) return if ( (is_zero_approx(across) and world_vertex.x >= center_x) or (is_equal_approx(across, 1.0) and world_vertex.x <= center_x) ): failures.append( "%s water must retain its authored centerline inside both banks" % label ) return print("[TEST] %s depth bounds | min=%.3f max=%.3f" % [label, min_depth, max_depth]) 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)