@tool
## Streams Azeroth terrain around an explicit [StreamingFocus] with chunk-based LODs.
extends Node3D

const ADT_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/adt_builder.gd")
const BAKED_TILE_SCRIPT := preload("res://src/resources/baked_adt_tile.gd")
const STREAMING_TILE_SCRIPT := preload("res://src/resources/streaming_adt_tile.gd")
const SPLAT_TILE_SCRIPT := preload("res://src/resources/splat_adt_tile.gd")
const CONTROL_SPLAT_TILE_SCRIPT := preload("res://src/resources/control_splat_adt_tile.gd")
const WMO_STREAMING_SCRIPT := preload("res://src/resources/wmo_streaming_resource.gd")
const WMO_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/wmo_builder.gd")
const WMO_PLACEMENT_RESOLVER_SCRIPT := preload(
	"res://src/render/wmo/wmo_placement_resolver.gd"
)
const WMO_PLACEMENT_REGISTRY_SCRIPT := preload(
	"res://src/render/wmo/wmo_placement_registry.gd"
)
const WMO_RENDER_BUILD_STEP_PLANNER_SCRIPT := preload(
	"res://src/render/wmo/wmo_render_build_step_planner.gd"
)
const WMO_RENDER_BUILD_QUEUE_SCRIPT := preload(
	"res://src/render/wmo/wmo_render_build_queue.gd"
)
const WMO_RENDER_RESOURCE_CACHE_STATE_SCRIPT := preload(
	"res://src/render/wmo/wmo_render_resource_cache_state.gd"
)
const WMO_RENDER_RESOURCE_FINALIZER_SCRIPT := preload(
	"res://src/render/wmo/wmo_render_resource_finalizer.gd"
)
const WMO_SCENE_RESOURCE_CACHE_STATE_SCRIPT := preload(
	"res://src/render/wmo/wmo_scene_resource_cache_state.gd"
)
const WMO_SCENE_RESOURCE_FINALIZER_SCRIPT := preload(
	"res://src/render/wmo/wmo_scene_resource_finalizer.gd"
)
const M2_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/m2_builder.gd")
const M2_NATIVE_ANIMATOR_SCRIPT := preload("res://src/scenes/streaming/m2_native_animator.gd")
const STREAMING_FOCUS_SCRIPT := preload("res://src/domain/streaming/streaming_focus.gd")
const GODOT_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/godot_world_position.gd")
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 TERRAIN_QUALITY_MESH_CACHE_SCRIPT := preload("res://src/render/terrain/terrain_quality_mesh_cache.gd")
const TERRAIN_CHUNK_LOD_POLICY_SCRIPT := preload("res://src/render/terrain/terrain_chunk_lod_policy.gd")
const TERRAIN_CHUNK_LOD_PLANNER_SCRIPT := preload("res://src/render/terrain/terrain_chunk_lod_planner.gd")
const TERRAIN_CHUNK_GEOMETRY_QUEUE_PLANNER_SCRIPT := preload(
	"res://src/render/terrain/terrain_chunk_geometry_queue_planner.gd"
)
const ADT_WATER_LOAD_PIPELINE_STATE_SCRIPT := preload(
	"res://src/render/liquid/adt_water_load_pipeline_state.gd"
)
const ADT_WATER_SCENE_FINALIZER_SCRIPT := preload(
	"res://src/render/liquid/adt_water_scene_finalizer.gd"
)
const M2_UNIQUE_PLACEMENT_REGISTRY_SCRIPT := preload(
	"res://src/render/m2/m2_unique_placement_registry.gd"
)
const M2_PLACEMENT_TRANSFORM_RESOLVER_SCRIPT := preload(
	"res://src/render/m2/m2_placement_transform_resolver.gd"
)
const M2_PLACEMENT_GROUPER_SCRIPT := preload(
	"res://src/render/m2/m2_placement_grouper.gd"
)
const M2_BUILD_BATCH_PLANNER_SCRIPT := preload(
	"res://src/render/m2/m2_build_batch_planner.gd"
)
const M2_BUILD_DISPATCH_PLANNER_SCRIPT := preload(
	"res://src/render/m2/m2_build_dispatch_planner.gd"
)
const M2_BUILD_RESOURCE_SNAPSHOT_SCRIPT := preload(
	"res://src/render/m2/m2_build_resource_snapshot.gd"
)
const M2_BUILD_QUEUE_SCRIPT := preload("res://src/render/m2/m2_build_queue.gd")
const M2_STATIC_BATCH_MATERIALIZER_SCRIPT := preload(
	"res://src/render/m2/m2_static_batch_materializer.gd"
)
const M2_STATIC_BUILD_RESOURCE_OBSERVER_SCRIPT := preload(
	"res://src/render/m2/m2_static_build_resource_observer.gd"
)
const M2_CACHED_ANIMATION_RESOURCE_OBSERVER_SCRIPT := preload(
	"res://src/render/m2/m2_cached_animation_resource_observer.gd"
)
const M2_NATIVE_ANIMATION_RESOURCE_OBSERVER_SCRIPT := preload(
	"res://src/render/m2/m2_native_animation_resource_observer.gd"
)
const M2_RUNTIME_MESH_FINALIZER_SCRIPT := preload(
	"res://src/render/m2/m2_runtime_mesh_finalizer.gd"
)
const M2_RAW_MODEL_REPOSITORY_SCRIPT := preload(
	"res://src/render/m2/m2_raw_model_repository.gd"
)
const M2_PROTOTYPE_CACHE_STATE_SCRIPT := preload(
	"res://src/render/m2/m2_prototype_cache_state.gd"
)
const M2_MESH_LOAD_PIPELINE_STATE_SCRIPT := preload(
	"res://src/render/m2/m2_mesh_load_pipeline_state.gd"
)
const M2_ANIMATION_LOAD_PIPELINE_STATE_SCRIPT := preload(
	"res://src/render/m2/m2_animation_load_pipeline_state.gd"
)
const M2_ANIMATED_SCENE_FINALIZER_SCRIPT := preload(
	"res://src/render/m2/m2_animated_scene_finalizer.gd"
)
const M2_ANIMATION_RESOURCE_FINALIZER_SCRIPT := preload(
	"res://src/render/m2/m2_animation_resource_finalizer.gd"
)
const M2_ANIMATED_INSTANCE_MATERIALIZER_SCRIPT := preload(
	"res://src/render/m2/m2_animated_instance_materializer.gd"
)
const M2_MESH_RESOURCE_CACHE_STATE_SCRIPT := preload(
	"res://src/render/m2/m2_mesh_resource_cache_state.gd"
)
const M2_MESH_RESOURCE_EXTRACTOR_SCRIPT := preload(
	"res://src/render/m2/m2_mesh_resource_extractor.gd"
)
const M2_MESH_RESOURCE_FINALIZER_SCRIPT := preload(
	"res://src/render/m2/m2_mesh_resource_finalizer.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")
const REQUIRED_BAKED_TILE_FORMAT_VERSION := 5
const REQUIRED_SPLAT_TILE_FORMAT_VERSION := 1
const REQUIRED_CONTROL_SPLAT_TILE_FORMAT_VERSION := 3
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
const QUALITY_CUSTOM := "Custom"
const QUALITY_PERFORMANCE := "Performance"
const QUALITY_BALANCED := "Balanced"
const QUALITY_HIGH := "High"

@export var extracted_dir: String = "res://data/extracted"
@export var map_name: String = "Azeroth"
## Optional scene adapter source. Any Node3D may provide the runtime focus.
@export var streaming_focus_source_path: NodePath
## Camera used only by the optional automatic overview positioning feature.
@export var camera_path: NodePath
@export_enum("Custom", "Performance", "Balanced", "High") var quality_preset: String = QUALITY_CUSTOM

## Seconds between streaming updates (lower = more responsive, higher = cheaper)
@export var update_interval: float = 0.25
## ADT tiles parsed per update tick (increase for faster initial load, decrease to reduce hitching)
@export var tiles_per_tick: int = 2
## Maximum number of ADT parse tasks running on worker threads at once
@export var max_concurrent_tile_tasks: int = 2
## Chunk mesh nodes created OR removed per update tick
@export var chunk_ops_per_tick: int = 24
## Heavy tile LOD creations per update tick
@export var tile_lod_ops_per_tick: int = 1
## Heavy tile LOD removals per update tick.
@export var tile_lod_remove_ops_per_tick: int = 2
## Loaded tile resources finalized on the main thread per frame.
@export var tile_finalize_ops_per_tick: int = 1
## Full-quality baked terrain tile upgrades finalized per frame.
@export var terrain_upgrade_finalize_ops_per_tick: int = 1
## Full-quality terrain tile resources loaded in parallel.
@export var max_concurrent_terrain_upgrade_tasks: int = 1
## M2/WMO detail batches attached to loaded tiles per frame.
@export var detail_asset_ops_per_tick: int = 1
## M2 MultiMesh groups materialized per frame after background grouping.
@export var m2_build_groups_per_tick: int = 2
## Maximum instances written into a single M2 MultiMesh per frame.
@export var m2_multimesh_batch_size: int = 128
## Experimental GLB M2 animation is allowed only for simple one-primitive creatures.
## Complex doodads (for example GryphonRoost) need a native M2 animator because GLB
## conversion currently breaks their skin/material fidelity.
@export var enable_m2_animated_instances: bool = true
## Reject complex animated M2 GLBs until the skinning converter is robust for composite doodads.
@export var m2_animated_max_primitives: int = 3
@export var m2_animated_denylist_patterns: PackedStringArray = PackedStringArray(["gryphonroost"])
@export var m2_animated_allowlist_patterns: PackedStringArray = PackedStringArray([
	"creature/fish/",
	"creature/eagle/",
	"world/critter/",
])
## Maximum animated M2 scene instances attached per build step. Keep low to avoid hitches.
@export var m2_animated_instances_per_tick: int = 8
## Loaded animated M2 scene resources finalized per frame.
@export var m2_animation_finalize_ops_per_tick: int = 1
## Loaded M2 scene resources converted to meshes per frame.
@export var m2_mesh_finalize_ops_per_tick: int = 1
## WMO instances attached per frame.
@export var wmo_build_instances_per_tick: int = 1
## WMO render-cache mesh groups attached per frame.
@export var wmo_render_group_ops_per_tick: int = 24
## Raw ADT water parse tasks running in parallel for cached terrain tiles.
@export var max_concurrent_water_tasks: int = 1
## Cached-tile water roots attached per frame.
@export var water_finalize_ops_per_tick: int = 1
## Maximum cached WMO .tscn size instantiated while streaming. Larger WMOs need a lightweight render cache.
@export var wmo_max_runtime_scene_mb: float = 8.0
## Minimum camera movement before recomputing streaming targets.
@export var streaming_update_distance: float = CHUNK_SIZE * 2.0
## Recently built tile meshes kept for quick revisits.
@export var cached_tile_mesh_limit: int = 24
## Full-quality terrain meshes kept after an ADT is unloaded, so revisits do not flash back to low quality.
@export var terrain_quality_mesh_cache_limit: int = 24

@export var lod0_radius_chunks: int = 6
@export var lod1_radius_chunks: int = 12
@export var lod2_radius_chunks: int = 24
@export var lod2_tile_radius: int = 3
@export var warm_tile_margin: int = 1
## Extra ADT ring loaded and GPU-warmed outside the visible terrain radius.
@export var prewarm_tile_margin: int = 1
@export var prewarm_tile_lod_instances: bool = true
## Extra ADT rings retained after leaving the prewarm radius to avoid boundary churn.
@export var retain_tile_margin: int = 2
## Pre-create hidden full terrain LOD this many ADT widths before it becomes visible.
@export var full_lod_prewarm_tiles: float = 1.0
## Start loading the next ADT-centered ring before crossing a tile boundary.
@export_range(0.0, 0.49, 0.01) var boundary_prefetch_threshold: float = 0.35
@export var auto_position_camera: bool = true
@export var editor_preview_enabled: bool = true
@export var editor_preview_center_x: int = 32
@export var editor_preview_center_y: int = 48
@export var editor_preview_tile_radius: int = 1
@export var editor_follow_view_camera: bool = true
@export var editor_persist_generated_nodes: bool = false
@export var enable_occlusion_culling: bool = true
@export var use_rendering_server_for_terrain: bool = true
@export var use_baked_tile_cache: bool = true
@export var baked_tile_cache_dir: String = "res://data/cache/baked_terrain_v2"
@export var streaming_tile_cache_dir: String = "res://data/cache/baked_terrain_stream_v1"
@export var terrain_control_splat_cache_dir: String = "res://data/cache/terrain_control_splat_v3"
@export var terrain_splat_cache_dir: String = "res://data/cache/terrain_splat_v1"
@export var terrain_full_quality_enabled: bool = true
@export var terrain_full_quality_radius_tiles: int = 1
@export var terrain_control_splat_quality_enabled: bool = true
@export var terrain_control_splat_primary: bool = true
@export var terrain_control_splat_hide_fallback: bool = false
@export var terrain_control_splat_hide_fallback_radius_tiles: int = 0
@export var terrain_control_splat_radius_tiles: int = 0
@export var max_active_terrain_control_splat_tiles: int = 5
@export var terrain_control_splat_cache_finalize_ops_per_tick: int = 1
@export var terrain_splat_quality_enabled: bool = true
@export var terrain_splat_radius_tiles: int = 0
@export var terrain_splat_lod: int = 0
@export var terrain_splat_runtime_build_enabled: bool = false
@export var max_active_terrain_splat_tiles: int = 5
@export var terrain_splat_cache_finalize_ops_per_tick: int = 1
@export var max_concurrent_terrain_splat_tasks: int = 1
@export var terrain_splat_builds_per_tick: int = 1
@export var use_chunk_terrain_lods: bool = false
@export var enable_water: bool = false
@export var enable_wmo: bool = true
@export var enable_m2_assets: bool = true
@export var enable_m2_placeholders: bool = false
@export var m2_placeholder_only_trees: bool = true
## Dynamic doodad render radius in ADT tiles. Negative keeps all loaded M2s active.
@export var m2_tile_radius: int = 1
## Dynamic WMO render radius in ADT tiles. Negative keeps all loaded WMOs active.
@export var wmo_tile_radius: int = 3
## Godot visibility range applied to instantiated M2 MultiMesh nodes. <= 0 disables it.
@export var m2_visibility_range: float = 700.0
## Godot visibility range applied to instantiated WMO trees. <= 0 disables it.
@export var wmo_visibility_range: float = 1800.0
@export var wmo_render_cache_dir: String = "res://data/cache/wmo_render_v1"
@export var m2_cache_dir: String = "res://data/cache/m2_glb"
@export var wmo_cache_dir: String = "res://data/cache/wmo_tscn"
@export var terrain_cast_shadows: bool = false
@export var m2_cast_shadows: bool = false
@export var wmo_cast_shadows: bool = false
@export var editor_reload_now: bool = false
@export var debug_streaming: bool = false
@export var runtime_stats_enabled: bool = false
@export var runtime_stats_interval: float = 2.0
@export var terrain_quality_log_enabled: bool = true
@export var terrain_quality_log_interval: float = 1.0
@export var hitch_profiler_enabled: bool = false
@export var hitch_profiler_threshold_ms: float = 20.0

var _builder
var _streaming_target_planner := STREAMING_TARGET_PLANNER_SCRIPT.new()
var _terrain_chunk_lod_planner := TERRAIN_CHUNK_LOD_PLANNER_SCRIPT.new()
var _terrain_chunk_geometry_operation_planner := (
	TERRAIN_CHUNK_GEOMETRY_QUEUE_PLANNER_SCRIPT.new()
)
var _map_dir := ""
var _abs_map_dir := ""
var _wdt_info: Dictionary = {}
var _available_tiles: Dictionary = {}
var _tile_states: Dictionary = {}
var _tile_load_queue: Array = []
var _chunk_remove_queue: Array = []
var _chunk_create_queue: Array = []
var _tile_lod_remove_queue: Array = []
var _tile_lod_create_queue: Array = []
var _detail_asset_queue: Array = []
var _detail_asset_queued: Dictionary = {}
var _m2_group_tasks: Dictionary = {}
var _m2_group_result_mutex := Mutex.new()
var _m2_group_result_queue: Array = []
var _m2_build_queue_state := M2_BUILD_QUEUE_SCRIPT.new()
var _m2_build_dispatch_planner := M2_BUILD_DISPATCH_PLANNER_SCRIPT.new()
var _m2_static_build_resource_observer := (
	M2_STATIC_BUILD_RESOURCE_OBSERVER_SCRIPT.new()
)
var _m2_animation_resource_observer := (
	M2_CACHED_ANIMATION_RESOURCE_OBSERVER_SCRIPT.new()
)
var _m2_native_animation_resource_observer := (
	M2_NATIVE_ANIMATION_RESOURCE_OBSERVER_SCRIPT.new()
)
var _m2_unique_placement_registry := (
	M2_UNIQUE_PLACEMENT_REGISTRY_SCRIPT.new()
)
var _m2_placement_transform_resolver := M2_PLACEMENT_TRANSFORM_RESOLVER_SCRIPT.new()
var _m2_placement_grouper := M2_PLACEMENT_GROUPER_SCRIPT.new()
var _m2_build_batch_planner := M2_BUILD_BATCH_PLANNER_SCRIPT.new()
var _m2_static_batch_materializer := M2_STATIC_BATCH_MATERIALIZER_SCRIPT.new()
var _m2_runtime_mesh_finalizer := M2_RUNTIME_MESH_FINALIZER_SCRIPT.new()
var _m2_raw_model_repository := M2_RAW_MODEL_REPOSITORY_SCRIPT.new()
var _m2_prototype_cache_state := M2_PROTOTYPE_CACHE_STATE_SCRIPT.new()
var _m2_mesh_resource_cache_state := M2_MESH_RESOURCE_CACHE_STATE_SCRIPT.new()
var _m2_mesh_resource_extractor := M2_MESH_RESOURCE_EXTRACTOR_SCRIPT.new()
var _m2_mesh_resource_finalizer := M2_MESH_RESOURCE_FINALIZER_SCRIPT.new(
	_m2_mesh_resource_extractor,
	_m2_runtime_mesh_finalizer,
	_m2_raw_model_repository
)
var _m2_mesh_load_pipeline_state := M2_MESH_LOAD_PIPELINE_STATE_SCRIPT.new()
var _m2_animation_load_pipeline_state := M2_ANIMATION_LOAD_PIPELINE_STATE_SCRIPT.new()
var _m2_animated_scene_finalizer := M2_ANIMATED_SCENE_FINALIZER_SCRIPT.new()
var _m2_animation_resource_finalizer := M2_ANIMATION_RESOURCE_FINALIZER_SCRIPT.new(
	_m2_animated_scene_finalizer
)
var _m2_animated_instance_materializer := M2_ANIMATED_INSTANCE_MATERIALIZER_SCRIPT.new()
var _wmo_build_jobs: Dictionary = {}
var _wmo_build_queue: Array = []
var _tile_loading_tasks: Dictionary = {}
var _terrain_upgrade_tasks: Dictionary = {}
var _terrain_control_splat_cache_tasks: Dictionary = {}
var _terrain_splat_cache_tasks: Dictionary = {}
var _terrain_splat_tasks: Dictionary = {}
var _terrain_splat_result_mutex := Mutex.new()
var _terrain_splat_result_queue: Array = []
var _adt_water_load_pipeline_state := ADT_WATER_LOAD_PIPELINE_STATE_SCRIPT.new()
var _adt_water_scene_finalizer := ADT_WATER_SCENE_FINALIZER_SCRIPT.new()
var _tile_result_mutex := Mutex.new()
var _tile_result_queue: Array = []
var _shared_tex_cache: Dictionary = {}
var _terrain_root: Node3D
var _update_accum := 0.0
var _tile_min := Vector2i(63, 63)
var _tile_max := Vector2i(0, 0)
var _camera_initialized := false
var _editor_signature := ""
var _streaming_focus: StreamingFocus
var _missing_focus_source_reported := false
var _last_focus_pos := Vector3.ZERO
var _dbg_chunks_created := 0
var _dbg_chunks_null := 0
var _dbg_last_report := 0.0
var _runtime_stats_accum := 0.0
var _terrain_quality_log_accum := 0.0
var _shutting_down := false
var _render_budget_scheduler := RENDER_BUDGET_SCHEDULER_SCRIPT.new()
var _last_refresh_focus_pos := Vector3.ZERO
var _has_refresh_focus := false
var _tile_mesh_cache: Dictionary = {}
var _tile_mesh_cache_order: Array = []
var _terrain_quality_mesh_cache: RefCounted = TERRAIN_QUALITY_MESH_CACHE_SCRIPT.new()
var _wmo_prototype_cache: Dictionary = {}
var _wmo_render_resource_cache_state := WMO_RENDER_RESOURCE_CACHE_STATE_SCRIPT.new()
var _wmo_render_resource_finalizer := WMO_RENDER_RESOURCE_FINALIZER_SCRIPT.new(
	WMO_STREAMING_SCRIPT,
	WMO_STREAMING_SCRIPT.FORMAT_VERSION
)
var _wmo_render_build_queue := WMO_RENDER_BUILD_QUEUE_SCRIPT.new()
var _wmo_scene_resource_cache_state := WMO_SCENE_RESOURCE_CACHE_STATE_SCRIPT.new()
var _wmo_scene_resource_finalizer := WMO_SCENE_RESOURCE_FINALIZER_SCRIPT.new(
	WMO_BUILDER_SCRIPT
)
var _wmo_missing_cache: Dictionary = {}
var _wmo_placement_resolver := WMO_PLACEMENT_RESOLVER_SCRIPT.new()
var _world_wmo_root: Node3D
var _wmo_placement_registry := WMO_PLACEMENT_REGISTRY_SCRIPT.new()
var _wmo_render_build_step_planner := WMO_RENDER_BUILD_STEP_PLANNER_SCRIPT.new()
var _wmo_instance_nodes_by_unique_key: Dictionary = {}


func _ready() -> void:
	_apply_quality_preset()

	if not ClassDB.class_exists("ADTLoader"):
		push_error("ADTLoader not found. Rebuild GDExtension first.")
		return

	if enable_occlusion_culling:
		get_viewport().set_use_occlusion_culling(true)

	_builder = ADT_BUILDER_SCRIPT.new()
	_map_dir = extracted_dir.path_join("World/Maps/%s" % map_name)
	_abs_map_dir = ProjectSettings.globalize_path(_map_dir)

	var existing_terrain := get_node_or_null("Terrain")
	if existing_terrain is Node3D:
		_terrain_root = existing_terrain
	else:
		_terrain_root = Node3D.new()
		_terrain_root.name = "Terrain"
		add_child(_terrain_root)
	_reset_terrain_root_children()
	_ensure_world_wmo_root()
	_set_editor_owner_recursive(_terrain_root)

	_scan_available_tiles()
	if _available_tiles.is_empty() and not _wdt_has_global_wmo():
		push_error("No ADT tiles found in %s" % _map_dir)
		return
	if _can_use_baked_tile_cache():
		var baked_dir := ProjectSettings.globalize_path(baked_tile_cache_dir.path_join(map_name))
		if not DirAccess.dir_exists_absolute(baked_dir):
			push_warning("Baked terrain cache not found: %s. Runtime will fall back to raw ADT builds." % baked_dir)
	if terrain_control_splat_quality_enabled:
		var control_splat_dir := ProjectSettings.globalize_path(terrain_control_splat_cache_dir.path_join(map_name))
		if not DirAccess.dir_exists_absolute(control_splat_dir):
			push_warning("Control splat terrain cache not found: %s. High terrain quality will use baked atlas fallback." % control_splat_dir)
	if terrain_splat_quality_enabled:
		var splat_dir := ProjectSettings.globalize_path(terrain_splat_cache_dir.path_join(map_name))
		if not DirAccess.dir_exists_absolute(splat_dir):
			push_warning("Terrain splat cache not found: %s. High terrain quality will use baked atlas fallback." % splat_dir)

	if enable_wmo:
		_place_global_wmo()

	if Engine.is_editor_hint():
		_tick_editor_streaming()
	elif auto_position_camera:
		_position_camera_over_world()
	if not Engine.is_editor_hint():
		_capture_streaming_focus_from_source()

	set_process(true)
	call_deferred("_refresh_streaming_targets_after_ready")


