tool extends Container var popup_position = Vector2(0, 0) const MENU = [ { command="load_texture", description="Load texture" }, { command="save_texture", description="Save texture" }, { name="sine", description="Sine" }, { name="bricks", description="Bricks" }, { name="iqnoise", description="IQ Noise" }, { name="perlin", description="Perlin noise" }, { name="transform", description="Transform" }, { name="colorize", description="Colorize" }, { name="blend", description="Blend" } ] func _ready(): $GraphEdit.add_valid_connection_type(0, 0) $GraphEdit/PopupMenu.clear() for i in MENU.size(): $GraphEdit/PopupMenu.add_item(MENU[i].description, i) func _on_GraphEdit_popup_request(position): popup_position = position $GraphEdit/PopupMenu.popup(Rect2(position, $GraphEdit/PopupMenu.rect_size)) func _on_PopupMenu_id_pressed(id): var node_type = null if MENU[id].has("command"): call(MENU[id].command) elif MENU[id].has("name"): node_type = load("res://addons/procedural_material/"+MENU[id].name+".tscn") if node_type != null: var node = node_type.instance() $GraphEdit.add_child(node) node.offset = ($GraphEdit.scroll_offset + popup_position - $GraphEdit.rect_global_position) / $GraphEdit.zoom func _on_GraphEdit_connection_request(from, from_slot, to, to_slot): var disconnect = $GraphEdit.get_source(to, to_slot) if disconnect != null: $GraphEdit.disconnect_node(disconnect.node, disconnect.slot, to, to_slot) $GraphEdit.connect_node(from, from_slot, to, to_slot) generate_shader(); func load_texture(): var dialog = EditorFileDialog.new() add_child(dialog) dialog.access = EditorFileDialog.ACCESS_FILESYSTEM dialog.mode = EditorFileDialog.MODE_OPEN_FILE dialog.add_filter("*.ptex;Procedural texture file") dialog.connect("file_selected", self, "load_file") dialog.popup_centered() func load_file(filename): var file = File.new() if file.open(filename, File.READ) != OK: return var data = file.get_var() file.close() $GraphEdit.clear_connections() for c in $GraphEdit.get_children(): if c is GraphNode: $GraphEdit.remove_child(c) c.free() for n in data.nodes: var node_type = load("res://addons/procedural_material/"+n.type+".tscn") if node_type != null: var node = node_type.instance() node.name = n.name $GraphEdit.add_child(node) node.deserialize(n) for c in data.connections: $GraphEdit.connect_node(c.from, c.from_port, c.to, c.to_port) generate_shader() func save_texture(): var dialog = EditorFileDialog.new() add_child(dialog) dialog.access = EditorFileDialog.ACCESS_FILESYSTEM dialog.mode = EditorFileDialog.MODE_SAVE_FILE dialog.add_filter("*.ptex;Procedural texture file") dialog.connect("file_selected", self, "save_file") dialog.popup_centered() func save_file(filename): var data = { nodes = [] } for c in $GraphEdit.get_children(): if c is GraphNode: data.nodes.append(c.serialize()) data.connections = $GraphEdit.get_connection_list() var file = File.new() if file.open(filename, File.WRITE) == OK: file.store_var(data) file.close() func generate_shader(): var code = "" var file = File.new() file.open("res://addons/procedural_material/shader_header.txt", File.READ) while !file.eof_reached(): code += file.get_line() code += "\n" for c in $GraphEdit.get_children(): if c is GraphNode: c.generated = false c.generated_variants = [] var shader_code = $GraphEdit/Material.get_shader_code("UV") print("GENERATED SHADER:\n"+shader_code.code) code += shader_code.code $Preview.material.shader.set_code(code)