From 3c87f2c491dea710ead102e01c31d4d8fce71b7e Mon Sep 17 00:00:00 2001 From: RodZill4 Date: Wed, 18 Mar 2020 21:40:02 +0100 Subject: [PATCH] Optimized 3D preview and buffer nodes --- addons/material_maker/engine/gen_base.gd | 3 +- addons/material_maker/engine/gen_buffer.gd | 50 ++++++++++++++++--- addons/material_maker/engine/gen_material.gd | 51 +++++++++++++++++--- addons/material_maker/engine/renderer.gd | 35 +++++++++++--- material_maker/nodes/generic.gd | 3 +- material_maker/preview/preview_2d.gd | 6 +-- material_maker/preview/preview_3d.gd | 4 -- material_maker/preview/preview_3d.tscn | 5 +- 8 files changed, 124 insertions(+), 33 deletions(-) diff --git a/addons/material_maker/engine/gen_base.gd b/addons/material_maker/engine/gen_base.gd index 72a5503..86112b1 100644 --- a/addons/material_maker/engine/gen_base.gd +++ b/addons/material_maker/engine/gen_base.gd @@ -117,12 +117,13 @@ func get_parameter(n : String): func set_parameter(n : String, v) -> void: parameters[n] = v - source_changed(0) emit_signal("parameter_changed", n, v) if is_inside_tree(): var parameter_def : Dictionary = get_parameter_def(n) if parameter_def.has("type") and parameter_def.type == "float": get_tree().call_group("preview", "on_float_parameter_changed", "p_o%s_%s" % [ str(get_instance_id()), n ], v) + else: + source_changed(0) func notify_output_change(output_index : int) -> void: var targets = get_targets(output_index) diff --git a/addons/material_maker/engine/gen_buffer.gd b/addons/material_maker/engine/gen_buffer.gd index 5de59ee..aad7a37 100644 --- a/addons/material_maker/engine/gen_buffer.gd +++ b/addons/material_maker/engine/gen_buffer.gd @@ -7,11 +7,16 @@ Texture generator buffers, that render their input in a specific resolution and This is useful when using generators that sample their inputs several times (such as convolutions) """ -var updated : bool = false +var material : ShaderMaterial = null +var updating : bool = false +var update_again : bool = false func _ready() -> void: + material = ShaderMaterial.new() + material.shader = Shader.new() if !parameters.has("size"): parameters.size = 9 + add_to_group("preview") func get_type() -> String: return "buffer" @@ -31,20 +36,51 @@ func get_input_defs() -> Array: func get_output_defs() -> Array: return [ { type="rgba" }, { type="rgba" } ] -func source_changed(input_port_index : int) -> void: - updated = false - .source_changed(input_port_index) +func source_changed(_input_port_index : int) -> void: + if !is_inside_tree(): + return + var context : MMGenContext = MMGenContext.new() + var source = {} + var source_output = get_source(0) + if source_output != null: + source = source_output.generator.get_shader_code("uv", source_output.output_index, context) + while source is GDScriptFunctionState: + source = yield(source, "completed") + if source.empty(): + source = { defs="", code="", textures={}, rgba="vec4(0.0)" } + material.shader.code = mm_renderer.generate_shader(source) + if source.has("textures"): + for k in source.textures.keys(): + material.set_shader_param(k, source.textures[k]) + update_buffer() -func _get_shader_code(uv : String, output_index : int, context : MMGenContext) -> Dictionary: +func on_float_parameter_changed(n : String, v : float) -> void: + material.set_shader_param(n, v) + update_buffer() + +func update_buffer(): + update_again = true + if !updating: + updating = true + while update_again: + update_again = false + var result = mm_renderer.render_material(material, pow(2, parameters.size)) + while result is GDScriptFunctionState: + result = yield(result, "completed") + if !update_again: + result.copy_to_texture(texture) + result.release() + updating = false + +func __get_shader_code(uv : String, output_index : int, context : MMGenContext) -> Dictionary: var source = get_source(0) - if source != null and !updated: + if source != null: var result = source.generator.render(source.output_index, pow(2, parameters.size)) while result is GDScriptFunctionState: result = yield(result, "completed") result.copy_to_texture(texture) result.release() texture.flags = Texture.FLAG_MIPMAPS - updated = true var rv = ._get_shader_code_lod(uv, output_index, context, 0 if output_index == 0 else parameters.lod) while rv is GDScriptFunctionState: rv = yield(rv, "completed") diff --git a/addons/material_maker/engine/gen_material.gd b/addons/material_maker/engine/gen_material.gd index 1af89e1..8fe0f43 100644 --- a/addons/material_maker/engine/gen_material.gd +++ b/addons/material_maker/engine/gen_material.gd @@ -5,7 +5,11 @@ class_name MMGenMaterial var export_paths = {} var material : SpatialMaterial +var shader_materials = {} +var need_update = {} var generated_textures = {} +var updating : bool = false +var update_again : bool = false const TEXTURE_LIST = [ { port=0, texture="albedo", sources=[0] }, @@ -38,7 +42,11 @@ const TEXTURE_SIZE_DEFAULT = 10 # 1024x1024 func _ready() -> void: for t in TEXTURE_LIST: generated_textures[t.texture] = null + need_update[t.texture] = true + shader_materials[t.texture] = ShaderMaterial.new() + shader_materials[t.texture].shader = Shader.new() material = SpatialMaterial.new() + add_to_group("preview") func can_be_deleted() -> bool: return false @@ -74,34 +82,63 @@ func set_parameter(p, v) -> void: func source_changed(input_index : int) -> void: for t in TEXTURE_LIST: if t.has("sources") and t.sources.find(input_index) != -1: - generated_textures[t.texture] = null + need_update[t.texture] = true update_preview() func render_textures() -> void: for t in TEXTURE_LIST: - var texture = null var result if t.has("port"): - if generated_textures[t.texture] != null: + if !need_update[t.texture]: continue - result = render(t.port, get_image_size()) + var context : MMGenContext = MMGenContext.new() + var source = get_shader_code("uv", t.port, context) + while source is GDScriptFunctionState: + source = yield(source, "completed") + if source.empty(): + source = { defs="", code="", textures={}, rgba="vec4(0.0)" } + shader_materials[t.texture].shader.code = mm_renderer.generate_shader(source) + if source.has("textures"): + for k in source.textures.keys(): + shader_materials[t.texture].set_shader_param(k, source.textures[k]) + result = mm_renderer.render_material(shader_materials[t.texture], get_image_size()) else: generated_textures[t.texture] = null + need_update[t.texture] = false continue while result is GDScriptFunctionState: result = yield(result, "completed") - texture = ImageTexture.new() + if generated_textures[t.texture] == null: + generated_textures[t.texture] = ImageTexture.new() + var texture = generated_textures[t.texture] result.copy_to_texture(texture) result.release() # To work, this must be set after calling `copy_to_texture()` texture.flags |= ImageTexture.FLAG_ANISOTROPIC_FILTER - # Disable filtering for small textures, as they're considered to be used # for a pixel art style if texture.get_size().x <= 128: texture.flags ^= ImageTexture.FLAG_FILTER + need_update[t.texture] = false - generated_textures[t.texture] = texture +func on_float_parameter_changed(n : String, v : float) -> void: + var image_size = get_image_size() + for t in TEXTURE_LIST: + if generated_textures[t.texture] != null: + shader_materials[t.texture].set_shader_param(n, v) + update_again = true + if !updating: + updating = true + while update_again: + update_again = false + for t in TEXTURE_LIST: + if generated_textures[t.texture] != null: + var result = mm_renderer.render_material(shader_materials[t.texture], image_size) + while result is GDScriptFunctionState: + result = yield(result, "completed") + result.copy_to_texture(generated_textures[t.texture]) + result.release() + updating = false func update_materials(material_list) -> void: render_textures() diff --git a/addons/material_maker/engine/renderer.gd b/addons/material_maker/engine/renderer.gd index 70c2980..7100562 100644 --- a/addons/material_maker/engine/renderer.gd +++ b/addons/material_maker/engine/renderer.gd @@ -5,19 +5,24 @@ class_name MMGenRenderer export(String) var debug_path = "" var debug_file_index : int = 0 +var common_shader : String + var rendering : bool = false + signal done + func _ready() -> void: $ColorRect.material = $ColorRect.material.duplicate(true) + var file = File.new() + file.open("res://addons/material_maker/common.shader", File.READ) + common_shader = file.get_as_text() -static func generate_shader(src_code) -> String: +func generate_shader(src_code) -> String: var code code = "shader_type canvas_item;\n" code += "render_mode blend_disabled;\n" - var file = File.new() - file.open("res://addons/material_maker/common.shader", File.READ) - code += file.get_as_text() + code += common_shader code += "\n" if src_code.has("textures"): for t in src_code.textures.keys(): @@ -38,13 +43,11 @@ static func generate_shader(src_code) -> String: code += shader_code return code -static func generate_combined_shader(red_code, green_code, blue_code) -> String: +func generate_combined_shader(red_code, green_code, blue_code) -> String: var code code = "shader_type canvas_item;\n" code += "render_mode blend_disabled;\n" - var file = File.new() - file.open("res://addons/material_maker/common.shader", File.READ) - code += file.get_as_text() + code += common_shader code += "\n" var globals = [] var textures = {} @@ -84,6 +87,22 @@ func setup_material(shader_material, textures, shader_code) -> void: shader_material.set_shader_param(k+"_tex", textures[k]) shader_material.shader.code = shader_code +func render_material(material, render_size) -> Object: + while rendering: + yield(self, "done") + rendering = true + var shader_material = $ColorRect.material + size = Vector2(render_size, render_size) + $ColorRect.rect_position = Vector2(0, 0) + $ColorRect.rect_size = size + $ColorRect.material = material + render_target_update_mode = Viewport.UPDATE_ONCE + update_worlds() + yield(get_tree(), "idle_frame") + yield(get_tree(), "idle_frame") + $ColorRect.material = shader_material + return self + func render_shader(shader, textures, render_size) -> Object: if debug_path != null and debug_path != "": var file_name = debug_path+str(debug_file_index)+".shader" diff --git a/material_maker/nodes/generic.gd b/material_maker/nodes/generic.gd index fdadf55..32f5cf5 100644 --- a/material_maker/nodes/generic.gd +++ b/material_maker/nodes/generic.gd @@ -95,7 +95,8 @@ func _on_value_changed(new_value, variable) -> void: ignore_parameter_change = variable generator.set_parameter(variable, new_value) ignore_parameter_change = "" - update_shaders() + if ! (new_value is float): + update_shaders() func _on_color_changed(new_color, variable) -> void: ignore_parameter_change = variable diff --git a/material_maker/preview/preview_2d.gd b/material_maker/preview/preview_2d.gd index 46e29a3..09e6d11 100644 --- a/material_maker/preview/preview_2d.gd +++ b/material_maker/preview/preview_2d.gd @@ -5,15 +5,12 @@ export(String, MULTILINE) var shader : String = "" var generator : MMGenBase = null func set_generator(g : MMGenBase) -> void: - if generator != null and is_instance_valid(generator): - generator.disconnect("float_param_changed", self, "on_float_param_changed") var source = { defs="", code="", textures={}, type="f", f="1.0" } if is_instance_valid(g): generator = g var param_defs : Array = generator.get_parameter_defs() for c in get_children(): c.setup_control(generator, param_defs) - generator.connect("float_param_changed", self, "on_float_param_changed") var gen_output_defs = generator.get_output_defs() if ! gen_output_defs.empty(): var context : MMGenContext = MMGenContext.new() @@ -27,6 +24,9 @@ func set_generator(g : MMGenBase) -> void: for c in get_children(): c.setup_control(generator, []) material.shader.code = MMGenBase.generate_preview_shader(source, source.type, shader) + if source.has("textures"): + for k in source.textures.keys(): + material.set_shader_param(k, source.textures[k]) func on_float_parameter_changed(n : String, v : float) -> void: material.set_shader_param(n, v) diff --git a/material_maker/preview/preview_3d.gd b/material_maker/preview/preview_3d.gd index 2fefb78..eec556b 100644 --- a/material_maker/preview/preview_3d.gd +++ b/material_maker/preview/preview_3d.gd @@ -1,9 +1,5 @@ extends ViewportContainer -const ENVIRONMENTS = [ - "experiment", "lobby", "night", "park", "schelde" -] - const CAMERA_DISTANCE_MIN = 1.0 const CAMERA_DISTANCE_MAX = 10.0 diff --git a/material_maker/preview/preview_3d.tscn b/material_maker/preview/preview_3d.tscn index 27d1643..cc646e8 100644 --- a/material_maker/preview/preview_3d.tscn +++ b/material_maker/preview/preview_3d.tscn @@ -3,10 +3,11 @@ [ext_resource path="res://material_maker/preview/preview_3d.gd" type="Script" id=1] [ext_resource path="res://material_maker/preview/preview_3d_scene.tscn" type="PackedScene" id=2] - [sub_resource type="World" id=1] -[node name="Preview3D" type="ViewportContainer"] +[node name="Preview3D" type="ViewportContainer" groups=[ +"preview", +]] visible = false anchor_right = 1.0 anchor_bottom = 1.0