Gododin Gododin Documentation

Linked repository source

examples/voxel_stream/project/main.gd

This read-only view comes from the file in the Gododin repository. It retains its design, example or evidence context.

Download the original file · Return to the guide

extends Node3D

# Godot owns presentation and input. VoxelWorld owns chunk work, staging,
# adopted mesh RIDs and instance RIDs. One Dictionary surface-add bridges them.
const CHUNK_MATERIAL := preload("res://chunk_material.tres")
const STREAM_EVAL := preload("res://stream_eval.gd")
# Time-spread upload budget: keep staging+committing finished chunks while the
# frame has headroom instead of a fixed chunk count. One pump+commit cycle
# always runs so progress never stalls; the native upload_budget stays as a
# hard per-frame ceiling.
const UPLOAD_BUDGET_USEC := 2000

@onready var world: Node = $World
@onready var cam_rig: Node3D = $CameraRig
@onready var camera: Camera3D = $CameraRig/Camera3D
@onready var stats_label: Label = $UI/Panel/VBox/Stats
@onready var seed_field: LineEdit = $UI/Panel/VBox/SeedRow/Seed
@onready var radius_slider: HSlider = $UI/Panel/VBox/RadiusRow/Radius
@onready var radius_label: Label = $UI/Panel/VBox/RadiusRow/RadiusLabel
@onready var feedback: Label = $UI/Panel/VBox/Feedback

# Keep the Resource alive for as long as any surface refers to its RID.
var _material: Material = CHUNK_MATERIAL
var _last_anchor := Vector3(1e9, 1e9, 1e9)
var _quitting := false
var _stats_commits := 0
var _stats_stamp := 0
var evaluation: RefCounted


func _ready() -> void:
	if OS.get_environment("GODODIN_VOXEL_STD_MATERIAL") != "":
		var standard := StandardMaterial3D.new()
		standard.vertex_color_use_as_albedo = true
		standard.roughness = 1.0
		_material = standard
	if OS.get_environment("GODODIN_VOXEL_NOSHADOW") != "":
		$Sun.shadow_enabled = false
	if OS.get_environment("GODODIN_VOXEL_NOFOG") != "":
		$WorldEnvironment.environment.fog_enabled = false
	$UI/Panel/VBox/SeedRow/Generate.pressed.connect(_on_generate)
	seed_field.text_submitted.connect(func(_text: String) -> void: _on_generate())
	radius_slider.value_changed.connect(_on_radius_changed)
	$StatsTimer.timeout.connect(_on_stats_timer)
	# EX-VOXEL: the authored world settings Resource seeds the controls, and
	# the coarse edit-committed event drives the success feedback.
	var settings: Resource = world.get("settings")
	if settings != null:
		seed_field.text = str(int(settings.seed))
		radius_slider.value = clampf(float(int(settings.view_radius)), 1.0, 12.0)
	world.connect("edit_committed", _on_edit_committed)
	if "--eval" in OS.get_cmdline_user_args() or "--probe" in OS.get_cmdline_user_args():
		seed_field.editable = false
		radius_slider.editable = false
		$UI/Panel/VBox/SeedRow/Generate.disabled = true
		evaluation = STREAM_EVAL.new(self)
		cam_rig.set_process(false)
		cam_rig.set_process_unhandled_input(false)
		if not evaluation.start():
			return
	else:
		_on_generate()
	_stats_stamp = Time.get_ticks_usec()


func _on_edit_committed(edit_id: int, chunks: int, wait_usec: int) -> void:
	# The coarse edit-committed event: geometry already changed as each chunk
	# committed; this is the success feedback, never the write notification.
	feedback.text = "Edit #%d committed — %d chunks in %d µs" % [edit_id, chunks, wait_usec]


func _on_generate() -> void:
	if not seed_field.text.is_valid_int():
		feedback.text = "Enter a whole-number seed."
		return
	if not configure_world(int(seed_field.text), int(radius_slider.value)):
		return
	feedback.text = "Streaming a new world…"
	seed_field.release_focus()
	_stats_commits = 0
	_stats_stamp = Time.get_ticks_usec()


func configure_world(seed_value: int, radius: int) -> bool:
	var code: int = world.call("configure", seed_value, radius)
	if code != 0:
		fail("configure=%d" % code)
		return false
	_last_anchor = Vector3(1e9, 1e9, 1e9)
	seed_field.text = str(seed_value)
	radius_slider.set_value_no_signal(radius)
	radius_label.text = "Radius %d" % radius
	_stats_commits = 0
	_stats_stamp = Time.get_ticks_usec()
	return true


func _on_radius_changed(value: float) -> void:
	radius_label.text = "Radius %d" % int(value)
	feedback.text = "Press Generate to apply the radius."


func fail(reason: String) -> void:
	if _quitting:
		return
	_quitting = true
	print("GODODIN_VOXEL_STREAM_FAIL %s" % reason)
	world.call("shutdown")
	get_tree().quit(1)


