diff --git a/.gitignore b/.gitignore index 37f05f2..589f6c7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .import +generated_image.png +generated_image.png diff --git a/addons/procedural_material/graph_edit.gd b/addons/procedural_material/graph_edit.gd index 9b7ecbb..b01a020 100644 --- a/addons/procedural_material/graph_edit.gd +++ b/addons/procedural_material/graph_edit.gd @@ -1,6 +1,8 @@ tool extends GraphEdit +signal graph_changed + func _ready(): pass @@ -14,6 +16,12 @@ func remove_node(node): if c.from == node or c.to == node: disconnect_node(c.from, c.from_port, c.to, c.to_port) +func send_changed_signal(): + $Timer.start() + +func do_send_changed_signal(): + emit_signal("graph_changed") + func generate_shader(node, shader_type = 0): var code if shader_type == 1: @@ -45,6 +53,6 @@ func generate_shader(node, shader_type = 0): else: shader_code += "COLOR = vec4(1.0);\n" shader_code += "}\n" - print("GENERATED SHADER:\n"+shader_code) + #print("GENERATED SHADER:\n"+shader_code) code += shader_code return code diff --git a/addons/procedural_material/material_preview.gd b/addons/procedural_material/material_preview.gd index 6b7cf18..877982f 100644 --- a/addons/procedural_material/material_preview.gd +++ b/addons/procedural_material/material_preview.gd @@ -6,6 +6,7 @@ var material = null func _ready(): material = ShaderMaterial.new() material.shader = Shader.new() + material.shader.set_code("shader_type spatial;\nvoid fragment() {\n ALBEDO=vec3(0.5);\n}\n") $Objects/Cube.set_surface_material(0, material) $Objects/Cylinder.set_surface_material(0, material) $AnimationPlayer.play("rotate") diff --git a/addons/procedural_material/node_base.gd b/addons/procedural_material/node_base.gd index 95f396b..9666c11 100644 --- a/addons/procedural_material/node_base.gd +++ b/addons/procedural_material/node_base.gd @@ -22,24 +22,32 @@ func initialize_properties(object_list): set(o.name, o.color) o.connect("color_changed", self, "_on_color_changed", [ o.name ]) +func get_seed(): + return int(offset.x)*3+int(offset.y)*5 + func update_property_widgets(): for o in property_widgets: if o is LineEdit: o.text = str(get(o.name)) + elif o is SpinBox: + o.value = get(o.name) elif o is ColorPickerButton: o.color = get(o.name) +func update_shaders(): + get_parent().send_changed_signal() + func _on_text_changed(new_text, variable): set(variable, float(new_text)) - get_parent().get_parent().generate_shader() + update_shaders() func _on_value_changed(new_value, variable): set(variable, new_value) - get_parent().get_parent().generate_shader() + update_shaders() func _on_color_changed(new_color, variable): set(variable, new_color) - get_parent().get_parent().generate_shader() + update_shaders() func get_source(index = 0): for c in get_parent().get_children(): @@ -51,7 +59,7 @@ func get_source(index = 0): func get_source_f(source): var rv if source.has("rgb"): - rv = "dot("+source.rgb+", vec3(1.0, 1.0, 1.0))" + rv = "dot("+source.rgb+", vec3(1.0))/3.0" elif source.has("f"): rv = source.f else: @@ -69,7 +77,12 @@ func get_source_rgb(source): return rv func get_shader_code(uv): - return _get_shader_code(uv) + var rv = _get_shader_code(uv) + if !rv.has("f") && rv.has("rgb"): + rv.f = "(dot("+rv.rgb+", vec3(1.0))/3.0)" + if !rv.has("rgb") && rv.has("f"): + rv.rgb = "vec3("+rv.f+")" + return rv func get_textures(): var list = {} @@ -107,10 +120,9 @@ func serialize(): return data func deserialize(data): - print("deserialize: "+name) offset = Vector2(data.node_position.x, data.node_position.y) for w in property_widgets: - var v = w.name - set(v, deserialize_element(data[v])) - print(" "+v+" = "+str(deserialize_element(data[v]))) + var variable = w.name + var value = deserialize_element(data[variable]) + set(variable, value) update_property_widgets() diff --git a/addons/procedural_material/nodes/blend.gd b/addons/procedural_material/nodes/blend.gd index 83638f1..88c8b72 100644 --- a/addons/procedural_material/nodes/blend.gd +++ b/addons/procedural_material/nodes/blend.gd @@ -22,10 +22,10 @@ func _get_shader_code(uv): var src0_code = src0.get_shader_code(uv) var src1_code = src1.get_shader_code(uv) var src2_code = { defs="", code="" } - var amount_str = str(amount) + var amount_str = "%.9f" % amount if src2 != null: src2_code = src2.get_shader_code(uv) - amount_str = str(src2_code.f) + amount_str = src2_code.f if generated_variants.empty(): rv.defs = src0_code.defs+src1_code.defs+src2_code.defs var variant_index = generated_variants.find(uv) diff --git a/addons/procedural_material/nodes/colorize.gd b/addons/procedural_material/nodes/colorize.gd index ae71d2b..6bfd92f 100644 --- a/addons/procedural_material/nodes/colorize.gd +++ b/addons/procedural_material/nodes/colorize.gd @@ -6,10 +6,6 @@ var color1 func _ready(): set_slot(0, true, 0, Color(0.5, 0.5, 1), true, 0, Color(0.5, 0.5, 1)) - initialize_properties([ $color0, $color1 ]) - -func color_to_string(c): - return "vec3("+str(c.r)+","+str(c.g)+","+str(c.b)+")" func _get_shader_code(uv): var rv = { defs="", code="" } @@ -18,15 +14,20 @@ func _get_shader_code(uv): return rv var src_code = src.get_shader_code(uv) if generated_variants.empty(): - rv.defs = src_code.defs; + rv.defs = src_code.defs+$Control.get_shader(name+"_gradient"); var variant_index = generated_variants.find(uv) if variant_index == -1: variant_index = generated_variants.size() generated_variants.append(uv) - rv.code = src_code.code+"vec3 "+name+"_"+str(variant_index)+"_rgb = mix("+color_to_string(color0)+", "+color_to_string(color1)+", "+get_source_f(src_code)+");\n" - rv.f = get_source_f(src_code) + rv.code = src_code.code+"vec3 "+name+"_"+str(variant_index)+"_rgb = "+name+"_gradient("+src_code.f+");\n" rv.rgb = name+"_"+str(variant_index)+"_rgb" return rv -func _get_state_variables(): - return [ "color0", "color1" ] +func serialize(): + var data = .serialize() + data.gradient = $Control.serialize() + return data + +func deserialize(data): + $Control.deserialize(data.gradient) + .deserialize(data) diff --git a/addons/procedural_material/nodes/colorize.tscn b/addons/procedural_material/nodes/colorize.tscn index 6840415..8e01ab3 100644 --- a/addons/procedural_material/nodes/colorize.tscn +++ b/addons/procedural_material/nodes/colorize.tscn @@ -1,7 +1,7 @@ -[gd_scene load_steps=3 format=2] +[gd_scene load_steps=4 format=2] [ext_resource path="res://addons/procedural_material/nodes/colorize.gd" type="Script" id=1] - +[ext_resource path="res://addons/procedural_material/widgets/gradient_editor.tscn" type="PackedScene" id=2] [sub_resource type="Theme" id=1] @@ -14,8 +14,8 @@ anchor_right = 0.0 anchor_bottom = 0.0 margin_left = 1.0 margin_top = 4.0 -margin_right = 98.0 -margin_bottom = 74.0 +margin_right = 133.0 +margin_bottom = 63.0 rect_pivot_offset = Vector2( 0, 0 ) rect_clip_content = false mouse_filter = 1 @@ -36,71 +36,18 @@ slot/0/left_color = Color( 0.5, 0.5, 1, 1 ) slot/0/right_enabled = true slot/0/right_type = 0 slot/0/right_color = Color( 0.5, 0.5, 1, 1 ) -slot/1/left_enabled = false -slot/1/left_type = 0 -slot/1/left_color = Color( 1, 1, 1, 1 ) -slot/1/right_enabled = false -slot/1/right_type = 0 -slot/1/right_color = Color( 1, 1, 1, 1 ) script = ExtResource( 1 ) _sections_unfolded = [ "Theme" ] -[node name="color0" type="ColorPickerButton" parent="." index="0"] +[node name="Control" parent="." index="0" instance=ExtResource( 2 )] -anchor_left = 0.0 -anchor_top = 0.0 -anchor_right = 0.0 -anchor_bottom = 0.0 margin_left = 16.0 margin_top = 24.0 -margin_right = 81.0 -margin_bottom = 44.0 -rect_pivot_offset = Vector2( 0, 0 ) -rect_clip_content = false -focus_mode = 2 -mouse_filter = 0 -mouse_default_cursor_shape = 0 -size_flags_horizontal = 1 -size_flags_vertical = 1 -toggle_mode = false -enabled_focus_mode = 2 -shortcut = null -group = null -text = "0" -flat = false -align = 1 -color = Color( 0, 0, 0, 1 ) -edit_alpha = true -_sections_unfolded = [ "Caret", "Placeholder" ] - -[node name="color1" type="ColorPickerButton" parent="." index="1"] - -anchor_left = 0.0 -anchor_top = 0.0 -anchor_right = 0.0 -anchor_bottom = 0.0 -margin_left = 16.0 -margin_top = 44.0 -margin_right = 81.0 -margin_bottom = 64.0 -rect_pivot_offset = Vector2( 0, 0 ) -rect_clip_content = false -focus_mode = 2 -mouse_filter = 0 -mouse_default_cursor_shape = 0 -size_flags_horizontal = 1 -size_flags_vertical = 1 -toggle_mode = false -enabled_focus_mode = 2 -shortcut = null -group = null -text = "0" -flat = false -align = 1 -color = Color( 1, 1, 1, 1 ) -edit_alpha = true -_sections_unfolded = [ "Caret", "Placeholder" ] +margin_right = 136.0 +margin_bottom = 54.0 [connection signal="close_request" from="." to="." method="queue_free"] +[connection signal="updated" from="Control" to="." method="update_shaders"] + diff --git a/addons/procedural_material/nodes/image.gd b/addons/procedural_material/nodes/image.gd index d74d3f3..340a2d5 100644 --- a/addons/procedural_material/nodes/image.gd +++ b/addons/procedural_material/nodes/image.gd @@ -39,3 +39,12 @@ func _on_TextureButton_pressed(): dialog.add_filter("*.jpg;JPG image") dialog.connect("file_selected", self, "set_texture") dialog.popup_centered() + +func serialize(): + var data = .serialize() + data.file_path = file_path + return data + +func deserialize(data): + set_texture(data.file_path) + .deserialize(data) diff --git a/addons/procedural_material/nodes/image.tscn b/addons/procedural_material/nodes/image.tscn index e743b6b..a5bfbe3 100644 --- a/addons/procedural_material/nodes/image.tscn +++ b/addons/procedural_material/nodes/image.tscn @@ -6,7 +6,7 @@ [sub_resource type="Theme" id=1] -[node name="Image" type="GraphNode"] +[node name="Image" type="GraphNode" index="0"] anchor_left = 0.0 anchor_top = 0.0 diff --git a/addons/procedural_material/nodes/iqnoise.gd b/addons/procedural_material/nodes/iqnoise.gd deleted file mode 100644 index ff90aed..0000000 --- a/addons/procedural_material/nodes/iqnoise.gd +++ /dev/null @@ -1,22 +0,0 @@ -tool -extends "res://addons/procedural_material/node_base.gd" - -var subdivide -var u -var v - -func _ready(): - set_slot(0, false, 0, Color(0.5, 0.5, 1), true, 0, Color(0.5, 0.5, 1)) - initialize_properties([ $GridContainer/subdivide, $GridContainer/u, $GridContainer/v ]) - -func _get_shader_code(uv): - var rv = { defs="", code="" } - if generated_variants.empty(): - rv.defs = "float "+name+"_f(vec2 uv) { return iqnoise(uv, "+str(subdivide)+", "+str(u)+", "+str(v)+"); }\n" - var variant_index = generated_variants.find(uv) - if variant_index == -1: - variant_index = generated_variants.size() - generated_variants.append(uv) - rv.code = "float "+name+"_"+str(variant_index)+"_f = "+name+"_f("+uv+");\n" - rv.f = name+"_"+str(variant_index)+"_f" - return rv diff --git a/addons/procedural_material/nodes/iqnoise.tscn b/addons/procedural_material/nodes/iqnoise.tscn deleted file mode 100644 index bf50456..0000000 --- a/addons/procedural_material/nodes/iqnoise.tscn +++ /dev/null @@ -1,198 +0,0 @@ -[gd_scene load_steps=3 format=2] - -[ext_resource path="res://addons/procedural_material/nodes/iqnoise.gd" type="Script" id=1] - -[sub_resource type="Theme" id=1] - - -[node name="IQNoise" type="GraphNode" index="0"] - -anchor_left = 0.0 -anchor_top = 0.0 -anchor_right = 0.0 -anchor_bottom = 0.0 -margin_left = 1.0 -margin_top = 2.0 -margin_right = 161.0 -margin_bottom = 111.0 -rect_pivot_offset = Vector2( 0, 0 ) -rect_clip_content = false -mouse_filter = 1 -mouse_default_cursor_shape = 0 -size_flags_horizontal = 1 -size_flags_vertical = 1 -theme = SubResource( 1 ) -title = "IQNoise" -offset = Vector2( 0, 0 ) -show_close = true -resizable = false -selected = false -comment = false -overlay = 0 -slot/0/left_enabled = false -slot/0/left_type = 0 -slot/0/left_color = Color( 0.5, 0.5, 1, 1 ) -slot/0/right_enabled = true -slot/0/right_type = 0 -slot/0/right_color = Color( 0.5, 0.5, 1, 1 ) -script = ExtResource( 1 ) -_sections_unfolded = [ "Theme" ] - -[node name="GridContainer" type="GridContainer" parent="." index="0"] - -anchor_left = 0.0 -anchor_top = 0.0 -anchor_right = 0.0 -anchor_bottom = 0.0 -margin_left = 16.0 -margin_top = 24.0 -margin_right = 144.0 -margin_bottom = 104.0 -rect_pivot_offset = Vector2( 0, 0 ) -rect_clip_content = false -mouse_filter = 1 -mouse_default_cursor_shape = 0 -size_flags_horizontal = 1 -size_flags_vertical = 1 -columns = 2 - -[node name="Label1" type="Label" parent="GridContainer" index="0"] - -anchor_left = 0.0 -anchor_top = 0.0 -anchor_right = 0.0 -anchor_bottom = 0.0 -margin_top = 5.0 -margin_right = 66.0 -margin_bottom = 19.0 -rect_pivot_offset = Vector2( 0, 0 ) -rect_clip_content = false -mouse_filter = 2 -mouse_default_cursor_shape = 0 -size_flags_horizontal = 1 -size_flags_vertical = 4 -text = "Subdivide:" -percent_visible = 1.0 -lines_skipped = 0 -max_lines_visible = -1 - -[node name="subdivide" type="LineEdit" parent="GridContainer" index="1"] - -anchor_left = 0.0 -anchor_top = 0.0 -anchor_right = 0.0 -anchor_bottom = 0.0 -margin_left = 70.0 -margin_right = 128.0 -margin_bottom = 24.0 -rect_pivot_offset = Vector2( 0, 0 ) -rect_clip_content = false -focus_mode = 2 -mouse_filter = 0 -mouse_default_cursor_shape = 1 -size_flags_horizontal = 1 -size_flags_vertical = 1 -text = "8" -focus_mode = 2 -context_menu_enabled = true -placeholder_alpha = 0.6 -caret_blink = false -caret_blink_speed = 0.65 -caret_position = 0 -_sections_unfolded = [ "Caret", "Placeholder" ] - -[node name="Label2" type="Label" parent="GridContainer" index="2"] - -anchor_left = 0.0 -anchor_top = 0.0 -anchor_right = 0.0 -anchor_bottom = 0.0 -margin_top = 33.0 -margin_right = 66.0 -margin_bottom = 47.0 -rect_pivot_offset = Vector2( 0, 0 ) -rect_clip_content = false -mouse_filter = 2 -mouse_default_cursor_shape = 0 -size_flags_horizontal = 1 -size_flags_vertical = 4 -text = "U:" -percent_visible = 1.0 -lines_skipped = 0 -max_lines_visible = -1 - -[node name="u" type="LineEdit" parent="GridContainer" index="3"] - -anchor_left = 0.0 -anchor_top = 0.0 -anchor_right = 0.0 -anchor_bottom = 0.0 -margin_left = 70.0 -margin_top = 28.0 -margin_right = 128.0 -margin_bottom = 52.0 -rect_pivot_offset = Vector2( 0, 0 ) -rect_clip_content = false -focus_mode = 2 -mouse_filter = 0 -mouse_default_cursor_shape = 1 -size_flags_horizontal = 1 -size_flags_vertical = 1 -text = "0.5" -focus_mode = 2 -context_menu_enabled = true -placeholder_alpha = 0.6 -caret_blink = false -caret_blink_speed = 0.65 -caret_position = 0 -_sections_unfolded = [ "Caret", "Placeholder" ] - -[node name="Label3" type="Label" parent="GridContainer" index="4"] - -anchor_left = 0.0 -anchor_top = 0.0 -anchor_right = 0.0 -anchor_bottom = 0.0 -margin_top = 61.0 -margin_right = 66.0 -margin_bottom = 75.0 -rect_pivot_offset = Vector2( 0, 0 ) -rect_clip_content = false -mouse_filter = 2 -mouse_default_cursor_shape = 0 -size_flags_horizontal = 1 -size_flags_vertical = 4 -text = "V:" -percent_visible = 1.0 -lines_skipped = 0 -max_lines_visible = -1 - -[node name="v" type="LineEdit" parent="GridContainer" index="5"] - -anchor_left = 0.0 -anchor_top = 0.0 -anchor_right = 0.0 -anchor_bottom = 0.0 -margin_left = 70.0 -margin_top = 56.0 -margin_right = 128.0 -margin_bottom = 80.0 -rect_pivot_offset = Vector2( 0, 0 ) -rect_clip_content = false -focus_mode = 2 -mouse_filter = 0 -mouse_default_cursor_shape = 1 -size_flags_horizontal = 1 -size_flags_vertical = 1 -text = "0.5" -focus_mode = 2 -context_menu_enabled = true -placeholder_alpha = 0.6 -caret_blink = false -caret_blink_speed = 0.65 -caret_position = 0 -_sections_unfolded = [ "Caret", "Placeholder" ] - -[connection signal="close_request" from="." to="." method="queue_free"] - - diff --git a/addons/procedural_material/nodes/material.tscn b/addons/procedural_material/nodes/material.tscn index 73e6493..4c6a268 100644 --- a/addons/procedural_material/nodes/material.tscn +++ b/addons/procedural_material/nodes/material.tscn @@ -33,12 +33,12 @@ slot/0/left_color = Color( 0.5, 0.5, 1, 1 ) slot/0/right_enabled = false slot/0/right_type = 0 slot/0/right_color = Color( 0.5, 0.5, 1, 1 ) -slot/1/left_enabled = false +slot/1/left_enabled = true slot/1/left_type = 0 -slot/1/left_color = Color( 1, 1, 1, 1 ) +slot/1/left_color = Color( 0.5, 0.5, 1, 1 ) slot/1/right_enabled = false slot/1/right_type = 0 -slot/1/right_color = Color( 1, 1, 1, 1 ) +slot/1/right_color = Color( 0.5, 0.5, 1, 1 ) script = ExtResource( 1 ) _sections_unfolded = [ "Theme" ] diff --git a/addons/procedural_material/nodes/perlin.gd b/addons/procedural_material/nodes/perlin.gd index 3382089..fb249e5 100644 --- a/addons/procedural_material/nodes/perlin.gd +++ b/addons/procedural_material/nodes/perlin.gd @@ -13,7 +13,7 @@ func _ready(): func _get_shader_code(uv): var rv = { defs="", code="" } if generated_variants.empty(): - rv.defs = "float "+name+"_f(vec2 uv) { return perlin(uv, vec2("+str(scale_x)+", "+str(scale_y)+"), "+str(iterations)+", "+str(persistance)+"); }\n" + rv.defs = "float "+name+"_f(vec2 uv) { return perlin(uv, vec2(%f, %f), %d, %.9f, %d); }\n" % [ scale_x, scale_y, iterations, persistance, get_seed() ] var variant_index = generated_variants.find(uv) if variant_index == -1: variant_index = generated_variants.size() @@ -21,3 +21,6 @@ func _get_shader_code(uv): rv.code = "float "+name+"_"+str(variant_index)+"_f = "+name+"_f("+uv+");\n" rv.f = name+"_"+str(variant_index)+"_f" return rv + +func _on_offset_changed(): + update_shaders() diff --git a/addons/procedural_material/nodes/perlin.tscn b/addons/procedural_material/nodes/perlin.tscn index c747a6f..05630eb 100644 --- a/addons/procedural_material/nodes/perlin.tscn +++ b/addons/procedural_material/nodes/perlin.tscn @@ -5,7 +5,7 @@ [sub_resource type="Theme" id=1] -[node name="Perlin" type="GraphNode" index="0"] +[node name="Perlin" type="GraphNode"] anchor_left = 0.0 anchor_top = 0.0 @@ -249,4 +249,6 @@ _sections_unfolded = [ "Caret", "Placeholder" ] [connection signal="close_request" from="." to="." method="queue_free"] +[connection signal="offset_changed" from="." to="." method="_on_offset_changed"] + diff --git a/addons/procedural_material/nodes/transform.gd b/addons/procedural_material/nodes/transform.gd index 83fa549..e76dc1b 100644 --- a/addons/procedural_material/nodes/transform.gd +++ b/addons/procedural_material/nodes/transform.gd @@ -16,7 +16,7 @@ func _get_shader_code(uv): rv.uv = name+"_uv("+uv+")" var src_code = src.get_shader_code(rv.uv) if !generated: - rv.defs = src_code.defs+"vec2 "+name+"_uv(vec2 uv) { return transform(uv, "+str(3.1415928*rotate/180.0)+", "+str(scale)+"); }\n" + rv.defs = src_code.defs+"vec2 "+name+"_uv(vec2 uv) { return transform(uv, %.9f, %.9f); }\n" % [ 3.1415928*rotate/180.0, scale ] generated = true rv.code = src_code.code; if src_code.has("f"): diff --git a/addons/procedural_material/nodes/voronoi.gd b/addons/procedural_material/nodes/voronoi.gd new file mode 100644 index 0000000..9a7b6b2 --- /dev/null +++ b/addons/procedural_material/nodes/voronoi.gd @@ -0,0 +1,25 @@ +tool +extends "res://addons/procedural_material/node_base.gd" + +var scale_x +var scale_y +var intensity + +func _ready(): + set_slot(0, false, 0, Color(0.5, 0.5, 1), true, 0, Color(0.5, 0.5, 1)) + initialize_properties([ $HBoxContainer1/scale_x, $HBoxContainer2/scale_y, $HBoxContainer3/intensity ]) + +func _get_shader_code(uv): + var rv = { defs="", code="" } + if generated_variants.empty(): + rv.defs = "vec4 "+name+"_xyzw(vec2 uv) { return voronoi(uv, vec2(%f, %f), %.9f, %d); }\n" % [ scale_x, scale_y, intensity, get_seed() ] + var variant_index = generated_variants.find(uv) + if variant_index == -1: + variant_index = generated_variants.size() + generated_variants.append(uv) + rv.code = "vec4 "+name+"_"+str(variant_index)+"_xyzw = "+name+"_xyzw("+uv+");\n" + rv.f = name+"_"+str(variant_index)+"_xyzw.z" + return rv + +func _on_offset_changed(): + update_shaders() diff --git a/addons/procedural_material/nodes/voronoi.tscn b/addons/procedural_material/nodes/voronoi.tscn new file mode 100644 index 0000000..0c82231 --- /dev/null +++ b/addons/procedural_material/nodes/voronoi.tscn @@ -0,0 +1,256 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://addons/procedural_material/nodes/voronoi.gd" type="Script" id=1] + +[sub_resource type="Theme" id=1] + + +[node name="Voronoi" type="GraphNode" index="0"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 1.0 +margin_top = 2.0 +margin_right = 172.0 +margin_bottom = 105.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 1 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +theme = SubResource( 1 ) +title = "Voronoi" +offset = Vector2( 0, 0 ) +show_close = true +resizable = false +selected = false +comment = false +overlay = 0 +slot/0/left_enabled = false +slot/0/left_type = 0 +slot/0/left_color = Color( 0.5, 0.5, 1, 1 ) +slot/0/right_enabled = true +slot/0/right_type = 0 +slot/0/right_color = Color( 0.5, 0.5, 1, 1 ) +slot/1/left_enabled = false +slot/1/left_type = 0 +slot/1/left_color = Color( 1, 1, 1, 1 ) +slot/1/right_enabled = false +slot/1/right_type = 0 +slot/1/right_color = Color( 1, 1, 1, 1 ) +slot/2/left_enabled = false +slot/2/left_type = 0 +slot/2/left_color = Color( 1, 1, 1, 1 ) +slot/2/right_enabled = false +slot/2/right_type = 0 +slot/2/right_color = Color( 1, 1, 1, 1 ) +script = ExtResource( 1 ) +_sections_unfolded = [ "Theme" ] + +[node name="HBoxContainer1" type="HBoxContainer" parent="." index="0"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 16.0 +margin_top = 24.0 +margin_right = 155.0 +margin_bottom = 48.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 1 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 3 +size_flags_vertical = 1 +alignment = 0 +_sections_unfolded = [ "Size Flags" ] + +[node name="Label1" type="Label" parent="HBoxContainer1" index="0"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_top = 5.0 +margin_right = 61.0 +margin_bottom = 19.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 2 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 3 +size_flags_vertical = 4 +text = "Scale X:" +percent_visible = 1.0 +lines_skipped = 0 +max_lines_visible = -1 +_sections_unfolded = [ "Anchor", "Size Flags" ] + +[node name="scale_x" type="SpinBox" parent="HBoxContainer1" index="1"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 65.0 +margin_right = 139.0 +margin_bottom = 24.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 0 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 8 +size_flags_vertical = 1 +min_value = 0.0 +max_value = 32.0 +step = 1.0 +page = 0.0 +value = 4.0 +exp_edit = false +rounded = false +editable = true +prefix = "" +suffix = "" +_sections_unfolded = [ "Anchor", "Caret", "Focus", "Grow Direction", "Hint", "Margin", "Mouse", "Placeholder", "Rect", "Size Flags" ] + +[node name="HBoxContainer2" type="HBoxContainer" parent="." index="1"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 16.0 +margin_top = 48.0 +margin_right = 155.0 +margin_bottom = 72.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 1 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +alignment = 0 + +[node name="Label2" type="Label" parent="HBoxContainer2" index="0"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_top = 5.0 +margin_right = 61.0 +margin_bottom = 19.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 2 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 3 +size_flags_vertical = 4 +text = "Scale Y:" +percent_visible = 1.0 +lines_skipped = 0 +max_lines_visible = -1 +_sections_unfolded = [ "Size Flags" ] + +[node name="scale_y" type="SpinBox" parent="HBoxContainer2" index="1"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 65.0 +margin_right = 139.0 +margin_bottom = 24.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 0 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 8 +size_flags_vertical = 1 +min_value = 0.0 +max_value = 32.0 +step = 1.0 +page = 0.0 +value = 4.0 +exp_edit = false +rounded = false +editable = true +prefix = "" +suffix = "" +_sections_unfolded = [ "Caret", "Placeholder", "Size Flags" ] + +[node name="HBoxContainer3" type="HBoxContainer" parent="." index="2"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 16.0 +margin_top = 73.0 +margin_right = 155.0 +margin_bottom = 97.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 1 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +alignment = 0 + +[node name="Label3" type="Label" parent="HBoxContainer3" index="0"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_top = 5.0 +margin_right = 61.0 +margin_bottom = 19.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 2 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 3 +size_flags_vertical = 4 +text = "Intensity:" +percent_visible = 1.0 +lines_skipped = 0 +max_lines_visible = -1 +_sections_unfolded = [ "Size Flags" ] + +[node name="intensity" type="SpinBox" parent="HBoxContainer3" index="1"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 65.0 +margin_right = 139.0 +margin_bottom = 24.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 0 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 8 +size_flags_vertical = 1 +min_value = 0.0 +max_value = 3.0 +step = 0.05 +page = 0.0 +value = 1.0 +exp_edit = false +rounded = false +editable = true +prefix = "" +suffix = "" +_sections_unfolded = [ "Caret", "Placeholder", "Size Flags" ] + +[connection signal="close_request" from="." to="." method="queue_free"] + +[connection signal="offset_changed" from="." to="." method="_on_offset_changed"] + + diff --git a/addons/procedural_material/nodes/warp.gd b/addons/procedural_material/nodes/warp.gd index 1c6f575..cc68b4b 100644 --- a/addons/procedural_material/nodes/warp.gd +++ b/addons/procedural_material/nodes/warp.gd @@ -24,7 +24,7 @@ func _get_shader_code(uv): var src1_code3 = src1.get_shader_code(uv+"-vec2(0.0, 0.01)") rv.defs = src1_code0.defs rv.code = src1_code0.code+src1_code1.code+src1_code2.code+src1_code3.code - rv.code += "vec2 "+name+"_"+str(variant_index)+"_uv = "+uv+"+"+str(amount)+"*vec2("+get_source_f(src1_code0)+"-"+get_source_f(src1_code1)+", "+get_source_f(src1_code2)+"-"+get_source_f(src1_code3)+");\n" + rv.code += "vec2 "+name+"_"+str(variant_index)+"_uv = "+uv+"+%.9f*vec2((%s)-(%s), (%s)-(%s));\n" % [ amount, src1_code0.f, src1_code1.f, src1_code2.f, src1_code3.f ] var src0_code = src0.get_shader_code(name+"_"+str(variant_index)+"_uv") rv.defs += src0_code.defs rv.code += src0_code.code diff --git a/addons/procedural_material/pm_editor.gd b/addons/procedural_material/pm_editor.gd index 09dc31e..5485917 100644 --- a/addons/procedural_material/pm_editor.gd +++ b/addons/procedural_material/pm_editor.gd @@ -1,6 +1,7 @@ tool extends Container +var popup_menu = null var popup_position = Vector2(0, 0) var selected_node = null @@ -11,31 +12,52 @@ const MENU = [ { command="load_texture", description="Load texture" }, { command="save_texture", description="Save texture" }, { command="export_texture", description="Export texture" }, - { name="image", description="Image" }, - { name="sine", description="Sine" }, - { name="bricks", description="Bricks" }, - { name="iqnoise", description="IQ Noise" }, - { name="perlin", description="Perlin noise" }, - { name="transform", description="Transform" }, - { name="warp", description="Warp" }, - { name="colorize", description="Colorize" }, - { name="normal_map", description="Normal Map" }, - { name="blend", description="Blend" } + { submenu="generator", description="Generator" }, + { name="image", description="Image", in_submenu="generator" }, + { name="sine", description="Sine", in_submenu="generator" }, + { name="bricks", description="Bricks", in_submenu="generator" }, + { name="perlin", description="Perlin noise", in_submenu="generator" }, + { name="voronoi", description="Voronoi Noise", in_submenu="generator" }, + { submenu="filter", description="Filter" }, + { name="colorize", description="Colorize", in_submenu="filter" }, + { name="blend", description="Blend", in_submenu="filter" }, + { name="transform", description="Transform", in_submenu="filter" }, + { name="warp", description="Warp", in_submenu="filter" }, + { name="normal_map", description="Normal Map", in_submenu="filter" } ] func _ready(): # Duplicate the material we'll modify and store the shaders - preview_material = $Container/ViewportContainer/MaterialPreview.material - $Container/ViewportContainer/SelectedPreview.material = $Container/ViewportContainer/SelectedPreview.material.duplicate(true) - texture_preview_material = $Container/ViewportContainer/SelectedPreview.material + preview_material = $Preview/ViewportContainer/MaterialPreview.material + $Preview/ViewportContainer/SelectedPreview.material = $Preview/ViewportContainer/SelectedPreview.material.duplicate(true) + texture_preview_material = $Preview/ViewportContainer/SelectedPreview.material $GraphEdit.add_valid_connection_type(0, 0) - $GraphEdit/PopupMenu.clear() + # create or update popup menu + if popup_menu != null: + popup_menu.queue_free() + popup_menu = create_menu() + $GraphEdit.add_child(popup_menu) + +func create_menu(in_submenu = null): + var menu = PopupMenu.new() + menu.connect("id_pressed", self, "_on_PopupMenu_id_pressed") for i in MENU.size(): - $GraphEdit/PopupMenu.add_item(MENU[i].description, i) + if MENU[i].has("in_submenu"): + if in_submenu != MENU[i].in_submenu: + continue + elif in_submenu != null: + continue + if MENU[i].has("submenu"): + var submenu = create_menu(MENU[i].submenu) + menu.add_child(submenu) + menu.add_submenu_item(MENU[i].description, submenu.get_name()) + else: + menu.add_item(MENU[i].description, i) + return menu func _on_GraphEdit_popup_request(position): popup_position = position - $GraphEdit/PopupMenu.popup(Rect2(position, $GraphEdit/PopupMenu.rect_size)) + popup_menu.popup(Rect2(position, popup_menu.rect_size)) func _on_PopupMenu_id_pressed(id): var node_type = null @@ -45,6 +67,13 @@ func _on_PopupMenu_id_pressed(id): node_type = load("res://addons/procedural_material/nodes/"+MENU[id].name+".tscn") if node_type != null: var node = node_type.instance() + var i = 0 + while true: + var name = MENU[id].name+"_"+str(i) + if !$GraphEdit.has_node(name): + node.set_name(name) + break + i += 1 $GraphEdit.add_child(node) node.offset = ($GraphEdit.scroll_offset + popup_position - $GraphEdit.rect_global_position) / $GraphEdit.zoom @@ -125,7 +154,7 @@ func export_texture(): func setup_material(shader_material, textures, shader_code): for k in textures.keys(): - print("Setting param "+k+" to "+str(textures[k])) + #print("Setting param "+k+" to "+str(textures[k])) shader_material.set_shader_param(k+"_tex", textures[k]) shader_material.shader.code = shader_code @@ -135,6 +164,7 @@ func generate_shader(): setup_material(texture_preview_material, selected_node.get_textures(), $GraphEdit.generate_shader(selected_node)) func _on_GraphEdit_node_selected(node): - selected_node = node - setup_material(texture_preview_material, selected_node.get_textures(), $GraphEdit.generate_shader(selected_node)) + if selected_node != node: + selected_node = node + setup_material(texture_preview_material, selected_node.get_textures(), $GraphEdit.generate_shader(selected_node)) diff --git a/addons/procedural_material/pm_editor.tscn b/addons/procedural_material/pm_editor.tscn index dff3f24..5664e84 100644 --- a/addons/procedural_material/pm_editor.tscn +++ b/addons/procedural_material/pm_editor.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=21 format=2] +[gd_scene load_steps=19 format=2] [ext_resource path="res://addons/procedural_material/pm_editor.gd" type="Script" id=1] [ext_resource path="res://addons/procedural_material/graph_edit.gd" type="Script" id=2] @@ -119,7 +119,11 @@ subdivide_depth = 0 [sub_resource type="Shader" id=5] -code = "" +code = "shader_type spatial; +void fragment() { + ALBEDO=vec3(0.5); +} +" [sub_resource type="ShaderMaterial" id=6] @@ -273,22 +277,19 @@ shader = SubResource( 11 ) code = "shader_type canvas_item; -float hash1(vec2 p) { - float q = dot(p,vec2(127.1,311.7)); - return fract(sin(q)*43758.5453); +float rand(vec2 x) { + return fract(sin(dot(x, vec2(13.9898, 8.141))) * 43758.5453); } -vec2 hash2(vec2 p) { - vec2 q = vec2( dot(p,vec2(127.1,311.7)), - dot(p,vec2(269.5,183.3)) ); - return fract(sin(q)*43758.5453); +vec2 rand2(vec2 x) { + return fract(sin(vec2(dot(x, vec2(13.9898, 8.141)), + dot(x, vec2(3.4562, 17.398)))) * 43758.5453); } -vec3 hash3(vec2 p) { - vec3 q = vec3( dot(p,vec2(127.1,311.7)), - dot(p,vec2(269.5,183.3)), - dot(p,vec2(419.2,371.9)) ); - return fract(sin(q)*43758.5453); +vec3 rand3(vec2 x) { + return fract(sin(vec3(dot(x, vec2(13.9898, 8.141)), + dot(x, vec2(3.4562, 17.398)), + dot(x, vec2(13.254, 5.867)))) * 43758.5453); } float sine(vec2 uv, float count, float sharpness) { @@ -326,113 +327,79 @@ float colored_bricks(vec2 uv, vec2 count, float offset) { return fract(x/3.0+y/7.0); } -float iqnoise(vec2 uv, float s, float u, float v) { - uv *= s; - vec2 p = floor(uv); - vec2 f = fract(uv); - - float k = 1.0+63.0*pow(1.0-v,4.0); - - float va = 0.0; - float wt = 0.0; - for( int j=-2; j<=2; j++ ) - for( int i=-2; i<=2; i++ ) - { - vec2 g = vec2( float(i),float(j) ); - vec3 o = hash3( p + g )*vec3(u,u,1.0); - vec2 r = g - f + o.xy; - float d = dot(r,r); - float ww = pow( 1.0-smoothstep(0.0,1.414,sqrt(d)), k ); - va += o.z*ww; - wt += ww; +float perlin(vec2 uv, vec2 size, int iterations, float persistence) { + float rv = 0.0; + float coef = 1.0; + float acc = 0.0; + for (int i = 0; i < iterations; ++i) { + vec2 step = vec2(1.0)/size; + float f0 = rand(floor(fract(uv)*size)); + float f1 = rand(floor(fract(uv+vec2(step.x, 0.0))*size)); + float f2 = rand(floor(fract(uv+vec2(0.0, step.y))*size)); + float f3 = rand(floor(fract(uv+vec2(step.x, step.y))*size)); + vec2 mixval = fract(uv*size); + mixval = 0.5*(1.0-cos(3.1415928*mixval)); + rv += coef * mix(mix(f0, f1, mixval.x), mix(f2, f3, mixval.x), mixval.y); + acc += coef; + size *= 2.0; + coef *= persistence; } - - return va/wt; + + return rv / acc; } -float perlin_old(vec2 uv, vec2 scale, int iterations, float turbulence) { - float f = 0.0; - float c = 1.0; - float m = 0.0; - for(int i = 0; i < iterations; i++) { - vec2 uv2 = scale * mod(uv, vec2(1.0, 1.0)); - vec2 uv2_floor = floor(uv2); - vec2 uv2_fract = fract(uv2); - f += c * ( (1.0 - uv2_fract.x) * ((1.0 - uv2_fract.y) * hash1(uv2_floor) + uv2_fract.y * hash1(uv2_floor+vec2(0.0, 1.0))) - + uv2_fract.x * ((1.0 - uv2_fract.y) * hash1(uv2_floor+vec2(1.0, 0.0)) + uv2_fract.y * hash1(uv2_floor+vec2(1.0, 1.0)))); - m += c; - scale *= 2.0; - c *= turbulence; - } - return f/m; +vec2 voronoi(vec2 uv, vec2 size, float intensity) { + uv *= size; + float rv = 10.0; + vec2 point; + vec2 p0 = floor(uv); + for (int dx = -1; dx < 2; ++dx) { + for (int dy = -1; dy < 2; ++dy) { + vec2 p = p0+vec2(float(dx), float(dy)); + p += rand2(mod(p, size)); + float l = length((uv - p) / size); + l *= l; + if (rv > l) { + rv = l; + point = p; + } + } + } + + return vec2(rand(point), rv*length(size)*intensity); } -float noise(vec2 p, vec2 freq ){ - vec2 unit = vec2(1.0)/freq; - vec2 ij = floor(p/unit); - vec2 xy = mod(p,unit)/unit; - float a = hash1((ij+vec2(0.0,0.0))); - float b = hash1((ij+vec2(1.0,0.0))); - float c = hash1((ij+vec2(0.0,1.0))); - float d = hash1((ij+vec2(1.0,1.0))); - return mix(mix(a, b, xy.x), mix(c, d, xy.x), xy.y); -} -float perlin(vec2 p, vec2 scale, int iterations, float persistance) { - float n = 0.0; - float normK = 0.0; - vec2 f = scale; - float amp = 1.0; - for (int i = 0; i distance) { + best_distance1 = best_distance0; + best_distance0 = distance; + point1 = point0; + point0 = p; + } else if (best_distance1 > distance) { + best_distance1 = distance; + point1 = p; + } + } + } + float edge_distance = dot(uv - 0.5*(point0+point1), normalize(point0-point1)); + + return vec4(point0, best_distance0*length(size)*intensity, edge_distance); } diff --git a/addons/procedural_material/widgets/gradient_editor.gd b/addons/procedural_material/widgets/gradient_editor.gd index 2b459cd..e8cd353 100644 --- a/addons/procedural_material/widgets/gradient_editor.gd +++ b/addons/procedural_material/widgets/gradient_editor.gd @@ -1,3 +1,4 @@ +tool extends Control class GradientCursor: @@ -29,6 +30,8 @@ class GradientCursor: return true return false +signal updated + func _ready(): $Gradient.material = $Gradient.material.duplicate(true) add_cursor(0, Color(0, 0, 0)) @@ -81,13 +84,13 @@ func get_color(x): # get_color_in_shader func gcis(color): - return "vec3(%.1f,%.1f,%.1f)" % [color.r, color.g, color.b] + return "vec3(%.9f,%.9f,%.9f)" % [color.r, color.g, color.b] func get_shader(name): var array = get_sorted_cursors() var shader shader = "vec3 "+name+"(float x) {\n" - shader += " if (x < %.1f) {\n" % array[0].get_position() + shader += " if (x < %.9f) {\n" % array[0].get_position() shader += " return "+gcis(array[0].color)+";\n" for i in range(array.size()-1): var p0 = array[i].get_position() @@ -95,7 +98,7 @@ func get_shader(name): var p1mp0 = array[i+1].get_position()-p0 var c1mc0 = array[i+1].color-c0 if p1mp0 > 0: - shader += " } else if (x < %.1f) {\n" % array[i+1].get_position() + shader += " } else if (x < %.9f) {\n" % array[i+1].get_position() shader += " return %s+x*%s;\n" % [gcis(c0-c1mc0*(p0/p1mp0)), gcis(c1mc0/p1mp0)] shader += " }\n" shader += " return "+gcis(array[array.size()-1].color)+";\n" @@ -106,8 +109,9 @@ func update_shader(): var shader shader = "shader_type canvas_item;\n" shader += get_shader("gradient") - shader += "void fragment() { COLOR = vec4(gradient((UV.x-%.1f)*%.1f), 1.0); }" % [ 0.5*GradientCursor.WIDTH/rect_size.x, (rect_size.x-GradientCursor.WIDTH)/rect_size.x ] + shader += "void fragment() { COLOR = vec4(gradient((UV.x-%.9f)*%.9f), 1.0); }" % [ float(GradientCursor.WIDTH)*0.5/float(rect_size.x), rect_size.x/(rect_size.x-GradientCursor.WIDTH) ] $Gradient.material.shader.set_code(shader) + emit_signal("updated") func serialize(): var rv = [] @@ -117,6 +121,7 @@ func serialize(): func deserialize(v): for c in get_sorted_cursors(): - queue_free(c) + remove_child(c) + c.free() for i in v: add_cursor(i.pos*(rect_size.x-GradientCursor.WIDTH), Color(i.r, i.g, i.b)) diff --git a/addons/procedural_material/widgets/gradient_editor.tscn b/addons/procedural_material/widgets/gradient_editor.tscn index 25f4777..44ab747 100644 --- a/addons/procedural_material/widgets/gradient_editor.tscn +++ b/addons/procedural_material/widgets/gradient_editor.tscn @@ -33,7 +33,7 @@ margin_left = 24.0 margin_top = 14.0 margin_right = 144.0 margin_bottom = 44.0 -rect_min_size = Vector2( 100, 30 ) +rect_min_size = Vector2( 120, 30 ) rect_pivot_offset = Vector2( 0, 0 ) rect_clip_content = false mouse_filter = 0 @@ -89,8 +89,8 @@ anchor_right = 0.0 anchor_bottom = 0.0 margin_left = 4.0 margin_top = 4.0 -margin_right = 302.0 -margin_bottom = 462.0 +margin_right = 4.0 +margin_bottom = 4.0 rect_scale = Vector2( 0.75, 0.75 ) rect_pivot_offset = Vector2( 0, 0 ) rect_clip_content = false diff --git a/addons/procedural_material/widgets/gradient_editor_cursor.gd b/addons/procedural_material/widgets/gradient_editor_cursor.gd deleted file mode 100644 index 0c160c1..0000000 --- a/addons/procedural_material/widgets/gradient_editor_cursor.gd +++ /dev/null @@ -1,14 +0,0 @@ -tool -extends ColorRect - -func _ready(): - pass - -func _on_gui_input(ev): - if ev is InputEventMouseButton && ev.button_index == 1 && ev.doubleclick: - var dialog = ColorPicker.new() - add_child(dialog) - elif ev is InputEventMouseMotion && (ev.button_mask & 1) != 0: - rect_position.x += ev.relative.x - rect_position.x = min(max(0, rect_position.x), get_parent().rect_size.x-rect_size.x) - diff --git a/examples/bricks.ptex b/examples/bricks.ptex index 0f26248..df62c2d 100644 --- a/examples/bricks.ptex +++ b/examples/bricks.ptex @@ -1 +1 @@ -{"connections":[{"from":"Perlin","from_port":0,"to":"Colorize2","to_port":0},{"from":"Perlin","from_port":0,"to":"Colorize","to_port":0},{"from":"Warp","from_port":0,"to":"Blend","to_port":2},{"from":"Colorize","from_port":0,"to":"Blend","to_port":0},{"from":"Colorize2","from_port":0,"to":"Blend","to_port":1},{"from":"Perlin2","from_port":0,"to":"Warp","to_port":1},{"from":"Blend","from_port":0,"to":"Material","to_port":0},{"from":"Warp","from_port":0,"to":"NormalMap","to_port":0},{"from":"Bricks","from_port":0,"to":"Warp","to_port":0},{"from":"NormalMap","from_port":0,"to":"Material","to_port":1}],"nodes":[{"color0":{"a":1,"b":0,"g":0,"r":1,"type":"Color"},"color1":{"a":1,"b":0.061523,"g":0.461907,"r":0.875,"type":"Color"},"name":"Colorize2","node_position":{"x":120.75943,"y":-319},"type":"colorize"},{"color0":{"a":1,"b":0,"g":0,"r":0,"type":"Color"},"color1":{"a":1,"b":1,"g":1,"r":1,"type":"Color"},"name":"Colorize","node_position":{"x":116.759399,"y":-431},"type":"colorize"},{"amount":0.2,"name":"Warp","node_position":{"x":124,"y":-199},"type":"warp"},{"amount":0.5,"name":"NormalMap","node_position":{"x":310.416809,"y":-124.375488},"type":"normal_map"},{"name":"Material","node_position":{"x":528,"y":-285},"type":"material"},{"iterations":7,"name":"Perlin","node_position":{"x":-149,"y":-455},"persistance":1,"scale_x":8,"scale_y":9,"type":"perlin"},{"iterations":6,"name":"Perlin2","node_position":{"x":-141.24057,"y":-86.125458},"persistance":0.4,"scale_x":4,"scale_y":4,"type":"perlin"},{"bevel":0.1,"columns":3,"mortar":0.1,"name":"Bricks","node_position":{"x":-147.327362,"y":-292.797089},"row_offset":0.5,"rows":6,"type":"bricks"},{"amount":0.5,"name":"Blend","node_position":{"x":326.759399,"y":-306},"type":"blend"}]} \ No newline at end of file +{"connections":[{"from":"Bricks","from_port":0,"to":"Warp","to_port":0},{"from":"Perlin","from_port":0,"to":"Warp","to_port":1},{"from":"Perlin2","from_port":0,"to":"Colorize2","to_port":0},{"from":"Perlin2","from_port":0,"to":"Colorize","to_port":0},{"from":"Colorize","from_port":0,"to":"Blend","to_port":0},{"from":"Colorize2","from_port":0,"to":"Blend","to_port":1},{"from":"Warp","from_port":0,"to":"Colorize3","to_port":0},{"from":"Colorize3","from_port":0,"to":"Blend","to_port":2},{"from":"Blend","from_port":0,"to":"Material","to_port":0},{"from":"Warp","from_port":0,"to":"NormalMap","to_port":0},{"from":"NormalMap","from_port":0,"to":"Material","to_port":1}],"nodes":[{"iterations":6,"name":"Perlin","node_position":{"x":254,"y":317},"persistance":0.35,"scale_x":4,"scale_y":4,"type":"perlin"},{"iterations":7,"name":"Perlin2","node_position":{"x":258,"y":-32},"persistance":0.9,"scale_x":8,"scale_y":8,"type":"perlin"},{"name":"Material","node_position":{"x":981,"y":-13},"type":"material"},{"amount":0.4,"name":"Blend","node_position":{"x":803,"y":-23.75},"type":"blend"},{"bevel":0.6,"columns":3,"mortar":0.05,"name":"Bricks","node_position":{"x":260,"y":132},"row_offset":0.5,"rows":6,"type":"bricks"},{"amount":0.5,"name":"Warp","node_position":{"x":504,"y":199.75},"type":"warp"},{"amount":0.5,"name":"NormalMap","node_position":{"x":820,"y":269},"type":"normal_map"},{"gradient":[{"b":0.001994,"g":0.150751,"pos":0,"r":0.382813},{"b":0.001994,"g":0.067447,"pos":1,"r":0.382813}],"name":"Colorize2","node_position":{"x":540,"y":46.25},"type":"colorize"},{"gradient":[{"b":0.299479,"g":0.299479,"pos":0,"r":0.299479},{"b":0.127604,"g":0.127604,"pos":1,"r":0.127604}],"name":"Colorize","node_position":{"x":540,"y":-18.75},"type":"colorize"},{"gradient":[{"b":0,"g":0,"pos":0,"r":0},{"b":1,"g":1,"pos":0.072727,"r":1}],"name":"Colorize3","node_position":{"x":649,"y":136},"type":"colorize"}]} \ No newline at end of file