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