mirror of
https://github.com/Relintai/material-maker.git
synced 2024-12-23 21:16:54 +01:00
Gradient and serialization related updates
Added a class for gradient (that handles serialization and shader generation), and updated the GradientEditor. Added a "Types" class that handles values serialization. Updated linked_control and config_control to link to GradientEditors.
This commit is contained in:
parent
95a737029a
commit
9dc6d4b18a
@ -20,6 +20,8 @@ var generated_variants = []
|
||||
|
||||
var property_widgets = []
|
||||
|
||||
const Types = preload("res://addons/material_maker/types/types.gd")
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
@ -46,6 +48,11 @@ func initialize_properties(object_list):
|
||||
elif o is ColorPickerButton:
|
||||
set(o.name, o.color)
|
||||
o.connect("color_changed", self, "_on_color_changed", [ o.name ])
|
||||
elif o is Control and o.filename == "res://addons/material_maker/widgets/gradient_editor.tscn":
|
||||
set(o.name, o.value)
|
||||
o.connect("updated", self, "_on_gradient_changed", [ o.name ])
|
||||
else:
|
||||
print("unsupported widget "+str(o))
|
||||
|
||||
func get_seed():
|
||||
return int(offset.x)*3+int(offset.y)*5
|
||||
@ -64,6 +71,10 @@ func update_property_widgets():
|
||||
o.pressed = get(o.name)
|
||||
elif o is ColorPickerButton:
|
||||
o.color = get(o.name)
|
||||
elif o is Control and o.filename == "res://addons/material_maker/widgets/gradient_editor.tscn":
|
||||
o.value = get(o.name)
|
||||
else:
|
||||
print("Failed to update "+o.name)
|
||||
|
||||
func update_shaders():
|
||||
get_parent().send_changed_signal()
|
||||
@ -80,6 +91,10 @@ func _on_color_changed(new_color, variable):
|
||||
set(variable, new_color)
|
||||
update_shaders()
|
||||
|
||||
func _on_gradient_changed(new_gradient, variable):
|
||||
set(variable, new_gradient)
|
||||
update_shaders()
|
||||
|
||||
func get_source(index = 0):
|
||||
for c in get_parent().get_connection_list():
|
||||
if c.to == name and c.to_port == index:
|
||||
@ -153,6 +168,10 @@ func deserialize_element(e):
|
||||
if typeof(e) == TYPE_DICTIONARY:
|
||||
if e.has("type") and e.type == "Color":
|
||||
return Color(e.r, e.g, e.b, e.a)
|
||||
elif typeof(e) == TYPE_ARRAY:
|
||||
var gradient = preload("res://addons/material_maker/types/gradient.gd").new()
|
||||
gradient.deserialize(e)
|
||||
return gradient
|
||||
return e
|
||||
|
||||
func generate_shader(slot = 0):
|
||||
@ -169,7 +188,7 @@ func serialize():
|
||||
var data = { name=name, type=type, node_position={x=offset.x, y=offset.y} }
|
||||
for w in property_widgets:
|
||||
var v = w.name
|
||||
data[v] = serialize_element(get(v))
|
||||
data[v] = Types.serialize_value(get(v)) # serialize_element(get(v))
|
||||
return data
|
||||
|
||||
func deserialize(data):
|
||||
@ -178,7 +197,7 @@ func deserialize(data):
|
||||
for w in property_widgets:
|
||||
var variable = w.name
|
||||
if data.has(variable):
|
||||
var value = deserialize_element(data[variable])
|
||||
var value = Types.deserialize_value(data[variable]) #deserialize_element(data[variable])
|
||||
set(variable, value)
|
||||
update_property_widgets()
|
||||
|
||||
|
@ -1,6 +1,11 @@
|
||||
tool
|
||||
extends "res://addons/material_maker/node_base.gd"
|
||||
|
||||
var gradient
|
||||
|
||||
func _ready():
|
||||
initialize_properties([ $gradient ])
|
||||
|
||||
func _get_shader_code(uv):
|
||||
var rv = { defs="", code="" }
|
||||
var src = get_source()
|
||||
@ -8,7 +13,7 @@ func _get_shader_code(uv):
|
||||
return rv
|
||||
var src_code = src.get_shader_code(uv)
|
||||
if generated_variants.empty():
|
||||
rv.defs = src_code.defs+$Control.get_shader("%s_gradient" % name);
|
||||
rv.defs = src_code.defs+gradient.get_shader("%s_gradient" % name);
|
||||
var variant_index = generated_variants.find(uv)
|
||||
if variant_index == -1:
|
||||
variant_index = generated_variants.size()
|
||||
@ -17,12 +22,6 @@ func _get_shader_code(uv):
|
||||
rv.rgb = "%s_%d_rgb" % [ name, variant_index ]
|
||||
return rv
|
||||
|
||||
func serialize():
|
||||
var data = .serialize()
|
||||
data.gradient = $Control.serialize()
|
||||
return data
|
||||
|
||||
func deserialize(data):
|
||||
if data.has("gradient"):
|
||||
$Control.deserialize(data.gradient)
|
||||
.deserialize(data)
|
||||
func _on_Control_updated(v):
|
||||
gradient = v
|
||||
update_shaders()
|
||||
|
@ -6,7 +6,7 @@
|
||||
[sub_resource type="Theme" id=1]
|
||||
|
||||
|
||||
[node name="Colorize" type="GraphNode" index="0"]
|
||||
[node name="Colorize" type="GraphNode"]
|
||||
|
||||
anchor_left = 0.0
|
||||
anchor_top = 0.0
|
||||
@ -39,13 +39,13 @@ slot/0/right_color = Color( 0.5, 0.5, 1, 1 )
|
||||
script = ExtResource( 1 )
|
||||
_sections_unfolded = [ "Theme", "slot", "slot/0" ]
|
||||
|
||||
[node name="Control" parent="." index="0" instance=ExtResource( 2 )]
|
||||
[node name="gradient" parent="." index="0" instance=ExtResource( 2 )]
|
||||
|
||||
margin_left = 16.0
|
||||
margin_top = 24.0
|
||||
margin_right = 136.0
|
||||
margin_bottom = 54.0
|
||||
|
||||
[connection signal="updated" from="Control" to="." method="update_shaders"]
|
||||
[connection signal="updated" from="gradient" to="." method="_on_Control_updated"]
|
||||
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
[sub_resource type="Theme" id=1]
|
||||
|
||||
|
||||
[node name="Remote" type="GraphNode"]
|
||||
[node name="Remote" type="GraphNode" index="0"]
|
||||
|
||||
anchor_left = 0.0
|
||||
anchor_top = 0.0
|
||||
|
86
addons/material_maker/types/gradient.gd
Normal file
86
addons/material_maker/types/gradient.gd
Normal file
@ -0,0 +1,86 @@
|
||||
extends Object
|
||||
|
||||
class CustomSorter:
|
||||
static func compare(a, b):
|
||||
return a.v < b.v
|
||||
|
||||
var points = [ { v=0.0, c=Color(0.0, 0.0, 0.0) }, { v=1.0, c=Color(1.0, 1.0, 1.0) } ]
|
||||
var sorted = true
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
func duplicate():
|
||||
var copy = get_script().new()
|
||||
copy.clear()
|
||||
for p in points:
|
||||
copy.add_point(p.v, p.c)
|
||||
return copy
|
||||
|
||||
func clear():
|
||||
points.clear()
|
||||
sorted = true
|
||||
|
||||
func add_point(v, c):
|
||||
points.append({ v=v, c=c })
|
||||
sorted = false
|
||||
|
||||
func sort():
|
||||
if !sorted:
|
||||
points.sort_custom(CustomSorter, "compare")
|
||||
sorted = true
|
||||
|
||||
func get_color(x):
|
||||
sort()
|
||||
if x < points[0].v:
|
||||
return points[0].c
|
||||
var s = points.size()-1
|
||||
for i in range(s):
|
||||
if x < points[i+1].v:
|
||||
var p0 = points[i].v
|
||||
var c0 = points[i].c
|
||||
var p1 = points[i+1].v
|
||||
var c1 = points[i+1].c
|
||||
return c0 + (c1-c0) * (x-p0) / (p1-p0)
|
||||
return points[s].c
|
||||
|
||||
# get_color_in_shader
|
||||
func gcis(color):
|
||||
return "vec3(%.9f,%.9f,%.9f)" % [color.r, color.g, color.b]
|
||||
|
||||
func get_shader(name):
|
||||
sort()
|
||||
var shader
|
||||
shader = "vec3 "+name+"(float x) {\n"
|
||||
shader += " if (x < %.9f) {\n" % points[0].v
|
||||
shader += " return "+gcis(points[0].c)+";\n"
|
||||
var s = points.size()-1
|
||||
for i in range(s):
|
||||
var p0 = points[i].v
|
||||
var c0 = points[i].c
|
||||
var p1mp0 = points[i+1].v-p0
|
||||
var c1mc0 = points[i+1].c-c0
|
||||
if p1mp0 > 0:
|
||||
shader += " } else if (x < %.9f) {\n" % points[i+1].v
|
||||
shader += " return %s+x*%s;\n" % [gcis(c0-c1mc0*(p0/p1mp0)), gcis(c1mc0/p1mp0)]
|
||||
shader += " }\n"
|
||||
shader += " return "+gcis(points[s].c)+";\n"
|
||||
shader += "}\n"
|
||||
return shader
|
||||
|
||||
func serialize():
|
||||
sort()
|
||||
var rv = []
|
||||
for p in points:
|
||||
rv.append({ pos=p.v, r=p.c.r, g=p.c.g, b=p.c.b })
|
||||
rv = { type="Gradient", points=rv }
|
||||
return rv
|
||||
|
||||
func deserialize(v):
|
||||
clear()
|
||||
if typeof(v) == TYPE_ARRAY:
|
||||
for i in v:
|
||||
add_point(i.pos, Color(i.r, i.g, i.b))
|
||||
elif typeof(v) == TYPE_DICTIONARY && v.has("type") && v.type == "Gradient":
|
||||
for i in v.points:
|
||||
add_point(i.pos, Color(i.r, i.g, i.b))
|
26
addons/material_maker/types/types.gd
Normal file
26
addons/material_maker/types/types.gd
Normal file
@ -0,0 +1,26 @@
|
||||
extends Node
|
||||
|
||||
const Gradient = preload("res://addons/material_maker/types/gradient.gd")
|
||||
|
||||
static func serialize_value(value):
|
||||
if typeof(value) == TYPE_COLOR:
|
||||
return { type= "Color", r=value.r, g=value.g, b=value.b, a=value.a }
|
||||
elif typeof(value) == TYPE_OBJECT && value.has_method("serialize"):
|
||||
return value.serialize()
|
||||
return value
|
||||
|
||||
static func deserialize_value(data):
|
||||
if typeof(data) == TYPE_DICTIONARY:
|
||||
if data.has("type"):
|
||||
if data.type == "Color":
|
||||
return Color(data.r, data.g, data.b, data.a)
|
||||
elif data.type == "Gradient":
|
||||
var gradient = Gradient.new()
|
||||
gradient.deserialize(data)
|
||||
return gradient
|
||||
# in previous releases, Gradients were serialized as arrays
|
||||
elif typeof(data) == TYPE_ARRAY:
|
||||
var gradient = Gradient.new()
|
||||
gradient.deserialize(data)
|
||||
return gradient
|
||||
return data
|
@ -8,7 +8,7 @@ class GradientCursor:
|
||||
|
||||
func _ready():
|
||||
rect_position = Vector2(0, 15)
|
||||
rect_size = Vector2(WIDTH, 20)
|
||||
rect_size = Vector2(WIDTH, 15)
|
||||
|
||||
func _gui_input(ev):
|
||||
if ev is InputEventMouseButton:
|
||||
@ -17,43 +17,63 @@ class GradientCursor:
|
||||
elif ev.button_index == BUTTON_RIGHT && get_parent().get_sorted_cursors().size() > 2:
|
||||
var parent = get_parent()
|
||||
parent.remove_child(self)
|
||||
parent.update_shader()
|
||||
parent.update_value()
|
||||
queue_free()
|
||||
elif ev is InputEventMouseMotion && (ev.button_mask & 1) != 0:
|
||||
rect_position.x += ev.relative.x
|
||||
rect_position.x = min(max(0, rect_position.x), get_parent().rect_size.x-rect_size.x)
|
||||
get_parent().update_shader()
|
||||
get_parent().update_value()
|
||||
|
||||
func get_position():
|
||||
return rect_position.x / (get_parent().rect_size.x - WIDTH)
|
||||
|
||||
func set_color(c):
|
||||
color = c
|
||||
get_parent().update_shader()
|
||||
get_parent().update_value()
|
||||
|
||||
static func sort(a, b):
|
||||
if a.get_position() < b.get_position():
|
||||
return true
|
||||
return false
|
||||
|
||||
var value = null setget set_value
|
||||
|
||||
const Gradient = preload("res://addons/material_maker/types/gradient.gd")
|
||||
|
||||
signal updated
|
||||
|
||||
func _ready():
|
||||
$Gradient.material = $Gradient.material.duplicate(true)
|
||||
add_cursor(0, Color(0, 0, 0))
|
||||
add_cursor(rect_size.x-GradientCursor.WIDTH, Color(1, 1, 1))
|
||||
set_value(Gradient.new())
|
||||
|
||||
func set_value(v):
|
||||
value = v
|
||||
for c in get_children():
|
||||
if c != $Gradient:
|
||||
remove_child(c)
|
||||
c.free()
|
||||
for p in value.points:
|
||||
add_cursor(p.v*(rect_size.x-GradientCursor.WIDTH), p.c)
|
||||
update_shader()
|
||||
|
||||
func update_value():
|
||||
value.clear()
|
||||
for p in get_children():
|
||||
if p != $Gradient:
|
||||
value.add_point(p.rect_position.x/(rect_size.x-GradientCursor.WIDTH), p.color)
|
||||
update_shader()
|
||||
|
||||
func add_cursor(x, color):
|
||||
var cursor = GradientCursor.new()
|
||||
add_child(cursor)
|
||||
cursor.rect_position.x = x
|
||||
cursor.color = color
|
||||
update_shader()
|
||||
|
||||
func _gui_input(ev):
|
||||
if ev is InputEventMouseButton && ev.button_index == 1 && ev.doubleclick && ev.position.y > 15:
|
||||
var p = max(0, min(ev.position.x, rect_size.x-GradientCursor.WIDTH))
|
||||
add_cursor(p, get_color(p))
|
||||
update_value()
|
||||
|
||||
# Showing a color picker popup to change a cursor's color
|
||||
|
||||
@ -78,59 +98,12 @@ func get_sorted_cursors():
|
||||
return array
|
||||
|
||||
func get_color(x):
|
||||
var array = get_sorted_cursors()
|
||||
x = x / (rect_size.x - array[0].rect_size.x)
|
||||
if x < array[0].get_position():
|
||||
return array[0].color
|
||||
for i in range(array.size()-1):
|
||||
if x < array[i+1].get_position():
|
||||
var p0 = array[i].get_position()
|
||||
var c0 = array[i].color
|
||||
var p1 = array[i+1].get_position()
|
||||
var c1 = array[i+1].color
|
||||
return c0 + (c1-c0) * (x-p0) / (p1-p0)
|
||||
return array[array.size()-1].color
|
||||
|
||||
# get_color_in_shader
|
||||
func gcis(color):
|
||||
return "vec3(%.9f,%.9f,%.9f)" % [color.r, color.g, color.b]
|
||||
|
||||
func get_shader(name):
|
||||
var array = get_sorted_cursors()
|
||||
var shader
|
||||
shader = "vec3 "+name+"(float x) {\n"
|
||||
shader += " if (x < %.9f) {\n" % array[0].get_position()
|
||||
shader += " return "+gcis(array[0].color)+";\n"
|
||||
for i in range(array.size()-1):
|
||||
var p0 = array[i].get_position()
|
||||
var c0 = array[i].color
|
||||
var p1mp0 = array[i+1].get_position()-p0
|
||||
var c1mc0 = array[i+1].color-c0
|
||||
if p1mp0 > 0:
|
||||
shader += " } else if (x < %.9f) {\n" % array[i+1].get_position()
|
||||
shader += " return %s+x*%s;\n" % [gcis(c0-c1mc0*(p0/p1mp0)), gcis(c1mc0/p1mp0)]
|
||||
shader += " }\n"
|
||||
shader += " return "+gcis(array[array.size()-1].color)+";\n"
|
||||
shader += "}\n"
|
||||
return shader
|
||||
return value.get_color(x / (rect_size.x - GradientCursor.WIDTH))
|
||||
|
||||
func update_shader():
|
||||
var shader
|
||||
shader = "shader_type canvas_item;\n"
|
||||
shader += get_shader("gradient")
|
||||
shader += "void fragment() { COLOR = vec4(gradient((UV.x-%.9f)*%.9f), 1.0); }" % [ float(GradientCursor.WIDTH)*0.5/float(rect_size.x), rect_size.x/(rect_size.x-GradientCursor.WIDTH) ]
|
||||
shader += value.get_shader("gradient")
|
||||
shader += "void fragment() { COLOR = vec4(gradient(UV.x), 1.0); }"
|
||||
$Gradient.material.shader.set_code(shader)
|
||||
emit_signal("updated")
|
||||
|
||||
func serialize():
|
||||
var rv = []
|
||||
for c in get_sorted_cursors():
|
||||
rv.append({ pos= c.get_position(), r= c.color.r, g= c.color.g, b= c.color.b })
|
||||
return rv
|
||||
|
||||
func deserialize(v):
|
||||
for c in get_sorted_cursors():
|
||||
remove_child(c)
|
||||
c.free()
|
||||
for i in v:
|
||||
add_cursor(i.pos*(rect_size.x-GradientCursor.WIDTH), Color(i.r, i.g, i.b))
|
||||
emit_signal("updated", value)
|
||||
|
@ -26,10 +26,8 @@ anchor_left = 0.0
|
||||
anchor_top = 0.0
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
margin_left = 24.0
|
||||
margin_top = 14.0
|
||||
margin_right = 144.0
|
||||
margin_bottom = 44.0
|
||||
margin_right = 120.0
|
||||
margin_bottom = 30.0
|
||||
rect_min_size = Vector2( 120, 30 )
|
||||
rect_pivot_offset = Vector2( 0, 0 )
|
||||
rect_clip_content = false
|
||||
@ -57,7 +55,7 @@ mouse_default_cursor_shape = 0
|
||||
size_flags_horizontal = 1
|
||||
size_flags_vertical = 1
|
||||
color = Color( 1, 1, 1, 1 )
|
||||
_sections_unfolded = [ "Material" ]
|
||||
_sections_unfolded = [ "Material", "Rect" ]
|
||||
|
||||
[node name="Popup" type="Popup" parent="Gradient" index="0"]
|
||||
|
||||
|
@ -6,6 +6,8 @@ var configurations = {}
|
||||
var current = null
|
||||
onready var button = null
|
||||
|
||||
const Types = preload("res://addons/material_maker/types/types.gd")
|
||||
|
||||
func _ready():
|
||||
update_options()
|
||||
|
||||
@ -41,10 +43,16 @@ func update_options():
|
||||
func add_linked(node, widget):
|
||||
linked_widgets.append({ node=node, widget=widget })
|
||||
|
||||
func duplicate_value(value):
|
||||
if typeof(value) == TYPE_OBJECT and value.has_method("duplicate"):
|
||||
value = value.duplicate()
|
||||
return value
|
||||
|
||||
func apply_configuration(c):
|
||||
for w in configurations[c]:
|
||||
w.widget.set(WIDGETS[get_widget_type(w.widget)].value_attr , w.value)
|
||||
w.node.set(w.widget.name, w.value)
|
||||
var value = duplicate_value(w.value)
|
||||
w.widget.set(WIDGETS[get_widget_type(w.widget)].value_attr, value)
|
||||
w.node.set(w.widget.name, value)
|
||||
var graph_node = get_parent()
|
||||
while !(graph_node is GraphNode):
|
||||
graph_node = graph_node.get_parent()
|
||||
@ -53,7 +61,7 @@ func apply_configuration(c):
|
||||
func do_update_configuration(name):
|
||||
var configuration = []
|
||||
for w in linked_widgets:
|
||||
configuration.append({ node=w.node, widget=w.widget, value=w.node.get(w.widget.name) })
|
||||
configuration.append({ node=w.node, widget=w.widget, value=duplicate_value(w.node.get(w.widget.name)) })
|
||||
configurations[name] = configuration
|
||||
current = name
|
||||
update_options()
|
||||
@ -90,7 +98,7 @@ func serialize():
|
||||
var c = configurations[k]
|
||||
var data_configuration = []
|
||||
for e in c:
|
||||
data_configuration.append({ node=e.node.name, widget=e.widget.name, value=e.value })
|
||||
data_configuration.append({ node=e.node.name, widget=e.widget.name, value=Types.serialize_value(e.value) })
|
||||
data_configurations[k] = data_configuration
|
||||
data.configurations = data_configurations
|
||||
return data
|
||||
@ -113,7 +121,7 @@ func deserialize(data):
|
||||
if w.name == e.widget:
|
||||
widget = w
|
||||
break
|
||||
configuration.append({ node=node, widget=widget, value=e.value })
|
||||
configuration.append({ node=node, widget=widget, value=Types.deserialize_value(e.value) })
|
||||
configurations[k] = configuration
|
||||
update_options()
|
||||
|
||||
|
@ -11,6 +11,9 @@ func add_linked(node, widget):
|
||||
elif widget is ColorPickerButton:
|
||||
new_widget = ColorPickerButton.new()
|
||||
type = "ColorPickerButton"
|
||||
elif widget is Control && widget.filename == "res://addons/material_maker/widgets/gradient_editor.tscn":
|
||||
new_widget = preload("res://addons/material_maker/widgets/gradient_editor.tscn").instance()
|
||||
type = "GradientEditor"
|
||||
elif widget is HSlider:
|
||||
new_widget = HSlider.new()
|
||||
type = "HSlider"
|
||||
@ -45,6 +48,11 @@ func _on_item_selected(i):
|
||||
for l in linked_widgets:
|
||||
l.widget.selected = i
|
||||
l.node.set(l.widget.name, i)
|
||||
|
||||
func _on_gradient_updated(i):
|
||||
for l in linked_widgets:
|
||||
l.widget.value = i
|
||||
l.node.set(l.widget.name, i)
|
||||
|
||||
func serialize():
|
||||
var data = .serialize()
|
||||
|
@ -13,7 +13,8 @@ const WIDGETS = {
|
||||
SpinBox={ attrs=[ "min_value", "max_value", "step", "value" ], value_attr="value", sig="value_changed", sig_handler="_on_value_changed" },
|
||||
HSlider={ attrs=[ "min_value", "max_value", "step", "value" ], value_attr="value", sig="value_changed", sig_handler="_on_value_changed" },
|
||||
ColorPickerButton={ attrs=[ "edit_alpha", "color" ], value_attr="color", sig="color_changed", sig_handler="_on_color_changed" },
|
||||
OptionButton={ attrs=[ "selected" ], value_attr="selected", sig="item_selected", sig_handler="_on_item_selected" }
|
||||
OptionButton={ attrs=[ "selected" ], value_attr="selected", sig="item_selected", sig_handler="_on_item_selected" },
|
||||
GradientEditor={ attrs=[ "value" ], value_attr="value", sig="updated", sig_handler="_on_gradient_updated" }
|
||||
}
|
||||
|
||||
func get_widget_type(widget):
|
||||
@ -21,6 +22,10 @@ func get_widget_type(widget):
|
||||
return "SpinBox"
|
||||
elif widget is ColorPickerButton:
|
||||
return "ColorPickerButton"
|
||||
elif widget is Control && widget.filename == "res://addons/material_maker/widgets/gradient_editor.tscn":
|
||||
return "GradientEditor"
|
||||
elif widget is HSlider:
|
||||
return "HSlider"
|
||||
elif widget is OptionButton:
|
||||
return "OptionButton"
|
||||
else:
|
||||
|
Loading…
Reference in New Issue
Block a user