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
@@ -17,6 +17,7 @@ const GODOT_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/godot
const COORDINATE_MAPPER_SCRIPT := preload("res://src/domain/coordinates/coordinate_mapper.gd")
const ADT_TILE_COORDINATE_SCRIPT := preload("res://src/domain/coordinates/adt_tile_coordinate.gd")
const ADT_TILE_LOCAL_POSITION_SCRIPT := preload("res://src/domain/coordinates/adt_tile_local_position.gd")
const RENDERED_GROUND_SAMPLE_SCRIPT := preload("res://src/render/terrain/rendered_ground_sample.gd")
const STREAMING_TARGET_PLANNER_SCRIPT := preload("res://src/render/streaming/streaming_target_planner.gd")
const STREAMING_TARGET_POLICY_SCRIPT := preload("res://src/render/streaming/streaming_target_policy.gd")
const RENDER_BUDGET_SCHEDULER_SCRIPT := preload("res://src/render/streaming/render_budget_scheduler.gd")
@@ -25,6 +26,7 @@ const REQUIRED_SPLAT_TILE_FORMAT_VERSION := 1
const REQUIRED_CONTROL_SPLAT_TILE_FORMAT_VERSION := 3
const M2_MATERIAL_REFRESH_VERSION := 2
const WMO_MATERIAL_REFRESH_VERSION := 10
const RENDER_GROUND_QUERY_RAY_HEIGHT_UNITS := 5000.0
const TILE_SIZE := COORDINATE_MAPPER_SCRIPT.ADT_TILE_SIZE_YARDS
const CHUNK_SIZE := COORDINATE_MAPPER_SCRIPT.ADT_CHUNK_SIZE_YARDS
@@ -1038,6 +1040,173 @@ func render_baseline_snapshot() -> Dictionary:
}
## Samples already loaded render terrain at one typed Godot world position.
## This diagnostic rendering view is not authoritative gameplay collision.
func sample_rendered_ground_height(godot_world_position: GodotWorldPosition) -> RefCounted:
var ground_query_snapshot := render_ground_query_snapshot(godot_world_position)
if ground_query_snapshot.has("terrain_height"):
return RENDERED_GROUND_SAMPLE_SCRIPT.available(float(ground_query_snapshot["terrain_height"]))
var query_status := StringName(ground_query_snapshot.get("status", "render_terrain_unavailable"))
match query_status:
&"mesh_not_ready":
return RENDERED_GROUND_SAMPLE_SCRIPT.unavailable(&"render_terrain_mesh_not_ready")
&"no_intersection":
return RENDERED_GROUND_SAMPLE_SCRIPT.unavailable(&"render_terrain_no_intersection")
_:
return RENDERED_GROUND_SAMPLE_SCRIPT.unavailable(&"render_terrain_unavailable")
## Returns a detached diagnostic read model for the loaded terrain around one
## typed position. Queue/cache ownership and mutable tile state stay internal.
func render_ground_query_snapshot(godot_world_position: GodotWorldPosition) -> Dictionary:
var world_position := Vector3(
godot_world_position.x_units,
godot_world_position.y_units,
godot_world_position.z_units
)
var tile_coordinate = COORDINATE_MAPPER_SCRIPT.godot_to_adt_tile(godot_world_position)
var highest_terrain_height := -INF
var intersected_tile_key := ""
var ready_mesh_count := 0
for tile_y in range(tile_coordinate.tile_y - 1, tile_coordinate.tile_y + 2):
for tile_x in range(tile_coordinate.tile_x - 1, tile_coordinate.tile_x + 2):
var tile_key := _tile_key(tile_x, tile_y)
if not _tile_states.has(tile_key):
continue
var terrain_state: Dictionary = _tile_states[tile_key]
var terrain_height_variant = _intersect_rendered_terrain_state(terrain_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 is_finite(highest_terrain_height):
return {
"status": "sampled",
"tile": intersected_tile_key,
"terrain_height": highest_terrain_height,
}
var focus_tile_key := _tile_key(tile_coordinate.tile_x, tile_coordinate.tile_y)
var missing_snapshot := {
"status": "no_intersection" if ready_mesh_count > 0 else "mesh_not_ready",
"tile": focus_tile_key,
}
missing_snapshot.merge(
_render_ground_tile_diagnostic(focus_tile_key, world_position),
true
)
if bool(missing_snapshot.get("quality_mesh_present", false)) or bool(
missing_snapshot.get("tile_lod_mesh_present", false)):
missing_snapshot["status"] = "no_intersection"
missing_snapshot.merge(_nearest_rendered_terrain_sample(world_position), true)
if missing_snapshot.get("nearest_sample_distance", null) != null:
missing_snapshot["status"] = "sampled_nearby"
missing_snapshot["terrain_height"] = float(missing_snapshot["nearest_sample_height"])
return missing_snapshot
func _intersect_rendered_terrain_state(terrain_state: Dictionary, world_position: Vector3):
var terrain_mesh: Mesh = terrain_state.get("quality_terrain_mesh", null)
if terrain_mesh == null:
terrain_mesh = terrain_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 := terrain_state.get("root", null) as Node3D
if tile_root == null or not is_instance_valid(tile_root):
return null
var inverse_transform := tile_root.global_transform.affine_inverse()
var local_ray_origin := inverse_transform * Vector3(
world_position.x,
RENDER_GROUND_QUERY_RAY_HEIGHT_UNITS,
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)
return (tile_root.global_transform * local_hit).y
func _render_ground_tile_diagnostic(tile_key: String, world_position: Vector3) -> Dictionary:
var queued_index := -1
for request_index in _tile_load_queue.size():
var tile_request: Dictionary = _tile_load_queue[request_index]
if String(tile_request.get("key", "")) == tile_key:
queued_index = request_index
break
var diagnostic_snapshot := {
"available": _available_tiles.has(tile_key),
"queued_index": queued_index,
"loading": _tile_loading_tasks.has(tile_key),
"state_present": _tile_states.has(tile_key),
"load_queue_size": _tile_load_queue.size(),
}
if not _tile_states.has(tile_key):
return diagnostic_snapshot
var terrain_state: Dictionary = _tile_states[tile_key]
var quality_mesh: Mesh = terrain_state.get("quality_terrain_mesh", null)
var tile_lod_mesh: Mesh = terrain_state.get("tile_lod_mesh", null)
diagnostic_snapshot["quality_mesh_present"] = quality_mesh != null
diagnostic_snapshot["tile_lod_mesh_present"] = tile_lod_mesh != null
diagnostic_snapshot["quality_source"] = String(terrain_state.get("quality_terrain_source", ""))
diagnostic_snapshot["tile_lod"] = int(terrain_state.get("tile_lod", -1))
var tile_root := terrain_state.get("root", null) as Node3D
if tile_root != null and is_instance_valid(tile_root):
var local_position := tile_root.global_transform.affine_inverse() * world_position
diagnostic_snapshot["tile_root_position"] = _render_ground_vector3_array(tile_root.global_position)
diagnostic_snapshot["probe_local_position"] = _render_ground_vector3_array(local_position)
var diagnostic_mesh := quality_mesh if quality_mesh != null else tile_lod_mesh
if diagnostic_mesh != null:
var mesh_bounds := diagnostic_mesh.get_aabb()
diagnostic_snapshot["mesh_aabb_position"] = _render_ground_vector3_array(mesh_bounds.position)
diagnostic_snapshot["mesh_aabb_size"] = _render_ground_vector3_array(mesh_bounds.size)
return diagnostic_snapshot
func _nearest_rendered_terrain_sample(world_position: Vector3) -> Dictionary:
for radius_units in [2.0, 5.0, 10.0, 20.0, 40.0]:
for offset in [
Vector2(radius_units, 0.0),
Vector2(-radius_units, 0.0),
Vector2(0.0, radius_units),
Vector2(0.0, -radius_units),
]:
var sample_position := world_position + Vector3(offset.x, 0.0, offset.y)
var typed_sample_position = GODOT_WORLD_POSITION_SCRIPT.new(
sample_position.x,
sample_position.y,
sample_position.z
)
var sample_tile = COORDINATE_MAPPER_SCRIPT.godot_to_adt_tile(typed_sample_position)
var sample_tile_key := _tile_key(sample_tile.tile_x, sample_tile.tile_y)
if not _tile_states.has(sample_tile_key):
continue
var terrain_height_variant = _intersect_rendered_terrain_state(
_tile_states[sample_tile_key],
sample_position
)
if terrain_height_variant != null:
return {
"nearest_sample_distance": radius_units,
"nearest_sample_height": float(terrain_height_variant),
"nearest_sample_tile": sample_tile_key,
}
return {"nearest_sample_distance": null}
func _render_ground_vector3_array(vector: Vector3) -> Array[float]:
return [vector.x, vector.y, vector.z]
func _tick_runtime_stats(delta: float) -> void:
if not runtime_stats_enabled or Engine.is_editor_hint():
return