1/**************************************************************************/
2/* shader_globals_override.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_globals_override.h"
32
33#include "scene/3d/node_3d.h"
34#include "scene/scene_string_names.h"
35
36StringName *ShaderGlobalsOverride::_remap(const StringName &p_name) const {
37 StringName *r = param_remaps.getptr(p_name);
38 if (!r) {
39 //not cached, do caching
40 String p = p_name;
41 if (p.begins_with("params/")) {
42 String q = p.replace_first("params/", "");
43 param_remaps[p] = q;
44 r = param_remaps.getptr(q);
45 }
46 }
47
48 return r;
49}
50
51bool ShaderGlobalsOverride::_set(const StringName &p_name, const Variant &p_value) {
52 StringName *r = _remap(p_name);
53
54 if (r) {
55 Override *o = overrides.getptr(*r);
56 if (!o) {
57 Override ov;
58 ov.in_use = false;
59 overrides[*r] = ov;
60 o = overrides.getptr(*r);
61 }
62 if (o) {
63 o->override = p_value;
64 if (active) {
65 if (o->override.get_type() == Variant::OBJECT) {
66 RID tex_rid = p_value;
67 RS::get_singleton()->global_shader_parameter_set_override(*r, tex_rid);
68 } else {
69 RS::get_singleton()->global_shader_parameter_set_override(*r, p_value);
70 }
71 }
72 o->in_use = p_value.get_type() != Variant::NIL;
73 return true;
74 }
75 }
76
77 return false;
78}
79
80bool ShaderGlobalsOverride::_get(const StringName &p_name, Variant &r_ret) const {
81 StringName *r = _remap(p_name);
82
83 if (r) {
84 const Override *o = overrides.getptr(*r);
85 if (o) {
86 r_ret = o->override;
87 return true;
88 }
89 }
90
91 return false;
92}
93
94void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const {
95 Vector<StringName> variables;
96 variables = RS::get_singleton()->global_shader_parameter_get_list();
97 for (int i = 0; i < variables.size(); i++) {
98 PropertyInfo pinfo;
99 pinfo.name = "params/" + variables[i];
100 pinfo.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE;
101
102 switch (RS::get_singleton()->global_shader_parameter_get_type(variables[i])) {
103 case RS::GLOBAL_VAR_TYPE_BOOL: {
104 pinfo.type = Variant::BOOL;
105 } break;
106 case RS::GLOBAL_VAR_TYPE_BVEC2: {
107 pinfo.type = Variant::INT;
108 pinfo.hint = PROPERTY_HINT_FLAGS;
109 pinfo.hint_string = "x,y";
110 } break;
111 case RS::GLOBAL_VAR_TYPE_BVEC3: {
112 pinfo.type = Variant::INT;
113 pinfo.hint = PROPERTY_HINT_FLAGS;
114 pinfo.hint_string = "x,y,z";
115 } break;
116 case RS::GLOBAL_VAR_TYPE_BVEC4: {
117 pinfo.type = Variant::INT;
118 pinfo.hint = PROPERTY_HINT_FLAGS;
119 pinfo.hint_string = "x,y,z,w";
120 } break;
121 case RS::GLOBAL_VAR_TYPE_INT: {
122 pinfo.type = Variant::INT;
123 } break;
124 case RS::GLOBAL_VAR_TYPE_IVEC2: {
125 pinfo.type = Variant::VECTOR2I;
126 } break;
127 case RS::GLOBAL_VAR_TYPE_IVEC3: {
128 pinfo.type = Variant::VECTOR3I;
129 } break;
130 case RS::GLOBAL_VAR_TYPE_IVEC4: {
131 pinfo.type = Variant::VECTOR4I;
132 } break;
133 case RS::GLOBAL_VAR_TYPE_RECT2I: {
134 pinfo.type = Variant::RECT2I;
135 } break;
136 case RS::GLOBAL_VAR_TYPE_UINT: {
137 pinfo.type = Variant::INT;
138 } break;
139 case RS::GLOBAL_VAR_TYPE_UVEC2: {
140 pinfo.type = Variant::VECTOR2I;
141 } break;
142 case RS::GLOBAL_VAR_TYPE_UVEC3: {
143 pinfo.type = Variant::VECTOR3I;
144 } break;
145 case RS::GLOBAL_VAR_TYPE_UVEC4: {
146 pinfo.type = Variant::VECTOR4I;
147 } break;
148 case RS::GLOBAL_VAR_TYPE_FLOAT: {
149 pinfo.type = Variant::FLOAT;
150 } break;
151 case RS::GLOBAL_VAR_TYPE_VEC2: {
152 pinfo.type = Variant::VECTOR2;
153 } break;
154 case RS::GLOBAL_VAR_TYPE_VEC3: {
155 pinfo.type = Variant::VECTOR3;
156 } break;
157 case RS::GLOBAL_VAR_TYPE_VEC4: {
158 pinfo.type = Variant::VECTOR4;
159 } break;
160 case RS::GLOBAL_VAR_TYPE_RECT2: {
161 pinfo.type = Variant::RECT2;
162 } break;
163 case RS::GLOBAL_VAR_TYPE_COLOR: {
164 pinfo.type = Variant::COLOR;
165 } break;
166 case RS::GLOBAL_VAR_TYPE_MAT2: {
167 pinfo.type = Variant::PACKED_FLOAT32_ARRAY;
168 } break;
169 case RS::GLOBAL_VAR_TYPE_MAT3: {
170 pinfo.type = Variant::BASIS;
171 } break;
172 case RS::GLOBAL_VAR_TYPE_MAT4: {
173 pinfo.type = Variant::PROJECTION;
174 } break;
175 case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: {
176 pinfo.type = Variant::TRANSFORM2D;
177 } break;
178 case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
179 pinfo.type = Variant::TRANSFORM3D;
180 } break;
181 case RS::GLOBAL_VAR_TYPE_SAMPLER2D: {
182 pinfo.type = Variant::OBJECT;
183 pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
184 pinfo.hint_string = "Texture2D";
185 } break;
186 case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: {
187 pinfo.type = Variant::OBJECT;
188 pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
189 pinfo.hint_string = "Texture2DArray";
190 } break;
191 case RS::GLOBAL_VAR_TYPE_SAMPLER3D: {
192 pinfo.type = Variant::OBJECT;
193 pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
194 pinfo.hint_string = "Texture3D";
195 } break;
196 case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: {
197 pinfo.type = Variant::OBJECT;
198 pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
199 pinfo.hint_string = "Cubemap";
200 } break;
201 default: {
202 } break;
203 }
204
205 if (!overrides.has(variables[i])) {
206 Override o;
207 o.in_use = false;
208 Callable::CallError ce;
209 Variant::construct(pinfo.type, o.override, nullptr, 0, ce);
210 overrides[variables[i]] = o;
211 }
212
213 Override *o = overrides.getptr(variables[i]);
214 if (o->in_use && o->override.get_type() != Variant::NIL) {
215 pinfo.usage |= PROPERTY_USAGE_CHECKED;
216 pinfo.usage |= PROPERTY_USAGE_STORAGE;
217 }
218
219 p_list->push_back(pinfo);
220 }
221}
222
223void ShaderGlobalsOverride::_activate() {
224 ERR_FAIL_NULL(get_tree());
225 List<Node *> nodes;
226 get_tree()->get_nodes_in_group(SceneStringNames::get_singleton()->shader_overrides_group_active, &nodes);
227 if (nodes.size() == 0) {
228 //good we are the only override, enable all
229 active = true;
230 add_to_group(SceneStringNames::get_singleton()->shader_overrides_group_active);
231
232 for (const KeyValue<StringName, Override> &E : overrides) {
233 const Override *o = &E.value;
234 if (o->in_use && o->override.get_type() != Variant::NIL) {
235 if (o->override.get_type() == Variant::OBJECT) {
236 RID tex_rid = o->override;
237 RS::get_singleton()->global_shader_parameter_set_override(E.key, tex_rid);
238 } else {
239 RS::get_singleton()->global_shader_parameter_set_override(E.key, o->override);
240 }
241 }
242
243 update_configuration_warnings(); //may have activated
244 }
245 }
246}
247
248void ShaderGlobalsOverride::_notification(int p_what) {
249 switch (p_what) {
250 case Node3D::NOTIFICATION_ENTER_TREE: {
251 add_to_group(SceneStringNames::get_singleton()->shader_overrides_group);
252 _activate();
253 } break;
254
255 case Node3D::NOTIFICATION_EXIT_TREE: {
256 if (active) {
257 //remove overrides
258 for (const KeyValue<StringName, Override> &E : overrides) {
259 const Override *o = &E.value;
260 if (o->in_use) {
261 RS::get_singleton()->global_shader_parameter_set_override(E.key, Variant());
262 }
263 }
264 }
265
266 remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group_active);
267 remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group);
268 get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->shader_overrides_group, "_activate"); //another may want to activate when this is removed
269 active = false;
270 } break;
271 }
272}
273
274PackedStringArray ShaderGlobalsOverride::get_configuration_warnings() const {
275 PackedStringArray warnings = Node::get_configuration_warnings();
276
277 if (!active) {
278 warnings.push_back(RTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene."));
279 }
280
281 return warnings;
282}
283
284void ShaderGlobalsOverride::_bind_methods() {
285 ClassDB::bind_method(D_METHOD("_activate"), &ShaderGlobalsOverride::_activate);
286}
287
288ShaderGlobalsOverride::ShaderGlobalsOverride() {}
289