func _process(delta: float) -> void:
	if _available_tiles.is_empty():
		return
	_render_budget_scheduler.begin_frame(_render_operation_limits_for_frame())

	var profile_enabled := hitch_profiler_enabled and not Engine.is_editor_hint()
	var profile_start := Time.get_ticks_usec() if profile_enabled else 0
	var section_start := profile_start
	var timings: Array[String] = []

	# Queues are always drained — works in both editor and game.
	_drain_tile_load_results()
	_profile_section(timings, "finalize", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_drain_terrain_upgrade_results()
	_profile_section(timings, "terrainup", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_drain_terrain_control_splat_cache_results()
	_profile_section(timings, "terrainctl", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_drain_terrain_splat_cache_results()
	_profile_section(timings, "terrainsplatcache", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_drain_terrain_splat_results()
	_profile_section(timings, "terrainsplat", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_process_water_load_queue()
	_drain_water_load_results()
	_profile_section(timings, "water", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_process_queues()
	_profile_section(timings, "terrainq", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_drain_m2_group_results()
	_profile_section(timings, "m2results", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_drain_m2_animation_loads()
	_profile_section(timings, "m2anim", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_drain_m2_mesh_loads()
	_profile_section(timings, "m2mesh", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_process_m2_build_jobs()
	_profile_section(timings, "m2build", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_process_wmo_build_jobs()
	_profile_section(timings, "wmobuild", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_process_wmo_render_build_jobs()
	_profile_section(timings, "wmogroups", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_process_detail_asset_queue()
	_profile_section(timings, "details", section_start, profile_enabled)
	section_start = Time.get_ticks_usec() if profile_enabled else 0
	_tick_runtime_stats(delta)
	_tick_terrain_quality_log(delta)

	_update_accum += delta
	var did_refresh := false
	if _update_accum < update_interval:
		_log_hitch_profile(profile_start, timings, did_refresh, profile_enabled)
		return
	_update_accum = 0.0

	if Engine.is_editor_hint():
		_tick_editor_streaming()
	else:
		refresh_streaming_focus(false)
	did_refresh = true
	_profile_section(timings, "refresh", section_start, profile_enabled)
	_log_hitch_profile(profile_start, timings, did_refresh, profile_enabled)


func _exit_tree() -> void:
	_shutting_down = true
	_render_budget_scheduler.cancel()
	_wait_for_tile_tasks()
	_clear_streamed_world()
	_release_runtime_caches_for_shutdown()


func _render_operation_limits_for_frame() -> Dictionary:
	return {
		RENDER_BUDGET_SCHEDULER_SCRIPT.TILE_FINALIZE: maxi(1, tile_finalize_ops_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_UPGRADE_FINALIZE: maxi(0, terrain_upgrade_finalize_ops_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_CONTROL_SPLAT_FINALIZE: maxi(0, terrain_control_splat_cache_finalize_ops_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_SPLAT_CACHE_FINALIZE: maxi(0, terrain_splat_cache_finalize_ops_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_SPLAT_BUILD: maxi(0, terrain_splat_builds_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.WATER_FINALIZE: maxi(0, water_finalize_ops_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.CHUNK_GEOMETRY: maxi(0, chunk_ops_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.TILE_LOAD_START: maxi(0, tiles_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.TILE_LOD_CREATE: maxi(0, tile_lod_ops_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.TILE_LOD_REMOVE: maxi(0, tile_lod_remove_ops_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.M2_ANIMATION_FINALIZE: maxi(0, m2_animation_finalize_ops_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.M2_MESH_FINALIZE: maxi(0, m2_mesh_finalize_ops_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.M2_BUILD: maxi(0, m2_build_groups_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.WMO_BUILD: maxi(0, wmo_build_instances_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.WMO_RENDER_GROUP_BUILD: maxi(0, wmo_render_group_ops_per_tick),
		RENDER_BUDGET_SCHEDULER_SCRIPT.DETAIL_ASSET_SYNC: maxi(0, detail_asset_ops_per_tick),
	}


## Frees detached prototype nodes and releases render resources owned by this
## loader after all asynchronous work has completed. Map refreshes retain these
## caches; only scene shutdown destroys them.
func _release_runtime_caches_for_shutdown() -> void:
	_m2_prototype_cache_state.clear_and_release()
	_free_detached_node_cache(_wmo_prototype_cache)
	_m2_mesh_resource_cache_state.clear()
	_wmo_render_resource_cache_state.clear_all()
	_wmo_scene_resource_cache_state.clear_all()
	_wmo_missing_cache.clear()
	_shared_tex_cache.clear()


func _free_detached_node_cache(node_cache: Dictionary) -> void:
	for cached_value in node_cache.values():
		if not (cached_value is Node):
			continue
		var cached_node := cached_value as Node
		if not is_instance_valid(cached_node):
			continue
		if cached_node.is_inside_tree():
			cached_node.queue_free()
		else:
			cached_node.free()
	node_cache.clear()


func _apply_quality_preset() -> void:
	match quality_preset:
		QUALITY_PERFORMANCE:
			update_interval = 0.12
			tiles_per_tick = 1
			max_concurrent_tile_tasks = 1
			chunk_ops_per_tick = 16
			tile_lod_ops_per_tick = 1
			tile_lod_remove_ops_per_tick = 1
			tile_finalize_ops_per_tick = 1
			terrain_upgrade_finalize_ops_per_tick = 1
			max_concurrent_terrain_upgrade_tasks = 1
			detail_asset_ops_per_tick = 1
			m2_build_groups_per_tick = 1
			m2_multimesh_batch_size = 32
			m2_animation_finalize_ops_per_tick = 1
			m2_mesh_finalize_ops_per_tick = 1
			wmo_build_instances_per_tick = 1
			wmo_render_group_ops_per_tick = 16
			terrain_quality_mesh_cache_limit = 0
			lod0_radius_chunks = 6
			lod1_radius_chunks = 12
			lod2_radius_chunks = 24
			lod2_tile_radius = 3
			warm_tile_margin = 1
			prewarm_tile_margin = 2
			retain_tile_margin = 3
			full_lod_prewarm_tiles = 1.0
			boundary_prefetch_threshold = 0.4
			terrain_full_quality_enabled = false
			terrain_full_quality_radius_tiles = 0
			terrain_control_splat_quality_enabled = false
			terrain_control_splat_primary = false
			terrain_control_splat_hide_fallback = false
			terrain_control_splat_hide_fallback_radius_tiles = 0
			terrain_control_splat_radius_tiles = 0
			max_active_terrain_control_splat_tiles = 0
			terrain_control_splat_cache_finalize_ops_per_tick = 1
			terrain_splat_quality_enabled = false
			terrain_splat_radius_tiles = 0
			terrain_splat_lod = 3
			terrain_splat_runtime_build_enabled = false
			max_active_terrain_splat_tiles = 0
			terrain_splat_cache_finalize_ops_per_tick = 1
			max_concurrent_terrain_splat_tasks = 1
			terrain_splat_builds_per_tick = 1
			m2_tile_radius = 1
			wmo_tile_radius = 3
			m2_visibility_range = 700.0
			wmo_visibility_range = 1800.0
			m2_cast_shadows = false
			wmo_cast_shadows = false
			terrain_cast_shadows = false
		QUALITY_BALANCED:
			update_interval = 0.10
			tiles_per_tick = 1
			max_concurrent_tile_tasks = 1
			chunk_ops_per_tick = 20
			tile_lod_ops_per_tick = 1
			tile_lod_remove_ops_per_tick = 1
			tile_finalize_ops_per_tick = 1
			terrain_upgrade_finalize_ops_per_tick = 1
			max_concurrent_terrain_upgrade_tasks = 1
			detail_asset_ops_per_tick = 1
			m2_build_groups_per_tick = 1
			m2_multimesh_batch_size = 32
			m2_animation_finalize_ops_per_tick = 1
			m2_mesh_finalize_ops_per_tick = 1
			wmo_build_instances_per_tick = 1
			wmo_render_group_ops_per_tick = 20
			terrain_quality_mesh_cache_limit = 24
			lod0_radius_chunks = 8
			lod1_radius_chunks = 16
			lod2_radius_chunks = 32
			lod2_tile_radius = 4
			warm_tile_margin = 1
			prewarm_tile_margin = 2
			retain_tile_margin = 3
			full_lod_prewarm_tiles = 1.0
			boundary_prefetch_threshold = 0.4
			terrain_full_quality_enabled = true
			terrain_full_quality_radius_tiles = 1
			terrain_control_splat_quality_enabled = true
			terrain_control_splat_primary = true
			terrain_control_splat_hide_fallback = false
			terrain_control_splat_hide_fallback_radius_tiles = 0
			terrain_control_splat_radius_tiles = 0
			max_active_terrain_control_splat_tiles = 1
			terrain_control_splat_cache_finalize_ops_per_tick = 1
			terrain_splat_quality_enabled = false
			terrain_splat_radius_tiles = 0
			terrain_splat_lod = 0
			terrain_splat_runtime_build_enabled = false
			max_active_terrain_splat_tiles = 1
			terrain_splat_cache_finalize_ops_per_tick = 1
			max_concurrent_terrain_splat_tasks = 1
			terrain_splat_builds_per_tick = 1
			m2_tile_radius = 2
			wmo_tile_radius = 4
			m2_visibility_range = 1100.0
			wmo_visibility_range = 2600.0
			m2_cast_shadows = false
			wmo_cast_shadows = false
			terrain_cast_shadows = false
		QUALITY_HIGH:
			update_interval = 0.10
			tiles_per_tick = 1
			max_concurrent_tile_tasks = 1
			chunk_ops_per_tick = 24
			tile_lod_ops_per_tick = 1
			tile_lod_remove_ops_per_tick = 1
			tile_finalize_ops_per_tick = 1
			terrain_upgrade_finalize_ops_per_tick = 1
			max_concurrent_terrain_upgrade_tasks = 1
			detail_asset_ops_per_tick = 1
			m2_build_groups_per_tick = 1
			m2_multimesh_batch_size = 64
			m2_animation_finalize_ops_per_tick = 1
			m2_mesh_finalize_ops_per_tick = 1
			wmo_build_instances_per_tick = 1
			wmo_render_group_ops_per_tick = 16
			terrain_quality_mesh_cache_limit = 48
			lod0_radius_chunks = 10
			lod1_radius_chunks = 20
			lod2_radius_chunks = 40
			lod2_tile_radius = 5
			warm_tile_margin = 1
			prewarm_tile_margin = 3
			retain_tile_margin = 4
			full_lod_prewarm_tiles = 1.5
			boundary_prefetch_threshold = 0.42
			terrain_full_quality_enabled = true
			terrain_full_quality_radius_tiles = 5
			terrain_control_splat_quality_enabled = true
			terrain_control_splat_primary = true
			terrain_control_splat_hide_fallback = true
			terrain_control_splat_hide_fallback_radius_tiles = 0
			terrain_control_splat_radius_tiles = 5
			max_active_terrain_control_splat_tiles = 121
			terrain_control_splat_cache_finalize_ops_per_tick = 8
			terrain_splat_quality_enabled = false
			terrain_splat_radius_tiles = 0
			terrain_splat_lod = 0
			terrain_splat_runtime_build_enabled = false
			max_active_terrain_splat_tiles = 5
			terrain_splat_cache_finalize_ops_per_tick = 1
			max_concurrent_terrain_splat_tasks = 1
			terrain_splat_builds_per_tick = 1
			m2_tile_radius = 3
			wmo_tile_radius = 5
			m2_visibility_range = 1600.0
			wmo_visibility_range = 3600.0
			m2_cast_shadows = false
			wmo_cast_shadows = false
			terrain_cast_shadows = false


func _tick_editor_streaming() -> void:
	if editor_reload_now:
		editor_reload_now = false
		_clear_streamed_world()
		_editor_signature = ""

	if not editor_preview_enabled:
		if not _editor_signature.is_empty():
			_clear_streamed_world()
			_terrain_root.position = Vector3.ZERO
			_editor_signature = ""
		return

	var sig := _make_editor_signature()
	if sig != _editor_signature:
		_editor_signature = sig
		_clear_streamed_world()
		_reset_refresh_focus()
		# Offset terrain_root so preview center sits near Godot origin.
		var focus := _tile_center_to_world(editor_preview_center_x, editor_preview_center_y)
		_terrain_root.position = -focus

	var focus_world := _tile_center_to_world(editor_preview_center_x, editor_preview_center_y)
	if editor_follow_view_camera:
		var editor_camera := _get_editor_view_camera()
		if editor_camera:
			focus_world += editor_camera.global_position
	_set_streaming_focus_from_vector3(focus_world)
	_refresh_editor_streaming_targets_at(_streaming_focus_to_vector3(), false)


func _scan_available_tiles() -> void:
	_available_tiles.clear()
	_wdt_info = {}

	if _load_tiles_from_wdt():
		print("StreamingWorld: loaded %d ADT tiles from WDT for %s" % [_available_tiles.size(), map_name])
		return

	push_warning("StreamingWorld: falling back to directory scan for %s (no WDT)" % map_name)
	_load_tiles_from_directory()
	print("StreamingWorld: found %d ADT tiles by directory scan for %s" % [_available_tiles.size(), map_name])


func _load_tiles_from_wdt() -> bool:
	if not ClassDB.class_exists("WDTLoader"):
		return false

	var wdt_path_abs := _abs_map_dir.path_join("%s.wdt" % map_name)
	if not FileAccess.file_exists(wdt_path_abs):
		return false

	var loader = ClassDB.instantiate("WDTLoader")
	if loader == null:
		return false

	var data: Dictionary = loader.call("load_wdt", wdt_path_abs)
	if data.is_empty():
		push_warning("StreamingWorld: WDTLoader returned empty data for %s" % wdt_path_abs)
		return false

	var tiles: Array = data.get("tiles", [])
	for tile_variant in tiles:
		if not (tile_variant is Dictionary):
			continue
		var entry: Dictionary = tile_variant
		var tx: int = int(entry.get("x", -1))
		var ty: int = int(entry.get("y", -1))
		if tx < 0 or tx > 63 or ty < 0 or ty > 63:
			continue

		var adt_file := "%s_%d_%d.adt" % [map_name, tx, ty]
		var adt_abs := _abs_map_dir.path_join(adt_file)
		# WDT flags the tile as present, but the ADT may still be missing in
		# a partial extraction. Skip so downstream code never chases a ghost.
		if not FileAccess.file_exists(adt_abs):
			continue

		var key := _tile_key(tx, ty)
		_available_tiles[key] = adt_abs
		_tile_min.x = min(_tile_min.x, tx)
		_tile_min.y = min(_tile_min.y, ty)
		_tile_max.x = max(_tile_max.x, tx)
		_tile_max.y = max(_tile_max.y, ty)

	_wdt_info = data
	return not _available_tiles.is_empty()


func _load_tiles_from_directory() -> void:
	var dir := DirAccess.open(_map_dir)
	if dir == null:
		push_error("Cannot open map directory: %s" % _map_dir)
		return

	for file_name in dir.get_files():
		if not file_name.ends_with(".adt"):
			continue

		var stem := file_name.trim_suffix(".adt")
		var parts := stem.split("_")
		if parts.size() != 3 or parts[0] != map_name:
			continue
		if not parts[1].is_valid_int() or not parts[2].is_valid_int():
			continue

		var tx := parts[1].to_int()
		var ty := parts[2].to_int()
		var key := _tile_key(tx, ty)
		_available_tiles[key] = _abs_map_dir.path_join(file_name)
		_tile_min.x = min(_tile_min.x, tx)
		_tile_min.y = min(_tile_min.y, ty)
		_tile_max.x = max(_tile_max.x, tx)
		_tile_max.y = max(_tile_max.y, ty)


## Replaces the current streaming point without requiring a Node or Camera3D.
func set_streaming_focus(streaming_focus: StreamingFocus) -> void:
	_streaming_focus = streaming_focus


## Samples the configured scene source, then refreshes streaming from the typed focus.
## Returns false when no valid focus is available; existing streamed content is retained.
func refresh_streaming_focus(force: bool = false) -> bool:
	_capture_streaming_focus_from_source()
	if _streaming_focus == null or _streaming_focus.world_position == null:
		return false
	_terrain_root.position = Vector3.ZERO
	_refresh_streaming_targets_at(_streaming_focus_to_vector3(), force)
	return true


func _refresh_streaming_targets_after_ready() -> void:
	_has_refresh_focus = false
	refresh_streaming_focus(false)


## Core streaming update for runtime and editor focus positions.
func _refresh_editor_streaming_targets_at(focus_pos: Vector3, force: bool) -> void:
	if not force and not _should_refresh_focus(focus_pos):
		return

	var typed_focus_position = GODOT_WORLD_POSITION_SCRIPT.new(focus_pos.x, focus_pos.y, focus_pos.z)
	var target_plan = _streaming_target_planner.plan_editor(
		_available_tiles,
		typed_focus_position,
		editor_preview_tile_radius
	)
	var wanted_tiles: Dictionary = target_plan.wanted_tile_keys()

	_apply_streaming_target(wanted_tiles, target_plan.retained_tile_keys(), focus_pos)
	_note_refresh_focus(focus_pos)

	if force or debug_streaming:
		print("StreamingWorld(editor): focus_tile=%s wanted=%d queued=%d" % [
			Vector2i(target_plan.focus_tile.tile_x, target_plan.focus_tile.tile_y),
			wanted_tiles.size(),
			_tile_load_queue.size(),
		])


func _refresh_streaming_targets_at(focus_pos: Vector3, force: bool) -> void:
	if not force and not _should_refresh_focus(focus_pos):
		return

	var typed_focus_position = GODOT_WORLD_POSITION_SCRIPT.new(focus_pos.x, focus_pos.y, focus_pos.z)
	var target_plan = _streaming_target_planner.plan_runtime(
		_available_tiles,
		typed_focus_position,
		_streaming_target_policy()
	)
	var wanted_tiles: Dictionary = target_plan.wanted_tile_keys()
	var retained_tiles: Dictionary = target_plan.retained_tile_keys()

	_apply_streaming_target(wanted_tiles, retained_tiles, focus_pos)
	_note_refresh_focus(focus_pos)

	if force or debug_streaming:
		print("StreamingWorld: focus_tile=%s  wanted=%d  queued=%d" % [
			Vector2i(target_plan.focus_tile.tile_x, target_plan.focus_tile.tile_y),
			wanted_tiles.size(),
			_tile_load_queue.size(),
		])




func _apply_streaming_target(wanted_tiles: Dictionary, retained_tiles: Dictionary, focus_pos: Vector3) -> void:
	_last_focus_pos = focus_pos
	var typed_focus_position = GODOT_WORLD_POSITION_SCRIPT.new(focus_pos.x, focus_pos.y, focus_pos.z)
	var terrain_chunk_lod_policy = _create_terrain_chunk_lod_policy()

	var filtered_load_queue: Array = []
	for request in _tile_load_queue:
		if wanted_tiles.has(request["key"]) and not _tile_states.has(request["key"]):
			filtered_load_queue.append(request)
	_tile_load_queue = filtered_load_queue

	for key in wanted_tiles.keys():
		if not _tile_states.has(key) and not _is_tile_queued(key):
			var parts: PackedStringArray = key.split("_")
			var tx: int = int(parts[0])
			var ty: int = int(parts[1])
			var tile_center := _tile_center_to_world(tx, ty)
			var dx: float = tile_center.x - focus_pos.x
			var dz: float = tile_center.z - focus_pos.z
			_tile_load_queue.append({
				"key":      key,
				"tx":       tx,
				"ty":       ty,
				"path":     _available_tiles[key],
				"dist_sq":  dx * dx + dz * dz,
			})

	# Nearest tiles first
	_tile_load_queue.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
		return a["dist_sq"] < b["dist_sq"])

	for key in _tile_states.keys():
		var state: Dictionary = _tile_states[key]
		var wanted := wanted_tiles.has(key)
		var retained := retained_tiles.has(key)
		state["wanted"] = wanted
		state["retained"] = retained
		if wanted:
			state["desired_lods"] = _compute_desired_lods(state, typed_focus_position, terrain_chunk_lod_policy)
			state["desired_tile_lod"] = -1 if not state["desired_lods"].is_empty() else _compute_desired_tile_lod(state, focus_pos)
			state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, focus_pos)
		elif retained:
			state["desired_lods"] = {}
			var current_tile_lod: int = int(state.get("tile_lod", -1))
			state["desired_tile_lod"] = current_tile_lod if current_tile_lod >= 0 else -1
			state["desired_tile_lod_visible"] = false
		else:
			state["desired_lods"] = {}
			state["desired_tile_lod"] = -1
			state["desired_tile_lod_visible"] = false
		state = _sync_terrain_quality(state, focus_pos)
		state = _sync_detail_assets(state, focus_pos)
		_tile_states[key] = state

	_prune_active_terrain_control_splat_tiles(focus_pos)
	_prune_active_terrain_splat_tiles(focus_pos)
	_rebuild_chunk_queues()


func _rebuild_chunk_queues() -> void:
	var typed_focus_position = GODOT_WORLD_POSITION_SCRIPT.new(
		_last_focus_pos.x,
		_last_focus_pos.y,
		_last_focus_pos.z
	)
	var chunk_queue_plan: Dictionary = _terrain_chunk_geometry_operation_planner.plan(
		_tile_states,
		typed_focus_position
	)
	_chunk_remove_queue = chunk_queue_plan.get("remove_requests", [])
	_chunk_create_queue = chunk_queue_plan.get("create_requests", [])
	_tile_lod_remove_queue.clear()
	_tile_lod_create_queue.clear()

	for key in _tile_states.keys():
		var state: Dictionary = _tile_states[key]
		var current_tile_lod: int = int(state.get("tile_lod", -1))
		var desired_tile_lod: int = int(state.get("desired_tile_lod", -1))
		var current_tile_lod_visible: bool = bool(state.get("tile_lod_visible", false))
		var desired_tile_lod_visible: bool = bool(state.get("desired_tile_lod_visible", false))

		if desired_tile_lod >= 0:
			if current_tile_lod != desired_tile_lod or current_tile_lod_visible != desired_tile_lod_visible:
				_tile_lod_create_queue.append({
					"tile": key,
					"lod": desired_tile_lod,
					"visible": desired_tile_lod_visible,
					"dist_sq": _tile_dist_sq(state, _last_focus_pos),
				})
			elif _should_prepare_full_tile_lod(state, _last_focus_pos):
				_tile_lod_create_queue.append({
					"tile": key,
					"lod": 0,
					"visible": false,
					"prepare": true,
					"dist_sq": _tile_dist_sq(state, _last_focus_pos),
				})
			continue

		if current_tile_lod >= 0 and _is_tile_chunk_set_ready(state):
			_tile_lod_remove_queue.append({"tile": key})

	# Sort tile LOD creates after fully rebuilding; chunk creates arrive nearest-first.
	_tile_lod_create_queue.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
		return a["dist_sq"] < b["dist_sq"])


func _process_queues() -> void:
	# Free stale terrain first so GPU descriptors/memory are reclaimed before new creates.
	while not _chunk_remove_queue.is_empty() and _render_budget_scheduler.try_consume_permit(
			RENDER_BUDGET_SCHEDULER_SCRIPT.CHUNK_GEOMETRY):
		_remove_chunk(_chunk_remove_queue.pop_back())

	while not _tile_lod_remove_queue.is_empty() and _render_budget_scheduler.try_consume_permit(
			RENDER_BUDGET_SCHEDULER_SCRIPT.TILE_LOD_REMOVE):
		_remove_tile_lod(_tile_lod_remove_queue.pop_back())

	# Parse N ADT files this tick — the main throttle knob
	while (
			not _tile_load_queue.is_empty()
			and _tile_loading_tasks.size() < max_concurrent_tile_tasks
			and _render_budget_scheduler.try_consume_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TILE_LOAD_START)
	):
		_start_tile_load_async(_tile_load_queue.pop_front())

	while not _tile_lod_create_queue.is_empty() and _render_budget_scheduler.try_consume_permit(
			RENDER_BUDGET_SCHEDULER_SCRIPT.TILE_LOD_CREATE):
		_create_tile_lod(_tile_lod_create_queue.pop_front())

	# Create chunk meshes — sorted nearest-first so visible detail appears first
	while not _chunk_create_queue.is_empty() and _render_budget_scheduler.try_consume_permit(
			RENDER_BUDGET_SCHEDULER_SCRIPT.CHUNK_GEOMETRY):
		_create_chunk(_chunk_create_queue.pop_front())

	# Debug summary every 2 seconds
	_dbg_last_report += get_process_delta_time()
	if debug_streaming and _dbg_last_report >= 2.0:
		_dbg_last_report = 0.0
		print("DBG QUEUES  tiles_load=%d tile_lod_create=%d tile_lod_remove=%d chunk_create=%d chunk_remove=%d  created=%d null=%d states=%d" % [
			_tile_load_queue.size(), _tile_lod_create_queue.size(), _tile_lod_remove_queue.size(),
			_chunk_create_queue.size(), _chunk_remove_queue.size(),
			_dbg_chunks_created, _dbg_chunks_null, _tile_states.size()])


func _profile_section(timings: Array[String], label: String, start_usec: int, enabled: bool) -> void:
	if not enabled:
		return
	var elapsed_ms := float(Time.get_ticks_usec() - start_usec) / 1000.0
	if elapsed_ms >= 0.05:
		timings.append("%s=%.2f" % [label, elapsed_ms])


func _log_hitch_profile(start_usec: int, timings: Array[String], did_refresh: bool, enabled: bool) -> void:
	if not enabled:
		return
	var total_ms := float(Time.get_ticks_usec() - start_usec) / 1000.0
	if total_ms < maxf(1.0, hitch_profiler_threshold_ms):
		return
	print("HITCH %.2fms refresh=%s queues[tile=%d loading=%d terrainup=%d terrainctl=%d terrainsplat=%d water=%d detail=%d m2task=%d m2anim=%d m2mesh=%d m2build=%d wmobuild=%d wmogroups=%d lod+=%d lod-=%d chunk+=%d chunk-=%d] %s" % [
		total_ms,
		str(did_refresh),
		_tile_load_queue.size(),
		_tile_loading_tasks.size(),
		_terrain_upgrade_tasks.size(),
		_terrain_control_splat_cache_tasks.size(),
		_terrain_splat_queue_size(),
		_adt_water_load_pipeline_state.total_load_count(),
		_detail_asset_queue.size(),
		_m2_group_tasks.size(),
		_m2_animation_load_pipeline_state.total_work_count(),
		_m2_mesh_load_pipeline_state.total_work_count(),
		_m2_build_queue_state.pending_count(),
		(
			_wmo_build_queue.size()
			+ _wmo_scene_resource_cache_state.pending_request_count()
			+ _wmo_render_resource_cache_state.pending_request_count()
		),
		_wmo_render_build_queue.pending_count(),
		_tile_lod_create_queue.size(),
		_tile_lod_remove_queue.size(),
		_chunk_create_queue.size(),
		_chunk_remove_queue.size(),
		" ".join(timings),
	])


## Read-only machine-readable state used by renderer baseline tooling.
## Keep this free of queue mutation so checkpoint capture cannot alter runtime behavior.
func render_baseline_snapshot() -> Dictionary:
	return {
		"tiles": _tile_states.size(),
		"loading": _tile_loading_tasks.size(),
		"queues": {
			"tile": _tile_load_queue.size(),
			"terrain_upgrade": _terrain_upgrade_tasks.size(),
			"terrain_control": _terrain_control_splat_cache_tasks.size(),
			"terrain_splat": _terrain_splat_queue_size(),
			"water": _adt_water_load_pipeline_state.total_load_count(),
			"detail": _detail_asset_queue.size(),
			"m2_task": _m2_group_tasks.size(),
			"m2_animation": _m2_animation_load_pipeline_state.total_work_count(),
			"m2_mesh": _m2_mesh_load_pipeline_state.total_work_count(),
			"m2_build": _m2_build_queue_state.pending_count(),
			"wmo_build": (
				_wmo_build_queue.size()
				+ _wmo_scene_resource_cache_state.pending_request_count()
				+ _wmo_render_resource_cache_state.pending_request_count()
			),
			"wmo_groups": _wmo_render_build_queue.pending_count(),
			"lod_create": _tile_lod_create_queue.size(),
			"lod_remove": _tile_lod_remove_queue.size(),
			"chunk_create": _chunk_create_queue.size(),
			"chunk_remove": _chunk_remove_queue.size(),
		},
		"cache_entries": _tile_mesh_cache.size(),
		"m2_active_unique_ids": _m2_unique_placement_registry.active_count(),
		"wmo_instances": _wmo_placement_registry.active_count(),
	}


## 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

	_runtime_stats_accum += delta
	if _runtime_stats_accum < maxf(runtime_stats_interval, 0.25):
		return
	_runtime_stats_accum = 0.0

	var wanted_tiles := 0
	var retained_tiles := 0
	var chunk_nodes := 0
	var chunk_rids := 0
	var tile_lod_nodes := 0
	var tile_lod_rids := 0
	var tile_lod_hidden := 0
	var tile_lod_prepared := 0
	var m2_tiles := 0
	var m2_multimeshes := 0
	var m2_instances := 0
	var terrain_baked_full := 0
	var terrain_control_splat := 0
	var terrain_splat := 0

	for state_variant in _tile_states.values():
		if not (state_variant is Dictionary):
			continue
		var state: Dictionary = state_variant
		if bool(state.get("wanted", false)):
			wanted_tiles += 1
		if bool(state.get("retained", false)):
			retained_tiles += 1

		var chunks: Dictionary = state.get("chunks", {})
		for entry_variant in chunks.values():
			if not (entry_variant is Dictionary):
				continue
			var entry: Dictionary = entry_variant
			if bool(entry.get("empty", false)):
				continue
			var chunk_rid: RID = entry.get("rid", RID())
			if chunk_rid.is_valid():
				chunk_rids += 1
			elif entry.get("node", null) != null:
				chunk_nodes += 1

		var tile_lod_rid: RID = state.get("tile_lod_rid", RID())
		if tile_lod_rid.is_valid():
			tile_lod_rids += 1
		elif state.get("tile_lod_node", null) != null:
			tile_lod_nodes += 1
		if int(state.get("tile_lod", -1)) >= 0 and not bool(state.get("tile_lod_visible", true)):
			tile_lod_hidden += 1
		var prepared_tile_lod_rid: RID = state.get("prepared_tile_lod_rid", RID())
		if prepared_tile_lod_rid.is_valid():
			tile_lod_prepared += 1
		var terrain_source := String(state.get("quality_terrain_source", ""))
		if terrain_source == "baked_full":
			terrain_baked_full += 1
		elif terrain_source == "control_splat_cache":
			terrain_control_splat += 1
		elif terrain_source == "splat_cache" or terrain_source == "splat":
			terrain_splat += 1

		var root: Node = state.get("root", null)
		if root == null or not is_instance_valid(root):
			continue
		var m2_root := root.get_node_or_null("M2s")
		if m2_root == null:
			continue
		m2_tiles += 1
		var m2_counts := {"multimeshes": 0, "instances": 0}
		_accumulate_multimesh_stats(m2_root, m2_counts)
		m2_multimeshes += int(m2_counts["multimeshes"])
		m2_instances += int(m2_counts["instances"])

	print("PERF fps=%.1f tiles=%d wanted=%d retained=%d loading=%d terrainup=%d terrainctl=%d terrainsplat=%d water=%d src[baked=%d ctl=%d splat=%d] cache=%d lod_rid=%d lod_node=%d lod_hidden=%d lod_prepared=%d chunk_rid=%d chunk_node=%d queues[tile=%d detail=%d m2_task=%d m2_anim=%d m2_mesh=%d m2_build=%d wmo_build=%d wmo_groups=%d lod+=%d lod-=%d chunk+=%d chunk-=%d] m2_tiles=%d m2_mm=%d m2_inst=%d wmo=%d" % [
		Engine.get_frames_per_second(),
		_tile_states.size(),
		wanted_tiles,
		retained_tiles,
		_tile_loading_tasks.size(),
		_terrain_upgrade_tasks.size(),
		_terrain_control_splat_cache_tasks.size(),
		_terrain_splat_queue_size(),
		_adt_water_load_pipeline_state.total_load_count(),
		terrain_baked_full,
		terrain_control_splat,
		terrain_splat,
		_tile_mesh_cache.size(),
		tile_lod_rids,
		tile_lod_nodes,
		tile_lod_hidden,
		tile_lod_prepared,
		chunk_rids,
		chunk_nodes,
		_tile_load_queue.size(),
		_detail_asset_queue.size(),
		_m2_group_tasks.size(),
		_m2_animation_load_pipeline_state.total_work_count(),
		_m2_mesh_load_pipeline_state.total_work_count(),
		_m2_build_queue_state.pending_count(),
		(
			_wmo_build_queue.size()
			+ _wmo_scene_resource_cache_state.pending_request_count()
			+ _wmo_render_resource_cache_state.pending_request_count()
		),
		_wmo_render_build_queue.pending_count(),
		_tile_lod_create_queue.size(),
		_tile_lod_remove_queue.size(),
		_chunk_create_queue.size(),
		_chunk_remove_queue.size(),
		m2_tiles,
		m2_multimeshes,
		m2_instances,
		_wmo_placement_registry.active_count(),
	])


func _tick_terrain_quality_log(delta: float) -> void:
	if not terrain_quality_log_enabled or Engine.is_editor_hint():
		return
	_terrain_quality_log_accum += delta
	if _terrain_quality_log_accum < maxf(terrain_quality_log_interval, 0.25):
		return
	_terrain_quality_log_accum = 0.0

	var target := 0
	var ready := 0
	var pending := 0
	var fallback_baked := 0
	var fallback_stream := 0
	var hidden_wait := 0
	var missing_cache := 0
	var stale_or_other := 0
	var focus_state_source := "none"
	var focus_tile := _world_to_tile(_last_focus_pos)

	for state_variant in _tile_states.values():
		if not (state_variant is Dictionary):
			continue
		var state: Dictionary = state_variant
		if not bool(state.get("wanted", false)):
			continue
		if not terrain_control_splat_quality_enabled:
			continue
		if not _tile_within_control_splat_terrain_radius(state, _last_focus_pos):
			continue

		target += 1
		var key := String(state.get("key", ""))
		var source := String(state.get("quality_terrain_source", ""))
		var is_focus := int(state.get("tx", -9999)) == focus_tile.x and int(state.get("ty", -9999)) == focus_tile.y
		if is_focus:
			focus_state_source = source if not source.is_empty() else "stream"

		if source == "control_splat_cache":
			ready += 1
			continue
		if not key.is_empty() and _terrain_control_splat_cache_tasks.has(key):
			pending += 1
			continue

		var cache_path := _get_terrain_control_splat_resource_path(int(state.get("tx", 0)), int(state.get("ty", 0)))
		if not ResourceLoader.exists(cache_path):
			missing_cache += 1
			continue

		var desired_lod := int(state.get("desired_tile_lod", -1))
		if desired_lod < 0:
			hidden_wait += 1
		elif source == "baked_full":
			fallback_baked += 1
		else:
			var tile_lod := int(state.get("tile_lod", -1))
			if tile_lod >= 0:
				fallback_stream += 1
			else:
				stale_or_other += 1

	var done := ready >= target and pending == 0 and missing_cache == 0 and hidden_wait == 0 and fallback_baked == 0 and fallback_stream == 0
	print("TERRAIN_QUALITY target=%d ready=%d pending=%d fallback[baked=%d stream=%d hidden=%d other=%d] missing_cache=%d queue=%d focus=%d_%d:%s done=%s" % [
		target,
		ready,
		pending,
		fallback_baked,
		fallback_stream,
		hidden_wait,
		stale_or_other,
		missing_cache,
		_terrain_control_splat_cache_tasks.size(),
		focus_tile.x,
		focus_tile.y,
		focus_state_source,
		str(done),
	])


func _accumulate_multimesh_stats(node: Node, counts: Dictionary) -> void:
	if node is MultiMeshInstance3D:
		var mmi := node as MultiMeshInstance3D
		if mmi.multimesh:
			counts["multimeshes"] = int(counts["multimeshes"]) + 1
			counts["instances"] = int(counts["instances"]) + mmi.multimesh.instance_count
	for child in node.get_children():
		_accumulate_multimesh_stats(child, counts)


func _start_tile_load_async(request: Dictionary) -> void:
	var key: String = request["key"]
	if _tile_loading_tasks.has(key):
		return

	var stream_res_path := _get_streaming_tile_resource_path(int(request["tx"]), int(request["ty"]))
	if _can_use_baked_tile_cache() and ResourceLoader.exists(stream_res_path):
		var err := ResourceLoader.load_threaded_request(
			stream_res_path,
			"",
			false,
			ResourceLoader.CACHE_MODE_REUSE)
		if err == OK:
			_tile_loading_tasks[key] = {
				"mode": "stream",
				"path": stream_res_path,
				"request": request,
			}
			return

	var baked_res_path := _get_baked_tile_resource_path(int(request["tx"]), int(request["ty"]))
	if _can_use_baked_tile_cache() and ResourceLoader.exists(baked_res_path):
		var err := ResourceLoader.load_threaded_request(
			baked_res_path,
			"",
			false,
			ResourceLoader.CACHE_MODE_REUSE)
		if err == OK:
			_tile_loading_tasks[key] = {
				"mode": "baked",
				"path": baked_res_path,
				"request": request,
			}
			return

	_start_raw_tile_load_async(request)


func _start_raw_tile_load_async(request: Dictionary) -> void:
	var key: String = request["key"]
	if _tile_loading_tasks.has(key):
		return

	var task_id: int = WorkerThreadPool.add_task(_load_tile_task.bind(request))
	_tile_loading_tasks[key] = {
		"mode": "raw",
		"task_id": task_id,
		"request": request,
	}


func _load_tile_task(request: Dictionary) -> void:
	var data: Dictionary = {}
	var loader = ClassDB.instantiate("ADTLoader")
	if loader:
		data = loader.call("load_adt", request["path"])

	_tile_result_mutex.lock()
	_tile_result_queue.append({
		"key": request["key"],
		"data": data,
	})
	_tile_result_mutex.unlock()


func _drain_tile_load_results() -> void:
	var results: Array = []
	_tile_result_mutex.lock()
	while not _tile_result_queue.is_empty() and _render_budget_scheduler.try_consume_permit(
			RENDER_BUDGET_SCHEDULER_SCRIPT.TILE_FINALIZE):
		results.append(_tile_result_queue.pop_front())
	_tile_result_mutex.unlock()

	for result in results:
		var key: String = result["key"]
		if not _tile_loading_tasks.has(key):
			continue

		var pending: Dictionary = _tile_loading_tasks[key]
		if pending.get("mode", "raw") != "raw":
			continue
		WorkerThreadPool.wait_for_task_completion(int(pending["task_id"]))
		_tile_loading_tasks.erase(key)

		if _shutting_down:
			continue

		var request: Dictionary = pending["request"]
		if not _should_accept_loaded_tile(request):
			continue

		_finalize_loaded_tile(request, result["data"])

	var baked_ready: Array = []
	if not _render_budget_scheduler.has_remaining_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TILE_FINALIZE):
		return
	for key in _tile_loading_tasks.keys():
		if not _render_budget_scheduler.has_remaining_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TILE_FINALIZE):
			break
		var pending: Dictionary = _tile_loading_tasks[key]
		if pending.get("mode", "raw") != "baked" and pending.get("mode", "raw") != "stream":
			continue

		var status := ResourceLoader.load_threaded_get_status(pending["path"])
		if status == ResourceLoader.THREAD_LOAD_LOADED or status == ResourceLoader.THREAD_LOAD_FAILED:
			if _render_budget_scheduler.try_consume_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TILE_FINALIZE):
				baked_ready.append(key)

	for key in baked_ready:
		if not _tile_loading_tasks.has(key):
			continue

		var pending: Dictionary = _tile_loading_tasks[key]
		_tile_loading_tasks.erase(key)
		var request: Dictionary = pending["request"]
		if _shutting_down:
			continue

		var status := ResourceLoader.load_threaded_get_status(pending["path"])
		if status != ResourceLoader.THREAD_LOAD_LOADED:
			_start_raw_tile_load_async(request)
			continue

		var loaded_tile: Resource = ResourceLoader.load_threaded_get(pending["path"])
		if not _should_accept_loaded_tile(request):
			continue
		if pending.get("mode", "raw") == "stream":
			if loaded_tile == null or loaded_tile.get_script() != STREAMING_TILE_SCRIPT:
				push_warning("Invalid streaming tile resource: %s" % pending["path"])
				_start_raw_tile_load_async(request)
				continue
			_finalize_loaded_streaming_tile(request, loaded_tile)
			continue
		if loaded_tile == null or loaded_tile.get_script() != BAKED_TILE_SCRIPT:
			push_warning("Invalid baked tile resource: %s" % pending["path"])
			_start_raw_tile_load_async(request)
			continue

		_finalize_loaded_baked_tile(request, loaded_tile)


func _sync_terrain_quality(state: Dictionary, focus_pos: Vector3) -> Dictionary:
	state = _drop_splat_quality_if_inactive(state, focus_pos)
	if not terrain_full_quality_enabled:
		return state
	if state.get("stream_tile", null) == null:
		return state
	if not bool(state.get("wanted", false)):
		return state
	if not _tile_within_full_terrain_radius(state, focus_pos):
		return state

	var key := String(state.get("key", ""))
	var quality_mesh: Mesh = state.get("quality_terrain_mesh", null)
	var quality_source := String(state.get("quality_terrain_source", ""))
	var control_splat_requested := false
	if terrain_control_splat_quality_enabled and _tile_within_control_splat_terrain_radius(state, focus_pos):
		if quality_source == "control_splat_cache":
			return state
		if key.is_empty():
			return state
		if not _can_activate_terrain_control_splat(key, state, focus_pos):
			if quality_mesh != null:
				return state
			return state
		if terrain_control_splat_primary and _tile_is_focus_tile(state, focus_pos):
			var immediate_state := _load_terrain_control_splat_immediate(state, focus_pos)
			if String(immediate_state.get("quality_terrain_source", "")) == "control_splat_cache":
				return immediate_state
		control_splat_requested = _request_terrain_control_splat_cache(state)
		if control_splat_requested:
			return state

	if state.get("baked_tile", null) != null:
		return state

	if terrain_splat_quality_enabled and _tile_within_splat_terrain_radius(state, focus_pos):
		if quality_source == "splat_cache" or quality_source == "splat":
			return state
		if key.is_empty():
			return state
		if not _can_activate_terrain_splat(key, state, focus_pos):
			if quality_mesh != null:
				return state
			return state
		if _request_terrain_splat_cache(state):
			return state
		if terrain_splat_runtime_build_enabled:
			if _terrain_splat_tasks.has(key):
				return state
			if _terrain_splat_tasks.size() >= maxi(1, max_concurrent_terrain_splat_tasks):
				return state
			var task_id: int = WorkerThreadPool.add_task(_load_terrain_splat_task.bind(key, String(state.get("path", ""))))
			_terrain_splat_tasks[key] = task_id
			return state

	if quality_mesh != null:
		return state
	if terrain_control_splat_primary and control_splat_requested:
		return state
	if terrain_control_splat_primary and terrain_control_splat_quality_enabled and _tile_within_control_splat_terrain_radius(state, focus_pos):
		var control_path := _get_terrain_control_splat_resource_path(int(state.get("tx", 0)), int(state.get("ty", 0)))
		if ResourceLoader.exists(control_path):
			return state

	if key.is_empty() or _terrain_upgrade_tasks.has(key):
		return state
	if _terrain_upgrade_tasks.size() >= maxi(1, max_concurrent_terrain_upgrade_tasks):
		return state

	var baked_path := _get_baked_tile_resource_path(int(state.get("tx", 0)), int(state.get("ty", 0)))
	if not ResourceLoader.exists(baked_path):
		return state

	var err := ResourceLoader.load_threaded_request(
		baked_path,
		"",
		false,
		ResourceLoader.CACHE_MODE_IGNORE)
	if err == OK or err == ERR_BUSY:
		_terrain_upgrade_tasks[key] = {
			"path": baked_path,
			"tx": int(state.get("tx", 0)),
			"ty": int(state.get("ty", 0)),
		}
	return state


func _drop_splat_quality_if_inactive(state: Dictionary, focus_pos: Vector3) -> Dictionary:
	var quality_source := String(state.get("quality_terrain_source", ""))
	if quality_source == "control_splat_cache":
		if bool(state.get("wanted", false)) and _tile_within_control_splat_terrain_radius(state, focus_pos):
			return state
		return _drop_splat_quality(state)
	if quality_source != "splat_cache" and quality_source != "splat":
		return state
	if bool(state.get("wanted", false)) and _tile_within_splat_terrain_radius(state, focus_pos):
		return state

	return _drop_splat_quality(state)


func _drop_splat_quality(state: Dictionary) -> Dictionary:
	state.erase("quality_terrain_mesh")
	state.erase("quality_terrain_source")
	var key := String(state.get("key", ""))
	if not key.is_empty():
		state = _restore_quality_terrain_mesh(key, state)
		_remove_tile_mesh_cache_entries(key)
	return state


func _can_activate_terrain_splat(key: String, state: Dictionary, focus_pos: Vector3) -> bool:
	var limit := maxi(0, max_active_terrain_splat_tiles)
	if limit <= 0:
		return false
	if _tile_is_focus_tile(state, focus_pos):
		return true
	if _terrain_splat_cache_tasks.has(key) or _terrain_splat_tasks.has(key):
		return true
	return _active_terrain_splat_count(focus_pos) < limit


func _can_activate_terrain_control_splat(key: String, state: Dictionary, focus_pos: Vector3) -> bool:
	var limit := maxi(0, max_active_terrain_control_splat_tiles)
	if limit <= 0:
		return false
	if _tile_is_focus_tile(state, focus_pos):
		return true
	if _terrain_control_splat_cache_tasks.has(key):
		return true
	return _active_terrain_control_splat_count(focus_pos) < limit


func _active_terrain_control_splat_count(focus_pos: Vector3) -> int:
	var count := _terrain_control_splat_cache_tasks.size()
	for state_variant in _tile_states.values():
		if not (state_variant is Dictionary):
			continue
		var state: Dictionary = state_variant
		if not bool(state.get("wanted", false)):
			continue
		if String(state.get("quality_terrain_source", "")) != "control_splat_cache":
			continue
		if _tile_within_control_splat_terrain_radius(state, focus_pos):
			count += 1
	return count


func _active_terrain_splat_count(focus_pos: Vector3) -> int:
	var count := _terrain_splat_cache_tasks.size() + _terrain_splat_tasks.size()
	for state_variant in _tile_states.values():
		if not (state_variant is Dictionary):
			continue
		var state: Dictionary = state_variant
		if not bool(state.get("wanted", false)):
			continue
		var source := String(state.get("quality_terrain_source", ""))
		if source != "splat_cache" and source != "splat":
			continue
		if _tile_within_splat_terrain_radius(state, focus_pos):
			count += 1
	return count


func _prune_active_terrain_control_splat_tiles(focus_pos: Vector3) -> void:
	var limit := maxi(0, max_active_terrain_control_splat_tiles)
	if limit <= 0:
		for key_variant in _tile_states.keys():
			var key := String(key_variant)
			var state: Dictionary = _tile_states[key]
			if String(state.get("quality_terrain_source", "")) == "control_splat_cache":
				state = _drop_splat_quality(state)
				state["desired_tile_lod"] = _compute_desired_tile_lod(state, focus_pos)
				state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, focus_pos)
				_tile_states[key] = state
		return

	var active: Array = []
	for key_variant in _tile_states.keys():
		var key := String(key_variant)
		var state: Dictionary = _tile_states[key]
		if String(state.get("quality_terrain_source", "")) != "control_splat_cache":
			continue
		if not bool(state.get("wanted", false)) or not _tile_within_control_splat_terrain_radius(state, focus_pos):
			state = _drop_splat_quality(state)
			state["desired_tile_lod"] = _compute_desired_tile_lod(state, focus_pos)
			state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, focus_pos)
			_tile_states[key] = state
			continue
		active.append({
			"key": key,
			"dist_sq": _tile_dist_sq(state, focus_pos),
			"focus": _tile_is_focus_tile(state, focus_pos),
		})

	if active.size() <= limit:
		return

	active.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
		if bool(a["focus"]) != bool(b["focus"]):
			return bool(a["focus"])
		return float(a["dist_sq"]) < float(b["dist_sq"]))

	var keep := {}
	for i in range(mini(limit, active.size())):
		keep[String(active[i]["key"])] = true

	for row in active:
		var key := String(row["key"])
		if keep.has(key) or not _tile_states.has(key):
			continue
		var state: Dictionary = _tile_states[key]
		state = _drop_splat_quality(state)
		state["desired_tile_lod"] = _compute_desired_tile_lod(state, focus_pos)
		state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, focus_pos)
		_tile_states[key] = state


func _prune_active_terrain_splat_tiles(focus_pos: Vector3) -> void:
	var limit := maxi(0, max_active_terrain_splat_tiles)
	if limit <= 0:
		for key_variant in _tile_states.keys():
			var key := String(key_variant)
			var state: Dictionary = _tile_states[key]
			var source := String(state.get("quality_terrain_source", ""))
			if source == "splat_cache" or source == "splat":
				state = _drop_splat_quality(state)
				state["desired_tile_lod"] = _compute_desired_tile_lod(state, focus_pos)
				state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, focus_pos)
				_tile_states[key] = state
		return

	var active: Array = []
	for key_variant in _tile_states.keys():
		var key := String(key_variant)
		var state: Dictionary = _tile_states[key]
		var source := String(state.get("quality_terrain_source", ""))
		if source != "splat_cache" and source != "splat":
			continue
		if not bool(state.get("wanted", false)) or not _tile_within_splat_terrain_radius(state, focus_pos):
			state = _drop_splat_quality(state)
			state["desired_tile_lod"] = _compute_desired_tile_lod(state, focus_pos)
			state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, focus_pos)
			_tile_states[key] = state
			continue
		active.append({
			"key": key,
			"dist_sq": _tile_dist_sq(state, focus_pos),
			"focus": _tile_is_focus_tile(state, focus_pos),
		})

	if active.size() <= limit:
		return

	active.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
		if bool(a["focus"]) != bool(b["focus"]):
			return bool(a["focus"])
		return float(a["dist_sq"]) < float(b["dist_sq"]))

	var keep := {}
	for i in range(mini(limit, active.size())):
		keep[String(active[i]["key"])] = true

	for row in active:
		var key := String(row["key"])
		if keep.has(key):
			continue
		if not _tile_states.has(key):
			continue
		var state: Dictionary = _tile_states[key]
		state = _drop_splat_quality(state)
		state["desired_tile_lod"] = _compute_desired_tile_lod(state, focus_pos)
		state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, focus_pos)
		_tile_states[key] = state


func _tile_is_focus_tile(state: Dictionary, focus_pos: Vector3) -> bool:
	var focus_tile := _world_to_tile(focus_pos)
	return int(state.get("tx", -9999)) == focus_tile.x and int(state.get("ty", -9999)) == focus_tile.y


func _request_terrain_control_splat_cache(state: Dictionary) -> bool:
	var key := String(state.get("key", ""))
	if key.is_empty():
		return false
	if _terrain_control_splat_cache_tasks.has(key):
		return true

	var cache_path := _get_terrain_control_splat_resource_path(
		int(state.get("tx", 0)),
		int(state.get("ty", 0)))
	if not ResourceLoader.exists(cache_path):
		return false

	var err := ResourceLoader.load_threaded_request(
		cache_path,
		"",
		false,
		ResourceLoader.CACHE_MODE_IGNORE)
	if err == OK or err == ERR_BUSY:
		_terrain_control_splat_cache_tasks[key] = {
			"path": cache_path,
			"tx": int(state.get("tx", 0)),
			"ty": int(state.get("ty", 0)),
		}
		return true
	return false


func _load_terrain_control_splat_immediate(state: Dictionary, focus_pos: Vector3) -> Dictionary:
	var key := String(state.get("key", ""))
	if key.is_empty():
		return state
	var cache_path := _get_terrain_control_splat_resource_path(
		int(state.get("tx", 0)),
		int(state.get("ty", 0)))
	if not ResourceLoader.exists(cache_path):
		return state

	var control_tile: Resource = ResourceLoader.load(cache_path, "", ResourceLoader.CACHE_MODE_IGNORE)
	if control_tile == null or control_tile.get_script() != CONTROL_SPLAT_TILE_SCRIPT:
		push_warning("Invalid immediate control splat tile resource: %s" % cache_path)
		return state
	if int(control_tile.get("format_version")) < REQUIRED_CONTROL_SPLAT_TILE_FORMAT_VERSION:
		push_warning("Outdated immediate control splat tile resource: %s" % cache_path)
		return state

	var mesh: Mesh = control_tile.get("terrain_mesh")
	if mesh == null:
		push_warning("Immediate control splat tile has no terrain mesh: %s" % cache_path)
		return state
	mesh = mesh.duplicate(true)
	if _builder.has_method("apply_control_splat_material"):
		mesh = _builder.apply_control_splat_material(
			mesh,
			control_tile.get("texture_images"),
			control_tile.get("alpha_atlas"),
			control_tile.get("layer_index_map"))
	if mesh == null or mesh.surface_get_material(0) == null:
		push_warning("Immediate control splat material failed: %s" % cache_path)
		return state

	state["quality_terrain_mesh"] = mesh
	state["quality_terrain_source"] = "control_splat_cache"
	state["desired_tile_lod"] = _compute_desired_tile_lod(state, focus_pos)
	state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, focus_pos)
	_terrain_control_splat_cache_tasks.erase(key)
	var cache_key := _tile_mesh_cache_key(key, 0)
	_tile_mesh_cache.erase(cache_key)
	_tile_mesh_cache_order.erase(cache_key)
	return state


func _request_terrain_splat_cache(state: Dictionary) -> bool:
	var key := String(state.get("key", ""))
	if key.is_empty():
		return false
	if _terrain_splat_cache_tasks.has(key):
		return true

	var cache_path := _get_terrain_splat_resource_path(
		int(state.get("tx", 0)),
		int(state.get("ty", 0)))
	if not ResourceLoader.exists(cache_path):
		return false

	var err := ResourceLoader.load_threaded_request(
		cache_path,
		"",
		false,
		ResourceLoader.CACHE_MODE_IGNORE)
	if err == OK or err == ERR_BUSY:
		_terrain_splat_cache_tasks[key] = {
			"path": cache_path,
			"tx": int(state.get("tx", 0)),
			"ty": int(state.get("ty", 0)),
		}
		return true
	return false


func _drain_terrain_upgrade_results() -> void:
	var ready: Array[String] = []
	if not _render_budget_scheduler.has_remaining_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_UPGRADE_FINALIZE):
		return

	for key_variant in _terrain_upgrade_tasks.keys():
		if not _render_budget_scheduler.has_remaining_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_UPGRADE_FINALIZE):
			break
		var key := String(key_variant)
		var pending: Dictionary = _terrain_upgrade_tasks[key]
		var status := ResourceLoader.load_threaded_get_status(String(pending.get("path", "")))
		if status == ResourceLoader.THREAD_LOAD_LOADED or status == ResourceLoader.THREAD_LOAD_FAILED:
			if _render_budget_scheduler.try_consume_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_UPGRADE_FINALIZE):
				ready.append(key)

	var changed := false
	for key in ready:
		if not _terrain_upgrade_tasks.has(key):
			continue
		var pending: Dictionary = _terrain_upgrade_tasks[key]
		_terrain_upgrade_tasks.erase(key)
		var path := String(pending.get("path", ""))
		var status := ResourceLoader.load_threaded_get_status(path)
		if status != ResourceLoader.THREAD_LOAD_LOADED:
			continue
		var baked_tile: Resource = ResourceLoader.load_threaded_get(path)
		if baked_tile == null or baked_tile.get_script() != BAKED_TILE_SCRIPT:
			continue
		if not _tile_states.has(key):
			continue

		var state: Dictionary = _tile_states[key]
		if state.get("stream_tile", null) == null:
			continue
		if String(state.get("quality_terrain_source", "")) == "control_splat_cache":
			continue
		var baked_version: int = int(baked_tile.get("format_version"))
		if baked_version < REQUIRED_BAKED_TILE_FORMAT_VERSION:
			continue
		state["baked_tile"] = baked_tile
		var full_mesh: Mesh = baked_tile.get("full_mesh")
		if full_mesh != null:
			full_mesh = full_mesh.duplicate(true)
			if _builder.has_method("upgrade_cached_baked_mesh_materials"):
				full_mesh = _builder.upgrade_cached_baked_mesh_materials(full_mesh, 1.0, -1.25, 0.35)
			state["quality_terrain_mesh"] = full_mesh
			state["quality_terrain_source"] = "baked_full"
			_store_quality_terrain_mesh(key, full_mesh, "baked_full")
		state["desired_tile_lod"] = _compute_desired_tile_lod(state, _last_focus_pos)
		state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, _last_focus_pos)
		_tile_states[key] = state
		_tile_mesh_cache.erase(_tile_mesh_cache_key(key, 0))
		_tile_mesh_cache_order.erase(_tile_mesh_cache_key(key, 0))
		changed = true

	if changed:
		_rebuild_chunk_queues()


func _drain_terrain_control_splat_cache_results() -> void:
	var ready: Array[String] = []
	if not _render_budget_scheduler.has_remaining_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_CONTROL_SPLAT_FINALIZE):
		return

	for key_variant in _terrain_control_splat_cache_tasks.keys():
		if not _render_budget_scheduler.has_remaining_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_CONTROL_SPLAT_FINALIZE):
			break
		var key := String(key_variant)
		var pending: Dictionary = _terrain_control_splat_cache_tasks[key]
		var status := ResourceLoader.load_threaded_get_status(String(pending.get("path", "")))
		if status == ResourceLoader.THREAD_LOAD_LOADED or status == ResourceLoader.THREAD_LOAD_FAILED:
			if _render_budget_scheduler.try_consume_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_CONTROL_SPLAT_FINALIZE):
				ready.append(key)

	var changed := false
	for key in ready:
		if not _terrain_control_splat_cache_tasks.has(key):
			continue
		var pending: Dictionary = _terrain_control_splat_cache_tasks[key]
		_terrain_control_splat_cache_tasks.erase(key)
		var path := String(pending.get("path", ""))
		var status := ResourceLoader.load_threaded_get_status(path)
		if status != ResourceLoader.THREAD_LOAD_LOADED:
			continue
		var control_tile: Resource = ResourceLoader.load_threaded_get(path)
		if control_tile == null or control_tile.get_script() != CONTROL_SPLAT_TILE_SCRIPT:
			continue
		if int(control_tile.get("format_version")) < REQUIRED_CONTROL_SPLAT_TILE_FORMAT_VERSION:
			continue
		if not _tile_states.has(key):
			continue

		var state: Dictionary = _tile_states[key]
		if not bool(state.get("wanted", false)):
			continue
		if not _tile_within_control_splat_terrain_radius(state, _last_focus_pos):
			continue
		if not _can_activate_terrain_control_splat(key, state, _last_focus_pos):
			continue

		var mesh: Mesh = control_tile.get("terrain_mesh")
		if mesh == null:
			continue
		mesh = mesh.duplicate(true)
		if _builder.has_method("apply_control_splat_material"):
			mesh = _builder.apply_control_splat_material(
				mesh,
				control_tile.get("texture_images"),
				control_tile.get("alpha_atlas"),
				control_tile.get("layer_index_map"))

		state["quality_terrain_mesh"] = mesh
		state["quality_terrain_source"] = "control_splat_cache"
		state["desired_tile_lod"] = _compute_desired_tile_lod(state, _last_focus_pos)
		state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, _last_focus_pos)
		_tile_states[key] = state
		var cache_key := _tile_mesh_cache_key(key, 0)
		_tile_mesh_cache.erase(cache_key)
		_tile_mesh_cache_order.erase(cache_key)
		changed = true

	if changed:
		_rebuild_chunk_queues()


func _drain_terrain_splat_cache_results() -> void:
	var ready: Array[String] = []
	if not _render_budget_scheduler.has_remaining_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_SPLAT_CACHE_FINALIZE):
		return

	for key_variant in _terrain_splat_cache_tasks.keys():
		if not _render_budget_scheduler.has_remaining_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_SPLAT_CACHE_FINALIZE):
			break
		var key := String(key_variant)
		var pending: Dictionary = _terrain_splat_cache_tasks[key]
		var status := ResourceLoader.load_threaded_get_status(String(pending.get("path", "")))
		if status == ResourceLoader.THREAD_LOAD_LOADED or status == ResourceLoader.THREAD_LOAD_FAILED:
			if _render_budget_scheduler.try_consume_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_SPLAT_CACHE_FINALIZE):
				ready.append(key)

	var changed := false
	for key in ready:
		if not _terrain_splat_cache_tasks.has(key):
			continue
		var pending: Dictionary = _terrain_splat_cache_tasks[key]
		_terrain_splat_cache_tasks.erase(key)
		var path := String(pending.get("path", ""))
		var status := ResourceLoader.load_threaded_get_status(path)
		if status != ResourceLoader.THREAD_LOAD_LOADED:
			continue
		var splat_tile: Resource = ResourceLoader.load_threaded_get(path)
		if splat_tile == null or splat_tile.get_script() != SPLAT_TILE_SCRIPT:
			continue
		if int(splat_tile.get("format_version")) < REQUIRED_SPLAT_TILE_FORMAT_VERSION:
			continue
		if not _tile_states.has(key):
			continue

		var state: Dictionary = _tile_states[key]
		if not bool(state.get("wanted", false)):
			continue
		if not _tile_within_splat_terrain_radius(state, _last_focus_pos):
			continue
		if not _can_activate_terrain_splat(key, state, _last_focus_pos):
			continue

		var mesh: Mesh = splat_tile.get("terrain_mesh")
		if mesh == null:
			continue

		state["quality_terrain_mesh"] = mesh
		state["quality_terrain_source"] = "splat_cache"
		state["desired_tile_lod"] = _compute_desired_tile_lod(state, _last_focus_pos)
		state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, _last_focus_pos)
		_tile_states[key] = state
		var cache_key := _tile_mesh_cache_key(key, 0)
		_tile_mesh_cache.erase(cache_key)
		_tile_mesh_cache_order.erase(cache_key)
		changed = true

	if changed:
		_rebuild_chunk_queues()


