From 186e2259ac4312074d9d9672e43c6e1c8c00b293 Mon Sep 17 00:00:00 2001 From: OverloadedOrama <35376950+OverloadedOrama@users.noreply.github.com> Date: Sat, 25 Jul 2020 16:26:57 +0300 Subject: [PATCH] [EXPERIMENTAL] Added ability to load a shader as an image effect This new feature allows users to load a .shader file (must be a GLES2 Godot shader) that will modify the image itself. This feature is experimental and possibly buggy, not all shaders are working properly and I'm not sure yet why. As such, this feature may not be included in v0.8 stable. --- src/Autoload/HTML5FileExchange.gd | 55 ++++++++++++++++++++ src/Main.tscn | 12 +++-- src/UI/Dialogs/ShaderEffect.gd | 56 ++++++++++++++++++++ src/UI/Dialogs/ShaderEffect.tscn | 86 +++++++++++++++++++++++++++++++ src/UI/TopMenuContainer.gd | 6 ++- 5 files changed, 209 insertions(+), 6 deletions(-) create mode 100644 src/UI/Dialogs/ShaderEffect.gd create mode 100644 src/UI/Dialogs/ShaderEffect.tscn diff --git a/src/Autoload/HTML5FileExchange.gd b/src/Autoload/HTML5FileExchange.gd index 19d4fad..96b3802 100644 --- a/src/Autoload/HTML5FileExchange.gd +++ b/src/Autoload/HTML5FileExchange.gd @@ -69,6 +69,27 @@ func _define_js() -> void: } }); } + function upload_shader() { + canceled = true; + var input = document.createElement('INPUT'); + input.setAttribute("type", "file"); + input.setAttribute("accept", ".shader"); + input.click(); + input.addEventListener('change', event => { + if (event.target.files.length > 0){ + canceled = false;} + var file = event.target.files[0]; + var reader = new FileReader(); + fileType = file.type; + fileName = file.name; + reader.readAsText(file); + reader.onloadend = function (evt) { + if (evt.target.readyState == FileReader.DONE) { + fileData = evt.target.result; + } + } + }); + } function download(fileName, byte, type) { var buffer = Uint8Array.from(byte); var blob = new Blob([buffer], { type: type}); @@ -174,3 +195,37 @@ func save_image(image : Image, file_name : String = "export") -> void: var png_data = Array(image.save_png_to_buffer()) JavaScript.eval("download('%s', %s, 'image/png');" % [file_name, str(png_data)], true) + + +func load_shader() -> void: + if OS.get_name() != "HTML5" or !OS.has_feature('JavaScript'): + return + + # Execute JS function + JavaScript.eval("upload_shader();", true) # Opens prompt for choosing file + + yield(self, "InFocus") # Wait until JS prompt is closed + + yield(get_tree().create_timer(0.5), "timeout") # Give some time for async JS data load + + if JavaScript.eval("canceled;", true): # If File Dialog closed w/o file + return + + # Use data from png data + var file_data + while true: + file_data = JavaScript.eval("fileData;", true) + if file_data != null: + break + yield(get_tree().create_timer(1.0), "timeout") # Need more time to load data + +# var file_type = JavaScript.eval("fileType;", true) + var file_name = JavaScript.eval("fileName;", true) + + var shader = Shader.new() + shader.code = file_data + + var shader_effect_dialog = Global.control.get_node("Dialogs/ImageEffects/ShaderEffect") + shader_effect_dialog.preview.material.shader = shader + shader_effect_dialog.shader_loaded_label.text = tr("Shader loaded:") + " " + file_name.get_basename() + diff --git a/src/Main.tscn b/src/Main.tscn index 3f38647..eaee706 100644 --- a/src/Main.tscn +++ b/src/Main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=21 format=2] +[gd_scene load_steps=22 format=2] [ext_resource path="res://assets/themes/dark/theme.tres" type="Theme" id=1] [ext_resource path="res://src/Main.gd" type="Script" id=2] @@ -9,6 +9,7 @@ [ext_resource path="res://src/UI/Dialogs/ResizeCanvas.tscn" type="PackedScene" id=7] [ext_resource path="res://src/UI/Dialogs/SaveSpriteHTML5.tscn" type="PackedScene" id=8] [ext_resource path="res://src/UI/Dialogs/GradientDialog.tscn" type="PackedScene" id=9] +[ext_resource path="res://src/UI/Dialogs/ShaderEffect.tscn" type="PackedScene" id=10] [ext_resource path="res://src/UI/Dialogs/SaveSprite.tscn" type="PackedScene" id=11] [ext_resource path="res://src/UI/Dialogs/OpenSprite.tscn" type="PackedScene" id=12] [ext_resource path="res://src/UI/Dialogs/SplashDialog.tscn" type="PackedScene" id=27] @@ -45,17 +46,16 @@ __meta__ = { [node name="UI" parent="MenuAndUI" instance=ExtResource( 4 )] [node name="Dialogs" type="Control" parent="."] -margin_right = 40.0 -margin_bottom = 40.0 mouse_filter = 2 __meta__ = { "_edit_use_anchors_": false } [node name="ImageEffects" type="Control" parent="Dialogs"] -margin_right = 40.0 -margin_bottom = 40.0 mouse_filter = 2 +__meta__ = { +"_edit_use_anchors_": false +} [node name="ScaleImage" parent="Dialogs/ImageEffects" instance=ExtResource( 31 )] margin_bottom = 127.0 @@ -75,6 +75,8 @@ margin_bottom = 106.0 [node name="GradientDialog" parent="Dialogs/ImageEffects" instance=ExtResource( 9 )] margin_bottom = 214.0 +[node name="ShaderEffect" parent="Dialogs/ImageEffects" instance=ExtResource( 10 )] + [node name="BrushesPopup" parent="Dialogs" instance=ExtResource( 6 )] [node name="PatternsPopup" parent="Dialogs" instance=ExtResource( 5 )] diff --git a/src/UI/Dialogs/ShaderEffect.gd b/src/UI/Dialogs/ShaderEffect.gd new file mode 100644 index 0000000..b9a161d --- /dev/null +++ b/src/UI/Dialogs/ShaderEffect.gd @@ -0,0 +1,56 @@ +extends ConfirmationDialog + + +var current_cel : Image + +onready var viewport : Viewport = $VBoxContainer/ViewportContainer/Viewport +onready var preview : TextureRect = viewport.get_node("Preview") +onready var shader_loaded_label : Label = $VBoxContainer/ShaderLoadedLabel + + +func _on_ShaderEffect_about_to_show() -> void: + current_cel = Global.current_project.frames[Global.current_project.current_frame].cels[Global.current_project.current_layer].image + current_cel.unlock() + viewport.size = current_cel.get_size() + var viewport_texture = viewport.get_texture().get_data() + viewport_texture.convert(Image.FORMAT_RGBA8) + + var preview_image := Image.new() + preview_image.copy_from(current_cel) + var preview_texture = ImageTexture.new() + preview_texture.create_from_image(preview_image, 0) + preview.texture = preview_texture + + +func _on_ShaderEffect_confirmed() -> void: + var viewport_texture = viewport.get_texture().get_data() + viewport_texture.flip_y() + viewport_texture.convert(Image.FORMAT_RGBA8) + print(viewport_texture.get_size()) + Global.canvas.handle_undo("Draw") + current_cel.copy_from(viewport_texture) + Global.canvas.handle_redo("Draw") + + +func _on_ShaderEffect_popup_hide() -> void: + current_cel.lock() + Global.dialog_open(false) + yield(get_tree().create_timer(0.2), "timeout") + preview.texture = null + viewport.size = Vector2.ONE + rect_size = Vector2.ONE + + +func _on_ChooseShader_pressed() -> void: + if OS.get_name() == "HTML5": + Html5FileExchange.load_shader() + else: + $FileDialog.popup_centered(Vector2(200, 340)) + + +func _on_FileDialog_file_selected(path : String) -> void: + var shader = load(path) + if !shader is Shader: + return + preview.material.shader = shader + shader_loaded_label.text = tr("Shader loaded:") + " " + path.get_file().get_basename() diff --git a/src/UI/Dialogs/ShaderEffect.tscn b/src/UI/Dialogs/ShaderEffect.tscn new file mode 100644 index 0000000..f341280 --- /dev/null +++ b/src/UI/Dialogs/ShaderEffect.tscn @@ -0,0 +1,86 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://src/UI/Dialogs/ShaderEffect.gd" type="Script" id=2] + +[sub_resource type="ShaderMaterial" id=1] + +[node name="ShaderEffect" type="ConfirmationDialog"] +margin_right = 200.0 +margin_bottom = 228.0 +rect_min_size = Vector2( 0, 0 ) +resizable = true +script = ExtResource( 2 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 8.0 +margin_top = 8.0 +margin_right = -8.0 +margin_bottom = -36.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Label" type="Label" parent="VBoxContainer"] +margin_right = 255.0 +margin_bottom = 48.0 +text = "This is an experimental feature and may not be included in the stable version" +autowrap = true + +[node name="ViewportContainer" type="ViewportContainer" parent="VBoxContainer"] +margin_top = 52.0 +margin_right = 255.0 +margin_bottom = 53.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Viewport" type="Viewport" parent="VBoxContainer/ViewportContainer"] +size = Vector2( 1, 1 ) +transparent_bg = true +handle_input_locally = false +disable_3d = true +usage = 0 +render_target_update_mode = 0 +gui_disable_input = true + +[node name="Preview" type="TextureRect" parent="VBoxContainer/ViewportContainer/Viewport"] +material = SubResource( 1 ) +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="ChooseShader" type="Button" parent="VBoxContainer"] +margin_top = 57.0 +margin_right = 255.0 +margin_bottom = 77.0 +mouse_default_cursor_shape = 2 +text = "Choose Shader" + +[node name="ShaderLoadedLabel" type="Label" parent="VBoxContainer"] +margin_top = 81.0 +margin_right = 255.0 +margin_bottom = 95.0 +text = "No shader loaded!" +autowrap = true + +[node name="FileDialog" type="FileDialog" parent="."] +margin_left = 8.0 +margin_top = 8.0 +margin_right = 263.0 +margin_bottom = 192.0 +window_title = "Open a File" +resizable = true +mode = 0 +access = 2 +filters = PoolStringArray( "*shader" ) +current_dir = "/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama" +current_path = "/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/" +[connection signal="about_to_show" from="." to="." method="_on_ShaderEffect_about_to_show"] +[connection signal="confirmed" from="." to="." method="_on_ShaderEffect_confirmed"] +[connection signal="popup_hide" from="." to="." method="_on_ShaderEffect_popup_hide"] +[connection signal="pressed" from="VBoxContainer/ChooseShader" to="." method="_on_ChooseShader_pressed"] +[connection signal="file_selected" from="FileDialog" to="." method="_on_FileDialog_file_selected"] diff --git a/src/UI/TopMenuContainer.gd b/src/UI/TopMenuContainer.gd index 3bdaf5c..534eab8 100644 --- a/src/UI/TopMenuContainer.gd +++ b/src/UI/TopMenuContainer.gd @@ -94,7 +94,8 @@ func setup_image_menu() -> void: "Desaturation" : 0, "Outline" : 0, "Adjust Hue/Saturation/Value" : 0, - "Gradient" : 0 + "Gradient" : 0, + "Shader" : 0 } var image_menu : PopupMenu = Global.image_menu.get_popup() @@ -333,6 +334,9 @@ func image_menu_id_pressed(id : int) -> void: 10: # Gradient Global.control.get_node("Dialogs/ImageEffects/GradientDialog").popup_centered() Global.dialog_open(true) + 11: # Shader + Global.control.get_node("Dialogs/ImageEffects/ShaderEffect").popup_centered() + Global.dialog_open(true) func show_scale_image_popup() -> void: