material-maker/addons/procedural_material/widgets/gradient_editor.gd

123 lines
3.6 KiB
GDScript3
Raw Normal View History

extends Control
class GradientCursor:
extends ColorRect
const WIDTH = 10
func _ready():
rect_position = Vector2(0, 15)
rect_size = Vector2(WIDTH, 20)
func _gui_input(ev):
if ev is InputEventMouseButton && ev.button_index == 1 && ev.doubleclick:
get_parent().select_color(self)
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()
func get_position():
return rect_position.x / (get_parent().rect_size.x - WIDTH)
func set_color(c):
color = c
get_parent().update_shader()
static func sort(a, b):
if a.get_position() < b.get_position():
return true
return false
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))
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:
add_cursor(ev.position.x, get_color(ev.position.x))
# Showing a color picker popup to change a cursor's color
var active_cursor
func select_color(cursor):
active_cursor = cursor
$Gradient/Popup/ColorPicker.connect("color_changed", cursor, "set_color")
$Gradient/Popup.popup()
func _on_Popup_popup_hide():
$Gradient/Popup/ColorPicker.disconnect("color_changed", active_cursor, "set_color")
# Calculating a color from the gradient and generating the shader
func get_sorted_cursors():
var array = get_children()
array.erase($Gradient)
array.sort_custom(GradientCursor, "sort")
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(%.1f,%.1f,%.1f)" % [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 < %.1f) {\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 < %.1f) {\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
func update_shader():
var shader
shader = "shader_type canvas_item;\n"
shader += get_shader("gradient")
shader += "void fragment() { COLOR = vec4(gradient((UV.x-%.1f)*%.1f), 1.0); }" % [ 0.5*GradientCursor.WIDTH/rect_size.x, (rect_size.x-GradientCursor.WIDTH)/rect_size.x ]
$Gradient.material.shader.set_code(shader)
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():
queue_free(c)
for i in v:
add_cursor(i.pos*(rect_size.x-GradientCursor.WIDTH), Color(i.r, i.g, i.b))