func _build_high_quality_stream_terrain_mesh(stream_mesh: Mesh, full_mesh: Mesh) -> Mesh:
	if stream_mesh == null or full_mesh == null:
		return null
	var upgraded := stream_mesh.duplicate(true) as ArrayMesh
	if upgraded == null:
		return null
	var surface_count := mini(upgraded.get_surface_count(), full_mesh.get_surface_count())
	for surface_idx in surface_count:
		var material := full_mesh.surface_get_material(surface_idx)
		if material != null:
			upgraded.surface_set_material(surface_idx, material)
	return upgraded


func _load_terrain_splat_task(key: String, adt_path: String) -> void:
	var data: Dictionary = {}
	if not adt_path.is_empty():
		var loader = ClassDB.instantiate("ADTLoader")
		if loader:
			data = loader.call("load_adt", adt_path)

	_terrain_splat_result_mutex.lock()
	_terrain_splat_result_queue.append({
		"key": key,
		"data": data,
	})
	_terrain_splat_result_mutex.unlock()


func _drain_terrain_splat_results() -> void:
	var results: Array = []
	if not _render_budget_scheduler.has_remaining_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_SPLAT_BUILD):
		return

	_terrain_splat_result_mutex.lock()
	while not _terrain_splat_result_queue.is_empty() and _render_budget_scheduler.try_consume_permit(
			RENDER_BUDGET_SCHEDULER_SCRIPT.TERRAIN_SPLAT_BUILD):
		results.append(_terrain_splat_result_queue.pop_front())
	_terrain_splat_result_mutex.unlock()

	var changed := false
	for result_variant in results:
		if not (result_variant is Dictionary):
			continue
		var result: Dictionary = result_variant
		var key := String(result.get("key", ""))
		if _terrain_splat_tasks.has(key):
			WorkerThreadPool.wait_for_task_completion(int(_terrain_splat_tasks[key]))
			_terrain_splat_tasks.erase(key)
		if not _tile_states.has(key):
			continue
		var state: Dictionary = _tile_states[key]
		if not bool(state.get("wanted", false)):
			continue
		if not _tile_within_splat_terrain_radius(state, _last_focus_pos):
			continue
		if not _can_activate_terrain_splat(key, state, _last_focus_pos):
			continue

		var data: Dictionary = result.get("data", {})
		if data.is_empty():
			continue
		var payload: Dictionary = _builder.build_tile_coarse_render_payload(
			data,
			_shared_tex_cache,
			ProjectSettings.globalize_path(extracted_dir),
			maxi(0, terrain_splat_lod))
		var mesh: Mesh = payload.get("mesh", null)
		if mesh == null:
			continue

		state["quality_terrain_mesh"] = mesh
		state["quality_terrain_source"] = "splat"
		state["data"] = data
		state["tex_names"] = data.get("textures", PackedStringArray())
		state["desired_tile_lod"] = _compute_desired_tile_lod(state, _last_focus_pos)
		state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, _last_focus_pos)
		_tile_states[key] = state
		var cache_key := _tile_mesh_cache_key(key, 0)
		_tile_mesh_cache.erase(cache_key)
		_tile_mesh_cache_order.erase(cache_key)
		changed = true

	if changed:
		_rebuild_chunk_queues()


