rnd(M03): add rendered ground query facade

This commit is contained in:
2026-07-16 00:49:28 +04:00
parent 6117e5282e
commit b697a896e8
12 changed files with 416 additions and 158 deletions
+9 -134
View File
@@ -5,9 +5,7 @@ extends SceneTree
## [--wait 3.0] [--output user://render_terrain_height/report.json]
const MANIFEST_PATH := "res://src/tools/render_baseline_manifest.json"
const COORDINATE_MAPPER_SCRIPT := preload("res://src/domain/coordinates/coordinate_mapper.gd")
const GODOT_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/godot_world_position.gd")
const RAY_HEIGHT := 5000.0
func _initialize() -> void:
@@ -61,7 +59,15 @@ func _run_async() -> void:
camera.global_position = camera_position
render_facade.call("refresh_streaming_focus", true)
await create_timer(maxf(0.1, wait_seconds)).timeout
var terrain_sample := _sample_terrain(world, camera_position)
var typed_camera_position = GODOT_WORLD_POSITION_SCRIPT.new(
camera_position.x,
camera_position.y,
camera_position.z
)
var terrain_sample: Dictionary = render_facade.call(
"renderer_ground_query_snapshot",
typed_camera_position
)
terrain_sample["name"] = checkpoint.get("name", "checkpoint")
terrain_sample["camera_y"] = camera_position.y
if terrain_sample.has("terrain_height"):
@@ -94,137 +100,6 @@ func _run_async() -> void:
quit(1 if failed else 0)
func _sample_terrain(world: Node3D, world_position: Vector3) -> Dictionary:
var tile_coordinate := _adt_tile_vector2i(world_position)
var tile_states: Dictionary = world.get("_tile_states")
var highest_terrain_height := -INF
var intersected_tile_key := ""
var ready_mesh_count := 0
for tile_y in range(tile_coordinate.y - 1, tile_coordinate.y + 2):
for tile_x in range(tile_coordinate.x - 1, tile_coordinate.x + 2):
var tile_key := "%d_%d" % [tile_x, tile_y]
if not tile_states.has(tile_key):
continue
var state: Dictionary = tile_states[tile_key]
var terrain_height_variant = _intersect_terrain_state(state, world_position)
if terrain_height_variant == null:
continue
ready_mesh_count += 1
var terrain_height := float(terrain_height_variant)
if terrain_height > highest_terrain_height:
highest_terrain_height = terrain_height
intersected_tile_key = tile_key
if not is_finite(highest_terrain_height):
var missing_result := {
"status": "no_intersection" if ready_mesh_count > 0 else "mesh_not_ready",
"tile": "%d_%d" % [tile_coordinate.x, tile_coordinate.y],
}
missing_result.merge(_tile_runtime_diagnostic(world, String(missing_result.tile), world_position), true)
if bool(missing_result.get("quality_mesh_present", false)) or bool(missing_result.get("tile_lod_mesh_present", false)):
missing_result["status"] = "no_intersection"
missing_result.merge(_nearest_terrain_sample(world, world_position), true)
if missing_result.get("nearest_sample_distance", null) != null:
missing_result["status"] = "sampled_nearby"
missing_result["terrain_height"] = float(missing_result.nearest_sample_height)
return missing_result
return {
"status": "sampled",
"tile": intersected_tile_key,
"terrain_height": highest_terrain_height,
}
func _intersect_terrain_state(state: Dictionary, world_position: Vector3):
var terrain_mesh: Mesh = state.get("quality_terrain_mesh", null)
if terrain_mesh == null:
terrain_mesh = state.get("tile_lod_mesh", null)
if terrain_mesh == null:
return null
var triangle_mesh := terrain_mesh.generate_triangle_mesh()
if triangle_mesh == null:
return null
var tile_root := state.get("root", null) as Node3D
if tile_root == null:
return null
var inverse_transform := tile_root.global_transform.affine_inverse()
var local_ray_origin := inverse_transform * Vector3(world_position.x, RAY_HEIGHT, world_position.z)
var local_ray_direction := inverse_transform.basis * Vector3.DOWN
var intersection: Dictionary = triangle_mesh.intersect_ray(local_ray_origin, local_ray_direction)
if intersection.is_empty():
return null
var local_hit: Vector3 = intersection.get("position", Vector3.ZERO)
var world_hit := tile_root.global_transform * local_hit
return world_hit.y
func _tile_runtime_diagnostic(world: Node3D, tile_key: String, world_position: Vector3) -> Dictionary:
var available_tiles: Dictionary = world.get("_available_tiles")
var loading_tasks: Dictionary = world.get("_tile_loading_tasks")
var tile_states: Dictionary = world.get("_tile_states")
var load_queue: Array = world.get("_tile_load_queue")
var queued_index := -1
for index in load_queue.size():
var request: Dictionary = load_queue[index]
if String(request.get("key", "")) == tile_key:
queued_index = index
break
var diagnostic := {
"available": available_tiles.has(tile_key),
"queued_index": queued_index,
"loading": loading_tasks.has(tile_key),
"state_present": tile_states.has(tile_key),
"load_queue_size": load_queue.size(),
}
if tile_states.has(tile_key):
var state: Dictionary = tile_states[tile_key]
var quality_mesh: Mesh = state.get("quality_terrain_mesh", null)
var tile_lod_mesh: Mesh = state.get("tile_lod_mesh", null)
diagnostic["quality_mesh_present"] = quality_mesh != null
diagnostic["tile_lod_mesh_present"] = tile_lod_mesh != null
diagnostic["quality_source"] = String(state.get("quality_terrain_source", ""))
diagnostic["tile_lod"] = int(state.get("tile_lod", -1))
var tile_root := state.get("root", null) as Node3D
if tile_root != null:
var local_position := tile_root.global_transform.affine_inverse() * world_position
diagnostic["tile_root_position"] = _vector3_array(tile_root.global_position)
diagnostic["probe_local_position"] = _vector3_array(local_position)
var diagnostic_mesh := quality_mesh if quality_mesh != null else tile_lod_mesh
if diagnostic_mesh != null:
var mesh_aabb := diagnostic_mesh.get_aabb()
diagnostic["mesh_aabb_position"] = _vector3_array(mesh_aabb.position)
diagnostic["mesh_aabb_size"] = _vector3_array(mesh_aabb.size)
return diagnostic
func _nearest_terrain_sample(world: Node3D, world_position: Vector3) -> Dictionary:
var tile_states: Dictionary = world.get("_tile_states")
for radius in [2.0, 5.0, 10.0, 20.0, 40.0]:
for offset in [Vector2(radius, 0.0), Vector2(-radius, 0.0), Vector2(0.0, radius), Vector2(0.0, -radius)]:
var sample_position := world_position + Vector3(offset.x, 0.0, offset.y)
var sample_tile := _adt_tile_vector2i(sample_position)
var sample_key := "%d_%d" % [sample_tile.x, sample_tile.y]
if not tile_states.has(sample_key):
continue
var height_variant = _intersect_terrain_state(tile_states[sample_key], sample_position)
if height_variant != null:
return {
"nearest_sample_distance": radius,
"nearest_sample_height": float(height_variant),
"nearest_sample_tile": sample_key,
}
return {"nearest_sample_distance": null}
func _adt_tile_vector2i(world_position: Vector3) -> Vector2i:
var typed_world_position = GODOT_WORLD_POSITION_SCRIPT.new(world_position.x, world_position.y, world_position.z)
var tile_coordinate = COORDINATE_MAPPER_SCRIPT.godot_to_adt_tile(typed_world_position)
return Vector2i(tile_coordinate.tile_x, tile_coordinate.tile_y)
func _vector3_array(value: Vector3) -> Array[float]:
return [value.x, value.y, value.z]
func _vector3(value_variant) -> Vector3:
if not (value_variant is Array) or value_variant.size() != 3:
return Vector3.ZERO
@@ -23,7 +23,6 @@ const REQUIRED_MAPPER_CALLS := {
"res://src/render/streaming/streaming_target_planner.gd": ["godot_to_adt_tile", "godot_to_adt_tile_local"],
"res://src/scenes/player/third_person_wow_controller.gd": ["adt_tile_local_to_godot"],
"res://src/gameplay/terrain/adt_terrain_query.gd": ["godot_to_adt_tile", "godot_to_adt_tile_local", "adt_tile_local_to_chunk"],
"res://src/tools/probe_render_terrain_height.gd": ["godot_to_adt_tile"],
}
@@ -61,7 +60,7 @@ func _initialize() -> void:
quit(1)
return
print("COORDINATE_CONVERSION_BOUNDARIES PASS files=%d native_boundary=1 oracle=1 consumers=6 classifier_cases=6" % source_paths.size())
print("COORDINATE_CONVERSION_BOUNDARIES PASS files=%d native_boundary=1 oracle=1 consumers=5 classifier_cases=6" % source_paths.size())
quit(0)
+7 -1
View File
@@ -5,6 +5,9 @@ extends SceneTree
const STREAMING_WORLD_LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
const GAMEPLAY_ROOT := "res://src/gameplay"
const SOURCE_ROOTS: Array[String] = ["res://src", "res://addons"]
const RENDERER_DIAGNOSTIC_PATHS: Array[String] = [
"res://src/tools/probe_render_terrain_height.gd",
]
func _initialize() -> void:
@@ -20,6 +23,8 @@ func _initialize() -> void:
consumer_paths_by_path[gameplay_path] = true
for editor_source_path in editor_source_paths:
consumer_paths_by_path[editor_source_path] = true
for renderer_diagnostic_path in RENDERER_DIAGNOSTIC_PATHS:
consumer_paths_by_path[renderer_diagnostic_path] = true
for consumer_path_variant in consumer_paths_by_path:
var consumer_path := String(consumer_path_variant)
@@ -33,10 +38,11 @@ func _initialize() -> void:
quit(1)
return
print("RENDERER_INTERNAL_ACCESS PASS private_symbols=%d gameplay=%d editor_sources=%d" % [
print("RENDERER_INTERNAL_ACCESS PASS private_symbols=%d gameplay=%d editor_sources=%d renderer_tools=%d" % [
private_renderer_symbols.size(),
gameplay_paths.size(),
editor_source_paths.size(),
RENDERER_DIAGNOSTIC_PATHS.size(),
])
quit(0)
+88 -1
View File
@@ -5,6 +5,7 @@ extends SceneTree
const WORLD_RENDER_FACADE_SCRIPT := preload("res://src/render/world_render_facade.gd")
const GODOT_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/godot_world_position.gd")
const STREAMING_FOCUS_SCRIPT := preload("res://src/domain/streaming/streaming_focus.gd")
const RENDERED_GROUND_SAMPLE_SCRIPT := preload("res://src/render/terrain/rendered_ground_sample.gd")
const RUNTIME_SCENE_PATHS: Array[String] = [
"res://src/scenes/streaming/eastern_kingdoms_streaming.tscn",
@@ -15,12 +16,19 @@ const TOOL_PATHS: Array[String] = [
"res://src/tools/probe_render_camera_occluders.gd",
"res://src/tools/probe_render_terrain_height.gd",
]
const STREAMING_WORLD_LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
class StreamingWorldLoaderDouble extends Node:
var current_focus: StreamingFocus
var refresh_count := 0
var metrics := {"tiles": 2, "queues": {"tile": 1}}
var ground_sample: RefCounted = RENDERED_GROUND_SAMPLE_SCRIPT.available(42.25)
var ground_snapshot := {
"status": "sampled",
"terrain_height": 42.25,
"diagnostic": {"state_present": true},
}
func set_streaming_focus(streaming_focus: StreamingFocus) -> void:
current_focus = streaming_focus
@@ -32,9 +40,16 @@ class StreamingWorldLoaderDouble extends Node:
func render_baseline_snapshot() -> Dictionary:
return metrics
func sample_rendered_ground_height(_godot_world_position: GodotWorldPosition) -> RefCounted:
return ground_sample
func render_ground_query_snapshot(_godot_world_position: GodotWorldPosition) -> Dictionary:
return ground_snapshot
func _initialize() -> void:
var failures: Array[String] = []
_verify_rendered_ground_sample_contract(failures)
await _verify_delegation_and_snapshot_isolation(failures)
_verify_repository_wiring(failures)
@@ -44,10 +59,26 @@ func _initialize() -> void:
quit(1)
return
print("WORLD_RENDER_FACADE PASS delegation=3 runtime_scenes=2 tools=3")
print("WORLD_RENDER_FACADE PASS delegation=5 ground_contract=2 runtime_scenes=2 tools=3")
quit(0)
func _verify_rendered_ground_sample_contract(failures: Array[String]) -> void:
var non_finite_sample = RENDERED_GROUND_SAMPLE_SCRIPT.available(INF)
_expect_true(not non_finite_sample.is_available, "non-finite rendered ground is unavailable", failures)
_expect_true(
non_finite_sample.failure_code == &"render_terrain_height_not_finite",
"non-finite rendered ground failure code",
failures
)
var default_unavailable_sample = RENDERED_GROUND_SAMPLE_SCRIPT.unavailable(&"")
_expect_true(
default_unavailable_sample.failure_code == &"render_terrain_unavailable",
"empty rendered ground failure code normalizes",
failures
)
func _verify_delegation_and_snapshot_isolation(failures: Array[String]) -> void:
var test_root := Node.new()
var loader := StreamingWorldLoaderDouble.new()
@@ -82,6 +113,24 @@ func _verify_delegation_and_snapshot_isolation(failures: Array[String]) -> void:
(snapshot["queues"] as Dictionary)["tile"] = 999
_expect_true(int(loader.metrics.tiles) == 2, "top-level metrics are detached", failures)
_expect_true(int(loader.metrics.queues.tile) == 1, "nested metrics are detached", failures)
var query_position = GODOT_WORLD_POSITION_SCRIPT.new(100.0, 200.0, 300.0)
var ground_sample: RefCounted = facade.sample_ground_height(query_position)
_expect_true(ground_sample.is_available, "rendered ground sample delegates", failures)
_expect_near(ground_sample.height_units, 42.25, "rendered ground height", failures)
loader.ground_sample = RENDERED_GROUND_SAMPLE_SCRIPT.unavailable(&"render_terrain_mesh_not_ready")
var unavailable_ground_sample: RefCounted = facade.sample_ground_height(query_position)
_expect_true(not unavailable_ground_sample.is_available, "unavailable ground sample delegates", failures)
_expect_true(
unavailable_ground_sample.failure_code == &"render_terrain_mesh_not_ready",
"ground failure code delegates",
failures
)
var ground_snapshot: Dictionary = facade.renderer_ground_query_snapshot(query_position)
ground_snapshot["terrain_height"] = 999.0
(ground_snapshot["diagnostic"] as Dictionary)["state_present"] = false
_expect_near(float(loader.ground_snapshot.terrain_height), 42.25, "ground snapshot top-level detached", failures)
_expect_true(bool(loader.ground_snapshot.diagnostic.state_present), "ground snapshot nested detached", failures)
test_root.queue_free()
@@ -98,6 +147,44 @@ func _verify_repository_wiring(failures: Array[String]) -> void:
_expect_true(not tool_source.contains('world.call("refresh_streaming_focus"'), "%s avoids direct streamer refresh" % tool_path, failures)
_expect_true(not tool_source.contains('world.set("streaming_focus_source_path"'), "%s avoids direct streamer focus config" % tool_path, failures)
var terrain_probe_source := _read_text("res://src/tools/probe_render_terrain_height.gd", failures)
_expect_true(
terrain_probe_source.contains('"renderer_ground_query_snapshot"'),
"terrain probe delegates ground diagnostics",
failures
)
_expect_true(
not terrain_probe_source.contains('world.get("_'),
"terrain probe avoids private streamer state",
failures
)
for forbidden_probe_text in [
"_tile_states",
"_tile_load_queue",
"_tile_loading_tasks",
"_available_tiles",
"generate_triangle_mesh",
]:
_expect_true(
not terrain_probe_source.contains(forbidden_probe_text),
"terrain probe omits %s" % forbidden_probe_text,
failures
)
var loader_source := _read_text(STREAMING_WORLD_LOADER_PATH, failures)
for required_loader_text in [
"func sample_rendered_ground_height(godot_world_position: GodotWorldPosition)",
"func render_ground_query_snapshot(godot_world_position: GodotWorldPosition)",
"RENDER_GROUND_QUERY_RAY_HEIGHT_UNITS := 5000.0",
"for radius_units in [2.0, 5.0, 10.0, 20.0, 40.0]",
"generate_triangle_mesh()",
]:
_expect_true(
loader_source.contains(required_loader_text),
"streamer ground query retains %s" % required_loader_text,
failures
)
func _read_text(path: String, failures: Array[String]) -> String:
var file := FileAccess.open(path, FileAccess.READ)