From 0fad7cb575d9278fbc61b6a49a8108cd3daa6673 Mon Sep 17 00:00:00 2001 From: ChaoticByte Date: Sun, 19 Jan 2025 12:51:10 +0100 Subject: [PATCH] Implement a commandline interface that can be used in scripts - implements #42 --- README.md | 30 ++++++++++++++++++ scenes/main.tscn | 1 + src/ImageCompositor.gd | 16 ++++++---- src/Main.gd | 70 +++++++++++++++++++++++++++++++++++------- 4 files changed, 100 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 4c3496d..c658569 100644 --- a/README.md +++ b/README.md @@ -74,3 +74,33 @@ void fragment() { COLOR = hsv_offset(COLOR, 0.32, 0.2, 0.0); } ``` + +## Commandline interface + +You can run Fragmented from the commandline or scripts. + +> Note: Headless mode is not supported. Using the commandline interface still opens a window. + +### Usage + +``` +./Fragmented cmd --shader PATH [--load-image PATH] + + --shader PATH The path to the shader + --output PATH Where to write the resulting image to + --load-image PATH The path to the image. This will overwrite the + load directive of the shader file (optional) + +``` + +You can also run `./Fragmented cmd help` to show the help message. + +#### Examples + +``` +./Fragmented cmd --shader ./examples/oklab.gdshader --output ./output.png +``` + +``` +./Fragmented cmd --shader ./examples/oklab.gdshader --load-image ~/Pictures/test.png --output ./output.png +``` diff --git a/scenes/main.tscn b/scenes/main.tscn index 5365da3..2d104a3 100644 --- a/scenes/main.tscn +++ b/scenes/main.tscn @@ -37,6 +37,7 @@ unique_name_in_owner = true disable_3d = true position = Vector2i(48, 36) size = Vector2i(704, 704) +visible = false script = ExtResource("6_8k0ha") [node name="UserInterfaceContainer" parent="EditorWindow" instance=ExtResource("7_5ci0e")] diff --git a/src/ImageCompositor.gd b/src/ImageCompositor.gd index 9fe58ec..5999b88 100644 --- a/src/ImageCompositor.gd +++ b/src/ImageCompositor.gd @@ -44,7 +44,7 @@ func inject_step_uniform(shader_code: String) -> Shader: shader.code = shader_code.insert(fragment_function_match.get_start(), "\nuniform int STEP;") return shader -func update() -> Array: # returns error messages (strings) +func update(overwrite_image_path: String = "") -> Array: # returns error messages (strings) # inject STEP uniform & get number of steps var shader: Shader = inject_step_uniform(Filesystem.shader_code) var steps: int = ShaderDirectiveParser.parse_steps_directive(shader.code) @@ -53,11 +53,15 @@ func update() -> Array: # returns error messages (strings) return ["Shader compilation failed!"] var errors = [] # load texture(s) from //!load directive -> TEXTURE - var m = ShaderDirectiveParser.parse_load_directive(shader.code) - if len(m) < 1: - errors.append("Didn't find a load directive!") - return errors - var original_image_path = Filesystem.get_absolute_path(m[1]) + var original_image_path = "" + if overwrite_image_path == "": + var m = ShaderDirectiveParser.parse_load_directive(shader.code) + if len(m) < 1: + errors.append("Didn't find a load directive!") + return errors + original_image_path = Filesystem.get_absolute_path(m[1]) + else: + original_image_path = overwrite_image_path var fit_image = false if original_image_path != Filesystem.last_original_image_path: fit_image = true diff --git a/src/Main.gd b/src/Main.gd index 2acdbf4..8e54a86 100644 --- a/src/Main.gd +++ b/src/Main.gd @@ -4,18 +4,66 @@ extends Node @onready var ui_container = %UserInterfaceContainer @onready var app_name = ProjectSettings.get_setting("application/config/name") +func show_help(): + print( + "Usage:\n\n", + "./Fragmented cmd --shader PATH [--load-image PATH]\n\n", + " --shader PATH The path to the shader\n", + " --output PATH Where to write the resulting image to\n", + " --load-image PATH The path to the image. This will overwrite the\n", + " load directive of the shader file (optional)\n") + +func parse_custom_cmdline(args: PackedStringArray): + var kwargs: Dictionary = {"--shader": null, "--output": null, "--load-image": null} + var args_len = args.size() + var i = 0 + while i < args_len: + var a = args[i] + if a in kwargs && args_len > i+1: + i += 1 + kwargs[a] = args[i] + i += 1 + return kwargs + func _ready(): - update_title() - # position windows - get_window().position = Vector2i( - editor_window.position.x + editor_window.size.x + 50, - editor_window.position.y) - get_window().min_size = Vector2i(400, 400) - editor_window.min_size = Vector2i(560, 400) - # Load last opened file - Filesystem.remember_last_opened_file() - if Filesystem.last_shader_savepath != "": - ui_container.get_node("Editor")._on_open_shader_dialog_file_selected(Filesystem.last_shader_savepath) + var args = OS.get_cmdline_args() + if "cmd" in args: # commandline interface + if "help" in args: + show_help() + get_tree().quit(1) + else: + var kwargs: Dictionary = parse_custom_cmdline(args) + if kwargs["--shader"] == null or kwargs["--output"] == null: + show_help() + get_tree().quit(1) + else: + Filesystem.load_shader(kwargs["--shader"]) + var errors = [] + if kwargs["--load-image"] == null: + errors = await $Compositor.update() + else: + errors = await $Compositor.update(kwargs["--load-image"]) + if errors.size() > 0: + print("One or more errors occurred.") + for e in errors: + printerr(e) + get_tree().quit(1) + else: + Filesystem.save_result(kwargs["--output"]) + get_tree().quit(0) + else: + update_title() + # position windows + get_window().position = Vector2i( + editor_window.position.x + editor_window.size.x + 50, + editor_window.position.y) + get_window().min_size = Vector2i(400, 400) + editor_window.min_size = Vector2i(560, 400) + editor_window.show() + # Load last opened file + Filesystem.remember_last_opened_file() + if Filesystem.last_shader_savepath != "": + ui_container.get_node("Editor")._on_open_shader_dialog_file_selected(Filesystem.last_shader_savepath) func update_title(current_file: String = ""): if current_file == "":