func _request_tile_water_load(state: Dictionary) -> void:
	if not enable_water:
		return
	if bool(state.get("water_loaded", false)):
		return
	var key := String(state.get("key", ""))
	var path := String(state.get("path", ""))
	if key.is_empty() or path.is_empty():
		return
	_adt_water_load_pipeline_state.enqueue_request(key, path)


func _process_water_load_queue() -> void:
	if not enable_water:
		return
	var limit := maxi(1, max_concurrent_water_tasks)
	while (
			_adt_water_load_pipeline_state.active_task_count() < limit
			and _adt_water_load_pipeline_state.pending_request_count() > 0
	):
		var request: Dictionary = _adt_water_load_pipeline_state.take_next_request()
		var key := String(request.get("key", ""))
		if key.is_empty() or _adt_water_load_pipeline_state.has_active_task(key):
			continue
		if not _tile_states.has(key):
			continue
		var path := String(request.get("path", ""))
		if path.is_empty():
			continue
		var task_id: int = WorkerThreadPool.add_task(_load_tile_water_task.bind(key, path))
		_adt_water_load_pipeline_state.remember_active_task(key, task_id)


func _load_tile_water_task(key: String, adt_path: String) -> void:
	var data: Dictionary = {}
	var loader = ClassDB.instantiate("ADTLoader")
	if loader:
		data = loader.call("load_adt", adt_path)

	_adt_water_load_pipeline_state.publish_result(key, adt_path, data)


func _drain_water_load_results() -> void:
	var results: Array = []
	if not _render_budget_scheduler.has_remaining_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.WATER_FINALIZE):
		return

	while _adt_water_load_pipeline_state.has_result() and _render_budget_scheduler.try_consume_permit(
			RENDER_BUDGET_SCHEDULER_SCRIPT.WATER_FINALIZE):
		results.append(_adt_water_load_pipeline_state.pop_result())

	for result in results:
		var key := String(result.get("key", ""))
		if key.is_empty():
			continue
		if not _adt_water_load_pipeline_state.has_active_task(key):
			continue
		WorkerThreadPool.wait_for_task_completion(
			_adt_water_load_pipeline_state.active_task_id_for(key)
		)
		_adt_water_load_pipeline_state.complete_active_task(key)
		if _shutting_down or not _tile_states.has(key):
			continue

		var state: Dictionary = _tile_states[key]
		if String(state.get("path", "")) != String(result.get("path", "")):
			continue
		var tile_root: Node = state.get("root", null)
		if tile_root == null or not is_instance_valid(tile_root):
			continue

		var data: Dictionary = result.get("data", {})
		if not data.is_empty():
			var origin: Vector3 = state.get("origin", Vector3.ZERO)
			_adt_water_scene_finalizer.attach_water_scene(
				data,
				origin,
				tile_root,
				_editor_owner_for_generated_nodes()
			)

		state["water_loaded"] = true
		_tile_states[key] = state


func _wait_for_tile_tasks() -> void:
	for pending in _tile_loading_tasks.values():
		if pending.get("mode", "raw") == "raw":
			WorkerThreadPool.wait_for_task_completion(int(pending["task_id"]))
		elif pending.get("mode", "raw") == "baked" or pending.get("mode", "raw") == "stream":
			var path: String = String(pending.get("path", ""))
			if not path.is_empty():
				var status := ResourceLoader.load_threaded_get_status(path)
				if status == ResourceLoader.THREAD_LOAD_IN_PROGRESS or status == ResourceLoader.THREAD_LOAD_LOADED:
					ResourceLoader.load_threaded_get(path)
	_tile_loading_tasks.clear()

	for task_id in _m2_group_tasks.values():
		WorkerThreadPool.wait_for_task_completion(int(task_id))
	_m2_group_tasks.clear()

	_tile_result_mutex.lock()
	_tile_result_queue.clear()
	_tile_result_mutex.unlock()

	_m2_group_result_mutex.lock()
	_m2_group_result_queue.clear()
	_m2_group_result_mutex.unlock()

	for pending in _m2_mesh_load_pipeline_state.request_records_snapshot():
		var path: String = String(pending.get("path", ""))
		if path.is_empty():
			continue
		var status := ResourceLoader.load_threaded_get_status(path)
		if status == ResourceLoader.THREAD_LOAD_IN_PROGRESS or status == ResourceLoader.THREAD_LOAD_LOADED:
			ResourceLoader.load_threaded_get(path)
	_m2_mesh_load_pipeline_state.clear()
	_m2_runtime_mesh_finalizer.clear()

	for pending in _m2_animation_load_pipeline_state.request_records_snapshot():
		var path: String = String(pending.get("path", ""))
		if path.is_empty():
			continue
		var status := ResourceLoader.load_threaded_get_status(path)
		if status == ResourceLoader.THREAD_LOAD_IN_PROGRESS or status == ResourceLoader.THREAD_LOAD_LOADED:
			ResourceLoader.load_threaded_get(path)
	_m2_animation_load_pipeline_state.clear()

	var wmo_scene_request_paths := _wmo_scene_resource_cache_state.request_paths_snapshot()
	for path_variant in wmo_scene_request_paths.values():
		var path := String(path_variant)
		if path.is_empty():
			continue
		var status := ResourceLoader.load_threaded_get_status(path)
		if status == ResourceLoader.THREAD_LOAD_IN_PROGRESS or status == ResourceLoader.THREAD_LOAD_LOADED:
			ResourceLoader.load_threaded_get(path)
	_wmo_scene_resource_cache_state.clear_transient_state()

	var wmo_render_request_paths := (
		_wmo_render_resource_cache_state.request_paths_snapshot()
	)
	for path_variant in wmo_render_request_paths.values():
		var path := String(path_variant)
		if path.is_empty():
			continue
		var status := ResourceLoader.load_threaded_get_status(path)
		if status == ResourceLoader.THREAD_LOAD_IN_PROGRESS or status == ResourceLoader.THREAD_LOAD_LOADED:
			ResourceLoader.load_threaded_get(path)
	_wmo_render_resource_cache_state.clear_transient_state()
	_wmo_render_build_queue.clear()

	for pending in _terrain_upgrade_tasks.values():
		var path := String(pending.get("path", ""))
		if path.is_empty():
			continue
		var status := ResourceLoader.load_threaded_get_status(path)
		if status == ResourceLoader.THREAD_LOAD_IN_PROGRESS or status == ResourceLoader.THREAD_LOAD_LOADED:
			ResourceLoader.load_threaded_get(path)
	_terrain_upgrade_tasks.clear()

	for pending in _terrain_control_splat_cache_tasks.values():
		if not (pending is Dictionary):
			continue
		var path := String(pending.get("path", ""))
		if path.is_empty():
			continue
		var status := ResourceLoader.load_threaded_get_status(path)
		if status == ResourceLoader.THREAD_LOAD_IN_PROGRESS or status == ResourceLoader.THREAD_LOAD_LOADED:
			ResourceLoader.load_threaded_get(path)
	_terrain_control_splat_cache_tasks.clear()

	for task_id in _terrain_splat_tasks.values():
		WorkerThreadPool.wait_for_task_completion(int(task_id))
	_terrain_splat_tasks.clear()
	for pending in _terrain_splat_cache_tasks.values():
		if not (pending is Dictionary):
			continue
		var path := String(pending.get("path", ""))
		if path.is_empty():
			continue
		var status := ResourceLoader.load_threaded_get_status(path)
		if status == ResourceLoader.THREAD_LOAD_IN_PROGRESS or status == ResourceLoader.THREAD_LOAD_LOADED:
			ResourceLoader.load_threaded_get(path)
	_terrain_splat_cache_tasks.clear()
	_terrain_splat_result_mutex.lock()
	_terrain_splat_result_queue.clear()
	_terrain_splat_result_mutex.unlock()

	for task_id in _adt_water_load_pipeline_state.active_task_ids_snapshot():
		WorkerThreadPool.wait_for_task_completion(int(task_id))
	_adt_water_load_pipeline_state.clear()


