diff --git a/src/Classes/ShaderLoader.gd b/src/Classes/ShaderLoader.gd index 68f342967..3e0a755a3 100644 --- a/src/Classes/ShaderLoader.gd +++ b/src/Classes/ShaderLoader.gd @@ -19,6 +19,9 @@ static func create_ui_for_shader_uniforms( var uniform_data: PackedStringArray = [] var description: String = "" var description_began := false + # A Dictionary of [String] and [Control], used to group together nodes + # under the same group_uniform. Currently only used for CurveTextures. + var group_nodes := {} var color_button_hbox: HBoxContainer = null # Used for RGBA buttons, if they exist. for line in code: # Management of "end" tags @@ -28,7 +31,7 @@ static func create_ui_for_shader_uniforms( description += "\n" + line.strip_edges() # Detection of uniforms - if line.begins_with("uniform"): + if line.begins_with("uniform") or line.begins_with("group_uniforms"): uniforms.append(line) if line.begins_with("// uniform_data"): uniform_data.append(line) @@ -45,6 +48,7 @@ static func create_ui_for_shader_uniforms( "Description:\n", description.replace("//", "").strip_edges() ) + var current_group := "" for uniform in uniforms: # Example uniform: # uniform float parameter_name : hint_range(0, 255) = 100.0; @@ -62,6 +66,10 @@ static func create_ui_for_shader_uniforms( u_hint = u_hint.replace(";", "") var u_init := u_left_side[0].split(" ") + var uniform_string := u_init[0] + if uniform_string == "group_uniforms": + current_group = u_init[1] + continue var u_type := u_init[1] var u_name := u_init[2] if u_name in ["PXO_time", "PXO_frame_index", "PXO_layer_index"]: @@ -237,11 +245,13 @@ static func create_ui_for_shader_uniforms( func(): _shader_update_palette_texture(palette, value_changed, u_name) ) continue - var label := Label.new() - label.text = humanized_u_name - label.size_flags_horizontal = Control.SIZE_EXPAND_FILL var hbox := HBoxContainer.new() - hbox.add_child(label) + if not (u_name.begins_with("curve_") and not current_group.is_empty()): + var label := Label.new() + label.text = humanized_u_name + label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + hbox.add_child(label) + parent_node.add_child(hbox) if shader is VisualShader and u_name.begins_with("tex_frg_"): var node_id := int(u_name.replace("tex_frg_", "")) var shader_node := (shader as VisualShader).get_node( @@ -263,14 +273,50 @@ static func create_ui_for_shader_uniforms( elif u_name.begins_with("gradient_"): _create_gradient_texture_ui(params, u_name, hbox, value_changed) elif u_name.begins_with("curve_"): - _create_curve_texture_ui(params, u_name, hbox, value_changed) + if current_group.is_empty(): + _create_curve_texture_ui(params, u_name, hbox, value_changed) + else: + # If this curve uniform belongs in a group, group them into the same + # CurveEdit node and use an OptionButton to switch between the different curves. + var group_option_button_str := current_group + "_option_button" + if group_nodes.has(group_option_button_str): + # Add it to the current group CurveEdit and OptionButton. + var option_button := group_nodes[group_option_button_str] as OptionButton + if not params.has(u_name): + var new_curve := Curve.new() + # Set linear preset to the new curve + CurveEdit.set_curve_preset(new_curve, 0) + params[u_name] = CurveEdit.to_texture(new_curve) + option_button.add_item(Keychain.humanize_snake_case(u_name.replace("curve_", ""))) + option_button.set_item_metadata(option_button.item_count - 1, u_name) + else: # Create a the group's CurveEdit and OptionButton. + var option_button := OptionButton.new() + parent_node.add_child(option_button) + var curve_edit := _create_curve_texture_ui(params, u_name, parent_node, value_changed) + option_button.add_item(Keychain.humanize_snake_case(u_name.replace("curve_", ""))) + option_button.set_item_metadata(option_button.item_count - 1, u_name) + option_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL + option_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + option_button.item_selected.connect( + # Disconnect all previous connections from the + # curve_edit's value_changed signal. Then change the curve, and then + # connect a new callable to the value_changed signal. + func(index: int): + var new_uniform_name = option_button.get_item_metadata(index) + for connection in curve_edit.value_changed.get_connections(): + curve_edit.value_changed.disconnect(connection.callable) + curve_edit.curve = params[new_uniform_name].curve + curve_edit.value_changed.connect( + func(curve: Curve): value_changed.call(CurveEdit.to_texture(curve), new_uniform_name) + ) + ) + group_nodes[group_option_button_str] = option_button elif u_name.begins_with("noise_"): _create_noise_texture_ui(params, u_name, hbox, value_changed, parent_node) else: # Simple texture _create_simple_texture_ui( params, u_name, hbox, value_changed, parent_node, file_selected ) - parent_node.add_child(hbox) elif u_type == "bool": var button: BaseButton if u_name in ["red", "green", "blue", "alpha"]: @@ -436,8 +482,8 @@ static func _create_gradient_texture_ui( static func _create_curve_texture_ui( - params: Dictionary, u_name: String, hbox: BoxContainer, value_changed: Callable -) -> void: + params: Dictionary, u_name: String, hbox: Control, value_changed: Callable +) -> CurveEdit: var curve_edit := CurveEdit.new() curve_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL if params.has(u_name) and params[u_name] is CurveTexture: @@ -449,6 +495,7 @@ static func _create_curve_texture_ui( func(curve: Curve): value_changed.call(CurveEdit.to_texture(curve), u_name) ) hbox.add_child(curve_edit) + return curve_edit static func _create_noise_texture_ui( diff --git a/src/Shaders/Effects/ColorCurves.gdshader b/src/Shaders/Effects/ColorCurves.gdshader index bd36d04c6..6d5e6ba1a 100644 --- a/src/Shaders/Effects/ColorCurves.gdshader +++ b/src/Shaders/Effects/ColorCurves.gdshader @@ -1,6 +1,8 @@ shader_type canvas_item; +uniform sampler2D selection : filter_nearest; // CurveTexture(s) +group_uniforms channel; uniform sampler2D curve_rgb; uniform sampler2D curve_red; uniform sampler2D curve_green; @@ -9,7 +11,6 @@ uniform sampler2D curve_alpha; uniform sampler2D curve_hue; uniform sampler2D curve_sat; uniform sampler2D curve_value; -uniform sampler2D selection : filter_nearest; vec3 rgb2hsb(vec3 c) { diff --git a/src/UI/Nodes/CurveEditor/CurveEdit.gd b/src/UI/Nodes/CurveEditor/CurveEdit.gd index 1a4571f8e..305d1d182 100644 --- a/src/UI/Nodes/CurveEditor/CurveEdit.gd +++ b/src/UI/Nodes/CurveEditor/CurveEdit.gd @@ -18,7 +18,7 @@ signal value_changed(value: Curve) update_controls() ## Array of dictionaries of key [String] and value [Array] of type [CurveEdit.CurvePoint]. -var presets: Array[Dictionary] = [ +static var presets: Array[Dictionary] = [ {"Linear": [CurvePoint.new(0.0, 0.0, 0.0, 1.0), CurvePoint.new(1.0, 1.0, 1.0, 0.0)]}, { "Ease out": @@ -154,6 +154,13 @@ static func to_texture(from_curve: Curve, width := 256) -> CurveTexture: return texture +static func set_curve_preset(curve_to_edit: Curve, preset_index: int) -> void: + curve_to_edit.clear_points() + var preset_points: Array = presets[preset_index].values()[0] + for point: CurvePoint in preset_points: + curve_to_edit.add_point(point.pos, point.left_tangent, point.right_tangent) + + func set_default_curve() -> void: if not is_instance_valid(curve): curve = Curve.new() @@ -256,10 +263,7 @@ func _on_resize() -> void: func _on_presets_item_selected(index: int) -> void: - curve.clear_points() - var preset_points: Array = presets[index].values()[0] - for point: CurvePoint in preset_points: - curve.add_point(point.pos, point.left_tangent, point.right_tangent) + set_curve_preset(curve, index) curve = curve # Call setter