extends Node3D
## Minimal DC Terrain test — fly camera + CSG edit controls.
## No player, no creatures, no clouds. Pure terrain verification.
@onready var terrain: Node3D = $DCTerrain
@onready var stats_label: Label = $CanvasLayer/StatsLabel
@onready var camera: Camera3D = $Camera3D
var _terrain_ready: bool = false
var _wait_frames: int = 0
var _wireframe: bool = false
var _edit_radius: float = 3.0
# Benchmark
var _bench_active: bool = false
var _bench_phase: int = 0 # 0=gpu_rebuild, 1=gpu_edit, 2=cpu_rebuild, 3=cpu_edit, 4=done
var _bench_start_ms: int = 0
var _bench_results: Array = []
var _bench_edit_count: int = 0
# Fly camera
var _fly_speed: float = 10.0
var _mouse_sens: float = 0.003
var _cam_yaw: float = 0.0
var _cam_pitch: float = -0.3
func _ready() -> void:
process_mode = Node.PROCESS_MODE_ALWAYS
terrain.rebuild()
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
camera.global_position = Vector3(0, 10, 0)
func _process(delta: float) -> void:
if not _terrain_ready:
var active: int = terrain.get_active_chunk_count()
var collision_q: int = terrain.get_collision_queue_size()
if active >= 10 and collision_q == 0:
_wait_frames += 1
if _wait_frames >= 3:
_terrain_ready = true
print("Terrain ready (active: %d)" % active)
# Fly camera
_fly_camera(delta)
# Benchmark state machine
if _bench_active:
_run_benchmark()
# HUD
if stats_label and terrain:
var chunks: int = terrain.get_chunk_count()
var active: int = terrain.get_active_chunk_count()
var pending: int = terrain.get_pending_count()
var edits: int = terrain.get_edit_count()
var fps: int = Engine.get_frames_per_second()
var pos: Vector3 = camera.global_position
var gpu_str: String = "GPU" if terrain.use_gpu else "CPU"
var bench_str: String = " [BENCH RUNNING]" if _bench_active else ""
stats_label.text = "FPS: %d [%s]%s\nChunks: %d (active: %d, pending: %d)\nEdits: %d\nPos: (%.0f, %.0f, %.0f)\n[R] rebuild [C] clear [B] bench [F1] wire [F2] fandisk\nRMB: dig Shift+RMB: add Shift+Scroll: size" % [
fps, gpu_str, bench_str, chunks, active, pending, edits, pos.x, pos.y, pos.z]
func _start_benchmark() -> void:
print("=== BENCHMARK START ===")
_bench_active = true
_bench_phase = 0
_bench_results = []
_bench_edit_count = 0
# Phase 0: GPU rebuild
terrain.use_gpu = true
terrain.clear_edits()
terrain.rebuild()
_bench_start_ms = Time.get_ticks_msec()
_terrain_ready = false
_wait_frames = 0
print(" Phase 0: GPU rebuild...")
func _run_benchmark() -> void:
var active: int = terrain.get_active_chunk_count()
var pending: int = terrain.get_pending_count()
var collision_q: int = terrain.get_collision_queue_size()
var settled: bool = active >= 10 and pending == 0 and collision_q == 0
if not settled:
return
var elapsed: int = Time.get_ticks_msec() - _bench_start_ms
match _bench_phase:
0: # GPU rebuild done
_bench_results.append("GPU rebuild: %d ms (%d chunks)" % [elapsed, active])
print(" GPU rebuild: %d ms (%d chunks)" % [elapsed, active])
# Phase 1: GPU edits — 5 box edits
_bench_phase = 1
_bench_start_ms = Time.get_ticks_msec()
for i in range(5):
var pos := Vector3(i * 6.0 - 12.0, 2.0, 10.0)
var half := Vector3(2.0, 2.0, 2.0)
terrain.edit_terrain_box(pos, half, -3.0)
print(" Phase 1: GPU box edits (5)...")
1: # GPU edits done
_bench_results.append("GPU 5 box edits: %d ms" % elapsed)
print(" GPU 5 box edits: %d ms" % elapsed)
# Phase 2: GPU fandisk stamp
_bench_phase = 2
_bench_start_ms = Time.get_ticks_msec()
_stamp_fandisk_at(Vector3(0, 5, 20))
print(" Phase 2: GPU fandisk stamp...")
2: # GPU fandisk done
_bench_results.append("GPU fandisk stamp: %d ms" % elapsed)
print(" GPU fandisk stamp: %d ms" % elapsed)
# Phase 3: CPU rebuild
_bench_phase = 3
terrain.use_gpu = false
terrain.clear_edits()
terrain.rebuild()
_bench_start_ms = Time.get_ticks_msec()
_terrain_ready = false
_wait_frames = 0
print(" Phase 3: CPU rebuild...")
3: # CPU rebuild done
_bench_results.append("CPU rebuild: %d ms (%d chunks)" % [elapsed, active])
print(" CPU rebuild: %d ms (%d chunks)" % [elapsed, active])
# Phase 4: CPU edits — 5 box edits
_bench_phase = 4
_bench_start_ms = Time.get_ticks_msec()
for i in range(5):
var pos := Vector3(i * 6.0 - 12.0, 2.0, 10.0)
var half := Vector3(2.0, 2.0, 2.0)
terrain.edit_terrain_box(pos, half, -3.0)
print(" Phase 4: CPU box edits (5)...")
4: # CPU edits done
_bench_results.append("CPU 5 box edits: %d ms" % elapsed)
print(" CPU 5 box edits: %d ms" % elapsed)
# Done
_bench_active = false
terrain.use_gpu = true
terrain.clear_edits()
terrain.rebuild()
print("=== BENCHMARK RESULTS ===")
for r in _bench_results:
print(" %s" % r)
print("=========================")
func _stamp_fandisk_at(hit_pos: Vector3) -> void:
var scene: PackedScene = load("res://fandisk.glb")
if not scene:
print("Failed to load fandisk.glb")
return
var instance: Node3D = scene.instantiate()
var mesh_inst: MeshInstance3D = null
for child in instance.get_children():
if child is MeshInstance3D:
mesh_inst = child
break
if not mesh_inst or not mesh_inst.mesh:
instance.queue_free()
return
var mesh: Mesh = mesh_inst.mesh
var arrays: Array = mesh.surface_get_arrays(0)
var verts: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]
var idxs: PackedInt32Array = arrays[Mesh.ARRAY_INDEX]
terrain.stamp_mesh_arrays(verts, idxs, hit_pos, _edit_radius, 3.0)
instance.queue_free()
func _fly_camera(delta: float) -> void:
var input := Vector3.ZERO
if Input.is_key_pressed(KEY_W): input.z -= 1.0
if Input.is_key_pressed(KEY_S): input.z += 1.0
if Input.is_key_pressed(KEY_A): input.x -= 1.0
if Input.is_key_pressed(KEY_D): input.x += 1.0
if Input.is_key_pressed(KEY_E) or Input.is_key_pressed(KEY_SPACE): input.y += 1.0
if Input.is_key_pressed(KEY_Q) or Input.is_key_pressed(KEY_CTRL): input.y -= 1.0
var speed := _fly_speed
if Input.is_key_pressed(KEY_SHIFT): speed *= 3.0
if input.length_squared() > 0.0:
input = input.normalized()
camera.global_position += camera.global_transform.basis * input * speed * delta
func _unhandled_input(event: InputEvent) -> void:
# Mouse look
if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
_cam_yaw -= event.relative.x * _mouse_sens
_cam_pitch = clampf(_cam_pitch - event.relative.y * _mouse_sens, -PI * 0.49, PI * 0.49)
camera.rotation = Vector3(_cam_pitch, _cam_yaw, 0)
# Mouse wheel: fly speed
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed:
if event.shift_pressed:
_edit_radius = minf(_edit_radius + 0.5, 20.0)
print("Edit size: %.1f" % _edit_radius)
get_viewport().set_input_as_handled()
else:
_fly_speed = minf(100.0, _fly_speed * 1.2)
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed:
if event.shift_pressed:
_edit_radius = maxf(_edit_radius - 0.5, 1.0)
print("Edit size: %.1f" % _edit_radius)
get_viewport().set_input_as_handled()
else:
_fly_speed = maxf(1.0, _fly_speed / 1.2)
# Escape
if event.is_action_pressed("ui_cancel"):
if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
else:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
if not event is InputEventKey or not event.pressed:
if not (event is InputEventMouseButton and event.pressed):
return
# Key controls
if event is InputEventKey and event.pressed:
match event.keycode:
KEY_R:
terrain.rebuild()
print("Terrain rebuild triggered")
KEY_C:
terrain.clear_edits()
print("Terrain edits cleared")
KEY_F1:
_wireframe = !_wireframe
if _wireframe:
get_viewport().debug_draw = Viewport.DEBUG_DRAW_WIREFRAME
else:
get_viewport().debug_draw = Viewport.DEBUG_DRAW_DISABLED
print("Wireframe: %s" % _wireframe)
KEY_B:
if not _bench_active:
_start_benchmark()
KEY_L:
terrain.debug_lod_colors = !terrain.debug_lod_colors
print("LOD colors: %s" % terrain.debug_lod_colors)
KEY_F12:
var img: Image = get_viewport().get_texture().get_image()
var ts: String = Time.get_datetime_string_from_system().replace(":", "-")
var path: String = "/home/fleeky/Desktop/dc-terrain/terrain_%s.png" % ts
DirAccess.make_dir_recursive_absolute("/home/fleeky/Desktop/dc-terrain")
img.save_png(path)
print("Screenshot saved: %s" % path)
# F2 to stamp fandisk at crosshair
if event is InputEventKey and event.pressed and event.keycode == KEY_F2:
var viewport_center: Vector2 = get_viewport().get_visible_rect().size * 0.5
var from: Vector3 = camera.project_ray_origin(viewport_center)
var dir: Vector3 = camera.project_ray_normal(viewport_center)
var space_state: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state
var query: PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(from, from + dir * 100.0)
var result: Dictionary = space_state.intersect_ray(query)
if result.size() > 0:
var hit_pos: Vector3 = result["position"]
var scene: PackedScene = load("res://fandisk.glb")
if not scene:
print("Failed to load fandisk.glb")
return
var instance: Node3D = scene.instantiate()
var mesh_inst: MeshInstance3D = null
for child in instance.get_children():
if child is MeshInstance3D:
mesh_inst = child
break
if not mesh_inst or not mesh_inst.mesh:
print("No MeshInstance3D found in fandisk.glb")
instance.queue_free()
return
var mesh: Mesh = mesh_inst.mesh
var arrays: Array = mesh.surface_get_arrays(0)
var verts: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]
var idxs: PackedInt32Array = arrays[Mesh.ARRAY_INDEX]
hit_pos.y += _edit_radius * 0.8
print("Fandisk: %d verts, %d indices at %s scale=%.1f" % [verts.size(), idxs.size(), hit_pos, _edit_radius])
terrain.stamp_mesh_arrays(verts, idxs, hit_pos, _edit_radius, 3.0)
instance.queue_free()
# Right click = dig box, Shift+Right = add box
if event is InputEventMouseButton and event.pressed and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
if event.button_index == MOUSE_BUTTON_RIGHT:
var viewport_center: Vector2 = get_viewport().get_visible_rect().size * 0.5
var from: Vector3 = camera.project_ray_origin(viewport_center)
var dir: Vector3 = camera.project_ray_normal(viewport_center)
var space_state: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state
var query: PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(from, from + dir * 100.0)
var result: Dictionary = space_state.intersect_ray(query)
if result.size() > 0:
var hit_pos: Vector3 = result["position"]
var half_ext: Vector3 = Vector3(_edit_radius, _edit_radius, _edit_radius)
var adding: bool = event.shift_pressed
var strength: float = 3.0 if adding else -3.0
terrain.edit_terrain_box(hit_pos, half_ext, strength)
print("Box edit at %s (size=%.1f, strength=%.1f)" % [hit_pos, _edit_radius, strength])