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