1/**************************************************************************/
2/* animation_blend_space_2d_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_2d_editor.h"
32
33#include "core/config/project_settings.h"
34#include "core/input/input.h"
35#include "core/io/resource_loader.h"
36#include "core/math/geometry_2d.h"
37#include "core/os/keyboard.h"
38#include "editor/editor_node.h"
39#include "editor/editor_scale.h"
40#include "editor/editor_settings.h"
41#include "editor/editor_string_names.h"
42#include "editor/editor_undo_redo_manager.h"
43#include "editor/gui/editor_file_dialog.h"
44#include "scene/animation/animation_blend_tree.h"
45#include "scene/animation/animation_player.h"
46#include "scene/gui/check_box.h"
47#include "scene/gui/grid_container.h"
48#include "scene/gui/menu_button.h"
49#include "scene/gui/option_button.h"
50#include "scene/gui/panel.h"
51#include "scene/gui/panel_container.h"
52#include "scene/main/window.h"
53
54bool AnimationNodeBlendSpace2DEditor::can_edit(const Ref<AnimationNode> &p_node) {
55 Ref<AnimationNodeBlendSpace2D> bs2d = p_node;
56 return bs2d.is_valid();
57}
58
59void AnimationNodeBlendSpace2DEditor::_blend_space_changed() {
60 blend_space_draw->queue_redraw();
61}
62
63void AnimationNodeBlendSpace2DEditor::edit(const Ref<AnimationNode> &p_node) {
64 if (blend_space.is_valid()) {
65 blend_space->disconnect("triangles_updated", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_blend_space_changed));
66 }
67 blend_space = p_node;
68 read_only = false;
69
70 if (!blend_space.is_null()) {
71 read_only = EditorNode::get_singleton()->is_resource_read_only(blend_space);
72
73 blend_space->connect("triangles_updated", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_blend_space_changed));
74 _update_space();
75 }
76
77 tool_create->set_disabled(read_only);
78 max_x_value->set_editable(!read_only);
79 min_x_value->set_editable(!read_only);
80 max_y_value->set_editable(!read_only);
81 min_y_value->set_editable(!read_only);
82 label_x->set_editable(!read_only);
83 label_y->set_editable(!read_only);
84 edit_x->set_editable(!read_only);
85 edit_y->set_editable(!read_only);
86 tool_triangle->set_disabled(read_only);
87 auto_triangles->set_disabled(read_only);
88 sync->set_disabled(read_only);
89 interpolation->set_disabled(read_only);
90}
91
92StringName AnimationNodeBlendSpace2DEditor::get_blend_position_path() const {
93 StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + "blend_position";
94 return path;
95}
96
97void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) {
98 AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
99 if (!tree) {
100 return;
101 }
102
103 Ref<InputEventKey> k = p_event;
104 if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && !k->is_echo()) {
105 if (selected_point != -1 || selected_triangle != -1) {
106 if (!read_only) {
107 _erase_selected();
108 }
109 accept_event();
110 }
111 }
112
113 Ref<InputEventMouseButton> mb = p_event;
114
115 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()))) {
116 if (!read_only) {
117 menu->clear();
118 animations_menu->clear();
119 animations_to_add.clear();
120 List<StringName> classes;
121 classes.sort_custom<StringName::AlphCompare>();
122
123 ClassDB::get_inheriters_from_class("AnimationRootNode", &classes);
124 menu->add_submenu_item(TTR("Add Animation"), "animations");
125
126 if (tree->has_node(tree->get_animation_player())) {
127 AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(tree->get_node(tree->get_animation_player()));
128 if (ap) {
129 List<StringName> names;
130 ap->get_animation_list(&names);
131 for (const StringName &E : names) {
132 animations_menu->add_icon_item(get_editor_theme_icon(SNAME("Animation")), E);
133 animations_to_add.push_back(E);
134 }
135 }
136 }
137
138 for (const StringName &E : classes) {
139 String name = String(E).replace_first("AnimationNode", "");
140 if (name == "Animation" || name == "StartState" || name == "EndState") {
141 continue; // nope
142 }
143 int idx = menu->get_item_count();
144 menu->add_item(vformat(TTR("Add %s"), name), idx);
145 menu->set_item_metadata(idx, E);
146 }
147
148 Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard();
149 if (clipb.is_valid()) {
150 menu->add_separator();
151 menu->add_item(TTR("Paste"), MENU_PASTE);
152 }
153 menu->add_separator();
154 menu->add_item(TTR("Load..."), MENU_LOAD_FILE);
155
156 menu->set_position(blend_space_draw->get_screen_position() + mb->get_position());
157 menu->reset_size();
158 menu->popup();
159 add_point_pos = (mb->get_position() / blend_space_draw->get_size());
160 add_point_pos.y = 1.0 - add_point_pos.y;
161 add_point_pos *= (blend_space->get_max_space() - blend_space->get_min_space());
162 add_point_pos += blend_space->get_min_space();
163
164 if (snap->is_pressed()) {
165 add_point_pos = add_point_pos.snapped(blend_space->get_snap());
166 }
167 }
168 }
169
170 if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
171 blend_space_draw->queue_redraw(); //update anyway
172 //try to see if a point can be selected
173 selected_point = -1;
174 selected_triangle = -1;
175 _update_tool_erase();
176
177 for (int i = 0; i < points.size(); i++) {
178 if (points[i].distance_to(mb->get_position()) < 10 * EDSCALE) {
179 selected_point = i;
180 Ref<AnimationNode> node = blend_space->get_blend_point_node(i);
181 EditorNode::get_singleton()->push_item(node.ptr(), "", true);
182 dragging_selected_attempt = true;
183 drag_from = mb->get_position();
184 _update_tool_erase();
185 _update_edited_point_pos();
186 return;
187 }
188 }
189
190 //then try to see if a triangle can be selected
191 if (!blend_space->get_auto_triangles()) { //if autotriangles use, disable this
192 for (int i = 0; i < blend_space->get_triangle_count(); i++) {
193 Vector<Vector2> triangle;
194
195 for (int j = 0; j < 3; j++) {
196 int idx = blend_space->get_triangle_point(i, j);
197 ERR_FAIL_INDEX(idx, points.size());
198 triangle.push_back(points[idx]);
199 }
200
201 if (Geometry2D::is_point_in_triangle(mb->get_position(), triangle[0], triangle[1], triangle[2])) {
202 selected_triangle = i;
203 _update_tool_erase();
204 return;
205 }
206 }
207 }
208 }
209
210 if (mb.is_valid() && mb->is_pressed() && tool_triangle->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
211 blend_space_draw->queue_redraw(); //update anyway
212 //try to see if a point can be selected
213 selected_point = -1;
214
215 for (int i = 0; i < points.size(); i++) {
216 if (making_triangle.has(i)) {
217 continue;
218 }
219
220 if (points[i].distance_to(mb->get_position()) < 10 * EDSCALE) {
221 making_triangle.push_back(i);
222 if (making_triangle.size() == 3) {
223 //add triangle!
224 if (blend_space->has_triangle(making_triangle[0], making_triangle[1], making_triangle[2])) {
225 making_triangle.clear();
226 EditorNode::get_singleton()->show_warning(TTR("Triangle already exists."));
227 return;
228 }
229
230 updating = true;
231 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
232 undo_redo->create_action(TTR("Add Triangle"));
233 undo_redo->add_do_method(blend_space.ptr(), "add_triangle", making_triangle[0], making_triangle[1], making_triangle[2]);
234 undo_redo->add_undo_method(blend_space.ptr(), "remove_triangle", blend_space->get_triangle_count());
235 undo_redo->add_do_method(this, "_update_space");
236 undo_redo->add_undo_method(this, "_update_space");
237 undo_redo->commit_action();
238 updating = false;
239 making_triangle.clear();
240 }
241 return;
242 }
243 }
244 }
245
246 if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == MouseButton::LEFT) {
247 if (dragging_selected) {
248 //move
249 Vector2 point = blend_space->get_blend_point_position(selected_point);
250 point += drag_ofs;
251 if (snap->is_pressed()) {
252 point = point.snapped(blend_space->get_snap());
253 }
254
255 if (!read_only) {
256 updating = true;
257 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
258 undo_redo->create_action(TTR("Move Node Point"));
259 undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point);
260 undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));
261 undo_redo->add_do_method(this, "_update_space");
262 undo_redo->add_undo_method(this, "_update_space");
263 undo_redo->add_do_method(this, "_update_edited_point_pos");
264 undo_redo->add_undo_method(this, "_update_edited_point_pos");
265 undo_redo->commit_action();
266 updating = false;
267 _update_edited_point_pos();
268 }
269 }
270 dragging_selected_attempt = false;
271 dragging_selected = false;
272 blend_space_draw->queue_redraw();
273 }
274
275 if (mb.is_valid() && mb->is_pressed() && tool_blend->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
276 Vector2 blend_pos = (mb->get_position() / blend_space_draw->get_size());
277 blend_pos.y = 1.0 - blend_pos.y;
278 blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space());
279 blend_pos += blend_space->get_min_space();
280
281 tree->set(get_blend_position_path(), blend_pos);
282
283 blend_space_draw->queue_redraw();
284 }
285
286 Ref<InputEventMouseMotion> mm = p_event;
287
288 if (mm.is_valid() && !blend_space_draw->has_focus()) {
289 blend_space_draw->grab_focus();
290 blend_space_draw->queue_redraw();
291 }
292
293 if (mm.is_valid() && dragging_selected_attempt) {
294 dragging_selected = true;
295 if (!read_only) {
296 drag_ofs = ((mm->get_position() - drag_from) / blend_space_draw->get_size()) * (blend_space->get_max_space() - blend_space->get_min_space()) * Vector2(1, -1);
297 }
298 blend_space_draw->queue_redraw();
299 _update_edited_point_pos();
300 }
301
302 if (mm.is_valid() && tool_triangle->is_pressed() && making_triangle.size()) {
303 blend_space_draw->queue_redraw();
304 }
305
306 if (mm.is_valid() && !tool_triangle->is_pressed() && making_triangle.size()) {
307 making_triangle.clear();
308 blend_space_draw->queue_redraw();
309 }
310
311 if (mm.is_valid() && tool_blend->is_pressed() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
312 Vector2 blend_pos = (mm->get_position() / blend_space_draw->get_size());
313 blend_pos.y = 1.0 - blend_pos.y;
314 blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space());
315 blend_pos += blend_space->get_min_space();
316
317 tree->set(get_blend_position_path(), blend_pos);
318
319 blend_space_draw->queue_redraw();
320 }
321}
322
323void AnimationNodeBlendSpace2DEditor::_file_opened(const String &p_file) {
324 file_loaded = ResourceLoader::load(p_file);
325 if (file_loaded.is_valid()) {
326 _add_menu_type(MENU_LOAD_FILE_CONFIRM);
327 } else {
328 EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only animation nodes are allowed."));
329 }
330}
331
332void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) {
333 Ref<AnimationRootNode> node;
334 if (p_index == MENU_LOAD_FILE) {
335 open_file->clear_filters();
336 List<String> filters;
337 ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters);
338 for (const String &E : filters) {
339 open_file->add_filter("*." + E);
340 }
341 open_file->popup_file_dialog();
342 return;
343 } else if (p_index == MENU_LOAD_FILE_CONFIRM) {
344 node = file_loaded;
345 file_loaded.unref();
346 } else if (p_index == MENU_PASTE) {
347 node = EditorSettings::get_singleton()->get_resource_clipboard();
348 } else {
349 String type = menu->get_item_metadata(p_index);
350
351 Object *obj = ClassDB::instantiate(type);
352 ERR_FAIL_NULL(obj);
353 AnimationNode *an = Object::cast_to<AnimationNode>(obj);
354 ERR_FAIL_NULL(an);
355
356 node = Ref<AnimationNode>(an);
357 }
358
359 if (!node.is_valid()) {
360 EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed."));
361 return;
362 }
363
364 updating = true;
365 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
366 undo_redo->create_action(TTR("Add Node Point"));
367 undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos);
368 undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
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
377void AnimationNodeBlendSpace2DEditor::_add_animation_type(int p_index) {
378 Ref<AnimationNodeAnimation> anim;
379 anim.instantiate();
380
381 anim->set_animation(animations_to_add[p_index]);
382
383 updating = true;
384 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
385 undo_redo->create_action(TTR("Add Animation Point"));
386 undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos);
387 undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());
388 undo_redo->add_do_method(this, "_update_space");
389 undo_redo->add_undo_method(this, "_update_space");
390 undo_redo->commit_action();
391 updating = false;
392
393 blend_space_draw->queue_redraw();
394}
395
396void AnimationNodeBlendSpace2DEditor::_update_tool_erase() {
397 tool_erase->set_disabled(
398 (!(selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) && !(selected_triangle >= 0 && selected_triangle < blend_space->get_triangle_count())) ||
399 read_only);
400
401 if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {
402 Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point);
403 if (AnimationTreeEditor::get_singleton()->can_edit(an)) {
404 open_editor->show();
405 } else {
406 open_editor->hide();
407 }
408 if (!read_only) {
409 edit_hb->show();
410 } else {
411 edit_hb->hide();
412 }
413 } else {
414 edit_hb->hide();
415 }
416}
417
418void AnimationNodeBlendSpace2DEditor::_tool_switch(int p_tool) {
419 making_triangle.clear();
420
421 if (p_tool == 2) {
422 Vector<Vector2> bl_points;
423 for (int i = 0; i < blend_space->get_blend_point_count(); i++) {
424 bl_points.push_back(blend_space->get_blend_point_position(i));
425 }
426 Vector<Delaunay2D::Triangle> tr = Delaunay2D::triangulate(bl_points);
427 for (int i = 0; i < tr.size(); i++) {
428 blend_space->add_triangle(tr[i].points[0], tr[i].points[1], tr[i].points[2]);
429 }
430 }
431
432 if (p_tool == 0) {
433 tool_erase->show();
434 tool_erase_sep->show();
435 } else {
436 tool_erase->hide();
437 tool_erase_sep->hide();
438 }
439 _update_tool_erase();
440 blend_space_draw->queue_redraw();
441}
442
443void AnimationNodeBlendSpace2DEditor::_blend_space_draw() {
444 AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
445 if (!tree) {
446 return;
447 }
448
449 Color linecolor = get_theme_color(SNAME("font_color"), SNAME("Label"));
450 Color linecolor_soft = linecolor;
451 linecolor_soft.a *= 0.5;
452 Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
453 int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
454 Ref<Texture2D> icon = get_editor_theme_icon(SNAME("KeyValue"));
455 Ref<Texture2D> icon_selected = get_editor_theme_icon(SNAME("KeySelected"));
456
457 Size2 s = blend_space_draw->get_size();
458
459 if (blend_space_draw->has_focus()) {
460 Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
461 blend_space_draw->draw_rect(Rect2(Point2(), s), color, false);
462 }
463 blend_space_draw->draw_line(Point2(1, 0), Point2(1, s.height - 1), linecolor, Math::round(EDSCALE));
464 blend_space_draw->draw_line(Point2(1, s.height - 1), Point2(s.width - 1, s.height - 1), linecolor, Math::round(EDSCALE));
465
466 blend_space_draw->draw_line(Point2(0, 0), Point2(5 * EDSCALE, 0), linecolor, Math::round(EDSCALE));
467 if (blend_space->get_min_space().y < 0) {
468 int y = (blend_space->get_max_space().y / (blend_space->get_max_space().y - blend_space->get_min_space().y)) * s.height;
469 blend_space_draw->draw_line(Point2(0, y), Point2(5 * EDSCALE, y), linecolor, Math::round(EDSCALE));
470 blend_space_draw->draw_string(font, Point2(2 * EDSCALE, y - font->get_height(font_size) + font->get_ascent(font_size)), "0", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, linecolor);
471 blend_space_draw->draw_line(Point2(5 * EDSCALE, y), Point2(s.width, y), linecolor_soft, Math::round(EDSCALE));
472 }
473
474 if (blend_space->get_min_space().x < 0) {
475 int x = (-blend_space->get_min_space().x / (blend_space->get_max_space().x - blend_space->get_min_space().x)) * s.width;
476 blend_space_draw->draw_line(Point2(x, s.height - 1), Point2(x, s.height - 5 * EDSCALE), linecolor, Math::round(EDSCALE));
477 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);
478 blend_space_draw->draw_line(Point2(x, s.height - 5 * EDSCALE), Point2(x, 0), linecolor_soft, Math::round(EDSCALE));
479 }
480
481 if (snap->is_pressed()) {
482 linecolor_soft.a = linecolor.a * 0.1;
483
484 if (blend_space->get_snap().x > 0) {
485 int prev_idx = 0;
486 for (int i = 0; i < s.x; i++) {
487 float v = blend_space->get_min_space().x + i * (blend_space->get_max_space().x - blend_space->get_min_space().x) / s.x;
488 int idx = int(v / blend_space->get_snap().x);
489
490 if (i > 0 && prev_idx != idx) {
491 blend_space_draw->draw_line(Point2(i, 0), Point2(i, s.height), linecolor_soft, Math::round(EDSCALE));
492 }
493
494 prev_idx = idx;
495 }
496 }
497
498 if (blend_space->get_snap().y > 0) {
499 int prev_idx = 0;
500 for (int i = 0; i < s.y; i++) {
501 float v = blend_space->get_max_space().y - i * (blend_space->get_max_space().y - blend_space->get_min_space().y) / s.y;
502 int idx = int(v / blend_space->get_snap().y);
503
504 if (i > 0 && prev_idx != idx) {
505 blend_space_draw->draw_line(Point2(0, i), Point2(s.width, i), linecolor_soft, Math::round(EDSCALE));
506 }
507
508 prev_idx = idx;
509 }
510 }
511 }
512
513 //triangles first
514 for (int i = 0; i < blend_space->get_triangle_count(); i++) {
515 Vector<Vector2> bl_points;
516 bl_points.resize(3);
517
518 for (int j = 0; j < 3; j++) {
519 int point_idx = blend_space->get_triangle_point(i, j);
520 Vector2 point = blend_space->get_blend_point_position(point_idx);
521 if (dragging_selected && selected_point == point_idx) {
522 point += drag_ofs;
523 if (snap->is_pressed()) {
524 point = point.snapped(blend_space->get_snap());
525 }
526 }
527 point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
528 point *= s;
529 point.y = s.height - point.y;
530 bl_points.write[j] = point;
531 }
532
533 for (int j = 0; j < 3; j++) {
534 blend_space_draw->draw_line(bl_points[j], bl_points[(j + 1) % 3], linecolor, Math::round(EDSCALE), true);
535 }
536
537 Color color;
538 if (i == selected_triangle) {
539 color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
540 color.a *= 0.5;
541 } else {
542 color = linecolor;
543 color.a *= 0.2;
544 }
545
546 Vector<Color> colors = {
547 color,
548 color,
549 color
550 };
551 blend_space_draw->draw_primitive(bl_points, colors, Vector<Vector2>());
552 }
553
554 points.clear();
555 for (int i = 0; i < blend_space->get_blend_point_count(); i++) {
556 Vector2 point = blend_space->get_blend_point_position(i);
557 if (!read_only) {
558 if (dragging_selected && selected_point == i) {
559 point += drag_ofs;
560 if (snap->is_pressed()) {
561 point = point.snapped(blend_space->get_snap());
562 }
563 }
564 }
565 point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
566 point *= s;
567 point.y = s.height - point.y;
568
569 points.push_back(point);
570 point -= (icon->get_size() / 2);
571 point = point.floor();
572
573 if (i == selected_point) {
574 blend_space_draw->draw_texture(icon_selected, point);
575 } else {
576 blend_space_draw->draw_texture(icon, point);
577 }
578 }
579
580 if (making_triangle.size()) {
581 Vector<Vector2> bl_points;
582 for (int i = 0; i < making_triangle.size(); i++) {
583 Vector2 point = blend_space->get_blend_point_position(making_triangle[i]);
584 point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
585 point *= s;
586 point.y = s.height - point.y;
587 bl_points.push_back(point);
588 }
589
590 for (int i = 0; i < bl_points.size() - 1; i++) {
591 blend_space_draw->draw_line(bl_points[i], bl_points[i + 1], linecolor, Math::round(2 * EDSCALE), true);
592 }
593 blend_space_draw->draw_line(bl_points[bl_points.size() - 1], blend_space_draw->get_local_mouse_position(), linecolor, Math::round(2 * EDSCALE), true);
594 }
595
596 ///draw cursor position
597
598 {
599 Color color;
600 if (tool_blend->is_pressed()) {
601 color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
602 } else {
603 color = linecolor;
604 color.a *= 0.5;
605 }
606
607 Vector2 blend_pos = tree->get(get_blend_position_path());
608 Vector2 point = blend_pos;
609
610 point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
611 point *= s;
612 point.y = s.height - point.y;
613
614 if (blend_space->get_triangle_count()) {
615 Vector2 closest = blend_space->get_closest_point(blend_pos);
616 closest = (closest - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
617 closest *= s;
618 closest.y = s.height - closest.y;
619
620 Color lcol = color;
621 lcol.a *= 0.4;
622 blend_space_draw->draw_line(point, closest, lcol, Math::round(2 * EDSCALE), true);
623 }
624
625 float mind = 5 * EDSCALE;
626 float maxd = 15 * EDSCALE;
627 blend_space_draw->draw_line(point + Vector2(mind, 0), point + Vector2(maxd, 0), color, Math::round(2 * EDSCALE));
628 blend_space_draw->draw_line(point + Vector2(-mind, 0), point + Vector2(-maxd, 0), color, Math::round(2 * EDSCALE));
629 blend_space_draw->draw_line(point + Vector2(0, mind), point + Vector2(0, maxd), color, Math::round(2 * EDSCALE));
630 blend_space_draw->draw_line(point + Vector2(0, -mind), point + Vector2(0, -maxd), color, Math::round(2 * EDSCALE));
631 }
632}
633
634void AnimationNodeBlendSpace2DEditor::_snap_toggled() {
635 blend_space_draw->queue_redraw();
636}
637
638void AnimationNodeBlendSpace2DEditor::_update_space() {
639 if (updating) {
640 return;
641 }
642
643 updating = true;
644
645 if (blend_space->get_auto_triangles()) {
646 tool_triangle->hide();
647 } else {
648 tool_triangle->show();
649 }
650
651 auto_triangles->set_pressed(blend_space->get_auto_triangles());
652
653 sync->set_pressed(blend_space->is_using_sync());
654 interpolation->select(blend_space->get_blend_mode());
655
656 max_x_value->set_value(blend_space->get_max_space().x);
657 max_y_value->set_value(blend_space->get_max_space().y);
658
659 min_x_value->set_value(blend_space->get_min_space().x);
660 min_y_value->set_value(blend_space->get_min_space().y);
661
662 label_x->set_text(blend_space->get_x_label());
663 label_y->set_text(blend_space->get_y_label());
664
665 snap_x->set_value(blend_space->get_snap().x);
666 snap_y->set_value(blend_space->get_snap().y);
667
668 blend_space_draw->queue_redraw();
669
670 updating = false;
671}
672
673void AnimationNodeBlendSpace2DEditor::_config_changed(double) {
674 if (updating) {
675 return;
676 }
677
678 updating = true;
679 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
680 undo_redo->create_action(TTR("Change BlendSpace2D Config"));
681 undo_redo->add_do_method(blend_space.ptr(), "set_max_space", Vector2(max_x_value->get_value(), max_y_value->get_value()));
682 undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space());
683 undo_redo->add_do_method(blend_space.ptr(), "set_min_space", Vector2(min_x_value->get_value(), min_y_value->get_value()));
684 undo_redo->add_undo_method(blend_space.ptr(), "set_min_space", blend_space->get_min_space());
685 undo_redo->add_do_method(blend_space.ptr(), "set_snap", Vector2(snap_x->get_value(), snap_y->get_value()));
686 undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap());
687 undo_redo->add_do_method(blend_space.ptr(), "set_use_sync", sync->is_pressed());
688 undo_redo->add_undo_method(blend_space.ptr(), "set_use_sync", blend_space->is_using_sync());
689 undo_redo->add_do_method(blend_space.ptr(), "set_blend_mode", interpolation->get_selected());
690 undo_redo->add_undo_method(blend_space.ptr(), "set_blend_mode", blend_space->get_blend_mode());
691 undo_redo->add_do_method(this, "_update_space");
692 undo_redo->add_undo_method(this, "_update_space");
693 undo_redo->commit_action();
694 updating = false;
695
696 blend_space_draw->queue_redraw();
697}
698
699void AnimationNodeBlendSpace2DEditor::_labels_changed(String) {
700 if (updating) {
701 return;
702 }
703
704 updating = true;
705 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
706 undo_redo->create_action(TTR("Change BlendSpace2D Labels"), UndoRedo::MERGE_ENDS);
707 undo_redo->add_do_method(blend_space.ptr(), "set_x_label", label_x->get_text());
708 undo_redo->add_undo_method(blend_space.ptr(), "set_x_label", blend_space->get_x_label());
709 undo_redo->add_do_method(blend_space.ptr(), "set_y_label", label_y->get_text());
710 undo_redo->add_undo_method(blend_space.ptr(), "set_y_label", blend_space->get_y_label());
711 undo_redo->add_do_method(this, "_update_space");
712 undo_redo->add_undo_method(this, "_update_space");
713 undo_redo->commit_action();
714 updating = false;
715}
716
717void AnimationNodeBlendSpace2DEditor::_erase_selected() {
718 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
719 if (selected_point != -1) {
720 updating = true;
721 undo_redo->create_action(TTR("Remove BlendSpace2D Point"));
722 undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point);
723 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);
724
725 //restore triangles using this point
726 for (int i = 0; i < blend_space->get_triangle_count(); i++) {
727 for (int j = 0; j < 3; j++) {
728 if (blend_space->get_triangle_point(i, j) == selected_point) {
729 undo_redo->add_undo_method(blend_space.ptr(), "add_triangle", blend_space->get_triangle_point(i, 0), blend_space->get_triangle_point(i, 1), blend_space->get_triangle_point(i, 2), i);
730 break;
731 }
732 }
733 }
734
735 undo_redo->add_do_method(this, "_update_space");
736 undo_redo->add_undo_method(this, "_update_space");
737 undo_redo->commit_action();
738 updating = false;
739
740 blend_space_draw->queue_redraw();
741 } else if (selected_triangle != -1) {
742 updating = true;
743 undo_redo->create_action(TTR("Remove BlendSpace2D Triangle"));
744 undo_redo->add_do_method(blend_space.ptr(), "remove_triangle", selected_triangle);
745 undo_redo->add_undo_method(blend_space.ptr(), "add_triangle", blend_space->get_triangle_point(selected_triangle, 0), blend_space->get_triangle_point(selected_triangle, 1), blend_space->get_triangle_point(selected_triangle, 2), selected_triangle);
746
747 undo_redo->add_do_method(this, "_update_space");
748 undo_redo->add_undo_method(this, "_update_space");
749 undo_redo->commit_action();
750 updating = false;
751
752 blend_space_draw->queue_redraw();
753 }
754}
755
756void AnimationNodeBlendSpace2DEditor::_update_edited_point_pos() {
757 if (updating) {
758 return;
759 }
760
761 if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {
762 Vector2 pos = blend_space->get_blend_point_position(selected_point);
763 if (dragging_selected) {
764 pos += drag_ofs;
765 if (snap->is_pressed()) {
766 pos = pos.snapped(blend_space->get_snap());
767 }
768 }
769 updating = true;
770 edit_x->set_value(pos.x);
771 edit_y->set_value(pos.y);
772 updating = false;
773 }
774}
775
776void AnimationNodeBlendSpace2DEditor::_edit_point_pos(double) {
777 if (updating) {
778 return;
779 }
780 updating = true;
781 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
782 undo_redo->create_action(TTR("Move Node Point"));
783 undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, Vector2(edit_x->get_value(), edit_y->get_value()));
784 undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));
785 undo_redo->add_do_method(this, "_update_space");
786 undo_redo->add_undo_method(this, "_update_space");
787 undo_redo->add_do_method(this, "_update_edited_point_pos");
788 undo_redo->add_undo_method(this, "_update_edited_point_pos");
789 undo_redo->commit_action();
790 updating = false;
791
792 blend_space_draw->queue_redraw();
793}
794
795void AnimationNodeBlendSpace2DEditor::_notification(int p_what) {
796 switch (p_what) {
797 case NOTIFICATION_ENTER_TREE:
798 case NOTIFICATION_THEME_CHANGED: {
799 error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
800 error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
801 panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
802 tool_blend->set_icon(get_editor_theme_icon(SNAME("EditPivot")));
803 tool_select->set_icon(get_editor_theme_icon(SNAME("ToolSelect")));
804 tool_create->set_icon(get_editor_theme_icon(SNAME("EditKey")));
805 tool_triangle->set_icon(get_editor_theme_icon(SNAME("ToolTriangle")));
806 tool_erase->set_icon(get_editor_theme_icon(SNAME("Remove")));
807 snap->set_icon(get_editor_theme_icon(SNAME("SnapGrid")));
808 open_editor->set_icon(get_editor_theme_icon(SNAME("Edit")));
809 auto_triangles->set_icon(get_editor_theme_icon(SNAME("AutoTriangle")));
810 interpolation->clear();
811 interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous")), "", 0);
812 interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete")), "", 1);
813 interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackCapture")), "", 2);
814 } break;
815
816 case NOTIFICATION_PROCESS: {
817 AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
818 if (!tree) {
819 return;
820 }
821
822 String error;
823
824 if (!tree->is_active()) {
825 error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails.");
826 } else if (tree->is_state_invalid()) {
827 error = tree->get_invalid_state_reason();
828 } else if (blend_space->get_triangle_count() == 0) {
829 error = TTR("No triangles exist, so no blending can take place.");
830 }
831
832 if (error != error_label->get_text()) {
833 error_label->set_text(error);
834 if (!error.is_empty()) {
835 error_panel->show();
836 } else {
837 error_panel->hide();
838 }
839 }
840 } break;
841
842 case NOTIFICATION_VISIBILITY_CHANGED: {
843 set_process(is_visible_in_tree());
844 } break;
845 }
846}
847
848void AnimationNodeBlendSpace2DEditor::_open_editor() {
849 if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {
850 Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point);
851 ERR_FAIL_COND(an.is_null());
852 AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point));
853 }
854}
855
856void AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled() {
857 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
858 undo_redo->create_action(TTR("Toggle Auto Triangles"));
859 undo_redo->add_do_method(blend_space.ptr(), "set_auto_triangles", auto_triangles->is_pressed());
860 undo_redo->add_undo_method(blend_space.ptr(), "set_auto_triangles", blend_space->get_auto_triangles());
861 undo_redo->add_do_method(this, "_update_space");
862 undo_redo->add_undo_method(this, "_update_space");
863 undo_redo->commit_action();
864}
865
866void AnimationNodeBlendSpace2DEditor::_bind_methods() {
867 ClassDB::bind_method("_update_space", &AnimationNodeBlendSpace2DEditor::_update_space);
868 ClassDB::bind_method("_update_tool_erase", &AnimationNodeBlendSpace2DEditor::_update_tool_erase);
869
870 ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace2DEditor::_update_edited_point_pos);
871}
872
873AnimationNodeBlendSpace2DEditor *AnimationNodeBlendSpace2DEditor::singleton = nullptr;
874
875AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
876 singleton = this;
877 updating = false;
878
879 HBoxContainer *top_hb = memnew(HBoxContainer);
880 add_child(top_hb);
881
882 Ref<ButtonGroup> bg;
883 bg.instantiate();
884
885 tool_blend = memnew(Button);
886 tool_blend->set_flat(true);
887 tool_blend->set_toggle_mode(true);
888 tool_blend->set_button_group(bg);
889 top_hb->add_child(tool_blend);
890 tool_blend->set_pressed(true);
891 tool_blend->set_tooltip_text(TTR("Set the blending position within the space"));
892 tool_blend->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_tool_switch).bind(3));
893
894 tool_select = memnew(Button);
895 tool_select->set_flat(true);
896 tool_select->set_toggle_mode(true);
897 tool_select->set_button_group(bg);
898 top_hb->add_child(tool_select);
899 tool_select->set_tooltip_text(TTR("Select and move points, create points with RMB."));
900 tool_select->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_tool_switch).bind(0));
901
902 tool_create = memnew(Button);
903 tool_create->set_flat(true);
904 tool_create->set_toggle_mode(true);
905 tool_create->set_button_group(bg);
906 top_hb->add_child(tool_create);
907 tool_create->set_tooltip_text(TTR("Create points."));
908 tool_create->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_tool_switch).bind(1));
909
910 tool_triangle = memnew(Button);
911 tool_triangle->set_flat(true);
912 tool_triangle->set_toggle_mode(true);
913 tool_triangle->set_button_group(bg);
914 top_hb->add_child(tool_triangle);
915 tool_triangle->set_tooltip_text(TTR("Create triangles by connecting points."));
916 tool_triangle->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_tool_switch).bind(2));
917
918 tool_erase_sep = memnew(VSeparator);
919 top_hb->add_child(tool_erase_sep);
920 tool_erase = memnew(Button);
921 tool_erase->set_flat(true);
922 top_hb->add_child(tool_erase);
923 tool_erase->set_tooltip_text(TTR("Erase points and triangles."));
924 tool_erase->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_erase_selected));
925 tool_erase->set_disabled(true);
926
927 top_hb->add_child(memnew(VSeparator));
928
929 auto_triangles = memnew(Button);
930 auto_triangles->set_flat(true);
931 top_hb->add_child(auto_triangles);
932 auto_triangles->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled));
933 auto_triangles->set_toggle_mode(true);
934 auto_triangles->set_tooltip_text(TTR("Generate blend triangles automatically (instead of manually)"));
935
936 top_hb->add_child(memnew(VSeparator));
937
938 snap = memnew(Button);
939 snap->set_flat(true);
940 snap->set_toggle_mode(true);
941 top_hb->add_child(snap);
942 snap->set_pressed(true);
943 snap->set_tooltip_text(TTR("Enable snap and show grid."));
944 snap->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_snap_toggled));
945
946 snap_x = memnew(SpinBox);
947 top_hb->add_child(snap_x);
948 snap_x->set_prefix("x:");
949 snap_x->set_min(0.01);
950 snap_x->set_step(0.01);
951 snap_x->set_max(1000);
952
953 snap_y = memnew(SpinBox);
954 top_hb->add_child(snap_y);
955 snap_y->set_prefix("y:");
956 snap_y->set_min(0.01);
957 snap_y->set_step(0.01);
958 snap_y->set_max(1000);
959
960 top_hb->add_child(memnew(VSeparator));
961
962 top_hb->add_child(memnew(Label(TTR("Sync:"))));
963 sync = memnew(CheckBox);
964 top_hb->add_child(sync);
965 sync->connect("toggled", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));
966
967 top_hb->add_child(memnew(VSeparator));
968
969 top_hb->add_child(memnew(Label(TTR("Blend:"))));
970 interpolation = memnew(OptionButton);
971 top_hb->add_child(interpolation);
972 interpolation->connect("item_selected", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));
973
974 edit_hb = memnew(HBoxContainer);
975 top_hb->add_child(edit_hb);
976 edit_hb->add_child(memnew(VSeparator));
977 edit_hb->add_child(memnew(Label(TTR("Point"))));
978 edit_x = memnew(SpinBox);
979 edit_hb->add_child(edit_x);
980 edit_x->set_min(-1000);
981 edit_x->set_step(0.01);
982 edit_x->set_max(1000);
983 edit_x->connect("value_changed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_edit_point_pos));
984 edit_y = memnew(SpinBox);
985 edit_hb->add_child(edit_y);
986 edit_y->set_min(-1000);
987 edit_y->set_step(0.01);
988 edit_y->set_max(1000);
989 edit_y->connect("value_changed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_edit_point_pos));
990 open_editor = memnew(Button);
991 edit_hb->add_child(open_editor);
992 open_editor->set_text(TTR("Open Editor"));
993 open_editor->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_open_editor), CONNECT_DEFERRED);
994 edit_hb->hide();
995 open_editor->hide();
996
997 HBoxContainer *main_hb = memnew(HBoxContainer);
998 add_child(main_hb);
999 main_hb->set_v_size_flags(SIZE_EXPAND_FILL);
1000
1001 GridContainer *main_grid = memnew(GridContainer);
1002 main_grid->set_columns(2);
1003 main_hb->add_child(main_grid);
1004 main_grid->set_h_size_flags(SIZE_EXPAND_FILL);
1005 {
1006 VBoxContainer *left_vbox = memnew(VBoxContainer);
1007 main_grid->add_child(left_vbox);
1008 left_vbox->set_v_size_flags(SIZE_EXPAND_FILL);
1009 max_y_value = memnew(SpinBox);
1010 left_vbox->add_child(max_y_value);
1011 left_vbox->add_spacer();
1012 label_y = memnew(LineEdit);
1013 left_vbox->add_child(label_y);
1014 label_y->set_expand_to_text_length_enabled(true);
1015 left_vbox->add_spacer();
1016 min_y_value = memnew(SpinBox);
1017 left_vbox->add_child(min_y_value);
1018
1019 max_y_value->set_max(10000);
1020 max_y_value->set_min(0.01);
1021 max_y_value->set_step(0.01);
1022
1023 min_y_value->set_min(-10000);
1024 min_y_value->set_max(0);
1025 min_y_value->set_step(0.01);
1026 }
1027
1028 panel = memnew(PanelContainer);
1029 panel->set_clip_contents(true);
1030 main_grid->add_child(panel);
1031 panel->set_h_size_flags(SIZE_EXPAND_FILL);
1032
1033 blend_space_draw = memnew(Control);
1034 blend_space_draw->connect("gui_input", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_blend_space_gui_input));
1035 blend_space_draw->connect("draw", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_blend_space_draw));
1036 blend_space_draw->set_focus_mode(FOCUS_ALL);
1037
1038 panel->add_child(blend_space_draw);
1039 main_grid->add_child(memnew(Control)); //empty bottom left
1040
1041 {
1042 HBoxContainer *bottom_vbox = memnew(HBoxContainer);
1043 main_grid->add_child(bottom_vbox);
1044 bottom_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
1045 min_x_value = memnew(SpinBox);
1046 bottom_vbox->add_child(min_x_value);
1047 bottom_vbox->add_spacer();
1048 label_x = memnew(LineEdit);
1049 bottom_vbox->add_child(label_x);
1050 label_x->set_expand_to_text_length_enabled(true);
1051 bottom_vbox->add_spacer();
1052 max_x_value = memnew(SpinBox);
1053 bottom_vbox->add_child(max_x_value);
1054
1055 max_x_value->set_max(10000);
1056 max_x_value->set_min(0.01);
1057 max_x_value->set_step(0.01);
1058
1059 min_x_value->set_min(-10000);
1060 min_x_value->set_max(0);
1061 min_x_value->set_step(0.01);
1062 }
1063
1064 snap_x->connect("value_changed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));
1065 snap_y->connect("value_changed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));
1066 max_x_value->connect("value_changed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));
1067 min_x_value->connect("value_changed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));
1068 max_y_value->connect("value_changed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));
1069 min_y_value->connect("value_changed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));
1070 label_x->connect("text_changed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_labels_changed));
1071 label_y->connect("text_changed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_labels_changed));
1072
1073 error_panel = memnew(PanelContainer);
1074 add_child(error_panel);
1075 error_label = memnew(Label);
1076 error_panel->add_child(error_label);
1077 error_label->set_text("eh");
1078
1079 set_custom_minimum_size(Size2(0, 300 * EDSCALE));
1080
1081 menu = memnew(PopupMenu);
1082 add_child(menu);
1083 menu->connect("id_pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_add_menu_type));
1084
1085 animations_menu = memnew(PopupMenu);
1086 menu->add_child(animations_menu);
1087 animations_menu->set_name("animations");
1088 animations_menu->connect("index_pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_add_animation_type));
1089
1090 open_file = memnew(EditorFileDialog);
1091 add_child(open_file);
1092 open_file->set_title(TTR("Open Animation Node"));
1093 open_file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
1094 open_file->connect("file_selected", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_file_opened));
1095
1096 selected_point = -1;
1097 selected_triangle = -1;
1098
1099 dragging_selected = false;
1100 dragging_selected_attempt = false;
1101}
1102