rnd(M03): extract streaming target planner
Work-Package: M03-RND-STREAMING-PLANNER-001 Agent: sindo-main-codex Tests: planner/facade/focus/coordinate/manifest/shutdown contracts, seven-checkpoint dry-run, repository gates Fidelity: preserves wanted/retained radii, boundary prefetch, editor preview, queue priority and cache/render behavior
This commit is contained in:
@@ -17,6 +17,8 @@ const GODOT_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/godot
|
||||
const COORDINATE_MAPPER_SCRIPT := preload("res://src/domain/coordinates/coordinate_mapper.gd")
|
||||
const ADT_TILE_COORDINATE_SCRIPT := preload("res://src/domain/coordinates/adt_tile_coordinate.gd")
|
||||
const ADT_TILE_LOCAL_POSITION_SCRIPT := preload("res://src/domain/coordinates/adt_tile_local_position.gd")
|
||||
const 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 REQUIRED_BAKED_TILE_FORMAT_VERSION := 5
|
||||
const REQUIRED_SPLAT_TILE_FORMAT_VERSION := 1
|
||||
const REQUIRED_CONTROL_SPLAT_TILE_FORMAT_VERSION := 3
|
||||
@@ -174,6 +176,7 @@ const QUALITY_HIGH := "High"
|
||||
@export var hitch_profiler_threshold_ms: float = 20.0
|
||||
|
||||
var _builder
|
||||
var _streaming_target_planner := STREAMING_TARGET_PLANNER_SCRIPT.new()
|
||||
var _map_dir := ""
|
||||
var _abs_map_dir := ""
|
||||
var _wdt_info: Dictionary = {}
|
||||
@@ -720,57 +723,47 @@ func _refresh_editor_streaming_targets_at(focus_pos: Vector3, force: bool) -> vo
|
||||
if not force and not _should_refresh_focus(focus_pos):
|
||||
return
|
||||
|
||||
var focus_tile := _world_to_tile(focus_pos)
|
||||
var total_tile_radius: int = maxi(0, editor_preview_tile_radius)
|
||||
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()
|
||||
|
||||
var wanted_tiles: Dictionary = {}
|
||||
for ty in range(focus_tile.y - total_tile_radius, focus_tile.y + total_tile_radius + 1):
|
||||
for tx in range(focus_tile.x - total_tile_radius, focus_tile.x + total_tile_radius + 1):
|
||||
var key := _tile_key(tx, ty)
|
||||
if _available_tiles.has(key):
|
||||
wanted_tiles[key] = true
|
||||
|
||||
_apply_streaming_target(wanted_tiles, wanted_tiles, focus_pos)
|
||||
_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" % [
|
||||
focus_tile, wanted_tiles.size(), _tile_load_queue.size()])
|
||||
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 focus_tile := _world_to_tile(focus_pos)
|
||||
|
||||
# Chunk-LOD radius → tile radius (ceiling), plus margin for pre-warming.
|
||||
var focus_tiles := _predictive_focus_tiles(focus_pos)
|
||||
var total_tile_radius: int = _runtime_load_tile_radius()
|
||||
var retain_tile_radius: int = _runtime_retain_tile_radius()
|
||||
|
||||
var retained_tiles: Dictionary = {}
|
||||
for center in focus_tiles:
|
||||
for ty in range(center.y - retain_tile_radius, center.y + retain_tile_radius + 1):
|
||||
for tx in range(center.x - retain_tile_radius, center.x + retain_tile_radius + 1):
|
||||
var retained_key := _tile_key(tx, ty)
|
||||
if _available_tiles.has(retained_key):
|
||||
retained_tiles[retained_key] = true
|
||||
|
||||
var wanted_tiles: Dictionary = {}
|
||||
for center in focus_tiles:
|
||||
for ty in range(center.y - total_tile_radius, center.y + total_tile_radius + 1):
|
||||
for tx in range(center.x - total_tile_radius, center.x + total_tile_radius + 1):
|
||||
var key := _tile_key(tx, ty)
|
||||
if _available_tiles.has(key):
|
||||
wanted_tiles[key] = true
|
||||
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" % [
|
||||
focus_tile, wanted_tiles.size(), _tile_load_queue.size()])
|
||||
Vector2i(target_plan.focus_tile.tile_x, target_plan.focus_tile.tile_y),
|
||||
wanted_tiles.size(),
|
||||
_tile_load_queue.size(),
|
||||
])
|
||||
|
||||
|
||||
|
||||
@@ -2891,50 +2884,24 @@ func _compute_desired_lods(state: Dictionary, cam_pos: Vector3) -> Dictionary:
|
||||
return desired
|
||||
|
||||
|
||||
func _runtime_visible_tile_radius() -> int:
|
||||
var chunk_tile_radius: int = int(ceil(float(lod2_radius_chunks) / 16.0)) + warm_tile_margin
|
||||
return maxi(chunk_tile_radius, lod2_tile_radius)
|
||||
|
||||
|
||||
func _runtime_load_tile_radius() -> int:
|
||||
return _runtime_visible_tile_radius() + maxi(0, prewarm_tile_margin)
|
||||
return STREAMING_TARGET_POLICY_SCRIPT.calculate_load_tile_radius(
|
||||
lod2_radius_chunks,
|
||||
lod2_tile_radius,
|
||||
warm_tile_margin,
|
||||
prewarm_tile_margin
|
||||
)
|
||||
|
||||
|
||||
func _runtime_retain_tile_radius() -> int:
|
||||
return _runtime_load_tile_radius() + maxi(0, retain_tile_margin)
|
||||
|
||||
|
||||
func _predictive_focus_tiles(focus_pos: Vector3) -> Array[Vector2i]:
|
||||
var base := _world_to_tile(focus_pos)
|
||||
var result: Array[Vector2i] = [base]
|
||||
var threshold: float = clampf(boundary_prefetch_threshold, 0.0, 0.49)
|
||||
if threshold <= 0.0:
|
||||
return result
|
||||
|
||||
var typed_focus_position = GODOT_WORLD_POSITION_SCRIPT.new(focus_pos.x, focus_pos.y, focus_pos.z)
|
||||
var tile_local_position = COORDINATE_MAPPER_SCRIPT.godot_to_adt_tile_local(typed_focus_position)
|
||||
var local_x: float = tile_local_position.east_yards / TILE_SIZE
|
||||
var local_z: float = tile_local_position.south_yards / TILE_SIZE
|
||||
var x_offsets: Array[int] = [0]
|
||||
var z_offsets: Array[int] = [0]
|
||||
|
||||
if local_x <= threshold:
|
||||
x_offsets.append(-1)
|
||||
elif local_x >= 1.0 - threshold:
|
||||
x_offsets.append(1)
|
||||
|
||||
if local_z <= threshold:
|
||||
z_offsets.append(-1)
|
||||
elif local_z >= 1.0 - threshold:
|
||||
z_offsets.append(1)
|
||||
|
||||
for ox in x_offsets:
|
||||
for oz in z_offsets:
|
||||
if ox == 0 and oz == 0:
|
||||
continue
|
||||
result.append(Vector2i(base.x + ox, base.y + oz))
|
||||
|
||||
return result
|
||||
func _streaming_target_policy() -> StreamingTargetPolicy:
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user