func _should_accept_loaded_tile(request: Dictionary) -> bool:
	if _tile_states.has(request["key"]):
		return false
	if not _available_tiles.has(request["key"]):
		return false
	return _is_tile_wanted_now(int(request["tx"]), int(request["ty"]))


func _is_tile_wanted_now(tx: int, ty: int) -> bool:
	var focus_tile := _world_to_tile(_last_focus_pos)
	var radius: int
	if Engine.is_editor_hint():
		radius = maxi(0, editor_preview_tile_radius)
	else:
		radius = _runtime_load_tile_radius()
	return abs(tx - focus_tile.x) <= radius and abs(ty - focus_tile.y) <= radius


func _finalize_loaded_tile(request: Dictionary, data: Dictionary) -> void:
	var key: String = request["key"]
	if _tile_states.has(key):
		return
	if data.is_empty() or not data.has("chunks"):
		push_warning("Failed to load ADT tile: %s" % request["path"])
		return

	var tile_root := Node3D.new()
	tile_root.name = "Tile_%d_%d" % [request["tx"], request["ty"]]
	_terrain_root.add_child(tile_root)
	_set_editor_owner_recursive(tile_root)

	var tile_origin: Vector3 = _builder.get_tile_origin_for_data(data)
	tile_root.position = tile_origin

	if enable_water:
		_adt_water_scene_finalizer.attach_water_scene(
			data,
			tile_origin,
			tile_root,
			_editor_owner_for_generated_nodes()
		)
	if enable_m2_placeholders:
		_build_tile_m2_placeholders(
			tile_root,
			tile_origin,
			data.get("m2_names", PackedStringArray()),
			data.get("m2_placements", []))
	var state := {
		"key": key,
		"tx": request["tx"],
		"ty": request["ty"],
		"path": request["path"],
		"origin": tile_origin,
		"data": data,
		"baked_tile": null,
		"tex_names": data.get("textures", PackedStringArray()),
		"chunks": {},
		"desired_lods": {},
		"tile_lod": -1,
		"tile_lod_node": null,
		"tile_lod_rid": RID(),
		"tile_lod_mesh": null,
		"tile_lod_visible": false,
		"prepared_tile_lod": -1,
		"prepared_tile_lod_rid": RID(),
		"prepared_tile_lod_mesh": null,
		"desired_tile_lod": -1,
		"desired_tile_lod_visible": false,
		"wanted": true,
		"retained": true,
		"root": tile_root,
		"water_loaded": true,
		"m2_names": data.get("m2_names", PackedStringArray()),
		"m2_placements": data.get("m2_placements", []),
		"m2_built": false,
		"m2_unique_keys": [],
		"m2_skipped_unique_keys": [],
		"wmo_names": data.get("wmo_names", PackedStringArray()),
		"wmo_placements": data.get("wmo_placements", []),
		"wmo_refs": [],
	}

	# Use _last_focus_pos — already set correctly for both editor and game.
	var typed_focus_position = GODOT_WORLD_POSITION_SCRIPT.new(
		_last_focus_pos.x,
		_last_focus_pos.y,
		_last_focus_pos.z
	)
	state["desired_lods"] = _compute_desired_lods(
		state,
		typed_focus_position,
		_create_terrain_chunk_lod_policy()
	)
	state["desired_tile_lod"] = -1 if not state["desired_lods"].is_empty() else _compute_desired_tile_lod(state, _last_focus_pos)
	state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, _last_focus_pos)
	_tile_states[key] = state
	state = _sync_terrain_quality(state, _last_focus_pos)
	state = _sync_detail_assets(state, _last_focus_pos)
	_tile_states[key] = state
	_rebuild_chunk_queues()
	if debug_streaming:
		print("StreamingWorld: loaded tile %d,%d" % [request["tx"], request["ty"]])


func _finalize_loaded_baked_tile(request: Dictionary, baked_tile: Resource) -> void:
	var key: String = request["key"]
	if _tile_states.has(key):
		return

	var baked_version: int = int(baked_tile.get("format_version"))
	if baked_version < REQUIRED_BAKED_TILE_FORMAT_VERSION:
		push_warning(
			"Outdated baked tile cache for %s_%d_%d (format=%d, required=%d). Falling back to raw ADT load." % [
				map_name, request["tx"], request["ty"], baked_version, REQUIRED_BAKED_TILE_FORMAT_VERSION
			]
		)
		_start_raw_tile_load_async(request)
		return

	var tile_root := Node3D.new()
	tile_root.name = "Tile_%d_%d" % [request["tx"], request["ty"]]
	_terrain_root.add_child(tile_root)
	_set_editor_owner_recursive(tile_root)

	var tile_origin: Vector3 = baked_tile.get("tile_origin")
	tile_root.position = tile_origin
	var baked_m2_names_value: Variant = baked_tile.get("m2_names")
	var baked_m2_placements_value: Variant = baked_tile.get("m2_placements")
	var baked_m2_names: PackedStringArray = baked_m2_names_value if baked_m2_names_value is PackedStringArray else PackedStringArray()
	var baked_m2_placements: Array = baked_m2_placements_value if baked_m2_placements_value is Array else []
	var baked_wmo_names_value: Variant = baked_tile.get("wmo_names")
	var baked_wmo_placements_value: Variant = baked_tile.get("wmo_placements")
	var baked_wmo_names: PackedStringArray = baked_wmo_names_value if baked_wmo_names_value is PackedStringArray else PackedStringArray()
	var baked_wmo_placements: Array = baked_wmo_placements_value if baked_wmo_placements_value is Array else []
	var state := {
		"key": key,
		"tx": request["tx"],
		"ty": request["ty"],
		"path": request["path"],
		"origin": tile_origin,
		"data": {},
		"baked_tile": baked_tile,
		"tex_names": PackedStringArray(),
		"chunks": {},
		"desired_lods": {},
		"tile_lod": -1,
		"tile_lod_node": null,
		"tile_lod_rid": RID(),
		"tile_lod_mesh": null,
		"tile_lod_visible": false,
		"prepared_tile_lod": -1,
		"prepared_tile_lod_rid": RID(),
		"prepared_tile_lod_mesh": null,
		"desired_tile_lod": -1,
		"desired_tile_lod_visible": false,
		"wanted": true,
		"retained": true,
		"root": tile_root,
		"water_loaded": false,
		"m2_names": baked_m2_names,
		"m2_placements": baked_m2_placements,
		"m2_built": false,
		"m2_unique_keys": [],
		"m2_skipped_unique_keys": [],
		"wmo_names": baked_wmo_names,
		"wmo_placements": baked_wmo_placements,
		"wmo_refs": [],
	}

	state["desired_tile_lod"] = _compute_desired_tile_lod(state, _last_focus_pos)
	state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, _last_focus_pos)
	_tile_states[key] = state
	_request_tile_water_load(state)
	state = _sync_detail_assets(state, _last_focus_pos)
	_tile_states[key] = state
	_rebuild_chunk_queues()
	if debug_streaming:
		print("StreamingWorld: loaded baked tile %d,%d" % [request["tx"], request["ty"]])


func _finalize_loaded_streaming_tile(request: Dictionary, stream_tile: Resource) -> void:
	var key: String = request["key"]
	if _tile_states.has(key):
		return

	var stream_version: int = int(stream_tile.get("format_version"))
	if stream_version < STREAMING_TILE_SCRIPT.FORMAT_VERSION:
		push_warning(
			"Outdated streaming tile cache for %s_%d_%d (format=%d, required=%d). Falling back to baked/raw ADT load." % [
				map_name, request["tx"], request["ty"], stream_version, STREAMING_TILE_SCRIPT.FORMAT_VERSION
			]
		)
		_start_raw_tile_load_async(request)
		return

	var tile_root := Node3D.new()
	tile_root.name = "Tile_%d_%d" % [request["tx"], request["ty"]]
	_terrain_root.add_child(tile_root)
	_set_editor_owner_recursive(tile_root)

	var tile_origin: Vector3 = stream_tile.get("tile_origin")
	tile_root.position = tile_origin
	var stream_m2_names_value: Variant = stream_tile.get("m2_names")
	var stream_m2_placements_value: Variant = stream_tile.get("m2_placements")
	var stream_m2_names: PackedStringArray = stream_m2_names_value if stream_m2_names_value is PackedStringArray else PackedStringArray()
	var stream_m2_placements: Array = stream_m2_placements_value if stream_m2_placements_value is Array else []
	var stream_wmo_names_value: Variant = stream_tile.get("wmo_names")
	var stream_wmo_placements_value: Variant = stream_tile.get("wmo_placements")
	var stream_wmo_names: PackedStringArray = stream_wmo_names_value if stream_wmo_names_value is PackedStringArray else PackedStringArray()
	var stream_wmo_placements: Array = stream_wmo_placements_value if stream_wmo_placements_value is Array else []
	var state := {
		"key": key,
		"tx": request["tx"],
		"ty": request["ty"],
		"path": request["path"],
		"origin": tile_origin,
		"data": {},
		"baked_tile": null,
		"stream_tile": stream_tile,
		"tex_names": PackedStringArray(),
		"chunks": {},
		"desired_lods": {},
		"tile_lod": -1,
		"tile_lod_node": null,
		"tile_lod_rid": RID(),
		"tile_lod_mesh": null,
		"tile_lod_visible": false,
		"prepared_tile_lod": -1,
		"prepared_tile_lod_rid": RID(),
		"prepared_tile_lod_mesh": null,
		"desired_tile_lod": -1,
		"desired_tile_lod_visible": false,
		"wanted": true,
		"retained": true,
		"root": tile_root,
		"water_loaded": false,
		"m2_names": stream_m2_names,
		"m2_placements": stream_m2_placements,
		"m2_built": false,
		"m2_unique_keys": [],
		"m2_skipped_unique_keys": [],
		"wmo_names": stream_wmo_names,
		"wmo_placements": stream_wmo_placements,
		"wmo_refs": [],
	}

	state = _restore_quality_terrain_mesh(key, state)
	state["desired_tile_lod"] = _compute_desired_tile_lod(state, _last_focus_pos)
	state["desired_tile_lod_visible"] = _compute_desired_tile_lod_visible(state, _last_focus_pos)
	_tile_states[key] = state
	_request_tile_water_load(state)
	state = _sync_terrain_quality(state, _last_focus_pos)
	state = _sync_detail_assets(state, _last_focus_pos)
	_tile_states[key] = state
	_rebuild_chunk_queues()
	if debug_streaming:
		print("StreamingWorld: loaded streaming tile %d,%d" % [request["tx"], request["ty"]])


func _create_tile_lod(op: Dictionary) -> void:
	var key: String = op["tile"]
	if not _tile_states.has(key):
		return

	var state: Dictionary = _tile_states[key]
	var lod: int = int(op["lod"])
	var visible: bool = bool(op.get("visible", true))
	var prepare_only: bool = bool(op.get("prepare", false))
	if int(state.get("tile_lod", -1)) == lod:
		_set_tile_lod_visible(state, visible)
		state["tile_lod_visible"] = visible
		_tile_states[key] = state
		return

	if prepare_only:
		if int(state.get("prepared_tile_lod", -1)) == lod:
			return
		var prepared_mesh := _get_tile_lod_mesh_for_state(key, state, lod)
		if prepared_mesh == null or not use_rendering_server_for_terrain:
			return
		var prepared_rid := _create_render_instance(
			prepared_mesh,
			_tile_local_to_world_transform(state, Vector3.ZERO))
		if not prepared_rid.is_valid():
			return
		RenderingServer.instance_set_visible(prepared_rid, false)
		var old_prepared_rid: RID = state.get("prepared_tile_lod_rid", RID())
		if old_prepared_rid.is_valid():
			_free_render_instance(old_prepared_rid)
		state["prepared_tile_lod"] = lod
		state["prepared_tile_lod_rid"] = prepared_rid
		state["prepared_tile_lod_mesh"] = prepared_mesh
		_tile_states[key] = state
		return

	var old_node: Node = state.get("tile_lod_node", null)
	var old_rid: RID = state.get("tile_lod_rid", RID())

	var prepared_lod: int = int(state.get("prepared_tile_lod", -1))
	var prepared_rid: RID = state.get("prepared_tile_lod_rid", RID())
	if prepared_lod == lod and prepared_rid.is_valid():
		state["tile_lod"] = lod
		state["tile_lod_node"] = null
		state["tile_lod_rid"] = prepared_rid
		state["tile_lod_mesh"] = state.get("prepared_tile_lod_mesh", null)
		state["prepared_tile_lod"] = -1
		state["prepared_tile_lod_rid"] = RID()
		state["prepared_tile_lod_mesh"] = null
		RenderingServer.instance_set_visible(prepared_rid, visible)
		if old_rid.is_valid():
			_free_render_instance(old_rid)
		if old_node:
			old_node.queue_free()
		state["tile_lod_visible"] = visible
		_tile_states[key] = state
		return

	state["tile_lod"] = lod
	state["tile_lod_node"] = null
	state["tile_lod_rid"] = RID()
	state["tile_lod_mesh"] = null

	var mesh := _get_tile_lod_mesh_for_state(key, state, lod)

	if use_rendering_server_for_terrain and mesh:
		var instance_rid := _create_render_instance(
			mesh,
			_tile_local_to_world_transform(state, Vector3.ZERO))
		if instance_rid.is_valid():
			RenderingServer.instance_set_visible(instance_rid, visible)
			state["tile_lod_rid"] = instance_rid
			state["tile_lod_mesh"] = mesh

	if not state["tile_lod_rid"].is_valid() and mesh:
		var node := MeshInstance3D.new()
		node.name = "TileLOD%d" % lod
		node.mesh = mesh
		node.visible = visible
		node.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_ON if terrain_cast_shadows else GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
		state["tile_lod_node"] = node
		state["tile_lod_mesh"] = mesh
		state["root"].add_child(node)
		_set_editor_owner_recursive(node)

	if old_rid.is_valid():
		_free_render_instance(old_rid)
	if old_node:
		old_node.queue_free()
	state["tile_lod_visible"] = visible
	_tile_states[key] = state


func _get_tile_lod_mesh_for_state(key: String, state: Dictionary, lod: int) -> Mesh:
	if lod <= 0:
		var quality_mesh: Mesh = state.get("quality_terrain_mesh", null)
		if quality_mesh != null:
			return quality_mesh

	var cache_key := _tile_mesh_cache_key(key, lod)
	var mesh: Mesh = _get_cached_tile_mesh(cache_key)
	if mesh != null:
		return mesh

	mesh = _get_baked_tile_mesh(state, lod)
	if mesh == null:
		var payload: Dictionary = _builder.build_tile_coarse_render_payload(
			state["data"],
			_shared_tex_cache,
			ProjectSettings.globalize_path(extracted_dir),
			lod)
		if not payload.is_empty():
			mesh = payload.get("mesh", null)
	if mesh:
		_store_cached_tile_mesh(cache_key, mesh)
	return mesh


func _create_chunk(op: Dictionary) -> void:
	var key: String = op["tile"]
	if not _tile_states.has(key):
		return

	var state: Dictionary = _tile_states[key]
	var chunk_id: int = int(op["chunk"])
	var lod: int = int(op["lod"])
	var chunks: Array = state["data"].get("chunks", [])
	if chunk_id < 0 or chunk_id >= chunks.size():
		return

	var chunk: Dictionary = chunks[chunk_id]
	if chunk.is_empty():
		return

	var current_chunks: Dictionary = state["chunks"]
	if current_chunks.has(chunk_id):
		_remove_chunk({"tile": key, "chunk": chunk_id})
		current_chunks = state["chunks"]

	var instance_rid := RID()
	var node: MeshInstance3D = null
	var mesh: Mesh = null

	if use_rendering_server_for_terrain:
		var payload: Dictionary = _builder.build_chunk_render_payload(
			chunk,
			state["tex_names"],
			_shared_tex_cache,
			ProjectSettings.globalize_path(extracted_dir),
			lod,
			state.get("origin", Vector3.ZERO))
		if not payload.is_empty():
			mesh = payload.get("mesh", null)
			instance_rid = _create_render_instance(
				mesh,
				_tile_local_to_world_transform(state, payload.get("position", Vector3.ZERO)))

	if not instance_rid.is_valid():
		node = _builder.build_chunk_scene(
			chunk,
			state["tex_names"],
			_shared_tex_cache,
			ProjectSettings.globalize_path(extracted_dir),
			lod,
			state.get("origin", Vector3.ZERO))

	if node == null and not instance_rid.is_valid():
		_dbg_chunks_null += 1
		current_chunks[chunk_id] = {
			"node": null,
			"rid": RID(),
			"mesh": null,
			"lod": lod,
			"empty": true,
		}
		state["chunks"] = current_chunks
		_tile_states[key] = state
		if debug_streaming and _dbg_chunks_null <= 3:
			var h: PackedFloat32Array = chunk.get("heights", PackedFloat32Array())
			print("DBG NULL chunk tile=%s id=%d lod=%d heights=%d origin=%s" % [
				key, chunk_id, lod, h.size(), chunk.get("origin", Vector3.ZERO)])
		return

	if node:
		node.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_ON if terrain_cast_shadows else GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
		state["root"].add_child(node)
		_set_editor_owner_recursive(node)
	_dbg_chunks_created += 1
	if debug_streaming and _dbg_chunks_created <= 3:
		var dbg_origin := node.global_position if node else _tile_local_to_world_transform(
			state,
			chunk.get("origin", Vector3.ZERO) - state.get("origin", Vector3.ZERO)).origin
		print("DBG CHUNK ADDED tile=%s id=%d lod=%d world_pos=%s rs=%s" % [
			key, chunk_id, lod, dbg_origin, instance_rid.is_valid()])
	current_chunks[chunk_id] = {
		"node": node,
		"rid": instance_rid,
		"mesh": mesh,
		"lod": lod,
	}
	state["chunks"] = current_chunks
	_tile_states[key] = state


func _remove_chunk(op: Dictionary) -> void:
	var key: String = op["tile"]
	if not _tile_states.has(key):
		return

	var state: Dictionary = _tile_states[key]
	var chunk_id: int = int(op["chunk"])
	var current_chunks: Dictionary = state["chunks"]
	if not current_chunks.has(chunk_id):
		return

	var entry: Dictionary = current_chunks[chunk_id]
	var node: Node = entry.get("node", null)
	var instance_rid: RID = entry.get("rid", RID())
	if instance_rid.is_valid():
		_free_render_instance(instance_rid)
	if node:
		node.queue_free()
	current_chunks.erase(chunk_id)
	state["chunks"] = current_chunks

	if not bool(state.get("retained", false)) and current_chunks.is_empty():
		_release_tile_if_unused(key)
	else:
		_tile_states[key] = state


func _remove_tile_lod(op: Dictionary) -> void:
	var key: String = op["tile"]
	if not _tile_states.has(key):
		return

	var state: Dictionary = _tile_states[key]
	var node: Node = state.get("tile_lod_node", null)
	var instance_rid: RID = state.get("tile_lod_rid", RID())
	if instance_rid.is_valid():
		_free_render_instance(instance_rid)
	if node:
		node.queue_free()
	state["tile_lod_node"] = null
	state["tile_lod_rid"] = RID()
	state["tile_lod_mesh"] = null
	state["tile_lod_visible"] = false
	state["tile_lod"] = -1

	if not bool(state.get("retained", false)) and state["chunks"].is_empty():
		_release_tile_if_unused(key)
	else:
		_tile_states[key] = state


func _set_tile_lod_visible(state: Dictionary, visible: bool) -> void:
	var instance_rid: RID = state.get("tile_lod_rid", RID())
	if instance_rid.is_valid():
		RenderingServer.instance_set_visible(instance_rid, visible)
	var node: Node = state.get("tile_lod_node", null)
	if node and is_instance_valid(node):
		node.visible = visible


func _release_tile(key: String) -> void:
	if not _tile_states.has(key):
		return

	var state: Dictionary = _tile_states[key]
	_unregister_tile_wmos(state)
	_remove_tile_m2_assets(state)
	_terrain_upgrade_tasks.erase(key)
	_terrain_control_splat_cache_tasks.erase(key)
	_terrain_splat_cache_tasks.erase(key)
	_terrain_splat_tasks.erase(key)
	_adt_water_load_pipeline_state.cancel_pending_requests(key)
	var tile_lod_rid: RID = state.get("tile_lod_rid", RID())
	if tile_lod_rid.is_valid():
		_free_render_instance(tile_lod_rid)
	var prepared_tile_lod_rid: RID = state.get("prepared_tile_lod_rid", RID())
	if prepared_tile_lod_rid.is_valid():
		_free_render_instance(prepared_tile_lod_rid)
	for entry in state.get("chunks", {}).values():
		var chunk_rid: RID = entry.get("rid", RID())
		if chunk_rid.is_valid():
			_free_render_instance(chunk_rid)
	var tile_root: Node = state.get("root", null)
	if tile_root:
		tile_root.queue_free()
	_remove_tile_mesh_cache_entries(key)
	_tile_states.erase(key)


func _clear_streamed_world() -> void:
	_tile_load_queue.clear()
	_chunk_remove_queue.clear()
	_chunk_create_queue.clear()
	_tile_lod_remove_queue.clear()
	_tile_lod_create_queue.clear()
	_terrain_upgrade_tasks.clear()
	_terrain_control_splat_cache_tasks.clear()
	_terrain_splat_cache_tasks.clear()
	_terrain_splat_tasks.clear()
	_terrain_splat_result_mutex.lock()
	_terrain_splat_result_queue.clear()
	_terrain_splat_result_mutex.unlock()
	_adt_water_load_pipeline_state.clear()
	_detail_asset_queue.clear()
	_detail_asset_queued.clear()
	for key in _m2_build_queue_state.job_keys():
		_cancel_m2_build_job(String(key))
	_m2_build_queue_state.clear()
	_m2_unique_placement_registry.clear()
	_m2_mesh_load_pipeline_state.clear()
	_m2_runtime_mesh_finalizer.clear()
	_m2_animation_load_pipeline_state.clear()
	_wmo_build_jobs.clear()
	_wmo_build_queue.clear()
	_wmo_scene_resource_cache_state.clear_transient_state()
	_wmo_render_resource_cache_state.clear_transient_state()
	_wmo_render_build_queue.clear()

	_tile_result_mutex.lock()
	_tile_result_queue.clear()
	_tile_result_mutex.unlock()

	for key in _tile_states.keys():
		var state: Dictionary = _tile_states[key]
		var tile_lod_rid: RID = state.get("tile_lod_rid", RID())
		if tile_lod_rid.is_valid():
			_free_render_instance(tile_lod_rid)
		var prepared_tile_lod_rid: RID = state.get("prepared_tile_lod_rid", RID())
		if prepared_tile_lod_rid.is_valid():
			_free_render_instance(prepared_tile_lod_rid)
		for entry in state.get("chunks", {}).values():
			var chunk_rid: RID = entry.get("rid", RID())
			if chunk_rid.is_valid():
				_free_render_instance(chunk_rid)
		var tile_root: Node = state.get("root", null)
		if tile_root:
			_queue_free_streamed_node(tile_root)

	_tile_states.clear()
	_clear_tile_mesh_cache()
	_clear_quality_terrain_mesh_cache()
	_clear_wmo_registry()
	if not _shutting_down:
		_ensure_world_wmo_root()
		if enable_wmo:
			_place_global_wmo()


## During world shutdown, parented nodes are recursively owned by the world
## root. Queueing them separately from `_exit_tree()` can detach them from that
## deletion and leave empty Node objects alive at engine teardown.
func _queue_free_streamed_node(node: Node) -> void:
	if node == null or not is_instance_valid(node):
		return
	if _shutting_down and node.is_inside_tree():
		return
	node.queue_free()


func _reset_terrain_root_children() -> void:
	if _terrain_root == null:
		return

	for child in _terrain_root.get_children():
		_terrain_root.remove_child(child)
		child.free()


func _compute_desired_lods(
		state: Dictionary,
		typed_focus_position: RefCounted,
		terrain_chunk_lod_policy: RefCounted
) -> Dictionary:
	var chunks: Array = state["data"].get("chunks", [])
	return _terrain_chunk_lod_planner.plan(chunks, typed_focus_position, terrain_chunk_lod_policy)


func _create_terrain_chunk_lod_policy() -> RefCounted:
	return TERRAIN_CHUNK_LOD_POLICY_SCRIPT.new(
		use_chunk_terrain_lods,
		lod0_radius_chunks,
		lod1_radius_chunks,
		lod2_radius_chunks,
		CHUNK_SIZE
	)


func _runtime_load_tile_radius() -> int:
	return STREAMING_TARGET_POLICY_SCRIPT.calculate_load_tile_radius(
		lod2_radius_chunks,
		lod2_tile_radius,
		warm_tile_margin,
		prewarm_tile_margin
	)


func _streaming_target_policy():
	return STREAMING_TARGET_POLICY_SCRIPT.new(
		lod2_radius_chunks,
		lod2_tile_radius,
		warm_tile_margin,
		prewarm_tile_margin,
		retain_tile_margin,
		boundary_prefetch_threshold
	)


func _compute_desired_tile_lod(state: Dictionary, cam_pos: Vector3) -> int:
	var tile_dist_sq: float = _tile_dist_sq(state, cam_pos)
	var chunk_radius_sq: float = (lod2_radius_chunks * CHUNK_SIZE) ** 2
	var tile_radius_sq: float = (lod2_tile_radius * TILE_SIZE) ** 2
	var prewarm_radius_sq: float = (_runtime_load_tile_radius() * TILE_SIZE) ** 2
	if state.get("stream_tile", null) != null:
		if _state_has_applicable_quality_terrain(state, cam_pos):
			return 0
		if _should_hide_terrain_fallback_for_quality(state, cam_pos):
			return -1
		if state.get("baked_tile", null) != null and _tile_within_full_terrain_radius(state, cam_pos):
			return 0
		return 3 if tile_dist_sq <= prewarm_radius_sq else -1

	if not use_chunk_terrain_lods:
		if tile_dist_sq <= chunk_radius_sq:
			return 0
		if tile_dist_sq <= tile_radius_sq:
			return 3
		if prewarm_tile_lod_instances and tile_dist_sq <= prewarm_radius_sq:
			return 3
		return -1

	if tile_dist_sq <= chunk_radius_sq:
		return -1
	if tile_dist_sq <= tile_radius_sq:
		return 3
	return -1


func _compute_desired_tile_lod_visible(state: Dictionary, cam_pos: Vector3) -> bool:
	var tile_dist_sq: float = _tile_dist_sq(state, cam_pos)
	var chunk_radius_sq: float = (lod2_radius_chunks * CHUNK_SIZE) ** 2
	var tile_radius_sq: float = (lod2_tile_radius * TILE_SIZE) ** 2
	return tile_dist_sq <= maxf(chunk_radius_sq, tile_radius_sq)


func _tile_within_full_terrain_radius(state: Dictionary, cam_pos: Vector3) -> bool:
	var radius_tiles := maxi(0, terrain_full_quality_radius_tiles)
	var radius_sq := (float(radius_tiles) * TILE_SIZE) ** 2
	return _tile_dist_sq(state, cam_pos) <= radius_sq


func _tile_within_control_splat_terrain_radius(state: Dictionary, cam_pos: Vector3) -> bool:
	var radius_tiles := maxi(0, terrain_control_splat_radius_tiles)
	var radius_sq := (float(radius_tiles) * TILE_SIZE) ** 2
	return _tile_dist_sq(state, cam_pos) <= radius_sq


func _tile_within_splat_terrain_radius(state: Dictionary, cam_pos: Vector3) -> bool:
	var radius_tiles := maxi(0, terrain_splat_radius_tiles)
	var radius_sq := (float(radius_tiles) * TILE_SIZE) ** 2
	return _tile_dist_sq(state, cam_pos) <= radius_sq


func _should_hide_terrain_fallback_for_quality(state: Dictionary, cam_pos: Vector3) -> bool:
	if not terrain_control_splat_hide_fallback:
		return false
	if not terrain_control_splat_primary or not terrain_control_splat_quality_enabled:
		return false
	if not bool(state.get("wanted", false)):
		return false
	var radius_tiles := maxi(0, terrain_control_splat_hide_fallback_radius_tiles)
	var radius_sq := (float(radius_tiles) * TILE_SIZE) ** 2
	if _tile_dist_sq(state, cam_pos) > radius_sq:
		return false
	if String(state.get("quality_terrain_source", "")) == "control_splat_cache":
		return false
	var control_path := _get_terrain_control_splat_resource_path(int(state.get("tx", 0)), int(state.get("ty", 0)))
	return ResourceLoader.exists(control_path)


func _should_prepare_full_tile_lod(state: Dictionary, cam_pos: Vector3) -> bool:
	if use_chunk_terrain_lods or not prewarm_tile_lod_instances:
		return false
	if state.get("stream_tile", null) != null:
		return false
	if int(state.get("tile_lod", -1)) <= 0:
		return false
	if int(state.get("prepared_tile_lod", -1)) == 0:
		return false

	var tile_dist_sq: float = _tile_dist_sq(state, cam_pos)
	var chunk_radius: float = float(lod2_radius_chunks) * CHUNK_SIZE
	var prepare_radius: float = chunk_radius + maxf(0.0, full_lod_prewarm_tiles) * TILE_SIZE
	return tile_dist_sq <= prepare_radius * prepare_radius


func _is_tile_chunk_set_ready(state: Dictionary) -> bool:
	var desired_lods: Dictionary = state.get("desired_lods", {})
	var current_chunks: Dictionary = state.get("chunks", {})
	for chunk_id in desired_lods.keys():
		if not current_chunks.has(chunk_id):
			return false
		if int(current_chunks[chunk_id].get("lod", -999)) != int(desired_lods[chunk_id]):
			return false
	return true


func _tile_dist_sq(state: Dictionary, cam_pos: Vector3) -> float:
	var tile_coordinate = ADT_TILE_COORDINATE_SCRIPT.new(int(state["tx"]), int(state["ty"]))
	var north_west_position = COORDINATE_MAPPER_SCRIPT.adt_tile_local_to_godot(
		tile_coordinate,
		ADT_TILE_LOCAL_POSITION_SCRIPT.new(0.0, 0.0, 0.0))
	var south_east_position = COORDINATE_MAPPER_SCRIPT.adt_tile_local_to_godot(
		tile_coordinate,
		ADT_TILE_LOCAL_POSITION_SCRIPT.new(TILE_SIZE, 0.0, TILE_SIZE))
	var min_x: float = north_west_position.x_units
	var max_x: float = south_east_position.x_units
	var min_z: float = north_west_position.z_units
	var max_z: float = south_east_position.z_units

	var dx := 0.0
	if cam_pos.x < min_x:
		dx = min_x - cam_pos.x
	elif cam_pos.x > max_x:
		dx = cam_pos.x - max_x

	var dz := 0.0
	if cam_pos.z < min_z:
		dz = min_z - cam_pos.z
	elif cam_pos.z > max_z:
		dz = cam_pos.z - max_z

	return dx * dx + dz * dz


func _is_tile_queued(key: String) -> bool:
	if _tile_loading_tasks.has(key):
		return true
	for request in _tile_load_queue:
		if request["key"] == key:
			return true
	return false


func _capture_streaming_focus_from_source() -> void:
	if streaming_focus_source_path == NodePath():
		return
	var focus_source := get_node_or_null(streaming_focus_source_path) as Node3D
	if focus_source == null:
		if not _missing_focus_source_reported:
			push_warning("Streaming focus source is missing or is not Node3D: %s" % streaming_focus_source_path)
			_missing_focus_source_reported = true
		return
	_missing_focus_source_reported = false
	_set_streaming_focus_from_vector3(focus_source.global_position)


func _set_streaming_focus_from_vector3(world_position: Vector3) -> void:
	var typed_world_position = GODOT_WORLD_POSITION_SCRIPT.new(
		world_position.x,
		world_position.y,
		world_position.z
	)
	set_streaming_focus(STREAMING_FOCUS_SCRIPT.new(typed_world_position))


func _streaming_focus_to_vector3() -> Vector3:
	var world_position: GodotWorldPosition = _streaming_focus.world_position
	return Vector3(world_position.x_units, world_position.y_units, world_position.z_units)


func _get_editor_view_camera() -> Camera3D:
	# EditorInterface owns this camera. This method is the explicit editor adapter;
	# runtime streaming never discovers a viewport camera.
	var editor_viewport := EditorInterface.get_editor_viewport_3d(0)
	if editor_viewport == null:
		return null
	return editor_viewport.get_camera_3d()


func _get_auto_position_camera() -> Camera3D:
	if camera_path == NodePath():
		return null
	return get_node_or_null(camera_path) as Camera3D


func _can_use_baked_tile_cache() -> bool:
	return use_baked_tile_cache and not use_chunk_terrain_lods


func _get_baked_tile_resource_path(tx: int, ty: int) -> String:
	return baked_tile_cache_dir.path_join(map_name).path_join("%s_%d_%d.res" % [map_name, tx, ty])


func _get_streaming_tile_resource_path(tx: int, ty: int) -> String:
	return streaming_tile_cache_dir.path_join(map_name).path_join("%s_%d_%d.res" % [map_name, tx, ty])


func _get_terrain_control_splat_resource_path(tx: int, ty: int) -> String:
	return terrain_control_splat_cache_dir.path_join(map_name).path_join("%s_%d_%d.res" % [map_name, tx, ty])


func _get_terrain_splat_resource_path(tx: int, ty: int) -> String:
	return terrain_splat_cache_dir.path_join(map_name).path_join("%s_%d_%d.res" % [map_name, tx, ty])


func _terrain_splat_queue_size() -> int:
	return _terrain_splat_cache_tasks.size() + _terrain_splat_tasks.size() + _terrain_splat_result_queue.size()


func _state_has_applicable_quality_terrain(state: Dictionary, cam_pos: Vector3) -> bool:
	var quality_mesh: Mesh = state.get("quality_terrain_mesh", null)
	if quality_mesh == null:
		return false
	var quality_source := String(state.get("quality_terrain_source", ""))
	if quality_source == "control_splat_cache":
		return _tile_within_control_splat_terrain_radius(state, cam_pos)
	if quality_source == "splat_cache" or quality_source == "splat":
		return _tile_within_splat_terrain_radius(state, cam_pos)
	if quality_source == "baked_full":
		return _tile_within_full_terrain_radius(state, cam_pos)
	return _tile_within_full_terrain_radius(state, cam_pos)


func _get_baked_tile_mesh(state: Dictionary, lod: int) -> Mesh:
	var stream_tile: Resource = state.get("stream_tile", null)
	var baked_tile: Resource = state.get("baked_tile", null)
	if stream_tile != null:
		if lod <= 0:
			var quality_mesh: Mesh = state.get("quality_terrain_mesh", null)
			if quality_mesh != null:
				return quality_mesh
		return stream_tile.get("terrain_mesh")
	if baked_tile == null:
		return null
	if lod <= 0:
		return baked_tile.get("full_mesh")
	return baked_tile.get("coarse_mesh")


func _should_refresh_focus(focus_pos: Vector3) -> bool:
	if not _has_refresh_focus:
		return true

	var current_tile := _world_to_tile(focus_pos)
	var last_tile := _world_to_tile(_last_refresh_focus_pos)
	if current_tile != last_tile:
		return true

	var min_distance := maxf(1.0, streaming_update_distance)
	return focus_pos.distance_squared_to(_last_refresh_focus_pos) >= min_distance * min_distance


func _note_refresh_focus(focus_pos: Vector3) -> void:
	_last_refresh_focus_pos = focus_pos
	_has_refresh_focus = true


func _reset_refresh_focus() -> void:
	_last_refresh_focus_pos = Vector3.ZERO
	_has_refresh_focus = false


func _tile_mesh_cache_key(tile_key: String, lod: int) -> String:
	return "%s|%d" % [tile_key, lod]


func _ensure_world_wmo_root() -> void:
	if _world_wmo_root != null and is_instance_valid(_world_wmo_root):
		return
	_world_wmo_root = Node3D.new()
	_world_wmo_root.name = "WMOs"
	_terrain_root.add_child(_world_wmo_root)
	_set_editor_owner_recursive(_world_wmo_root)


func _wdt_has_global_wmo() -> bool:
	return bool(_wdt_info.get("has_global_wmo", false)) and _wdt_info.has("global_wmo_name")


func _place_global_wmo() -> void:
	if not _wdt_has_global_wmo():
		return
	if _wmo_placement_registry.contains("global"):
		return

	_ensure_world_wmo_root()

	var rel_path := String(_wdt_info.get("global_wmo_name", "")).replace("\\", "/")
	if rel_path.is_empty():
		return

	var placement: Dictionary = _wdt_info.get("global_wmo_placement", {})
	if placement.is_empty():
		placement = {
			"pos":   Vector3.ZERO,
			"rot":   Vector3.ZERO,
			"scale": 1.0,
		}

	var instance := _instantiate_wmo_world(rel_path, placement)
	if instance == null:
		push_warning("StreamingWorld: failed to load global WMO: %s" % rel_path)
		return

	_world_wmo_root.add_child(instance)
	_set_editor_owner_recursive(instance)
	_register_wmo_instance_reference("global", "__global__", instance)


func _sync_detail_assets(state: Dictionary, focus_pos: Vector3) -> Dictionary:
	if not bool(state.get("wanted", false)):
		_disable_tile_detail_assets(state)
		return state

	var tile_root: Node = state.get("root", null)
	if tile_root == null or not is_instance_valid(tile_root):
		return state

	if enable_wmo and _tile_within_detail_radius(state, focus_pos, wmo_tile_radius):
		var refs: Array = state.get("wmo_refs", [])
		var wmo_placements: Array = state.get("wmo_placements", [])
		var wmo_key := String(state.get("key", ""))
		if refs.is_empty() and not wmo_placements.is_empty() and not _wmo_build_jobs.has(wmo_key):
			_enqueue_detail_asset_sync(String(state.get("key", "")))
	else:
		_unregister_tile_wmos(state)

	if enable_m2_assets and _tile_within_detail_radius(state, focus_pos, m2_tile_radius):
		var key := String(state.get("key", ""))
		var m2_placements: Array = state.get("m2_placements", [])
		if (
				not bool(state.get("m2_built", false))
				and not m2_placements.is_empty()
				and not _m2_group_tasks.has(key)
				and not _m2_build_queue_state.has_job(key)
		):
			_enqueue_detail_asset_sync(key)
	else:
		_remove_tile_m2_assets(state)
		state["m2_built"] = false

	return state


func _enqueue_detail_asset_sync(key: String) -> void:
	if key.is_empty() or _detail_asset_queued.has(key):
		return
	_detail_asset_queued[key] = true
	_detail_asset_queue.append(key)


func _process_detail_asset_queue() -> void:
	while not _detail_asset_queue.is_empty() and _render_budget_scheduler.try_consume_permit(
			RENDER_BUDGET_SCHEDULER_SCRIPT.DETAIL_ASSET_SYNC):
		var key: String = String(_detail_asset_queue.pop_front())
		_detail_asset_queued.erase(key)
		_process_detail_asset_key(key)


func _process_detail_asset_key(key: String) -> void:
	if not _tile_states.has(key):
		return
	var state: Dictionary = _tile_states[key]
	if not bool(state.get("wanted", false)):
		_disable_tile_detail_assets(state)
		_tile_states[key] = state
		return

	var tile_root: Node = state.get("root", null)
	if tile_root == null or not is_instance_valid(tile_root):
		return

	if enable_wmo and _tile_within_detail_radius(state, _last_focus_pos, wmo_tile_radius):
		var refs: Array = state.get("wmo_refs", [])
		if refs.is_empty():
			_register_tile_wmos(
				state,
				state.get("wmo_names", PackedStringArray()),
				state.get("wmo_placements", []))
	else:
		_unregister_tile_wmos(state)

	if enable_m2_assets and _tile_within_detail_radius(state, _last_focus_pos, m2_tile_radius):
		if not bool(state.get("m2_built", false)):
			var m2_request := _request_tile_m2_assets(
				tile_root as Node3D,
				String(state.get("key", "")),
				state.get("origin", Vector3.ZERO),
				state.get("m2_names", PackedStringArray()),
				state.get("m2_placements", []))
			state["m2_built"] = bool(m2_request.get("built", false))
			if m2_request.has("unique_keys"):
				state["m2_unique_keys"] = m2_request.get("unique_keys", [])
			if m2_request.has("skipped_unique_keys"):
				state["m2_skipped_unique_keys"] = m2_request.get("skipped_unique_keys", [])
	else:
		_remove_tile_m2_assets(state)
		state["m2_built"] = false

	_tile_states[key] = state


func _disable_tile_detail_assets(state: Dictionary) -> void:
	_unregister_tile_wmos(state)
	_remove_tile_m2_assets(state)
	state["m2_built"] = false


func _tile_within_detail_radius(state: Dictionary, focus_pos: Vector3, radius_tiles: int) -> bool:
	if radius_tiles < 0:
		return true
	var radius_sq := (float(radius_tiles) * TILE_SIZE) ** 2
	return _tile_dist_sq(state, focus_pos) <= radius_sq


func _register_tile_wmos(state: Dictionary, wmo_names: PackedStringArray, wmo_placements: Array) -> void:
	var refs: Array = state.get("wmo_refs", [])
	if wmo_names.is_empty() or wmo_placements.is_empty():
		state["wmo_refs"] = refs
		return

	_ensure_world_wmo_root()
	var tile_key: String = state["key"]
	if _wmo_build_jobs.has(tile_key):
		state["wmo_building"] = true
		return

	_wmo_build_jobs[tile_key] = {
		"names": wmo_names,
		"placements": wmo_placements,
		"index": 0,
		"refs": refs,
	}
	_wmo_build_queue.append(tile_key)
	state["wmo_building"] = true


func _process_wmo_build_jobs() -> void:
	_drain_wmo_render_loads()
	_drain_wmo_scene_loads()
	while _render_budget_scheduler.has_remaining_permit(
			RENDER_BUDGET_SCHEDULER_SCRIPT.WMO_BUILD) and not _wmo_build_queue.is_empty():
		var tile_key: String = String(_wmo_build_queue.front())
		if not _wmo_build_jobs.has(tile_key):
			_wmo_build_queue.pop_front()
			continue
		if not _tile_states.has(tile_key):
			_wmo_build_jobs.erase(tile_key)
			_wmo_build_queue.pop_front()
			continue

		var state: Dictionary = _tile_states[tile_key]
		if not bool(state.get("wanted", false)) or not _tile_within_detail_radius(state, _last_focus_pos, wmo_tile_radius):
			_cancel_wmo_build_job(tile_key)
			_unregister_tile_wmos(state)
			_tile_states[tile_key] = state
			continue

		var job: Dictionary = _wmo_build_jobs[tile_key]
		var wmo_names: PackedStringArray = job.get("names", PackedStringArray())
		var wmo_placements: Array = job.get("placements", [])
		var refs: Array = job.get("refs", [])
		var index: int = int(job.get("index", 0))
		if index >= wmo_placements.size():
			state["wmo_refs"] = refs
			state["wmo_building"] = false
			_tile_states[tile_key] = state
			_wmo_build_jobs.erase(tile_key)
			_wmo_build_queue.pop_front()
			continue

		var placement_variant = wmo_placements[index]
		var advance_job := true
		if placement_variant is Dictionary:
			var placement: Dictionary = placement_variant
			var name_id: int = int(placement.get("name_id", -1))
			if name_id >= 0 and name_id < wmo_names.size():
				var unique_key := _wmo_placement_resolver.resolve_unique_key(
					placement,
					tile_key,
					index
				)
				if _wmo_placement_registry.contains(unique_key):
					_wmo_placement_registry.add_reference(unique_key, tile_key)
					refs.append(unique_key)
				else:
					var rel_path: String = str(wmo_names[name_id]).replace("\\", "/")
					var render_resource := _get_wmo_render_or_request(rel_path)
					if render_resource != null:
						var instance := _instantiate_wmo_render_root(rel_path, placement)
						if instance != null:
							_world_wmo_root.add_child(instance)
							_set_editor_owner_recursive(instance)
							_register_wmo_instance_reference(unique_key, tile_key, instance)
							refs.append(unique_key)
							_start_wmo_render_build(unique_key, instance, render_resource)
					else:
						var normalized_rel := _wmo_placement_resolver.normalize_relative_path(
							rel_path
						)
						if _wmo_render_resource_cache_state.has_request(normalized_rel):
							advance_job = false
						else:
							var scene := _get_wmo_scene_or_request(rel_path)
							if scene == null:
								if _wmo_scene_resource_cache_state.has_request(normalized_rel):
									advance_job = false
								else:
									var live_instance := _instantiate_wmo_world(rel_path, placement)
									if live_instance != null:
										_prepare_runtime_wmo_instance(live_instance)
										_world_wmo_root.add_child(live_instance)
										_set_editor_owner_recursive(live_instance)
										_register_wmo_instance_reference(
											unique_key,
											tile_key,
											live_instance
										)
										refs.append(unique_key)
							else:
								var scene_instance := _instantiate_wmo_scene(rel_path, scene, placement)
								if scene_instance != null:
									_prepare_runtime_wmo_instance(scene_instance)
									_world_wmo_root.add_child(scene_instance)
									_set_editor_owner_recursive(scene_instance)
									_register_wmo_instance_reference(
										unique_key,
										tile_key,
										scene_instance
									)
									refs.append(unique_key)
				if advance_job:
					job["refs"] = refs

		if not advance_job:
			_wmo_build_jobs[tile_key] = job
			state["wmo_refs"] = refs
			state["wmo_building"] = true
			_tile_states[tile_key] = state
			_wmo_build_queue.pop_front()
			_wmo_build_queue.append(tile_key)
			_render_budget_scheduler.try_consume_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.WMO_BUILD)
			continue

		job["index"] = index + 1
		_wmo_build_jobs[tile_key] = job
		state["wmo_refs"] = refs
		state["wmo_building"] = true
		_tile_states[tile_key] = state
		_render_budget_scheduler.try_consume_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.WMO_BUILD)

		if int(job["index"]) >= wmo_placements.size():
			state["wmo_refs"] = refs
			state["wmo_building"] = false
			_tile_states[tile_key] = state
			_wmo_build_jobs.erase(tile_key)
			_wmo_build_queue.pop_front()


func _drain_wmo_scene_loads() -> void:
	_wmo_scene_resource_finalizer.poll_terminal_requests(
		_wmo_scene_resource_cache_state
	)


func _drain_wmo_render_loads() -> void:
	_wmo_render_resource_finalizer.poll_terminal_requests(
		_wmo_render_resource_cache_state
	)


func _get_wmo_render_or_request(rel_path: String) -> Resource:
	var normalized_rel := _wmo_placement_resolver.normalize_relative_path(rel_path)
	if normalized_rel.is_empty():
		return null
	if _wmo_render_resource_cache_state.contains_resource(normalized_rel):
		return _wmo_render_resource_cache_state.resource_for(normalized_rel)
	if _wmo_render_resource_cache_state.is_missing(normalized_rel):
		return null
	if _wmo_render_resource_cache_state.has_request(normalized_rel):
		return null

	var cache_path := wmo_render_cache_dir.path_join(normalized_rel.get_basename() + ".res")
	if not ResourceLoader.exists(cache_path):
		return null

	var err := ResourceLoader.load_threaded_request(
		cache_path,
		"",
		false,
		ResourceLoader.CACHE_MODE_REUSE)
	if err == OK or err == ERR_BUSY:
		_wmo_render_resource_cache_state.remember_request(normalized_rel, cache_path)
	return null


