diff --git a/addons/material_maker/engine/gen_base.gd b/addons/material_maker/engine/gen_base.gd index e046681..eb0e861 100644 --- a/addons/material_maker/engine/gen_base.gd +++ b/addons/material_maker/engine/gen_base.gd @@ -67,7 +67,7 @@ func notify_output_change(output_index : int): for target in targets: target.generator.source_changed(target.input_index) -func source_changed(input_index : int): +func source_changed(__): for i in range(get_output_defs().size()): notify_output_change(i) @@ -97,12 +97,12 @@ func render(output_index : int, renderer : MMGenRenderer, size : int): while source is GDScriptFunctionState: source = yield(source, "completed") if source == null: - return false + source = { defs="", code="", textures={}, rgba="vec4(0.0)" } var shader : String = renderer.generate_shader(source) - var status = renderer.render_shader(shader, source.textures, size) - while status is GDScriptFunctionState: - status = yield(status, "completed") - return status + var result = renderer.render_shader(shader, source.textures, size) + while result is GDScriptFunctionState: + result = yield(result, "completed") + return result func get_shader_code(uv : String, output_index : int, context : MMGenContext): var rv = _get_shader_code(uv, output_index, context) @@ -125,7 +125,7 @@ func get_shader_code(uv : String, output_index : int, context : MMGenContext): rv.rgba = "vec4("+rv.rgb+", 1.0)" return rv -func _get_shader_code(uv : String, output_index : int, context : MMGenContext): +func _get_shader_code(__, __, __): return null func _serialize(data): diff --git a/addons/material_maker/engine/gen_buffer.gd b/addons/material_maker/engine/gen_buffer.gd index 9b677c5..217aefc 100644 --- a/addons/material_maker/engine/gen_buffer.gd +++ b/addons/material_maker/engine/gen_buffer.gd @@ -35,14 +35,13 @@ func source_changed(input_port_index : int): func _get_shader_code(uv : String, output_index : int, context : MMGenContext): var source = get_source(0) if source != null and !updated: - var status = source.generator.render(source.output_index, context.renderer, pow(2, 4+parameters.size)) - while status is GDScriptFunctionState: - status = yield(status, "completed") - if status: - var image : Image = context.renderer.get_texture().get_data() - texture.create_from_image(image) - texture.flags = 0 - updated = true + var result = source.generator.render(source.output_index, context.renderer, pow(2, 4+parameters.size)) + while result is GDScriptFunctionState: + result = yield(result, "completed") + result.copy_to_texture(texture) + result.release() + texture.flags = 0 + updated = true var rv = ._get_shader_code(uv, output_index, context) while rv is GDScriptFunctionState: rv = yield(rv, "completed") diff --git a/addons/material_maker/engine/gen_graph.gd b/addons/material_maker/engine/gen_graph.gd index 2ec4c89..6862c8b 100644 --- a/addons/material_maker/engine/gen_graph.gd +++ b/addons/material_maker/engine/gen_graph.gd @@ -129,10 +129,28 @@ func _serialize(data): func edit(node): node.get_parent().call_deferred("update_view", self) -func create_subgraph(generators): +func create_subgraph(gens): + # Remove material, gen_inputs and gen_outputs nodes + var generators = [] + var center = Vector2(0, 0) + var left_bound = 65535 + var right_bound = -65536 + var count = 0 + for g in gens: + if g.name != "Material" and g.name != "gen_inputs" and g.name != "gen_outputs": + generators.push_back(g) + var p = g.position + center += p + count += 1 + if left_bound > p.x: left_bound = p.x + if right_bound < p.x: right_bound = p.x + if count == 0: + return + center /= count var new_graph = get_script().new() new_graph.name = "graph" - add_child(new_graph) + add_generator(new_graph) + new_graph.position = center var names : Array = [] for g in generators: names.push_back(g.name) @@ -140,27 +158,39 @@ func create_subgraph(generators): new_graph.add_generator(g) var new_graph_connections = [] var my_new_connections = [] - var inputs = null - var outputs = null + var gen_inputs = null + var gen_outputs = null + var inputs = [] + var outputs = [] for c in connections: + var src_name = c.from+"."+str(c.from_port) if names.find(c.from) != -1 and names.find(c.to) != -1: new_graph_connections.push_back(c) elif names.find(c.from) != -1: - if outputs == null: - outputs = MMGenIOs.new() - outputs.name = "gen_outputs" - new_graph.add_generator(outputs) - var port_index = outputs.ports.size() - outputs.ports.push_back( { name="port"+str(port_index), type="rgba" } ) + var port_index = outputs.find(src_name) + if port_index == -1: + port_index = outputs.size() + outputs.push_back(src_name) + if gen_outputs == null: + gen_outputs = MMGenIOs.new() + gen_outputs.name = "gen_outputs" + gen_outputs.position = Vector2(right_bound+300, center.y) + new_graph.add_generator(gen_outputs) + gen_outputs.ports.push_back( { name="port"+str(port_index), type="rgba" } ) + print(gen_outputs.ports) my_new_connections.push_back( { from=new_graph.name, from_port=port_index, to=c.to, to_port=c.to_port } ) new_graph_connections.push_back( { from=c.from, from_port=c.from_port, to="gen_outputs", to_port=port_index } ) elif names.find(c.to) != -1: - if inputs == null: - inputs = MMGenIOs.new() - inputs.name = "gen_inputs" - new_graph.add_generator(inputs) - var port_index = inputs.ports.size() - inputs.ports.push_back( { name="port"+str(port_index), type="rgba" } ) + var port_index = inputs.find(src_name) + if port_index == -1: + port_index = inputs.size() + inputs.push_back(src_name) + if gen_inputs == null: + gen_inputs = MMGenIOs.new() + gen_inputs.name = "gen_inputs" + gen_inputs.position = Vector2(left_bound-300, center.y) + new_graph.add_generator(gen_inputs) + gen_inputs.ports.push_back( { name="port"+str(port_index), type="rgba" } ) my_new_connections.push_back( { from=c.from, from_port=c.from_port, to=new_graph.name, to_port=port_index } ) new_graph_connections.push_back( { from="gen_inputs", from_port=port_index, to=c.to, to_port=c.to_port } ) else: diff --git a/addons/material_maker/engine/gen_ios.gd b/addons/material_maker/engine/gen_ios.gd index 1ce1d8c..e4bc433 100644 --- a/addons/material_maker/engine/gen_ios.gd +++ b/addons/material_maker/engine/gen_ios.gd @@ -6,24 +6,21 @@ class_name MMGenIOs IOs just forward their inputs to their outputs and are used to specify graph interfaces """ -var mask : int = 3 var ports : Array = [] func get_type(): - return "buffer" + return "ios" func get_type_name(): match name: "gen_inputs": return "Inputs" "gen_outputs": return "Outputs" _: return "IOs" - return "Buffer" func get_io_defs(): var rv : Array = [] - if mask != 2: - for p in ports: - rv.push_back({ name=p.name, type="rgba" }) + for p in ports: + rv.push_back({ name=p.name, type="rgba" }) return rv func get_input_defs(): @@ -39,17 +36,15 @@ func source_changed(input_index : int): notify_output_change(input_index) func _get_shader_code(uv : String, output_index : int, context : MMGenContext): - if mask != 2: - var source = get_source(output_index) - if source != null: - var rv = source.generator._get_shader_code(uv, source.output_index, context) - while rv is GDScriptFunctionState: - rv = yield(rv, "completed") - return rv + var source = get_source(output_index) + if source != null: + var rv = source.generator._get_shader_code(uv, source.output_index, context) + while rv is GDScriptFunctionState: + rv = yield(rv, "completed") + return rv return { defs="", code="", textures={} } func _serialize(data): data.type = "ios" - data.mask = mask data.ports = ports return data diff --git a/addons/material_maker/engine/gen_material.gd b/addons/material_maker/engine/gen_material.gd index 2fe6e2c..63c76d0 100644 --- a/addons/material_maker/engine/gen_material.gd +++ b/addons/material_maker/engine/gen_material.gd @@ -69,11 +69,12 @@ func render_textures(renderer : MMGenRenderer): if t.has("port"): var source = get_source(t.port) if source != null: - var status = source.generator.render(source.output_index, renderer, 1024) - while status is GDScriptFunctionState: - status = yield(status, "completed") + var result = source.generator.render(source.output_index, renderer, 1024) + while result is GDScriptFunctionState: + result = yield(result, "completed") texture = ImageTexture.new() - texture.create_from_image(renderer.get_texture().get_data()) + result.copy_to_texture(texture) + result.release() elif t.has("ports"): var context : MMGenContext = MMGenContext.new(renderer) var code = [] @@ -90,11 +91,12 @@ func render_textures(renderer : MMGenRenderer): else: code.push_back({ defs="", code="", f=t.default_values[i] }) var shader : String = renderer.generate_combined_shader(code[0], code[1], code[2]) - var status = renderer.render_shader(shader, shader_textures, 1024) - while status is GDScriptFunctionState: - status = yield(status, "completed") + var result = renderer.render_shader(shader, shader_textures, 1024) + while result is GDScriptFunctionState: + result = yield(result, "completed") texture = ImageTexture.new() - texture.create_from_image(renderer.get_texture().get_data()) + result.copy_to_texture(texture) + result.release() generated_textures[t.texture] = texture func update_materials(material_list): diff --git a/addons/material_maker/engine/loader.gd b/addons/material_maker/engine/loader.gd index c26bcd1..9998247 100644 --- a/addons/material_maker/engine/loader.gd +++ b/addons/material_maker/engine/loader.gd @@ -55,8 +55,6 @@ static func create_gen(data) -> MMGenBase: generator = MMGenImage.new() elif data.type == "ios": generator = MMGenIOs.new() - if data.has("mask"): - generator.mask = int(data.mask) generator.ports = data.ports elif data.type == "switch": generator = MMGenSwitch.new() diff --git a/addons/material_maker/engine/renderer.gd b/addons/material_maker/engine/renderer.gd index 6f7789f..0ddb537 100644 --- a/addons/material_maker/engine/renderer.gd +++ b/addons/material_maker/engine/renderer.gd @@ -92,7 +92,14 @@ func render_shader(shader, textures, render_size): update_worlds() yield(get_tree(), "idle_frame") yield(get_tree(), "idle_frame") + return self + +func copy_to_texture(t : ImageTexture): + get_texture().get_data().lock() + t.create_from_image(get_texture().get_data()) + get_texture().get_data().unlock() + +func release(): rendering = false emit_signal("done") - return true diff --git a/addons/material_maker/icons/icons.svg b/addons/material_maker/icons/icons.svg index 2a31081..a4a4505 100644 --- a/addons/material_maker/icons/icons.svg +++ b/addons/material_maker/icons/icons.svg @@ -49,8 +49,8 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="14.304427" - inkscape:cx="46.907045" - inkscape:cy="41.347265" + inkscape:cx="33.274902" + inkscape:cy="30.161917" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" @@ -67,8 +67,8 @@ + spacingx="16" + spacingy="16" /> @@ -265,5 +265,24 @@ id="rect863" style="opacity:0;fill:#ffed46;fill-opacity:1;stroke:#c7b115;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" /> + + + diff --git a/addons/material_maker/main_window.gd b/addons/material_maker/main_window.gd index a3afb78..eee325b 100644 --- a/addons/material_maker/main_window.gd +++ b/addons/material_maker/main_window.gd @@ -26,7 +26,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="create_subgraph", shortcut="Control+G", description="Create subgraph" }, + { 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", command="add_to_user_library", description="Add selected node to user library" }, { menu="Tools", command="save_user_library", description="Save user library" }, @@ -331,14 +331,13 @@ func update_preview_2d(node = null): node = n break if node != null: - var status = node.generator.render(0, renderer, 1024) - while status is GDScriptFunctionState: - status = yield(status, "completed") - if status: - var image = renderer.get_texture().get_data() - var tex = ImageTexture.new() - tex.create_from_image(image) - preview.set_2d(tex) + var result = node.generator.render(0, renderer, 1024) + while result is GDScriptFunctionState: + result = yield(result, "completed") + var tex = ImageTexture.new() + result.copy_to_texture(tex) + result.release() + preview.set_2d(tex) func update_preview_3d(): var graph_edit = get_current_graph_edit() diff --git a/addons/material_maker/node_factory.gd b/addons/material_maker/node_factory.gd index ac2f2c0..9d46086 100644 --- a/addons/material_maker/node_factory.gd +++ b/addons/material_maker/node_factory.gd @@ -8,9 +8,11 @@ func _ready(): func create_node(type): var node = null - var node_type = load("res://addons/material_maker/nodes/"+type+".tscn") - if node_type != null: - node = node_type.instance() + var file_name = "res://addons/material_maker/nodes/"+type+".tscn" + if ResourceLoader.exists(file_name): + var node_type = load(file_name) + if node_type != null: + node = node_type.instance() else: node = preload("res://addons/material_maker/nodes/generic.tscn").instance() return node diff --git a/addons/material_maker/nodes/generic.gd b/addons/material_maker/nodes/generic.gd index 606efb3..194982d 100644 --- a/addons/material_maker/nodes/generic.gd +++ b/addons/material_maker/nodes/generic.gd @@ -6,6 +6,13 @@ var generator = null setget set_generator var controls = {} var ignore_parameter_change = "" +var output_count = 0 + +var preview : TextureRect +var preview_index : int = -1 +var preview_position : int +var preview_size : int +var preview_timer : Timer func set_generator(g): generator = g @@ -71,6 +78,7 @@ func initialize_properties(): func update_shaders(): get_parent().send_changed_signal() + update_preview() func _on_text_changed(new_text, variable): ignore_parameter_change = variable @@ -158,9 +166,15 @@ func update_node(): set_slot(i, enable_left, 0, color_left, false, 0, Color()) var hsizer : HBoxContainer = HBoxContainer.new() hsizer.size_flags_horizontal = SIZE_EXPAND | SIZE_FILL - var label : Label = Label.new() - label.text = input.label if input.has("label") else "" - hsizer.add_child(label) + if input.has("label") and input.label != "": + var label : Label = Label.new() + label.text = input.label + hsizer.add_child(label) + else: + var control : Control = Control.new() + control.rect_min_size.y = 16 + hsizer.add_child(control) + add_child(hsizer) var input_names_width : int = 0 for c in get_children(): @@ -211,7 +225,9 @@ func update_node(): initialize_properties() # Outputs var outputs = generator.get_output_defs() - for i in range(outputs.size()): + var button_width = 0 + output_count = outputs.size() + for i in range(output_count): var output = outputs[i] var enable_right = true var color_right = Color(0.5, 0.5, 0.5) @@ -222,14 +238,49 @@ func update_node(): "rgb": color_right = Color(0.5, 0.5, 1.0) "rgba": color_right = Color(0.0, 0.5, 0.0, 0.5) set_slot(i, is_slot_enabled_left(i), get_slot_type_left(i), get_slot_color_left(i), enable_right, 0, color_right) - if i >= get_child_count(): - var control = Control.new() - control.rect_min_size = Vector2(0, 16) - add_child(control) + var hsizer : HBoxContainer + while i >= get_child_count(): + hsizer = HBoxContainer.new() + hsizer.size_flags_horizontal = SIZE_EXPAND | SIZE_FILL + add_child(hsizer) + hsizer = get_child(i) + var has_filler = false + for c in hsizer.get_children(): + if c.size_flags_horizontal & SIZE_EXPAND != 0: + has_filler = true + break + if !has_filler: + var empty_control : Control = Control.new() + empty_control.size_flags_horizontal = SIZE_EXPAND | SIZE_FILL + hsizer.add_child(empty_control) + var button = preload("res://addons/material_maker/widgets/preview_button.tscn").instance() + button.size_flags_horizontal = SIZE_SHRINK_END + button.size_flags_vertical = SIZE_SHRINK_CENTER + hsizer.add_child(button) + button.connect("toggled", self, "on_preview_button", [ i ]) + button_width = button.rect_size.x + if !outputs.empty(): + for i in range(output_count, get_child_count()): + var hsizer : HBoxContainer = get_child(i) + var empty_control : Control = Control.new() + empty_control.rect_min_size.x = button_width + hsizer.add_child(empty_control) + # Preview + if preview == null: + preview = TextureRect.new() + preview.visible = false + preview_position = get_child_count() + # Edit buttons 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") + # Preview timer + preview_timer = Timer.new() + preview_timer.one_shot = true + preview_timer.connect("timeout", self, "do_update_preview") + add_child(preview_timer) + func edit_generator(): if generator.has_method("edit"): @@ -284,3 +335,48 @@ func do_save_generator(file_name : String): data.node_position = { x=0, y=0 } file.store_string(to_json(data)) file.close() + +func on_preview_button(pressed : bool, index : int): + if pressed: + preview_index = index + var width + if preview.visible: + for i in range(output_count): + if i != index: + var line = get_child(i) + line.get_child(line.get_child_count()-1).pressed = false + update_preview() + else: + var status = update_preview(get_child(0).rect_size.x) + while status is GDScriptFunctionState: + status = yield(status, "completed") + else: + preview_index = -1 + preview.visible = false + remove_child(preview) + rect_size = Vector2(0, 0) + +func update_preview(size : int = 0): + if preview_index == -1: + return + if size != 0: + preview_size = size + preview_timer.start(0.2) + +func do_update_preview(): + var renderer = get_parent().renderer + var result = generator.render(preview_index, renderer, preview_size) + while result is GDScriptFunctionState: + result = yield(result, "completed") + if preview.texture == null: + preview.texture = ImageTexture.new() + result.copy_to_texture(preview.texture) + result.release() + if !preview.visible: + add_child(preview) + move_child(preview, preview_position) + preview.visible = true + + + + diff --git a/addons/material_maker/preview.tscn b/addons/material_maker/preview.tscn index 52e1d75..48f0e95 100644 --- a/addons/material_maker/preview.tscn +++ b/addons/material_maker/preview.tscn @@ -3,7 +3,7 @@ [ext_resource path="res://addons/material_maker/preview.gd" type="Script" id=1] [ext_resource path="res://addons/material_maker/preview_3d.tscn" type="PackedScene" id=2] -[sub_resource type="World" id=4] +[sub_resource type="World" id=1] [sub_resource type="Shader" id=2] code = "shader_type canvas_item; @@ -33,7 +33,7 @@ script = ExtResource( 1 ) [node name="MaterialPreview" type="Viewport" parent="."] size = Vector2( 395, 370 ) own_world = true -world = SubResource( 4 ) +world = SubResource( 1 ) handle_input_locally = false render_target_clear_mode = 1 render_target_update_mode = 3 @@ -50,7 +50,7 @@ margin_right = 100.0 margin_bottom = 20.0 rect_min_size = Vector2( 100, 0 ) text = "Cube" -items = [ "Cube", null, false, -1, null, "Cylinder", null, false, -1, null, "Sphere", null, false, -1, null, "Quad", null, false, -1, null, "Plane", null, false, -1, null ] +items = [ "Cube", null, false, -1, null, "Cylinder", null, false, -1, null, "Sphere", null, false, -1, null, "Sphere2", null, false, -1, null, "Quad", null, false, -1, null, "Plane", null, false, -1, null ] selected = 0 [node name="Environment" type="OptionButton" parent="Config"] diff --git a/addons/material_maker/preview_3d.tscn b/addons/material_maker/preview_3d.tscn index 7b06440..dd4d76d 100644 --- a/addons/material_maker/preview_3d.tscn +++ b/addons/material_maker/preview_3d.tscn @@ -14,7 +14,7 @@ background_sky = SubResource( 1 ) [sub_resource type="Animation" id=3] loop = true tracks/0/type = "value" -tracks/0/path = NodePath("MaterialPreview/Spatial/Objects:rotation_degrees") +tracks/0/path = NodePath("Objects:rotation_degrees") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/imported = false @@ -28,7 +28,7 @@ tracks/0/keys = { [node name="Preview3d" type="Spatial"] -[node name="Objects" type="Spatial" parent="." instance=ExtResource( 1 )] +[node name="Objects" parent="." instance=ExtResource( 1 )] transform = Transform( -0.799512, 0, 0.60065, 0, 1, 0, -0.60065, 0, -0.799512, 0, 0, 0 ) [node name="OmniLight" type="OmniLight" parent="."] diff --git a/addons/material_maker/widgets/preview_button.tscn b/addons/material_maker/widgets/preview_button.tscn new file mode 100644 index 0000000..52bfa9c --- /dev/null +++ b/addons/material_maker/widgets/preview_button.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://addons/material_maker/icons/icons.svg" type="Texture" id=1] + +[sub_resource type="AtlasTexture" id=1] +flags = 4 +atlas = ExtResource( 1 ) +region = Rect2( 16, 32, 16, 16 ) + +[sub_resource type="AtlasTexture" id=2] +flags = 4 +atlas = ExtResource( 1 ) +region = Rect2( 0, 32, 16, 16 ) + +[node name="PreviewButton" type="TextureButton"] +anchor_right = 1.0 +anchor_bottom = 1.0 +toggle_mode = true +shortcut_in_tooltip = false +texture_normal = SubResource( 1 ) +texture_pressed = SubResource( 2 )