diff --git a/src/Autoload/Global.gd b/src/Autoload/Global.gd index 5cea961ca..783d60e76 100644 --- a/src/Autoload/Global.gd +++ b/src/Autoload/Global.gd @@ -118,6 +118,7 @@ const CONFIG_SUBDIR_NAME := "pixelorama_data" ## The path of the directory where the UI layouts are being stored. const LAYOUT_DIR := "user://layouts" const VALUE_SLIDER_V2_TSCN := preload("res://src/UI/Nodes/ValueSliderV2.tscn") +const BASIS_SLIDERS_TSCN := preload("res://src/UI/Nodes/BasisSliders.tscn") const GRADIENT_EDIT_TSCN := preload("res://src/UI/Nodes/GradientEdit.tscn") ## It is path to the executable's base drectory. @@ -1214,6 +1215,25 @@ func create_ui_for_shader_uniforms( hbox.add_child(label) hbox.add_child(color_button) parent_node.add_child(hbox) + elif u_type == "mat3": + var label := Label.new() + label.text = humanized_u_name + label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + var basis := _mat3str_to_basis(u_value) + var sliders := BASIS_SLIDERS_TSCN.instantiate() as BasisSliders + sliders.allow_greater = true + sliders.allow_lesser = true + sliders.size_flags_horizontal = Control.SIZE_EXPAND_FILL + sliders.value = basis + if params.has(u_name): + sliders.value = params[u_name] + else: + params[u_name] = sliders.value + sliders.value_changed.connect(value_changed.bind(u_name)) + var hbox := HBoxContainer.new() + hbox.add_child(label) + hbox.add_child(sliders) + parent_node.add_child(hbox) elif u_type == "sampler2D": if u_name == "selection": continue @@ -1299,6 +1319,24 @@ func _vec2str_to_vector2(vec2: String) -> Vector2: return vector2 +func _vec3str_to_vector3(vec3: String) -> Vector3: + vec3 = vec3.replace("uvec3", "vec3") + vec3 = vec3.replace("ivec3", "vec3") + vec3 = vec3.replace("vec3(", "") + vec3 = vec3.replace(")", "") + var vec_values := vec3.split(",") + if vec_values.size() == 0: + return Vector3.ZERO + var y := float(vec_values[0]) + var z := float(vec_values[0]) + if vec_values.size() >= 2: + y = float(vec_values[1]) + if vec_values.size() == 3: + z = float(vec_values[2]) + var vector3 := Vector3(float(vec_values[0]), y, z) + return vector3 + + func _vec4str_to_color(vec4: String) -> Color: vec4 = vec4.replace("vec4(", "") vec4 = vec4.replace(")", "") @@ -1320,6 +1358,24 @@ func _vec4str_to_color(vec4: String) -> Color: return color +func _mat3str_to_basis(mat3: String) -> Basis: + mat3 = mat3.replace("mat3(", "") + mat3 = mat3.replace("))", ")") + mat3 = mat3.replace("), ", ")") + var vec3_values := mat3.split("vec3", false) + var vec3_x := _vec3str_to_vector3(vec3_values[0]) + + var vec3_y := _vec3str_to_vector3(vec3_values[0]) + if vec3_values.size() >= 2: + vec3_y = _vec3str_to_vector3(vec3_values[1]) + + var vec3_z := _vec3str_to_vector3(vec3_values[0]) + if vec3_values.size() == 3: + vec3_z = _vec3str_to_vector3(vec3_values[2]) + var basis := Basis(vec3_x, vec3_y, vec3_z) + return basis + + func _shader_change_palette(value_changed: Callable, parameter_name: String) -> void: var palette := Palettes.current_palette _shader_update_palette_texture(palette, value_changed, parameter_name) diff --git a/src/Shaders/Effects/ConvolutionMatrix.gdshader b/src/Shaders/Effects/ConvolutionMatrix.gdshader new file mode 100644 index 000000000..bb709eeba --- /dev/null +++ b/src/Shaders/Effects/ConvolutionMatrix.gdshader @@ -0,0 +1,82 @@ +shader_type canvas_item; + +// author : csblo +// Work made just by consulting : +// https://en.wikipedia.org/wiki/Kernel_(image_processing) + +uniform mat3 kernel = mat3(vec3(0, 0, 0), vec3(0, 1, 0), vec3(0, 0, 0)); + +// Find coordinate of matrix element from index +vec2 kpos(int index, vec2 iResolution) +{ + return vec2[9] ( + vec2(-1, -1), vec2(0, -1), vec2(1, -1), + vec2(-1, 0), vec2(0, 0), vec2(1, 0), + vec2(-1, 1), vec2(0, 1), vec2(1, 1) + )[index] / iResolution.xy; +} + + +// Extract region of dimension 3x3 from sampler centered in uv +// sampler : texture sampler +// uv : current coordinates on sampler +// return : an array of mat3, each index corresponding with a color channel +mat3[3] region3x3(sampler2D sampler, vec2 uv, vec2 iResolution) +{ + // Create each pixels for region + vec4[9] region; + + for (int i = 0; i < 9; i++) + region[i] = texture(sampler, uv + kpos(i, iResolution)); + + // Create 3x3 region with 3 color channels (red, green, blue) + mat3[3] mRegion; + + for (int i = 0; i < 3; i++) + mRegion[i] = mat3( + vec3(region[0][i], region[1][i], region[2][i]), + vec3(region[3][i], region[4][i], region[5][i]), + vec3(region[6][i], region[7][i], region[8][i]) + ); + + return mRegion; +} + +// Convolve a texture with kernel +// kernel : kernel used for convolution +// sampler : texture sampler +// uv : current coordinates on sampler +vec3 convolution(sampler2D sampler, vec2 uv, vec2 iResolution) +{ + vec3 fragment; + + // Extract a 3x3 region centered in uv + mat3[3] region = region3x3(sampler, uv, iResolution); + + // for each color channel of region + for (int i = 0; i < 3; i++) + { + // get region channel + mat3 rc = region[i]; + // component wise multiplication of kernel by region channel + mat3 c = matrixCompMult(kernel, rc); + // add each component of matrix + float r = c[0][0] + c[1][0] + c[2][0] + + c[0][1] + c[1][1] + c[2][1] + + c[0][2] + c[1][2] + c[2][2]; + + // for fragment at channel i, set result + fragment[i] = r; + } + + return fragment; +} + +void fragment() +{ + // Convolve kernel with texture + vec3 col = convolution(TEXTURE, UV, 1.0 / TEXTURE_PIXEL_SIZE); + + // Output to screen + COLOR.rgb = col; +} diff --git a/src/UI/Nodes/BasisSliders.gd b/src/UI/Nodes/BasisSliders.gd new file mode 100644 index 000000000..b81096dd3 --- /dev/null +++ b/src/UI/Nodes/BasisSliders.gd @@ -0,0 +1,65 @@ +@tool +class_name BasisSliders +extends HBoxContainer + +signal value_changed(value: Basis) + +@export var value: Basis: + set(val): + value = val + _can_emit_signal = false + get_sliders()[0].value = value.x + get_sliders()[1].value = value.y + get_sliders()[2].value = value.z + _can_emit_signal = true +@export var min_value := Vector3.ZERO: + set(val): + min_value = val + get_sliders()[0].min_value = val + get_sliders()[1].min_value = val + get_sliders()[2].min_value = val +@export var max_value := Vector3(100.0, 100.0, 100.0): + set(val): + max_value = val + get_sliders()[0].max_value = val + get_sliders()[1].max_value = val + get_sliders()[2].max_value = val +@export var step := 1.0: + set(val): + step = val + for slider in get_sliders(): + slider.step = val +@export var allow_greater := false: + set(val): + allow_greater = val + for slider in get_sliders(): + slider.allow_greater = val +@export var allow_lesser := false: + set(val): + allow_lesser = val + for slider in get_sliders(): + slider.allow_lesser = val + +var _can_emit_signal := true + + +func get_sliders() -> Array[ValueSliderV3]: + return [$XSlider, $YSlider, $ZSlider] + + +func _on_x_slider_value_changed(val: Vector3) -> void: + value.x = val + if _can_emit_signal: + value_changed.emit(value) + + +func _on_y_slider_value_changed(val: Vector3) -> void: + value.y = val + if _can_emit_signal: + value_changed.emit(value) + + +func _on_z_slider_value_changed(val: Vector3) -> void: + value.z = val + if _can_emit_signal: + value_changed.emit(value) diff --git a/src/UI/Nodes/BasisSliders.tscn b/src/UI/Nodes/BasisSliders.tscn new file mode 100644 index 000000000..299ab83d4 --- /dev/null +++ b/src/UI/Nodes/BasisSliders.tscn @@ -0,0 +1,40 @@ +[gd_scene load_steps=3 format=3 uid="uid://d0d66oh6bw3kt"] + +[ext_resource type="Script" path="res://src/UI/Nodes/BasisSliders.gd" id="1_sbf5t"] +[ext_resource type="PackedScene" uid="uid://dpoteid430evf" path="res://src/UI/Nodes/ValueSliderV3.tscn" id="2_7swri"] + +[node name="BasisSliders" type="HBoxContainer"] +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("1_sbf5t") + +[node name="XSlider" parent="." instance=ExtResource("2_7swri")] +layout_mode = 2 +size_flags_horizontal = 3 +value = Vector3(1, 0, 0) +slider_min_size = Vector2(64, 24) +prefix_x = "XX:" +prefix_y = "YX:" +prefix_z = "ZX:" + +[node name="YSlider" parent="." instance=ExtResource("2_7swri")] +layout_mode = 2 +size_flags_horizontal = 3 +value = Vector3(0, 1, 0) +slider_min_size = Vector2(64, 24) +prefix_x = "XY:" +prefix_y = "YY:" +prefix_z = "ZY:" + +[node name="ZSlider" parent="." instance=ExtResource("2_7swri")] +layout_mode = 2 +size_flags_horizontal = 3 +value = Vector3(0, 0, 1) +slider_min_size = Vector2(64, 24) +prefix_x = "XZ:" +prefix_y = "YZ:" +prefix_z = "ZZ:" + +[connection signal="value_changed" from="XSlider" to="." method="_on_x_slider_value_changed"] +[connection signal="value_changed" from="YSlider" to="." method="_on_y_slider_value_changed"] +[connection signal="value_changed" from="ZSlider" to="." method="_on_z_slider_value_changed"] diff --git a/src/UI/Nodes/ValueSliderV3.gd b/src/UI/Nodes/ValueSliderV3.gd index 0c9f44874..e1ba93c0c 100644 --- a/src/UI/Nodes/ValueSliderV3.gd +++ b/src/UI/Nodes/ValueSliderV3.gd @@ -4,8 +4,8 @@ extends HBoxContainer ## A class that combines three ValueSlider nodes, for easy usage with Vector3 values. ## Also supports aspect ratio locking. -signal value_changed(value) -signal ratio_toggled(button_pressed) +signal value_changed(value: Vector3) +signal ratio_toggled(button_pressed: bool) @export var editable := true: set(val): diff --git a/src/UI/Nodes/ValueSliderV3.tscn b/src/UI/Nodes/ValueSliderV3.tscn index 9c96cd74d..4c0eed5d7 100644 --- a/src/UI/Nodes/ValueSliderV3.tscn +++ b/src/UI/Nodes/ValueSliderV3.tscn @@ -1,86 +1,83 @@ -[gd_scene load_steps=6 format=2] +[gd_scene load_steps=6 format=3 uid="uid://dpoteid430evf"] -[ext_resource path="res://src/UI/Nodes/ValueSlider.gd" type="Script" id=1] -[ext_resource path="res://src/UI/Nodes/ValueSliderV3.gd" type="Script" id=2] -[ext_resource path="res://assets/graphics/misc/lock_aspect_2.png" type="Texture2D" id=3] -[ext_resource path="res://assets/graphics/misc/lock_aspect_guides.png" type="Texture2D" id=4] -[ext_resource path="res://assets/graphics/misc/lock_aspect.png" type="Texture2D" id=5] +[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="1"] +[ext_resource type="Script" path="res://src/UI/Nodes/ValueSliderV3.gd" id="2"] +[ext_resource type="Texture2D" uid="uid://cancw70yw0pv7" path="res://assets/graphics/misc/lock_aspect_2.png" id="3"] +[ext_resource type="Texture2D" uid="uid://kd10jfc1dxf5" path="res://assets/graphics/misc/lock_aspect_guides.png" id="4"] +[ext_resource type="Texture2D" uid="uid://beqermx8s5q8y" path="res://assets/graphics/misc/lock_aspect.png" id="5"] [node name="ValueSliderV3" type="HBoxContainer"] offset_right = 45.0 offset_bottom = 52.0 -script = ExtResource( 2 ) +script = ExtResource("2") [node name="GridContainer" type="GridContainer" parent="."] -offset_right = 45.0 -offset_bottom = 80.0 +layout_mode = 2 size_flags_horizontal = 3 [node name="X" type="TextureProgressBar" parent="GridContainer"] -offset_right = 45.0 -offset_bottom = 24.0 -custom_minimum_size = Vector2( 32, 24 ) -mouse_default_cursor_shape = 2 +custom_minimum_size = Vector2(32, 24) +layout_mode = 2 size_flags_horizontal = 3 -theme_type_variation = "ValueSlider" +focus_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"ValueSlider" nine_patch_stretch = true stretch_margin_left = 3 stretch_margin_top = 3 stretch_margin_right = 3 stretch_margin_bottom = 3 -script = ExtResource( 1 ) +script = ExtResource("1") prefix = "X:" [node name="Y" type="TextureProgressBar" parent="GridContainer"] -offset_top = 28.0 -offset_right = 45.0 -offset_bottom = 52.0 -custom_minimum_size = Vector2( 32, 24 ) -mouse_default_cursor_shape = 2 +custom_minimum_size = Vector2(32, 24) +layout_mode = 2 size_flags_horizontal = 3 -theme_type_variation = "ValueSlider" +focus_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"ValueSlider" nine_patch_stretch = true stretch_margin_left = 3 stretch_margin_top = 3 stretch_margin_right = 3 stretch_margin_bottom = 3 -script = ExtResource( 1 ) +script = ExtResource("1") prefix = "Y:" [node name="Z" type="TextureProgressBar" parent="GridContainer"] -offset_top = 56.0 -offset_right = 45.0 -offset_bottom = 80.0 -custom_minimum_size = Vector2( 32, 24 ) -mouse_default_cursor_shape = 2 +custom_minimum_size = Vector2(32, 24) +layout_mode = 2 size_flags_horizontal = 3 -theme_type_variation = "ValueSlider" +focus_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"ValueSlider" nine_patch_stretch = true stretch_margin_left = 3 stretch_margin_top = 3 stretch_margin_right = 3 stretch_margin_bottom = 3 -script = ExtResource( 1 ) +script = ExtResource("1") prefix = "Z:" [node name="Ratio" type="Control" parent="."] visible = false -offset_left = 36.0 -offset_right = 52.0 -offset_bottom = 80.0 -custom_minimum_size = Vector2( 16, 0 ) +custom_minimum_size = Vector2(16, 0) +layout_mode = 2 [node name="RatioGuides" type="NinePatchRect" parent="Ratio" groups=["UIButtons"]] +custom_minimum_size = Vector2(9, 0) +layout_mode = 0 anchor_bottom = 1.0 offset_right = 9.0 -custom_minimum_size = Vector2( 9, 0 ) -texture = ExtResource( 4 ) -region_rect = Rect2( 0, 0, 9, 44 ) +texture = ExtResource("4") +region_rect = Rect2(0, 0, 9, 44) patch_margin_top = 15 patch_margin_bottom = 13 [node name="RatioButton" type="TextureButton" parent="Ratio" groups=["UIButtons"]] unique_name_in_owner = true +layout_mode = 0 anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 @@ -92,8 +89,8 @@ offset_bottom = 8.0 tooltip_text = "Lock aspect ratio" mouse_default_cursor_shape = 2 toggle_mode = true -texture_normal = ExtResource( 3 ) -texture_pressed = ExtResource( 5 ) +texture_normal = ExtResource("3") +texture_pressed = ExtResource("5") [connection signal="value_changed" from="GridContainer/X" to="." method="_on_X_value_changed"] [connection signal="value_changed" from="GridContainer/Y" to="." method="_on_Y_value_changed"] diff --git a/src/UI/Timeline/LayerEffects/LayerEffectsSettings.gd b/src/UI/Timeline/LayerEffects/LayerEffectsSettings.gd index 32230e239..324d8e257 100644 --- a/src/UI/Timeline/LayerEffects/LayerEffectsSettings.gd +++ b/src/UI/Timeline/LayerEffects/LayerEffectsSettings.gd @@ -4,6 +4,9 @@ const LAYER_EFFECT_BUTTON = preload("res://src/UI/Timeline/LayerEffects/LayerEff const DELETE_TEXTURE := preload("res://assets/graphics/misc/close.svg") var effects: Array[LayerEffect] = [ + LayerEffect.new( + "Convolution Matrix", preload("res://src/Shaders/Effects/ConvolutionMatrix.gdshader") + ), LayerEffect.new("Offset", preload("res://src/Shaders/Effects/OffsetPixels.gdshader")), LayerEffect.new("Outline", preload("res://src/Shaders/Effects/OutlineInline.gdshader")), LayerEffect.new("Drop Shadow", preload("res://src/Shaders/Effects/DropShadow.gdshader")),