func _get_wmo_scene_or_request(rel_path: String) -> PackedScene:
	var normalized_rel := _wmo_placement_resolver.normalize_relative_path(rel_path)
	if normalized_rel.is_empty():
		return null
	if _wmo_scene_resource_cache_state.contains_scene(normalized_rel):
		return _wmo_scene_resource_cache_state.scene_for(normalized_rel)
	if _wmo_scene_resource_cache_state.is_missing(normalized_rel):
		return null
	if _wmo_scene_resource_cache_state.has_request(normalized_rel):
		return null

	var cache_path := wmo_cache_dir.path_join(normalized_rel.get_basename() + ".tscn")
	if not ResourceLoader.exists(cache_path):
		_wmo_scene_resource_cache_state.mark_missing(normalized_rel)
		return null

	if wmo_max_runtime_scene_mb > 0.0:
		var size_bytes := _get_resource_file_size(cache_path)
		var max_bytes := int(wmo_max_runtime_scene_mb * 1024.0 * 1024.0)
		if size_bytes > max_bytes:
			_wmo_scene_resource_cache_state.mark_missing(normalized_rel)
			if debug_streaming:
				print("StreamingWorld: skipping large WMO %.2f MB: %s" % [
					float(size_bytes) / (1024.0 * 1024.0),
					normalized_rel,
				])
			return null

	var err := ResourceLoader.load_threaded_request(
		cache_path,
		"",
		false,
		ResourceLoader.CACHE_MODE_REUSE)
	if err == OK or err == ERR_BUSY:
		_wmo_scene_resource_cache_state.remember_request(normalized_rel, cache_path)
	else:
		_wmo_scene_resource_cache_state.mark_missing(normalized_rel)
	return null


func _instantiate_wmo_render_root(rel_path: String, placement: Dictionary) -> Node3D:
	var root := Node3D.new()
	root.name = rel_path.get_file().get_basename()
	root.transform = _wmo_placement_resolver.resolve_world_transform(placement)
	return root


func _start_wmo_render_build(unique_key: String, root: Node3D, render_resource: Resource) -> void:
	_wmo_render_build_queue.enqueue(unique_key, root, render_resource)


func _process_wmo_render_build_jobs() -> void:
	while _render_budget_scheduler.has_remaining_permit(
			RENDER_BUDGET_SCHEDULER_SCRIPT.WMO_RENDER_GROUP_BUILD) and _wmo_render_build_queue.has_pending():
		var unique_key := _wmo_render_build_queue.front_key()
		if not _wmo_render_build_queue.has_job(unique_key):
			_wmo_render_build_queue.pop_front()
			continue

		var root: Node = _wmo_render_build_queue.root_for(unique_key)
		if root == null or not is_instance_valid(root):
			_cancel_wmo_render_build_job(unique_key)
			continue

		var render_resource: Resource = _wmo_render_build_queue.render_resource_for(unique_key)
		if render_resource == null:
			_cancel_wmo_render_build_job(unique_key)
			continue

		var meshes: Array = render_resource.get("meshes")
		var mesh_transforms: Array = render_resource.get("mesh_transforms")
		var mesh_names: PackedStringArray = render_resource.get("mesh_names")
		var mesh_index := _wmo_render_build_queue.mesh_index_for(unique_key)
		var multimeshes: Array = render_resource.get("multimeshes")
		var multimesh_transforms: Array = render_resource.get("multimesh_transforms")
		var multimesh_names: PackedStringArray = render_resource.get("multimesh_names")
		var multimesh_index := _wmo_render_build_queue.multimesh_index_for(unique_key)
		var build_step: Dictionary = _wmo_render_build_step_planner.plan_step(
			meshes.size(),
			mesh_index,
			multimeshes.size(),
			multimesh_index
		)
		var selected_index := int(build_step["selected_index"])
		if build_step["operation"] == WMO_RENDER_BUILD_STEP_PLANNER_SCRIPT.OPERATION_MESH:
			var mesh := meshes[selected_index] as Mesh
			if mesh != null:
				_refresh_cached_wmo_mesh_materials(mesh)
				var mesh_instance := MeshInstance3D.new()
				mesh_instance.name = (
					mesh_names[selected_index]
					if selected_index < mesh_names.size()
					else "Group_%d" % selected_index
				)
				mesh_instance.mesh = mesh
				if selected_index < mesh_transforms.size():
					mesh_instance.transform = mesh_transforms[selected_index]
				mesh_instance.cast_shadow = (
					GeometryInstance3D.SHADOW_CASTING_SETTING_ON
					if wmo_cast_shadows
					else GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
				)
				if wmo_visibility_range > 0.0:
					mesh_instance.visibility_range_end = wmo_visibility_range
					mesh_instance.visibility_range_end_margin = CHUNK_SIZE
				(root as Node3D).add_child(mesh_instance)
				_set_editor_owner_recursive(mesh_instance)
			_wmo_render_build_queue.adopt_cursors(
				unique_key,
				int(build_step["next_mesh_index"]),
				int(build_step["next_multimesh_index"])
			)
			_render_budget_scheduler.try_consume_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.WMO_RENDER_GROUP_BUILD)
			continue

		if build_step["operation"] == WMO_RENDER_BUILD_STEP_PLANNER_SCRIPT.OPERATION_MULTIMESH:
			var multimesh := multimeshes[selected_index] as MultiMesh
			if multimesh != null:
				_refresh_cached_wmo_mesh_materials(multimesh.mesh)
				var multimesh_instance := MultiMeshInstance3D.new()
				multimesh_instance.name = (
					multimesh_names[selected_index]
					if selected_index < multimesh_names.size()
					else "DoodadGroup_%d" % selected_index
				)
				multimesh_instance.multimesh = multimesh
				if selected_index < multimesh_transforms.size():
					multimesh_instance.transform = multimesh_transforms[selected_index]
				multimesh_instance.cast_shadow = (
					GeometryInstance3D.SHADOW_CASTING_SETTING_ON
					if wmo_cast_shadows
					else GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
				)
				if wmo_visibility_range > 0.0:
					multimesh_instance.visibility_range_end = wmo_visibility_range
					multimesh_instance.visibility_range_end_margin = CHUNK_SIZE
				(root as Node3D).add_child(multimesh_instance)
				_set_editor_owner_recursive(multimesh_instance)
			_wmo_render_build_queue.adopt_cursors(
				unique_key,
				int(build_step["next_mesh_index"]),
				int(build_step["next_multimesh_index"])
			)
			_render_budget_scheduler.try_consume_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.WMO_RENDER_GROUP_BUILD)
			continue

		_cancel_wmo_render_build_job(unique_key)


func _cancel_wmo_render_build_job(unique_key: String) -> void:
	_wmo_render_build_queue.cancel(unique_key)


func _get_resource_file_size(path: String) -> int:
	var file := FileAccess.open(path, FileAccess.READ)
	if file == null:
		file = FileAccess.open(ProjectSettings.globalize_path(path), FileAccess.READ)
	if file == null:
		return 0
	var length := file.get_length()
	file.close()
	return int(length)


func _is_wmo_node_cache_current(node: Node) -> bool:
	if node == null:
		return false
	return bool(WMO_BUILDER_SCRIPT.is_scene_cache_current(node))


func _instantiate_wmo_scene(rel_path: String, scene: PackedScene, placement: Dictionary) -> Node3D:
	if scene == null:
		return null
	var instance := scene.instantiate() as Node3D
	if instance == null:
		return null
	if not _is_wmo_node_cache_current(instance):
		instance.free()
		return null
	instance.name = rel_path.get_file().get_basename()
	instance.transform = _wmo_placement_resolver.resolve_world_transform(placement)
	return instance


func _prepare_runtime_wmo_instance(instance: Node3D) -> void:
	_refresh_cached_wmo_materials_recursive(instance)
	if not enable_occlusion_culling:
		var occluders := instance.get_node_or_null("Occluders")
		if occluders != null:
			instance.remove_child(occluders)
			occluders.queue_free()
	if wmo_cast_shadows:
		_apply_shadow_cast_recursive(instance, true)


func _refresh_cached_wmo_materials_recursive(node: Node) -> void:
	if node is MeshInstance3D:
		_refresh_cached_wmo_mesh_materials((node as MeshInstance3D).mesh)
	elif node is MultiMeshInstance3D:
		var multimesh := (node as MultiMeshInstance3D).multimesh
		if multimesh != null:
			_refresh_cached_wmo_mesh_materials(multimesh.mesh)
	for child in node.get_children():
		_refresh_cached_wmo_materials_recursive(child)


func _refresh_cached_wmo_mesh_materials(mesh: Mesh) -> void:
	if mesh == null:
		return
	if int(mesh.get_meta("wow_wmo_material_refresh_version", 0)) >= WMO_MATERIAL_REFRESH_VERSION:
		return
	mesh.set_meta("wow_wmo_material_refresh_version", WMO_MATERIAL_REFRESH_VERSION)
	if not (mesh is ArrayMesh):
		return

	var array_mesh := mesh as ArrayMesh
	for surface_idx in array_mesh.get_surface_count():
		var rebuilt := _rebuild_cached_wmo_material(array_mesh.surface_get_material(surface_idx))
		if rebuilt != null:
			array_mesh.surface_set_material(surface_idx, rebuilt)


func _rebuild_cached_wmo_material(material: Material) -> Material:
	if material == null or not material.has_meta("texture0_path"):
		return null

	var tex0_path := String(material.get_meta("texture0_path", ""))
	var tex1_path := String(material.get_meta("texture1_path", ""))
	var tex2_path := String(material.get_meta("texture2_path", ""))
	var textures := PackedStringArray()
	var tex0_index := -1
	var tex1_index := -1
	var tex2_index := -1
	if not tex0_path.is_empty():
		tex0_index = textures.size()
		textures.append(tex0_path)
	if not tex1_path.is_empty():
		tex1_index = textures.size()
		textures.append(tex1_path)
	if not tex2_path.is_empty():
		tex2_index = textures.size()
		textures.append(tex2_path)

	var diffuse := Color.WHITE
	var emissive := Color.BLACK
	var secondary := Color.WHITE
	if material is ShaderMaterial:
		var shader_material := material as ShaderMaterial
		var diffuse_value: Variant = shader_material.get_shader_parameter("diffuse_color")
		var emissive_value: Variant = shader_material.get_shader_parameter("emissive_color")
		var secondary_value: Variant = shader_material.get_shader_parameter("secondary_color")
		if diffuse_value is Color:
			diffuse = diffuse_value
		if emissive_value is Color:
			emissive = emissive_value
		if secondary_value is Color:
			secondary = secondary_value

	var mat_def := {
		"texture0": tex0_index,
		"texture1": tex1_index,
		"texture2": tex2_index,
		"flags": int(material.get_meta("wow_flags", 0)),
		"shader": int(material.get_meta("wow_shader", 0)),
		"blend_mode": int(material.get_meta("wow_blend_mode", 0)),
		"diffuse_color": diffuse,
		"emissive_color": emissive,
		"color2": secondary,
	}
	return WMO_BUILDER_SCRIPT._build_material(mat_def, textures, extracted_dir)


func _cancel_wmo_build_job(tile_key: String) -> void:
	if _wmo_build_jobs.has(tile_key):
		_wmo_build_jobs.erase(tile_key)
	_wmo_build_queue.erase(tile_key)


func _register_wmo_instance_reference(
		placement_key: String,
		reference_key: String,
		instance: Node3D
) -> void:
	if placement_key.is_empty() or reference_key.is_empty() or instance == null:
		return
	_wmo_instance_nodes_by_unique_key[placement_key] = instance
	_wmo_placement_registry.add_reference(placement_key, reference_key)


func _unregister_tile_wmos(state: Dictionary) -> void:
	var refs: Array = state.get("wmo_refs", [])
	var tile_key: String = String(state.get("key", ""))
	if not tile_key.is_empty():
		_cancel_wmo_build_job(tile_key)
	if refs.is_empty():
		state["wmo_building"] = false
		return

	for unique_key_variant in refs:
		var unique_key := String(unique_key_variant)
		if not _wmo_placement_registry.contains(unique_key):
			continue
		if _wmo_placement_registry.release_reference(unique_key, tile_key):
			_cancel_wmo_render_build_job(String(unique_key))
			var node: Node = _wmo_instance_nodes_by_unique_key.get(unique_key, null)
			if node != null and is_instance_valid(node):
				node.queue_free()
			_wmo_instance_nodes_by_unique_key.erase(unique_key)

	state["wmo_refs"] = []
	state["wmo_building"] = false


func _clear_wmo_registry() -> void:
	_wmo_render_build_queue.clear()
	for node_variant in _wmo_instance_nodes_by_unique_key.values():
		if not (node_variant is Node):
			continue
		var node := node_variant as Node
		if is_instance_valid(node):
			_queue_free_streamed_node(node)
	_wmo_instance_nodes_by_unique_key.clear()
	_wmo_placement_registry.clear()
	if _world_wmo_root and is_instance_valid(_world_wmo_root):
		_queue_free_streamed_node(_world_wmo_root)
	_world_wmo_root = null


func _request_tile_m2_assets(
		tile_root: Node3D,
		key: String,
		tile_origin: Vector3,
		m2_names: PackedStringArray,
		m2_placements: Array) -> Dictionary:
	var result := {
		"built": false,
	}
	if key.is_empty() or m2_names.is_empty() or m2_placements.is_empty():
		return result
	var existing_m2_root := tile_root.get_node_or_null("M2s")
	if existing_m2_root != null:
		result["built"] = not existing_m2_root.is_queued_for_deletion()
		return result
	if _m2_group_tasks.has(key) or _m2_build_queue_state.has_job(key):
		return result

	var filtered: Dictionary = _m2_unique_placement_registry.reserve(
		key,
		m2_placements
	)
	var filtered_placements: Array = filtered.get("placements", [])
	result["unique_keys"] = filtered.get("unique_keys", [])
	result["skipped_unique_keys"] = filtered.get("skipped_unique_keys", [])
	if filtered_placements.is_empty():
		result["built"] = true
		return result

	var task_id: int = WorkerThreadPool.add_task(
		_group_tile_m2_task.bind(key, tile_origin, m2_names, filtered_placements))
	_m2_group_tasks[key] = task_id
	return result


func _release_tile_m2_unique_keys(state: Dictionary, notify_candidates: bool = true) -> void:
	var tile_key := String(state.get("key", ""))
	var unique_keys: Array = state.get("m2_unique_keys", [])
	if tile_key.is_empty() or unique_keys.is_empty():
		state["m2_unique_keys"] = []
		state["m2_skipped_unique_keys"] = []
		return

	var released_keys: Array = _m2_unique_placement_registry.release(tile_key, unique_keys)

	state["m2_unique_keys"] = []
	state["m2_skipped_unique_keys"] = []
	if not notify_candidates:
		return
	for unique_key in released_keys:
		_queue_m2_tiles_skipping_unique_key(String(unique_key), tile_key)


func _queue_m2_tiles_skipping_unique_key(unique_key: String, released_tile_key: String) -> void:
	if unique_key.is_empty():
		return
	for tile_key_variant in _tile_states.keys():
		var tile_key := String(tile_key_variant)
		if tile_key == released_tile_key:
			continue
		var state: Dictionary = _tile_states[tile_key]
		var skipped_keys: Array = state.get("m2_skipped_unique_keys", [])
		if not skipped_keys.has(unique_key):
			continue
		if not bool(state.get("wanted", false)):
			continue
		if not _tile_within_detail_radius(state, _last_focus_pos, m2_tile_radius):
			continue
		_cancel_m2_build_job(tile_key)
		_remove_tile_m2_root(state)
		state["m2_built"] = false
		state["m2_skipped_unique_keys"] = []
		_tile_states[tile_key] = state
		_enqueue_detail_asset_sync(tile_key)


func _group_tile_m2_task(
		key: String,
		tile_origin: Vector3,
		m2_names: PackedStringArray,
		m2_placements: Array) -> void:
	var groups: Dictionary = _m2_placement_grouper.group_placements(
		tile_origin,
		m2_names,
		m2_placements
	)

	_m2_group_result_mutex.lock()
	_m2_group_result_queue.append({
		"key": key,
		"groups": groups,
	})
	_m2_group_result_mutex.unlock()


func _drain_m2_group_results() -> void:
	var results: Array = []
	_m2_group_result_mutex.lock()
	if not _m2_group_result_queue.is_empty():
		results = _m2_group_result_queue
		_m2_group_result_queue = []
	_m2_group_result_mutex.unlock()

	for result_variant in results:
		if not (result_variant is Dictionary):
			continue
		var result: Dictionary = result_variant
		var key: String = String(result.get("key", ""))
		if _m2_group_tasks.has(key):
			WorkerThreadPool.wait_for_task_completion(int(_m2_group_tasks[key]))
			_m2_group_tasks.erase(key)
		if not _tile_states.has(key):
			continue
		var state: Dictionary = _tile_states[key]
		if not bool(state.get("wanted", false)):
			continue
		if not _tile_within_detail_radius(state, _last_focus_pos, m2_tile_radius):
			continue
		if bool(state.get("m2_built", false)) or _m2_build_queue_state.has_job(key):
			continue

		var groups: Dictionary = result.get("groups", {})
		if groups.is_empty():
			state["m2_built"] = true
			_tile_states[key] = state
			continue

		var tile_root: Node = state.get("root", null)
		if tile_root == null or not is_instance_valid(tile_root):
			continue
		var m2_root := Node3D.new()
		m2_root.name = "M2s"
		(tile_root as Node3D).add_child(m2_root)
		_set_editor_owner_recursive(m2_root)

		_m2_build_queue_state.enqueue(key, m2_root, groups)


func _drain_m2_animation_loads() -> void:
	_m2_animation_resource_finalizer.poll_terminal_requests(
		_m2_animation_load_pipeline_state,
		_m2_prototype_cache_state
	)

	while _m2_animation_load_pipeline_state.has_finalize_record() and _render_budget_scheduler.try_consume_permit(
			RENDER_BUDGET_SCHEDULER_SCRIPT.M2_ANIMATION_FINALIZE):
		var preparation := _m2_animation_resource_finalizer.prepare_next_candidate(
			_m2_animation_load_pipeline_state,
			_m2_prototype_cache_state
		)
		if preparation.is_empty():
			continue
		var normalized_rel := String(preparation.get("normalized", ""))
		var material_source := _get_or_load_m2_material_prototype(normalized_rel)
		_m2_animation_resource_finalizer.finalize_prepared_candidate(
			preparation,
			material_source,
			_m2_prototype_cache_state,
			debug_streaming
		)


func _drain_m2_mesh_loads() -> void:
	_m2_mesh_resource_finalizer.poll_terminal_requests(
		_m2_mesh_load_pipeline_state,
		_m2_prototype_cache_state
	)

	while _m2_mesh_load_pipeline_state.has_finalize_record() and _render_budget_scheduler.try_consume_permit(
			RENDER_BUDGET_SCHEDULER_SCRIPT.M2_MESH_FINALIZE):
		_m2_mesh_resource_finalizer.finalize_next_resource(
			_m2_mesh_load_pipeline_state,
			_m2_mesh_resource_cache_state,
			_m2_prototype_cache_state,
			extracted_dir
		)


func _process_m2_build_jobs() -> void:
	while _render_budget_scheduler.has_remaining_permit(
			RENDER_BUDGET_SCHEDULER_SCRIPT.M2_BUILD) and _m2_build_queue_state.has_pending():
		var key := _m2_build_queue_state.front_key()
		if not _m2_build_queue_state.has_job(key):
			_m2_build_queue_state.pop_front()
			continue
		if not _tile_states.has(key):
			_cancel_m2_build_job(key)
			_m2_build_queue_state.pop_front()
			continue

		var state: Dictionary = _tile_states[key]
		if not bool(state.get("wanted", false)) or not _tile_within_detail_radius(state, _last_focus_pos, m2_tile_radius):
			_cancel_m2_build_job(key)
			_release_tile_m2_unique_keys(state)
			state["m2_built"] = false
			_tile_states[key] = state
			_m2_build_queue_state.pop_front()
			continue

		var root := _m2_build_queue_state.root_for(key)
		if root == null or not is_instance_valid(root):
			_m2_build_queue_state.erase_job(key)
			_m2_build_queue_state.pop_front()
			continue

		var group_keys := _m2_build_queue_state.group_keys_for(key)
		var index := _m2_build_queue_state.group_index_for(key)
		if index >= group_keys.size():
			_finish_m2_build_job(key)
			_m2_build_queue_state.pop_front()
			continue

		var rel_path: String = String(group_keys[index])
		var normalized_rel := _normalize_m2_rel_path(rel_path)
		var groups := _m2_build_queue_state.groups_for(key)
		var transforms: Array = groups.get(rel_path, [])
		var offset := _m2_build_queue_state.transform_offset_for(key)
		var resource_snapshot: RefCounted
		if enable_m2_animated_instances:
			var native_animated_prototype := (
				_m2_native_animation_resource_observer.observe(
					normalized_rel,
					extracted_dir,
					_m2_raw_model_repository,
					_m2_prototype_cache_state,
					debug_streaming
				)
			)
			if native_animated_prototype != null:
				resource_snapshot = M2_BUILD_RESOURCE_SNAPSHOT_SCRIPT.new(
					normalized_rel,
					native_animated_prototype,
					false
				)
			else:
				resource_snapshot = _m2_animation_resource_observer.observe(
					normalized_rel,
					m2_cache_dir,
					m2_animated_max_primitives,
					m2_animated_allowlist_patterns,
					m2_animated_denylist_patterns,
					_m2_prototype_cache_state,
					_m2_animation_load_pipeline_state,
					debug_streaming
				)
		else:
			resource_snapshot = M2_BUILD_RESOURCE_SNAPSHOT_SCRIPT.new(
				normalized_rel,
				null,
				false
			)
		var batch_plan: Dictionary = {}
		var batch_count := 0
		if not bool(resource_snapshot.call("animation_request_pending")):
			batch_plan = _m2_build_batch_planner.plan_batch(
				transforms.size(),
				offset,
				bool(resource_snapshot.call("has_animated_prototype")),
				m2_animated_instances_per_tick,
				m2_multimesh_batch_size
			)
			batch_count = int(batch_plan["batch_count"])
			if (
				batch_count > 0
				and not bool(resource_snapshot.call("has_animated_prototype"))
			):
				_m2_static_build_resource_observer.observe(
					resource_snapshot,
					normalized_rel,
					m2_cache_dir,
					_m2_mesh_resource_cache_state,
					_m2_prototype_cache_state,
					_m2_mesh_load_pipeline_state
				)
		var dispatch_plan: Dictionary = _m2_build_dispatch_planner.plan_step(
			batch_count,
			resource_snapshot
		)
		if bool(dispatch_plan["rotate_queue"]):
			_m2_build_queue_state.rotate_front()
			_render_budget_scheduler.try_consume_permit(
				RENDER_BUDGET_SCHEDULER_SCRIPT.M2_BUILD
			)
			continue
		var serial := _m2_build_queue_state.batch_serial_for(key)
		var next_serial := serial
		var dispatch_action: StringName = dispatch_plan["action"]
		if (
			dispatch_action
			== M2_BUILD_DISPATCH_PLANNER_SCRIPT.ACTION_MATERIALIZE_ANIMATED
		):
			_materialize_m2_animated_batch(
				root as Node3D,
				rel_path,
				resource_snapshot.call("animated_prototype") as Node3D,
				transforms,
				offset,
				batch_count,
				serial
			)
		elif (
			dispatch_action
			== M2_BUILD_DISPATCH_PLANNER_SCRIPT.ACTION_MATERIALIZE_STATIC
		):
			_materialize_m2_group_batch(
				root as Node3D,
				rel_path,
				resource_snapshot.call("static_mesh") as Mesh,
				transforms,
				offset,
				batch_count,
				serial
			)
		if bool(dispatch_plan["increment_batch_serial"]):
			next_serial = serial + 1
		var next_group_index := index
		var next_transform_offset := offset
		if bool(batch_plan["group_complete"]):
			next_group_index = index + 1
			next_transform_offset = 0
		else:
			next_transform_offset = int(batch_plan["next_offset"])
		_m2_build_queue_state.adopt_progress(
			key,
			next_group_index,
			next_transform_offset,
			next_serial
		)
		_render_budget_scheduler.try_consume_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.M2_BUILD)

		if next_group_index >= group_keys.size():
			_finish_m2_build_job(key)
			_m2_build_queue_state.pop_front()
