diff --git a/assets/error.svg b/assets/error.svg new file mode 100644 index 0000000..af2f066 --- /dev/null +++ b/assets/error.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/error.svg.import b/assets/error.svg.import new file mode 100644 index 0000000..07666be --- /dev/null +++ b/assets/error.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://04iv1gogpuhu" +path="res://.godot/imported/error.svg-75fe5f417585e01e99de8885f1f45c3b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/error.svg" +dest_files=["res://.godot/imported/error.svg-75fe5f417585e01e99de8885f1f45c3b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/icon.png b/assets/icon.png similarity index 100% rename from icon.png rename to assets/icon.png diff --git a/icon.png.import b/assets/icon.png.import similarity index 73% rename from icon.png.import rename to assets/icon.png.import index a0840e4..ce38ae5 100644 --- a/icon.png.import +++ b/assets/icon.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://kqwc4avs2xdp" -path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex" +path="res://.godot/imported/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://icon.png" -dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"] +source_file="res://assets/icon.png" +dest_files=["res://.godot/imported/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.ctex"] [params] diff --git a/assets/okay.svg b/assets/okay.svg new file mode 100644 index 0000000..5668fe8 --- /dev/null +++ b/assets/okay.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/okay.svg.import b/assets/okay.svg.import new file mode 100644 index 0000000..6b6c11f --- /dev/null +++ b/assets/okay.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://m1omb6g45vst" +path="res://.godot/imported/okay.svg-7f6df15523471a86f27b6817e9528a4e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/okay.svg" +dest_files=["res://.godot/imported/okay.svg-7f6df15523471a86f27b6817e9528a4e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=2.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/project.godot b/project.godot index b65f388..f6f242c 100644 --- a/project.godot +++ b/project.godot @@ -15,7 +15,7 @@ config/version="v5.0" run/main_scene="res://scenes/main.tscn" config/features=PackedStringArray("4.3", "Mobile") run/low_processor_mode=true -config/icon="res://icon.png" +config/icon="res://assets/icon.png" [autoload] diff --git a/scenes/ui_container.tscn b/scenes/ui_container.tscn index b804125..1c008b4 100644 --- a/scenes/ui_container.tscn +++ b/scenes/ui_container.tscn @@ -165,6 +165,29 @@ offset_bottom = 31.0 grow_horizontal = 0 text = "Apply (F5)" +[node name="StatusIndicator" type="TextureButton" parent="Editor"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -80.0 +offset_top = 56.0 +offset_right = -56.0 +offset_bottom = 80.0 +grow_horizontal = 0 +disabled = true +ignore_texture_size = true +stretch_mode = 0 + +[node name="ErrorMessageDialog" type="AcceptDialog" parent="Editor"] +unique_name_in_owner = true +title = "Status" +initial_position = 2 +size = Vector2i(256, 128) +popup_window = true +ok_button_text = "Close" + [connection signal="file_selected" from="Editor/OpenShaderDialog" to="Editor" method="_on_open_shader_dialog_file_selected"] [connection signal="file_selected" from="Editor/SaveShaderDialog" to="Editor" method="_on_save_shader_dialog_file_selected"] [connection signal="file_selected" from="Editor/SaveImageDialog" to="Editor" method="_on_save_image_dialog_file_selected"] @@ -176,3 +199,4 @@ text = "Apply (F5)" [connection signal="pressed" from="Editor/SaveImageButton" to="Editor" method="_on_save_image_button_pressed"] [connection signal="pressed" from="Editor/FitImageButton" to="Editor" method="_on_fit_image_button_pressed"] [connection signal="pressed" from="Editor/ApplyShaderButton" to="Editor" method="_on_apply_shader_button_pressed"] +[connection signal="pressed" from="Editor/StatusIndicator" to="Editor" method="_on_status_indicator_pressed"] diff --git a/src/Editor.gd b/src/Editor.gd index cad4f38..e191d47 100644 --- a/src/Editor.gd +++ b/src/Editor.gd @@ -6,9 +6,17 @@ extends Control @onready var save_shader_dialog = %SaveShaderDialog @onready var ui_control_filesave = %SaveImageDialog +@onready var status_indicator = %StatusIndicator +@onready var error_msg_dialog = %ErrorMessageDialog + @onready var image_viewport = get_tree().root.get_node("Main/%ImageViewport") @onready var camera = get_tree().root.get_node("Main/%Camera") +# + +var status_okay_texture: CompressedTexture2D = preload("uid://m1omb6g45vst") +var status_error_texture: CompressedTexture2D = preload("uid://04iv1gogpuhu") + # # # # # # # # # # # # GDShader keywords # # https://github.com/godotengine/godot/blob/e96ad5af98547df71b50c4c4695ac348638113e0/servers/rendering/shader_language.cpp @@ -173,7 +181,7 @@ func _on_code_edit_code_completion_requested(): func _ready(): code_editor.code_completion_enabled = true code_editor.syntax_highlighter = ShaderSyntaxHighlighter.new() - self.update() + self.update_code_edit() func _input(event): if event.is_action_pressed("apply_shader"): @@ -182,15 +190,32 @@ func _input(event): accept_event() # Event is now handled. _on_save_shader_button_pressed() -func update(): +func update_code_edit(): code_editor.text = Filesystem.shader.code +enum Status {OKAY, ERROR, UNKNOWN = -1} + +func update_status(status: Status, msg: String = ""): + error_msg_dialog.dialog_text = msg + error_msg_dialog.reset_size() + if status == Status.OKAY: + status_indicator.texture_normal = status_okay_texture + elif status == Status.ERROR: + status_indicator.texture_normal = status_error_texture + else: + status_indicator.texture_normal = null + if msg == "": + status_indicator.disabled = true + else: + status_indicator.disabled = false + # func _on_new_shader_button_pressed(): Filesystem.reset() - self.update() + self.update_code_edit() image_viewport.update() + update_status(Status.UNKNOWN) func _on_open_shader_button_pressed(): open_shader_dialog.show() @@ -215,7 +240,11 @@ func _on_fit_image_button_pressed(): func _on_apply_shader_button_pressed(): Filesystem.shader.code = code_editor.text - image_viewport.update() + var errors = await image_viewport.update() + if len(errors) > 0: + update_status(Status.ERROR, "\n".join(errors)) + else: + update_status(Status.OKAY) func _on_save_image_button_pressed(): if Filesystem.result != null: @@ -226,11 +255,16 @@ func _on_save_image_button_pressed(): func _on_open_shader_dialog_file_selected(path: String): Filesystem.load_shader(path) - image_viewport.update() - self.update() + self.update_code_edit() + self._on_apply_shader_button_pressed() func _on_save_shader_dialog_file_selected(path): Filesystem.save_shader(path) func _on_save_image_dialog_file_selected(path): Filesystem.save_result(path) + +# + +func _on_status_indicator_pressed() -> void: + error_msg_dialog.show() diff --git a/src/Filesystem.gd b/src/Filesystem.gd index 18fd3d4..9f92261 100644 --- a/src/Filesystem.gd +++ b/src/Filesystem.gd @@ -2,7 +2,9 @@ extends Node @onready var template_shader: Shader = load("res://src/shader/template.gdshader") @onready var shader: Shader = template_shader.duplicate() + var original_image: ImageTexture +var additional_images: Dictionary var result: Image var cwd = "." @@ -24,8 +26,7 @@ func get_absolute_path(p: String) -> String: return self.cwd + "/" + p.lstrip("./") return p -func load_original_image(path: String): - print("Load ", path) +func load_original_image(path: String) -> String: # returns an error message var img = Image.new() var err = img.load(path) if err == OK: @@ -34,12 +35,20 @@ func load_original_image(path: String): self.last_original_image_path = path if self.last_image_savepath == "": self.last_image_savepath = path - else: - print("An error occured!") + return "" + return error_string(err) + " " + path -func load_image(path: String) -> ImageTexture: - print("Load ", path) - return ImageTexture.create_from_image(Image.load_from_file(path)) +func clear_additional_images(): + additional_images.clear() + +func load_additional_image(key: String, path: String) -> String: # returns Error Message String + var img = Image.new() + var err = img.load(path) + if err == OK: + additional_images[key] = ImageTexture.create_from_image(img) + return "" + else: + return error_string(err) + " " + path func save_result(path: String): print("Export ", path) diff --git a/src/ImageViewport.gd b/src/ImageViewport.gd index d327ce8..288929f 100644 --- a/src/ImageViewport.gd +++ b/src/ImageViewport.gd @@ -4,31 +4,42 @@ extends SubViewport @onready var image_sprite = %ImageSprite @onready var image_viewport_display = %ImageViewportDisplay -func update(): +func update() -> Array: # returns error messages (strings) + var errors = [] var fit_image = false - # load image from //!load directive -> TEXTURE + # load texture(s) + Filesystem.clear_additional_images() + # ... from //!load directive -> TEXTURE var m = ShaderDirectiveParser.parse_load_directive(Filesystem.shader) if len(m) < 1: - return # AAAAAAAa + errors.append("Didn't find a load directive!") + return errors var original_image_path = Filesystem.get_absolute_path(m[1]) if original_image_path != Filesystem.last_original_image_path: fit_image = true - Filesystem.load_original_image(original_image_path) - if Filesystem.original_image == null: + var err = Filesystem.load_original_image(original_image_path) + if err != "": + errors.append(err) image_viewport_display.hide() - return + return errors + # ... from //!load+ directives + for n in ShaderDirectiveParser.parse_load_additional_directive(Filesystem.shader): + err = Filesystem.load_additional_image(n[1], Filesystem.get_absolute_path(n[2])) + if err != "": + errors.append(err) + if len(errors) > 0: + return errors + # apply textures image_sprite.texture = Filesystem.original_image image_sprite.offset = Filesystem.original_image.get_size() / 2 self.size = Filesystem.original_image.get_size() var mat = ShaderMaterial.new() mat.shader = Filesystem.shader - # load images from //!load+ directives and apply them to - # the material as shader parameters - for n in ShaderDirectiveParser.parse_load_additional_directive(Filesystem.shader): + # ... as shader parameters + for key in Filesystem.additional_images: mat.set_shader_parameter( - n[1], # uniform param name - Filesystem.load_image(Filesystem.get_absolute_path(n[2])) - ) + key, # uniform param name + Filesystem.additional_images[key]) # assign material image_sprite.material = mat # Get viewport texture @@ -39,3 +50,5 @@ func update(): if fit_image: camera.fit_image() image_viewport_display.show() + # done + return errors diff --git a/src/ShaderDirectiveParser.gd b/src/ShaderDirectiveParser.gd index 936eaa7..6dd031b 100644 --- a/src/ShaderDirectiveParser.gd +++ b/src/ShaderDirectiveParser.gd @@ -11,8 +11,7 @@ func _ready(): func parse_load_directive(shader: Shader) -> PackedStringArray: var regex_match = self._load_regex.search(Filesystem.shader.code) - if regex_match == null: # Error! - printerr("Didn't find any load directives!") + if regex_match == null: return [] return regex_match.strings