diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index d24dcdef83b0..c39ee198ff5c 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -453,6 +453,24 @@ void ShaderEditor::apply_shaders() { shader->set_code(editor_code); shader->set_edited(true); } + refresh_shader_dependencies(); + } +} + +void ShaderEditor::refresh_shader_dependencies() { + //We could use the arguments to find exactly what shaders we should update that depend on the argument shader. + //For now go through cached shaders, which are usually(?) all shaders that are currently used in editor + //Best solution would be to create a dependency graph about all #includes and use it + + List cached; + ResourceCache::get_cached_resources(&cached); + + for (int i = 0; i < cached.size(); i++) { + Shader *shader = Object::cast_to(*cached[i]); + if (shader) { + // Workaround to refreshing code + shader->set_code(shader->get_code()); + } } } diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index e81a782ac84a..e4fbb1ebdf67 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -129,6 +129,8 @@ class ShaderEditor : public PanelContainer { public: void apply_shaders(); + static void refresh_shader_dependencies(); + void ensure_select_current(); void edit(const Ref &p_shader); diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 742ad8a7bfcd..d3979421f319 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -29,8 +29,13 @@ /*************************************************************************/ #include "shader_language.h" + +#include "core/config/engine.h" +#include "core/io/resource_loader.h" #include "core/os/os.h" #include "core/string/print_string.h" +#include "scene/resources/shader.h" +#include "servers/rendering/shader_language.h" #include "servers/rendering_server.h" static bool _is_text_char(char32_t c) { @@ -216,6 +221,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "REPEAT_ENABLE", "REPEAT_DISABLE", "SHADER_TYPE", + "IMPORT_SHADER", "CURSOR", "ERROR", "EOF", @@ -330,6 +336,9 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_REPEAT_ENABLE, "repeat_enable" }, { TK_REPEAT_DISABLE, "repeat_disable" }, { TK_SHADER_TYPE, "shader_type" }, + { TK_IMPORT, "import" }, + { TK_QUOTE, "\"" }, + { TK_ERROR, nullptr } }; @@ -449,8 +458,13 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { return _make_token(TK_OP_NOT); } break; - //case '"' //string - no strings in shader - //case '\'' //string - no strings in shader + case '"': { + int end_quote = code.find_char('"', char_idx); + String quoted = code.substr(char_idx, end_quote - char_idx); + char_idx = end_quote + 1; + char_idx++; + return _make_token(TK_QUOTE, quoted); + } case '{': return _make_token(TK_CURLY_BRACKET_OPEN); case '}': @@ -5912,9 +5926,83 @@ Error ShaderLanguage::_parse_shader(const Map &p_funct ShaderNode::Uniform::Scope uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL; stages = &p_functions; + Set includes; + int include_depth = 0; while (tk.type != TK_EOF) { switch (tk.type) { + case TK_IMPORT: { + tk = _get_token(); + + if (tk.type != TK_QUOTE) { + _set_error("Expected quote."); + return ERR_PARSE_ERROR; + } + + String path = tk.text; + + if (path.empty()) { + _set_error("Invalid path"); + return ERR_PARSE_ERROR; + } + + RES res = ResourceLoader::load(path); + if (res.is_null()) { + _set_error("Shader include load failed"); + return ERR_PARSE_ERROR; + } + + String replacement = String("import \"") + tk.text + String("\""); + String empty; + for (int i = 0; i < replacement.size(); i++) { + empty += " "; + } + code = code.replace_first(replacement, empty); + + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + _set_error("Expected semicolon."); + return ERR_PARSE_ERROR; + } + + Ref shader = res; + if (shader.is_null()) { + _set_error("Shader include resource type is wrong"); + return ERR_PARSE_ERROR; + } + + String included = shader->get_code(); + if (included.empty()) { + _set_error("Shader include not found"); + return ERR_PARSE_ERROR; + } + + int type_end = included.find(";"); + if (type_end == -1) { + _set_error("Shader include shader_type not found"); + return ERR_PARSE_ERROR; + } + + const String real_path = shader->get_path(); + if (includes.has(real_path)) { + //Already included, skip. + return ERR_PARSE_ERROR; + } + + //Mark as included + includes.insert(real_path); + + include_depth++; + if (include_depth > 25) { + _set_error("Shader max include depth exceeded"); + return ERR_PARSE_ERROR; + } + + //Remove "shader_type xyz;" prefix from included files + included = included.substr(type_end + 1, included.length()); + + code = code.insert(char_idx, included); + } break; case TK_RENDER_MODE: { while (true) { StringName mode; diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 9d2d59154274..fb61de4a511d 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -150,6 +150,8 @@ class ShaderLanguage { TK_ARG_OUT, TK_ARG_INOUT, TK_RENDER_MODE, + TK_IMPORT, + TK_QUOTE, TK_HINT_WHITE_TEXTURE, TK_HINT_BLACK_TEXTURE, TK_HINT_NORMAL_TEXTURE,