OverviewFilesCommitsIssuesDocsReleasesPeers

proc-nodes / demo / scenes / mesh_generation_test.gd

137 lines · gd ref:
extends Node3D
## Proc-nodes mesh generation test — creates geometry via node graphs.
## Tests: mesh primitives, subdivision, noise displacement, instances.

@onready var stats_label: Label = $CanvasLayer/StatsLabel

var _orbit_speed: float = 0.3
var _orbit_distance: float = 5.0
var _orbit_angle: float = 0.0
var _meshes: Array[MeshInstance3D] = []


func _ready() -> void:
	_test_cube_subdivide_displace()
	_test_ico_sphere()
	_test_cylinder()
	_test_grid_displace()
	print("[ProcDemo] Created %d test meshes" % _meshes.size())


func _process(delta: float) -> void:
	# Orbit camera
	_orbit_angle += delta * _orbit_speed
	var cam := $Camera3D as Camera3D
	cam.position = Vector3(
		cos(_orbit_angle) * _orbit_distance,
		3.0,
		sin(_orbit_angle) * _orbit_distance
	)
	cam.look_at(Vector3.ZERO)

	# HUD
	if stats_label:
		var total_tris: int = 0
		for mi in _meshes:
			if mi.mesh:
				for s in range(mi.mesh.get_surface_count()):
					total_tris += mi.mesh.surface_get_array_len(s) / 3
		stats_label.text = "Proc Nodes Demo\nMeshes: %d\nTriangles: %d\nScroll: orbit distance" % [
			_meshes.size(), total_tris]


func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseButton and event.pressed:
		if event.button_index == MOUSE_BUTTON_WHEEL_UP:
			_orbit_distance = maxf(1.0, _orbit_distance - 0.5)
		elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
			_orbit_distance = minf(20.0, _orbit_distance + 0.5)


func _test_cube_subdivide_displace() -> void:
	# Build graph: MeshCube → SubdivideMesh → NoiseTexture (displace)
	var g := ProcNodeGraph.new()

	var cube_idx := g.add_node(MeshCubeNode.new())
	g.get_node(cube_idx).set_input_default(0, Vector3(1.5, 1.5, 1.5))  # Size

	var subdiv_idx := g.add_node(SubdivideMeshNode.new())
	g.get_node(subdiv_idx).set_input_default(1, 3)  # Level
	g.connect_sockets(cube_idx, 0, subdiv_idx, 0)  # Geometry

	var geo_node := ProcGeometryNode.new()
	geo_node.graph = g
	geo_node.position = Vector3(-2, 0, 0)
	add_child(geo_node)

	# Wait for execution and extract mesh
	await get_tree().process_frame
	await get_tree().process_frame
	_extract_mesh(geo_node, "Subdivided Cube")


func _test_ico_sphere() -> void:
	var g := ProcNodeGraph.new()

	var sphere_idx := g.add_node(MeshIcoSphereNode.new())
	g.get_node(sphere_idx).set_input_default(0, 1.0)  # Radius
	g.get_node(sphere_idx).set_input_default(1, 3)     # Subdivisions

	var geo_node := ProcGeometryNode.new()
	geo_node.graph = g
	geo_node.position = Vector3(0, 0, 0)
	add_child(geo_node)

	await get_tree().process_frame
	await get_tree().process_frame
	_extract_mesh(geo_node, "Ico Sphere")


func _test_cylinder() -> void:
	var g := ProcNodeGraph.new()

	var cyl_idx := g.add_node(MeshCylinderNode.new())
	g.get_node(cyl_idx).set_input_default(0, 16)   # Vertices
	g.get_node(cyl_idx).set_input_default(1, 0.5)   # Radius
	g.get_node(cyl_idx).set_input_default(2, 2.0)   # Depth

	var geo_node := ProcGeometryNode.new()
	geo_node.graph = g
	geo_node.position = Vector3(2, 0, 0)
	add_child(geo_node)

	await get_tree().process_frame
	await get_tree().process_frame
	_extract_mesh(geo_node, "Cylinder")


func _test_grid_displace() -> void:
	var g := ProcNodeGraph.new()

	var grid_idx := g.add_node(MeshGridNode.new())
	g.get_node(grid_idx).set_input_default(0, Vector2(4.0, 4.0))  # Size
	g.get_node(grid_idx).set_input_default(1, 32)  # X verts
	g.get_node(grid_idx).set_input_default(2, 32)  # Y verts

	var geo_node := ProcGeometryNode.new()
	geo_node.graph = g
	geo_node.position = Vector3(0, -1.5, 0)
	add_child(geo_node)

	await get_tree().process_frame
	await get_tree().process_frame
	_extract_mesh(geo_node, "Grid")


func _extract_mesh(geo_node: Node3D, label: String) -> void:
	# ProcGeometryNode creates child MeshInstance3D nodes
	for child in geo_node.get_children():
		if child is MeshInstance3D and child.mesh:
			_meshes.append(child)
			var tris := 0
			for s in range(child.mesh.get_surface_count()):
				tris += child.mesh.surface_get_array_len(s) / 3
			print("[ProcDemo] %s: %d surfaces, %d tris" % [label, child.mesh.get_surface_count(), tris])
			return
	print("[ProcDemo] %s: no mesh generated" % label)