1 | /**************************************************************************/ |
2 | /* animation_blend_space_1d_editor.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 "animation_blend_space_1d_editor.h" |
32 | |
33 | #include "core/os/keyboard.h" |
34 | #include "editor/editor_node.h" |
35 | #include "editor/editor_scale.h" |
36 | #include "editor/editor_settings.h" |
37 | #include "editor/editor_string_names.h" |
38 | #include "editor/editor_undo_redo_manager.h" |
39 | #include "editor/gui/editor_file_dialog.h" |
40 | #include "scene/animation/animation_blend_tree.h" |
41 | #include "scene/gui/check_box.h" |
42 | #include "scene/gui/option_button.h" |
43 | #include "scene/gui/panel_container.h" |
44 | |
45 | StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const { |
46 | StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + "blend_position" ; |
47 | return path; |
48 | } |
49 | |
50 | void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) { |
51 | AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree(); |
52 | if (!tree) { |
53 | return; |
54 | } |
55 | |
56 | Ref<InputEventKey> k = p_event; |
57 | |
58 | if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && !k->is_echo()) { |
59 | if (selected_point != -1) { |
60 | if (!read_only) { |
61 | _erase_selected(); |
62 | } |
63 | accept_event(); |
64 | } |
65 | } |
66 | |
67 | Ref<InputEventMouseButton> mb = p_event; |
68 | |
69 | if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) || (mb->get_button_index() == MouseButton::LEFT && tool_create->is_pressed()))) { |
70 | if (!read_only) { |
71 | menu->clear(); |
72 | animations_menu->clear(); |
73 | animations_to_add.clear(); |
74 | |
75 | List<StringName> classes; |
76 | ClassDB::get_inheriters_from_class("AnimationRootNode" , &classes); |
77 | classes.sort_custom<StringName::AlphCompare>(); |
78 | |
79 | menu->add_submenu_item(TTR("Add Animation" ), "animations" ); |
80 | |
81 | if (tree->has_node(tree->get_animation_player())) { |
82 | AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(tree->get_node(tree->get_animation_player())); |
83 | |
84 | if (ap) { |
85 | List<StringName> names; |
86 | ap->get_animation_list(&names); |
87 | |
88 | for (const StringName &E : names) { |
89 | animations_menu->add_icon_item(get_editor_theme_icon(SNAME("Animation" )), E); |
90 | animations_to_add.push_back(E); |
91 | } |
92 | } |
93 | } |
94 | |
95 | for (const StringName &E : classes) { |
96 | String name = String(E).replace_first("AnimationNode" , "" ); |
97 | if (name == "Animation" || name == "StartState" || name == "EndState" ) { |
98 | continue; |
99 | } |
100 | |
101 | int idx = menu->get_item_count(); |
102 | menu->add_item(vformat(TTR("Add %s" ), name), idx); |
103 | menu->set_item_metadata(idx, E); |
104 | } |
105 | |
106 | Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); |
107 | if (clipb.is_valid()) { |
108 | menu->add_separator(); |
109 | menu->add_item(TTR("Paste" ), MENU_PASTE); |
110 | } |
111 | menu->add_separator(); |
112 | menu->add_item(TTR("Load..." ), MENU_LOAD_FILE); |
113 | |
114 | menu->set_position(blend_space_draw->get_screen_position() + mb->get_position()); |
115 | menu->reset_size(); |
116 | menu->popup(); |
117 | |
118 | add_point_pos = (mb->get_position() / blend_space_draw->get_size()).x; |
119 | add_point_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); |
120 | add_point_pos += blend_space->get_min_space(); |
121 | |
122 | if (snap->is_pressed()) { |
123 | add_point_pos = Math::snapped(add_point_pos, blend_space->get_snap()); |
124 | } |
125 | } |
126 | } |
127 | |
128 | if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { |
129 | blend_space_draw->queue_redraw(); // why not |
130 | |
131 | // try to see if a point can be selected |
132 | selected_point = -1; |
133 | _update_tool_erase(); |
134 | |
135 | for (int i = 0; i < points.size(); i++) { |
136 | if (Math::abs(float(points[i] - mb->get_position().x)) < 10 * EDSCALE) { |
137 | selected_point = i; |
138 | |
139 | Ref<AnimationNode> node = blend_space->get_blend_point_node(i); |
140 | EditorNode::get_singleton()->push_item(node.ptr(), "" , true); |
141 | dragging_selected_attempt = true; |
142 | drag_from = mb->get_position(); |
143 | _update_tool_erase(); |
144 | _update_edited_point_pos(); |
145 | return; |
146 | } |
147 | } |
148 | } |
149 | |
150 | if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == MouseButton::LEFT) { |
151 | if (!read_only) { |
152 | if (dragging_selected) { |
153 | // move |
154 | float point = blend_space->get_blend_point_position(selected_point); |
155 | point += drag_ofs.x; |
156 | |
157 | if (snap->is_pressed()) { |
158 | point = Math::snapped(point, blend_space->get_snap()); |
159 | } |
160 | |
161 | updating = true; |
162 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
163 | undo_redo->create_action(TTR("Move Node Point" )); |
164 | undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position" , selected_point, point); |
165 | undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position" , selected_point, blend_space->get_blend_point_position(selected_point)); |
166 | undo_redo->add_do_method(this, "_update_space" ); |
167 | undo_redo->add_undo_method(this, "_update_space" ); |
168 | undo_redo->add_do_method(this, "_update_edited_point_pos" ); |
169 | undo_redo->add_undo_method(this, "_update_edited_point_pos" ); |
170 | undo_redo->commit_action(); |
171 | updating = false; |
172 | _update_edited_point_pos(); |
173 | } |
174 | |
175 | dragging_selected_attempt = false; |
176 | dragging_selected = false; |
177 | blend_space_draw->queue_redraw(); |
178 | } |
179 | } |
180 | |
181 | // *set* the blend |
182 | if (mb.is_valid() && !mb->is_pressed() && tool_blend->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { |
183 | float blend_pos = mb->get_position().x / blend_space_draw->get_size().x; |
184 | blend_pos *= blend_space->get_max_space() - blend_space->get_min_space(); |
185 | blend_pos += blend_space->get_min_space(); |
186 | |
187 | tree->set(get_blend_position_path(), blend_pos); |
188 | blend_space_draw->queue_redraw(); |
189 | } |
190 | |
191 | Ref<InputEventMouseMotion> mm = p_event; |
192 | |
193 | if (mm.is_valid() && !blend_space_draw->has_focus()) { |
194 | blend_space_draw->grab_focus(); |
195 | blend_space_draw->queue_redraw(); |
196 | } |
197 | |
198 | if (mm.is_valid() && dragging_selected_attempt) { |
199 | dragging_selected = true; |
200 | drag_ofs = ((mm->get_position() - drag_from) / blend_space_draw->get_size()) * ((blend_space->get_max_space() - blend_space->get_min_space()) * Vector2(1, 0)); |
201 | blend_space_draw->queue_redraw(); |
202 | _update_edited_point_pos(); |
203 | } |
204 | |
205 | if (mm.is_valid() && tool_blend->is_pressed() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) { |
206 | float blend_pos = mm->get_position().x / blend_space_draw->get_size().x; |
207 | blend_pos *= blend_space->get_max_space() - blend_space->get_min_space(); |
208 | blend_pos += blend_space->get_min_space(); |
209 | |
210 | tree->set(get_blend_position_path(), blend_pos); |
211 | |
212 | blend_space_draw->queue_redraw(); |
213 | } |
214 | } |
215 | |
216 | void AnimationNodeBlendSpace1DEditor::_blend_space_draw() { |
217 | AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree(); |
218 | if (!tree) { |
219 | return; |
220 | } |
221 | |
222 | Color linecolor = get_theme_color(SNAME("font_color" ), SNAME("Label" )); |
223 | Color linecolor_soft = linecolor; |
224 | linecolor_soft.a *= 0.5; |
225 | |
226 | Ref<Font> font = get_theme_font(SNAME("font" ), SNAME("Label" )); |
227 | int font_size = get_theme_font_size(SNAME("font_size" ), SNAME("Label" )); |
228 | Ref<Texture2D> icon = get_editor_theme_icon(SNAME("KeyValue" )); |
229 | Ref<Texture2D> icon_selected = get_editor_theme_icon(SNAME("KeySelected" )); |
230 | |
231 | Size2 s = blend_space_draw->get_size(); |
232 | |
233 | if (blend_space_draw->has_focus()) { |
234 | Color color = get_theme_color(SNAME("accent_color" ), EditorStringName(Editor)); |
235 | blend_space_draw->draw_rect(Rect2(Point2(), s), color, false); |
236 | } |
237 | |
238 | blend_space_draw->draw_line(Point2(1, s.height - 1), Point2(s.width - 1, s.height - 1), linecolor, Math::round(EDSCALE)); |
239 | |
240 | if (blend_space->get_min_space() < 0) { |
241 | float point = 0.0; |
242 | point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); |
243 | point *= s.width; |
244 | |
245 | float x = point; |
246 | |
247 | blend_space_draw->draw_line(Point2(x, s.height - 1), Point2(x, s.height - 5 * EDSCALE), linecolor, Math::round(EDSCALE)); |
248 | blend_space_draw->draw_string(font, Point2(x + 2 * EDSCALE, s.height - 2 * EDSCALE - font->get_height(font_size) + font->get_ascent(font_size)), "0" , HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, linecolor); |
249 | blend_space_draw->draw_line(Point2(x, s.height - 5 * EDSCALE), Point2(x, 0), linecolor_soft, Math::round(EDSCALE)); |
250 | } |
251 | |
252 | if (snap->is_pressed()) { |
253 | linecolor_soft.a = linecolor.a * 0.1; |
254 | |
255 | if (blend_space->get_snap() > 0) { |
256 | int prev_idx = -1; |
257 | |
258 | for (int i = 0; i < s.x; i++) { |
259 | float v = blend_space->get_min_space() + i * (blend_space->get_max_space() - blend_space->get_min_space()) / s.x; |
260 | int idx = int(v / blend_space->get_snap()); |
261 | |
262 | if (i > 0 && prev_idx != idx) { |
263 | blend_space_draw->draw_line(Point2(i, 0), Point2(i, s.height), linecolor_soft, Math::round(EDSCALE)); |
264 | } |
265 | |
266 | prev_idx = idx; |
267 | } |
268 | } |
269 | } |
270 | |
271 | points.clear(); |
272 | |
273 | for (int i = 0; i < blend_space->get_blend_point_count(); i++) { |
274 | float point = blend_space->get_blend_point_position(i); |
275 | |
276 | if (!read_only) { |
277 | if (dragging_selected && selected_point == i) { |
278 | point += drag_ofs.x; |
279 | if (snap->is_pressed()) { |
280 | point = Math::snapped(point, blend_space->get_snap()); |
281 | } |
282 | } |
283 | } |
284 | |
285 | point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); |
286 | point *= s.width; |
287 | |
288 | points.push_back(point); |
289 | |
290 | Vector2 gui_point = Vector2(point, s.height / 2.0); |
291 | |
292 | gui_point -= (icon->get_size() / 2.0); |
293 | |
294 | gui_point = gui_point.floor(); |
295 | |
296 | if (i == selected_point) { |
297 | blend_space_draw->draw_texture(icon_selected, gui_point); |
298 | } else { |
299 | blend_space_draw->draw_texture(icon, gui_point); |
300 | } |
301 | } |
302 | |
303 | // blend position |
304 | { |
305 | Color color; |
306 | if (tool_blend->is_pressed()) { |
307 | color = get_theme_color(SNAME("accent_color" ), EditorStringName(Editor)); |
308 | } else { |
309 | color = linecolor; |
310 | color.a *= 0.5; |
311 | } |
312 | |
313 | float point = tree->get(get_blend_position_path()); |
314 | |
315 | point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); |
316 | point *= s.width; |
317 | |
318 | Vector2 gui_point = Vector2(point, s.height / 2.0); |
319 | |
320 | float mind = 5 * EDSCALE; |
321 | float maxd = 15 * EDSCALE; |
322 | blend_space_draw->draw_line(gui_point + Vector2(mind, 0), gui_point + Vector2(maxd, 0), color, Math::round(2 * EDSCALE)); |
323 | blend_space_draw->draw_line(gui_point + Vector2(-mind, 0), gui_point + Vector2(-maxd, 0), color, Math::round(2 * EDSCALE)); |
324 | blend_space_draw->draw_line(gui_point + Vector2(0, mind), gui_point + Vector2(0, maxd), color, Math::round(2 * EDSCALE)); |
325 | blend_space_draw->draw_line(gui_point + Vector2(0, -mind), gui_point + Vector2(0, -maxd), color, Math::round(2 * EDSCALE)); |
326 | } |
327 | } |
328 | |
329 | void AnimationNodeBlendSpace1DEditor::_update_space() { |
330 | if (updating) { |
331 | return; |
332 | } |
333 | |
334 | updating = true; |
335 | |
336 | max_value->set_value(blend_space->get_max_space()); |
337 | min_value->set_value(blend_space->get_min_space()); |
338 | |
339 | sync->set_pressed(blend_space->is_using_sync()); |
340 | interpolation->select(blend_space->get_blend_mode()); |
341 | |
342 | label_value->set_text(blend_space->get_value_label()); |
343 | |
344 | snap_value->set_value(blend_space->get_snap()); |
345 | |
346 | blend_space_draw->queue_redraw(); |
347 | |
348 | updating = false; |
349 | } |
350 | |
351 | void AnimationNodeBlendSpace1DEditor::_config_changed(double) { |
352 | if (updating) { |
353 | return; |
354 | } |
355 | |
356 | updating = true; |
357 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
358 | undo_redo->create_action(TTR("Change BlendSpace1D Config" )); |
359 | undo_redo->add_do_method(blend_space.ptr(), "set_max_space" , max_value->get_value()); |
360 | undo_redo->add_undo_method(blend_space.ptr(), "set_max_space" , blend_space->get_max_space()); |
361 | undo_redo->add_do_method(blend_space.ptr(), "set_min_space" , min_value->get_value()); |
362 | undo_redo->add_undo_method(blend_space.ptr(), "set_min_space" , blend_space->get_min_space()); |
363 | undo_redo->add_do_method(blend_space.ptr(), "set_snap" , snap_value->get_value()); |
364 | undo_redo->add_undo_method(blend_space.ptr(), "set_snap" , blend_space->get_snap()); |
365 | undo_redo->add_do_method(blend_space.ptr(), "set_use_sync" , sync->is_pressed()); |
366 | undo_redo->add_undo_method(blend_space.ptr(), "set_use_sync" , blend_space->is_using_sync()); |
367 | undo_redo->add_do_method(blend_space.ptr(), "set_blend_mode" , interpolation->get_selected()); |
368 | undo_redo->add_undo_method(blend_space.ptr(), "set_blend_mode" , blend_space->get_blend_mode()); |
369 | undo_redo->add_do_method(this, "_update_space" ); |
370 | undo_redo->add_undo_method(this, "_update_space" ); |
371 | undo_redo->commit_action(); |
372 | updating = false; |
373 | |
374 | blend_space_draw->queue_redraw(); |
375 | } |
376 | |
377 | void AnimationNodeBlendSpace1DEditor::_labels_changed(String) { |
378 | if (updating) { |
379 | return; |
380 | } |
381 | |
382 | updating = true; |
383 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
384 | undo_redo->create_action(TTR("Change BlendSpace1D Labels" ), UndoRedo::MERGE_ENDS); |
385 | undo_redo->add_do_method(blend_space.ptr(), "set_value_label" , label_value->get_text()); |
386 | undo_redo->add_undo_method(blend_space.ptr(), "set_value_label" , blend_space->get_value_label()); |
387 | undo_redo->add_do_method(this, "_update_space" ); |
388 | undo_redo->add_undo_method(this, "_update_space" ); |
389 | undo_redo->commit_action(); |
390 | updating = false; |
391 | } |
392 | |
393 | void AnimationNodeBlendSpace1DEditor::_snap_toggled() { |
394 | blend_space_draw->queue_redraw(); |
395 | } |
396 | |
397 | void AnimationNodeBlendSpace1DEditor::_file_opened(const String &p_file) { |
398 | file_loaded = ResourceLoader::load(p_file); |
399 | if (file_loaded.is_valid()) { |
400 | _add_menu_type(MENU_LOAD_FILE_CONFIRM); |
401 | } else { |
402 | EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only animation nodes are allowed." )); |
403 | } |
404 | } |
405 | |
406 | void AnimationNodeBlendSpace1DEditor::(int p_index) { |
407 | Ref<AnimationRootNode> node; |
408 | if (p_index == MENU_LOAD_FILE) { |
409 | open_file->clear_filters(); |
410 | List<String> filters; |
411 | ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode" , &filters); |
412 | for (const String &E : filters) { |
413 | open_file->add_filter("*." + E); |
414 | } |
415 | open_file->popup_file_dialog(); |
416 | return; |
417 | } else if (p_index == MENU_LOAD_FILE_CONFIRM) { |
418 | node = file_loaded; |
419 | file_loaded.unref(); |
420 | } else if (p_index == MENU_PASTE) { |
421 | node = EditorSettings::get_singleton()->get_resource_clipboard(); |
422 | } else { |
423 | String type = menu->get_item_metadata(p_index); |
424 | |
425 | Object *obj = ClassDB::instantiate(type); |
426 | ERR_FAIL_NULL(obj); |
427 | AnimationNode *an = Object::cast_to<AnimationNode>(obj); |
428 | ERR_FAIL_NULL(an); |
429 | |
430 | node = Ref<AnimationNode>(an); |
431 | } |
432 | |
433 | if (!node.is_valid()) { |
434 | EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed." )); |
435 | return; |
436 | } |
437 | |
438 | updating = true; |
439 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
440 | undo_redo->create_action(TTR("Add Node Point" )); |
441 | undo_redo->add_do_method(blend_space.ptr(), "add_blend_point" , node, add_point_pos); |
442 | undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point" , blend_space->get_blend_point_count()); |
443 | undo_redo->add_do_method(this, "_update_space" ); |
444 | undo_redo->add_undo_method(this, "_update_space" ); |
445 | undo_redo->commit_action(); |
446 | updating = false; |
447 | |
448 | blend_space_draw->queue_redraw(); |
449 | } |
450 | |
451 | void AnimationNodeBlendSpace1DEditor::_add_animation_type(int p_index) { |
452 | Ref<AnimationNodeAnimation> anim; |
453 | anim.instantiate(); |
454 | |
455 | anim->set_animation(animations_to_add[p_index]); |
456 | |
457 | updating = true; |
458 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
459 | undo_redo->create_action(TTR("Add Animation Point" )); |
460 | undo_redo->add_do_method(blend_space.ptr(), "add_blend_point" , anim, add_point_pos); |
461 | undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point" , blend_space->get_blend_point_count()); |
462 | undo_redo->add_do_method(this, "_update_space" ); |
463 | undo_redo->add_undo_method(this, "_update_space" ); |
464 | undo_redo->commit_action(); |
465 | updating = false; |
466 | |
467 | blend_space_draw->queue_redraw(); |
468 | } |
469 | |
470 | void AnimationNodeBlendSpace1DEditor::_tool_switch(int p_tool) { |
471 | if (p_tool == 0) { |
472 | tool_erase->show(); |
473 | tool_erase_sep->show(); |
474 | } else { |
475 | tool_erase->hide(); |
476 | tool_erase_sep->hide(); |
477 | } |
478 | |
479 | _update_tool_erase(); |
480 | blend_space_draw->queue_redraw(); |
481 | } |
482 | |
483 | void AnimationNodeBlendSpace1DEditor::_update_edited_point_pos() { |
484 | if (updating) { |
485 | return; |
486 | } |
487 | |
488 | if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { |
489 | float pos = blend_space->get_blend_point_position(selected_point); |
490 | |
491 | if (dragging_selected) { |
492 | pos += drag_ofs.x; |
493 | |
494 | if (snap->is_pressed()) { |
495 | pos = Math::snapped(pos, blend_space->get_snap()); |
496 | } |
497 | } |
498 | |
499 | updating = true; |
500 | edit_value->set_value(pos); |
501 | updating = false; |
502 | } |
503 | } |
504 | |
505 | void AnimationNodeBlendSpace1DEditor::_update_tool_erase() { |
506 | bool point_valid = selected_point >= 0 && selected_point < blend_space->get_blend_point_count(); |
507 | tool_erase->set_disabled(!point_valid || read_only); |
508 | |
509 | if (point_valid) { |
510 | Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); |
511 | |
512 | if (AnimationTreeEditor::get_singleton()->can_edit(an)) { |
513 | open_editor->show(); |
514 | } else { |
515 | open_editor->hide(); |
516 | } |
517 | |
518 | if (!read_only) { |
519 | edit_hb->show(); |
520 | } else { |
521 | edit_hb->hide(); |
522 | } |
523 | } else { |
524 | edit_hb->hide(); |
525 | } |
526 | } |
527 | |
528 | void AnimationNodeBlendSpace1DEditor::_erase_selected() { |
529 | if (selected_point != -1) { |
530 | updating = true; |
531 | |
532 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
533 | undo_redo->create_action(TTR("Remove BlendSpace1D Point" )); |
534 | undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point" , selected_point); |
535 | undo_redo->add_undo_method(blend_space.ptr(), "add_blend_point" , blend_space->get_blend_point_node(selected_point), blend_space->get_blend_point_position(selected_point), selected_point); |
536 | undo_redo->add_do_method(this, "_update_space" ); |
537 | undo_redo->add_undo_method(this, "_update_space" ); |
538 | undo_redo->commit_action(); |
539 | |
540 | updating = false; |
541 | |
542 | blend_space_draw->queue_redraw(); |
543 | } |
544 | } |
545 | |
546 | void AnimationNodeBlendSpace1DEditor::_edit_point_pos(double) { |
547 | if (updating) { |
548 | return; |
549 | } |
550 | |
551 | updating = true; |
552 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
553 | undo_redo->create_action(TTR("Move BlendSpace1D Node Point" )); |
554 | undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position" , selected_point, edit_value->get_value()); |
555 | undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position" , selected_point, blend_space->get_blend_point_position(selected_point)); |
556 | undo_redo->add_do_method(this, "_update_space" ); |
557 | undo_redo->add_undo_method(this, "_update_space" ); |
558 | undo_redo->add_do_method(this, "_update_edited_point_pos" ); |
559 | undo_redo->add_undo_method(this, "_update_edited_point_pos" ); |
560 | undo_redo->commit_action(); |
561 | updating = false; |
562 | |
563 | blend_space_draw->queue_redraw(); |
564 | } |
565 | |
566 | void AnimationNodeBlendSpace1DEditor::_open_editor() { |
567 | if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { |
568 | Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); |
569 | ERR_FAIL_COND(an.is_null()); |
570 | AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point)); |
571 | } |
572 | } |
573 | |
574 | void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { |
575 | switch (p_what) { |
576 | case NOTIFICATION_ENTER_TREE: |
577 | case NOTIFICATION_THEME_CHANGED: { |
578 | error_panel->add_theme_style_override("panel" , get_theme_stylebox(SNAME("panel" ), SNAME("Tree" ))); |
579 | error_label->add_theme_color_override("font_color" , get_theme_color(SNAME("error_color" ), EditorStringName(Editor))); |
580 | panel->add_theme_style_override("panel" , get_theme_stylebox(SNAME("panel" ), SNAME("Tree" ))); |
581 | tool_blend->set_icon(get_editor_theme_icon(SNAME("EditPivot" ))); |
582 | tool_select->set_icon(get_editor_theme_icon(SNAME("ToolSelect" ))); |
583 | tool_create->set_icon(get_editor_theme_icon(SNAME("EditKey" ))); |
584 | tool_erase->set_icon(get_editor_theme_icon(SNAME("Remove" ))); |
585 | snap->set_icon(get_editor_theme_icon(SNAME("SnapGrid" ))); |
586 | open_editor->set_icon(get_editor_theme_icon(SNAME("Edit" ))); |
587 | interpolation->clear(); |
588 | interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous" )), "" , 0); |
589 | interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete" )), "" , 1); |
590 | interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackCapture" )), "" , 2); |
591 | } break; |
592 | |
593 | case NOTIFICATION_PROCESS: { |
594 | AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree(); |
595 | if (!tree) { |
596 | return; |
597 | } |
598 | |
599 | String error; |
600 | |
601 | if (!tree->is_active()) { |
602 | error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails." ); |
603 | } else if (tree->is_state_invalid()) { |
604 | error = tree->get_invalid_state_reason(); |
605 | } |
606 | |
607 | if (error != error_label->get_text()) { |
608 | error_label->set_text(error); |
609 | if (!error.is_empty()) { |
610 | error_panel->show(); |
611 | } else { |
612 | error_panel->hide(); |
613 | } |
614 | } |
615 | } break; |
616 | |
617 | case NOTIFICATION_VISIBILITY_CHANGED: { |
618 | set_process(is_visible_in_tree()); |
619 | } break; |
620 | } |
621 | } |
622 | |
623 | void AnimationNodeBlendSpace1DEditor::_bind_methods() { |
624 | ClassDB::bind_method("_update_space" , &AnimationNodeBlendSpace1DEditor::_update_space); |
625 | ClassDB::bind_method("_update_tool_erase" , &AnimationNodeBlendSpace1DEditor::_update_tool_erase); |
626 | |
627 | ClassDB::bind_method("_update_edited_point_pos" , &AnimationNodeBlendSpace1DEditor::_update_edited_point_pos); |
628 | } |
629 | |
630 | bool AnimationNodeBlendSpace1DEditor::can_edit(const Ref<AnimationNode> &p_node) { |
631 | Ref<AnimationNodeBlendSpace1D> b1d = p_node; |
632 | return b1d.is_valid(); |
633 | } |
634 | |
635 | void AnimationNodeBlendSpace1DEditor::edit(const Ref<AnimationNode> &p_node) { |
636 | blend_space = p_node; |
637 | read_only = false; |
638 | |
639 | if (!blend_space.is_null()) { |
640 | read_only = EditorNode::get_singleton()->is_resource_read_only(blend_space); |
641 | |
642 | _update_space(); |
643 | } |
644 | |
645 | tool_create->set_disabled(read_only); |
646 | edit_value->set_editable(!read_only); |
647 | label_value->set_editable(!read_only); |
648 | min_value->set_editable(!read_only); |
649 | max_value->set_editable(!read_only); |
650 | sync->set_disabled(read_only); |
651 | interpolation->set_disabled(read_only); |
652 | } |
653 | |
654 | AnimationNodeBlendSpace1DEditor *AnimationNodeBlendSpace1DEditor::singleton = nullptr; |
655 | |
656 | AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { |
657 | singleton = this; |
658 | |
659 | HBoxContainer *top_hb = memnew(HBoxContainer); |
660 | add_child(top_hb); |
661 | |
662 | Ref<ButtonGroup> bg; |
663 | bg.instantiate(); |
664 | |
665 | tool_blend = memnew(Button); |
666 | tool_blend->set_flat(true); |
667 | tool_blend->set_toggle_mode(true); |
668 | tool_blend->set_button_group(bg); |
669 | top_hb->add_child(tool_blend); |
670 | tool_blend->set_pressed(true); |
671 | tool_blend->set_tooltip_text(TTR("Set the blending position within the space" )); |
672 | tool_blend->connect("pressed" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_tool_switch).bind(3)); |
673 | |
674 | tool_select = memnew(Button); |
675 | tool_select->set_flat(true); |
676 | tool_select->set_toggle_mode(true); |
677 | tool_select->set_button_group(bg); |
678 | top_hb->add_child(tool_select); |
679 | tool_select->set_tooltip_text(TTR("Select and move points, create points with RMB." )); |
680 | tool_select->connect("pressed" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_tool_switch).bind(0)); |
681 | |
682 | tool_create = memnew(Button); |
683 | tool_create->set_flat(true); |
684 | tool_create->set_toggle_mode(true); |
685 | tool_create->set_button_group(bg); |
686 | top_hb->add_child(tool_create); |
687 | tool_create->set_tooltip_text(TTR("Create points." )); |
688 | tool_create->connect("pressed" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_tool_switch).bind(1)); |
689 | |
690 | tool_erase_sep = memnew(VSeparator); |
691 | top_hb->add_child(tool_erase_sep); |
692 | tool_erase = memnew(Button); |
693 | tool_erase->set_flat(true); |
694 | top_hb->add_child(tool_erase); |
695 | tool_erase->set_tooltip_text(TTR("Erase points." )); |
696 | tool_erase->connect("pressed" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_erase_selected)); |
697 | |
698 | top_hb->add_child(memnew(VSeparator)); |
699 | |
700 | snap = memnew(Button); |
701 | snap->set_flat(true); |
702 | snap->set_toggle_mode(true); |
703 | top_hb->add_child(snap); |
704 | snap->set_pressed(true); |
705 | snap->set_tooltip_text(TTR("Enable snap and show grid." )); |
706 | snap->connect("pressed" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_snap_toggled)); |
707 | |
708 | snap_value = memnew(SpinBox); |
709 | top_hb->add_child(snap_value); |
710 | snap_value->set_min(0.01); |
711 | snap_value->set_step(0.01); |
712 | snap_value->set_max(1000); |
713 | |
714 | top_hb->add_child(memnew(VSeparator)); |
715 | top_hb->add_child(memnew(Label(TTR("Sync:" )))); |
716 | sync = memnew(CheckBox); |
717 | top_hb->add_child(sync); |
718 | sync->connect("toggled" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed)); |
719 | |
720 | top_hb->add_child(memnew(VSeparator)); |
721 | |
722 | top_hb->add_child(memnew(Label(TTR("Blend:" )))); |
723 | interpolation = memnew(OptionButton); |
724 | top_hb->add_child(interpolation); |
725 | interpolation->connect("item_selected" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed)); |
726 | |
727 | edit_hb = memnew(HBoxContainer); |
728 | top_hb->add_child(edit_hb); |
729 | edit_hb->add_child(memnew(VSeparator)); |
730 | edit_hb->add_child(memnew(Label(TTR("Point" )))); |
731 | |
732 | edit_value = memnew(SpinBox); |
733 | edit_hb->add_child(edit_value); |
734 | edit_value->set_min(-1000); |
735 | edit_value->set_max(1000); |
736 | edit_value->set_step(0.01); |
737 | edit_value->connect("value_changed" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_edit_point_pos)); |
738 | |
739 | open_editor = memnew(Button); |
740 | edit_hb->add_child(open_editor); |
741 | open_editor->set_text(TTR("Open Editor" )); |
742 | open_editor->connect("pressed" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_open_editor), CONNECT_DEFERRED); |
743 | |
744 | edit_hb->hide(); |
745 | open_editor->hide(); |
746 | |
747 | VBoxContainer *main_vb = memnew(VBoxContainer); |
748 | add_child(main_vb); |
749 | main_vb->set_v_size_flags(SIZE_EXPAND_FILL); |
750 | |
751 | panel = memnew(PanelContainer); |
752 | panel->set_clip_contents(true); |
753 | main_vb->add_child(panel); |
754 | panel->set_h_size_flags(SIZE_EXPAND_FILL); |
755 | panel->set_v_size_flags(SIZE_EXPAND_FILL); |
756 | |
757 | blend_space_draw = memnew(Control); |
758 | blend_space_draw->connect("gui_input" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_blend_space_gui_input)); |
759 | blend_space_draw->connect("draw" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_blend_space_draw)); |
760 | blend_space_draw->set_focus_mode(FOCUS_ALL); |
761 | |
762 | panel->add_child(blend_space_draw); |
763 | |
764 | { |
765 | HBoxContainer *bottom_hb = memnew(HBoxContainer); |
766 | main_vb->add_child(bottom_hb); |
767 | bottom_hb->set_h_size_flags(SIZE_EXPAND_FILL); |
768 | |
769 | min_value = memnew(SpinBox); |
770 | min_value->set_min(-10000); |
771 | min_value->set_max(0); |
772 | min_value->set_step(0.01); |
773 | |
774 | max_value = memnew(SpinBox); |
775 | max_value->set_min(0.01); |
776 | max_value->set_max(10000); |
777 | max_value->set_step(0.01); |
778 | |
779 | label_value = memnew(LineEdit); |
780 | label_value->set_expand_to_text_length_enabled(true); |
781 | |
782 | // now add |
783 | |
784 | bottom_hb->add_child(min_value); |
785 | bottom_hb->add_spacer(); |
786 | bottom_hb->add_child(label_value); |
787 | bottom_hb->add_spacer(); |
788 | bottom_hb->add_child(max_value); |
789 | } |
790 | |
791 | snap_value->connect("value_changed" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed)); |
792 | min_value->connect("value_changed" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed)); |
793 | max_value->connect("value_changed" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed)); |
794 | label_value->connect("text_changed" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_labels_changed)); |
795 | |
796 | error_panel = memnew(PanelContainer); |
797 | add_child(error_panel); |
798 | |
799 | error_label = memnew(Label); |
800 | error_panel->add_child(error_label); |
801 | error_label->set_text("hmmm" ); |
802 | |
803 | menu = memnew(PopupMenu); |
804 | add_child(menu); |
805 | menu->connect("id_pressed" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_add_menu_type)); |
806 | |
807 | animations_menu = memnew(PopupMenu); |
808 | menu->add_child(animations_menu); |
809 | animations_menu->set_name("animations" ); |
810 | animations_menu->connect("index_pressed" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_add_animation_type)); |
811 | |
812 | open_file = memnew(EditorFileDialog); |
813 | add_child(open_file); |
814 | open_file->set_title(TTR("Open Animation Node" )); |
815 | open_file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); |
816 | open_file->connect("file_selected" , callable_mp(this, &AnimationNodeBlendSpace1DEditor::_file_opened)); |
817 | |
818 | set_custom_minimum_size(Size2(0, 150 * EDSCALE)); |
819 | } |
820 | |