diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index b42ef8a6cf04..ab125745c8b9 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -492,6 +492,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 bfd2eb930271..1f6d8cbc23c3 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -130,6 +130,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/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 3f6f99938ff7..12a430b688f9 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -28,10 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "shader_language.h" +#include "servers/visual/shader_language.h" +#include "core/engine.h" #include "core/os/os.h" #include "core/print_string.h" #include "servers/visual_server.h" +#include "core/io/resource_loader.h" +#include "servers/visual/shader_language.h" +#include "servers/visual/rasterizer.h" +#include "scene/resources/shader.h" static bool _is_text_char(CharType c) { @@ -209,6 +214,7 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "HINT_COLOR", "HINT_RANGE", "SHADER_TYPE", + "IMPORT_SHADER", "CURSOR", "ERROR", "EOF", @@ -307,6 +313,8 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_HINT_COLOR, "hint_color" }, { TK_HINT_RANGE, "hint_range" }, { TK_SHADER_TYPE, "shader_type" }, + { TK_IMPORT, "import" }, + { TK_QUOTE, "\"" }, { TK_ERROR, NULL } }; @@ -431,7 +439,13 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { return _make_token(TK_OP_NOT); } break; - //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 '\'' //string - no strings in shader case '{': return _make_token(TK_CURLY_BRACKET_OPEN); @@ -4865,10 +4879,85 @@ Error ShaderLanguage::_parse_shader(const Map &p_funct int texture_uniforms = 0; int uniforms = 0; + + 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) { @@ -5925,7 +6014,6 @@ ShaderLanguage::ShaderNode *ShaderLanguage::get_shader() { } ShaderLanguage::ShaderLanguage() { - nodes = NULL; completion_class = TAG_GLOBAL; } diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h index 7a6490d50a5b..b997219cb4eb 100644 --- a/servers/visual/shader_language.h +++ b/servers/visual/shader_language.h @@ -148,6 +148,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,