Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/scene/texture/texture_layered_editor_plugin.cpp
9912 views
1
/**************************************************************************/
2
/* texture_layered_editor_plugin.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 "texture_layered_editor_plugin.h"
32
33
#include "editor/editor_string_names.h"
34
#include "editor/scene/texture/color_channel_selector.h"
35
#include "editor/themes/editor_scale.h"
36
#include "scene/gui/label.h"
37
38
// Shader sources.
39
40
constexpr const char *array_2d_shader = R"(
41
// TextureLayeredEditor preview shader (2D array).
42
43
shader_type canvas_item;
44
45
uniform sampler2DArray tex;
46
uniform float layer;
47
uniform vec4 u_channel_factors = vec4(1.0);
48
49
vec4 filter_preview_colors(vec4 input_color, vec4 factors) {
50
// Filter RGB.
51
vec4 output_color = input_color * vec4(factors.rgb, input_color.a);
52
53
// Remove transparency when alpha is not enabled.
54
output_color.a = mix(1.0, output_color.a, factors.a);
55
56
// Switch to opaque grayscale when visualizing only one channel.
57
float csum = factors.r + factors.g + factors.b + factors.a;
58
float single = clamp(2.0 - csum, 0.0, 1.0);
59
for (int i = 0; i < 4; i++) {
60
float c = input_color[i];
61
output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);
62
}
63
64
return output_color;
65
}
66
67
void fragment() {
68
COLOR = textureLod(tex, vec3(UV, layer), 0.0);
69
COLOR = filter_preview_colors(COLOR, u_channel_factors);
70
}
71
)";
72
73
constexpr const char *cubemap_shader = R"(
74
// TextureLayeredEditor preview shader (cubemap).
75
76
shader_type canvas_item;
77
78
uniform samplerCube tex;
79
uniform vec3 normal;
80
uniform mat3 rot;
81
82
uniform vec4 u_channel_factors = vec4(1.0);
83
84
vec4 filter_preview_colors(vec4 input_color, vec4 factors) {
85
// Filter RGB.
86
vec4 output_color = input_color * vec4(factors.rgb, input_color.a);
87
88
// Remove transparency when alpha is not enabled.
89
output_color.a = mix(1.0, output_color.a, factors.a);
90
91
// Switch to opaque grayscale when visualizing only one channel.
92
float csum = factors.r + factors.g + factors.b + factors.a;
93
float single = clamp(2.0 - csum, 0.0, 1.0);
94
for (int i = 0; i < 4; i++) {
95
float c = input_color[i];
96
output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);
97
}
98
99
return output_color;
100
}
101
102
void fragment() {
103
vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z));
104
COLOR = textureLod(tex, n, 0.0);
105
COLOR = filter_preview_colors(COLOR, u_channel_factors);
106
}
107
)";
108
109
constexpr const char *cubemap_array_shader = R"(
110
// TextureLayeredEditor preview shader (cubemap array).
111
112
shader_type canvas_item;
113
uniform samplerCubeArray tex;
114
uniform vec3 normal;
115
uniform mat3 rot;
116
uniform float layer;
117
118
uniform vec4 u_channel_factors = vec4(1.0);
119
120
vec4 filter_preview_colors(vec4 input_color, vec4 factors) {
121
// Filter RGB.
122
vec4 output_color = input_color * vec4(factors.rgb, input_color.a);
123
124
// Remove transparency when alpha is not enabled.
125
output_color.a = mix(1.0, output_color.a, factors.a);
126
127
// Switch to opaque grayscale when visualizing only one channel.
128
float csum = factors.r + factors.g + factors.b + factors.a;
129
float single = clamp(2.0 - csum, 0.0, 1.0);
130
for (int i = 0; i < 4; i++) {
131
float c = input_color[i];
132
output_color = mix(output_color, vec4(c, c, c, 1.0), factors[i] * single);
133
}
134
135
return output_color;
136
}
137
138
void fragment() {
139
vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z));
140
COLOR = textureLod(tex, vec4(n, layer), 0.0);
141
COLOR = filter_preview_colors(COLOR, u_channel_factors);
142
}
143
)";
144
145
void TextureLayeredEditor::gui_input(const Ref<InputEvent> &p_event) {
146
ERR_FAIL_COND(p_event.is_null());
147
148
Ref<InputEventMouseMotion> mm = p_event;
149
if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
150
y_rot += -mm->get_relative().x * 0.01;
151
x_rot += -mm->get_relative().y * 0.01;
152
153
_update_material(false);
154
}
155
}
156
157
void TextureLayeredEditor::_texture_rect_draw() {
158
texture_rect->draw_rect(Rect2(Point2(), texture_rect->get_size()), Color(1, 1, 1, 1));
159
}
160
161
void TextureLayeredEditor::_update_gui() {
162
if (texture.is_null()) {
163
return;
164
}
165
166
_texture_rect_update_area();
167
168
const Image::Format format = texture->get_format();
169
const String format_name = Image::get_format_name(format);
170
String texture_info;
171
172
switch (texture->get_layered_type()) {
173
case TextureLayered::LAYERED_TYPE_2D_ARRAY: {
174
layer->set_max(texture->get_layers() - 1);
175
176
texture_info = vformat(String::utf8("%d×%d (×%d) %s\n"),
177
texture->get_width(),
178
texture->get_height(),
179
texture->get_layers(),
180
format_name);
181
182
} break;
183
case TextureLayered::LAYERED_TYPE_CUBEMAP: {
184
layer->hide();
185
186
texture_info = vformat(String::utf8("%d×%d %s\n"),
187
texture->get_width(),
188
texture->get_height(),
189
format_name);
190
191
} break;
192
case TextureLayered::LAYERED_TYPE_CUBEMAP_ARRAY: {
193
layer->set_max(texture->get_layers() / 6 - 1);
194
195
texture_info = vformat(String::utf8("%d×%d (×%d) %s\n"),
196
texture->get_width(),
197
texture->get_height(),
198
texture->get_layers() / 6,
199
format_name);
200
201
} break;
202
203
default: {
204
}
205
}
206
207
if (texture->has_mipmaps()) {
208
const int mip_count = Image::get_image_required_mipmaps(texture->get_width(), texture->get_height(), format);
209
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, true) * texture->get_layers();
210
211
texture_info += vformat(TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"),
212
mip_count,
213
String::humanize_size(memory));
214
215
} else {
216
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, false) * texture->get_layers();
217
218
texture_info += vformat(TTR("No Mipmaps") + "\n" + TTR("Memory: %s"),
219
String::humanize_size(memory));
220
}
221
222
info->set_text(texture_info);
223
224
const uint32_t components_mask = Image::get_format_component_mask(format);
225
if (is_power_of_2(components_mask)) {
226
// Only one channel available, no point in showing a channel selector.
227
channel_selector->hide();
228
} else {
229
channel_selector->show();
230
channel_selector->set_available_channels_mask(components_mask);
231
}
232
}
233
234
void TextureLayeredEditor::_notification(int p_what) {
235
switch (p_what) {
236
case NOTIFICATION_RESIZED: {
237
_texture_rect_update_area();
238
} break;
239
240
case NOTIFICATION_DRAW: {
241
Ref<Texture2D> checkerboard = get_editor_theme_icon(SNAME("Checkerboard"));
242
draw_texture_rect(checkerboard, texture_rect->get_rect(), true);
243
_draw_outline();
244
} break;
245
246
case NOTIFICATION_THEME_CHANGED: {
247
if (info) {
248
Ref<Font> metadata_label_font = get_theme_font(SNAME("expression"), EditorStringName(EditorFonts));
249
info->add_theme_font_override(SceneStringName(font), metadata_label_font);
250
}
251
theme_cache.outline_color = get_theme_color(SNAME("extra_border_color_1"), EditorStringName(Editor));
252
} break;
253
}
254
}
255
256
void TextureLayeredEditor::_texture_changed() {
257
if (!is_visible()) {
258
return;
259
}
260
261
setting = true;
262
_update_gui();
263
setting = false;
264
265
_update_material(true);
266
queue_redraw();
267
}
268
269
void TextureLayeredEditor::_update_material(bool p_texture_changed) {
270
materials[0]->set_shader_parameter("layer", layer->get_value());
271
materials[2]->set_shader_parameter("layer", layer->get_value());
272
273
Vector3 v(1, 1, 1);
274
v.normalize();
275
276
Basis b;
277
b.rotate(Vector3(1, 0, 0), x_rot);
278
b.rotate(Vector3(0, 1, 0), y_rot);
279
280
materials[1]->set_shader_parameter("normal", v);
281
materials[1]->set_shader_parameter("rot", b);
282
materials[2]->set_shader_parameter("normal", v);
283
materials[2]->set_shader_parameter("rot", b);
284
285
if (p_texture_changed) {
286
materials[texture->get_layered_type()]->set_shader_parameter("tex", texture->get_rid());
287
}
288
289
const Vector4 channel_factors = channel_selector->get_selected_channel_factors();
290
for (unsigned int i = 0; i < 3; ++i) {
291
materials[i]->set_shader_parameter("u_channel_factors", channel_factors);
292
}
293
}
294
295
void TextureLayeredEditor::on_selected_channels_changed() {
296
_update_material(false);
297
}
298
299
void TextureLayeredEditor::_draw_outline() {
300
const float outline_width = Math::round(EDSCALE);
301
const Rect2 outline_rect = texture_rect->get_rect().grow(outline_width * 0.5);
302
draw_rect(outline_rect, theme_cache.outline_color, false, outline_width);
303
}
304
305
void TextureLayeredEditor::_make_shaders() {
306
shaders[0].instantiate();
307
shaders[0]->set_code(array_2d_shader);
308
309
shaders[1].instantiate();
310
shaders[1]->set_code(cubemap_shader);
311
312
shaders[2].instantiate();
313
shaders[2]->set_code(cubemap_array_shader);
314
315
for (int i = 0; i < 3; i++) {
316
materials[i].instantiate();
317
materials[i]->set_shader(shaders[i]);
318
}
319
}
320
321
void TextureLayeredEditor::_texture_rect_update_area() {
322
Size2 size = get_size();
323
int tex_width = texture->get_width() * size.height / texture->get_height();
324
int tex_height = size.height;
325
326
if (tex_width > size.width) {
327
tex_width = size.width;
328
tex_height = texture->get_height() * tex_width / texture->get_width();
329
}
330
331
// Prevent the texture from being unpreviewable after the rescale, so that we can still see something
332
if (tex_height <= 0) {
333
tex_height = 1;
334
}
335
if (tex_width <= 0) {
336
tex_width = 1;
337
}
338
339
int ofs_x = (size.width - tex_width) / 2;
340
int ofs_y = (size.height - tex_height) / 2;
341
342
texture_rect->set_position(Vector2(ofs_x, ofs_y - Math::round(EDSCALE)));
343
texture_rect->set_size(Vector2(tex_width, tex_height));
344
}
345
346
void TextureLayeredEditor::edit(Ref<TextureLayered> p_texture) {
347
if (texture.is_valid()) {
348
texture->disconnect_changed(callable_mp(this, &TextureLayeredEditor::_texture_changed));
349
}
350
351
texture = p_texture;
352
353
if (texture.is_valid()) {
354
if (shaders[0].is_null()) {
355
_make_shaders();
356
}
357
358
texture->connect_changed(callable_mp(this, &TextureLayeredEditor::_texture_changed));
359
texture_rect->set_material(materials[texture->get_layered_type()]);
360
361
setting = true;
362
layer->set_value(0);
363
layer->show();
364
_update_gui();
365
setting = false;
366
367
x_rot = 0;
368
y_rot = 0;
369
370
_update_material(true);
371
queue_redraw();
372
373
} else {
374
hide();
375
}
376
}
377
378
TextureLayeredEditor::TextureLayeredEditor() {
379
set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
380
set_custom_minimum_size(Size2(0, 256.0) * EDSCALE);
381
382
texture_rect = memnew(Control);
383
texture_rect->set_mouse_filter(MOUSE_FILTER_IGNORE);
384
texture_rect->connect(SceneStringName(draw), callable_mp(this, &TextureLayeredEditor::_texture_rect_draw));
385
386
add_child(texture_rect);
387
388
layer = memnew(SpinBox);
389
layer->set_step(1);
390
layer->set_max(100);
391
392
layer->set_modulate(Color(1, 1, 1, 0.8));
393
layer->set_h_grow_direction(GROW_DIRECTION_BEGIN);
394
layer->set_anchor(SIDE_RIGHT, 1);
395
layer->set_anchor(SIDE_LEFT, 1);
396
layer->connect(SceneStringName(value_changed), callable_mp(this, &TextureLayeredEditor::_layer_changed));
397
398
add_child(layer);
399
400
channel_selector = memnew(ColorChannelSelector);
401
channel_selector->connect("selected_channels_changed", callable_mp(this, &TextureLayeredEditor::on_selected_channels_changed));
402
channel_selector->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);
403
add_child(channel_selector);
404
405
info = memnew(Label);
406
info->set_focus_mode(FOCUS_ACCESSIBILITY);
407
info->add_theme_color_override(SceneStringName(font_color), Color(1, 1, 1));
408
info->add_theme_color_override("font_shadow_color", Color(0, 0, 0));
409
info->add_theme_font_size_override(SceneStringName(font_size), 14 * EDSCALE);
410
info->add_theme_color_override("font_outline_color", Color(0, 0, 0));
411
info->add_theme_constant_override("outline_size", 8 * EDSCALE);
412
413
info->set_h_grow_direction(GROW_DIRECTION_BEGIN);
414
info->set_v_grow_direction(GROW_DIRECTION_BEGIN);
415
info->set_h_size_flags(Control::SIZE_SHRINK_END);
416
info->set_v_size_flags(Control::SIZE_SHRINK_END);
417
info->set_anchor(SIDE_RIGHT, 1);
418
info->set_anchor(SIDE_LEFT, 1);
419
info->set_anchor(SIDE_BOTTOM, 1);
420
info->set_anchor(SIDE_TOP, 1);
421
422
add_child(info);
423
}
424
425
bool EditorInspectorPluginLayeredTexture::can_handle(Object *p_object) {
426
return Object::cast_to<TextureLayered>(p_object) != nullptr;
427
}
428
429
void EditorInspectorPluginLayeredTexture::parse_begin(Object *p_object) {
430
TextureLayered *texture = Object::cast_to<TextureLayered>(p_object);
431
if (!texture) {
432
return;
433
}
434
Ref<TextureLayered> m(texture);
435
436
TextureLayeredEditor *editor = memnew(TextureLayeredEditor);
437
editor->edit(m);
438
add_custom_control(editor);
439
}
440
441
TextureLayeredEditorPlugin::TextureLayeredEditorPlugin() {
442
Ref<EditorInspectorPluginLayeredTexture> plugin;
443
plugin.instantiate();
444
add_inspector_plugin(plugin);
445
}
446
447