Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/scene/texture/texture_3d_editor_plugin.cpp
20985 views
1
/**************************************************************************/
2
/* texture_3d_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_3d_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 *texture_3d_shader = R"(
41
// Texture3DEditor preview shader.
42
43
shader_type canvas_item;
44
45
uniform sampler3D tex;
46
uniform float layer;
47
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
void Texture3DEditor::_texture_rect_draw() {
75
texture_rect->draw_rect(Rect2(Point2(), texture_rect->get_size()), Color(1, 1, 1, 1));
76
}
77
78
void Texture3DEditor::_notification(int p_what) {
79
switch (p_what) {
80
case NOTIFICATION_RESIZED: {
81
_texture_rect_update_area();
82
} break;
83
84
case NOTIFICATION_DRAW: {
85
Ref<Texture2D> checkerboard = get_editor_theme_icon(SNAME("Checkerboard"));
86
draw_texture_rect(checkerboard, texture_rect->get_rect(), true);
87
_draw_outline();
88
} break;
89
90
case NOTIFICATION_THEME_CHANGED: {
91
if (info) {
92
Ref<Font> metadata_label_font = get_theme_font(SNAME("expression"), EditorStringName(EditorFonts));
93
info->add_theme_font_override(SceneStringName(font), metadata_label_font);
94
}
95
theme_cache.outline_color = get_theme_color(SNAME("extra_border_color_1"), EditorStringName(Editor));
96
} break;
97
}
98
}
99
100
void Texture3DEditor::_texture_changed() {
101
if (!is_visible()) {
102
return;
103
}
104
105
setting = true;
106
_update_gui();
107
setting = false;
108
109
_update_material(true);
110
queue_redraw();
111
}
112
113
void Texture3DEditor::_update_material(bool p_texture_changed) {
114
texture_material->set_shader_parameter("layer", (layer->get_value() + 0.5) / texture->get_depth());
115
116
if (p_texture_changed) {
117
texture_material->set_shader_parameter("tex", texture->get_rid());
118
}
119
120
texture_material->set_shader_parameter("u_channel_factors", channel_selector->get_selected_channel_factors());
121
}
122
123
void Texture3DEditor::_draw_outline() {
124
const float outline_width = Math::round(EDSCALE);
125
const Rect2 outline_rect = texture_rect->get_rect().grow(outline_width * 0.5);
126
draw_rect(outline_rect, theme_cache.outline_color, false, outline_width);
127
}
128
129
void Texture3DEditor::_texture_rect_update_area() {
130
Size2 size = get_size();
131
int tex_width = texture->get_width() * size.height / texture->get_height();
132
int tex_height = size.height;
133
134
if (tex_width > size.width) {
135
tex_width = size.width;
136
tex_height = texture->get_height() * tex_width / texture->get_width();
137
}
138
139
// Prevent the texture from being unpreviewable after the rescale, so that we can still see something
140
if (tex_height <= 0) {
141
tex_height = 1;
142
}
143
if (tex_width <= 0) {
144
tex_width = 1;
145
}
146
147
int ofs_x = (size.width - tex_width) / 2;
148
int ofs_y = (size.height - tex_height) / 2;
149
150
texture_rect->set_position(Vector2(ofs_x, ofs_y - Math::round(EDSCALE)));
151
texture_rect->set_size(Vector2(tex_width, tex_height));
152
}
153
154
void Texture3DEditor::_update_gui() {
155
if (texture.is_null()) {
156
return;
157
}
158
159
_texture_rect_update_area();
160
161
layer->set_max(texture->get_depth() - 1);
162
163
const Image::Format format = texture->get_format();
164
const String format_name = Image::get_format_name(format);
165
166
if (texture->has_mipmaps()) {
167
const int mip_count = Image::get_image_required_mipmaps(texture->get_width(), texture->get_height(), format);
168
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, true) * texture->get_depth();
169
170
info->set_text(vformat(String::utf8("%d×%d×%d %s\n") + TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"),
171
texture->get_width(),
172
texture->get_height(),
173
texture->get_depth(),
174
format_name,
175
mip_count,
176
String::humanize_size(memory)));
177
178
} else {
179
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, false) * texture->get_depth();
180
181
info->set_text(vformat(String::utf8("%d×%d×%d %s\n") + TTR("No Mipmaps") + "\n" + TTR("Memory: %s"),
182
texture->get_width(),
183
texture->get_height(),
184
texture->get_depth(),
185
format_name,
186
String::humanize_size(memory)));
187
}
188
189
const uint32_t components_mask = Image::get_format_component_mask(format);
190
if (is_power_of_2(components_mask)) {
191
// Only one channel available, no point in showing a channel selector.
192
channel_selector->hide();
193
} else {
194
channel_selector->show();
195
channel_selector->set_available_channels_mask(components_mask);
196
}
197
}
198
199
void Texture3DEditor::on_selected_channels_changed() {
200
_update_material(false);
201
}
202
203
void Texture3DEditor::init_shaders() {
204
texture_shader.instantiate();
205
texture_shader->set_code(texture_3d_shader);
206
}
207
208
void Texture3DEditor::finish_shaders() {
209
texture_shader.unref();
210
}
211
212
void Texture3DEditor::edit(Ref<Texture3D> p_texture) {
213
if (texture.is_valid()) {
214
texture->disconnect_changed(callable_mp(this, &Texture3DEditor::_texture_changed));
215
}
216
217
texture = p_texture;
218
219
if (texture.is_valid()) {
220
if (texture_material.is_null()) {
221
texture_material.instantiate();
222
texture_material->set_shader(texture_shader);
223
}
224
225
texture->connect_changed(callable_mp(this, &Texture3DEditor::_texture_changed));
226
texture_rect->set_material(texture_material);
227
228
setting = true;
229
layer->set_value(0);
230
layer->show();
231
_update_gui();
232
setting = false;
233
234
_update_material(true);
235
queue_redraw();
236
237
} else {
238
hide();
239
}
240
}
241
242
Texture3DEditor::Texture3DEditor() {
243
set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
244
set_custom_minimum_size(Size2(1, 256.0) * EDSCALE);
245
246
texture_rect = memnew(Control);
247
texture_rect->set_mouse_filter(MOUSE_FILTER_IGNORE);
248
texture_rect->connect(SceneStringName(draw), callable_mp(this, &Texture3DEditor::_texture_rect_draw));
249
250
add_child(texture_rect);
251
252
layer = memnew(SpinBox);
253
layer->set_step(1);
254
layer->set_max(100);
255
256
layer->set_modulate(Color(1, 1, 1, 0.8));
257
layer->set_h_grow_direction(GROW_DIRECTION_BEGIN);
258
layer->set_anchor(SIDE_RIGHT, 1);
259
layer->set_anchor(SIDE_LEFT, 1);
260
layer->connect(SceneStringName(value_changed), callable_mp(this, &Texture3DEditor::_layer_changed));
261
262
add_child(layer);
263
264
channel_selector = memnew(ColorChannelSelector);
265
channel_selector->connect("selected_channels_changed", callable_mp(this, &Texture3DEditor::on_selected_channels_changed));
266
channel_selector->set_anchors_preset(Control::PRESET_TOP_LEFT);
267
add_child(channel_selector);
268
269
info = memnew(Label);
270
info->set_focus_mode(FOCUS_ACCESSIBILITY);
271
info->add_theme_color_override(SceneStringName(font_color), Color(1, 1, 1));
272
info->add_theme_color_override("font_shadow_color", Color(0, 0, 0));
273
info->add_theme_font_size_override(SceneStringName(font_size), 14 * EDSCALE);
274
info->add_theme_color_override("font_outline_color", Color(0, 0, 0));
275
info->add_theme_constant_override("outline_size", 8 * EDSCALE);
276
277
info->set_h_grow_direction(GROW_DIRECTION_BEGIN);
278
info->set_v_grow_direction(GROW_DIRECTION_BEGIN);
279
info->set_h_size_flags(Control::SIZE_SHRINK_END);
280
info->set_v_size_flags(Control::SIZE_SHRINK_END);
281
info->set_anchor(SIDE_RIGHT, 1);
282
info->set_anchor(SIDE_LEFT, 1);
283
info->set_anchor(SIDE_BOTTOM, 1);
284
info->set_anchor(SIDE_TOP, 1);
285
286
add_child(info);
287
}
288
289
Texture3DEditor::~Texture3DEditor() {
290
if (texture.is_valid()) {
291
texture->disconnect_changed(callable_mp(this, &Texture3DEditor::_texture_changed));
292
}
293
}
294
295
bool EditorInspectorPlugin3DTexture::can_handle(Object *p_object) {
296
return Object::cast_to<Texture3D>(p_object) != nullptr;
297
}
298
299
void EditorInspectorPlugin3DTexture::parse_begin(Object *p_object) {
300
Texture3D *texture = Object::cast_to<Texture3D>(p_object);
301
if (!texture) {
302
return;
303
}
304
Ref<Texture3D> m(texture);
305
306
Texture3DEditor *editor = memnew(Texture3DEditor);
307
editor->edit(m);
308
add_custom_control(editor);
309
}
310
311
Texture3DEditorPlugin::Texture3DEditorPlugin() {
312
Ref<EditorInspectorPlugin3DTexture> plugin;
313
plugin.instantiate();
314
add_inspector_plugin(plugin);
315
}
316
317