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
9912 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
material->set_shader_parameter("layer", (layer->get_value() + 0.5) / texture->get_depth());
115
116
if (p_texture_changed) {
117
material->set_shader_parameter("tex", texture->get_rid());
118
}
119
120
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::_make_shaders() {
130
shader.instantiate();
131
shader->set_code(texture_3d_shader);
132
133
material.instantiate();
134
material->set_shader(shader);
135
}
136
137
void Texture3DEditor::_texture_rect_update_area() {
138
Size2 size = get_size();
139
int tex_width = texture->get_width() * size.height / texture->get_height();
140
int tex_height = size.height;
141
142
if (tex_width > size.width) {
143
tex_width = size.width;
144
tex_height = texture->get_height() * tex_width / texture->get_width();
145
}
146
147
// Prevent the texture from being unpreviewable after the rescale, so that we can still see something
148
if (tex_height <= 0) {
149
tex_height = 1;
150
}
151
if (tex_width <= 0) {
152
tex_width = 1;
153
}
154
155
int ofs_x = (size.width - tex_width) / 2;
156
int ofs_y = (size.height - tex_height) / 2;
157
158
texture_rect->set_position(Vector2(ofs_x, ofs_y - Math::round(EDSCALE)));
159
texture_rect->set_size(Vector2(tex_width, tex_height));
160
}
161
162
void Texture3DEditor::_update_gui() {
163
if (texture.is_null()) {
164
return;
165
}
166
167
_texture_rect_update_area();
168
169
layer->set_max(texture->get_depth() - 1);
170
171
const Image::Format format = texture->get_format();
172
const String format_name = Image::get_format_name(format);
173
174
if (texture->has_mipmaps()) {
175
const int mip_count = Image::get_image_required_mipmaps(texture->get_width(), texture->get_height(), format);
176
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, true) * texture->get_depth();
177
178
info->set_text(vformat(String::utf8("%d×%d×%d %s\n") + TTR("%s Mipmaps") + "\n" + TTR("Memory: %s"),
179
texture->get_width(),
180
texture->get_height(),
181
texture->get_depth(),
182
format_name,
183
mip_count,
184
String::humanize_size(memory)));
185
186
} else {
187
const int memory = Image::get_image_data_size(texture->get_width(), texture->get_height(), format, false) * texture->get_depth();
188
189
info->set_text(vformat(String::utf8("%d×%d×%d %s\n") + TTR("No Mipmaps") + "\n" + TTR("Memory: %s"),
190
texture->get_width(),
191
texture->get_height(),
192
texture->get_depth(),
193
format_name,
194
String::humanize_size(memory)));
195
}
196
197
const uint32_t components_mask = Image::get_format_component_mask(format);
198
if (is_power_of_2(components_mask)) {
199
// Only one channel available, no point in showing a channel selector.
200
channel_selector->hide();
201
} else {
202
channel_selector->show();
203
channel_selector->set_available_channels_mask(components_mask);
204
}
205
}
206
207
void Texture3DEditor::on_selected_channels_changed() {
208
_update_material(false);
209
}
210
211
void Texture3DEditor::edit(Ref<Texture3D> p_texture) {
212
if (texture.is_valid()) {
213
texture->disconnect_changed(callable_mp(this, &Texture3DEditor::_texture_changed));
214
}
215
216
texture = p_texture;
217
218
if (texture.is_valid()) {
219
if (shader.is_null()) {
220
_make_shaders();
221
}
222
223
texture->connect_changed(callable_mp(this, &Texture3DEditor::_texture_changed));
224
texture_rect->set_material(material);
225
226
setting = true;
227
layer->set_value(0);
228
layer->show();
229
_update_gui();
230
setting = false;
231
232
_update_material(true);
233
queue_redraw();
234
235
} else {
236
hide();
237
}
238
}
239
240
Texture3DEditor::Texture3DEditor() {
241
set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
242
set_custom_minimum_size(Size2(1, 256.0) * EDSCALE);
243
244
texture_rect = memnew(Control);
245
texture_rect->set_mouse_filter(MOUSE_FILTER_IGNORE);
246
texture_rect->connect(SceneStringName(draw), callable_mp(this, &Texture3DEditor::_texture_rect_draw));
247
248
add_child(texture_rect);
249
250
layer = memnew(SpinBox);
251
layer->set_step(1);
252
layer->set_max(100);
253
254
layer->set_modulate(Color(1, 1, 1, 0.8));
255
layer->set_h_grow_direction(GROW_DIRECTION_BEGIN);
256
layer->set_anchor(SIDE_RIGHT, 1);
257
layer->set_anchor(SIDE_LEFT, 1);
258
layer->connect(SceneStringName(value_changed), callable_mp(this, &Texture3DEditor::_layer_changed));
259
260
add_child(layer);
261
262
channel_selector = memnew(ColorChannelSelector);
263
channel_selector->connect("selected_channels_changed", callable_mp(this, &Texture3DEditor::on_selected_channels_changed));
264
channel_selector->set_anchors_preset(Control::PRESET_TOP_LEFT);
265
add_child(channel_selector);
266
267
info = memnew(Label);
268
info->set_focus_mode(FOCUS_ACCESSIBILITY);
269
info->add_theme_color_override(SceneStringName(font_color), Color(1, 1, 1));
270
info->add_theme_color_override("font_shadow_color", Color(0, 0, 0));
271
info->add_theme_font_size_override(SceneStringName(font_size), 14 * EDSCALE);
272
info->add_theme_color_override("font_outline_color", Color(0, 0, 0));
273
info->add_theme_constant_override("outline_size", 8 * EDSCALE);
274
275
info->set_h_grow_direction(GROW_DIRECTION_BEGIN);
276
info->set_v_grow_direction(GROW_DIRECTION_BEGIN);
277
info->set_h_size_flags(Control::SIZE_SHRINK_END);
278
info->set_v_size_flags(Control::SIZE_SHRINK_END);
279
info->set_anchor(SIDE_RIGHT, 1);
280
info->set_anchor(SIDE_LEFT, 1);
281
info->set_anchor(SIDE_BOTTOM, 1);
282
info->set_anchor(SIDE_TOP, 1);
283
284
add_child(info);
285
}
286
287
Texture3DEditor::~Texture3DEditor() {
288
if (texture.is_valid()) {
289
texture->disconnect_changed(callable_mp(this, &Texture3DEditor::_texture_changed));
290
}
291
}
292
293
bool EditorInspectorPlugin3DTexture::can_handle(Object *p_object) {
294
return Object::cast_to<Texture3D>(p_object) != nullptr;
295
}
296
297
void EditorInspectorPlugin3DTexture::parse_begin(Object *p_object) {
298
Texture3D *texture = Object::cast_to<Texture3D>(p_object);
299
if (!texture) {
300
return;
301
}
302
Ref<Texture3D> m(texture);
303
304
Texture3DEditor *editor = memnew(Texture3DEditor);
305
editor->edit(m);
306
add_custom_control(editor);
307
}
308
309
Texture3DEditorPlugin::Texture3DEditorPlugin() {
310
Ref<EditorInspectorPlugin3DTexture> plugin;
311
plugin.instantiate();
312
add_inspector_plugin(plugin);
313
}
314
315