func _materialize_m2_group_batch(
		m2_root: Node3D,
		rel_path: String,
		mesh: Mesh,
		transforms: Array,
		start: int,
		count: int,
		serial: int) -> void:
	var multimesh_instance := _m2_static_batch_materializer.materialize_batch(
		m2_root,
		rel_path,
		mesh,
		transforms,
		start,
		count,
		serial,
		m2_visibility_range,
		CHUNK_SIZE,
		m2_cast_shadows
	)
	if multimesh_instance != null:
		_set_editor_owner_recursive(multimesh_instance)


func _materialize_m2_animated_batch(
		m2_root: Node3D,
		rel_path: String,
		prototype: Node3D,
		transforms: Array,
		start: int,
		count: int,
		serial: int) -> void:
	var materialization := _m2_animated_instance_materializer.materialize_batch(
		m2_root,
		rel_path,
		prototype,
		transforms,
		start,
		count,
		serial,
		m2_visibility_range,
		CHUNK_SIZE,
		m2_cast_shadows,
		M2_NATIVE_ANIMATOR_SCRIPT,
		debug_streaming
	)
	if materialization.is_empty():
		return
	if debug_streaming:
		for diagnostic_entry in materialization.get("native_diagnostics", []):
			var state: Dictionary = diagnostic_entry.get("state", {})
			print("M2_NATIVE_ANIMATOR path=%s instance=%d prepared=%s processing=%s mesh=%s bones=%d surfaces=%d length=%.2f" % [
				_normalize_m2_rel_path(rel_path),
				int(diagnostic_entry.get("instance_index", -1)),
				str(state.get("prepared", false)),
				str(state.get("processing", false)),
				str(state.get("has_mesh", false)),
				int(state.get("bones", 0)),
				int(state.get("surfaces", 0)),
				float(state.get("length", 0.0)),
			])
	var batch_root: Node3D = materialization.get("batch_root", null)
	_set_editor_owner_recursive(batch_root)


func _finish_m2_build_job(key: String) -> void:
	if not _m2_build_queue_state.has_job(key):
		return
	var root := _m2_build_queue_state.root_for(key)
	var built := false
	if root != null and is_instance_valid(root):
		built = root.get_child_count() > 0
		if not built:
			root.queue_free()
	if _tile_states.has(key):
		var state: Dictionary = _tile_states[key]
		state["m2_built"] = true
		_tile_states[key] = state
	_m2_build_queue_state.erase_job(key)


func _cancel_m2_build_job(key: String) -> void:
	if not _m2_build_queue_state.has_job(key):
		return
	var root := _m2_build_queue_state.root_for(key)
	if root != null and is_instance_valid(root):
		_queue_free_streamed_node(root)
	_m2_build_queue_state.erase_job(key)


func _remove_tile_m2_assets(state: Dictionary) -> void:
	var key := String(state.get("key", ""))
	if not key.is_empty():
		_cancel_m2_build_job(key)
	_release_tile_m2_unique_keys(state)
	_remove_tile_m2_root(state)


func _remove_tile_m2_root(state: Dictionary) -> void:
	var tile_root: Node = state.get("root", null)
	if tile_root == null or not is_instance_valid(tile_root):
		return
	var m2_root := tile_root.get_node_or_null("M2s")
	if m2_root:
		m2_root.queue_free()


func _apply_visibility_range_recursive(node: Node, range_end: float) -> void:
	if range_end <= 0.0:
		return
	if node is GeometryInstance3D:
		var geometry := node as GeometryInstance3D
		geometry.visibility_range_end = range_end
		geometry.visibility_range_end_margin = CHUNK_SIZE
	for child in node.get_children():
		_apply_visibility_range_recursive(child, range_end)


func _apply_shadow_cast_recursive(node: Node, cast_shadows: bool) -> void:
	if node is GeometryInstance3D:
		var geometry := node as GeometryInstance3D
		geometry.cast_shadow = (
			GeometryInstance3D.SHADOW_CASTING_SETTING_ON
			if cast_shadows
			else GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
		)
	for child in node.get_children():
		_apply_shadow_cast_recursive(child, cast_shadows)


func _strip_occluders_recursive(node: Node) -> void:
	for child in node.get_children():
		if child is OccluderInstance3D:
			node.remove_child(child)
			child.free()
		else:
			_strip_occluders_recursive(child)


func _normalize_m2_rel_path(rel_path: String) -> String:
	var normalized_rel := rel_path.replace("\\", "/")
	if normalized_rel.ends_with(".mdx") or normalized_rel.ends_with(".mdl"):
		normalized_rel = normalized_rel.get_basename() + ".m2"
	return normalized_rel


func _prepare_m2_node_for_runtime(normalized_rel: String, root: Node3D) -> void:
	if root == null:
		return
	for mesh_instance in _m2_animated_scene_finalizer.mesh_instances_in_subtree(root):
		if mesh_instance == null or mesh_instance.mesh == null:
			continue
		var prepared := _m2_mesh_resource_finalizer.prepare_mesh_for_runtime(
			normalized_rel,
			mesh_instance.mesh,
			extracted_dir
		)
		if prepared != null:
			mesh_instance.mesh = prepared


func _get_or_load_m2_mesh(rel_path: String) -> Mesh:
	var normalized_rel := _normalize_m2_rel_path(rel_path)
	if _m2_mesh_resource_cache_state.has_mesh(normalized_rel):
		return _m2_mesh_resource_cache_state.find_mesh(normalized_rel)
	var prototype: Node3D = _get_or_load_m2_prototype(rel_path)
	if prototype == null:
		return null
	var mesh := _m2_mesh_resource_extractor.find_first_mesh_in_subtree(prototype)
	if mesh != null and not normalized_rel.is_empty():
		mesh = _m2_mesh_resource_finalizer.prepare_mesh_for_runtime(
			normalized_rel,
			mesh,
			extracted_dir
		)
		_m2_mesh_resource_cache_state.store_mesh(normalized_rel, mesh)
	return mesh


func _build_tile_m2_placeholders(tile_root: Node3D, tile_origin: Vector3, m2_names: PackedStringArray, m2_placements: Array) -> void:
	if m2_names.is_empty() or m2_placements.is_empty():
		return

	var tree_mesh := CylinderMesh.new()
	tree_mesh.top_radius = 0.35
	tree_mesh.bottom_radius = 0.45
	tree_mesh.height = 6.0
	tree_mesh.radial_segments = 6

	var prop_mesh := BoxMesh.new()
	prop_mesh.size = Vector3(1.2, 1.2, 1.2)

	var tree_material := StandardMaterial3D.new()
	tree_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
	tree_material.albedo_color = Color(0.2, 0.75, 0.2, 1.0)

	var prop_material := StandardMaterial3D.new()
	prop_material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
	prop_material.albedo_color = Color(0.8, 0.7, 0.35, 1.0)

	var tree_mm := MultiMesh.new()
	tree_mm.transform_format = MultiMesh.TRANSFORM_3D
	tree_mm.mesh = tree_mesh

	var prop_mm := MultiMesh.new()
	prop_mm.transform_format = MultiMesh.TRANSFORM_3D
	prop_mm.mesh = prop_mesh

	var tree_transforms: Array[Transform3D] = []
	var prop_transforms: Array[Transform3D] = []

	for placement_variant in m2_placements:
		if not (placement_variant is Dictionary):
			continue
		var placement: Dictionary = placement_variant
		var name_id: int = int(placement.get("name_id", -1))
		if name_id < 0 or name_id >= m2_names.size():
			continue

		var rel_path := str(m2_names[name_id]).to_lower()
		var is_tree := rel_path.contains("tree") or rel_path.contains("bush") or rel_path.contains("shrub") or rel_path.contains("plant")
		if m2_placeholder_only_trees and not is_tree:
			continue

		var local_pos: Vector3 = placement.get("pos", Vector3.ZERO) - tile_origin
		var rot: Vector3 = placement.get("rot", Vector3.ZERO)
		var scale_value: float = float(placement.get("scale", 1.0))
		var xform_basis := _m2_placement_transform_resolver.resolve_basis(
			rot,
			rel_path
		).scaled(Vector3.ONE * maxf(scale_value, 0.01))
		var xf := Transform3D(
			xform_basis,
			local_pos + _m2_placement_transform_resolver.resolve_origin_offset(
				rot,
				rel_path,
				scale_value
			)
		)
		if is_tree:
			tree_transforms.append(xf)
		else:
			prop_transforms.append(xf)

	var root := Node3D.new()
	root.name = "M2Placeholders"

	if not tree_transforms.is_empty():
		tree_mm.instance_count = tree_transforms.size()
		for i in tree_transforms.size():
			tree_mm.set_instance_transform(i, tree_transforms[i])
		var tree_mmi := MultiMeshInstance3D.new()
		tree_mmi.name = "Trees"
		tree_mmi.multimesh = tree_mm
		tree_mmi.material_override = tree_material
		root.add_child(tree_mmi)
		_set_editor_owner_recursive(tree_mmi)

	if not prop_transforms.is_empty():
		prop_mm.instance_count = prop_transforms.size()
		for i in prop_transforms.size():
			prop_mm.set_instance_transform(i, prop_transforms[i])
		var prop_mmi := MultiMeshInstance3D.new()
		prop_mmi.name = "Props"
		prop_mmi.multimesh = prop_mm
		prop_mmi.material_override = prop_material
		root.add_child(prop_mmi)
		_set_editor_owner_recursive(prop_mmi)

	if root.get_child_count() > 0:
		tile_root.add_child(root)
		_set_editor_owner_recursive(root)


func _instantiate_m2(rel_path: String, placement: Dictionary, tile_origin: Vector3) -> Node3D:
	var prototype: Node3D = _get_or_load_m2_prototype(rel_path)
	if prototype == null:
		return null

	var node := prototype.duplicate() as Node3D
	if node == null:
		return null

	node.name = rel_path.get_file().get_basename()
	var local_pos: Vector3 = placement.get("pos", Vector3.ZERO) - tile_origin
	var rot: Vector3 = placement.get("rot", Vector3.ZERO)
	var scale_value: float = float(placement.get("scale", 1.0))
	node.transform = Transform3D(
		_m2_placement_transform_resolver.resolve_basis(
			rot,
			rel_path
		).scaled(Vector3.ONE * maxf(scale_value, 0.0001)),
		local_pos + _m2_placement_transform_resolver.resolve_origin_offset(
			rot,
			rel_path,
			scale_value
		)
	)
	return node


func _instantiate_wmo_world(rel_path: String, placement: Dictionary) -> Node3D:
	var prototype := _get_or_load_wmo_prototype(rel_path)
	if prototype == null:
		return null

	var instance := prototype.duplicate()
	instance.name = rel_path.get_file().get_basename()
	# WMOs are parented to _terrain_root, whose position absorbs the editor
	# offset — use world-space placement.pos directly (no tile_origin subtraction).
	instance.transform = _wmo_placement_resolver.resolve_world_transform(placement)
	if not enable_occlusion_culling:
		var occluders := instance.get_node_or_null("Occluders")
		if occluders != null:
			instance.remove_child(occluders)
			occluders.queue_free()
	if wmo_cast_shadows:
		_apply_shadow_cast_recursive(instance, true)
	return instance


func _get_or_load_m2_prototype(rel_path: String) -> Node3D:
	var normalized_rel := _normalize_m2_rel_path(rel_path)
	if normalized_rel.is_empty():
		return null
	var cached := _m2_prototype_cache_state.find_static_prototype(normalized_rel)
	if cached != null:
		_prepare_m2_node_for_runtime(normalized_rel, cached)
		return cached
	if _m2_prototype_cache_state.is_model_missing(normalized_rel):
		return null

	# Try pre-baked cache (.tscn first, then legacy .glb)
	for cache_res_path in _get_m2_cache_resource_paths(normalized_rel, [".tscn", ".glb"]):
		if not ResourceLoader.exists(cache_res_path):
			continue
		if (
			cache_res_path.get_extension().to_lower() == "glb"
			and _m2_static_build_resource_observer.glb_animation_schema(
				cache_res_path
			) == "pivot_prefix_v1"
		):
			continue
		var resource: Resource = load(cache_res_path)
		if resource is PackedScene:
			var node = (resource as PackedScene).instantiate()
			if node is Node3D:
				_prepare_m2_node_for_runtime(normalized_rel, node as Node3D)
				return _m2_prototype_cache_state.adopt_static_prototype(
					normalized_rel,
					node as Node3D
				)
		break

	# Fall back to the raw native M2 repository.
	var data := _m2_raw_model_repository.load_static_model_data(
		extracted_dir,
		normalized_rel
	)
	if data.is_empty():
		_m2_prototype_cache_state.mark_model_missing(normalized_rel)
		return null

	var prototype: Node3D = M2_BUILDER_SCRIPT.build(data, extracted_dir)
	if prototype == null:
		_m2_prototype_cache_state.mark_model_missing(normalized_rel)
		return null

	return _m2_prototype_cache_state.adopt_static_prototype(
		normalized_rel,
		prototype
	)


func _get_or_load_m2_material_prototype(normalized_rel: String) -> Node3D:
	if normalized_rel.is_empty():
		return null
	var cached := _m2_prototype_cache_state.find_static_prototype(normalized_rel)
	if cached != null:
		_prepare_m2_node_for_runtime(normalized_rel, cached)
		return cached

	for cache_res_path in _get_m2_cache_resource_paths(normalized_rel, [".tscn"]):
		if not ResourceLoader.exists(cache_res_path):
			continue
		var resource: Resource = load(cache_res_path)
		if resource is PackedScene:
			var node = (resource as PackedScene).instantiate()
			if node is Node3D:
				_prepare_m2_node_for_runtime(normalized_rel, node as Node3D)
				return _m2_prototype_cache_state.adopt_static_prototype(
					normalized_rel,
					node as Node3D
				)
	return null


func _get_or_load_wmo_prototype(rel_path: String) -> Node3D:
	var normalized_rel := rel_path.replace("\\", "/")
	if normalized_rel.is_empty():
		return null
	if _wmo_prototype_cache.has(normalized_rel):
		return _wmo_prototype_cache[normalized_rel]
	if _wmo_missing_cache.has(normalized_rel):
		return null

	# Try pre-baked .tscn cache
	var cache_path := wmo_cache_dir.path_join(normalized_rel.get_basename() + ".tscn")
	if ResourceLoader.exists(cache_path):
		var resource: Resource = load(cache_path)
		if resource is PackedScene:
			var node = (resource as PackedScene).instantiate()
			if node is Node3D and _is_wmo_node_cache_current(node):
				_wmo_prototype_cache[normalized_rel] = node as Node3D
				return node as Node3D
			if node is Node:
				(node as Node).free()

	# Fall back to live WMOLoader
	if not ClassDB.class_exists("WMOLoader"):
		_wmo_missing_cache[normalized_rel] = true
		return null

	var abs_path := ProjectSettings.globalize_path(extracted_dir.path_join(normalized_rel))
	if not FileAccess.file_exists(abs_path):
		_wmo_missing_cache[normalized_rel] = true
		return null

	var loader = ClassDB.instantiate("WMOLoader")
	if loader == null:
		return null

	var data: Dictionary = loader.call("load_wmo", abs_path)
	if data.is_empty():
		_wmo_missing_cache[normalized_rel] = true
		return null

	var prototype: Node3D = WMO_BUILDER_SCRIPT.build(data, extracted_dir)
	if prototype == null:
		_wmo_missing_cache[normalized_rel] = true
		return null

	_wmo_prototype_cache[normalized_rel] = prototype
	return prototype


func _get_m2_cache_resource_path(rel_path: String, ext: String = ".tscn") -> String:
	var normalized := rel_path.replace("\\", "/")
	var stem := normalized.get_basename()
	return m2_cache_dir.path_join(stem + ext)


func _get_m2_cache_resource_paths(rel_path: String, extensions: Array[String]) -> PackedStringArray:
	var normalized := rel_path.replace("\\", "/")
	var lower := normalized.to_lower()
	var stems := [
		normalized.get_basename(),
		lower.get_basename(),
		normalized.get_file().get_basename(),
		lower.get_file().get_basename(),
	]
	var result := PackedStringArray()
	for ext in extensions:
		for stem in stems:
			if stem.is_empty():
				continue
			var path := m2_cache_dir.path_join(stem + ext)
			if not result.has(path):
				result.append(path)
	return result


func _get_cached_tile_mesh(cache_key: String) -> Mesh:
	if not _tile_mesh_cache.has(cache_key):
		return null
	_touch_tile_mesh_cache(cache_key)
	return _tile_mesh_cache[cache_key]


func _store_cached_tile_mesh(cache_key: String, mesh: Mesh) -> void:
	if mesh == null:
		return
	_tile_mesh_cache[cache_key] = mesh
	_touch_tile_mesh_cache(cache_key)
	_trim_tile_mesh_cache()


func _touch_tile_mesh_cache(cache_key: String) -> void:
	_tile_mesh_cache_order.erase(cache_key)
	_tile_mesh_cache_order.append(cache_key)


func _trim_tile_mesh_cache() -> void:
	var limit := maxi(0, cached_tile_mesh_limit)
	while _tile_mesh_cache_order.size() > limit:
		var oldest: String = _tile_mesh_cache_order.pop_front()
		_tile_mesh_cache.erase(oldest)


func _remove_tile_mesh_cache_entries(tile_key: String) -> void:
	for lod in range(0, 4):
		var cache_key := _tile_mesh_cache_key(tile_key, lod)
		_tile_mesh_cache.erase(cache_key)
		_tile_mesh_cache_order.erase(cache_key)


func _clear_tile_mesh_cache() -> void:
	_tile_mesh_cache.clear()
	_tile_mesh_cache_order.clear()


func _restore_quality_terrain_mesh(tile_key: String, state: Dictionary) -> Dictionary:
	var cached: Dictionary = _terrain_quality_mesh_cache.call("restore", tile_key)
	if cached.is_empty():
		return state
	var source := String(cached.get("source", "baked_full"))
	var mesh: Mesh = cached.get("mesh", null)
	state["quality_terrain_mesh"] = mesh
	state["quality_terrain_source"] = source
	return state


func _store_quality_terrain_mesh(tile_key: String, mesh: Mesh, source: String) -> void:
	_terrain_quality_mesh_cache.call(
		"store",
		tile_key,
		mesh,
		source,
		terrain_quality_mesh_cache_limit
	)


func _clear_quality_terrain_mesh_cache() -> void:
	_terrain_quality_mesh_cache.call("clear")


func _position_camera_over_world() -> void:
	if _camera_initialized:
		return

	var camera := _get_auto_position_camera()
	if camera == null:
		return

	var minimum_tile_center := _tile_center_to_world(_tile_min.x, _tile_min.y)
	var maximum_tile_center := _tile_center_to_world(_tile_max.x, _tile_max.y)
	var center := (minimum_tile_center + maximum_tile_center) * 0.5
	var tile_span: int = int(max(_tile_max.x - _tile_min.x + 1, _tile_max.y - _tile_min.y + 1))
	var height: float = max(2000.0, float(tile_span) * TILE_SIZE * 0.18)

	camera.global_position = center + Vector3(0.0, height, height * 0.2)
	camera.look_at(center, Vector3.UP)
	_camera_initialized = true


func _tile_center_to_world(tile_x: int, tile_y: int) -> Vector3:
	var tile_coordinate = ADT_TILE_COORDINATE_SCRIPT.new(tile_x, tile_y)
	var half_tile_size := TILE_SIZE * 0.5
	var tile_center_position = COORDINATE_MAPPER_SCRIPT.adt_tile_local_to_godot(
		tile_coordinate,
		ADT_TILE_LOCAL_POSITION_SCRIPT.new(half_tile_size, 0.0, half_tile_size))
	return Vector3(tile_center_position.x_units, tile_center_position.y_units, tile_center_position.z_units)


func _world_to_tile(world_pos: Vector3) -> Vector2i:
	var typed_world_position = GODOT_WORLD_POSITION_SCRIPT.new(world_pos.x, world_pos.y, world_pos.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 _tile_key(tx: int, ty: int) -> String:
	return "%d_%d" % [tx, ty]


func _set_editor_owner_recursive(node: Node) -> void:
	var edited_root := _editor_owner_for_generated_nodes()
	if edited_root == null:
		return

	node.owner = edited_root
	for child in node.get_children():
		_set_editor_owner_recursive(child)


func _editor_owner_for_generated_nodes() -> Node:
	if not Engine.is_editor_hint() or not editor_persist_generated_nodes:
		return null
	return get_tree().edited_scene_root


func _make_editor_signature() -> String:
	var values := [
		extracted_dir,
		map_name,
		quality_preset,
		1 if editor_preview_enabled else 0,
		editor_preview_center_x,
		editor_preview_center_y,
		editor_preview_tile_radius,
		1 if editor_follow_view_camera else 0,
		lod0_radius_chunks,
		lod1_radius_chunks,
		lod2_radius_chunks,
		lod2_tile_radius,
		warm_tile_margin,
		prewarm_tile_margin,
		1 if prewarm_tile_lod_instances else 0,
		retain_tile_margin,
		1 if terrain_full_quality_enabled else 0,
		terrain_full_quality_radius_tiles,
		max_concurrent_terrain_upgrade_tasks,
		1 if terrain_control_splat_quality_enabled else 0,
		1 if terrain_control_splat_primary else 0,
		1 if terrain_control_splat_hide_fallback else 0,
		terrain_control_splat_hide_fallback_radius_tiles,
		terrain_control_splat_radius_tiles,
		max_active_terrain_control_splat_tiles,
		terrain_control_splat_cache_dir,
		1 if terrain_splat_quality_enabled else 0,
		terrain_splat_radius_tiles,
		max_active_terrain_splat_tiles,
		max_concurrent_terrain_splat_tasks,
		1 if enable_m2_assets else 0,
		1 if enable_m2_placeholders else 0,
		m2_multimesh_batch_size,
		m2_mesh_finalize_ops_per_tick,
		wmo_build_instances_per_tick,
		wmo_render_group_ops_per_tick,
		wmo_max_runtime_scene_mb,
		m2_tile_radius,
		wmo_tile_radius,
		1 if wmo_cast_shadows else 0,
		m2_visibility_range,
		wmo_visibility_range,
		full_lod_prewarm_tiles,
		boundary_prefetch_threshold,
		1 if terrain_quality_log_enabled else 0,
		terrain_quality_log_interval,
		m2_cache_dir,
		wmo_cache_dir,
		wmo_render_cache_dir,
		streaming_tile_cache_dir,
	]
	var parts := PackedStringArray()
	for value in values:
		parts.append(str(value))
	return "|".join(parts)


func _release_tile_if_unused(key: String) -> void:
	if not _tile_states.has(key):
		return

	var state: Dictionary = _tile_states[key]
	if bool(state.get("retained", false)):
		_tile_states[key] = state
		return
	if not state["chunks"].is_empty():
		_tile_states[key] = state
		return
	if state.get("tile_lod_node", null) != null:
		_tile_states[key] = state
		return
	if state.get("tile_lod_rid", RID()).is_valid():
		_tile_states[key] = state
		return
	if int(state.get("tile_lod", -1)) >= 0:
		_tile_states[key] = state
		return

	_release_tile(key)


func _tile_local_to_world_transform(state: Dictionary, local_position: Vector3) -> Transform3D:
	var root: Node3D = state.get("root", null)
	var world_pos := local_position
	if root:
		world_pos += root.global_position
	return Transform3D(Basis.IDENTITY, world_pos)


func _create_render_instance(mesh: Mesh, world_transform: Transform3D) -> RID:
	if mesh == null:
		return RID()

	var world := get_world_3d()
	if world == null:
		return RID()

	var scenario: RID = world.scenario
	if not scenario.is_valid():
		return RID()

	var instance_rid := RenderingServer.instance_create()
	RenderingServer.instance_set_base(instance_rid, mesh.get_rid())
	RenderingServer.instance_set_scenario(instance_rid, scenario)
	RenderingServer.instance_set_transform(instance_rid, world_transform)
	RenderingServer.instance_geometry_set_cast_shadows_setting(
		instance_rid,
		RenderingServer.SHADOW_CASTING_SETTING_ON if terrain_cast_shadows else RenderingServer.SHADOW_CASTING_SETTING_OFF)
	return instance_rid


func _free_render_instance(instance_rid: RID) -> void:
	if instance_rid.is_valid():
		RenderingServer.free_rid(instance_rid)
