OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / erosion_procnode_test.gd

130 lines · gd ref:
@tool
extends Node3D

## Three-way A/B/C comparison:
## Left:   GLSL reference shader (known good)
## Center: ErosionNoise baked (base noise internal)
## Right:  Decomposed: GradientNoise → ErosionNoise → AltitudeColor

@export var generate: bool = false:
	set(v):
		if v: _generate()
		generate = false

var _bridge: BlenderBridge

func _ready() -> void:
	call_deferred("_generate")
	_bridge = $BlenderBridge as BlenderBridge
	if _bridge and Engine.is_editor_hint():
		var err := _bridge.start_server("/tmp/blender_bridge.sock")
		if err == OK:
			print("[ErosionTest] Bridge started")
			set_process(true)

var _exported := false

func _process(_delta: float) -> void:
	if not Engine.is_editor_hint() or not _bridge:
		return
	if _bridge.is_blender_connected() and not _exported:
		var tree := get_tree()
		if tree and tree.edited_scene_root:
			_bridge.export_scene(tree.edited_scene_root)
			_exported = true
			print("[ErosionTest] Scene exported to Blender")

func _generate() -> void:
	_build_baked()
	_build_decomposed()

func _build_baked() -> void:
	## Center: MeshGrid → ErosionNoise (base noise baked inside)
	var proc := $ProcBaked as ProcGeometryNode
	if not proc: return

	var g := ProcNodeGraph.new()
	var grid := g.add_node(MeshGridNode.new())
	g.get_node(grid).set_input_default(0, 10.0)
	g.get_node(grid).set_input_default(1, 10.0)
	g.get_node(grid).set_input_default(2, 256)
	g.get_node(grid).set_input_default(3, 256)

	var erosion := g.add_node(ErosionNoiseNode.new())
	# All defaults from Shadertoy

	var color := g.add_node(AltitudeColorNode.new())

	g.connect_sockets(grid, 0, erosion, 0)
	g.connect_sockets(erosion, 0, color, 0)
	g.connect_sockets(erosion, 1, color, 3) # erosion field → altitude color

	g.get_node(grid).set_graph_position(Vector2(-400, 0))
	g.get_node(erosion).set_graph_position(Vector2(-200, 0))
	g.get_node(color).set_graph_position(Vector2(0, 0))

	proc.set_graph(g)
	call_deferred("_apply_vertex_mat", proc)
	print("[ErosionTest] Baked: MeshGrid → ErosionNoise → AltitudeColor")

func _build_decomposed() -> void:
	## Right: MeshGrid → UV → GradientNoise → ErosionNoise(external) → AltitudeColor
	var proc := $ProcDecomposed as ProcGeometryNode
	if not proc: return

	var g := ProcNodeGraph.new()
	var grid := g.add_node(MeshGridNode.new())
	g.get_node(grid).set_input_default(0, 10.0)
	g.get_node(grid).set_input_default(1, 10.0)
	g.get_node(grid).set_input_default(2, 256)
	g.get_node(grid).set_input_default(3, 256)

	# UV field [0,1] — same coordinate space as the Shadertoy
	var uv := g.add_node(UVNode.new())

	# GradientNoise: iq's noise with analytical derivatives
	# Scale=3 = HEIGHT_TILES, Detail=3 = HEIGHT_OCTAVES, Gain=0.1 = HEIGHT_GAIN
	var noise := g.add_node(GradientNoiseNode.new())
	g.get_node(noise).set_input_default(1, 3.0)
	g.get_node(noise).set_input_default(2, 3)
	g.get_node(noise).set_input_default(3, 0.1)

	# ErosionNoise with external height+slope
	var erosion := g.add_node(ErosionNoiseNode.new())

	# AltitudeColor
	var color := g.add_node(AltitudeColorNode.new())

	# Wire
	g.connect_sockets(grid, 0, uv, 0)           # Grid → UV
	g.connect_sockets(uv, 0, noise, 0)           # UV field [0,1] → GradientNoise
	g.connect_sockets(grid, 0, erosion, 0)       # Grid geometry → ErosionNoise
	g.connect_sockets(noise, 0, erosion, 7)      # Height field → ErosionNoise Height
	g.connect_sockets(noise, 1, erosion, 8)      # Slope field → ErosionNoise Slope
	g.connect_sockets(erosion, 0, color, 0)      # Eroded geo → AltitudeColor
	g.connect_sockets(erosion, 1, color, 3)      # Erosion field → AltitudeColor

	g.get_node(grid).set_graph_position(Vector2(-600, 0))
	g.get_node(uv).set_graph_position(Vector2(-400, -80))
	g.get_node(noise).set_graph_position(Vector2(-200, -80))
	g.get_node(erosion).set_graph_position(Vector2(0, 0))
	g.get_node(color).set_graph_position(Vector2(200, 0))

	proc.set_graph(g)
	call_deferred("_apply_vertex_mat", proc)
	print("[ErosionTest] Decomposed: Grid → Position → GradientNoise → ErosionNoise → AltitudeColor")

func _apply_vertex_mat(proc: ProcGeometryNode) -> void:
	# Wait for MeshOutput to exist (created after first graph evaluation)
	if not is_inside_tree(): return
	await get_tree().process_frame
	if not is_inside_tree(): return
	await get_tree().process_frame
	var mo := proc.get_node_or_null(NodePath("MeshOutput")) as MeshInstance3D
	if mo:
		var mat := StandardMaterial3D.new()
		mat.vertex_color_use_as_albedo = true
		mat.roughness = 0.85
		mo.material_override = mat
		print("[ErosionTest] Applied vertex color material to ", proc.name)