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 "scene/gui/label.h" |
34 | |
35 | void TextureLayeredEditor::gui_input(const Ref<InputEvent> &p_event) { |
36 | ERR_FAIL_COND(p_event.is_null()); |
37 | |
38 | Ref<InputEventMouseMotion> mm = p_event; |
39 | if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) { |
40 | y_rot += -mm->get_relative().x * 0.01; |
41 | x_rot += mm->get_relative().y * 0.01; |
42 | _update_material(); |
43 | } |
44 | } |
45 | |
46 | void TextureLayeredEditor::_texture_rect_draw() { |
47 | texture_rect->draw_rect(Rect2(Point2(), texture_rect->get_size()), Color(1, 1, 1, 1)); |
48 | } |
49 | |
50 | void TextureLayeredEditor::_notification(int p_what) { |
51 | switch (p_what) { |
52 | case NOTIFICATION_RESIZED: { |
53 | _texture_rect_update_area(); |
54 | } break; |
55 | |
56 | case NOTIFICATION_DRAW: { |
57 | Ref<Texture2D> checkerboard = get_editor_theme_icon(SNAME("Checkerboard" )); |
58 | Size2 size = get_size(); |
59 | |
60 | draw_texture_rect(checkerboard, Rect2(Point2(), size), true); |
61 | } break; |
62 | } |
63 | } |
64 | |
65 | void TextureLayeredEditor::_texture_changed() { |
66 | if (!is_visible()) { |
67 | return; |
68 | } |
69 | queue_redraw(); |
70 | } |
71 | |
72 | void TextureLayeredEditor::_update_material() { |
73 | materials[0]->set_shader_parameter("layer" , layer->get_value()); |
74 | materials[2]->set_shader_parameter("layer" , layer->get_value()); |
75 | materials[texture->get_layered_type()]->set_shader_parameter("tex" , texture->get_rid()); |
76 | |
77 | Vector3 v(1, 1, 1); |
78 | v.normalize(); |
79 | |
80 | Basis b; |
81 | b.rotate(Vector3(1, 0, 0), x_rot); |
82 | b.rotate(Vector3(0, 1, 0), y_rot); |
83 | |
84 | materials[1]->set_shader_parameter("normal" , v); |
85 | materials[1]->set_shader_parameter("rot" , b); |
86 | materials[2]->set_shader_parameter("normal" , v); |
87 | materials[2]->set_shader_parameter("rot" , b); |
88 | |
89 | String format = Image::get_format_name(texture->get_format()); |
90 | |
91 | String text; |
92 | if (texture->get_layered_type() == TextureLayered::LAYERED_TYPE_2D_ARRAY) { |
93 | text = itos(texture->get_width()) + "x" + itos(texture->get_height()) + " (x " + itos(texture->get_layers()) + ")" + format; |
94 | } else if (texture->get_layered_type() == TextureLayered::LAYERED_TYPE_CUBEMAP) { |
95 | text = itos(texture->get_width()) + "x" + itos(texture->get_height()) + " " + format; |
96 | } else if (texture->get_layered_type() == TextureLayered::LAYERED_TYPE_CUBEMAP_ARRAY) { |
97 | text = itos(texture->get_width()) + "x" + itos(texture->get_height()) + " (x " + itos(texture->get_layers() / 6) + ")" + format; |
98 | } |
99 | |
100 | info->set_text(text); |
101 | } |
102 | |
103 | void TextureLayeredEditor::_make_shaders() { |
104 | shaders[0].instantiate(); |
105 | shaders[0]->set_code(R"( |
106 | // TextureLayeredEditor preview shader (2D array). |
107 | |
108 | shader_type canvas_item; |
109 | |
110 | uniform sampler2DArray tex; |
111 | uniform float layer; |
112 | |
113 | void fragment() { |
114 | COLOR = textureLod(tex, vec3(UV, layer), 0.0); |
115 | } |
116 | )" ); |
117 | |
118 | shaders[1].instantiate(); |
119 | shaders[1]->set_code(R"( |
120 | // TextureLayeredEditor preview shader (cubemap). |
121 | |
122 | shader_type canvas_item; |
123 | |
124 | uniform samplerCube tex; |
125 | uniform vec3 normal; |
126 | uniform mat3 rot; |
127 | |
128 | void fragment() { |
129 | vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z)); |
130 | COLOR = textureLod(tex, n, 0.0); |
131 | } |
132 | )" ); |
133 | |
134 | shaders[2].instantiate(); |
135 | shaders[2]->set_code(R"( |
136 | // TextureLayeredEditor preview shader (cubemap array). |
137 | |
138 | shader_type canvas_item; |
139 | |
140 | uniform samplerCubeArray tex; |
141 | uniform vec3 normal; |
142 | uniform mat3 rot; |
143 | uniform float layer; |
144 | |
145 | void fragment() { |
146 | vec3 n = rot * normalize(vec3(normal.xy * (UV * 2.0 - 1.0), normal.z)); |
147 | COLOR = textureLod(tex, vec4(n, layer), 0.0); |
148 | } |
149 | )" ); |
150 | |
151 | for (int i = 0; i < 3; i++) { |
152 | materials[i].instantiate(); |
153 | materials[i]->set_shader(shaders[i]); |
154 | } |
155 | } |
156 | |
157 | void TextureLayeredEditor::_texture_rect_update_area() { |
158 | Size2 size = get_size(); |
159 | int tex_width = texture->get_width() * size.height / texture->get_height(); |
160 | int tex_height = size.height; |
161 | |
162 | if (tex_width > size.width) { |
163 | tex_width = size.width; |
164 | tex_height = texture->get_height() * tex_width / texture->get_width(); |
165 | } |
166 | |
167 | // Prevent the texture from being unpreviewable after the rescale, so that we can still see something |
168 | if (tex_height <= 0) { |
169 | tex_height = 1; |
170 | } |
171 | if (tex_width <= 0) { |
172 | tex_width = 1; |
173 | } |
174 | |
175 | int ofs_x = (size.width - tex_width) / 2; |
176 | int ofs_y = (size.height - tex_height) / 2; |
177 | |
178 | texture_rect->set_position(Vector2(ofs_x, ofs_y)); |
179 | texture_rect->set_size(Vector2(tex_width, tex_height)); |
180 | } |
181 | |
182 | void TextureLayeredEditor::edit(Ref<TextureLayered> p_texture) { |
183 | if (!texture.is_null()) { |
184 | texture->disconnect_changed(callable_mp(this, &TextureLayeredEditor::_texture_changed)); |
185 | } |
186 | |
187 | texture = p_texture; |
188 | |
189 | if (!texture.is_null()) { |
190 | if (shaders[0].is_null()) { |
191 | _make_shaders(); |
192 | } |
193 | |
194 | texture->connect_changed(callable_mp(this, &TextureLayeredEditor::_texture_changed)); |
195 | queue_redraw(); |
196 | texture_rect->set_material(materials[texture->get_layered_type()]); |
197 | setting = true; |
198 | if (texture->get_layered_type() == TextureLayered::LAYERED_TYPE_2D_ARRAY) { |
199 | layer->set_max(texture->get_layers() - 1); |
200 | layer->set_value(0); |
201 | layer->show(); |
202 | } else if (texture->get_layered_type() == TextureLayered::LAYERED_TYPE_CUBEMAP_ARRAY) { |
203 | layer->set_max(texture->get_layers() / 6 - 1); |
204 | layer->set_value(0); |
205 | layer->show(); |
206 | } else { |
207 | layer->hide(); |
208 | } |
209 | x_rot = 0; |
210 | y_rot = 0; |
211 | _update_material(); |
212 | setting = false; |
213 | _texture_rect_update_area(); |
214 | } else { |
215 | hide(); |
216 | } |
217 | } |
218 | |
219 | TextureLayeredEditor::TextureLayeredEditor() { |
220 | set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED); |
221 | set_custom_minimum_size(Size2(1, 150)); |
222 | texture_rect = memnew(Control); |
223 | texture_rect->connect("draw" , callable_mp(this, &TextureLayeredEditor::_texture_rect_draw)); |
224 | texture_rect->set_mouse_filter(MOUSE_FILTER_IGNORE); |
225 | add_child(texture_rect); |
226 | |
227 | layer = memnew(SpinBox); |
228 | layer->set_step(1); |
229 | layer->set_max(100); |
230 | add_child(layer); |
231 | layer->set_anchor(SIDE_RIGHT, 1); |
232 | layer->set_anchor(SIDE_LEFT, 1); |
233 | layer->set_h_grow_direction(GROW_DIRECTION_BEGIN); |
234 | layer->set_modulate(Color(1, 1, 1, 0.8)); |
235 | info = memnew(Label); |
236 | add_child(info); |
237 | info->set_anchor(SIDE_RIGHT, 1); |
238 | info->set_anchor(SIDE_LEFT, 1); |
239 | info->set_anchor(SIDE_BOTTOM, 1); |
240 | info->set_anchor(SIDE_TOP, 1); |
241 | info->set_h_grow_direction(GROW_DIRECTION_BEGIN); |
242 | info->set_v_grow_direction(GROW_DIRECTION_BEGIN); |
243 | info->add_theme_color_override("font_color" , Color(1, 1, 1, 1)); |
244 | info->add_theme_color_override("font_shadow_color" , Color(0, 0, 0, 0.5)); |
245 | info->add_theme_constant_override("shadow_outline_size" , 1); |
246 | info->add_theme_constant_override("shadow_offset_x" , 2); |
247 | info->add_theme_constant_override("shadow_offset_y" , 2); |
248 | |
249 | setting = false; |
250 | layer->connect("value_changed" , callable_mp(this, &TextureLayeredEditor::_layer_changed)); |
251 | } |
252 | |
253 | TextureLayeredEditor::~TextureLayeredEditor() { |
254 | } |
255 | |
256 | // |
257 | bool EditorInspectorPluginLayeredTexture::can_handle(Object *p_object) { |
258 | return Object::cast_to<TextureLayered>(p_object) != nullptr; |
259 | } |
260 | |
261 | void EditorInspectorPluginLayeredTexture::parse_begin(Object *p_object) { |
262 | TextureLayered *texture = Object::cast_to<TextureLayered>(p_object); |
263 | if (!texture) { |
264 | return; |
265 | } |
266 | Ref<TextureLayered> m(texture); |
267 | |
268 | TextureLayeredEditor *editor = memnew(TextureLayeredEditor); |
269 | editor->edit(m); |
270 | add_custom_control(editor); |
271 | } |
272 | |
273 | TextureLayeredEditorPlugin::TextureLayeredEditorPlugin() { |
274 | Ref<EditorInspectorPluginLayeredTexture> plugin; |
275 | plugin.instantiate(); |
276 | add_inspector_plugin(plugin); |
277 | } |
278 | |