diff --git a/addons/material_maker/engine/loader.gd b/addons/material_maker/engine/loader.gd index 9998247..9418058 100644 --- a/addons/material_maker/engine/loader.gd +++ b/addons/material_maker/engine/loader.gd @@ -2,6 +2,8 @@ tool extends Object class_name MMGenLoader +const STD_GENDEF_PATH = "res://addons/material_maker/nodes" + static func load_gen(filename: String) -> MMGenBase: var file = File.new() if file.open(filename, File.READ) == OK: @@ -28,6 +30,20 @@ static func add_to_gen_graph(gen_graph, generators, connections): return rv static func create_gen(data) -> MMGenBase: + var guess = [ + { keyword="connections", type=MMGenGraph }, + { keyword="nodes", type=MMGenGraph }, + { keyword="shader_model", type=MMGenShader }, + { keyword="model_data", type=MMGenShader }, + { keyword="convolution_params", type=MMGenConvolution }, + { keyword="widgets", type=MMGenRemote } + ] + var types = { + material = MMGenMaterial, + buffer = MMGenBuffer, + image = MMGenImage, + switch = MMGenSwitch + } var generator = null if data.has("connections") and data.has("nodes"): generator = MMGenGraph.new() @@ -60,21 +76,20 @@ static func create_gen(data) -> MMGenBase: generator = MMGenSwitch.new() else: var file = File.new() - var gen_path = OS.get_executable_path().get_base_dir()+"/generators" - if file.open(gen_path+"/"+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+".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() - generator.model = data.type - generator.set_shader_model(parse_json(file.get_as_text())) - file.close() - else: + var gen_paths = [ STD_GENDEF_PATH, OS.get_executable_path().get_base_dir()+"/generators" ] + for p in gen_paths: + if file.open(p+"/"+data.type+".mmg", File.READ) == OK: + generator = create_gen(parse_json(file.get_as_text())) + generator.model = data.type + file.close() + break + elif file.open(p+"/"+data.type+".mmn", File.READ) == OK: + generator = MMGenShader.new() + generator.model = data.type + generator.set_shader_model(parse_json(file.get_as_text())) + file.close() + break + if generator == null: print("Cannot find description for "+data.type) if generator != null: generator.name = data.type @@ -90,3 +105,20 @@ static func create_gen(data) -> MMGenBase: for p in data.parameters.keys(): generator.parameters[p] = MMType.deserialize_value(data.parameters[p]) return generator + +static func get_generator_list() -> Array: + var rv = [] + var dir : Directory = Directory.new() + for p in [ STD_GENDEF_PATH, OS.get_executable_path().get_base_dir()+"/generators" ]: + dir.open(p) + dir.list_dir_begin(true) + while true: + var f = dir.get_next() + if f == "": + break + if f.right(f.length()-4) == ".mmg": + var n = f.left(f.length()-4) + if rv.find(n) == -1: + rv.push_back(n) + rv.sort() + return rv diff --git a/addons/material_maker/engine/renderer.gd b/addons/material_maker/engine/renderer.gd index 0ddb537..7d2439c 100644 --- a/addons/material_maker/engine/renderer.gd +++ b/addons/material_maker/engine/renderer.gd @@ -95,11 +95,17 @@ func render_shader(shader, textures, render_size): return self func copy_to_texture(t : ImageTexture): - get_texture().get_data().lock() + var image : Image = get_texture().get_data() + image.lock() t.create_from_image(get_texture().get_data()) - get_texture().get_data().unlock() + image.unlock() + +func save_to_file(fn : String): + var image : Image = get_texture().get_data() + image.lock() + image.save_png(fn) + image.unlock() func release(): rendering = false emit_signal("done") - diff --git a/addons/material_maker/fonts/hack.ttf b/addons/material_maker/fonts/hack.ttf new file mode 100644 index 0000000..92a90cb Binary files /dev/null and b/addons/material_maker/fonts/hack.ttf differ diff --git a/addons/material_maker/graph_edit.gd b/addons/material_maker/graph_edit.gd index 854665f..edcad99 100644 --- a/addons/material_maker/graph_edit.gd +++ b/addons/material_maker/graph_edit.gd @@ -1,5 +1,6 @@ tool extends GraphEdit +class_name MMGraphEdit var editor_interface = null var node_factory = null @@ -167,6 +168,9 @@ func create_nodes(data, position : Vector2 = Vector2(0, 0)): return update_graph(new_stuff.generators, new_stuff.connections) return [] +func create_gen_from_type(gen_name): + create_nodes({ type=gen_name }, scroll_offset+0.5*rect_size) + func load_file(filename): clear_material() top_generator = MMGenLoader.load_gen(filename) diff --git a/addons/material_maker/main_window.gd b/addons/material_maker/main_window.gd index eee325b..0c17ca5 100644 --- a/addons/material_maker/main_window.gd +++ b/addons/material_maker/main_window.gd @@ -2,13 +2,13 @@ tool extends Panel var recent_files = [] -var recent_files_submenu = null var editor_interface = null var current_tab = null onready var renderer = $Renderer onready var projects = $VBoxContainer/HBoxContainer/ProjectsPane/Projects +onready var library = $VBoxContainer/HBoxContainer/VBoxContainer/Library const MENU = [ { menu="File", command="new_material", description="New material" }, @@ -26,8 +26,10 @@ 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", submenu="create", description="Create" }, { menu="Tools", command="create_subgraph", shortcut="Control+G", description="Create group" }, { menu="Tools", command="make_selected_nodes_editable", shortcut="Control+F", description="Make selected nodes editable" }, + { menu="Tools" }, { 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" }, @@ -48,7 +50,7 @@ func _ready(): m.connect("about_to_show", self, "menu_about_to_show", [ m.name, menu ]) new_material() -func get_current_graph_edit(): +func get_current_graph_edit() -> MMGraphEdit: var graph_edit = projects.get_current_tab_control() if graph_edit != null and graph_edit is GraphEdit: return graph_edit @@ -66,7 +68,7 @@ func create_menu(menu, menu_name): var submenu = PopupMenu.new() var submenu_function = "create_menu_"+MENU[i].submenu if has_method(submenu_function): - call(submenu_function, submenu) + submenu.connect("about_to_show", self, submenu_function, [ submenu ]); else: create_menu(submenu, MENU[i].submenu) menu.add_child(submenu) @@ -88,16 +90,15 @@ func create_menu(menu, menu_name): menu.add_separator() return menu -func update_recents_menu(): - if recent_files_submenu != null: - recent_files_submenu.clear() - for i in recent_files.size(): - recent_files_submenu.add_item(recent_files[i], i) - func create_menu_load_recent(menu): - menu.connect("id_pressed", self, "_on_LoadRecent_id_pressed") - recent_files_submenu = menu - update_recents_menu() + menu.clear() + for i in recent_files.size(): + menu.add_item(recent_files[i], i) + if !menu.is_connected("id_pressed", self, "_on_LoadRecent_id_pressed"): + menu.connect("id_pressed", self, "_on_LoadRecent_id_pressed") + +func _on_LoadRecent_id_pressed(id): + do_load_material(recent_files[id]) func load_recents(): var f = File.new() @@ -113,12 +114,25 @@ func add_recent(path): else: break recent_files.push_front(path) - update_recents_menu() var f = File.new() f.open("user://recent_files.bin", File.WRITE) f.store_string(to_json(recent_files)) f.close() +func create_menu_create(menu): + var gens = MMGenLoader.get_generator_list() + menu.clear() + for i in gens.size(): + menu.add_item(gens[i], i) + if !menu.is_connected("id_pressed", self, "_on_Create_id_pressed"): + menu.connect("id_pressed", self, "_on_Create_id_pressed") + +func _on_Create_id_pressed(id): + var graph_edit : MMGraphEdit = get_current_graph_edit() + if graph_edit != null: + var gens = MMGenLoader.get_generator_list() + graph_edit.create_gen_from_type(gens[id]) + func menu_about_to_show(name, menu): for i in MENU.size(): if MENU[i].menu != name: @@ -160,7 +174,7 @@ func do_load_materials(filenames): do_load_material(f) func do_load_material(filename): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() var node_count = 2 # So test below succeeds if graph_edit is null... if graph_edit != null: node_count = 0 @@ -175,7 +189,7 @@ func do_load_material(filename): add_recent(filename) func save_material(): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() if graph_edit != null: if graph_edit.save_path != null: graph_edit.save_file(graph_edit.save_path) @@ -183,7 +197,7 @@ func save_material(): save_material_as() func save_material_as(): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() if graph_edit != null: var dialog = FileDialog.new() add_child(dialog) @@ -198,12 +212,12 @@ func close_material(): projects.close_tab() func export_material(): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() if graph_edit != null : graph_edit.export_textures() func export_material_is_disabled(): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() if graph_edit == null or graph_edit.save_path == null: return true return false @@ -216,16 +230,16 @@ func quit(): func edit_cut(): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() if graph_edit != null: graph_edit.cut() func edit_cut_is_disabled(): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() return graph_edit == null or !graph_edit.can_copy() func edit_copy(): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() if graph_edit != null: graph_edit.copy() @@ -233,7 +247,7 @@ func edit_copy_is_disabled(): return edit_cut_is_disabled() func edit_paste(): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() if graph_edit != null: graph_edit.paste() @@ -242,14 +256,14 @@ func edit_paste_is_disabled(): return data == null func get_selected_nodes(): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() if graph_edit != null: return graph_edit.get_selected_nodes() else: return [] func create_subgraph(): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() if graph_edit != null: graph_edit.create_subgraph() @@ -270,10 +284,10 @@ func add_to_user_library(): dialog.popup_centered() func do_add_to_user_library(name, nodes): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() var data if nodes.size() == 1: - data = nodes[0].serialize() + data = nodes[0].generator.serialize() data.erase("node_position") elif graph_edit != null: data = graph_edit.serialize_selection() @@ -282,12 +296,16 @@ func do_add_to_user_library(name, nodes): dir.make_dir("user://library/user") data.library = "user://library/user.json" data.icon = name.right(name.rfind("/")+1).to_lower() - $VBoxContainer/HBoxContainer/VBoxContainer/Library.add_item(data, name) - graph_edit.export_texture(nodes[0], "user://library/user/"+data.icon+".png", 64) + library.add_item(data, name) + var result = nodes[0].generator.render(0, renderer, 64) + while result is GDScriptFunctionState: + result = yield(result, "completed") + result.save_to_file("user://library/user/"+data.icon+".png") + result.release() func save_user_library(): print("Saving user library") - $VBoxContainer/HBoxContainer/VBoxContainer/Library.save_library("user://library/user.json") + library.save_library("user://library/user.json") func show_doc(): var doc_path = OS.get_executable_path() @@ -312,9 +330,6 @@ func _on_PopupMenu_id_pressed(id): if has_method(command): call(command) -func _on_LoadRecent_id_pressed(id): - do_load_material(recent_files[id]) - # Preview func update_preview(): @@ -322,7 +337,7 @@ func update_preview(): update_preview_3d() func update_preview_2d(node = null): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() if graph_edit != null: var preview = $VBoxContainer/HBoxContainer/VBoxContainer/Preview if node == null: @@ -340,7 +355,7 @@ func update_preview_2d(node = null): preview.set_2d(tex) func update_preview_3d(): - var graph_edit = get_current_graph_edit() + var graph_edit : MMGraphEdit = get_current_graph_edit() if graph_edit != null and graph_edit.top_generator != null and graph_edit.top_generator.has_node("Material"): var gen_material = graph_edit.top_generator.get_node("Material") var status = gen_material.render_textures(renderer) diff --git a/addons/material_maker/main_window.tscn b/addons/material_maker/main_window.tscn index efbf5da..f2e496a 100644 --- a/addons/material_maker/main_window.tscn +++ b/addons/material_maker/main_window.tscn @@ -42,14 +42,14 @@ margin_left = 79.0 margin_right = 125.0 margin_bottom = 20.0 text = "Tools" -items = [ "Create group", null, 0, false, false, 15, 268435527, null, "", false, "Make selected nodes editable", null, 0, false, false, 16, 268435526, null, "", false, "Add selected node to user library", null, 0, false, false, 17, 0, null, "", false, "Save user library", null, 0, false, false, 18, 0, null, "", false ] +items = [ "Create", null, 0, false, false, -1, 0, null, "PopupMenu", false, "Create group", null, 0, false, false, 16, 268435527, null, "", false, "Make selected nodes editable", null, 0, false, false, 17, 268435526, null, "", false, "", null, 0, false, false, -1, 0, null, "", true, "Add selected node to user library", null, 0, false, false, 19, 0, null, "", false, "Save user library", null, 0, false, false, 20, 0, null, "", false ] [node name="Help" type="MenuButton" parent="VBoxContainer/Menu"] margin_left = 129.0 margin_right = 171.0 margin_bottom = 20.0 text = "Help" -items = [ "User manual", null, 0, false, false, 19, 0, null, "", false, "Report a bug", null, 0, false, false, 20, 0, null, "", false, "", null, 0, false, false, -1, 0, null, "", true, "About", null, 0, false, false, 22, 0, null, "", false ] +items = [ "User manual", null, 0, false, false, 21, 0, null, "", false, "Report a bug", null, 0, false, false, 22, 0, null, "", false, "", null, 0, false, false, -1, 0, null, "", true, "About", null, 0, false, false, 24, 0, null, "", false ] [node name="HBoxContainer" type="HSplitContainer" parent="VBoxContainer"] margin_top = 24.0 diff --git a/addons/material_maker/nodes/generic.gd b/addons/material_maker/nodes/generic.gd index 4ea439d..6ed5ae3 100644 --- a/addons/material_maker/nodes/generic.gd +++ b/addons/material_maker/nodes/generic.gd @@ -142,7 +142,8 @@ func create_parameter_control(p : Dictionary): func update_node(): # Clean node var custom_node_buttons = null - remove_child(preview) + if preview != null: + remove_child(preview) if preview_timer != null: preview_timer.stop() remove_child(preview_timer) @@ -319,10 +320,13 @@ func do_load_generator(file_name : String): else: new_generator = MMGenLoader.load_gen(file_name) if new_generator != null: + var gen_name = MMGenLoader.generator_from_path(file_name) + if gen_name != "": + new_generator.type = gen_name var parent_generator = generator.get_parent() parent_generator.replace_generator(generator, new_generator) generator = new_generator - update_node() + call_deferred("update_node") func save_generator(): var dialog = FileDialog.new() diff --git a/addons/material_maker/nodes/weave.mmg b/addons/material_maker/nodes/weave.mmg index aab2d1d..35fcbf8 100644 --- a/addons/material_maker/nodes/weave.mmg +++ b/addons/material_maker/nodes/weave.mmg @@ -1 +1 @@ -{"name":"weave","node_position":{"x":0,"y":0},"parameters":{"columns":4,"rows":4},"shader_model":{"global":"float weave(vec2 uv) {\n\tvec2 v = sin(3.1415892*vec2(uv.x+floor(uv.y), 1.0+uv.y+floor(uv.x)));\n\treturn max(v.x, v.y)*0.5+0.5;\n}","inputs":[],"instance":"","name":"Weave","outputs":[{"f":"weave(vec2($columns, $rows)*$uv)","type":"f"}],"parameters":[{"default":0,"label":"","max":32,"min":2,"name":"rows","step":1,"type":"float","widget":"spinbox"},{"default":0,"label":"","max":32,"min":2,"name":"columns","step":1,"type":"float","widget":"spinbox"}]},"type":"shader"} \ No newline at end of file +{"name":"weave","node_position":{"x":0,"y":0},"parameters":{"columns":8,"rows":8,"width":0.85},"shader_model":{"global":"float weave(vec2 uv, vec2 count, float width) {\n uv *= count;\n\tfloat c = (sin(3.1415926*(uv.x+floor(uv.y)))*0.5+0.5)*step(abs(fract(uv.y)-0.5), width*0.5);\n\tc = max(c, (sin(3.1415926*(1.0+uv.y+floor(uv.x)))*0.5+0.5)*step(abs(fract(uv.x)-0.5), width*0.5));\n\treturn c;\n}","inputs":[],"instance":"","name":"Weave","outputs":[{"f":"weave($uv, vec2($columns, $rows), $width)","type":"f"}],"parameters":[{"default":4,"label":"","max":32,"min":2,"name":"columns","step":1,"type":"float","widget":"spinbox"},{"default":4,"label":"","max":32,"min":2,"name":"rows","step":1,"type":"float","widget":"spinbox"},{"default":0.8,"label":"","max":1,"min":0,"name":"width","step":0.05,"type":"float"}]},"type":"shader"} \ No newline at end of file diff --git a/addons/material_maker/widgets/node_editor/node_editor.gd b/addons/material_maker/widgets/node_editor/node_editor.gd index 54c167a..01866c3 100644 --- a/addons/material_maker/widgets/node_editor/node_editor.gd +++ b/addons/material_maker/widgets/node_editor/node_editor.gd @@ -3,12 +3,18 @@ extends WindowDialog var model_data = null +onready var global_editor : TextEdit = $Sizer/Tabs/Global + const ParameterEditor = preload("res://addons/material_maker/widgets/node_editor/parameter.tscn") const InputEditor = preload("res://addons/material_maker/widgets/node_editor/input.tscn") const OutputEditor = preload("res://addons/material_maker/widgets/node_editor/output.tscn") signal node_changed +func _ready(): + global_editor.add_color_region("//", "", Color(0, 0.5, 0), true) + + func add_item(parent, scene): var object = scene.instance() parent.add_child(object) diff --git a/addons/material_maker/widgets/node_editor/node_editor.tscn b/addons/material_maker/widgets/node_editor/node_editor.tscn index 6f59874..bda376b 100644 --- a/addons/material_maker/widgets/node_editor/node_editor.tscn +++ b/addons/material_maker/widgets/node_editor/node_editor.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=4 format=2] +[gd_scene load_steps=6 format=2] [ext_resource path="res://addons/material_maker/widgets/node_editor/node_editor.gd" type="Script" id=1] [ext_resource path="res://addons/material_maker/icons/icons.svg" type="Texture" id=2] @@ -8,6 +8,13 @@ flags = 4 atlas = ExtResource( 2 ) region = Rect2( 48, 0, 16, 16 ) +[sub_resource type="DynamicFontData" id=2] +font_path = "res://addons/material_maker/fonts/hack.ttf" + +[sub_resource type="DynamicFont" id=3] +size = 12 +font_data = SubResource( 2 ) + [node name="NodeEditor" type="WindowDialog"] margin_right = 833.0 margin_bottom = 398.0 @@ -148,6 +155,10 @@ margin_top = 32.0 margin_right = -4.0 margin_bottom = -4.0 mouse_default_cursor_shape = 0 +custom_fonts/font = SubResource( 3 ) +custom_colors/brace_mismatch_color = Color( 1, 0, 0, 1 ) +syntax_highlighting = true +show_line_numbers = true [node name="Instance" type="TextEdit" parent="Sizer/Tabs"] visible = false @@ -158,6 +169,8 @@ margin_top = 32.0 margin_right = -4.0 margin_bottom = -4.0 mouse_default_cursor_shape = 0 +syntax_highlighting = true +show_line_numbers = true [node name="HBoxContainer" type="HBoxContainer" parent="Sizer"] margin_left = 307.0 diff --git a/addons/material_maker/widgets/node_editor/output.tscn b/addons/material_maker/widgets/node_editor/output.tscn index d6ce248..09137e1 100644 --- a/addons/material_maker/widgets/node_editor/output.tscn +++ b/addons/material_maker/widgets/node_editor/output.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=4 format=2] +[gd_scene load_steps=6 format=2] [ext_resource path="res://addons/material_maker/widgets/node_editor/output.gd" type="Script" id=1] [ext_resource path="res://addons/material_maker/icons/icons.svg" type="Texture" id=2] @@ -8,6 +8,13 @@ flags = 4 atlas = ExtResource( 2 ) region = Rect2( 0, 16, 16, 16 ) +[sub_resource type="DynamicFontData" id=2] +font_path = "res://addons/material_maker/fonts/hack.ttf" + +[sub_resource type="DynamicFont" id=3] +size = 12 +font_data = SubResource( 2 ) + [node name="Output" type="HBoxContainer"] margin_right = 201.0 margin_bottom = 24.0 @@ -30,11 +37,14 @@ text = "GreyScale" items = [ "GreyScale", null, false, 0, null, "Color", null, false, 1, null, "RGBA", null, false, -1, null ] selected = 0 -[node name="Value" type="LineEdit" parent="."] +[node name="Value" type="TextEdit" parent="."] margin_left = 140.0 margin_right = 201.0 margin_bottom = 24.0 hint_tooltip = "Default value" size_flags_horizontal = 3 +custom_fonts/font = SubResource( 3 ) text = "0.0" +syntax_highlighting = true +context_menu_enabled = false [connection signal="pressed" from="Delete" to="." method="_on_Delete_pressed"]