1/**************************************************************************/
2/* shader_globals_editor.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_editor.h"
32
33#include "core/config/project_settings.h"
34#include "editor/editor_node.h"
35#include "editor/editor_undo_redo_manager.h"
36#include "servers/rendering/shader_language.h"
37
38static const char *global_var_type_names[RS::GLOBAL_VAR_TYPE_MAX] = {
39 "bool",
40 "bvec2",
41 "bvec3",
42 "bvec4",
43 "int",
44 "ivec2",
45 "ivec3",
46 "ivec4",
47 "rect2i",
48 "uint",
49 "uvec2",
50 "uvec3",
51 "uvec4",
52 "float",
53 "vec2",
54 "vec3",
55 "vec4",
56 "color",
57 "rect2",
58 "mat2",
59 "mat3",
60 "mat4",
61 "transform_2d",
62 "transform",
63 "sampler2D",
64 "sampler2DArray",
65 "sampler3D",
66 "samplerCube",
67};
68
69class ShaderGlobalsEditorInterface : public Object {
70 GDCLASS(ShaderGlobalsEditorInterface, Object)
71
72 void _set_var(const StringName &p_name, const Variant &p_value, const Variant &p_prev_value) {
73 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
74
75 undo_redo->create_action(TTR("Set Shader Global Variable"));
76 undo_redo->add_do_method(RS::get_singleton(), "global_shader_parameter_set", p_name, p_value);
77 undo_redo->add_undo_method(RS::get_singleton(), "global_shader_parameter_set", p_name, p_prev_value);
78 RS::GlobalShaderParameterType type = RS::get_singleton()->global_shader_parameter_get_type(p_name);
79 Dictionary gv;
80 gv["type"] = global_var_type_names[type];
81 if (type >= RS::GLOBAL_VAR_TYPE_SAMPLER2D) {
82 Ref<Resource> res = p_value;
83 if (res.is_valid()) {
84 gv["value"] = res->get_path();
85 } else {
86 gv["value"] = "";
87 }
88 } else {
89 gv["value"] = p_value;
90 }
91
92 String path = "shader_globals/" + String(p_name);
93 undo_redo->add_do_property(ProjectSettings::get_singleton(), path, gv);
94 undo_redo->add_undo_property(ProjectSettings::get_singleton(), path, GLOBAL_GET(path));
95 undo_redo->add_do_method(this, "_var_changed");
96 undo_redo->add_undo_method(this, "_var_changed");
97 block_update = true;
98 undo_redo->commit_action();
99 block_update = false;
100 }
101
102 void _var_changed() {
103 emit_signal(SNAME("var_changed"));
104 }
105
106protected:
107 static void _bind_methods() {
108 ClassDB::bind_method("_set_var", &ShaderGlobalsEditorInterface::_set_var);
109 ClassDB::bind_method("_var_changed", &ShaderGlobalsEditorInterface::_var_changed);
110 ADD_SIGNAL(MethodInfo("var_changed"));
111 }
112
113 bool _set(const StringName &p_name, const Variant &p_value) {
114 Variant existing = RS::get_singleton()->global_shader_parameter_get(p_name);
115
116 if (existing.get_type() == Variant::NIL) {
117 return false;
118 }
119
120 call_deferred("_set_var", p_name, p_value, existing);
121
122 return true;
123 }
124
125 bool _get(const StringName &p_name, Variant &r_ret) const {
126 r_ret = RS::get_singleton()->global_shader_parameter_get(p_name);
127 return r_ret.get_type() != Variant::NIL;
128 }
129 void _get_property_list(List<PropertyInfo> *p_list) const {
130 Vector<StringName> variables;
131 variables = RS::get_singleton()->global_shader_parameter_get_list();
132 for (int i = 0; i < variables.size(); i++) {
133 PropertyInfo pinfo;
134 pinfo.name = variables[i];
135
136 switch (RS::get_singleton()->global_shader_parameter_get_type(variables[i])) {
137 case RS::GLOBAL_VAR_TYPE_BOOL: {
138 pinfo.type = Variant::BOOL;
139 } break;
140 case RS::GLOBAL_VAR_TYPE_BVEC2: {
141 pinfo.type = Variant::INT;
142 pinfo.hint = PROPERTY_HINT_FLAGS;
143 pinfo.hint_string = "x,y";
144 } break;
145 case RS::GLOBAL_VAR_TYPE_BVEC3: {
146 pinfo.type = Variant::INT;
147 pinfo.hint = PROPERTY_HINT_FLAGS;
148 pinfo.hint_string = "x,y,z";
149 } break;
150 case RS::GLOBAL_VAR_TYPE_BVEC4: {
151 pinfo.type = Variant::INT;
152 pinfo.hint = PROPERTY_HINT_FLAGS;
153 pinfo.hint_string = "x,y,z,w";
154 } break;
155 case RS::GLOBAL_VAR_TYPE_INT: {
156 pinfo.type = Variant::INT;
157 } break;
158 case RS::GLOBAL_VAR_TYPE_IVEC2: {
159 pinfo.type = Variant::VECTOR2I;
160 } break;
161 case RS::GLOBAL_VAR_TYPE_IVEC3: {
162 pinfo.type = Variant::VECTOR3I;
163 } break;
164 case RS::GLOBAL_VAR_TYPE_IVEC4: {
165 pinfo.type = Variant::VECTOR4I;
166 } break;
167 case RS::GLOBAL_VAR_TYPE_RECT2I: {
168 pinfo.type = Variant::RECT2I;
169 } break;
170 case RS::GLOBAL_VAR_TYPE_UINT: {
171 pinfo.type = Variant::INT;
172 } break;
173 case RS::GLOBAL_VAR_TYPE_UVEC2: {
174 pinfo.type = Variant::VECTOR2I;
175 } break;
176 case RS::GLOBAL_VAR_TYPE_UVEC3: {
177 pinfo.type = Variant::VECTOR3I;
178 } break;
179 case RS::GLOBAL_VAR_TYPE_UVEC4: {
180 pinfo.type = Variant::VECTOR4I;
181 } break;
182 case RS::GLOBAL_VAR_TYPE_FLOAT: {
183 pinfo.type = Variant::FLOAT;
184 } break;
185 case RS::GLOBAL_VAR_TYPE_VEC2: {
186 pinfo.type = Variant::VECTOR2;
187 } break;
188 case RS::GLOBAL_VAR_TYPE_VEC3: {
189 pinfo.type = Variant::VECTOR3;
190 } break;
191 case RS::GLOBAL_VAR_TYPE_VEC4: {
192 pinfo.type = Variant::VECTOR4;
193 } break;
194 case RS::GLOBAL_VAR_TYPE_RECT2: {
195 pinfo.type = Variant::RECT2;
196 } break;
197 case RS::GLOBAL_VAR_TYPE_COLOR: {
198 pinfo.type = Variant::COLOR;
199 } break;
200 case RS::GLOBAL_VAR_TYPE_MAT2: {
201 pinfo.type = Variant::PACKED_FLOAT32_ARRAY;
202 } break;
203 case RS::GLOBAL_VAR_TYPE_MAT3: {
204 pinfo.type = Variant::BASIS;
205 } break;
206 case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: {
207 pinfo.type = Variant::TRANSFORM2D;
208 } break;
209 case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
210 pinfo.type = Variant::TRANSFORM3D;
211 } break;
212 case RS::GLOBAL_VAR_TYPE_MAT4: {
213 pinfo.type = Variant::PROJECTION;
214 } break;
215 case RS::GLOBAL_VAR_TYPE_SAMPLER2D: {
216 pinfo.type = Variant::OBJECT;
217 pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
218 pinfo.hint_string = "Texture2D";
219 } break;
220 case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: {
221 pinfo.type = Variant::OBJECT;
222 pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
223 pinfo.hint_string = "Texture2DArray";
224 } break;
225 case RS::GLOBAL_VAR_TYPE_SAMPLER3D: {
226 pinfo.type = Variant::OBJECT;
227 pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
228 pinfo.hint_string = "Texture3D";
229 } break;
230 case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: {
231 pinfo.type = Variant::OBJECT;
232 pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
233 pinfo.hint_string = "Cubemap";
234 } break;
235 default: {
236 } break;
237 }
238
239 p_list->push_back(pinfo);
240 }
241 }
242
243public:
244 bool block_update = false;
245
246 ShaderGlobalsEditorInterface() {
247 }
248};
249
250static Variant create_var(RS::GlobalShaderParameterType p_type) {
251 switch (p_type) {
252 case RS::GLOBAL_VAR_TYPE_BOOL: {
253 return false;
254 }
255 case RS::GLOBAL_VAR_TYPE_BVEC2: {
256 return 0; //bits
257 }
258 case RS::GLOBAL_VAR_TYPE_BVEC3: {
259 return 0; //bits
260 }
261 case RS::GLOBAL_VAR_TYPE_BVEC4: {
262 return 0; //bits
263 }
264 case RS::GLOBAL_VAR_TYPE_INT: {
265 return 0; //bits
266 }
267 case RS::GLOBAL_VAR_TYPE_IVEC2: {
268 return Vector2i();
269 }
270 case RS::GLOBAL_VAR_TYPE_IVEC3: {
271 return Vector3i();
272 }
273 case RS::GLOBAL_VAR_TYPE_IVEC4: {
274 return Vector4i();
275 }
276 case RS::GLOBAL_VAR_TYPE_RECT2I: {
277 return Rect2i();
278 }
279 case RS::GLOBAL_VAR_TYPE_UINT: {
280 return 0;
281 }
282 case RS::GLOBAL_VAR_TYPE_UVEC2: {
283 return Vector2i();
284 }
285 case RS::GLOBAL_VAR_TYPE_UVEC3: {
286 return Vector3i();
287 }
288 case RS::GLOBAL_VAR_TYPE_UVEC4: {
289 return Vector4i();
290 }
291 case RS::GLOBAL_VAR_TYPE_FLOAT: {
292 return 0.0;
293 }
294 case RS::GLOBAL_VAR_TYPE_VEC2: {
295 return Vector2();
296 }
297 case RS::GLOBAL_VAR_TYPE_VEC3: {
298 return Vector3();
299 }
300 case RS::GLOBAL_VAR_TYPE_VEC4: {
301 return Vector4();
302 }
303 case RS::GLOBAL_VAR_TYPE_RECT2: {
304 return Rect2();
305 }
306 case RS::GLOBAL_VAR_TYPE_COLOR: {
307 return Color();
308 }
309 case RS::GLOBAL_VAR_TYPE_MAT2: {
310 Vector<float> xform;
311 xform.resize(4);
312 xform.write[0] = 1;
313 xform.write[1] = 0;
314 xform.write[2] = 0;
315 xform.write[3] = 1;
316 return xform;
317 }
318 case RS::GLOBAL_VAR_TYPE_MAT3: {
319 return Basis();
320 }
321 case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: {
322 return Transform2D();
323 }
324 case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
325 return Transform3D();
326 }
327 case RS::GLOBAL_VAR_TYPE_MAT4: {
328 return Projection();
329 }
330 case RS::GLOBAL_VAR_TYPE_SAMPLER2D: {
331 return "";
332 }
333 case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: {
334 return "";
335 }
336 case RS::GLOBAL_VAR_TYPE_SAMPLER3D: {
337 return "";
338 }
339 case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: {
340 return "";
341 }
342 default: {
343 return Variant();
344 }
345 }
346}
347
348void ShaderGlobalsEditor::_variable_added() {
349 String var = variable_name->get_text().strip_edges();
350 if (var.is_empty() || !var.is_valid_identifier()) {
351 EditorNode::get_singleton()->show_warning(TTR("Please specify a valid shader uniform identifier name."));
352 return;
353 }
354
355 if (RenderingServer::get_singleton()->global_shader_parameter_get(var).get_type() != Variant::NIL) {
356 EditorNode::get_singleton()->show_warning(vformat(TTR("Global shader parameter '%s' already exists'"), var));
357 return;
358 }
359
360 List<String> keywords;
361 ShaderLanguage::get_keyword_list(&keywords);
362
363 if (keywords.find(var) != nullptr || var == "script") {
364 EditorNode::get_singleton()->show_warning(vformat(TTR("Name '%s' is a reserved shader language keyword."), var));
365 return;
366 }
367
368 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
369
370 Variant value = create_var(RS::GlobalShaderParameterType(variable_type->get_selected()));
371
372 undo_redo->create_action(TTR("Add Shader Global Parameter"));
373 undo_redo->add_do_method(RS::get_singleton(), "global_shader_parameter_add", var, RS::GlobalShaderParameterType(variable_type->get_selected()), value);
374 undo_redo->add_undo_method(RS::get_singleton(), "global_shader_parameter_remove", var);
375 Dictionary gv;
376 gv["type"] = global_var_type_names[variable_type->get_selected()];
377 gv["value"] = value;
378
379 undo_redo->add_do_property(ProjectSettings::get_singleton(), "shader_globals/" + var, gv);
380 undo_redo->add_undo_property(ProjectSettings::get_singleton(), "shader_globals/" + var, Variant());
381 undo_redo->add_do_method(this, "_changed");
382 undo_redo->add_undo_method(this, "_changed");
383 undo_redo->commit_action();
384}
385
386void ShaderGlobalsEditor::_variable_deleted(const String &p_variable) {
387 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
388
389 undo_redo->create_action(TTR("Add Shader Global Parameter"));
390 undo_redo->add_do_method(RS::get_singleton(), "global_shader_parameter_remove", p_variable);
391 undo_redo->add_undo_method(RS::get_singleton(), "global_shader_parameter_add", p_variable, RS::get_singleton()->global_shader_parameter_get_type(p_variable), RS::get_singleton()->global_shader_parameter_get(p_variable));
392
393 undo_redo->add_do_property(ProjectSettings::get_singleton(), "shader_globals/" + p_variable, Variant());
394 undo_redo->add_undo_property(ProjectSettings::get_singleton(), "shader_globals/" + p_variable, GLOBAL_GET("shader_globals/" + p_variable));
395 undo_redo->add_do_method(this, "_changed");
396 undo_redo->add_undo_method(this, "_changed");
397 undo_redo->commit_action();
398}
399
400void ShaderGlobalsEditor::_changed() {
401 emit_signal(SNAME("globals_changed"));
402 if (!interface->block_update) {
403 interface->notify_property_list_changed();
404 }
405}
406
407void ShaderGlobalsEditor::_bind_methods() {
408 ClassDB::bind_method("_changed", &ShaderGlobalsEditor::_changed);
409 ADD_SIGNAL(MethodInfo("globals_changed"));
410}
411
412void ShaderGlobalsEditor::_notification(int p_what) {
413 switch (p_what) {
414 case NOTIFICATION_VISIBILITY_CHANGED: {
415 if (is_visible_in_tree()) {
416 inspector->edit(interface);
417 }
418 } break;
419
420 case NOTIFICATION_PREDELETE: {
421 inspector->edit(nullptr);
422 } break;
423 }
424}
425
426ShaderGlobalsEditor::ShaderGlobalsEditor() {
427 ProjectSettings::get_singleton()->add_hidden_prefix("shader_globals/");
428
429 HBoxContainer *add_menu_hb = memnew(HBoxContainer);
430 add_child(add_menu_hb);
431
432 add_menu_hb->add_child(memnew(Label(TTR("Name:"))));
433 variable_name = memnew(LineEdit);
434 variable_name->set_h_size_flags(SIZE_EXPAND_FILL);
435 add_menu_hb->add_child(variable_name);
436
437 add_menu_hb->add_child(memnew(Label(TTR("Type:"))));
438 variable_type = memnew(OptionButton);
439 variable_type->set_h_size_flags(SIZE_EXPAND_FILL);
440 add_menu_hb->add_child(variable_type);
441
442 for (int i = 0; i < RS::GLOBAL_VAR_TYPE_MAX; i++) {
443 variable_type->add_item(global_var_type_names[i]);
444 }
445
446 variable_add = memnew(Button(TTR("Add")));
447 add_menu_hb->add_child(variable_add);
448 variable_add->connect("pressed", callable_mp(this, &ShaderGlobalsEditor::_variable_added));
449
450 inspector = memnew(EditorInspector);
451 inspector->set_v_size_flags(SIZE_EXPAND_FILL);
452 add_child(inspector);
453 inspector->set_use_wide_editors(true);
454 inspector->set_property_name_style(EditorPropertyNameProcessor::STYLE_RAW);
455 inspector->set_use_deletable_properties(true);
456 inspector->connect("property_deleted", callable_mp(this, &ShaderGlobalsEditor::_variable_deleted), CONNECT_DEFERRED);
457
458 interface = memnew(ShaderGlobalsEditorInterface);
459 interface->connect("var_changed", callable_mp(this, &ShaderGlobalsEditor::_changed));
460}
461
462ShaderGlobalsEditor::~ShaderGlobalsEditor() {
463 memdelete(interface);
464}
465