func _process(delta: float) -> void:
	if _quitting:
		return
	var entered := Time.get_ticks_usec()
	var anchor := cam_rig.global_position
	if anchor.distance_squared_to(_last_anchor) > 0.5:
		_last_anchor = anchor
		var code: int = world.call("set_anchor", anchor.x, anchor.y, anchor.z)
		if code < 0:
			fail("set_anchor=%d" % code)
			return
	var began: int = world.call("begin_frame")
	if began < 0:
		fail("begin_frame=%d" % began)
		return
	while true:
		var pumped: int = world.call("pump")
		if pumped < 0:
			fail("pump=%d last_error=%d" % [pumped, world.call("get_last_error")])
			return
		if pumped == 0:
			break
		if not _consume_staged():
			fail("consume")
			return
		if Time.get_ticks_usec() - entered > UPLOAD_BUDGET_USEC:
			break
	if evaluation != null:
		evaluation.tick(delta, entered, Time.get_ticks_usec() - entered)


func _consume_staged() -> bool:
	var vertex: PackedByteArray = world.call("take_vertex_bytes")
	var attribute: PackedByteArray = world.call("take_attribute_bytes")
	var index: PackedByteArray = world.call("take_index_bytes")
	var box_bytes: PackedByteArray = world.call("take_aabb_bytes")
	if box_bytes.size() != 24:
		return false
	var count: int = world.call("front_vertex_count")
	var index_count: int = world.call("front_index_count")
	if count == 0:
		return vertex.is_empty() and attribute.is_empty() and index.is_empty() and world.call("commit_front") == 0
	if vertex.size() != count * 16 or attribute.size() != count * 4 or index.size() != index_count * 2:
		return false
	var box := box_bytes.to_float32_array()
	var bounds := AABB(Vector3(box[0], box[1], box[2]), Vector3.ZERO)
	bounds.end = Vector3(box[3], box[4], box[5])
	var mesh := RenderingServer.mesh_create()
	# stage_front adopts the RID immediately; native rollback/commit/shutdown
	# owns it from here, including an upload or verification failure.
	if world.call("stage_front", mesh.get_id()) != 0:
		RenderingServer.free_rid(mesh)
		return false
	var surface := {
		"primitive": RenderingServer.PRIMITIVE_TRIANGLES,
		"format": int(world.call("get_surface_format")),
		"vertex_data": vertex,
		"attribute_data": attribute,
		"vertex_count": count,
		"aabb": bounds,
		"index_data": index,
		"index_count": index_count,
		"material": _material.get_rid(),
	}
	RenderingServer.mesh_add_surface(mesh, surface)
	if RenderingServer.mesh_get_surface_count(mesh) != 1:
		return false
	if evaluation != null and not evaluation.check_upload(mesh, surface, int(world.call("front_lod"))):
		return false
	return world.call("commit_front") == 0


func _unhandled_input(event: InputEvent) -> void:
	if evaluation != null:
		return
	if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
		if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
			_dig()


func _dig() -> void:
	# No physics bodies are needed for the demo: sample the short voxel ray.
	# This is input-event work, never a per-frame/per-voxel foreign loop.
	var direction := -camera.global_transform.basis.z
	for sample_index in range(1, 49):
		var position := camera.global_position + direction * (float(sample_index) * 0.25)
		var cell := Vector3i(position.floor())
		if world.call("get_voxel", cell.x, cell.y, cell.z) > 0:
			var edit_id: int = world.call("begin_edit")
			if edit_id < 0:
				return
			var changed := carve_sphere(cell)
			feedback.text = "Dug %d voxels — waiting for chunk commits…" % changed
			return
	feedback.text = "No terrain within 12 blocks."


func carve_sphere(center: Vector3i) -> int:
	var changed := 0
	for dx in range(-2, 3):
		for dy in range(-2, 3):
			for dz in range(-2, 3):
				if dx * dx + dy * dy + dz * dz > 4:
					continue
				var at := center + Vector3i(dx, dy, dz)
				var code: int = world.call("set_voxel", at.x, at.y, at.z, 0)
				if code < 0:
					fail("set_voxel=%d" % code)
					return -1
				changed += code
	if changed > 0:
		print("GODODIN_VOXEL_STREAM_DIG voxels=%d" % changed)
	return changed


func _on_stats_timer() -> void:
	if _quitting:
		return
	var now := Time.get_ticks_usec()
	var commits := int(world.call("get_chunks_meshed"))
	var rate := float(maxi(commits - _stats_commits, 0)) * 1e6 / float(maxi(now - _stats_stamp, 1))
	_stats_commits = commits
	_stats_stamp = now
	stats_label.text = "%d resident · %d waiting · %d fps\n%.1f uploads/s · %d LOD transitions" % [
		world.call("get_chunks_resident"), world.call("get_unsettled_chunks"),
		Engine.get_frames_per_second(), rate, world.call("get_lod_remeshes")]
	if feedback.text == "Streaming a new world…" and world.call("is_settled"):
		feedback.text = "Ready · edits last while chunks remain loaded"


func _exit_tree() -> void:
	# Native deinit has no Godot call authority, so free server RIDs here.
	var code: int = world.call("shutdown")
	if code != 0:
		push_error("GODODIN_VOXEL_STREAM_FAIL exit_shutdown=%d" % code)
	evaluation = null