1 | /**************************************************************************/ |
2 | /* shader_include.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "shader_include.h" |
32 | #include "servers/rendering/shader_language.h" |
33 | #include "servers/rendering/shader_preprocessor.h" |
34 | |
35 | void ShaderInclude::_dependency_changed() { |
36 | emit_changed(); |
37 | } |
38 | |
39 | void ShaderInclude::set_code(const String &p_code) { |
40 | code = p_code; |
41 | |
42 | for (const Ref<ShaderInclude> &E : dependencies) { |
43 | E->disconnect_changed(callable_mp(this, &ShaderInclude::_dependency_changed)); |
44 | } |
45 | |
46 | { |
47 | String path = get_path(); |
48 | if (path.is_empty()) { |
49 | path = include_path; |
50 | } |
51 | |
52 | String pp_code; |
53 | HashSet<Ref<ShaderInclude>> new_dependencies; |
54 | ShaderPreprocessor preprocessor; |
55 | Error result = preprocessor.preprocess(p_code, path, pp_code, nullptr, nullptr, nullptr, &new_dependencies); |
56 | if (result == OK) { |
57 | // This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower) |
58 | dependencies = new_dependencies; |
59 | } |
60 | } |
61 | |
62 | for (const Ref<ShaderInclude> &E : dependencies) { |
63 | E->connect_changed(callable_mp(this, &ShaderInclude::_dependency_changed)); |
64 | } |
65 | |
66 | emit_changed(); |
67 | } |
68 | |
69 | String ShaderInclude::get_code() const { |
70 | return code; |
71 | } |
72 | |
73 | void ShaderInclude::set_include_path(const String &p_path) { |
74 | include_path = p_path; |
75 | } |
76 | |
77 | void ShaderInclude::_bind_methods() { |
78 | ClassDB::bind_method(D_METHOD("set_code" , "code" ), &ShaderInclude::set_code); |
79 | ClassDB::bind_method(D_METHOD("get_code" ), &ShaderInclude::get_code); |
80 | |
81 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "code" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR), "set_code" , "get_code" ); |
82 | } |
83 | |
84 | // ResourceFormatLoaderShaderInclude |
85 | |
86 | Ref<Resource> ResourceFormatLoaderShaderInclude::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { |
87 | if (r_error) { |
88 | *r_error = ERR_FILE_CANT_OPEN; |
89 | } |
90 | |
91 | Error error = OK; |
92 | Vector<uint8_t> buffer = FileAccess::get_file_as_bytes(p_path, &error); |
93 | ERR_FAIL_COND_V_MSG(error, nullptr, "Cannot load shader include: " + p_path); |
94 | |
95 | String str; |
96 | if (buffer.size() > 0) { |
97 | error = str.parse_utf8((const char *)buffer.ptr(), buffer.size()); |
98 | ERR_FAIL_COND_V_MSG(error, nullptr, "Cannot parse shader include: " + p_path); |
99 | } |
100 | |
101 | Ref<ShaderInclude> shader_inc; |
102 | shader_inc.instantiate(); |
103 | |
104 | shader_inc->set_include_path(p_path); |
105 | shader_inc->set_code(str); |
106 | |
107 | if (r_error) { |
108 | *r_error = OK; |
109 | } |
110 | |
111 | return shader_inc; |
112 | } |
113 | |
114 | void ResourceFormatLoaderShaderInclude::get_recognized_extensions(List<String> *p_extensions) const { |
115 | p_extensions->push_back("gdshaderinc" ); |
116 | } |
117 | |
118 | bool ResourceFormatLoaderShaderInclude::handles_type(const String &p_type) const { |
119 | return (p_type == "ShaderInclude" ); |
120 | } |
121 | |
122 | String ResourceFormatLoaderShaderInclude::get_resource_type(const String &p_path) const { |
123 | String extension = p_path.get_extension().to_lower(); |
124 | if (extension == "gdshaderinc" ) { |
125 | return "ShaderInclude" ; |
126 | } |
127 | return "" ; |
128 | } |
129 | |
130 | // ResourceFormatSaverShaderInclude |
131 | |
132 | Error ResourceFormatSaverShaderInclude::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { |
133 | Ref<ShaderInclude> shader_inc = p_resource; |
134 | ERR_FAIL_COND_V(shader_inc.is_null(), ERR_INVALID_PARAMETER); |
135 | |
136 | String source = shader_inc->get_code(); |
137 | |
138 | Error error; |
139 | Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &error); |
140 | |
141 | ERR_FAIL_COND_V_MSG(error, error, "Cannot save shader include '" + p_path + "'." ); |
142 | |
143 | file->store_string(source); |
144 | if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { |
145 | return ERR_CANT_CREATE; |
146 | } |
147 | |
148 | return OK; |
149 | } |
150 | |
151 | void ResourceFormatSaverShaderInclude::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const { |
152 | const ShaderInclude *shader_inc = Object::cast_to<ShaderInclude>(*p_resource); |
153 | if (shader_inc != nullptr) { |
154 | p_extensions->push_back("gdshaderinc" ); |
155 | } |
156 | } |
157 | |
158 | bool ResourceFormatSaverShaderInclude::recognize(const Ref<Resource> &p_resource) const { |
159 | return p_resource->get_class_name() == "ShaderInclude" ; //only shader, not inherited |
160 | } |
161 | |