From 2621ff4b46f30af70a2303261dc40d5c03658394 Mon Sep 17 00:00:00 2001 From: RodZill4 Date: Tue, 4 Sep 2018 21:45:14 +0200 Subject: [PATCH] Improved integration in Godot Exporting a material will now create a SpatialMaterial. The Material graph node now generates different textures when used as a Godot addon (metallic, roughness and ambient occlusion are merged into a single texture). Rendering code (that was in graph_edit and node_base scenes) is now in a single "renderer scene" attached to the main window. --- addons/material_maker/examples/wood.ptex | 2 +- addons/material_maker/graph_edit.gd | 60 +------- addons/material_maker/graph_edit.tscn | 76 +--------- addons/material_maker/main_window.gd | 10 +- addons/material_maker/main_window.tscn | 5 +- addons/material_maker/node_base.gd | 18 +-- addons/material_maker/nodes/blur.gd | 22 ++- addons/material_maker/nodes/emboss.gd | 6 +- addons/material_maker/nodes/export.gd | 2 +- addons/material_maker/nodes/material.gd | 170 +++++++++++++++------- addons/material_maker/nodes/normal_map.gd | 6 +- addons/material_maker/plugin.gd | 16 +- addons/material_maker/renderer.gd | 98 +++++++++++++ addons/material_maker/renderer.tscn | 72 +++++++++ test.ptex | 1 - 15 files changed, 338 insertions(+), 226 deletions(-) create mode 100644 addons/material_maker/renderer.gd create mode 100644 addons/material_maker/renderer.tscn delete mode 100644 test.ptex diff --git a/addons/material_maker/examples/wood.ptex b/addons/material_maker/examples/wood.ptex index 47ac618..d07a98f 100644 --- a/addons/material_maker/examples/wood.ptex +++ b/addons/material_maker/examples/wood.ptex @@ -1 +1 @@ -{"connections":[{"from":"perlin_0","from_port":0,"to":"warp_0","to_port":0},{"from":"perlin_1","from_port":0,"to":"warp_0","to_port":1},{"from":"perlin_2","from_port":0,"to":"blend_0","to_port":0},{"from":"blend_0","from_port":0,"to":"normal_map_0","to_port":0},{"from":"normal_map_0","from_port":0,"to":"Material","to_port":4},{"from":"blend_0","from_port":0,"to":"colorize_0","to_port":0},{"from":"colorize_0","from_port":0,"to":"Material","to_port":2},{"from":"blend_0","from_port":0,"to":"Material","to_port":1},{"from":"voronoi_0","from_port":0,"to":"colorize_1","to_port":0},{"from":"warp_0","from_port":0,"to":"warp_1","to_port":0},{"from":"colorize_1","from_port":0,"to":"warp_1","to_port":1},{"from":"warp_1","from_port":0,"to":"blend_0","to_port":1},{"from":"combine_0","from_port":0,"to":"export_0","to_port":0},{"from":"colorize_0","from_port":0,"to":"combine_0","to_port":1},{"from":"blend_0","from_port":0,"to":"colorize_2","to_port":0},{"from":"blend_0","from_port":0,"to":"combine_0","to_port":0},{"from":"colorize_2","from_port":0,"to":"Material","to_port":0}],"nodes":[{"amount":0.1,"name":"normal_map_0","node_position":{"x":319,"y":265.5},"type":"normal_map"},{"gradient":[{"b":0.53125,"g":0.53125,"pos":0,"r":0.53125},{"b":0.708333,"g":0.708333,"pos":1,"r":0.708333}],"name":"colorize_0","node_position":{"x":313,"y":176.5},"type":"colorize"},{"iterations":6,"name":"perlin_2","node_position":{"x":-312,"y":2.5},"persistence":1,"scale_x":32,"scale_y":4,"type":"perlin"},{"iterations":3,"name":"perlin_1","node_position":{"x":-400,"y":359.5},"persistence":0.5,"scale_x":4,"scale_y":4,"type":"perlin"},{"iterations":3,"name":"perlin_0","node_position":{"x":-393,"y":203.5},"persistence":0.5,"scale_x":32,"scale_y":4,"type":"perlin"},{"amount":0.3,"name":"warp_0","node_position":{"x":-180,"y":317.5},"type":"warp"},{"gradient":[{"b":0.432292,"g":0.432292,"pos":0,"r":0.432292},{"b":0,"g":0,"pos":0.345455,"r":0}],"name":"colorize_1","node_position":{"x":-194,"y":466.5},"type":"colorize"},{"amount":0.1,"name":"warp_1","node_position":{"x":-31,"y":336.5},"type":"warp"},{"intensity":1,"name":"voronoi_0","node_position":{"x":-381,"y":505.5},"scale_x":5,"scale_y":4,"type":"voronoi"},{"amount":1,"blend_type":2,"name":"blend_0","node_position":{"x":83,"y":245.5},"type":"blend"},{"name":"combine_0","node_position":{"x":515.35144,"y":-15.818176},"type":"combine"},{"name":"export_0","node_position":{"x":656.35144,"y":-20.818176},"suffix":"mr","type":"export"},{"name":"Material","node_position":{"x":544,"y":79},"type":"material"},{"gradient":[{"b":0.071126,"g":0.34877,"pos":0,"r":0.59375},{"b":0.013021,"g":0.144043,"pos":1,"r":0.3125}],"name":"colorize_2","node_position":{"x":305.35144,"y":76.181824},"type":"colorize"}]} \ No newline at end of file +{"connections":[{"from":"perlin_0","from_port":0,"to":"warp_0","to_port":0},{"from":"perlin_1","from_port":0,"to":"warp_0","to_port":1},{"from":"perlin_2","from_port":0,"to":"blend_0","to_port":0},{"from":"blend_0","from_port":0,"to":"normal_map_0","to_port":0},{"from":"normal_map_0","from_port":0,"to":"Material","to_port":4},{"from":"blend_0","from_port":0,"to":"colorize_0","to_port":0},{"from":"colorize_0","from_port":0,"to":"Material","to_port":2},{"from":"blend_0","from_port":0,"to":"Material","to_port":1},{"from":"voronoi_0","from_port":0,"to":"colorize_1","to_port":0},{"from":"warp_0","from_port":0,"to":"warp_1","to_port":0},{"from":"colorize_1","from_port":0,"to":"warp_1","to_port":1},{"from":"warp_1","from_port":0,"to":"blend_0","to_port":1},{"from":"combine_0","from_port":0,"to":"export_0","to_port":0},{"from":"colorize_0","from_port":0,"to":"combine_0","to_port":1},{"from":"blend_0","from_port":0,"to":"colorize_2","to_port":0},{"from":"blend_0","from_port":0,"to":"combine_0","to_port":0},{"from":"colorize_2","from_port":0,"to":"Material","to_port":0}],"nodes":[{"iterations":6,"name":"perlin_2","node_position":{"x":-312,"y":2.5},"persistence":1,"scale_x":32,"scale_y":4,"type":"perlin"},{"iterations":3,"name":"perlin_1","node_position":{"x":-400,"y":359.5},"persistence":0.5,"scale_x":4,"scale_y":4,"type":"perlin"},{"iterations":3,"name":"perlin_0","node_position":{"x":-393,"y":203.5},"persistence":0.5,"scale_x":32,"scale_y":4,"type":"perlin"},{"amount":0.3,"name":"warp_0","node_position":{"x":-180,"y":317.5},"type":"warp"},{"gradient":[{"b":0.432292,"g":0.432292,"pos":0,"r":0.432292},{"b":0,"g":0,"pos":0.345455,"r":0}],"name":"colorize_1","node_position":{"x":-194,"y":466.5},"type":"colorize"},{"amount":0.1,"name":"warp_1","node_position":{"x":-31,"y":336.5},"type":"warp"},{"intensity":1,"name":"voronoi_0","node_position":{"x":-381,"y":505.5},"scale_x":5,"scale_y":4,"type":"voronoi"},{"amount":1,"blend_type":2,"name":"blend_0","node_position":{"x":83,"y":245.5},"type":"blend"},{"name":"combine_0","node_position":{"x":515.35144,"y":-15.818176},"type":"combine"},{"name":"export_0","node_position":{"x":656.35144,"y":-20.818176},"resolution":1,"suffix":"mr","type":"export"},{"albedo_color":{"a":1,"b":1,"g":1,"r":1,"type":"Color"},"ao_light_affect":1,"depth_scale":1,"emission_energy":1,"metallic":1,"name":"Material","node_position":{"x":544,"y":79},"normal_scale":1,"resolution":1,"roughness":1,"type":"material"},{"gradient":[{"b":0.071126,"g":0.34877,"pos":0,"r":0.59375},{"b":0.013021,"g":0.144043,"pos":1,"r":0.3125}],"name":"colorize_2","node_position":{"x":305.35144,"y":76.181824},"type":"colorize"},{"amount":0.1,"name":"normal_map_0","node_position":{"x":319,"y":265.5},"size":5,"type":"normal_map"},{"gradient":[{"b":0.53125,"g":0.53125,"pos":0,"r":0.53125},{"b":0.708333,"g":0.708333,"pos":1,"r":0.708333}],"name":"colorize_0","node_position":{"x":313,"y":176.5},"type":"colorize"}]} \ No newline at end of file diff --git a/addons/material_maker/graph_edit.gd b/addons/material_maker/graph_edit.gd index 288a84e..24a1ab7 100644 --- a/addons/material_maker/graph_edit.gd +++ b/addons/material_maker/graph_edit.gd @@ -1,6 +1,9 @@ tool extends GraphEdit +var editor_interface = null +var renderer = null + var save_path = null var need_save = false @@ -8,7 +11,6 @@ signal save_path_changed signal graph_changed func _ready(): - $SaveViewport/ColorRect.material = $SaveViewport/ColorRect.material.duplicate(true) OS.low_processor_usage_mode = true center_view() @@ -284,59 +286,3 @@ func drop_data(position, data): Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) create_nodes(data, offset_from_global_position(get_global_transform().xform(position))) return true - -# Save shader to image, create image texture - -func setup_material(shader_material, textures, shader_code): - for k in textures.keys(): - shader_material.set_shader_param(k+"_tex", textures[k]) - shader_material.shader.code = shader_code - -var render_queue = [] - -func render_shader_to_viewport(shader, textures, size, method, args): - render_queue.append( { shader=shader, textures=textures, size=size, method=method, args=args } ) - if render_queue.size() == 1: - while !render_queue.empty(): - var job = render_queue.front() - $SaveViewport.size = Vector2(job.size, job.size) - $SaveViewport/ColorRect.rect_position = Vector2(0, 0) - $SaveViewport/ColorRect.rect_size = Vector2(job.size, job.size) - var shader_material = $SaveViewport/ColorRect.material - shader_material.shader.code = job.shader - if job.textures != null: - for k in job.textures.keys(): - shader_material.set_shader_param(k+"_tex", job.textures[k]) - $SaveViewport.render_target_update_mode = Viewport.UPDATE_ONCE - $SaveViewport.update_worlds() - yield(get_tree(), "idle_frame") - yield(get_tree(), "idle_frame") - callv(job.method, job.args) - render_queue.pop_front() - -func render_to_viewport(node, size, method, args): - render_shader_to_viewport(node.generate_shader(), node.get_textures(), size, method, args) - -func export_texture(node, filename, size = 256): - if node == null: - return null - render_to_viewport(node, size, "do_export_texture", [ filename ]) - -func do_export_texture(filename): - var viewport_texture = $SaveViewport.get_texture() - var viewport_image = viewport_texture.get_data() - viewport_image.save_png(filename) - -func precalculate_node(node, size, target_texture, object, method, args): - if node == null: - return null - render_to_viewport(node, size, "do_precalculate_texture", [ object, method, args ]) - -func precalculate_shader(shader, textures, size, target_texture, object, method, args): - render_shader_to_viewport(shader, textures, size, "do_precalculate_texture", [ target_texture, object, method, args ]) - -func do_precalculate_texture(target_texture, object, method, args): - var viewport_texture = $SaveViewport.get_texture() - target_texture.create_from_image(viewport_texture.get_data()) - object.callv(method, args) - diff --git a/addons/material_maker/graph_edit.tscn b/addons/material_maker/graph_edit.tscn index fc411e2..362c89e 100644 --- a/addons/material_maker/graph_edit.tscn +++ b/addons/material_maker/graph_edit.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=6 format=2] +[gd_scene load_steps=4 format=2] [ext_resource path="res://addons/material_maker/graph_edit.gd" type="Script" id=1] [ext_resource path="res://addons/material_maker/nodes/material.tscn" type="PackedScene" id=2] @@ -6,20 +6,6 @@ [sub_resource type="Theme" id=1] -[sub_resource type="Shader" id=2] - -code = "shader_type canvas_item; - -void fragment() { - COLOR = vec4(1.0); -} -" - -[sub_resource type="ShaderMaterial" id=3] - -render_priority = 0 -shader = SubResource( 2 ) - [node name="GraphEdit" type="GraphEdit" index="0"] self_modulate = Color( 1, 1, 1, 0 ) @@ -35,7 +21,7 @@ mouse_default_cursor_shape = 0 size_flags_horizontal = 1 size_flags_vertical = 1 right_disconnects = true -scroll_offset = Vector2( 0, 0 ) +scroll_offset = Vector2( -525, -250 ) snap_distance = 20 use_snap = false zoom = 1.0 @@ -44,8 +30,12 @@ _sections_unfolded = [ "Material", "Mouse", "Visibility" ] [node name="Material" parent="." index="0" instance=ExtResource( 2 )] +margin_left = 525.0 +margin_top = 250.0 +margin_right = 755.0 +margin_bottom = 470.0 theme = SubResource( 1 ) -_sections_unfolded = [ "Anchor", "Margin", "Mouse", "Theme", "slot", "slot/2", "slot/3", "slot/4", "slot/5" ] +_sections_unfolded = [ "Anchor", "Margin", "Mouse", "Theme", "slot", "slot/0", "slot/1", "slot/2", "slot/3", "slot/4", "slot/5" ] [node name="Timer" type="Timer" parent="." index="1"] @@ -54,58 +44,6 @@ wait_time = 0.2 one_shot = true autostart = false -[node name="SaveViewport" type="Viewport" parent="." index="2"] - -arvr = false -size = Vector2( 0, 0 ) -own_world = true -world = null -transparent_bg = false -msaa = 2 -hdr = false -disable_3d = false -usage = 2 -debug_draw = 0 -render_target_v_flip = true -render_target_clear_mode = 0 -render_target_update_mode = 1 -audio_listener_enable_2d = false -audio_listener_enable_3d = false -physics_object_picking = false -gui_disable_input = true -gui_snap_controls_to_pixels = true -shadow_atlas_size = 0 -shadow_atlas_quad_0 = 2 -shadow_atlas_quad_1 = 2 -shadow_atlas_quad_2 = 3 -shadow_atlas_quad_3 = 4 -_sections_unfolded = [ "GUI", "Render Target", "Rendering" ] - -[node name="ColorRect" type="ColorRect" parent="SaveViewport" index="0"] - -material = SubResource( 3 ) -anchor_left = 0.0 -anchor_top = 0.0 -anchor_right = 0.0 -anchor_bottom = 0.0 -margin_right = 40.0 -margin_bottom = 40.0 -rect_pivot_offset = Vector2( 0, 0 ) -rect_clip_content = false -mouse_filter = 0 -mouse_default_cursor_shape = 0 -size_flags_horizontal = 1 -size_flags_vertical = 1 -color = Color( 1, 1, 1, 1 ) -_sections_unfolded = [ "Material", "Rect" ] - -[node name="Timer" type="Timer" parent="SaveViewport" index="1"] - -process_mode = 1 -wait_time = 0.1 -one_shot = true -autostart = false - [connection signal="connection_request" from="." to="." method="connect_node"] [connection signal="disconnection_request" from="." to="." method="disconnect_node"] diff --git a/addons/material_maker/main_window.gd b/addons/material_maker/main_window.gd index 70ce42a..bbd971b 100644 --- a/addons/material_maker/main_window.gd +++ b/addons/material_maker/main_window.gd @@ -1,6 +1,7 @@ tool extends Panel +var editor_interface = null var current_tab = null const MENU = [ @@ -26,6 +27,8 @@ const MENU = [ { menu="Help", command="about", description="About" } ] +signal quit + func _ready(): OS.set_window_title(ProjectSettings.get_setting("application/config/name")+" v"+ProjectSettings.get_setting("application/config/release")) for m in $VBoxContainer/Menu.get_children(): @@ -76,6 +79,8 @@ func menu_about_to_show(name, menu): func new_pane(): var graph_edit = preload("res://addons/material_maker/graph_edit.tscn").instance() + graph_edit.renderer = $Renderer + graph_edit.editor_interface = editor_interface $VBoxContainer/HBoxContainer/Projects.add_child(graph_edit) $VBoxContainer/HBoxContainer/Projects.current_tab = graph_edit.get_index() return graph_edit @@ -140,8 +145,7 @@ func export_material_is_disabled(): func quit(): if Engine.editor_hint: - get_parent().hide() - get_parent().queue_free() + emit_signal("quit") else: get_tree().quit() @@ -245,7 +249,7 @@ func update_preview_2d(node = null): node = n break if node != null: - graph_edit.setup_material(preview.get_2d_material(), node.get_textures(), node.generate_shader()) + graph_edit.renderer.setup_material(preview.get_2d_material(), node.get_textures(), node.generate_shader()) func _on_Projects_tab_changed(tab): var new_tab = $VBoxContainer/HBoxContainer/Projects.get_current_tab_control() diff --git a/addons/material_maker/main_window.tscn b/addons/material_maker/main_window.tscn index abef85d..5ae8cf6 100644 --- a/addons/material_maker/main_window.tscn +++ b/addons/material_maker/main_window.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=5 format=2] +[gd_scene load_steps=6 format=2] [ext_resource path="res://addons/material_maker/main_window.gd" type="Script" id=1] [ext_resource path="res://addons/material_maker/library.gd" type="Script" id=2] [ext_resource path="res://addons/material_maker/preview.tscn" type="PackedScene" id=3] [ext_resource path="res://addons/material_maker/widgets/tabs.gd" type="Script" id=4] +[ext_resource path="res://addons/material_maker/renderer.tscn" type="PackedScene" id=5] [node name="MainWindow" type="Panel" index="0"] @@ -260,6 +261,8 @@ tab_align = 0 tab_close_display_policy = 1 scrolling_enabled = true +[node name="Renderer" parent="." index="1" instance=ExtResource( 5 )] + [connection signal="no_more_tabs" from="VBoxContainer/HBoxContainer/Projects" to="." method="new_material"] [connection signal="resized" from="VBoxContainer/HBoxContainer/Projects" to="VBoxContainer/HBoxContainer/Projects" method="_on_Projects_resized"] diff --git a/addons/material_maker/node_base.gd b/addons/material_maker/node_base.gd index 4bdff22..c559ca2 100644 --- a/addons/material_maker/node_base.gd +++ b/addons/material_maker/node_base.gd @@ -155,28 +155,12 @@ func deserialize_element(e): return Color(e.r, e.g, e.b, e.a) return e -func do_generate_shader(src_code): - var code - code = "shader_type canvas_item;\n" - var file = File.new() - file.open("res://addons/material_maker/common.shader", File.READ) - code += file.get_as_text() - code += "\n" - var shader_code = src_code.defs - shader_code += "void fragment() {\n" - shader_code += src_code.code - shader_code += "COLOR = vec4("+src_code.rgb+", 1.0);\n" - shader_code += "}\n" - #print("GENERATED SHADER:\n"+shader_code) - code += shader_code - return code - func generate_shader(slot = 0): # Reset all nodes for c in get_parent().get_children(): if c is GraphNode: c.reset() - return do_generate_shader(get_shader_code("UV", slot)) + return get_parent().renderer.generate_shader(get_shader_code("UV", slot)) func serialize(): var type = get_script().resource_path diff --git a/addons/material_maker/nodes/blur.gd b/addons/material_maker/nodes/blur.gd index aad02b0..11bb9e7 100644 --- a/addons/material_maker/nodes/blur.gd +++ b/addons/material_maker/nodes/blur.gd @@ -6,9 +6,7 @@ var direction = 0 var sigma = 1.0 var input_shader = "" -var input_texture -var mid_texture -var final_texture +var saved_texture const DIRECTION_H = 1 const DIRECTION_V = 2 @@ -30,9 +28,7 @@ func _ready(): $HBoxContainer2/direction.add_item(d.name) $HBoxContainer2/direction.selected = direction initialize_properties([ $HBoxContainer1/size, $HBoxContainer2/direction, $HBoxContainer3/sigma ]) - input_texture = ImageTexture.new() - mid_texture = ImageTexture.new() - final_texture = ImageTexture.new() + saved_texture = ImageTexture.new() func get_gaussian_blur_shader(horizontal): var convolution = { x=0, y=0, kernel=[], epsilon=1.0/pow(2, 5+size) } @@ -53,23 +49,23 @@ func get_gaussian_blur_shader(horizontal): func _rerender(): if DIRECTIONS[direction].mask & DIRECTION_H != 0: - get_parent().precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), input_texture, self, "pass_1", []) + get_parent().renderer.precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), saved_texture, self, "pass_1", []) else: - get_parent().precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), mid_texture, self, "pass_2", []) + get_parent().renderer.precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), saved_texture, self, "pass_2", []) func pass_1(): if DIRECTIONS[direction].mask & DIRECTION_V != 0: - get_parent().precalculate_shader(get_gaussian_blur_shader(true), { input=input_texture}, int(pow(2, 5+size)), mid_texture, self, "pass_2", []) + get_parent().renderer.precalculate_shader(get_gaussian_blur_shader(true), { input=saved_texture }, int(pow(2, 5+size)), saved_texture, self, "pass_2", []) else: - get_parent().precalculate_shader(get_gaussian_blur_shader(true), { input=input_texture}, int(pow(2, 5+size)), final_texture, self, "rerender_targets", []) + get_parent().renderer.precalculate_shader(get_gaussian_blur_shader(true), { input=saved_texture }, int(pow(2, 5+size)), saved_texture, self, "rerender_targets", []) func pass_2(): - get_parent().precalculate_shader(get_gaussian_blur_shader(false), { input=mid_texture}, int(pow(2, 5+size)), final_texture, self, "rerender_targets", []) + get_parent().renderer.precalculate_shader(get_gaussian_blur_shader(false), { input=saved_texture }, int(pow(2, 5+size)), saved_texture, self, "rerender_targets", []) func get_textures(): var list = {} - list[name] = final_texture + list[name] = saved_texture return list func _get_shader_code(uv): @@ -77,7 +73,7 @@ func _get_shader_code(uv): var src = get_source() if src == null: return rv - input_shader = do_generate_shader(src.get_shader_code("UV")) + input_shader = get_parent().renderer.generate_shader(src.get_shader_code("UV")) _rerender() if generated_variants.empty(): rv.defs = "uniform sampler2D "+name+"_tex;\n" diff --git a/addons/material_maker/nodes/emboss.gd b/addons/material_maker/nodes/emboss.gd index edd578b..ee7809f 100644 --- a/addons/material_maker/nodes/emboss.gd +++ b/addons/material_maker/nodes/emboss.gd @@ -34,14 +34,14 @@ func _ready(): initialize_properties([ $HBoxContainer1/size, $HBoxContainer2/direction ]) func _rerender(): - get_parent().precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), input_texture, self, "pass_1", []) + get_parent().renderer.precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), input_texture, self, "pass_1", []) func pass_1(): var convolution = CONVOLUTION convolution.epsilon=1.0/pow(2, 5+size) for i in range(8): convolution.kernel[INDICES[i]] = COEFS[(i+8-int(direction))%8] - get_parent().precalculate_shader(get_convolution_shader(convolution), {input=input_texture}, int(pow(2, 5+size)), final_texture, self, "rerender_targets", []) + get_parent().renderer.precalculate_shader(get_convolution_shader(convolution), {input=input_texture}, int(pow(2, 5+size)), final_texture, self, "rerender_targets", []) func get_textures(): var list = {} @@ -53,7 +53,7 @@ func _get_shader_code(uv): var src = get_source() if src == null: return rv - input_shader = do_generate_shader(src.get_shader_code("UV")) + input_shader = get_parent().generate_shader(src.get_shader_code("UV")) _rerender() if generated_variants.empty(): rv.defs = "uniform sampler2D %s_tex;\n" % [ name ] diff --git a/addons/material_maker/nodes/export.gd b/addons/material_maker/nodes/export.gd index 5ea273f..5e81674 100644 --- a/addons/material_maker/nodes/export.gd +++ b/addons/material_maker/nodes/export.gd @@ -19,7 +19,7 @@ func export_textures(prefix, size = null): if suffix != "": if size == null: size = int(pow(2, 8+resolution)) - get_parent().export_texture(get_source(), "%s_%s.png" % [ prefix, suffix ], size) + get_parent().renderer.export_texture(get_source(), "%s_%s.png" % [ prefix, suffix ], size) func serialize(): var data = .serialize() diff --git a/addons/material_maker/nodes/material.gd b/addons/material_maker/nodes/material.gd index 0a5ee33..4f56092 100644 --- a/addons/material_maker/nodes/material.gd +++ b/addons/material_maker/nodes/material.gd @@ -10,6 +10,8 @@ var normal_scale var ao_light_affect var depth_scale +var texture_list + var current_material_list = [] var generated_textures = {} @@ -27,24 +29,30 @@ const ADDON_TEXTURE_LIST = [ { port=0, texture="albedo" }, { port=3, texture="emission" }, { port=4, texture="normal_map" }, - { ports=[1, 2, 5], texture="mrao" }, + { ports=[1, 2, 5], default_values=["0.0", "1.0", "1.0"], texture="mrao" }, { port=6, texture="depth_map" } ] func _ready(): - for t in TEXTURE_LIST: + texture_list = TEXTURE_LIST + if Engine.editor_hint: + texture_list = ADDON_TEXTURE_LIST + for t in texture_list: generated_textures[t.texture] = { shader=null, source=null, texture=null } initialize_properties([ $resolution, $Albedo/albedo_color, $Metallic/metallic, $Roughness/roughness, $Emission/emission_energy, $NormalMap/normal_scale, $AmbientOcclusion/ao_light_affect, $DepthMap/depth_scale ]) func _rerender(): + var size = int(pow(2, 8+resolution)) var has_textures = false - for t in TEXTURE_LIST: + for t in texture_list: var shader = generated_textures[t.texture].shader if shader != null: - var input_textures = null - if generated_textures[t.texture].source != null: - input_textures = generated_textures[t.texture].source.get_textures() - get_parent().precalculate_shader(shader, input_textures, 1024, generated_textures[t.texture].texture, self, "do_update_materials", [ current_material_list ]) + var input_textures = {} + for s in generated_textures[t.texture].sources: + var source_textures = s.get_textures() + for st in source_textures.keys(): + input_textures[st] = source_textures[st] + get_parent().renderer.precalculate_shader(shader, input_textures, size, generated_textures[t.texture].texture, self, "do_update_materials", [ current_material_list ]) has_textures = true if !has_textures: do_update_materials(current_material_list) @@ -59,63 +67,123 @@ func _get_shader_code(uv): func update_materials(material_list): current_material_list = material_list var has_textures = false - for t in TEXTURE_LIST: - var source = get_source(t.port) - if source == null: - generated_textures[t.texture].shader = null - generated_textures[t.texture].source = null + for t in texture_list: + var shader = null + var sources = [] + if t.has("port"): + var source = get_source(t.port) + if source != null: + shader = source.generate_shader() + sources.append(source) + elif t.has("ports"): + var source = [ null, null, null ] + generated_textures[t.texture].mask = 0 + for i in range(3): + source[i] = get_source(t.ports[i]) + if source[i] != null: + sources.append(source[i]) + generated_textures[t.texture].mask |= 1 << i + if !sources.empty(): + for c in get_parent().get_children(): + if c is GraphNode: + c.reset() + var source_code = [ null, null, null ] + for i in range(3): + if source[i] != null: + source_code[i] = source[i].get_shader_code("UV") + else: + source_code[i] = { defs="", code="", f=t.default_values[i] } + shader = get_parent().renderer.generate_combined_shader(source_code[0], source_code[1], source_code[2]) + generated_textures[t.texture].shader = shader + generated_textures[t.texture].sources = sources + if shader == null: if generated_textures[t.texture].texture != null: generated_textures[t.texture].texture = null else: - generated_textures[t.texture].shader = source.generate_shader() - generated_textures[t.texture].source = source if generated_textures[t.texture].texture == null: generated_textures[t.texture].texture = ImageTexture.new() _rerender() +func get_generated_texture(slot, file_prefix = null): + if file_prefix != null: + var file_name = "%s_%s.png" % [ file_prefix, slot ] + if File.new().file_exists(file_name): + return load(file_name) + else: + return null + else: + return generated_textures[slot].texture + +func update_spatial_material(m, file_prefix = null): + var texture + m.albedo_color = albedo_color + m.albedo_texture = get_generated_texture("albedo", file_prefix) + m.metallic = metallic + m.roughness = roughness + if Engine.editor_hint: + texture = get_generated_texture("mrao", file_prefix) + m.metallic_texture = texture + m.metallic_texture_channel = SpatialMaterial.TEXTURE_CHANNEL_RED + m.roughness_texture = texture + m.roughness_texture_channel = SpatialMaterial.TEXTURE_CHANNEL_GREEN + else: + m.metallic_texture = get_generated_texture("metallic", file_prefix) + m.roughness_texture = get_generated_texture("roughness", file_prefix) + texture = get_generated_texture("emission", file_prefix) + if texture != null: + m.emission_enabled = true + m.emission_energy = emission_energy + m.emission_texture = texture + else: + m.emission_enabled = false + texture = get_generated_texture("normal_map", file_prefix) + if texture != null: + m.normal_enabled = true + m.normal_texture = texture + else: + m.normal_enabled = false + if Engine.editor_hint: + if (generated_textures.mrao.mask & (1 << 2)) != 0: + m.ao_enabled = true + m.ao_light_affect = ao_light_affect + m.ao_texture = m.metallic_texture + m.ao_texture_channel = SpatialMaterial.TEXTURE_CHANNEL_BLUE + else: + m.ao_enabled = false + else: + texture = get_generated_texture("ambient_occlusion", file_prefix) + if texture != null: + m.ao_enabled = true + m.ao_light_affect = ao_light_affect + m.ao_texture = texture + else: + m.ao_enabled = false + texture = get_generated_texture("depth_map", file_prefix) + if texture != null: + m.depth_enabled = true + m.depth_scale = depth_scale + m.depth_texture = texture + else: + m.depth_enabled = false + func do_update_materials(material_list): for m in material_list: if m is SpatialMaterial: - m.albedo_color = albedo_color - m.albedo_texture = generated_textures.albedo.texture - m.metallic = metallic - m.metallic_texture = generated_textures.metallic.texture - m.roughness = roughness - m.roughness_texture = generated_textures.roughness.texture - if generated_textures.emission.texture != null: - m.emission_enabled = true - m.emission_energy = emission_energy - m.emission_texture = generated_textures.emission.texture - else: - m.emission_enabled = false - if generated_textures.normal_map.texture != null: - m.normal_enabled = true - m.normal_texture = generated_textures.normal_map.texture - else: - m.normal_enabled = false - if generated_textures.ambient_occlusion.texture != null: - m.ao_enabled = true - m.ao_light_affect = ao_light_affect - m.ao_texture = generated_textures.ambient_occlusion.texture - else: - m.ao_enabled = false - if generated_textures.depth_map.texture != null: - m.depth_enabled = true - m.depth_scale = depth_scale - m.depth_texture = generated_textures.depth_map.texture - else: - m.depth_enabled = false + update_spatial_material(m) func export_textures(prefix, size = null): if size == null: size = int(pow(2, 8+resolution)) - var texture_list = TEXTURE_LIST - if Engine.editor_hint: - texture_list = ADDON_TEXTURE_LIST for t in texture_list: - if t.has("port"): - get_parent().export_texture(get_source(t.port), "%s_%s.png" % [ prefix, t.texture ], size) - elif t.has("ports"): - get_parent().export_combined_texture(get_source(t.port[0]), get_source(t.port[1]), get_source(t.port[2]), "%s_%s.png" % [ prefix, t.texture ], size) + var texture = generated_textures[t.texture].texture + if texture != null: + var image = texture.get_data() + image.save_png("%s_%s.png" % [ prefix, t.texture ]) if Engine.editor_hint: - print("Exporting material") + var resource_filesystem = get_parent().editor_interface.get_resource_filesystem() + resource_filesystem.scan() + yield(resource_filesystem, "filesystem_changed") + var new_material = SpatialMaterial.new() + update_spatial_material(new_material, prefix) + ResourceSaver.save("%s.tres" % [ prefix ], new_material) + resource_filesystem.scan() diff --git a/addons/material_maker/nodes/normal_map.gd b/addons/material_maker/nodes/normal_map.gd index f0e1dbb..cf49e16 100644 --- a/addons/material_maker/nodes/normal_map.gd +++ b/addons/material_maker/nodes/normal_map.gd @@ -35,13 +35,13 @@ func _ready(): initialize_properties([ $HBoxContainer1/size, $HBoxContainer2/amount ]) func _rerender(): - get_parent().precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), input_texture, self, "pass_1", []) + get_parent().renderer.precalculate_shader(input_shader, get_source().get_textures(), int(pow(2, 5+size)), input_texture, self, "pass_1", []) func pass_1(): var convolution = CONVOLUTION convolution.epsilon=1.0/pow(2, 5+size) convolution.scale_before_normalize = pow(2, size-1)*amount - get_parent().precalculate_shader(get_convolution_shader(convolution), { input=input_texture}, int(pow(2, 5+size)), final_texture, self, "rerender_targets", []) + get_parent().renderer.precalculate_shader(get_convolution_shader(convolution), { input=input_texture}, int(pow(2, 5+size)), final_texture, self, "rerender_targets", []) func get_textures(): var list = {} @@ -53,7 +53,7 @@ func _get_shader_code(uv): var src = get_source() if src == null: return rv - input_shader = do_generate_shader(src.get_shader_code("UV")) + input_shader = get_parent().renderer.generate_shader(src.get_shader_code("UV")) _rerender() if generated_variants.empty(): rv.defs = "uniform sampler2D %s_tex;\n" % [ name ] diff --git a/addons/material_maker/plugin.gd b/addons/material_maker/plugin.gd index 38f3fb7..26236e3 100644 --- a/addons/material_maker/plugin.gd +++ b/addons/material_maker/plugin.gd @@ -5,17 +5,12 @@ var mm_button = null var material_maker = null func _enter_tree(): - #material_maker = preload("res://addons/material_maker/pm_material_maker.tscn").instance() - #add_control_to_bottom_panel(material_maker, "ProceduralMaterial") - print("Adding menu item") mm_button = Button.new() mm_button.connect("pressed", self, "open_material_maker") mm_button.text = "Material Maker" add_control_to_container(CONTAINER_TOOLBAR, mm_button) - print("done") func _exit_tree(): - #remove_control_from_bottom_panel(material_maker) if mm_button != null: remove_control_from_container(CONTAINER_TOOLBAR, mm_button) mm_button.queue_free() @@ -34,6 +29,15 @@ func _set_state(s): func open_material_maker(): if material_maker == null: - material_maker = load("res://addons/material_maker/window_dialog.tscn").instance() + material_maker = preload("res://addons/material_maker/window_dialog.tscn").instance() + var panel = material_maker.get_node("MainWindow") + panel.editor_interface = get_editor_interface() + panel.connect("quit", self, "close_material_maker") add_child(material_maker) material_maker.popup_centered() + +func close_material_maker(): + if material_maker != null: + material_maker.hide() + material_maker.queue_free() + material_maker = null diff --git a/addons/material_maker/renderer.gd b/addons/material_maker/renderer.gd new file mode 100644 index 0000000..12f71b7 --- /dev/null +++ b/addons/material_maker/renderer.gd @@ -0,0 +1,98 @@ +tool +extends Viewport + +var render_queue = [] + +func _ready(): + $ColorRect.material = $ColorRect.material.duplicate(true) + +# Save shader to image, create image texture + +static func generate_shader(src_code): + var code + code = "shader_type canvas_item;\n" + var file = File.new() + file.open("res://addons/material_maker/common.shader", File.READ) + code += file.get_as_text() + code += "\n" + var shader_code = src_code.defs + shader_code += "void fragment() {\n" + shader_code += src_code.code + shader_code += "COLOR = vec4("+src_code.rgb+", 1.0);\n" + shader_code += "}\n" + #print("GENERATED SHADER:\n"+shader_code) + code += shader_code + return code + +static func generate_combined_shader(red_code, green_code, blue_code): + var code + code = "shader_type canvas_item;\n" + var file = File.new() + file.open("res://addons/material_maker/common.shader", File.READ) + code += file.get_as_text() + code += "\n" + var shader_code = "" + shader_code += red_code.defs + shader_code += green_code.defs + shader_code += blue_code.defs + shader_code += "void fragment() {\n" + shader_code += red_code.code + shader_code += green_code.code + shader_code += blue_code.code + shader_code += "COLOR = vec4("+red_code.f+", "+green_code.f+", "+blue_code.f+", 1.0);\n" + shader_code += "}\n" + #print("GENERATED COMBINED SHADER:\n"+shader_code) + code += shader_code + return code + +func setup_material(shader_material, textures, shader_code): + for k in textures.keys(): + shader_material.set_shader_param(k+"_tex", textures[k]) + shader_material.shader.code = shader_code + +func render_shader_to_viewport(shader, textures, render_size, method, args): + render_queue.append( { shader=shader, textures=textures, size=render_size, method=method, args=args } ) + if render_queue.size() == 1: + while !render_queue.empty(): + var job = render_queue.front() + size = Vector2(job.size, job.size) + $ColorRect.rect_position = Vector2(0, 0) + $ColorRect.rect_size = Vector2(job.size, job.size) + var shader_material = $ColorRect.material + shader_material.shader.code = job.shader + if job.textures != null: + for k in job.textures.keys(): + shader_material.set_shader_param(k+"_tex", job.textures[k]) + render_target_update_mode = Viewport.UPDATE_ALWAYS + update_worlds() + yield(get_tree(), "idle_frame") + yield(get_tree(), "idle_frame") + render_target_update_mode = Viewport.UPDATE_DISABLED + callv(job.method, job.args) + render_queue.pop_front() + +func render_to_viewport(node, render_size, method, args): + render_shader_to_viewport(node.generate_shader(), node.get_textures(), render_size, method, args) + +func export_texture(node, filename, render_size = 256): + if node == null: + return null + render_to_viewport(node, render_size, "do_export_texture", [ filename ]) + +func do_export_texture(filename): + var viewport_texture = get_texture() + var viewport_image = viewport_texture.get_data() + viewport_image.save_png(filename) + +func precalculate_node(node, render_size, target_texture, object, method, args): + if node == null: + return null + render_to_viewport(node, render_size, "do_precalculate_texture", [ object, method, args ]) + +func precalculate_shader(shader, textures, render_size, target_texture, object, method, args): + render_shader_to_viewport(shader, textures, render_size, "do_precalculate_texture", [ target_texture, object, method, args ]) + +func do_precalculate_texture(target_texture, object, method, args): + var viewport_texture = get_texture() + target_texture.create_from_image(viewport_texture.get_data()) + object.callv(method, args) diff --git a/addons/material_maker/renderer.tscn b/addons/material_maker/renderer.tscn new file mode 100644 index 0000000..edf9cfd --- /dev/null +++ b/addons/material_maker/renderer.tscn @@ -0,0 +1,72 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://addons/material_maker/renderer.gd" type="Script" id=1] + +[sub_resource type="Shader" id=1] + +code = "shader_type canvas_item; + +void fragment() { + COLOR = vec4(1.0); +} +" + +[sub_resource type="ShaderMaterial" id=2] + +render_priority = 0 +shader = SubResource( 1 ) + +[node name="Renderer" type="Viewport"] + +arvr = false +size = Vector2( 0, 0 ) +own_world = true +world = null +transparent_bg = false +msaa = 2 +hdr = false +disable_3d = false +usage = 2 +debug_draw = 0 +render_target_v_flip = true +render_target_clear_mode = 0 +render_target_update_mode = 1 +audio_listener_enable_2d = false +audio_listener_enable_3d = false +physics_object_picking = false +gui_disable_input = true +gui_snap_controls_to_pixels = true +shadow_atlas_size = 0 +shadow_atlas_quad_0 = 2 +shadow_atlas_quad_1 = 2 +shadow_atlas_quad_2 = 3 +shadow_atlas_quad_3 = 4 +script = ExtResource( 1 ) +_sections_unfolded = [ "GUI", "Render Target", "Rendering" ] + +[node name="ColorRect" type="ColorRect" parent="." index="0"] + +material = SubResource( 2 ) +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 40.0 +margin_bottom = 40.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 0 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +color = Color( 1, 1, 1, 1 ) +_sections_unfolded = [ "Material", "Rect" ] + +[node name="Timer" type="Timer" parent="." index="1"] + +process_mode = 1 +wait_time = 0.1 +one_shot = true +autostart = false + + diff --git a/test.ptex b/test.ptex deleted file mode 100644 index 1fed050..0000000 --- a/test.ptex +++ /dev/null @@ -1 +0,0 @@ -{"connections":[{"from":"pattern_1","from_port":0,"to":"colorize_0","to_port":0},{"from":"perlin_0","from_port":0,"to":"transform_0","to_port":0},{"from":"colorize_0","from_port":0,"to":"transform_0","to_port":3},{"from":"uniform_0","from_port":0,"to":"Material","to_port":2},{"from":"normal_map_0","from_port":0,"to":"Material","to_port":4},{"from":"transform_0","from_port":0,"to":"normal_map_0","to_port":0},{"from":"transform_0","from_port":0,"to":"colorize_1","to_port":0},{"from":"colorize_1","from_port":0,"to":"Material","to_port":0}],"nodes":[{"iterations":7,"name":"perlin_0","node_position":{"x":-248,"y":-183.5},"persistence":0.5,"scale_x":4,"scale_y":4,"type":"perlin"},{"mix":0,"name":"pattern_1","node_position":{"x":-488,"y":6.5},"type":"pattern","x_scale":1,"x_wave":0,"y_scale":1,"y_wave":0},{"gradient":[{"b":0,"g":0,"pos":0,"r":0},{"b":1,"g":1,"pos":1,"r":1}],"name":"colorize_0","node_position":{"x":-214,"y":9.5},"type":"colorize"},{"albedo_color":{"a":1,"b":1,"g":1,"r":1,"type":"Color"},"ao_light_affect":1,"depth_scale":1,"emission_energy":1,"metallic":1,"name":"Material","node_position":{"x":387,"y":-79},"normal_scale":1,"resolution":1,"roughness":1,"type":"material"},{"color":{"a":1,"b":0,"g":0,"r":0,"type":"Color"},"name":"uniform_0","node_position":{"x":248.577271,"y":-53.178574},"type":"uniform"},{"amount":0.5,"name":"normal_map_0","node_position":{"x":234.577271,"y":52.821411},"type":"normal_map"},{"name":"transform_0","node_position":{"x":-8,"y":-73.5},"repeat":true,"rotate":100,"scale_x":1,"scale_y":1,"translate_x":0,"translate_y":0,"type":"transform"},{"gradient":[{"b":0,"g":0,"pos":0.3,"r":1},{"b":0,"g":1,"pos":0.409091,"r":0.96875},{"b":0,"g":0.801432,"pos":0.518182,"r":0.025045},{"b":1,"g":0.000001,"pos":0.627273,"r":0},{"b":0.708333,"g":0,"pos":0.745455,"r":0.641927}],"name":"colorize_1","node_position":{"x":213,"y":-149.5},"type":"colorize"}]} \ No newline at end of file