diff --git a/addons/material_maker/engine/gen_base.gd b/addons/material_maker/engine/gen_base.gd index 936ef64..0ca3742 100644 --- a/addons/material_maker/engine/gen_base.gd +++ b/addons/material_maker/engine/gen_base.gd @@ -18,6 +18,7 @@ class OutputPort: return generator.name+"("+str(output_index)+")" var position : Vector2 = Vector2(0, 0) +var model = null var parameters = {} func _ready(): @@ -112,3 +113,18 @@ func get_shader_code(uv : String, output_index : int, context : MMGenContext): func _get_shader_code(uv : String, output_index : int, context : MMGenContext): return null + +func _serialize(data): + print("cannot save "+name) + return data + +func serialize(): + var rv = { name=name, parameters={}, node_position={ x=position.x, y=position.y } } + for p in parameters.keys(): + rv.parameters[p] = MMType.serialize_value(parameters[p]) + if model != null: + rv.type = model + else: + rv = _serialize(rv) + + return rv \ No newline at end of file diff --git a/addons/material_maker/engine/gen_buffer.gd b/addons/material_maker/engine/gen_buffer.gd index 92efdfa..4861a78 100644 --- a/addons/material_maker/engine/gen_buffer.gd +++ b/addons/material_maker/engine/gen_buffer.gd @@ -38,3 +38,7 @@ func _get_shader_code(uv : String, output_index : int, context : MMGenContext): while rv is GDScriptFunctionState: rv = yield(rv, "completed") return rv + +func _serialize(data): + data.type = "buffer" + return data diff --git a/addons/material_maker/engine/gen_graph.gd b/addons/material_maker/engine/gen_graph.gd index 988094f..6aa2844 100644 --- a/addons/material_maker/engine/gen_graph.gd +++ b/addons/material_maker/engine/gen_graph.gd @@ -21,6 +21,14 @@ func remove_generator(generator : MMGenBase): if c.from != generator.name and c.to != generator.name: new_connections.append(c) connections = new_connections + generator.queue_free() + +func replace_generator(old : MMGenBase, new : MMGenBase): + new.name = old.name + new.position = old.position + remove_child(old) + old.free() + add_child(new) func connect_children(from, from_port : int, to, to_port : int): # disconnect target @@ -47,4 +55,11 @@ func disconnect_children(from, from_port : int, to, to_port : int): if remove == -1: break connections.remove(remove) - return true \ No newline at end of file + return true + +func _serialize(data): + data.nodes = [] + for c in get_children(): + data.nodes.append(c.serialize()) + data.connections = connections + return data diff --git a/addons/material_maker/engine/gen_material.gd b/addons/material_maker/engine/gen_material.gd index 963568e..fad8969 100644 --- a/addons/material_maker/engine/gen_material.gd +++ b/addons/material_maker/engine/gen_material.gd @@ -129,3 +129,7 @@ func export_textures(prefix, size = null): update_spatial_material(new_material, prefix) ResourceSaver.save("%s.tres" % [ prefix ], new_material) resource_filesystem.scan() + +func _serialize(data): + data.type = "material" + return data diff --git a/addons/material_maker/engine/gen_shader.gd b/addons/material_maker/engine/gen_shader.gd index 822d033..82136fa 100644 --- a/addons/material_maker/engine/gen_shader.gd +++ b/addons/material_maker/engine/gen_shader.gd @@ -120,7 +120,7 @@ func subst(string, context, uv = ""): elif p.type == "color": value_string = "vec4(%.9f, %.9f, %.9f, %.9f)" % [ value.r, value.g, value.b, value.a ] elif p.type == "gradient": - value_string = p.name+"_gradient_fct" + value_string = name+"__"+p.name+"_gradient_fct" if value_string != null: string = replace_variable(string, p.name, value_string) if shader_model.has("inputs") and typeof(shader_model.inputs) == TYPE_ARRAY: @@ -157,7 +157,7 @@ func _get_shader_code(uv : String, output_index : int, context : MMGenContext): if !(g is MMGradient): g = MMGradient.new() g.deserialize(parameters[p.name]) - rv.defs += g.get_shader(p.name+"_gradient_fct") + rv.defs += g.get_shader(name+"__"+p.name+"_gradient_fct") var variant_index = context.get_variant(self, variant_string) if variant_index == -1: variant_index = context.get_variant(self, variant_string) @@ -181,3 +181,7 @@ func get_globals(): if typeof(shader_model) == TYPE_DICTIONARY and shader_model.has("global") and list.find(shader_model.global) == -1: list.append(shader_model.global) return list + +func _serialize(data): + data.shader_model = shader_model + return data diff --git a/addons/material_maker/engine/loader.gd b/addons/material_maker/engine/loader.gd index a4253dd..baf9cef 100644 --- a/addons/material_maker/engine/loader.gd +++ b/addons/material_maker/engine/loader.gd @@ -2,14 +2,14 @@ tool extends Object class_name MMGenLoader -func load_gen(filename: String) -> MMGenBase: +static func load_gen(filename: String) -> MMGenBase: var file = File.new() if file.open(filename, File.READ) == OK: var data = parse_json(file.get_as_text()) return create_gen(data) return null -func add_to_gen_graph(gen_graph, generators, connections): +static func add_to_gen_graph(gen_graph, generators, connections): var rv = { generators=[], connections=[] } for n in generators: var g = create_gen(n) @@ -27,7 +27,7 @@ func add_to_gen_graph(gen_graph, generators, connections): rv.connections.append(c) return rv -func create_gen(data) -> MMGenBase: +static func create_gen(data) -> MMGenBase: var generator = null if data.has("connections") and data.has("nodes"): generator = MMGenGraph.new() @@ -35,6 +35,9 @@ func create_gen(data) -> MMGenBase: elif data.has("shader_model"): generator = MMGenShader.new() generator.set_shader_model(data.shader_model) + elif data.has("model_data"): + generator = MMGenShader.new() + generator.set_shader_model(data.model_data) elif data.has("type"): if data.type == "material": generator = MMGenMaterial.new() @@ -44,13 +47,13 @@ func create_gen(data) -> MMGenBase: generator = MMGenImage.new() else: var file = File.new() - if file.open("res://addons/material_maker/library/"+data.type+".mml", File.READ) == OK: - print("loaded description "+data.type+".mml") + if file.open("res://addons/material_maker/nodes/"+data.type+".mmg", File.READ) == OK: generator = create_gen(parse_json(file.get_as_text())) + generator.model = data.type file.close() elif file.open("res://addons/material_maker/nodes/"+data.type+".mmn", File.READ) == OK: generator = MMGenShader.new() - print("loaded description "+data.type+".mmn") + generator.model = data.type generator.set_shader_model(parse_json(file.get_as_text())) file.close() else: @@ -58,7 +61,7 @@ func create_gen(data) -> MMGenBase: if generator != null: generator.name = data.type else: - print(data) + print("LOADER: data not supported:"+str(data)) if generator != null: if data.has("name"): generator.name = data.name @@ -67,5 +70,5 @@ func create_gen(data) -> MMGenBase: generator.position.y = data.node_position.y if data.has("parameters"): for p in data.parameters.keys(): - generator.parameters[p] = data.parameters[p] + generator.parameters[p] = MMType.deserialize_value(data.parameters[p]) return generator diff --git a/addons/material_maker/graph_edit.gd b/addons/material_maker/graph_edit.gd index 2b5fca7..d500632 100644 --- a/addons/material_maker/graph_edit.gd +++ b/addons/material_maker/graph_edit.gd @@ -134,16 +134,14 @@ func create_nodes(data, position : Vector2 = Vector2(0, 0)): if data.has("type"): data = { nodes=[data], connections=[] } if typeof(data.nodes) == TYPE_ARRAY and typeof(data.connections) == TYPE_ARRAY: - var loader = MMGenLoader.new() - var new_stuff = loader.add_to_gen_graph(generator, data.nodes, data.connections) + var new_stuff = MMGenLoader.add_to_gen_graph(generator, data.nodes, data.connections) for g in new_stuff.generators: g.position += position update_graph(new_stuff.generators, new_stuff.connections) func load_file(filename): clear_material() - var loader = MMGenLoader.new() - generator = loader.load_gen(filename) + generator = MMGenLoader.load_gen(filename) if generator != null: add_child(generator) update_graph(generator.get_children(), generator.connections) @@ -152,11 +150,7 @@ func load_file(filename): center_view() func save_file(filename): - var data = { nodes = [] } - for c in get_children(): - if c is GraphNode: - data.nodes.append(c.serialize()) - data.connections = get_connection_list() + var data = generator.serialize() var file = File.new() if file.open(filename, File.WRITE) == OK: file.store_string(to_json(data)) @@ -191,7 +185,7 @@ func serialize_selection(): center += n.offset+0.5*n.rect_size center /= nodes.size() for n in nodes: - var s = n.serialize() + var s = n.generator.serialize() var p = n.offset-center s.node_position = { x=p.x, y=p.y } data.nodes.append(s) diff --git a/addons/material_maker/library/base.json b/addons/material_maker/library/base.json index 7e16e24..08bd7b8 100644 --- a/addons/material_maker/library/base.json +++ b/addons/material_maker/library/base.json @@ -254,7 +254,8 @@ }, { "tree_item":"Miscellaneous/Custom", - "type":"custom" + "type":"custom", + "shader_model":{} }, { "tree_item":"Miscellaneous/Remote", diff --git a/addons/material_maker/main_window.gd b/addons/material_maker/main_window.gd index 138c227..125aea8 100644 --- a/addons/material_maker/main_window.gd +++ b/addons/material_maker/main_window.gd @@ -25,6 +25,7 @@ const MENU = [ { menu="Edit", command="edit_cut", shortcut="Control+X", description="Cut" }, { menu="Edit", command="edit_copy", shortcut="Control+C", description="Copy" }, { menu="Edit", command="edit_paste", shortcut="Control+V", description="Paste" }, + { menu="Tools", command="make_selected_nodes_editable", description="Make selected nodes editable" }, { menu="Tools", command="add_to_user_library", description="Add selected node to user library" }, { menu="Tools", command="save_user_library", description="Save user library" }, { menu="Help", command="show_doc", description="User manual" }, @@ -233,19 +234,33 @@ func edit_paste_is_disabled(): var data = parse_json(OS.clipboard) return data == null -func add_to_user_library(): +func get_selected_nodes(): var graph_edit = $VBoxContainer/HBoxContainer/Projects.get_current_tab_control() if graph_edit != null and graph_edit is GraphEdit: var selected_nodes = [] for n in graph_edit.get_children(): if n is GraphNode and n.selected: selected_nodes.append(n) - if !selected_nodes.empty(): - var dialog = preload("res://addons/material_maker/widgets/line_dialog.tscn").instance() - dialog.set_texts("New library element", "Select a name for the new library element") - add_child(dialog) - dialog.connect("ok", self, "do_add_to_user_library", [ selected_nodes ]) - dialog.popup_centered() + return selected_nodes + else: + return [] + +func make_selected_nodes_editable(): + var selected_nodes = get_selected_nodes() + if !selected_nodes.empty(): + for n in selected_nodes: + print(n.name) + n.generator.model = null + n.update_node() + +func add_to_user_library(): + var selected_nodes = get_selected_nodes() + if !selected_nodes.empty(): + var dialog = preload("res://addons/material_maker/widgets/line_dialog.tscn").instance() + dialog.set_texts("New library element", "Select a name for the new library element") + add_child(dialog) + dialog.connect("ok", self, "do_add_to_user_library", [ selected_nodes ]) + dialog.popup_centered() func do_add_to_user_library(name, nodes): var data diff --git a/addons/material_maker/nodes/combine.mmg b/addons/material_maker/nodes/combine.mmg new file mode 100644 index 0000000..f263c0c --- /dev/null +++ b/addons/material_maker/nodes/combine.mmg @@ -0,0 +1 @@ +{"name":"uniform","node_position":{"x":-130,"y":-66},"parameters":{"color":{"a":1,"b":1,"g":1,"r":1,"type":"Color"},"name":0},"shader_model":{"global":"","inputs":[{"default":"0.0","label":"R","name":"r","type":"f"},{"default":"0.0","label":"G","name":"g","type":"f"},{"default":"0.0","label":"B","name":"b","type":"f"},{"default":"1.0","label":"A","name":"a","type":"f"}],"instance":"","name":"Combine","outputs":[{"rgba":"vec4($r($uv), $g($uv), $b($uv), $a($uv))"}],"parameters":[]}} \ No newline at end of file diff --git a/addons/material_maker/nodes/edit_buttons.gd b/addons/material_maker/nodes/edit_buttons.gd new file mode 100644 index 0000000..c62bbd5 --- /dev/null +++ b/addons/material_maker/nodes/edit_buttons.gd @@ -0,0 +1,6 @@ +extends HBoxContainer + +func connect_buttons(object, edit_fct, load_fct, save_fct): + $Edit.connect("pressed", object, edit_fct) + $Load.connect("pressed", object, load_fct) + $Save.connect("pressed", object, save_fct) \ No newline at end of file diff --git a/addons/material_maker/nodes/edit_buttons.tscn b/addons/material_maker/nodes/edit_buttons.tscn new file mode 100644 index 0000000..ce6a0e7 --- /dev/null +++ b/addons/material_maker/nodes/edit_buttons.tscn @@ -0,0 +1,31 @@ +[gd_scene load_steps=5 format=2] + +[ext_resource path="res://addons/material_maker/nodes/edit_buttons.gd" type="Script" id=1] +[ext_resource path="res://addons/material_maker/icons/edit.png" type="Texture" id=2] +[ext_resource path="res://addons/material_maker/icons/load.png" type="Texture" id=3] +[ext_resource path="res://addons/material_maker/icons/save.png" type="Texture" id=4] + +[node name="NodeEditButtons" type="HBoxContainer"] +margin_right = 91.0 +margin_bottom = 22.0 +script = ExtResource( 1 ) + +[node name="Edit" type="Button" parent="."] +margin_right = 27.0 +margin_bottom = 22.0 +icon = ExtResource( 2 ) +flat = true + +[node name="Load" type="Button" parent="."] +margin_left = 31.0 +margin_right = 59.0 +margin_bottom = 22.0 +icon = ExtResource( 3 ) +flat = true + +[node name="Save" type="Button" parent="."] +margin_left = 63.0 +margin_right = 91.0 +margin_bottom = 22.0 +icon = ExtResource( 4 ) +flat = true diff --git a/addons/material_maker/nodes/generic.gd b/addons/material_maker/nodes/generic.gd index cb8bfb8..ad312ba 100644 --- a/addons/material_maker/nodes/generic.gd +++ b/addons/material_maker/nodes/generic.gd @@ -12,6 +12,13 @@ func set_generator(g): generator = g update_node() +func on_close_request(): + print("close") + generator.get_parent().remove_generator(generator) + +func on_offset_changed(): + generator.position = offset + func initialize_properties(): for o in controls: if o == null: @@ -73,6 +80,7 @@ func update_node(): c.queue_free() else: custom_node_buttons = c + rect_size = Vector2(0, 0) # Rebuild node title = generator.get_type_name() # Parameters @@ -133,7 +141,6 @@ func update_node(): var inputs = generator.get_input_defs() for i in range(inputs.size()): var input = inputs[i] - print(input) var enable_left = false var color_left = Color(0.5, 0.5, 0.5) if typeof(input) == TYPE_DICTIONARY: @@ -146,11 +153,14 @@ func update_node(): else: enable_left = true set_slot(i, enable_left, 0, color_left, false, 0, Color()) + if i >= get_child_count(): + var control = Control.new() + control.rect_min_size = Vector2(0, 16) + add_child(control) # Outputs var outputs = generator.get_output_defs() for i in range(outputs.size()): var output = outputs[i] - print(output) var enable_right = false var color_right = Color(0.5, 0.5, 0.5) if typeof(output) == TYPE_DICTIONARY: @@ -163,5 +173,69 @@ func update_node(): elif output.has("f"): enable_right = true set_slot(i, is_slot_enabled_left(i), get_slot_type_left(i), get_slot_color_left(i), enable_right, 0, color_right) - if custom_node_buttons != null: - move_child(custom_node_buttons, get_child_count()-1) + if i >= get_child_count(): + var control = Control.new() + control.rect_min_size = Vector2(0, 16) + add_child(control) + if generator.model == null: + var edit_buttons = preload("res://addons/material_maker/nodes/edit_buttons.tscn").instance() + add_child(edit_buttons) + edit_buttons.connect_buttons(self, "edit_generator", "load_generator", "save_generator") + +func edit_generator(): + var edit_window = load("res://addons/material_maker/widgets/node_editor/node_editor.tscn").instance() + get_parent().add_child(edit_window) + if generator.shader_model != null: + edit_window.set_model_data(generator.shader_model) + edit_window.connect("node_changed", self, "update_generator") + edit_window.popup_centered() + +func update_generator(shader_model): + generator.shader_model = shader_model + update_node() + +func load_generator(): + var dialog = FileDialog.new() + add_child(dialog) + dialog.rect_min_size = Vector2(500, 500) + dialog.access = FileDialog.ACCESS_FILESYSTEM + dialog.mode = FileDialog.MODE_OPEN_FILE + dialog.add_filter("*.mmg,*.mmn;Material Maker Generator") + dialog.connect("file_selected", self, "do_load_generator") + dialog.popup_centered() + +func do_load_generator(file_name : String): + print(file_name) + var new_generator = null + if file_name.ends_with(".mmn"): + var file = File.new() + if file.open(file_name, File.READ) == OK: + new_generator = MMGenShader.new() + new_generator.set_shader_model(parse_json(file.get_as_text())) + file.close() + else: + new_generator = MMGenLoader.load_gen(file_name) + if new_generator != null: + var parent_generator = generator.get_parent() + print("before: "+generator.name) + parent_generator.replace_generator(generator, new_generator) + generator = new_generator + print("after: "+generator.name) + update_node() + +func save_generator(): + var dialog = FileDialog.new() + add_child(dialog) + dialog.rect_min_size = Vector2(500, 500) + dialog.access = FileDialog.ACCESS_FILESYSTEM + dialog.mode = FileDialog.MODE_SAVE_FILE + dialog.add_filter("*.mmg;Material Maker Generator") + dialog.connect("file_selected", self, "do_save_generator") + dialog.popup_centered() + +func do_save_generator(file_name : String): + var data = generator.serialize() + var file = File.new() + if file.open(file_name, File.WRITE) == OK: + file.store_string(to_json(data)) + file.close() diff --git a/addons/material_maker/nodes/generic.tscn b/addons/material_maker/nodes/generic.tscn index 3bdc771..9bc13f2 100644 --- a/addons/material_maker/nodes/generic.tscn +++ b/addons/material_maker/nodes/generic.tscn @@ -3,8 +3,10 @@ [ext_resource path="res://addons/material_maker/nodes/generic.gd" type="Script" id=1] [node name="Generic" type="GraphNode"] -margin_right = 82.0 +margin_right = 95.0 margin_bottom = 29.0 title = "Generic" show_close = true script = ExtResource( 1 ) +[connection signal="close_request" from="." to="." method="on_close_request"] +[connection signal="offset_changed" from="." to="." method="on_offset_changed"] diff --git a/addons/material_maker/plugin.gd b/addons/material_maker/plugin.gd index 467e3ed..bcb1b25 100644 --- a/addons/material_maker/plugin.gd +++ b/addons/material_maker/plugin.gd @@ -47,8 +47,7 @@ func close_material_maker(): material_maker = null func generate_material(ptex_filename: String) -> Material: - var loader = MMGenLoader.new() - var generator = loader.load_gen(ptex_filename) + var generator = MMGenLoader.load_gen(ptex_filename) add_child(generator) var material = generator.get_node("Material") var return_value = material.generate_material(renderer) diff --git a/addons/material_maker/types/gradient.gd b/addons/material_maker/types/gradient.gd index f776898..c56a6d4 100644 --- a/addons/material_maker/types/gradient.gd +++ b/addons/material_maker/types/gradient.gd @@ -36,7 +36,7 @@ func sort(): func get_color(x): sort() - if points.size() >0: + if points.size() > 0: if x < points[0].v: return points[0].c var s = points.size()-1 @@ -96,3 +96,7 @@ func deserialize(v): for i in v.points: if !i.has("a"): i.a = 1.0 add_point(i.pos, Color(i.r, i.g, i.b, i.a)) + elif typeof(v) == TYPE_OBJECT and v.get_script() == get_script(): + clear() + for p in v.points: + add_point(p.v, p.c) diff --git a/addons/material_maker/types/types.gd b/addons/material_maker/types/types.gd index a8d135f..f20fcd6 100644 --- a/addons/material_maker/types/types.gd +++ b/addons/material_maker/types/types.gd @@ -5,8 +5,8 @@ const Gradient = preload("res://addons/material_maker/types/gradient.gd") static func serialize_value(value): if typeof(value) == TYPE_COLOR: - return { type= "Color", r=value.r, g=value.g, b=value.b, a=value.a } - elif typeof(value) == TYPE_OBJECT && value.has_method("serialize"): + return { type="Color", r=value.r, g=value.g, b=value.b, a=value.a } + elif typeof(value) == TYPE_OBJECT and value.has_method("serialize"): return value.serialize() return value