From 9ab88205984a976ec5aac0af7f86cd223f200bbe Mon Sep 17 00:00:00 2001 From: RodZill4 Date: Thu, 26 Mar 2020 08:56:55 +0100 Subject: [PATCH] Optimized gradient parameters (passed as floats when possible) --- addons/material_maker/engine/gen_base.gd | 31 +++++++- addons/material_maker/engine/gen_shader.gd | 7 +- addons/material_maker/types/gradient.gd | 87 +++++++++++++--------- material_maker/nodes/generic.gd | 1 - material_maker/preview/preview_2d.gd | 2 +- material_maker/widgets/gradient_editor.gd | 7 +- 6 files changed, 89 insertions(+), 46 deletions(-) diff --git a/addons/material_maker/engine/gen_base.gd b/addons/material_maker/engine/gen_base.gd index 05f1b2d..77546ec 100644 --- a/addons/material_maker/engine/gen_base.gd +++ b/addons/material_maker/engine/gen_base.gd @@ -114,15 +114,38 @@ func get_parameter(n : String): else: return get_parameter_def(n).default +class CustomGradientSorter: + static func compare(a, b) -> bool: + return a.pos < b.pos + func set_parameter(n : String, v) -> void: + var old_value = parameters[n] if parameters.has(n) else null parameters[n] = v 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) + if parameter_def.has("type"): + if parameter_def.type == "float": + var parameter_name = "p_o%s_%s" % [ str(get_instance_id()), n ] + get_tree().call_group("preview", "on_float_parameter_changed", parameter_name, v) + return + elif parameter_def.type == "gradient": + if v.interpolation == old_value.interpolation && v.points.size() == old_value.points.size(): + # convert from old format + for i in range(old_value.points.size()): + if old_value.points[i].has("v"): + var old = old_value.points[i] + old_value.points[i] = { pos=old.v, r=old.c.r, g=old.c.g, b=old.c.b, a=old.c.a } + old_value.points.sort_custom(CustomGradientSorter, "compare") + v.points.sort_custom(CustomGradientSorter, "compare") + for i in range(old_value.points.size()): + for f in [ "pos", "r", "g", "b", "a" ]: + if v.points[i][f] != old_value.points[i][f]: + var parameter_name = "p_o%s_%s_%d_%s" % [ str(get_instance_id()), n, i, f ] + get_tree().call_group("preview", "on_float_parameter_changed", parameter_name, v.points[i][f]) + return + print("regenerating shader") + 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_shader.gd b/addons/material_maker/engine/gen_shader.gd index 1e7fcc7..2139812 100644 --- a/addons/material_maker/engine/gen_shader.gd +++ b/addons/material_maker/engine/gen_shader.gd @@ -200,7 +200,7 @@ func subst(string : String, context : MMGenContext, uv : String = "") -> Diction elif p.type == "color": value_string = "vec4(%.9f, %.9f, %.9f, %.9f)" % [ value.r, value.g, value.b, value.a ] elif p.type == "gradient": - value_string = genname+"_p_"+p.name+"_gradient_fct" + value_string = genname+"_"+p.name+"_gradient_fct" elif p.type == "boolean": value_string = "true" if value else "false" else: @@ -258,7 +258,10 @@ func _get_shader_code(uv : String, output_index : int, context : MMGenContext) - if !(g is MMGradient): g = MMGradient.new() g.deserialize(parameters[p.name]) - rv.defs += g.get_shader(genname+"_p_"+p.name+"_gradient_fct") + var params = g.get_shader_params(genname+"_"+p.name) + for sp in params.keys(): + rv.defs += "uniform float "+sp+" = "+str(params[sp])+";\n" + rv.defs += g.get_shader(genname+"_"+p.name) # Generate functions for inputs if shader_model.has("inputs"): for i in range(shader_model.inputs.size()): diff --git a/addons/material_maker/types/gradient.gd b/addons/material_maker/types/gradient.gd index b5e0832..97892fe 100644 --- a/addons/material_maker/types/gradient.gd +++ b/addons/material_maker/types/gradient.gd @@ -33,6 +33,9 @@ func add_point(v, c) -> void: func sort() -> void: if !sorted: points.sort_custom(CustomSorter, "compare") + for i in range(points.size()-1): + if points[i].v+0.0000005 >= points[i+1].v: + points[i+1].v = points[i].v+0.000001 sorted = true func get_color(x) -> Color: @@ -52,69 +55,81 @@ func get_color(x) -> Color: else: return Color(0.0, 0.0, 0.0, 1.0) +func get_shader_params(name) -> Dictionary: + sort() + var rv = {} + for i in range(points.size()): + rv["p_"+name+"_"+str(i)+"_pos"] = points[i].v + rv["p_"+name+"_"+str(i)+"_r"] = points[i].c.r + rv["p_"+name+"_"+str(i)+"_g"] = points[i].c.g + rv["p_"+name+"_"+str(i)+"_b"] = points[i].c.b + rv["p_"+name+"_"+str(i)+"_a"] = points[i].c.a + return rv + # get_color_in_shader func gcis(color) -> String: return "vec4(%.9f,%.9f,%.9f,%.9f)" % [color.r, color.g, color.b, color.a] +func pv(name : String, i : int) -> String: + return "p_"+name+"_"+str(i)+"_pos" + +func pc(name : String, i : int) -> String: + return "vec4(p_"+name+"_"+str(i)+"_r,p_"+name+"_"+str(i)+"_g,p_"+name+"_"+str(i)+"_b,p_"+name+"_"+str(i)+"_a)" + func get_shader(name) -> String: sort() var shader - shader = "vec4 "+name+"(float x) {\n" + shader = "vec4 "+name+"_gradient_fct(float x) {\n" match interpolation: 0: if points.size() > 0: - shader += " if (x < %.9f) {\n" % (0.5*(points[0].v + points[1].v)) - shader += " return "+gcis(points[0].c)+";\n" + shader += " if (x < 0.5*(%s+%s)) {\n" % [ pv(name, 0), pv(name, 1) ] + shader += " return "+pc(name, 0)+";\n" var s = points.size()-1 - for i in range(s): - if points[i+1].v-points[i].v > 0: - shader += " } else if (x < %.9f) {\n" % (0.5*(points[i].v + points[i+1].v)) - shader += " return "+gcis(points[i].c)+";\n" + for i in range(1, s): + shader += " } else if (x < 0.5*(%s+%s)) {\n" % [ pv(name, i), pv(name, i+1) ] + shader += " return "+pc(name, i)+";\n" shader += " }\n" - shader += " return "+gcis(points[s].c)+";\n" + shader += " return "+pc(name, s)+";\n" else: shader += " return vec4(0.0, 0.0, 0.0, 1.0);\n" 1, 2: if points.size() > 0: - shader += " if (x < %.9f) {\n" % points[0].v - shader += " return "+gcis(points[0].c)+";\n" + shader += " if (x < %s) {\n" % pv(name, 0) + shader += " return "+pc(name, 0)+";\n" var s = points.size()-1 for i in range(s): - var p1mp0 = points[i+1].v-points[i].v - if p1mp0 > 0: - shader += " } else if (x < %.9f) {\n" % points[i+1].v - var function = "(" if interpolation == 1 else "0.5-0.5*cos(3.14159265359*" - shader += " return mix(%s, %s, %s(x-%.9f)/%.9f));\n" % [ gcis(points[i].c), gcis(points[i+1].c), function, points[i].v, p1mp0 ] + shader += " } else if (x < %s) {\n" % pv(name, i+1) + var function = "(" if interpolation == 1 else "0.5-0.5*cos(3.14159265359*" + shader += " return mix(%s, %s, %s(x-%s)/(%s-%s)));\n" % [ pc(name, i), pc(name, i+1), function, pv(name, i), pv(name, i+1), pv(name, i) ] shader += " }\n" - shader += " return "+gcis(points[s].c)+";\n" + shader += " return "+pc(name, s)+";\n" else: shader += " return vec4(0.0, 0.0, 0.0, 1.0);\n" 3: if points.size() > 0: - shader += " if (x < %.9f) {\n" % points[0].v - shader += " return "+gcis(points[0].c)+";\n" + shader += " if (x < %s) {\n" % pv(name, 0) + shader += " return "+pc(name, 0)+";\n" var s = points.size()-1 for i in range(s): - var p1mp0 = points[i+1].v-points[i].v - if p1mp0 > 0: - shader += " } else if (x < %.9f) {\n" % points[i+1].v - var dx : String = "(x-%.9f)/%.9f" % [ points[i].v, p1mp0 ] - var b : String = "mix(%s, %s, %s)" % [ gcis(points[i].c), gcis(points[i+1].c), dx ] - if i > 0 and points[i-1].v < points[i].v: - var a : String = "mix(%s, %s, (x-%.9f)/%.9f)" % [ gcis(points[i-1].c), gcis(points[i].c), points[i-1].v, points[i].v-points[i-1].v ] - if i < s-1 and points[i+1].v < points[i+2].v: - var c : String = "mix(%s, %s, (x-%.9f)/%.9f)" % [ gcis(points[i+1].c), gcis(points[i+2].c), points[i+1].v, points[i+2].v-points[i+1].v ] - var ac : String = "mix("+a+", "+c+", 0.5-0.5*cos(3.14159265359*"+dx+"))" - shader += " return 0.5*("+b+" + "+ac+");\n" - else: - shader += " return mix("+a+", "+b+", 0.5+0.5*"+dx+");\n" - elif i < s-1 and points[i+1].v < points[i+2].v: - var c : String = "mix(%s, %s, (x-%.9f)/%.9f)" % [ gcis(points[i+1].c), gcis(points[i+2].c), points[i+1].v, points[i+2].v-points[i+1].v ] - shader += " return mix("+c+", "+b+", 1.0-0.5*"+dx+");\n" + shader += " } else if (x < %s) {\n" % pv(name, i+1) + var dx : String = "(x-%s)/(%s-%s)" % [ pv(name, i), pv(name, i+1), pv(name, i) ] + var b : String = "mix(%s, %s, %s)" % [ pc(name, i), pc(name, i+1), dx ] + if i > 0: + var a : String = "mix(%s, %s, (x-%s)/(%s-%s))" % [ pc(name, i-1), pc(name, i), pv(name, i-1), pv(name, i), pv(name, i-1) ] + if i < s-1: + var c : String = "mix(%s, %s, (x-%s)/(%s-%s))" % [ pc(name, i+1), pc(name, i+2), pv(name, i+1), pv(name, i+2), pv(name, i+1) ] + var ac : String = "mix("+a+", "+c+", 0.5-0.5*cos(3.14159265359*"+dx+"))" + shader += " return 0.5*("+b+" + "+ac+");\n" else: - shader += " return "+b+";\n" + shader += " return mix("+a+", "+b+", 0.5+0.5*"+dx+");\n" + elif i < s-1: + var c : String = "mix(%s, %s, (x-%s)/(%s-%s))" % [ pc(name, i+1), pc(name, i+2), pv(name, i+1), pv(name, i+2), pv(name, i+1) ] + shader += " return mix("+c+", "+b+", 1.0-0.5*"+dx+");\n" + else: + shader += " return "+b+";\n" shader += " }\n" - shader += " return "+gcis(points[s].c)+";\n" + shader += " return "+pc(name, s)+";\n" else: shader += " return vec4(0.0, 0.0, 0.0, 1.0);\n" _: diff --git a/material_maker/nodes/generic.gd b/material_maker/nodes/generic.gd index 1610519..27e0efd 100644 --- a/material_maker/nodes/generic.gd +++ b/material_maker/nodes/generic.gd @@ -109,7 +109,6 @@ func _on_gradient_changed(new_gradient, variable) -> void: ignore_parameter_change = variable generator.set_parameter(variable, MMType.serialize_value(new_gradient)) ignore_parameter_change = "" - update_shaders() func create_parameter_control(p : Dictionary) -> Control: var control = null diff --git a/material_maker/preview/preview_2d.gd b/material_maker/preview/preview_2d.gd index b40ac60..6f0d8ac 100644 --- a/material_maker/preview/preview_2d.gd +++ b/material_maker/preview/preview_2d.gd @@ -46,7 +46,7 @@ func on_parameter_changed(n : String, v) -> void: var p = generator.get_parameter_def(n) if p.has("type"): match p.type: - "float": + "float", "gradient": pass _: set_generator(generator, output) diff --git a/material_maker/widgets/gradient_editor.gd b/material_maker/widgets/gradient_editor.gd index 839fc08..cd21446 100644 --- a/material_maker/widgets/gradient_editor.gd +++ b/material_maker/widgets/gradient_editor.gd @@ -147,8 +147,11 @@ func get_gradient_color(x) -> Color: func update_shader() -> void: var shader shader = "shader_type canvas_item;\n" - shader += value.get_shader("gradient") - shader += "void fragment() { COLOR = gradient(UV.x); }" + var params = value.get_shader_params("") + for sp in params.keys(): + shader += "uniform float "+sp+" = "+str(params[sp])+";\n" + shader += value.get_shader("") + shader += "void fragment() { COLOR = _gradient_fct(UV.x); }" $Gradient.material.shader.set_code(shader) emit_signal("updated", value)