extends Control @onready var code_editor = %CodeEdit @onready var open_shader_dialog = %OpenShaderDialog @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 main = get_tree().root.get_node("Main") @onready var compositor = main.get_node("%Compositor") @onready var camera = main.get_node("%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 # https://github.com/godotengine/godot/blob/e96ad5af98547df71b50c4c4695ac348638113e0/servers/rendering/shader_types.cpp # const gdshader_boolean_values = [ "true", "false" ] const gdshader_datatypes = [ "void", "bool", "bvec2", "bvec3", "bvec4", "int", "ivec2", "ivec3", "ivec4", "uint", "uvec2", "uvec3", "uvec4", "float", "vec2", "vec3", "vec4", "mat2", "mat3", "mat4", "sampler2D", "isampler2D", "usampler2D", "sampler2DArray", "isampler2DArray", "usampler2DArray", ] const gdshader_precision_modifiers = [ "lowp", "mediump", "heighp" ] const gdshader_global_space_keywords = [ "uniform", "group_uniforms", "varying", "const", "struct", "shader_type", "render_mode" ] const gdshader_uniform_qualifiers = [ "instance", "global" ] const gdshader_block_keywords = [ "if", "else", "for", "while", "do", "switch", "case", "default", "break", "continue", "return", "discard" ] const gdshader_function_specifier_keywords = [ "in", "out", "inout" ] const gdshader_hints = [ "source_color", "hint_range", "instance_index" ] const gdshader_sampler_hints = [ "hint_normal", "hint_default_white", "hint_default_black", "hint_default_transparent", "hint_anisotropy", "hint_roughness_r", "hint_roughness_g", "hint_roughness_b", "hint_roughness_a", "hint_roughness_normal", "hint_roughness_gray", "hint_screen_texture", "hint_normal_roughness_texture", "hint_depth_texture", "filter_nearest", "filter_linear", "filter_nearest_mipmap", "filter_linear_mipmap", "filter_nearest_mipmap_anisotropic", "filter_linear_mipmap_anisotropic", "repeat_enable", "repeat_disable" ] const gdshader_builtin_functions = [ "radians", "degrees", "sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", "pow", "exp", "log", "exp2", "log2", "sqrt", "inversesqrt", "abs", "sign", "floor", "trunc", "round", "roundEven", "ceil", "fract", "mod", "modf", "min", "max", "clamp", "mix", "step", "smoothstep", "isnan", "isinf", "floatBitsToInt", "floatBitsToUint", "intBitsToFloat", "uintBitsToFloat", "length", "distance", "dot", "cross", "normalize", "reflect", "refract", "faceforward", "matrixCompMult", "outerProduct", "transpose", "determinant", "inverse", "lessThan", "greaterThan", "lessThanEqual", "greaterThanEqual", "equal", "notEqual", "any", "all", "not", "textureSize", "texture", "textureProj", "textureLod", "texelFetch", "textureProjLod", "textureGrad", "textureProjGrad", "textureGather", "textureQueryLod", "textureQueryLevels", "dFdx", "dFdxCoarse", "dFdxFine", "dFdy", "dFdyCoarse", "dFdyFine", "fwidth", "fwidthCoarse", "fwidthFine" ] const gdshader_sub_functions = [ "length", "fma", "packHalf2x16", "packUnorm2x16", "packSnorm2x16", "packUnorm4x8", "packSnorm4x8", "unpackHalf2x16", "unpackUnorm2x16", "unpackSnorm2x16", "unpackUnorm4x8", "unpackSnorm4x8", "bitfieldExtract", "bitfieldInsert", "bitfieldReverse", "bitCount", "findLSB", "findMSB", "umulExtended", "imulExtended", "uaddCarry", "usubBorrow", "ldexp", "frexp" ] const gdshader_builtins = [ "TIME", "PI", "TAU", "E", "VERTEX", "UV", "COLOR", "POINT_SIZE", "MODEL_MATRIX", "CANVAS_MATRIX", "SCREEN_MATRIX", "INSTANCE_CUSTOM", "INSTANCE_ID", "VERTEX_ID", "AT_LIGHT_PASS", "TEXTURE_PIXEL_SIZE", "CUSTOM0", "CUSTOM1", "SHADOW_VERTEX", "LIGHT_VERTEX", "FRAGCOORD", "NORMAL", "NORMAL_MAP", "NORMAL_MAP_DEPTH", "TEXTURE", "NORMAL_TEXTURE", "SCREEN_UV", "SCREEN_PIXEL_SIZE", "POINT_COORD", ] # # configure Highlighter # class ShaderSyntaxHighlighter extends CodeHighlighter: func _init(): add_color_region("//", "", Color.WEB_GRAY, true) add_color_region("/*", "*/", Color.WEB_GRAY, false) function_color = Color.INDIAN_RED for k in gdshader_boolean_values: keyword_colors[k] = Color.INDIAN_RED for k in ( gdshader_datatypes + gdshader_hints + gdshader_sampler_hints + gdshader_global_space_keywords + gdshader_function_specifier_keywords + gdshader_precision_modifiers + gdshader_uniform_qualifiers): keyword_colors[k] = Color.ORCHID; for k in gdshader_block_keywords: keyword_colors[k] = Color.CORAL for k in gdshader_builtins: keyword_colors[k] = Color.DARK_TURQUOISE member_variable_color = Color.LIGHT_BLUE number_color = Color.AQUA symbol_color = Color.GRAY # # and code completion # func _on_code_edit_code_completion_requested(): for k in gdshader_boolean_values: code_editor.code_completion_prefixes.append(k) code_editor.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, k, k, Color.INDIAN_RED) for k in ( gdshader_datatypes + gdshader_hints + gdshader_sampler_hints + gdshader_global_space_keywords + gdshader_function_specifier_keywords + gdshader_precision_modifiers + gdshader_uniform_qualifiers): code_editor.code_completion_prefixes.append(k) code_editor.add_code_completion_option(CodeEdit.KIND_CLASS, k, k, Color.ORCHID) for k in gdshader_block_keywords: code_editor.code_completion_prefixes.append(k) code_editor.add_code_completion_option(CodeEdit.KIND_PLAIN_TEXT, k, k, Color.CORAL) for k in gdshader_builtins: code_editor.code_completion_prefixes.append(k) code_editor.add_code_completion_option(CodeEdit.KIND_CONSTANT, k, k, Color.DARK_TURQUOISE) for k in gdshader_builtin_functions + gdshader_sub_functions: code_editor.code_completion_prefixes.append(k) code_editor.add_code_completion_option(CodeEdit.KIND_FUNCTION, k, k+"(", Color.INDIAN_RED) code_editor.update_code_completion_options(true) # # # # # # # # # # # # # func _ready(): code_editor.code_completion_enabled = true code_editor.syntax_highlighter = ShaderSyntaxHighlighter.new() self.update_code_edit() func _input(event): if event.is_action_pressed("apply_shader"): _on_apply_shader_button_pressed() elif event.is_action_pressed("save_shader"): accept_event() # Event is now handled. _on_save_shader_button_pressed() 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(): main.update_title() Filesystem.reset() self.update_code_edit() compositor.update() update_status(Status.UNKNOWN) func _on_open_shader_button_pressed(): open_shader_dialog.show() func _on_save_shader_button_pressed(): Filesystem.shader.code = code_editor.text if Filesystem.last_shader_savepath == "": _on_save_shader_as_button_pressed() else: _on_save_shader_dialog_file_selected(Filesystem.last_shader_savepath) func _on_save_shader_as_button_pressed() -> void: Filesystem.shader.code = code_editor.text if Filesystem.last_shader_savepath == "": save_shader_dialog.current_file = "filter.gdshader" else: save_shader_dialog.current_path = Filesystem.last_shader_savepath save_shader_dialog.show() func _on_fit_image_button_pressed(): camera.fit_image() func _on_apply_shader_button_pressed(): Filesystem.shader.code = code_editor.text var errors = await compositor.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: ui_control_filesave.current_path = Filesystem.last_image_savepath ui_control_filesave.show() # func _on_open_shader_dialog_file_selected(path: String): Filesystem.load_shader(path) main.update_title(path.split("/")[-1]) 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()