@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)