From 0f7c35f95561c41648415c370005093827e5e445 Mon Sep 17 00:00:00 2001 From: Thomas Herzog Date: Fri, 22 Jun 2018 21:52:13 +0200 Subject: added BlendSpace1D editor plugin This commit also fixes a crash in the BlendSpace2D as well as correct the drawing of the x-zero indicator in the BlendSpace2D editor plugin. --- editor/editor_node.cpp | 4 +- editor/plugins/animation_blend_space2d_editor.cpp | 1023 -------------------- editor/plugins/animation_blend_space2d_editor.h | 130 --- editor/plugins/animation_blend_space_1d_editor.cpp | 741 ++++++++++++++ editor/plugins/animation_blend_space_1d_editor.h | 117 +++ editor/plugins/animation_blend_space_2d_editor.cpp | 1023 ++++++++++++++++++++ editor/plugins/animation_blend_space_2d_editor.h | 130 +++ scene/animation/animation_blend_space1d.cpp | 272 ------ scene/animation/animation_blend_space1d.h | 68 -- scene/animation/animation_blend_space2d.cpp | 558 ----------- scene/animation/animation_blend_space2d.h | 94 -- scene/animation/animation_blend_space_1d.cpp | 272 ++++++ scene/animation/animation_blend_space_1d.h | 68 ++ scene/animation/animation_blend_space_2d.cpp | 558 +++++++++++ scene/animation/animation_blend_space_2d.h | 94 ++ scene/register_scene_types.cpp | 4 +- 16 files changed, 3008 insertions(+), 2148 deletions(-) delete mode 100644 editor/plugins/animation_blend_space2d_editor.cpp delete mode 100644 editor/plugins/animation_blend_space2d_editor.h create mode 100644 editor/plugins/animation_blend_space_1d_editor.cpp create mode 100644 editor/plugins/animation_blend_space_1d_editor.h create mode 100644 editor/plugins/animation_blend_space_2d_editor.cpp create mode 100644 editor/plugins/animation_blend_space_2d_editor.h delete mode 100644 scene/animation/animation_blend_space1d.cpp delete mode 100644 scene/animation/animation_blend_space1d.h delete mode 100644 scene/animation/animation_blend_space2d.cpp delete mode 100644 scene/animation/animation_blend_space2d.h create mode 100644 scene/animation/animation_blend_space_1d.cpp create mode 100644 scene/animation/animation_blend_space_1d.h create mode 100644 scene/animation/animation_blend_space_2d.cpp create mode 100644 scene/animation/animation_blend_space_2d.h diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 0cf5e94f4c..67e1ca79f3 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -66,7 +66,8 @@ #include "editor/import/resource_importer_scene.h" #include "editor/import/resource_importer_texture.h" #include "editor/import/resource_importer_wav.h" -#include "editor/plugins/animation_blend_space2d_editor.h" +#include "editor/plugins/animation_blend_space_1d_editor.h" +#include "editor/plugins/animation_blend_space_2d_editor.h" #include "editor/plugins/animation_blend_tree_editor_plugin.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/animation_state_machine_editor.h" @@ -5401,6 +5402,7 @@ EditorNode::EditorNode() { // FIXME: Disabled for Godot 3.0 as made incompatible, it needs to be ported to the new API. //add_editor_plugin(memnew(ShaderGraphEditorPlugin(this))); add_editor_plugin(memnew(AnimationNodeBlendTreeEditorPlugin(this))); + add_editor_plugin(memnew(AnimationNodeBlendSpace1DEditorPlugin(this))); add_editor_plugin(memnew(AnimationNodeBlendSpace2DEditorPlugin(this))); add_editor_plugin(memnew(AnimationNodeStateMachineEditorPlugin(this))); diff --git a/editor/plugins/animation_blend_space2d_editor.cpp b/editor/plugins/animation_blend_space2d_editor.cpp deleted file mode 100644 index de72bd5198..0000000000 --- a/editor/plugins/animation_blend_space2d_editor.cpp +++ /dev/null @@ -1,1023 +0,0 @@ -#include "animation_blend_space2d_editor.h" - -#include "core/io/resource_loader.h" -#include "core/project_settings.h" -#include "math/delaunay.h" -#include "os/input.h" -#include "os/keyboard.h" -#include "scene/animation/animation_blend_tree.h" -#include "scene/animation/animation_player.h" -#include "scene/gui/menu_button.h" -#include "scene/gui/panel.h" -#include "scene/main/viewport.h" - -void AnimationNodeBlendSpace2DEditor::edit(AnimationNodeBlendSpace2D *p_blend_space) { - - if (blend_space.is_valid()) { - blend_space->disconnect("removed_from_graph", this, "_removed_from_graph"); - } - - if (p_blend_space) { - blend_space = Ref(p_blend_space); - } else { - blend_space.unref(); - } - - if (blend_space.is_null()) { - hide(); - } else { - blend_space->connect("removed_from_graph", this, "_removed_from_graph"); - - _update_space(); - } -} - -void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref &p_event) { - - Ref k = p_event; - if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && !k->is_echo()) { - if (selected_point != -1 || selected_triangle != -1) { - _erase_selected(); - accept_event(); - } - } - - Ref mb = p_event; - - 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()))) { - menu->clear(); - animations_menu->clear(); - animations_to_add.clear(); - List classes; - classes.sort_custom(); - - ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); - menu->add_submenu_item(TTR("Add Animation"), "animations"); - - AnimationGraphPlayer *gp = blend_space->get_graph_player(); - ERR_FAIL_COND(!gp); - if (gp && gp->has_node(gp->get_animation_player())) { - AnimationPlayer *ap = Object::cast_to(gp->get_node(gp->get_animation_player())); - if (ap) { - List names; - ap->get_animation_list(&names); - for (List::Element *E = names.front(); E; E = E->next()) { - animations_menu->add_icon_item(get_icon("Animation", "EditorIcons"), E->get()); - animations_to_add.push_back(E->get()); - } - } - } - - for (List::Element *E = classes.front(); E; E = E->next()) { - - String name = String(E->get()).replace_first("AnimationNode", ""); - if (name == "Animation") - continue; // nope - int idx = menu->get_item_count(); - menu->add_item(vformat("Add %s", name)); - menu->set_item_metadata(idx, E->get()); - } - - menu->set_global_position(blend_space_draw->get_global_transform().xform(mb->get_position())); - menu->popup(); - add_point_pos = (mb->get_position() / blend_space_draw->get_size()); - add_point_pos.y = 1.0 - add_point_pos.y; - add_point_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); - add_point_pos += blend_space->get_min_space(); - - if (snap->is_pressed()) { - add_point_pos.x = Math::stepify(add_point_pos.x, blend_space->get_snap().x); - add_point_pos.y = Math::stepify(add_point_pos.y, blend_space->get_snap().y); - } - } - - if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - - blend_space_draw->update(); //update anyway - //try to see if a point can be selected - selected_point = -1; - selected_triangle = -1; - _update_tool_erase(); - - for (int i = 0; i < points.size(); i++) { - - if (points[i].distance_to(mb->get_position()) < 10 * EDSCALE) { - selected_point = i; - Ref node = blend_space->get_blend_point_node(i); - EditorNode::get_singleton()->push_item(node.ptr(), "", true); - dragging_selected_attempt = true; - drag_from = mb->get_position(); - _update_tool_erase(); - _update_edited_point_pos(); - return; - } - } - - //then try to see if a triangle can be selected - if (!blend_space->get_auto_triangles()) { //if autotriangles use, disable this - for (int i = 0; i < blend_space->get_triangle_count(); i++) { - Vector triangle; - - for (int j = 0; j < 3; j++) { - int idx = blend_space->get_triangle_point(i, j); - ERR_FAIL_INDEX(idx, points.size()); - triangle.push_back(points[idx]); - } - - if (Geometry::is_point_in_triangle(mb->get_position(), triangle[0], triangle[1], triangle[2])) { - selected_triangle = i; - _update_tool_erase(); - return; - } - } - } - } - - if (mb.is_valid() && mb->is_pressed() && tool_triangle->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - - blend_space_draw->update(); //update anyway - //try to see if a point can be selected - selected_point = -1; - - for (int i = 0; i < points.size(); i++) { - - if (making_triangle.find(i) != -1) - continue; - - if (points[i].distance_to(mb->get_position()) < 10 * EDSCALE) { - making_triangle.push_back(i); - if (making_triangle.size() == 3) { - //add triangle! - if (blend_space->has_triangle(making_triangle[0], making_triangle[1], making_triangle[2])) { - making_triangle.clear(); - EditorNode::get_singleton()->show_warning(TTR("Triangle already exists")); - return; - } - - updating = true; - undo_redo->create_action("Add Triangle"); - undo_redo->add_do_method(blend_space.ptr(), "add_triangle", making_triangle[0], making_triangle[1], making_triangle[2]); - undo_redo->add_undo_method(blend_space.ptr(), "remove_triangle", blend_space->get_triangle_count()); - undo_redo->add_do_method(this, "_update_space"); - undo_redo->add_undo_method(this, "_update_space"); - undo_redo->commit_action(); - updating = false; - making_triangle.clear(); - } - return; - } - } - } - - if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == BUTTON_LEFT) { - if (dragging_selected) { - //move - Vector2 point = blend_space->get_blend_point_position(selected_point); - point += drag_ofs; - if (snap->is_pressed()) { - point.x = Math::stepify(point.x, blend_space->get_snap().x); - point.y = Math::stepify(point.y, blend_space->get_snap().y); - } - - updating = true; - undo_redo->create_action("Move Node Point"); - undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point); - undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); - undo_redo->add_do_method(this, "_update_space"); - undo_redo->add_undo_method(this, "_update_space"); - undo_redo->add_do_method(this, "_update_edited_point_pos"); - undo_redo->add_undo_method(this, "_update_edited_point_pos"); - undo_redo->commit_action(); - updating = false; - _update_edited_point_pos(); - } - dragging_selected_attempt = false; - dragging_selected = false; - blend_space_draw->update(); - } - - if (mb.is_valid() && mb->is_pressed() && tool_blend->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - - Vector2 blend_pos = (mb->get_position() / blend_space_draw->get_size()); - blend_pos.y = 1.0 - blend_pos.y; - blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); - blend_pos += blend_space->get_min_space(); - - blend_space->set_blend_position(blend_pos); - blend_space_draw->update(); - } - - Ref mm = p_event; - - if (mm.is_valid() && !blend_space_draw->has_focus()) { - blend_space_draw->grab_focus(); - blend_space_draw->update(); - } - - if (mm.is_valid() && dragging_selected_attempt) { - dragging_selected = true; - 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); - blend_space_draw->update(); - _update_edited_point_pos(); - } - - if (mm.is_valid() && tool_triangle->is_pressed() && making_triangle.size()) { - blend_space_draw->update(); - } - - if (mm.is_valid() && !tool_triangle->is_pressed() && making_triangle.size()) { - making_triangle.clear(); - blend_space_draw->update(); - } - - if (mm.is_valid() && tool_blend->is_pressed() && mm->get_button_mask() & BUTTON_MASK_LEFT) { - - Vector2 blend_pos = (mm->get_position() / blend_space_draw->get_size()); - blend_pos.y = 1.0 - blend_pos.y; - blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); - blend_pos += blend_space->get_min_space(); - - blend_space->set_blend_position(blend_pos); - blend_space_draw->update(); - } -} - -void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) { - - String type = menu->get_item_metadata(p_index); - - Object *obj = ClassDB::instance(type); - ERR_FAIL_COND(!obj); - AnimationNode *an = Object::cast_to(obj); - ERR_FAIL_COND(!an); - - Ref node(an); - - updating = true; - undo_redo->create_action("Add Node Point"); - undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos); - undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); - undo_redo->add_do_method(this, "_update_space"); - undo_redo->add_undo_method(this, "_update_space"); - undo_redo->commit_action(); - updating = false; - - blend_space_draw->update(); -} - -void AnimationNodeBlendSpace2DEditor::_add_animation_type(int p_index) { - - Ref anim; - anim.instance(); - - anim->set_animation(animations_to_add[p_index]); - - updating = true; - undo_redo->create_action("Add Animation Point"); - undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos); - undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); - undo_redo->add_do_method(this, "_update_space"); - undo_redo->add_undo_method(this, "_update_space"); - undo_redo->commit_action(); - updating = false; - - blend_space_draw->update(); -} - -void AnimationNodeBlendSpace2DEditor::_update_tool_erase() { - 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())); - if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { - Ref an = blend_space->get_blend_point_node(selected_point); - if (EditorNode::get_singleton()->item_has_editor(an.ptr())) { - open_editor->show(); - } else { - open_editor->hide(); - } - edit_hb->show(); - } else { - edit_hb->hide(); - } -} - -void AnimationNodeBlendSpace2DEditor::_tool_switch(int p_tool) { - making_triangle.clear(); - - if (p_tool == 2) { - Vector points; - for (int i = 0; i < blend_space->get_blend_point_count(); i++) { - points.push_back(blend_space->get_blend_point_position(i)); - } - Vector tr = Delaunay2D::triangulate(points); - print_line("triangleS: " + itos(tr.size())); - for (int i = 0; i < tr.size(); i++) { - blend_space->add_triangle(tr[i].points[0], tr[i].points[1], tr[i].points[2]); - } - } - - if (p_tool == 0) { - tool_erase->show(); - tool_erase_sep->show(); - } else { - tool_erase->hide(); - tool_erase_sep->hide(); - } - _update_tool_erase(); - blend_space_draw->update(); -} - -void AnimationNodeBlendSpace2DEditor::_blend_space_draw() { - - Color linecolor = get_color("font_color", "Label"); - Color linecolor_soft = linecolor; - linecolor_soft.a *= 0.5; - Ref font = get_font("font", "Label"); - Ref icon = get_icon("KeyValue", "EditorIcons"); - Ref icon_selected = get_icon("KeySelected", "EditorIcons"); - - Size2 s = blend_space_draw->get_size(); - - if (blend_space_draw->has_focus()) { - Color color = get_color("accent_color", "Editor"); - blend_space_draw->draw_rect(Rect2(Point2(), s), color, false); - } - blend_space_draw->draw_line(Point2(1, 0), Point2(1, s.height - 1), linecolor); - blend_space_draw->draw_line(Point2(1, s.height - 1), Point2(s.width - 1, s.height - 1), linecolor); - - blend_space_draw->draw_line(Point2(0, 0), Point2(5 * EDSCALE, 0), linecolor); - if (blend_space->get_min_space().y < 0) { - int y = (blend_space->get_max_space().y / (blend_space->get_max_space().y - blend_space->get_min_space().y)) * s.height; - blend_space_draw->draw_line(Point2(0, y), Point2(5 * EDSCALE, y), linecolor); - blend_space_draw->draw_string(font, Point2(2 * EDSCALE, y - font->get_height() + font->get_ascent()), "0", linecolor); - blend_space_draw->draw_line(Point2(5 * EDSCALE, y), Point2(s.width, y), linecolor_soft); - } - - if (blend_space->get_min_space().x < 0) { - int x = (blend_space->get_max_space().x / (blend_space->get_max_space().x - blend_space->get_min_space().x)) * s.width; - blend_space_draw->draw_line(Point2(x, s.height - 1), Point2(x, s.height - 5 * EDSCALE), linecolor); - blend_space_draw->draw_string(font, Point2(x + 2 * EDSCALE, s.height - 2 * EDSCALE - font->get_height() + font->get_ascent()), "0", linecolor); - blend_space_draw->draw_line(Point2(x, s.height - 5 * EDSCALE), Point2(x, 0), linecolor_soft); - } - - if (snap->is_pressed()) { - - linecolor_soft.a = linecolor.a * 0.1; - - if (blend_space->get_snap().x > 0) { - - int prev_idx; - for (int i = 0; i < s.x; i++) { - - float v = blend_space->get_min_space().x + i * (blend_space->get_max_space().x - blend_space->get_min_space().x) / s.x; - int idx = int(v / blend_space->get_snap().x); - - if (i > 0 && prev_idx != idx) { - blend_space_draw->draw_line(Point2(i, 0), Point2(i, s.height), linecolor_soft); - } - - prev_idx = idx; - } - } - - if (blend_space->get_snap().y > 0) { - - int prev_idx; - for (int i = 0; i < s.y; i++) { - - float v = blend_space->get_max_space().y - i * (blend_space->get_max_space().y - blend_space->get_min_space().y) / s.y; - int idx = int(v / blend_space->get_snap().y); - - if (i > 0 && prev_idx != idx) { - blend_space_draw->draw_line(Point2(0, i), Point2(s.width, i), linecolor_soft); - } - - prev_idx = idx; - } - } - } - - //triangles first - for (int i = 0; i < blend_space->get_triangle_count(); i++) { - - Vector points; - points.resize(3); - - for (int j = 0; j < 3; j++) { - int point_idx = blend_space->get_triangle_point(i, j); - Vector2 point = blend_space->get_blend_point_position(point_idx); - if (dragging_selected && selected_point == point_idx) { - point += drag_ofs; - if (snap->is_pressed()) { - point.x = Math::stepify(point.x, blend_space->get_snap().x); - point.y = Math::stepify(point.y, blend_space->get_snap().y); - } - } - point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); - point *= s; - point.y = s.height - point.y; - points[j] = point; - } - - for (int j = 0; j < 3; j++) { - blend_space_draw->draw_line(points[j], points[(j + 1) % 3], linecolor, 1, true); - } - - Color color; - if (i == selected_triangle) { - color = get_color("accent_color", "Editor"); - color.a *= 0.5; - } else { - color = linecolor; - color.a *= 0.2; - } - - Vector colors; - colors.push_back(color); - colors.push_back(color); - colors.push_back(color); - blend_space_draw->draw_primitive(points, colors, Vector()); - } - - points.clear(); - for (int i = 0; i < blend_space->get_blend_point_count(); i++) { - - Vector2 point = blend_space->get_blend_point_position(i); - if (dragging_selected && selected_point == i) { - point += drag_ofs; - if (snap->is_pressed()) { - point.x = Math::stepify(point.x, blend_space->get_snap().x); - point.y = Math::stepify(point.y, blend_space->get_snap().y); - } - } - point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); - point *= s; - point.y = s.height - point.y; - - points.push_back(point); - point -= (icon->get_size() / 2); - point = point.floor(); - - if (i == selected_point) { - blend_space_draw->draw_texture(icon_selected, point); - } else { - blend_space_draw->draw_texture(icon, point); - } - } - - if (making_triangle.size()) { - Vector points; - for (int i = 0; i < making_triangle.size(); i++) { - Vector2 point = blend_space->get_blend_point_position(making_triangle[i]); - point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); - point *= s; - point.y = s.height - point.y; - points.push_back(point); - } - - for (int i = 0; i < points.size() - 1; i++) { - blend_space_draw->draw_line(points[i], points[i + 1], linecolor, 2, true); - } - blend_space_draw->draw_line(points[points.size() - 1], blend_space_draw->get_local_mouse_position(), linecolor, 2, true); - } - - ///draw cursor position - - { - Color color; - if (tool_blend->is_pressed()) { - color = get_color("accent_color", "Editor"); - } else { - color = linecolor; - color.a *= 0.5; - } - - Vector2 point = blend_space->get_blend_position(); - point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); - point *= s; - point.y = s.height - point.y; - - if (blend_space->get_triangle_count()) { - Vector2 closest = blend_space->get_closest_point(blend_space->get_blend_position()); - closest = (closest - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); - closest *= s; - closest.y = s.height - closest.y; - - Color lcol = color; - lcol.a *= 0.4; - blend_space_draw->draw_line(point, closest, lcol, 2); - } - - float mind = 5 * EDSCALE; - float maxd = 15 * EDSCALE; - blend_space_draw->draw_line(point + Vector2(mind, 0), point + Vector2(maxd, 0), color, 2); - blend_space_draw->draw_line(point + Vector2(-mind, 0), point + Vector2(-maxd, 0), color, 2); - blend_space_draw->draw_line(point + Vector2(0, mind), point + Vector2(0, maxd), color, 2); - blend_space_draw->draw_line(point + Vector2(0, -mind), point + Vector2(0, -maxd), color, 2); - } -} - -void AnimationNodeBlendSpace2DEditor::_snap_toggled() { - - blend_space_draw->update(); -} - -void AnimationNodeBlendSpace2DEditor::_update_space() { - - if (updating) - return; - - updating = true; - - if (blend_space->get_parent().is_valid()) { - goto_parent_hb->show(); - } else { - goto_parent_hb->hide(); - } - - if (blend_space->get_auto_triangles()) { - tool_triangle->hide(); - } else { - tool_triangle->show(); - } - - auto_triangles->set_pressed(blend_space->get_auto_triangles()); - - max_x_value->set_value(blend_space->get_max_space().x); - max_y_value->set_value(blend_space->get_max_space().y); - - min_x_value->set_value(blend_space->get_min_space().x); - min_y_value->set_value(blend_space->get_min_space().y); - - label_x->set_text(blend_space->get_x_label()); - label_y->set_text(blend_space->get_y_label()); - - snap_x->set_value(blend_space->get_snap().x); - snap_y->set_value(blend_space->get_snap().y); - - blend_space_draw->update(); - - updating = false; -} - -void AnimationNodeBlendSpace2DEditor::_config_changed(double) { - if (updating) - return; - - updating = true; - undo_redo->create_action("Change BlendSpace2D Limits"); - undo_redo->add_do_method(blend_space.ptr(), "set_max_space", Vector2(max_x_value->get_value(), max_y_value->get_value())); - undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space()); - undo_redo->add_do_method(blend_space.ptr(), "set_min_space", Vector2(min_x_value->get_value(), min_y_value->get_value())); - undo_redo->add_undo_method(blend_space.ptr(), "set_min_space", blend_space->get_min_space()); - undo_redo->add_do_method(blend_space.ptr(), "set_snap", Vector2(snap_x->get_value(), snap_y->get_value())); - undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap()); - undo_redo->add_do_method(this, "_update_space"); - undo_redo->add_undo_method(this, "_update_space"); - undo_redo->commit_action(); - updating = false; - - blend_space_draw->update(); -} - -void AnimationNodeBlendSpace2DEditor::_labels_changed(String) { - if (updating) - return; - - updating = true; - undo_redo->create_action("Change BlendSpace2D Labels", UndoRedo::MERGE_ENDS); - undo_redo->add_do_method(blend_space.ptr(), "set_x_label", label_x->get_text()); - undo_redo->add_undo_method(blend_space.ptr(), "set_x_label", blend_space->get_x_label()); - undo_redo->add_do_method(blend_space.ptr(), "set_y_label", label_y->get_text()); - undo_redo->add_undo_method(blend_space.ptr(), "set_y_label", blend_space->get_y_label()); - undo_redo->add_do_method(this, "_update_space"); - undo_redo->add_undo_method(this, "_update_space"); - undo_redo->commit_action(); - updating = false; -} - -void AnimationNodeBlendSpace2DEditor::_erase_selected() { - - if (selected_point != -1) { - - updating = true; - undo_redo->create_action("Remove BlendSpace2D Point"); - undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point); - 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); - - //restore triangles using this point - for (int i = 0; i < blend_space->get_triangle_count(); i++) { - for (int j = 0; j < 3; j++) { - if (blend_space->get_triangle_point(i, j) == selected_point) { - 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); - break; - } - } - } - - undo_redo->add_do_method(this, "_update_space"); - undo_redo->add_undo_method(this, "_update_space"); - undo_redo->commit_action(); - updating = false; - - blend_space_draw->update(); - } else if (selected_triangle != -1) { - - updating = true; - undo_redo->create_action("Remove BlendSpace2D Triangle"); - undo_redo->add_do_method(blend_space.ptr(), "remove_triangle", selected_triangle); - 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); - - undo_redo->add_do_method(this, "_update_space"); - undo_redo->add_undo_method(this, "_update_space"); - undo_redo->commit_action(); - updating = false; - - blend_space_draw->update(); - } -} - -void AnimationNodeBlendSpace2DEditor::_update_edited_point_pos() { - if (updating) - return; - - if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { - Vector2 pos = blend_space->get_blend_point_position(selected_point); - if (dragging_selected) { - pos += drag_ofs; - if (snap->is_pressed()) { - pos.x = Math::stepify(pos.x, blend_space->get_snap().x); - pos.y = Math::stepify(pos.y, blend_space->get_snap().y); - } - } - updating = true; - edit_x->set_value(pos.x); - edit_y->set_value(pos.y); - updating = false; - } -} - -void AnimationNodeBlendSpace2DEditor::_edit_point_pos(double) { - if (updating) - return; - updating = true; - undo_redo->create_action("Move Node Point"); - undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, Vector2(edit_x->get_value(), edit_y->get_value())); - undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); - undo_redo->add_do_method(this, "_update_space"); - undo_redo->add_undo_method(this, "_update_space"); - undo_redo->add_do_method(this, "_update_edited_point_pos"); - undo_redo->add_undo_method(this, "_update_edited_point_pos"); - undo_redo->commit_action(); - updating = false; - - blend_space_draw->update(); -} - -void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { - - if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { - error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); - error_label->add_color_override("font_color", get_color("error_color", "Editor")); - panel->add_style_override("panel", get_stylebox("bg", "Tree")); - tool_blend->set_icon(get_icon("EditPivot", "EditorIcons")); - tool_select->set_icon(get_icon("ToolSelect", "EditorIcons")); - tool_create->set_icon(get_icon("EditKey", "EditorIcons")); - tool_triangle->set_icon(get_icon("ToolTriangle", "EditorIcons")); - tool_erase->set_icon(get_icon("Remove", "EditorIcons")); - snap->set_icon(get_icon("SnapGrid", "EditorIcons")); - open_editor->set_icon(get_icon("Edit", "EditorIcons")); - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); - auto_triangles->set_icon(get_icon("AutoTriangle", "EditorIcons")); - } - - if (p_what == NOTIFICATION_PROCESS) { - - String error; - - if (!blend_space->get_graph_player()) { - error = TTR("BlendSpace2D does not belong to an AnimationGraphPlayer node."); - } else if (!blend_space->get_graph_player()->is_active()) { - error = TTR("AnimationGraphPlayer is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (blend_space->get_graph_player()->is_state_invalid()) { - error = blend_space->get_graph_player()->get_invalid_state_reason(); - } else if (blend_space->get_triangle_count() == 0) { - error = TTR("No triangles exist, so no blending can take place."); - } - - if (error != error_label->get_text()) { - error_label->set_text(error); - if (error != String()) { - error_panel->show(); - } else { - error_panel->hide(); - } - } - } -} - -void AnimationNodeBlendSpace2DEditor::_open_editor() { - - if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { - Ref an = blend_space->get_blend_point_node(selected_point); - ERR_FAIL_COND(!an.is_valid()); - EditorNode::get_singleton()->edit_item(an.ptr()); - } -} - -void AnimationNodeBlendSpace2DEditor::_goto_parent() { - - EditorNode::get_singleton()->edit_item(blend_space->get_parent().ptr()); -} - -void AnimationNodeBlendSpace2DEditor::_removed_from_graph() { - EditorNode::get_singleton()->edit_item(NULL); -} - -void AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled() { - - undo_redo->create_action("Toggle Auto Triangles"); - undo_redo->add_do_method(blend_space.ptr(), "set_auto_triangles", auto_triangles->is_pressed()); - undo_redo->add_undo_method(blend_space.ptr(), "set_auto_triangles", blend_space->get_auto_triangles()); - undo_redo->add_do_method(this, "_update_space"); - undo_redo->add_undo_method(this, "_update_space"); - undo_redo->commit_action(); -} - -void AnimationNodeBlendSpace2DEditor::_bind_methods() { - - ClassDB::bind_method("_blend_space_gui_input", &AnimationNodeBlendSpace2DEditor::_blend_space_gui_input); - ClassDB::bind_method("_blend_space_draw", &AnimationNodeBlendSpace2DEditor::_blend_space_draw); - ClassDB::bind_method("_config_changed", &AnimationNodeBlendSpace2DEditor::_config_changed); - ClassDB::bind_method("_labels_changed", &AnimationNodeBlendSpace2DEditor::_labels_changed); - ClassDB::bind_method("_update_space", &AnimationNodeBlendSpace2DEditor::_update_space); - ClassDB::bind_method("_snap_toggled", &AnimationNodeBlendSpace2DEditor::_snap_toggled); - ClassDB::bind_method("_tool_switch", &AnimationNodeBlendSpace2DEditor::_tool_switch); - ClassDB::bind_method("_erase_selected", &AnimationNodeBlendSpace2DEditor::_erase_selected); - ClassDB::bind_method("_update_tool_erase", &AnimationNodeBlendSpace2DEditor::_update_tool_erase); - ClassDB::bind_method("_edit_point_pos", &AnimationNodeBlendSpace2DEditor::_edit_point_pos); - - ClassDB::bind_method("_add_menu_type", &AnimationNodeBlendSpace2DEditor::_add_menu_type); - ClassDB::bind_method("_add_animation_type", &AnimationNodeBlendSpace2DEditor::_add_animation_type); - - ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace2DEditor::_update_edited_point_pos); - - ClassDB::bind_method("_open_editor", &AnimationNodeBlendSpace2DEditor::_open_editor); - ClassDB::bind_method("_goto_parent", &AnimationNodeBlendSpace2DEditor::_goto_parent); - - ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpace2DEditor::_removed_from_graph); - - ClassDB::bind_method("_auto_triangles_toggled", &AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled); -} - -AnimationNodeBlendSpace2DEditor *AnimationNodeBlendSpace2DEditor::singleton = NULL; - -AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { - - singleton = this; - updating = false; - - HBoxContainer *top_hb = memnew(HBoxContainer); - add_child(top_hb); - - Ref bg; - bg.instance(); - - goto_parent_hb = memnew(HBoxContainer); - top_hb->add_child(goto_parent_hb); - goto_parent = memnew(ToolButton); - goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED); - goto_parent_hb->add_child(goto_parent); - goto_parent_hb->add_child(memnew(VSeparator)); - goto_parent_hb->hide(); - - tool_blend = memnew(ToolButton); - tool_blend->set_toggle_mode(true); - tool_blend->set_button_group(bg); - top_hb->add_child(tool_blend); - tool_blend->set_pressed(true); - tool_blend->set_tooltip(TTR("Set the blending position within the space")); - tool_blend->connect("pressed", this, "_tool_switch", varray(3)); - - tool_select = memnew(ToolButton); - tool_select->set_toggle_mode(true); - tool_select->set_button_group(bg); - top_hb->add_child(tool_select); - tool_select->set_tooltip(TTR("Select and move points, create points with RMB.")); - tool_select->connect("pressed", this, "_tool_switch", varray(0)); - - tool_create = memnew(ToolButton); - tool_create->set_toggle_mode(true); - tool_create->set_button_group(bg); - top_hb->add_child(tool_create); - tool_create->set_tooltip(TTR("Create points.")); - tool_create->connect("pressed", this, "_tool_switch", varray(1)); - - tool_triangle = memnew(ToolButton); - tool_triangle->set_toggle_mode(true); - tool_triangle->set_button_group(bg); - top_hb->add_child(tool_triangle); - tool_triangle->set_tooltip(TTR("Create triangles by connecting points.")); - tool_triangle->connect("pressed", this, "_tool_switch", varray(2)); - - tool_erase_sep = memnew(VSeparator); - top_hb->add_child(tool_erase_sep); - tool_erase = memnew(ToolButton); - top_hb->add_child(tool_erase); - tool_erase->set_tooltip(TTR("Erase points and triangles.")); - tool_erase->connect("pressed", this, "_erase_selected"); - tool_erase->set_disabled(true); - - top_hb->add_child(memnew(VSeparator)); - - auto_triangles = memnew(ToolButton); - top_hb->add_child(auto_triangles); - auto_triangles->connect("pressed", this, "_auto_triangles_toggled"); - auto_triangles->set_toggle_mode(true); - auto_triangles->set_tooltip(TTR("Generate blend triangles automatically (instead of manually)")); - - top_hb->add_child(memnew(VSeparator)); - - snap = memnew(ToolButton); - snap->set_toggle_mode(true); - top_hb->add_child(snap); - //snap->set_text(TTR("Snap")); - snap->set_pressed(true); - snap->connect("pressed", this, "_snap_toggled"); - - snap_x = memnew(SpinBox); - top_hb->add_child(snap_x); - snap_x->set_prefix("x:"); - snap_x->set_min(0.01); - snap_x->set_step(0.01); - snap_x->set_max(1000); - - snap_y = memnew(SpinBox); - top_hb->add_child(snap_y); - snap_y->set_prefix("y:"); - snap_y->set_min(0.01); - snap_y->set_step(0.01); - snap_y->set_max(1000); - - edit_hb = memnew(HBoxContainer); - top_hb->add_child(edit_hb); - edit_hb->add_child(memnew(VSeparator)); - edit_hb->add_child(memnew(Label(TTR("Point")))); - edit_x = memnew(SpinBox); - edit_hb->add_child(edit_x); - edit_x->set_min(-1000); - edit_x->set_step(0.01); - edit_x->set_max(1000); - edit_x->connect("value_changed", this, "_edit_point_pos"); - edit_y = memnew(SpinBox); - edit_hb->add_child(edit_y); - edit_y->set_min(-1000); - edit_y->set_step(0.01); - edit_y->set_max(1000); - edit_y->connect("value_changed", this, "_edit_point_pos"); - open_editor = memnew(Button); - edit_hb->add_child(open_editor); - open_editor->set_text(TTR("Open Editor")); - open_editor->connect("pressed", this, "_open_editor", varray(), CONNECT_DEFERRED); - edit_hb->hide(); - open_editor->hide(); - - HBoxContainer *main_hb = memnew(HBoxContainer); - add_child(main_hb); - main_hb->set_v_size_flags(SIZE_EXPAND_FILL); - - GridContainer *main_grid = memnew(GridContainer); - main_grid->set_columns(2); - main_hb->add_child(main_grid); - main_grid->set_h_size_flags(SIZE_EXPAND_FILL); - { - VBoxContainer *left_vbox = memnew(VBoxContainer); - main_grid->add_child(left_vbox); - left_vbox->set_v_size_flags(SIZE_EXPAND_FILL); - max_y_value = memnew(SpinBox); - left_vbox->add_child(max_y_value); - left_vbox->add_spacer(); - label_y = memnew(LineEdit); - left_vbox->add_child(label_y); - label_y->set_expand_to_text_length(true); - left_vbox->add_spacer(); - min_y_value = memnew(SpinBox); - left_vbox->add_child(min_y_value); - - max_y_value->set_max(10000); - max_y_value->set_min(0.01); - max_y_value->set_step(0.01); - - min_y_value->set_min(-10000); - min_y_value->set_max(0); - min_y_value->set_step(0.01); - } - - panel = memnew(PanelContainer); - panel->set_clip_contents(true); - main_grid->add_child(panel); - panel->set_h_size_flags(SIZE_EXPAND_FILL); - - blend_space_draw = memnew(Control); - blend_space_draw->connect("gui_input", this, "_blend_space_gui_input"); - blend_space_draw->connect("draw", this, "_blend_space_draw"); - blend_space_draw->set_focus_mode(FOCUS_ALL); - - panel->add_child(blend_space_draw); - main_grid->add_child(memnew(Control)); //empty bottom left - - { - HBoxContainer *bottom_vbox = memnew(HBoxContainer); - main_grid->add_child(bottom_vbox); - bottom_vbox->set_h_size_flags(SIZE_EXPAND_FILL); - min_x_value = memnew(SpinBox); - bottom_vbox->add_child(min_x_value); - bottom_vbox->add_spacer(); - label_x = memnew(LineEdit); - bottom_vbox->add_child(label_x); - label_x->set_expand_to_text_length(true); - bottom_vbox->add_spacer(); - max_x_value = memnew(SpinBox); - bottom_vbox->add_child(max_x_value); - - max_x_value->set_max(10000); - max_x_value->set_min(0.01); - max_x_value->set_step(0.01); - - min_x_value->set_min(-10000); - min_x_value->set_max(0); - min_x_value->set_step(0.01); - } - - snap_x->connect("value_changed", this, "_config_changed"); - snap_y->connect("value_changed", this, "_config_changed"); - max_x_value->connect("value_changed", this, "_config_changed"); - min_x_value->connect("value_changed", this, "_config_changed"); - max_y_value->connect("value_changed", this, "_config_changed"); - min_y_value->connect("value_changed", this, "_config_changed"); - label_x->connect("text_changed", this, "_labels_changed"); - label_y->connect("text_changed", this, "_labels_changed"); - - error_panel = memnew(PanelContainer); - add_child(error_panel); - error_label = memnew(Label); - error_panel->add_child(error_label); - error_label->set_text("eh"); - - undo_redo = EditorNode::get_singleton()->get_undo_redo(); - - set_custom_minimum_size(Size2(0, 300 * EDSCALE)); - - menu = memnew(PopupMenu); - add_child(menu); - menu->connect("index_pressed", this, "_add_menu_type"); - - animations_menu = memnew(PopupMenu); - menu->add_child(animations_menu); - animations_menu->set_name("animations"); - animations_menu->connect("index_pressed", this, "_add_animation_type"); - - selected_point = -1; - selected_triangle = -1; - - dragging_selected = false; - dragging_selected_attempt = false; -} - -void AnimationNodeBlendSpace2DEditorPlugin::edit(Object *p_object) { - - anim_tree_editor->edit(Object::cast_to(p_object)); -} - -bool AnimationNodeBlendSpace2DEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("AnimationNodeBlendSpace2D"); -} - -void AnimationNodeBlendSpace2DEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - //editor->hide_animation_player_editors(); - //editor->animation_panel_make_visible(true); - button->show(); - editor->make_bottom_panel_item_visible(anim_tree_editor); - anim_tree_editor->set_process(true); - } else { - - if (anim_tree_editor->is_visible_in_tree()) - editor->hide_bottom_panel(); - button->hide(); - anim_tree_editor->set_process(false); - } -} - -AnimationNodeBlendSpace2DEditorPlugin::AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node) { - - editor = p_node; - anim_tree_editor = memnew(AnimationNodeBlendSpace2DEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); - - button = editor->add_bottom_panel_item(TTR("BlendSpace2D"), anim_tree_editor); - button->hide(); -} - -AnimationNodeBlendSpace2DEditorPlugin::~AnimationNodeBlendSpace2DEditorPlugin() { -} diff --git a/editor/plugins/animation_blend_space2d_editor.h b/editor/plugins/animation_blend_space2d_editor.h deleted file mode 100644 index 47b1ce13ad..0000000000 --- a/editor/plugins/animation_blend_space2d_editor.h +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef ANIMATION_BLEND_SPACE2D_EDITOR_H -#define ANIMATION_BLEND_SPACE2D_EDITOR_H - -#include "editor/editor_node.h" -#include "editor/editor_plugin.h" -#include "editor/property_editor.h" -#include "scene/animation/animation_blend_space2d.h" -#include "scene/gui/button.h" -#include "scene/gui/graph_edit.h" -#include "scene/gui/popup.h" -#include "scene/gui/tree.h" -/** - @author Juan Linietsky -*/ - -class AnimationNodeBlendSpace2DEditor : public VBoxContainer { - - GDCLASS(AnimationNodeBlendSpace2DEditor, VBoxContainer); - - Ref blend_space; - - HBoxContainer *goto_parent_hb; - ToolButton *goto_parent; - - PanelContainer *panel; - ToolButton *tool_blend; - ToolButton *tool_select; - ToolButton *tool_create; - ToolButton *tool_triangle; - VSeparator *tool_erase_sep; - ToolButton *tool_erase; - ToolButton *snap; - SpinBox *snap_x; - SpinBox *snap_y; - - ToolButton *auto_triangles; - - LineEdit *label_x; - LineEdit *label_y; - SpinBox *max_x_value; - SpinBox *min_x_value; - SpinBox *max_y_value; - SpinBox *min_y_value; - - HBoxContainer *edit_hb; - SpinBox *edit_x; - SpinBox *edit_y; - Button *open_editor; - - int selected_point; - int selected_triangle; - - Control *blend_space_draw; - - PanelContainer *error_panel; - Label *error_label; - - bool updating; - - UndoRedo *undo_redo; - - static AnimationNodeBlendSpace2DEditor *singleton; - - void _blend_space_gui_input(const Ref &p_event); - void _blend_space_draw(); - - void _update_space(); - - void _config_changed(double); - void _labels_changed(String); - void _snap_toggled(); - - PopupMenu *menu; - PopupMenu *animations_menu; - Vector animations_to_add; - Vector2 add_point_pos; - Vector points; - - bool dragging_selected_attempt; - bool dragging_selected; - Vector2 drag_from; - Vector2 drag_ofs; - - Vector making_triangle; - - void _add_menu_type(int p_index); - void _add_animation_type(int p_index); - - void _tool_switch(int p_tool); - void _update_edited_point_pos(); - void _update_tool_erase(); - void _erase_selected(); - void _edit_point_pos(double); - void _open_editor(); - - void _goto_parent(); - - void _removed_from_graph(); - - void _auto_triangles_toggled(); - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - static AnimationNodeBlendSpace2DEditor *get_singleton() { return singleton; } - void edit(AnimationNodeBlendSpace2D *p_blend_space); - AnimationNodeBlendSpace2DEditor(); -}; - -class AnimationNodeBlendSpace2DEditorPlugin : public EditorPlugin { - - GDCLASS(AnimationNodeBlendSpace2DEditorPlugin, EditorPlugin); - - AnimationNodeBlendSpace2DEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "BlendSpace2D"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node); - ~AnimationNodeBlendSpace2DEditorPlugin(); -}; -#endif // ANIMATION_BLEND_SPACE2D_EDITOR_H diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp new file mode 100644 index 0000000000..15d259e439 --- /dev/null +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -0,0 +1,741 @@ +#include "animation_blend_space_1d_editor.h" + +#include "os/keyboard.h" +#include "scene/animation/animation_blend_tree.h" + +void AnimationNodeBlendSpace1DEditorPlugin::edit(Object *p_object) { + anim_tree_editor->edit(Object::cast_to(p_object)); +} + +bool AnimationNodeBlendSpace1DEditorPlugin::handles(Object *p_object) const { + return p_object->is_class("AnimationNodeBlendSpace1D"); +} + +void AnimationNodeBlendSpace1DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + button->show(); + editor->make_bottom_panel_item_visible(anim_tree_editor); + anim_tree_editor->set_process(true); + } else { + if (anim_tree_editor->is_visible_in_tree()) { + editor->hide_bottom_panel(); + } + + button->hide(); + anim_tree_editor->set_process(false); + } +} + +AnimationNodeBlendSpace1DEditorPlugin::AnimationNodeBlendSpace1DEditorPlugin(EditorNode *p_node) { + editor = p_node; + anim_tree_editor = memnew(AnimationNodeBlendSpace1DEditor); + anim_tree_editor->set_custom_minimum_size(Size2(0, 150 * EDSCALE)); + + button = editor->add_bottom_panel_item(TTR("BlendSpace1D"), anim_tree_editor); + button->hide(); +} + +AnimationNodeBlendSpace1DEditorPlugin::~AnimationNodeBlendSpace1DEditorPlugin() { +} + +void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref &p_event) { + Ref k = p_event; + + if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && !k->is_echo()) { + if (selected_point != -1) { + _erase_selected(); + accept_event(); + } + } + + Ref mb = p_event; + + 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()))) { + menu->clear(); + animations_menu->clear(); + animations_to_add.clear(); + + List classes; + ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); + classes.sort_custom(); + + menu->add_submenu_item(TTR("Add Animation"), "animations"); + + AnimationGraphPlayer *gp = blend_space->get_graph_player(); + ERR_FAIL_COND(!gp); + + if (gp->has_node(gp->get_animation_player())) { + AnimationPlayer *ap = Object::cast_to(gp->get_node(gp->get_animation_player())); + + if (ap) { + List names; + ap->get_animation_list(&names); + + for (List::Element *E = names.front(); E; E = E->next()) { + animations_menu->add_icon_item(get_icon("Animation", "Editoricons"), E->get()); + animations_to_add.push_back(E->get()); + } + } + } + + for (List::Element *E = classes.front(); E; E = E->next()) { + String name = String(E->get()).replace_first("AnimationNode", ""); + if (name == "Animation") + continue; + + int idx = menu->get_item_count(); + menu->add_item(vformat("Add %s", name)); + menu->set_item_metadata(idx, E->get()); + } + + menu->set_global_position(blend_space_draw->get_global_transform().xform(mb->get_position())); + menu->popup(); + + add_point_pos = (mb->get_position() / blend_space_draw->get_size()).x; + add_point_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); + add_point_pos += blend_space->get_min_space(); + + if (snap->is_pressed()) { + add_point_pos = Math::stepify(add_point_pos, blend_space->get_snap()); + } + } + + if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + blend_space_draw->update(); // why not + + // try to see if a point can be selected + selected_point = -1; + _update_tool_erase(); + + for (int i = 0; i < points.size(); i++) { + + if (Math::abs(float(points[i] - mb->get_position().x)) < 10 * EDSCALE) { + selected_point = i; + + Ref node = blend_space->get_blend_point_node(i); + EditorNode::get_singleton()->push_item(node.ptr(), "", true); + dragging_selected_attempt = true; + drag_from = mb->get_position(); + _update_tool_erase(); + _update_edited_point_pos(); + return; + } + } + } + + if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == BUTTON_LEFT) { + if (dragging_selected) { + // move + float point = blend_space->get_blend_point_position(selected_point); + point += drag_ofs.x; + + if (snap->is_pressed()) { + point = Math::stepify(point, blend_space->get_snap()); + } + + updating = true; + undo_redo->create_action("Move Node Point"); + undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point); + undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->add_do_method(this, "_update_edited_point_pos"); + undo_redo->add_undo_method(this, "_update_edited_point_pos"); + undo_redo->commit_action(); + updating = false; + _update_edited_point_pos(); + } + + dragging_selected_attempt = false; + dragging_selected = false; + blend_space_draw->update(); + } + + // *set* the blend + if (mb.is_valid() && !mb->is_pressed() && tool_blend->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + float blend_pos = mb->get_position().x / blend_space_draw->get_size().x; + blend_pos *= blend_space->get_max_space() - blend_space->get_min_space(); + blend_pos += blend_space->get_min_space(); + + blend_space->set_blend_pos(blend_pos); + blend_space_draw->update(); + } + + Ref mm = p_event; + + if (mm.is_valid() && !blend_space_draw->has_focus()) { + blend_space_draw->grab_focus(); + blend_space_draw->update(); + } + + if (mm.is_valid() && dragging_selected_attempt) { + dragging_selected = true; + 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)); + blend_space_draw->update(); + _update_edited_point_pos(); + } + + if (mm.is_valid() && tool_blend->is_pressed() && mm->get_button_mask() & BUTTON_MASK_LEFT) { + float blend_pos = mm->get_position().x / blend_space_draw->get_size().x; + blend_pos *= blend_space->get_max_space() - blend_space->get_min_space(); + blend_pos += blend_space->get_min_space(); + + blend_space->set_blend_pos(blend_pos); + blend_space_draw->update(); + } +} + +void AnimationNodeBlendSpace1DEditor::_blend_space_draw() { + + Color linecolor = get_color("font_color", "Label"); + Color linecolor_soft = linecolor; + linecolor_soft.a *= 0.5; + + Ref font = get_font("font", "Label"); + Ref icon = get_icon("KeyValue", "EditorIcons"); + Ref icon_selected = get_icon("KeySelected", "EditorIcons"); + + Size2 s = blend_space_draw->get_size(); + + if (blend_space_draw->has_focus()) { + Color color = get_color("accent_color", "Editor"); + blend_space_draw->draw_rect(Rect2(Point2(), s), color, false); + } + + blend_space_draw->draw_line(Point2(1, s.height - 1), Point2(s.width - 1, s.height - 1), linecolor); + + if (blend_space->get_min_space() < 0) { + float point = 0.0; + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); + point *= s.width; + + float x = point; + + blend_space_draw->draw_line(Point2(x, s.height - 1), Point2(x, s.height - 5 * EDSCALE), linecolor); + blend_space_draw->draw_string(font, Point2(x + 2 * EDSCALE, s.height - 2 * EDSCALE - font->get_height() + font->get_ascent()), "0", linecolor); + blend_space_draw->draw_line(Point2(x, s.height - 5 * EDSCALE), Point2(x, 0), linecolor_soft); + } + + if (snap->is_pressed()) { + + linecolor_soft.a = linecolor.a * 0.1; + + if (blend_space->get_snap() > 0) { + int prev_idx = -1; + + for (int i = 0; i < s.x; i++) { + float v = blend_space->get_min_space() + i * (blend_space->get_max_space() - blend_space->get_min_space()) / s.x; + int idx = int(v / blend_space->get_snap()); + + if (i > 0 && prev_idx != idx) { + blend_space_draw->draw_line(Point2(i, 0), Point2(i, s.height), linecolor_soft); + } + + prev_idx = idx; + } + } + } + + points.clear(); + + for (int i = 0; i < blend_space->get_blend_point_count(); i++) { + float point = blend_space->get_blend_point_position(i); + + if (dragging_selected && selected_point == i) { + point += drag_ofs.x; + if (snap->is_pressed()) { + point = Math::stepify(point, blend_space->get_snap()); + } + } + + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); + point *= s.width; + + points.push_back(point); + + Vector2 gui_point = Vector2(point, s.height / 2.0); + + gui_point -= (icon->get_size() / 2.0); + + gui_point = gui_point.floor(); + + if (i == selected_point) { + blend_space_draw->draw_texture(icon_selected, gui_point); + } else { + blend_space_draw->draw_texture(icon, gui_point); + } + } + + // blend position + { + Color color; + if (tool_blend->is_pressed()) { + color = get_color("accent_color", "Editor"); + } else { + color = linecolor; + color.a *= 0.5; + } + + float point = blend_space->get_blend_pos(); + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); + point *= s.width; + + Vector2 gui_point = Vector2(point, s.height / 2.0); + + float mind = 5 * EDSCALE; + float maxd = 15 * EDSCALE; + blend_space_draw->draw_line(gui_point + Vector2(mind, 0), gui_point + Vector2(maxd, 0), color, 2); + blend_space_draw->draw_line(gui_point + Vector2(-mind, 0), gui_point + Vector2(-maxd, 0), color, 2); + blend_space_draw->draw_line(gui_point + Vector2(0, mind), gui_point + Vector2(0, maxd), color, 2); + blend_space_draw->draw_line(gui_point + Vector2(0, -mind), gui_point + Vector2(0, -maxd), color, 2); + } +} + +void AnimationNodeBlendSpace1DEditor::_update_space() { + + if (updating) + return; + + updating = true; + + if (blend_space->get_parent().is_valid()) { + goto_parent_hb->show(); + } else { + goto_parent_hb->hide(); + } + + max_value->set_value(blend_space->get_max_space()); + min_value->set_value(blend_space->get_min_space()); + + label_value->set_text(blend_space->get_value_label()); + + snap_value->set_value(blend_space->get_snap()); + + blend_space_draw->update(); + + updating = false; +} + +void AnimationNodeBlendSpace1DEditor::_config_changed(double) { + if (updating) + return; + + updating = true; + undo_redo->create_action("Change BlendSpace1D Limits"); + undo_redo->add_do_method(blend_space.ptr(), "set_max_space", max_value->get_value()); + undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space()); + undo_redo->add_do_method(blend_space.ptr(), "set_min_space", min_value->get_value()); + undo_redo->add_undo_method(blend_space.ptr(), "set_min_space", blend_space->get_min_space()); + undo_redo->add_do_method(blend_space.ptr(), "set_snap", snap_value->get_value()); + undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap()); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->commit_action(); + updating = false; + + blend_space_draw->update(); +} + +void AnimationNodeBlendSpace1DEditor::_labels_changed(String) { + if (updating) + return; + + updating = true; + undo_redo->create_action("Change BlendSpace1D Labels", UndoRedo::MERGE_ENDS); + undo_redo->add_do_method(blend_space.ptr(), "set_value_label", label_value->get_text()); + undo_redo->add_undo_method(blend_space.ptr(), "set_value_label", blend_space->get_value_label()); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->commit_action(); + updating = false; +} + +void AnimationNodeBlendSpace1DEditor::_snap_toggled() { + blend_space_draw->update(); +} + +void AnimationNodeBlendSpace1DEditor::_add_menu_type(int p_index) { + String type = menu->get_item_metadata(p_index); + + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to(obj); + ERR_FAIL_COND(!an); + + Ref node(an); + + updating = true; + undo_redo->create_action("Add Node Point"); + undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos); + undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->commit_action(); + updating = false; + + blend_space_draw->update(); +} + +void AnimationNodeBlendSpace1DEditor::_add_animation_type(int p_index) { + Ref anim; + anim.instance(); + + anim->set_animation(animations_to_add[p_index]); + + updating = true; + undo_redo->create_action("Add Animation Point"); + undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos); + undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->commit_action(); + updating = false; + + blend_space_draw->update(); +} + +void AnimationNodeBlendSpace1DEditor::_tool_switch(int p_tool) { + + if (p_tool == 0) { + tool_erase->show(); + tool_erase_sep->show(); + } else { + tool_erase->hide(); + tool_erase_sep->hide(); + } + + _update_tool_erase(); + blend_space_draw->update(); +} + +void AnimationNodeBlendSpace1DEditor::_update_edited_point_pos() { + if (updating) + return; + + if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { + float pos = blend_space->get_blend_point_position(selected_point); + + if (dragging_selected) { + pos += drag_ofs.x; + + if (snap->is_pressed()) { + pos = Math::stepify(pos, blend_space->get_snap()); + } + } + + updating = true; + edit_value->set_value(pos); + updating = false; + } +} + +void AnimationNodeBlendSpace1DEditor::_update_tool_erase() { + + bool point_valid = selected_point >= 0 && selected_point < blend_space->get_blend_point_count(); + tool_erase->set_disabled(!point_valid); + + if (point_valid) { + Ref an = blend_space->get_blend_point_node(selected_point); + + if (EditorNode::get_singleton()->item_has_editor(an.ptr())) { + open_editor->show(); + } else { + open_editor->hide(); + } + + edit_hb->show(); + } else { + edit_hb->hide(); + } +} + +void AnimationNodeBlendSpace1DEditor::_erase_selected() { + if (selected_point != -1) { + updating = true; + + undo_redo->create_action("Remove BlendSpace1D Point"); + undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point); + 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); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->commit_action(); + + updating = false; + + blend_space_draw->update(); + } +} + +void AnimationNodeBlendSpace1DEditor::_edit_point_pos(double) { + if (updating) + return; + + updating = true; + undo_redo->create_action("Move BlendSpace1D Node Point"); + undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, edit_value->get_value()); + undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->add_do_method(this, "_update_edited_point_pos"); + undo_redo->add_undo_method(this, "_update_edited_point_pos"); + undo_redo->commit_action(); + updating = false; + + blend_space_draw->update(); +} + +void AnimationNodeBlendSpace1DEditor::_open_editor() { + + if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { + Ref an = blend_space->get_blend_point_node(selected_point); + ERR_FAIL_COND(an.is_null()); + EditorNode::get_singleton()->edit_item(an.ptr()); + } +} + +void AnimationNodeBlendSpace1DEditor::_goto_parent() { + EditorNode::get_singleton()->edit_item(blend_space->get_parent().ptr()); +} + +void AnimationNodeBlendSpace1DEditor::_removed_from_graph() { + EditorNode::get_singleton()->edit_item(NULL); +} + +void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); + error_label->add_color_override("font_color", get_color("error_color", "Editor")); + panel->add_style_override("panel", get_stylebox("bg", "Tree")); + tool_blend->set_icon(get_icon("EditPivot", "EditorIcons")); + tool_select->set_icon(get_icon("ToolSelect", "EditorIcons")); + tool_create->set_icon(get_icon("EditKey", "EditorIcons")); + tool_erase->set_icon(get_icon("Remove", "EditorIcons")); + snap->set_icon(get_icon("SnapGrid", "EditorIcons")); + open_editor->set_icon(get_icon("Edit", "EditorIcons")); + goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); + } + + if (p_what == NOTIFICATION_PROCESS) { + String error; + + if (!blend_space->get_graph_player()) { + error = TTR("BlendSpace1D does not belong to an AnimationGraphPlayer node."); + } else if (!blend_space->get_graph_player()->is_active()) { + error = TTR("AnimationGraphPlayer is inactive.\nActivate to enable playback, check node warnings if activation fails."); + } else if (blend_space->get_graph_player()->is_state_invalid()) { + error = blend_space->get_graph_player()->get_invalid_state_reason(); + } + + if (error != error_label->get_text()) { + error_label->set_text(error); + if (error != String()) { + error_panel->show(); + } else { + error_panel->hide(); + } + } + } +} + +void AnimationNodeBlendSpace1DEditor::_bind_methods() { + ClassDB::bind_method("_blend_space_gui_input", &AnimationNodeBlendSpace1DEditor::_blend_space_gui_input); + ClassDB::bind_method("_blend_space_draw", &AnimationNodeBlendSpace1DEditor::_blend_space_draw); + ClassDB::bind_method("_config_changed", &AnimationNodeBlendSpace1DEditor::_config_changed); + ClassDB::bind_method("_labels_changed", &AnimationNodeBlendSpace1DEditor::_labels_changed); + ClassDB::bind_method("_update_space", &AnimationNodeBlendSpace1DEditor::_update_space); + ClassDB::bind_method("_snap_toggled", &AnimationNodeBlendSpace1DEditor::_snap_toggled); + ClassDB::bind_method("_tool_switch", &AnimationNodeBlendSpace1DEditor::_tool_switch); + ClassDB::bind_method("_erase_selected", &AnimationNodeBlendSpace1DEditor::_erase_selected); + ClassDB::bind_method("_update_tool_erase", &AnimationNodeBlendSpace1DEditor::_update_tool_erase); + ClassDB::bind_method("_edit_point_pos", &AnimationNodeBlendSpace1DEditor::_edit_point_pos); + + ClassDB::bind_method("_add_menu_type", &AnimationNodeBlendSpace1DEditor::_add_menu_type); + ClassDB::bind_method("_add_animation_type", &AnimationNodeBlendSpace1DEditor::_add_animation_type); + + ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace1DEditor::_update_edited_point_pos); + + ClassDB::bind_method("_open_editor", &AnimationNodeBlendSpace1DEditor::_open_editor); + ClassDB::bind_method("_goto_parent", &AnimationNodeBlendSpace1DEditor::_goto_parent); + + ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpace1DEditor::_removed_from_graph); +} + +void AnimationNodeBlendSpace1DEditor::edit(AnimationNodeBlendSpace1D *p_blend_space) { + + if (blend_space.is_valid()) { + blend_space->disconnect("removed_from_graph", this, "_removed_from_graph"); + } + + if (p_blend_space) { + blend_space = Ref(p_blend_space); + } else { + blend_space.unref(); + } + + if (blend_space.is_null()) { + hide(); + } else { + blend_space->connect("removed_from_graph", this, "_removed_from_graph"); + + _update_space(); + } +} + +AnimationNodeBlendSpace1DEditor *AnimationNodeBlendSpace1DEditor::singleton = NULL; + +AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { + singleton = this; + updating = false; + + HBoxContainer *top_hb = memnew(HBoxContainer); + add_child(top_hb); + + Ref bg; + bg.instance(); + + goto_parent_hb = memnew(HBoxContainer); + top_hb->add_child(goto_parent_hb); + + goto_parent = memnew(ToolButton); + goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED); + goto_parent_hb->add_child(goto_parent); + goto_parent_hb->add_child(memnew(VSeparator)); + goto_parent_hb->hide(); + + tool_blend = memnew(ToolButton); + tool_blend->set_toggle_mode(true); + tool_blend->set_button_group(bg); + top_hb->add_child(tool_blend); + tool_blend->set_pressed(true); + tool_blend->set_tooltip(TTR("Set the blending position within the space")); + tool_blend->connect("pressed", this, "_tool_switch", varray(3)); + + tool_select = memnew(ToolButton); + tool_select->set_toggle_mode(true); + tool_select->set_button_group(bg); + top_hb->add_child(tool_select); + tool_select->set_tooltip(TTR("Select and move points, create points with RMB.")); + tool_select->connect("pressed", this, "_tool_switch", varray(0)); + + tool_create = memnew(ToolButton); + tool_create->set_toggle_mode(true); + tool_create->set_button_group(bg); + top_hb->add_child(tool_create); + tool_create->set_tooltip(TTR("Create points.")); + tool_create->connect("pressed", this, "_tool_switch", varray(1)); + + tool_erase_sep = memnew(VSeparator); + top_hb->add_child(tool_erase_sep); + tool_erase = memnew(ToolButton); + top_hb->add_child(tool_erase); + tool_erase->set_tooltip(TTR("Erase points.")); + tool_erase->connect("pressed", this, "_erase_selected"); + + top_hb->add_child(memnew(VSeparator)); + + snap = memnew(ToolButton); + snap->set_toggle_mode(true); + top_hb->add_child(snap); + snap->set_pressed(true); + snap->connect("pressed", this, "_snap_toggled"); + + snap_value = memnew(SpinBox); + top_hb->add_child(snap_value); + snap_value->set_min(0.01); + snap_value->set_step(0.01); + snap_value->set_max(1000); + + edit_hb = memnew(HBoxContainer); + top_hb->add_child(edit_hb); + edit_hb->add_child(memnew(VSeparator)); + edit_hb->add_child(memnew(Label(TTR("Point")))); + + edit_value = memnew(SpinBox); + edit_hb->add_child(edit_value); + edit_value->set_min(-1000); + edit_value->set_max(1000); + edit_value->set_step(0.01); + edit_value->connect("value_changed", this, "_edit_point_pos"); + + open_editor = memnew(Button); + edit_hb->add_child(open_editor); + open_editor->set_text(TTR("Open Editor")); + open_editor->connect("pressed", this, "_open_editor", varray(), CONNECT_DEFERRED); + + edit_hb->hide(); + open_editor->hide(); + + VBoxContainer *main_vb = memnew(VBoxContainer); + add_child(main_vb); + main_vb->set_v_size_flags(SIZE_EXPAND_FILL); + + panel = memnew(PanelContainer); + panel->set_clip_contents(true); + main_vb->add_child(panel); + panel->set_h_size_flags(SIZE_EXPAND_FILL); + panel->set_v_size_flags(SIZE_EXPAND_FILL); + + blend_space_draw = memnew(Control); + blend_space_draw->connect("gui_input", this, "_blend_space_gui_input"); + blend_space_draw->connect("draw", this, "_blend_space_draw"); + blend_space_draw->set_focus_mode(FOCUS_ALL); + + panel->add_child(blend_space_draw); + + { + HBoxContainer *bottom_hb = memnew(HBoxContainer); + main_vb->add_child(bottom_hb); + bottom_hb->set_h_size_flags(SIZE_EXPAND_FILL); + + min_value = memnew(SpinBox); + min_value->set_max(0); + min_value->set_min(-10000); + min_value->set_step(0.01); + + max_value = memnew(SpinBox); + max_value->set_max(10000); + max_value->set_min(0.01); + max_value->set_step(0.01); + + label_value = memnew(LineEdit); + label_value->set_expand_to_text_length(true); + + // now add + + bottom_hb->add_child(min_value); + bottom_hb->add_spacer(); + bottom_hb->add_child(label_value); + bottom_hb->add_spacer(); + bottom_hb->add_child(max_value); + } + + snap_value->connect("value_changed", this, "_config_changed"); + min_value->connect("value_changed", this, "_config_changed"); + max_value->connect("value_changed", this, "_config_changed"); + label_value->connect("text_changed", this, "_labels_changed"); + + error_panel = memnew(PanelContainer); + add_child(error_panel); + + error_label = memnew(Label); + error_panel->add_child(error_label); + error_label->set_text("hmmm"); + + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + menu = memnew(PopupMenu); + add_child(menu); + menu->connect("index_pressed", this, "_add_menu_type"); + + animations_menu = memnew(PopupMenu); + menu->add_child(animations_menu); + animations_menu->set_name("animations"); + animations_menu->connect("index_pressed", this, "_add_animation_type"); + + selected_point = -1; + dragging_selected = false; + dragging_selected_attempt = false; + + set_custom_minimum_size(Size2(0, 150 * EDSCALE)); +} diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h new file mode 100644 index 0000000000..52139626e6 --- /dev/null +++ b/editor/plugins/animation_blend_space_1d_editor.h @@ -0,0 +1,117 @@ +#ifndef ANIMATION_BLEND_SPACE_1D_EDITOR_H +#define ANIMATION_BLEND_SPACE_1D_EDITOR_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "editor/property_editor.h" +#include "scene/animation/animation_blend_space_1d.h" +#include "scene/gui/button.h" +#include "scene/gui/graph_edit.h" +#include "scene/gui/popup.h" +#include "scene/gui/tree.h" + +class AnimationNodeBlendSpace1DEditor : public VBoxContainer { + + GDCLASS(AnimationNodeBlendSpace1DEditor, VBoxContainer) + + Ref blend_space; + + HBoxContainer *goto_parent_hb; + ToolButton *goto_parent; + + PanelContainer *panel; + ToolButton *tool_blend; + ToolButton *tool_select; + ToolButton *tool_create; + VSeparator *tool_erase_sep; + ToolButton *tool_erase; + ToolButton *snap; + SpinBox *snap_value; + + LineEdit *label_value; + SpinBox *max_value; + SpinBox *min_value; + + HBoxContainer *edit_hb; + SpinBox *edit_value; + Button *open_editor; + + int selected_point; + + Control *blend_space_draw; + + PanelContainer *error_panel; + Label *error_label; + + bool updating; + + UndoRedo *undo_redo; + + static AnimationNodeBlendSpace1DEditor *singleton; + + void _blend_space_gui_input(const Ref &p_event); + void _blend_space_draw(); + + void _update_space(); + + void _config_changed(double); + void _labels_changed(String); + void _snap_toggled(); + + PopupMenu *menu; + PopupMenu *animations_menu; + Vector animations_to_add; + float add_point_pos; + Vector points; + + bool dragging_selected_attempt; + bool dragging_selected; + Vector2 drag_from; + Vector2 drag_ofs; + + void _add_menu_type(int p_index); + void _add_animation_type(int p_index); + + void _tool_switch(int p_tool); + void _update_edited_point_pos(); + void _update_tool_erase(); + void _erase_selected(); + void _edit_point_pos(double); + void _open_editor(); + + void _goto_parent(); + + void _removed_from_graph(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + static AnimationNodeBlendSpace1DEditor *get_singleton() { return singleton; } + void edit(AnimationNodeBlendSpace1D *p_blend_space); + AnimationNodeBlendSpace1DEditor(); +}; + +class AnimationNodeBlendSpace1DEditorPlugin : public EditorPlugin { + + GDCLASS(AnimationNodeBlendSpace1DEditorPlugin, EditorPlugin) + + AnimationNodeBlendSpace1DEditor *anim_tree_editor; + EditorNode *editor; + Button *button; + +public: + virtual String get_name() const { return "BlendSpace1D"; } + + bool has_main_screen() const { return false; } + + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + AnimationNodeBlendSpace1DEditorPlugin(EditorNode *p_node); + ~AnimationNodeBlendSpace1DEditorPlugin(); +}; + +#endif // ANIMATION_BLEND_SPACE_1D_EDITOR_H diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp new file mode 100644 index 0000000000..775fdba932 --- /dev/null +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -0,0 +1,1023 @@ +#include "animation_blend_space_2d_editor.h" + +#include "core/io/resource_loader.h" +#include "core/project_settings.h" +#include "math/delaunay.h" +#include "os/input.h" +#include "os/keyboard.h" +#include "scene/animation/animation_blend_tree.h" +#include "scene/animation/animation_player.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/panel.h" +#include "scene/main/viewport.h" + +void AnimationNodeBlendSpace2DEditor::edit(AnimationNodeBlendSpace2D *p_blend_space) { + + if (blend_space.is_valid()) { + blend_space->disconnect("removed_from_graph", this, "_removed_from_graph"); + } + + if (p_blend_space) { + blend_space = Ref(p_blend_space); + } else { + blend_space.unref(); + } + + if (blend_space.is_null()) { + hide(); + } else { + blend_space->connect("removed_from_graph", this, "_removed_from_graph"); + + _update_space(); + } +} + +void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref &p_event) { + + Ref k = p_event; + if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && !k->is_echo()) { + if (selected_point != -1 || selected_triangle != -1) { + _erase_selected(); + accept_event(); + } + } + + Ref mb = p_event; + + 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()))) { + menu->clear(); + animations_menu->clear(); + animations_to_add.clear(); + List classes; + classes.sort_custom(); + + ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); + menu->add_submenu_item(TTR("Add Animation"), "animations"); + + AnimationGraphPlayer *gp = blend_space->get_graph_player(); + ERR_FAIL_COND(!gp); + if (gp && gp->has_node(gp->get_animation_player())) { + AnimationPlayer *ap = Object::cast_to(gp->get_node(gp->get_animation_player())); + if (ap) { + List names; + ap->get_animation_list(&names); + for (List::Element *E = names.front(); E; E = E->next()) { + animations_menu->add_icon_item(get_icon("Animation", "EditorIcons"), E->get()); + animations_to_add.push_back(E->get()); + } + } + } + + for (List::Element *E = classes.front(); E; E = E->next()) { + + String name = String(E->get()).replace_first("AnimationNode", ""); + if (name == "Animation") + continue; // nope + int idx = menu->get_item_count(); + menu->add_item(vformat("Add %s", name)); + menu->set_item_metadata(idx, E->get()); + } + + menu->set_global_position(blend_space_draw->get_global_transform().xform(mb->get_position())); + menu->popup(); + add_point_pos = (mb->get_position() / blend_space_draw->get_size()); + add_point_pos.y = 1.0 - add_point_pos.y; + add_point_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); + add_point_pos += blend_space->get_min_space(); + + if (snap->is_pressed()) { + add_point_pos.x = Math::stepify(add_point_pos.x, blend_space->get_snap().x); + add_point_pos.y = Math::stepify(add_point_pos.y, blend_space->get_snap().y); + } + } + + if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + + blend_space_draw->update(); //update anyway + //try to see if a point can be selected + selected_point = -1; + selected_triangle = -1; + _update_tool_erase(); + + for (int i = 0; i < points.size(); i++) { + + if (points[i].distance_to(mb->get_position()) < 10 * EDSCALE) { + selected_point = i; + Ref node = blend_space->get_blend_point_node(i); + EditorNode::get_singleton()->push_item(node.ptr(), "", true); + dragging_selected_attempt = true; + drag_from = mb->get_position(); + _update_tool_erase(); + _update_edited_point_pos(); + return; + } + } + + //then try to see if a triangle can be selected + if (!blend_space->get_auto_triangles()) { //if autotriangles use, disable this + for (int i = 0; i < blend_space->get_triangle_count(); i++) { + Vector triangle; + + for (int j = 0; j < 3; j++) { + int idx = blend_space->get_triangle_point(i, j); + ERR_FAIL_INDEX(idx, points.size()); + triangle.push_back(points[idx]); + } + + if (Geometry::is_point_in_triangle(mb->get_position(), triangle[0], triangle[1], triangle[2])) { + selected_triangle = i; + _update_tool_erase(); + return; + } + } + } + } + + if (mb.is_valid() && mb->is_pressed() && tool_triangle->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + + blend_space_draw->update(); //update anyway + //try to see if a point can be selected + selected_point = -1; + + for (int i = 0; i < points.size(); i++) { + + if (making_triangle.find(i) != -1) + continue; + + if (points[i].distance_to(mb->get_position()) < 10 * EDSCALE) { + making_triangle.push_back(i); + if (making_triangle.size() == 3) { + //add triangle! + if (blend_space->has_triangle(making_triangle[0], making_triangle[1], making_triangle[2])) { + making_triangle.clear(); + EditorNode::get_singleton()->show_warning(TTR("Triangle already exists")); + return; + } + + updating = true; + undo_redo->create_action("Add Triangle"); + undo_redo->add_do_method(blend_space.ptr(), "add_triangle", making_triangle[0], making_triangle[1], making_triangle[2]); + undo_redo->add_undo_method(blend_space.ptr(), "remove_triangle", blend_space->get_triangle_count()); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->commit_action(); + updating = false; + making_triangle.clear(); + } + return; + } + } + } + + if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == BUTTON_LEFT) { + if (dragging_selected) { + //move + Vector2 point = blend_space->get_blend_point_position(selected_point); + point += drag_ofs; + if (snap->is_pressed()) { + point.x = Math::stepify(point.x, blend_space->get_snap().x); + point.y = Math::stepify(point.y, blend_space->get_snap().y); + } + + updating = true; + undo_redo->create_action("Move Node Point"); + undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point); + undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->add_do_method(this, "_update_edited_point_pos"); + undo_redo->add_undo_method(this, "_update_edited_point_pos"); + undo_redo->commit_action(); + updating = false; + _update_edited_point_pos(); + } + dragging_selected_attempt = false; + dragging_selected = false; + blend_space_draw->update(); + } + + if (mb.is_valid() && mb->is_pressed() && tool_blend->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + + Vector2 blend_pos = (mb->get_position() / blend_space_draw->get_size()); + blend_pos.y = 1.0 - blend_pos.y; + blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); + blend_pos += blend_space->get_min_space(); + + blend_space->set_blend_position(blend_pos); + blend_space_draw->update(); + } + + Ref mm = p_event; + + if (mm.is_valid() && !blend_space_draw->has_focus()) { + blend_space_draw->grab_focus(); + blend_space_draw->update(); + } + + if (mm.is_valid() && dragging_selected_attempt) { + dragging_selected = true; + 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); + blend_space_draw->update(); + _update_edited_point_pos(); + } + + if (mm.is_valid() && tool_triangle->is_pressed() && making_triangle.size()) { + blend_space_draw->update(); + } + + if (mm.is_valid() && !tool_triangle->is_pressed() && making_triangle.size()) { + making_triangle.clear(); + blend_space_draw->update(); + } + + if (mm.is_valid() && tool_blend->is_pressed() && mm->get_button_mask() & BUTTON_MASK_LEFT) { + + Vector2 blend_pos = (mm->get_position() / blend_space_draw->get_size()); + blend_pos.y = 1.0 - blend_pos.y; + blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); + blend_pos += blend_space->get_min_space(); + + blend_space->set_blend_position(blend_pos); + blend_space_draw->update(); + } +} + +void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) { + + String type = menu->get_item_metadata(p_index); + + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to(obj); + ERR_FAIL_COND(!an); + + Ref node(an); + + updating = true; + undo_redo->create_action("Add Node Point"); + undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos); + undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->commit_action(); + updating = false; + + blend_space_draw->update(); +} + +void AnimationNodeBlendSpace2DEditor::_add_animation_type(int p_index) { + + Ref anim; + anim.instance(); + + anim->set_animation(animations_to_add[p_index]); + + updating = true; + undo_redo->create_action("Add Animation Point"); + undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos); + undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->commit_action(); + updating = false; + + blend_space_draw->update(); +} + +void AnimationNodeBlendSpace2DEditor::_update_tool_erase() { + 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())); + if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { + Ref an = blend_space->get_blend_point_node(selected_point); + if (EditorNode::get_singleton()->item_has_editor(an.ptr())) { + open_editor->show(); + } else { + open_editor->hide(); + } + edit_hb->show(); + } else { + edit_hb->hide(); + } +} + +void AnimationNodeBlendSpace2DEditor::_tool_switch(int p_tool) { + making_triangle.clear(); + + if (p_tool == 2) { + Vector points; + for (int i = 0; i < blend_space->get_blend_point_count(); i++) { + points.push_back(blend_space->get_blend_point_position(i)); + } + Vector tr = Delaunay2D::triangulate(points); + print_line("triangleS: " + itos(tr.size())); + for (int i = 0; i < tr.size(); i++) { + blend_space->add_triangle(tr[i].points[0], tr[i].points[1], tr[i].points[2]); + } + } + + if (p_tool == 0) { + tool_erase->show(); + tool_erase_sep->show(); + } else { + tool_erase->hide(); + tool_erase_sep->hide(); + } + _update_tool_erase(); + blend_space_draw->update(); +} + +void AnimationNodeBlendSpace2DEditor::_blend_space_draw() { + + Color linecolor = get_color("font_color", "Label"); + Color linecolor_soft = linecolor; + linecolor_soft.a *= 0.5; + Ref font = get_font("font", "Label"); + Ref icon = get_icon("KeyValue", "EditorIcons"); + Ref icon_selected = get_icon("KeySelected", "EditorIcons"); + + Size2 s = blend_space_draw->get_size(); + + if (blend_space_draw->has_focus()) { + Color color = get_color("accent_color", "Editor"); + blend_space_draw->draw_rect(Rect2(Point2(), s), color, false); + } + blend_space_draw->draw_line(Point2(1, 0), Point2(1, s.height - 1), linecolor); + blend_space_draw->draw_line(Point2(1, s.height - 1), Point2(s.width - 1, s.height - 1), linecolor); + + blend_space_draw->draw_line(Point2(0, 0), Point2(5 * EDSCALE, 0), linecolor); + if (blend_space->get_min_space().y < 0) { + int y = (blend_space->get_max_space().y / (blend_space->get_max_space().y - blend_space->get_min_space().y)) * s.height; + blend_space_draw->draw_line(Point2(0, y), Point2(5 * EDSCALE, y), linecolor); + blend_space_draw->draw_string(font, Point2(2 * EDSCALE, y - font->get_height() + font->get_ascent()), "0", linecolor); + blend_space_draw->draw_line(Point2(5 * EDSCALE, y), Point2(s.width, y), linecolor_soft); + } + + if (blend_space->get_min_space().x < 0) { + int x = (-blend_space->get_min_space().x / (blend_space->get_max_space().x - blend_space->get_min_space().x)) * s.width; + blend_space_draw->draw_line(Point2(x, s.height - 1), Point2(x, s.height - 5 * EDSCALE), linecolor); + blend_space_draw->draw_string(font, Point2(x + 2 * EDSCALE, s.height - 2 * EDSCALE - font->get_height() + font->get_ascent()), "0", linecolor); + blend_space_draw->draw_line(Point2(x, s.height - 5 * EDSCALE), Point2(x, 0), linecolor_soft); + } + + if (snap->is_pressed()) { + + linecolor_soft.a = linecolor.a * 0.1; + + if (blend_space->get_snap().x > 0) { + + int prev_idx; + for (int i = 0; i < s.x; i++) { + + float v = blend_space->get_min_space().x + i * (blend_space->get_max_space().x - blend_space->get_min_space().x) / s.x; + int idx = int(v / blend_space->get_snap().x); + + if (i > 0 && prev_idx != idx) { + blend_space_draw->draw_line(Point2(i, 0), Point2(i, s.height), linecolor_soft); + } + + prev_idx = idx; + } + } + + if (blend_space->get_snap().y > 0) { + + int prev_idx; + for (int i = 0; i < s.y; i++) { + + float v = blend_space->get_max_space().y - i * (blend_space->get_max_space().y - blend_space->get_min_space().y) / s.y; + int idx = int(v / blend_space->get_snap().y); + + if (i > 0 && prev_idx != idx) { + blend_space_draw->draw_line(Point2(0, i), Point2(s.width, i), linecolor_soft); + } + + prev_idx = idx; + } + } + } + + //triangles first + for (int i = 0; i < blend_space->get_triangle_count(); i++) { + + Vector points; + points.resize(3); + + for (int j = 0; j < 3; j++) { + int point_idx = blend_space->get_triangle_point(i, j); + Vector2 point = blend_space->get_blend_point_position(point_idx); + if (dragging_selected && selected_point == point_idx) { + point += drag_ofs; + if (snap->is_pressed()) { + point.x = Math::stepify(point.x, blend_space->get_snap().x); + point.y = Math::stepify(point.y, blend_space->get_snap().y); + } + } + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); + point *= s; + point.y = s.height - point.y; + points[j] = point; + } + + for (int j = 0; j < 3; j++) { + blend_space_draw->draw_line(points[j], points[(j + 1) % 3], linecolor, 1, true); + } + + Color color; + if (i == selected_triangle) { + color = get_color("accent_color", "Editor"); + color.a *= 0.5; + } else { + color = linecolor; + color.a *= 0.2; + } + + Vector colors; + colors.push_back(color); + colors.push_back(color); + colors.push_back(color); + blend_space_draw->draw_primitive(points, colors, Vector()); + } + + points.clear(); + for (int i = 0; i < blend_space->get_blend_point_count(); i++) { + + Vector2 point = blend_space->get_blend_point_position(i); + if (dragging_selected && selected_point == i) { + point += drag_ofs; + if (snap->is_pressed()) { + point.x = Math::stepify(point.x, blend_space->get_snap().x); + point.y = Math::stepify(point.y, blend_space->get_snap().y); + } + } + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); + point *= s; + point.y = s.height - point.y; + + points.push_back(point); + point -= (icon->get_size() / 2); + point = point.floor(); + + if (i == selected_point) { + blend_space_draw->draw_texture(icon_selected, point); + } else { + blend_space_draw->draw_texture(icon, point); + } + } + + if (making_triangle.size()) { + Vector points; + for (int i = 0; i < making_triangle.size(); i++) { + Vector2 point = blend_space->get_blend_point_position(making_triangle[i]); + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); + point *= s; + point.y = s.height - point.y; + points.push_back(point); + } + + for (int i = 0; i < points.size() - 1; i++) { + blend_space_draw->draw_line(points[i], points[i + 1], linecolor, 2, true); + } + blend_space_draw->draw_line(points[points.size() - 1], blend_space_draw->get_local_mouse_position(), linecolor, 2, true); + } + + ///draw cursor position + + { + Color color; + if (tool_blend->is_pressed()) { + color = get_color("accent_color", "Editor"); + } else { + color = linecolor; + color.a *= 0.5; + } + + Vector2 point = blend_space->get_blend_position(); + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); + point *= s; + point.y = s.height - point.y; + + if (blend_space->get_triangle_count()) { + Vector2 closest = blend_space->get_closest_point(blend_space->get_blend_position()); + closest = (closest - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); + closest *= s; + closest.y = s.height - closest.y; + + Color lcol = color; + lcol.a *= 0.4; + blend_space_draw->draw_line(point, closest, lcol, 2); + } + + float mind = 5 * EDSCALE; + float maxd = 15 * EDSCALE; + blend_space_draw->draw_line(point + Vector2(mind, 0), point + Vector2(maxd, 0), color, 2); + blend_space_draw->draw_line(point + Vector2(-mind, 0), point + Vector2(-maxd, 0), color, 2); + blend_space_draw->draw_line(point + Vector2(0, mind), point + Vector2(0, maxd), color, 2); + blend_space_draw->draw_line(point + Vector2(0, -mind), point + Vector2(0, -maxd), color, 2); + } +} + +void AnimationNodeBlendSpace2DEditor::_snap_toggled() { + + blend_space_draw->update(); +} + +void AnimationNodeBlendSpace2DEditor::_update_space() { + + if (updating) + return; + + updating = true; + + if (blend_space->get_parent().is_valid()) { + goto_parent_hb->show(); + } else { + goto_parent_hb->hide(); + } + + if (blend_space->get_auto_triangles()) { + tool_triangle->hide(); + } else { + tool_triangle->show(); + } + + auto_triangles->set_pressed(blend_space->get_auto_triangles()); + + max_x_value->set_value(blend_space->get_max_space().x); + max_y_value->set_value(blend_space->get_max_space().y); + + min_x_value->set_value(blend_space->get_min_space().x); + min_y_value->set_value(blend_space->get_min_space().y); + + label_x->set_text(blend_space->get_x_label()); + label_y->set_text(blend_space->get_y_label()); + + snap_x->set_value(blend_space->get_snap().x); + snap_y->set_value(blend_space->get_snap().y); + + blend_space_draw->update(); + + updating = false; +} + +void AnimationNodeBlendSpace2DEditor::_config_changed(double) { + if (updating) + return; + + updating = true; + undo_redo->create_action("Change BlendSpace2D Limits"); + undo_redo->add_do_method(blend_space.ptr(), "set_max_space", Vector2(max_x_value->get_value(), max_y_value->get_value())); + undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space()); + undo_redo->add_do_method(blend_space.ptr(), "set_min_space", Vector2(min_x_value->get_value(), min_y_value->get_value())); + undo_redo->add_undo_method(blend_space.ptr(), "set_min_space", blend_space->get_min_space()); + undo_redo->add_do_method(blend_space.ptr(), "set_snap", Vector2(snap_x->get_value(), snap_y->get_value())); + undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap()); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->commit_action(); + updating = false; + + blend_space_draw->update(); +} + +void AnimationNodeBlendSpace2DEditor::_labels_changed(String) { + if (updating) + return; + + updating = true; + undo_redo->create_action("Change BlendSpace2D Labels", UndoRedo::MERGE_ENDS); + undo_redo->add_do_method(blend_space.ptr(), "set_x_label", label_x->get_text()); + undo_redo->add_undo_method(blend_space.ptr(), "set_x_label", blend_space->get_x_label()); + undo_redo->add_do_method(blend_space.ptr(), "set_y_label", label_y->get_text()); + undo_redo->add_undo_method(blend_space.ptr(), "set_y_label", blend_space->get_y_label()); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->commit_action(); + updating = false; +} + +void AnimationNodeBlendSpace2DEditor::_erase_selected() { + + if (selected_point != -1) { + + updating = true; + undo_redo->create_action("Remove BlendSpace2D Point"); + undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point); + 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); + + //restore triangles using this point + for (int i = 0; i < blend_space->get_triangle_count(); i++) { + for (int j = 0; j < 3; j++) { + if (blend_space->get_triangle_point(i, j) == selected_point) { + 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); + break; + } + } + } + + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->commit_action(); + updating = false; + + blend_space_draw->update(); + } else if (selected_triangle != -1) { + + updating = true; + undo_redo->create_action("Remove BlendSpace2D Triangle"); + undo_redo->add_do_method(blend_space.ptr(), "remove_triangle", selected_triangle); + 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); + + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->commit_action(); + updating = false; + + blend_space_draw->update(); + } +} + +void AnimationNodeBlendSpace2DEditor::_update_edited_point_pos() { + if (updating) + return; + + if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { + Vector2 pos = blend_space->get_blend_point_position(selected_point); + if (dragging_selected) { + pos += drag_ofs; + if (snap->is_pressed()) { + pos.x = Math::stepify(pos.x, blend_space->get_snap().x); + pos.y = Math::stepify(pos.y, blend_space->get_snap().y); + } + } + updating = true; + edit_x->set_value(pos.x); + edit_y->set_value(pos.y); + updating = false; + } +} + +void AnimationNodeBlendSpace2DEditor::_edit_point_pos(double) { + if (updating) + return; + updating = true; + undo_redo->create_action("Move Node Point"); + undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, Vector2(edit_x->get_value(), edit_y->get_value())); + undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->add_do_method(this, "_update_edited_point_pos"); + undo_redo->add_undo_method(this, "_update_edited_point_pos"); + undo_redo->commit_action(); + updating = false; + + blend_space_draw->update(); +} + +void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); + error_label->add_color_override("font_color", get_color("error_color", "Editor")); + panel->add_style_override("panel", get_stylebox("bg", "Tree")); + tool_blend->set_icon(get_icon("EditPivot", "EditorIcons")); + tool_select->set_icon(get_icon("ToolSelect", "EditorIcons")); + tool_create->set_icon(get_icon("EditKey", "EditorIcons")); + tool_triangle->set_icon(get_icon("ToolTriangle", "EditorIcons")); + tool_erase->set_icon(get_icon("Remove", "EditorIcons")); + snap->set_icon(get_icon("SnapGrid", "EditorIcons")); + open_editor->set_icon(get_icon("Edit", "EditorIcons")); + goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); + auto_triangles->set_icon(get_icon("AutoTriangle", "EditorIcons")); + } + + if (p_what == NOTIFICATION_PROCESS) { + + String error; + + if (!blend_space->get_graph_player()) { + error = TTR("BlendSpace2D does not belong to an AnimationGraphPlayer node."); + } else if (!blend_space->get_graph_player()->is_active()) { + error = TTR("AnimationGraphPlayer is inactive.\nActivate to enable playback, check node warnings if activation fails."); + } else if (blend_space->get_graph_player()->is_state_invalid()) { + error = blend_space->get_graph_player()->get_invalid_state_reason(); + } else if (blend_space->get_triangle_count() == 0) { + error = TTR("No triangles exist, so no blending can take place."); + } + + if (error != error_label->get_text()) { + error_label->set_text(error); + if (error != String()) { + error_panel->show(); + } else { + error_panel->hide(); + } + } + } +} + +void AnimationNodeBlendSpace2DEditor::_open_editor() { + + if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { + Ref an = blend_space->get_blend_point_node(selected_point); + ERR_FAIL_COND(!an.is_valid()); + EditorNode::get_singleton()->edit_item(an.ptr()); + } +} + +void AnimationNodeBlendSpace2DEditor::_goto_parent() { + + EditorNode::get_singleton()->edit_item(blend_space->get_parent().ptr()); +} + +void AnimationNodeBlendSpace2DEditor::_removed_from_graph() { + EditorNode::get_singleton()->edit_item(NULL); +} + +void AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled() { + + undo_redo->create_action("Toggle Auto Triangles"); + undo_redo->add_do_method(blend_space.ptr(), "set_auto_triangles", auto_triangles->is_pressed()); + undo_redo->add_undo_method(blend_space.ptr(), "set_auto_triangles", blend_space->get_auto_triangles()); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->commit_action(); +} + +void AnimationNodeBlendSpace2DEditor::_bind_methods() { + + ClassDB::bind_method("_blend_space_gui_input", &AnimationNodeBlendSpace2DEditor::_blend_space_gui_input); + ClassDB::bind_method("_blend_space_draw", &AnimationNodeBlendSpace2DEditor::_blend_space_draw); + ClassDB::bind_method("_config_changed", &AnimationNodeBlendSpace2DEditor::_config_changed); + ClassDB::bind_method("_labels_changed", &AnimationNodeBlendSpace2DEditor::_labels_changed); + ClassDB::bind_method("_update_space", &AnimationNodeBlendSpace2DEditor::_update_space); + ClassDB::bind_method("_snap_toggled", &AnimationNodeBlendSpace2DEditor::_snap_toggled); + ClassDB::bind_method("_tool_switch", &AnimationNodeBlendSpace2DEditor::_tool_switch); + ClassDB::bind_method("_erase_selected", &AnimationNodeBlendSpace2DEditor::_erase_selected); + ClassDB::bind_method("_update_tool_erase", &AnimationNodeBlendSpace2DEditor::_update_tool_erase); + ClassDB::bind_method("_edit_point_pos", &AnimationNodeBlendSpace2DEditor::_edit_point_pos); + + ClassDB::bind_method("_add_menu_type", &AnimationNodeBlendSpace2DEditor::_add_menu_type); + ClassDB::bind_method("_add_animation_type", &AnimationNodeBlendSpace2DEditor::_add_animation_type); + + ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace2DEditor::_update_edited_point_pos); + + ClassDB::bind_method("_open_editor", &AnimationNodeBlendSpace2DEditor::_open_editor); + ClassDB::bind_method("_goto_parent", &AnimationNodeBlendSpace2DEditor::_goto_parent); + + ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpace2DEditor::_removed_from_graph); + + ClassDB::bind_method("_auto_triangles_toggled", &AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled); +} + +AnimationNodeBlendSpace2DEditor *AnimationNodeBlendSpace2DEditor::singleton = NULL; + +AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { + + singleton = this; + updating = false; + + HBoxContainer *top_hb = memnew(HBoxContainer); + add_child(top_hb); + + Ref bg; + bg.instance(); + + goto_parent_hb = memnew(HBoxContainer); + top_hb->add_child(goto_parent_hb); + goto_parent = memnew(ToolButton); + goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED); + goto_parent_hb->add_child(goto_parent); + goto_parent_hb->add_child(memnew(VSeparator)); + goto_parent_hb->hide(); + + tool_blend = memnew(ToolButton); + tool_blend->set_toggle_mode(true); + tool_blend->set_button_group(bg); + top_hb->add_child(tool_blend); + tool_blend->set_pressed(true); + tool_blend->set_tooltip(TTR("Set the blending position within the space")); + tool_blend->connect("pressed", this, "_tool_switch", varray(3)); + + tool_select = memnew(ToolButton); + tool_select->set_toggle_mode(true); + tool_select->set_button_group(bg); + top_hb->add_child(tool_select); + tool_select->set_tooltip(TTR("Select and move points, create points with RMB.")); + tool_select->connect("pressed", this, "_tool_switch", varray(0)); + + tool_create = memnew(ToolButton); + tool_create->set_toggle_mode(true); + tool_create->set_button_group(bg); + top_hb->add_child(tool_create); + tool_create->set_tooltip(TTR("Create points.")); + tool_create->connect("pressed", this, "_tool_switch", varray(1)); + + tool_triangle = memnew(ToolButton); + tool_triangle->set_toggle_mode(true); + tool_triangle->set_button_group(bg); + top_hb->add_child(tool_triangle); + tool_triangle->set_tooltip(TTR("Create triangles by connecting points.")); + tool_triangle->connect("pressed", this, "_tool_switch", varray(2)); + + tool_erase_sep = memnew(VSeparator); + top_hb->add_child(tool_erase_sep); + tool_erase = memnew(ToolButton); + top_hb->add_child(tool_erase); + tool_erase->set_tooltip(TTR("Erase points and triangles.")); + tool_erase->connect("pressed", this, "_erase_selected"); + tool_erase->set_disabled(true); + + top_hb->add_child(memnew(VSeparator)); + + auto_triangles = memnew(ToolButton); + top_hb->add_child(auto_triangles); + auto_triangles->connect("pressed", this, "_auto_triangles_toggled"); + auto_triangles->set_toggle_mode(true); + auto_triangles->set_tooltip(TTR("Generate blend triangles automatically (instead of manually)")); + + top_hb->add_child(memnew(VSeparator)); + + snap = memnew(ToolButton); + snap->set_toggle_mode(true); + top_hb->add_child(snap); + //snap->set_text(TTR("Snap")); + snap->set_pressed(true); + snap->connect("pressed", this, "_snap_toggled"); + + snap_x = memnew(SpinBox); + top_hb->add_child(snap_x); + snap_x->set_prefix("x:"); + snap_x->set_min(0.01); + snap_x->set_step(0.01); + snap_x->set_max(1000); + + snap_y = memnew(SpinBox); + top_hb->add_child(snap_y); + snap_y->set_prefix("y:"); + snap_y->set_min(0.01); + snap_y->set_step(0.01); + snap_y->set_max(1000); + + edit_hb = memnew(HBoxContainer); + top_hb->add_child(edit_hb); + edit_hb->add_child(memnew(VSeparator)); + edit_hb->add_child(memnew(Label(TTR("Point")))); + edit_x = memnew(SpinBox); + edit_hb->add_child(edit_x); + edit_x->set_min(-1000); + edit_x->set_step(0.01); + edit_x->set_max(1000); + edit_x->connect("value_changed", this, "_edit_point_pos"); + edit_y = memnew(SpinBox); + edit_hb->add_child(edit_y); + edit_y->set_min(-1000); + edit_y->set_step(0.01); + edit_y->set_max(1000); + edit_y->connect("value_changed", this, "_edit_point_pos"); + open_editor = memnew(Button); + edit_hb->add_child(open_editor); + open_editor->set_text(TTR("Open Editor")); + open_editor->connect("pressed", this, "_open_editor", varray(), CONNECT_DEFERRED); + edit_hb->hide(); + open_editor->hide(); + + HBoxContainer *main_hb = memnew(HBoxContainer); + add_child(main_hb); + main_hb->set_v_size_flags(SIZE_EXPAND_FILL); + + GridContainer *main_grid = memnew(GridContainer); + main_grid->set_columns(2); + main_hb->add_child(main_grid); + main_grid->set_h_size_flags(SIZE_EXPAND_FILL); + { + VBoxContainer *left_vbox = memnew(VBoxContainer); + main_grid->add_child(left_vbox); + left_vbox->set_v_size_flags(SIZE_EXPAND_FILL); + max_y_value = memnew(SpinBox); + left_vbox->add_child(max_y_value); + left_vbox->add_spacer(); + label_y = memnew(LineEdit); + left_vbox->add_child(label_y); + label_y->set_expand_to_text_length(true); + left_vbox->add_spacer(); + min_y_value = memnew(SpinBox); + left_vbox->add_child(min_y_value); + + max_y_value->set_max(10000); + max_y_value->set_min(0.01); + max_y_value->set_step(0.01); + + min_y_value->set_min(-10000); + min_y_value->set_max(0); + min_y_value->set_step(0.01); + } + + panel = memnew(PanelContainer); + panel->set_clip_contents(true); + main_grid->add_child(panel); + panel->set_h_size_flags(SIZE_EXPAND_FILL); + + blend_space_draw = memnew(Control); + blend_space_draw->connect("gui_input", this, "_blend_space_gui_input"); + blend_space_draw->connect("draw", this, "_blend_space_draw"); + blend_space_draw->set_focus_mode(FOCUS_ALL); + + panel->add_child(blend_space_draw); + main_grid->add_child(memnew(Control)); //empty bottom left + + { + HBoxContainer *bottom_vbox = memnew(HBoxContainer); + main_grid->add_child(bottom_vbox); + bottom_vbox->set_h_size_flags(SIZE_EXPAND_FILL); + min_x_value = memnew(SpinBox); + bottom_vbox->add_child(min_x_value); + bottom_vbox->add_spacer(); + label_x = memnew(LineEdit); + bottom_vbox->add_child(label_x); + label_x->set_expand_to_text_length(true); + bottom_vbox->add_spacer(); + max_x_value = memnew(SpinBox); + bottom_vbox->add_child(max_x_value); + + max_x_value->set_max(10000); + max_x_value->set_min(0.01); + max_x_value->set_step(0.01); + + min_x_value->set_min(-10000); + min_x_value->set_max(0); + min_x_value->set_step(0.01); + } + + snap_x->connect("value_changed", this, "_config_changed"); + snap_y->connect("value_changed", this, "_config_changed"); + max_x_value->connect("value_changed", this, "_config_changed"); + min_x_value->connect("value_changed", this, "_config_changed"); + max_y_value->connect("value_changed", this, "_config_changed"); + min_y_value->connect("value_changed", this, "_config_changed"); + label_x->connect("text_changed", this, "_labels_changed"); + label_y->connect("text_changed", this, "_labels_changed"); + + error_panel = memnew(PanelContainer); + add_child(error_panel); + error_label = memnew(Label); + error_panel->add_child(error_label); + error_label->set_text("eh"); + + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + set_custom_minimum_size(Size2(0, 300 * EDSCALE)); + + menu = memnew(PopupMenu); + add_child(menu); + menu->connect("index_pressed", this, "_add_menu_type"); + + animations_menu = memnew(PopupMenu); + menu->add_child(animations_menu); + animations_menu->set_name("animations"); + animations_menu->connect("index_pressed", this, "_add_animation_type"); + + selected_point = -1; + selected_triangle = -1; + + dragging_selected = false; + dragging_selected_attempt = false; +} + +void AnimationNodeBlendSpace2DEditorPlugin::edit(Object *p_object) { + + anim_tree_editor->edit(Object::cast_to(p_object)); +} + +bool AnimationNodeBlendSpace2DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("AnimationNodeBlendSpace2D"); +} + +void AnimationNodeBlendSpace2DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + //editor->hide_animation_player_editors(); + //editor->animation_panel_make_visible(true); + button->show(); + editor->make_bottom_panel_item_visible(anim_tree_editor); + anim_tree_editor->set_process(true); + } else { + + if (anim_tree_editor->is_visible_in_tree()) + editor->hide_bottom_panel(); + button->hide(); + anim_tree_editor->set_process(false); + } +} + +AnimationNodeBlendSpace2DEditorPlugin::AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node) { + + editor = p_node; + anim_tree_editor = memnew(AnimationNodeBlendSpace2DEditor); + anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); + + button = editor->add_bottom_panel_item(TTR("BlendSpace2D"), anim_tree_editor); + button->hide(); +} + +AnimationNodeBlendSpace2DEditorPlugin::~AnimationNodeBlendSpace2DEditorPlugin() { +} diff --git a/editor/plugins/animation_blend_space_2d_editor.h b/editor/plugins/animation_blend_space_2d_editor.h new file mode 100644 index 0000000000..a0e497804e --- /dev/null +++ b/editor/plugins/animation_blend_space_2d_editor.h @@ -0,0 +1,130 @@ +#ifndef ANIMATION_BLEND_SPACE_2D_EDITOR_H +#define ANIMATION_BLEND_SPACE_2D_EDITOR_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "editor/property_editor.h" +#include "scene/animation/animation_blend_space_2d.h" +#include "scene/gui/button.h" +#include "scene/gui/graph_edit.h" +#include "scene/gui/popup.h" +#include "scene/gui/tree.h" +/** + @author Juan Linietsky +*/ + +class AnimationNodeBlendSpace2DEditor : public VBoxContainer { + + GDCLASS(AnimationNodeBlendSpace2DEditor, VBoxContainer); + + Ref blend_space; + + HBoxContainer *goto_parent_hb; + ToolButton *goto_parent; + + PanelContainer *panel; + ToolButton *tool_blend; + ToolButton *tool_select; + ToolButton *tool_create; + ToolButton *tool_triangle; + VSeparator *tool_erase_sep; + ToolButton *tool_erase; + ToolButton *snap; + SpinBox *snap_x; + SpinBox *snap_y; + + ToolButton *auto_triangles; + + LineEdit *label_x; + LineEdit *label_y; + SpinBox *max_x_value; + SpinBox *min_x_value; + SpinBox *max_y_value; + SpinBox *min_y_value; + + HBoxContainer *edit_hb; + SpinBox *edit_x; + SpinBox *edit_y; + Button *open_editor; + + int selected_point; + int selected_triangle; + + Control *blend_space_draw; + + PanelContainer *error_panel; + Label *error_label; + + bool updating; + + UndoRedo *undo_redo; + + static AnimationNodeBlendSpace2DEditor *singleton; + + void _blend_space_gui_input(const Ref &p_event); + void _blend_space_draw(); + + void _update_space(); + + void _config_changed(double); + void _labels_changed(String); + void _snap_toggled(); + + PopupMenu *menu; + PopupMenu *animations_menu; + Vector animations_to_add; + Vector2 add_point_pos; + Vector points; + + bool dragging_selected_attempt; + bool dragging_selected; + Vector2 drag_from; + Vector2 drag_ofs; + + Vector making_triangle; + + void _add_menu_type(int p_index); + void _add_animation_type(int p_index); + + void _tool_switch(int p_tool); + void _update_edited_point_pos(); + void _update_tool_erase(); + void _erase_selected(); + void _edit_point_pos(double); + void _open_editor(); + + void _goto_parent(); + + void _removed_from_graph(); + + void _auto_triangles_toggled(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + static AnimationNodeBlendSpace2DEditor *get_singleton() { return singleton; } + void edit(AnimationNodeBlendSpace2D *p_blend_space); + AnimationNodeBlendSpace2DEditor(); +}; + +class AnimationNodeBlendSpace2DEditorPlugin : public EditorPlugin { + + GDCLASS(AnimationNodeBlendSpace2DEditorPlugin, EditorPlugin); + + AnimationNodeBlendSpace2DEditor *anim_tree_editor; + EditorNode *editor; + Button *button; + +public: + virtual String get_name() const { return "BlendSpace2D"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node); + ~AnimationNodeBlendSpace2DEditorPlugin(); +}; +#endif // ANIMATION_BLEND_SPACE_2D_EDITOR_H diff --git a/scene/animation/animation_blend_space1d.cpp b/scene/animation/animation_blend_space1d.cpp deleted file mode 100644 index 28555e41bb..0000000000 --- a/scene/animation/animation_blend_space1d.cpp +++ /dev/null @@ -1,272 +0,0 @@ -#include "animation_blend_space1d.h" - -void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const { - if (property.name.begins_with("blend_point_")) { - String left = property.name.get_slicec('/', 0); - int idx = left.get_slicec('_', 1).to_int(); - if (idx >= blend_points_used) { - property.usage = 0; - } - } - AnimationRootNode::_validate_property(property); -} - -void AnimationNodeBlendSpace1D::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace1D::add_blend_point, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace1D::set_blend_point_position); - ClassDB::bind_method(D_METHOD("get_blend_point_position", "point"), &AnimationNodeBlendSpace1D::get_blend_point_position); - ClassDB::bind_method(D_METHOD("set_blend_point_node", "point", "node"), &AnimationNodeBlendSpace1D::set_blend_point_node); - ClassDB::bind_method(D_METHOD("get_blend_point_node", "point"), &AnimationNodeBlendSpace1D::get_blend_point_node); - ClassDB::bind_method(D_METHOD("remove_blend_point", "point"), &AnimationNodeBlendSpace1D::remove_blend_point); - ClassDB::bind_method(D_METHOD("get_blend_point_count"), &AnimationNodeBlendSpace1D::get_blend_point_count); - - ClassDB::bind_method(D_METHOD("set_min_space", "min_space"), &AnimationNodeBlendSpace1D::set_min_space); - ClassDB::bind_method(D_METHOD("get_min_space"), &AnimationNodeBlendSpace1D::get_min_space); - - ClassDB::bind_method(D_METHOD("set_max_space", "max_space"), &AnimationNodeBlendSpace1D::set_max_space); - ClassDB::bind_method(D_METHOD("get_max_space"), &AnimationNodeBlendSpace1D::get_max_space); - - ClassDB::bind_method(D_METHOD("set_snap", "snap"), &AnimationNodeBlendSpace1D::set_snap); - ClassDB::bind_method(D_METHOD("get_snap"), &AnimationNodeBlendSpace1D::get_snap); - - ClassDB::bind_method(D_METHOD("set_blend_pos", "pos"), &AnimationNodeBlendSpace1D::set_blend_pos); - ClassDB::bind_method(D_METHOD("get_blend_pos"), &AnimationNodeBlendSpace1D::get_blend_pos); - - ClassDB::bind_method(D_METHOD("set_value_label", "text"), &AnimationNodeBlendSpace1D::set_value_label); - ClassDB::bind_method(D_METHOD("get_value_label"), &AnimationNodeBlendSpace1D::get_value_label); - - ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace1D::_add_blend_point); - - for (int i = 0; i < MAX_BLEND_POINTS; i++) { - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "_add_blend_point", "get_blend_point_node", i); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i); - } - - ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "blend_pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_blend_pos", "get_blend_pos"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_value_label", "get_value_label"); -} - -void AnimationNodeBlendSpace1D::add_blend_point(const Ref &p_node, float p_position, int p_at_index) { - ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); - ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(p_node->get_parent().is_valid()); - ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used); - - if (p_at_index == -1 || p_at_index == blend_points_used) { - p_at_index = blend_points_used; - } else { - for (int i = blend_points_used; i > p_at_index; i++) { - blend_points[i] = blend_points[i - 1]; - } - } - - blend_points[p_at_index].node = p_node; - blend_points[p_at_index].position = p_position; - - blend_points[p_at_index].node->set_parent(this); - blend_points[p_at_index].node->set_graph_player(get_graph_player()); - - blend_points_used++; -} - -void AnimationNodeBlendSpace1D::set_blend_point_position(int p_point, float p_position) { - ERR_FAIL_INDEX(p_point, blend_points_used); - - blend_points[p_point].position = p_position; -} - -void AnimationNodeBlendSpace1D::set_blend_point_node(int p_point, const Ref &p_node) { - ERR_FAIL_INDEX(p_point, blend_points_used); - ERR_FAIL_COND(p_node.is_null()); - - if (blend_points[p_point].node.is_valid()) { - blend_points[p_point].node->set_parent(NULL); - blend_points[p_point].node->set_graph_player(NULL); - } - - blend_points[p_point].node = p_node; - blend_points[p_point].node->set_parent(this); - blend_points[p_point].node->set_graph_player(get_graph_player()); -} - -float AnimationNodeBlendSpace1D::get_blend_point_position(int p_point) const { - ERR_FAIL_INDEX_V(p_point, blend_points_used, 0); - return blend_points[p_point].position; -} - -Ref AnimationNodeBlendSpace1D::get_blend_point_node(int p_point) const { - ERR_FAIL_INDEX_V(p_point, blend_points_used, Ref()); - return blend_points[p_point].node; -} - -void AnimationNodeBlendSpace1D::remove_blend_point(int p_point) { - ERR_FAIL_INDEX(p_point, blend_points_used); - - blend_points[p_point].node->set_parent(NULL); - blend_points[p_point].node->set_graph_player(NULL); - - for (int i = p_point; i < blend_points_used - 1; i++) { - blend_points[i] = blend_points[i + 1]; - } - - blend_points_used--; -} - -int AnimationNodeBlendSpace1D::get_blend_point_count() const { - - return blend_points_used; -} - -void AnimationNodeBlendSpace1D::set_min_space(float p_min) { - min_space = p_min; - - if (min_space >= max_space) { - min_space = max_space - 1; - } -} - -float AnimationNodeBlendSpace1D::get_min_space() const { - return min_space; -} - -void AnimationNodeBlendSpace1D::set_max_space(float p_max) { - max_space = p_max; - - if (max_space <= min_space) { - max_space = min_space + 1; - } -} - -float AnimationNodeBlendSpace1D::get_max_space() const { - return max_space; -} - -void AnimationNodeBlendSpace1D::set_snap(float p_snap) { - snap = p_snap; -} - -float AnimationNodeBlendSpace1D::get_snap() const { - return snap; -} - -void AnimationNodeBlendSpace1D::set_blend_pos(float p_pos) { - blend_pos = p_pos; -} - -float AnimationNodeBlendSpace1D::get_blend_pos() const { - return blend_pos; -} - -void AnimationNodeBlendSpace1D::set_value_label(const String &p_label) { - value_label = p_label; -} - -String AnimationNodeBlendSpace1D::get_value_label() const { - return value_label; -} - -void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref &p_node) { - if (p_index == blend_points_used) { - add_blend_point(p_node, 0); - } else { - set_blend_point_node(p_index, p_node); - } -} - -float AnimationNodeBlendSpace1D::process(float p_time, bool p_seek) { - - if (blend_points_used == 0) { - return 0.0; - } - - if (blend_points_used == 1) { - // only one point available, just play that animation - return blend_node(blend_points[0].node, p_time, p_seek, 1.0, FILTER_IGNORE, false); - } - - int point_lower = -1; - float pos_lower = 0.0; - int point_higher = -1; - float pos_higher = 0.0; - - // find the closest two points to blend between - for (int i = 0; i < blend_points_used; i++) { - - float pos = blend_points[i].position; - - if (pos <= blend_pos) { - if (point_lower == -1) { - point_lower = i; - pos_lower = pos; - } else if ((blend_pos - pos) < (blend_pos - pos_lower)) { - point_lower = i; - pos_lower = pos; - } - } else { - if (point_higher == -1) { - point_higher = i; - pos_higher = pos; - } else if ((pos - blend_pos) < (pos_higher - blend_pos)) { - point_higher = i; - pos_higher = pos; - } - } - } - - if (point_lower == -1) { - // we are on the left side, no other point to the left - // we just play the next point. - - return blend_node(blend_points[point_higher].node, p_time, p_seek, 1.0, FILTER_IGNORE, false); - } else if (point_higher == -1) { - // we are on the right side, no other point to the right - // we just play the previous point - return blend_node(blend_points[point_lower].node, p_time, p_seek, 1.0, FILTER_IGNORE, false); - } else { - - //w we are between two points. - // figure out weights, then blend the animations - - float distance_between_points = pos_higher - pos_lower; - - float current_pos_inbetween = blend_pos - pos_lower; - - float blend_percentage = current_pos_inbetween / distance_between_points; - - float blend_lower = 1.0 - blend_percentage; - float blend_higher = blend_percentage; - - float time_remaining_lower = 0.0; - float time_remaining_higher = 0.0; - - time_remaining_lower = blend_node(blend_points[point_lower].node, p_time, p_seek, blend_lower, FILTER_IGNORE, false); - time_remaining_higher = blend_node(blend_points[point_higher].node, p_time, p_seek, blend_higher, FILTER_IGNORE, false); - - return MAX(time_remaining_lower, time_remaining_higher); - } -} - -String AnimationNodeBlendSpace1D::get_caption() const { - return "BlendSpace1D"; -} - -AnimationNodeBlendSpace1D::AnimationNodeBlendSpace1D() { - - blend_points_used = 0; - max_space = 1; - min_space = -1; - - snap = 0.1; - value_label = "value"; -} - -AnimationNodeBlendSpace1D::~AnimationNodeBlendSpace1D() { - - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_parent(this); - blend_points[i].node->set_graph_player(get_graph_player()); - } -} diff --git a/scene/animation/animation_blend_space1d.h b/scene/animation/animation_blend_space1d.h deleted file mode 100644 index 9db8f53594..0000000000 --- a/scene/animation/animation_blend_space1d.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef ANIMATION_BLEND_SPACE1D_H -#define ANIMATION_BLEND_SPACE1D_H - -#include "scene/animation/animation_graph_player.h" - -class AnimationNodeBlendSpace1D : public AnimationRootNode { - GDCLASS(AnimationNodeBlendSpace1D, AnimationRootNode) - - enum { - MAX_BLEND_POINTS = 64 - }; - - struct BlendPoint { - Ref node; - float position; - }; - - BlendPoint blend_points[MAX_BLEND_POINTS]; - int blend_points_used; - - float blend_pos; - - float max_space; - float min_space; - - float snap; - - String value_label; - - void _add_blend_point(int p_index, const Ref &p_node); - -protected: - virtual void _validate_property(PropertyInfo &property) const; - static void _bind_methods(); - -public: - void add_blend_point(const Ref &p_node, float p_position, int p_at_index = -1); - void set_blend_point_position(int p_point, float p_position); - void set_blend_point_node(int p_point, const Ref &p_node); - - float get_blend_point_position(int p_point) const; - Ref get_blend_point_node(int p_point) const; - void remove_blend_point(int p_point); - int get_blend_point_count() const; - - void set_min_space(float p_min); - float get_min_space() const; - - void set_max_space(float p_max); - float get_max_space() const; - - void set_snap(float p_snap); - float get_snap() const; - - void set_blend_pos(float p_pos); - float get_blend_pos() const; - - void set_value_label(const String &p_label); - String get_value_label() const; - - float process(float p_time, bool p_seek); - String get_caption() const; - - AnimationNodeBlendSpace1D(); - ~AnimationNodeBlendSpace1D(); -}; - -#endif // ANIMATION_BLEND_SPACE1D_H diff --git a/scene/animation/animation_blend_space2d.cpp b/scene/animation/animation_blend_space2d.cpp deleted file mode 100644 index 6db4a6976c..0000000000 --- a/scene/animation/animation_blend_space2d.cpp +++ /dev/null @@ -1,558 +0,0 @@ -#include "animation_blend_space2d.h" -#include "math/delaunay.h" - -void AnimationNodeBlendSpace2D::add_blend_point(const Ref &p_node, const Vector2 &p_position, int p_at_index) { - ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); - ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(p_node->get_parent().is_valid()); - ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used); - - if (p_at_index == -1 || p_at_index == blend_points_used) { - p_at_index = blend_points_used; - } else { - for (int i = blend_points_used; i > p_at_index; i--) { - blend_points[i] = blend_points[i - 1]; - } - for (int i = 0; i < triangles.size(); i++) { - for (int j = 0; j < 3; j++) { - if (triangles[i].points[j] >= p_at_index) { - triangles[i].points[j]++; - } - } - } - } - blend_points[p_at_index].node = p_node; - blend_points[p_at_index].position = p_position; - - blend_points[p_at_index].node->set_parent(this); - blend_points[p_at_index].node->set_graph_player(get_graph_player()); - blend_points_used++; - - if (auto_triangles) { - trianges_dirty = true; - } -} - -void AnimationNodeBlendSpace2D::set_blend_point_position(int p_point, const Vector2 &p_position) { - ERR_FAIL_INDEX(p_point, blend_points_used); - blend_points[p_point].position = p_position; - if (auto_triangles) { - trianges_dirty = true; - } -} -void AnimationNodeBlendSpace2D::set_blend_point_node(int p_point, const Ref &p_node) { - ERR_FAIL_INDEX(p_point, blend_points_used); - ERR_FAIL_COND(p_node.is_null()); - - if (blend_points[p_point].node.is_valid()) { - blend_points[p_point].node->set_parent(NULL); - blend_points[p_point].node->set_graph_player(NULL); - } - blend_points[p_point].node = p_node; - blend_points[p_point].node->set_parent(this); - blend_points[p_point].node->set_graph_player(get_graph_player()); -} -Vector2 AnimationNodeBlendSpace2D::get_blend_point_position(int p_point) const { - ERR_FAIL_INDEX_V(p_point, blend_points_used, Vector2()); - return blend_points[p_point].position; -} -Ref AnimationNodeBlendSpace2D::get_blend_point_node(int p_point) const { - ERR_FAIL_INDEX_V(p_point, blend_points_used, Ref()); - return blend_points[p_point].node; -} -void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) { - ERR_FAIL_INDEX(p_point, blend_points_used); - - blend_points[p_point].node->set_parent(NULL); - blend_points[p_point].node->set_graph_player(NULL); - - for (int i = 0; i < triangles.size(); i++) { - bool erase = false; - for (int j = 0; j < 3; j++) { - if (triangles[i].points[j] == p_point) { - erase = true; - break; - } else if (triangles[i].points[j] > p_point) { - triangles[i].points[j]--; - } - } - if (erase) { - triangles.remove(i); - - i--; - } - } - - for (int i = p_point; i < blend_points_used - 1; i++) { - blend_points[i] = blend_points[i + 1]; - } - blend_points_used--; -} - -int AnimationNodeBlendSpace2D::get_blend_point_count() const { - - return blend_points_used; -} - -bool AnimationNodeBlendSpace2D::has_triangle(int p_x, int p_y, int p_z) const { - - ERR_FAIL_INDEX_V(p_x, blend_points_used, false); - ERR_FAIL_INDEX_V(p_y, blend_points_used, false); - ERR_FAIL_INDEX_V(p_z, blend_points_used, false); - - BlendTriangle t; - t.points[0] = p_x; - t.points[1] = p_y; - t.points[2] = p_z; - - SortArray sort; - sort.sort(t.points, 3); - - for (int i = 0; i < triangles.size(); i++) { - bool all_equal = true; - for (int j = 0; j < 3; j++) { - if (triangles[i].points[j] != t.points[j]) { - all_equal = false; - break; - } - } - if (all_equal) - return true; - } - - return false; -} - -void AnimationNodeBlendSpace2D::add_triangle(int p_x, int p_y, int p_z, int p_at_index) { - - ERR_FAIL_INDEX(p_x, blend_points_used); - ERR_FAIL_INDEX(p_y, blend_points_used); - ERR_FAIL_INDEX(p_z, blend_points_used); - - _update_triangles(); - - BlendTriangle t; - t.points[0] = p_x; - t.points[1] = p_y; - t.points[2] = p_z; - - SortArray sort; - sort.sort(t.points, 3); - - for (int i = 0; i < triangles.size(); i++) { - bool all_equal = true; - for (int j = 0; j < 3; j++) { - if (triangles[i].points[j] != t.points[j]) { - all_equal = false; - break; - } - } - ERR_FAIL_COND(all_equal); - } - - if (p_at_index == -1 || p_at_index == triangles.size()) { - triangles.push_back(t); - } else { - triangles.insert(p_at_index, t); - } -} -int AnimationNodeBlendSpace2D::get_triangle_point(int p_triangle, int p_point) { - - _update_triangles(); - - ERR_FAIL_INDEX_V(p_point, 3, -1); - ERR_FAIL_INDEX_V(p_triangle, triangles.size(), -1); - return triangles[p_triangle].points[p_point]; -} -void AnimationNodeBlendSpace2D::remove_triangle(int p_triangle) { - ERR_FAIL_INDEX(p_triangle, triangles.size()); - - triangles.remove(p_triangle); -} - -int AnimationNodeBlendSpace2D::get_triangle_count() const { - return triangles.size(); -} - -void AnimationNodeBlendSpace2D::set_min_space(const Vector2 &p_min) { - - min_space = p_min; - if (min_space.x >= max_space.x) { - min_space.x = max_space.x - 1; - } - if (min_space.y >= max_space.y) { - min_space.y = max_space.y - 1; - } -} -Vector2 AnimationNodeBlendSpace2D::get_min_space() const { - return min_space; -} - -void AnimationNodeBlendSpace2D::set_max_space(const Vector2 &p_max) { - - max_space = p_max; - if (max_space.x <= min_space.x) { - max_space.x = min_space.x + 1; - } - if (max_space.y <= min_space.y) { - max_space.y = min_space.y + 1; - } -} -Vector2 AnimationNodeBlendSpace2D::get_max_space() const { - return max_space; -} - -void AnimationNodeBlendSpace2D::set_snap(const Vector2 &p_snap) { - snap = p_snap; -} -Vector2 AnimationNodeBlendSpace2D::get_snap() const { - return snap; -} - -void AnimationNodeBlendSpace2D::set_blend_position(const Vector2 &p_pos) { - blend_pos = p_pos; -} -Vector2 AnimationNodeBlendSpace2D::get_blend_position() const { - return blend_pos; -} - -void AnimationNodeBlendSpace2D::set_x_label(const String &p_label) { - x_label = p_label; -} -String AnimationNodeBlendSpace2D::get_x_label() const { - return x_label; -} - -void AnimationNodeBlendSpace2D::set_y_label(const String &p_label) { - y_label = p_label; -} -String AnimationNodeBlendSpace2D::get_y_label() const { - return y_label; -} - -void AnimationNodeBlendSpace2D::_add_blend_point(int p_index, const Ref &p_node) { - if (p_index == blend_points_used) { - add_blend_point(p_node, Vector2()); - } else { - set_blend_point_node(p_index, p_node); - } -} - -void AnimationNodeBlendSpace2D::_set_triangles(const Vector &p_triangles) { - - if (auto_triangles) - return; - ERR_FAIL_COND(p_triangles.size() % 3 != 0); - for (int i = 0; i < p_triangles.size(); i += 3) { - add_triangle(p_triangles[i + 0], p_triangles[i + 1], p_triangles[i + 2]); - } -} - -Vector AnimationNodeBlendSpace2D::_get_triangles() const { - - Vector t; - if (auto_triangles && trianges_dirty) - return t; - - t.resize(triangles.size() * 3); - for (int i = 0; i < triangles.size(); i++) { - t[i * 3 + 0] = triangles[i].points[0]; - t[i * 3 + 1] = triangles[i].points[1]; - t[i * 3 + 2] = triangles[i].points[2]; - } - return t; -} - -void AnimationNodeBlendSpace2D::_update_triangles() { - - if (!auto_triangles || !trianges_dirty) - return; - - trianges_dirty = false; - triangles.clear(); - if (blend_points_used < 3) - return; - - Vector points; - points.resize(blend_points_used); - for (int i = 0; i < blend_points_used; i++) { - points[i] = blend_points[i].position; - } - - Vector triangles = Delaunay2D::triangulate(points); - - for (int i = 0; i < triangles.size(); i++) { - add_triangle(triangles[i].points[0], triangles[i].points[1], triangles[i].points[2]); - } -} - -Vector2 AnimationNodeBlendSpace2D::get_closest_point(const Vector2 &p_point) { - - _update_triangles(); - - if (triangles.size() == 0) - return Vector2(); - - Vector2 best_point; - bool first = true; - - for (int i = 0; i < triangles.size(); i++) { - Vector2 points[3]; - for (int j = 0; j < 3; j++) { - points[j] = get_blend_point_position(get_triangle_point(i, j)); - } - - if (Geometry::is_point_in_triangle(p_point, points[0], points[1], points[2])) { - - return p_point; - } - - for (int j = 0; j < 3; j++) { - Vector2 s[2] = { - points[j], - points[(j + 1) % 3] - }; - Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, s); - if (first || closest.distance_to(p_point) < best_point.distance_to(p_point)) { - best_point = closest; - first = false; - } - } - } - - return best_point; -} - -void AnimationNodeBlendSpace2D::_blend_triangle(const Vector2 &p_pos, const Vector2 *p_points, float *r_weights) { - - if (p_pos.distance_squared_to(p_points[0]) < CMP_EPSILON2) { - r_weights[0] = 1; - r_weights[1] = 0; - r_weights[2] = 0; - return; - } - if (p_pos.distance_squared_to(p_points[1]) < CMP_EPSILON2) { - r_weights[0] = 0; - r_weights[1] = 1; - r_weights[2] = 0; - return; - } - if (p_pos.distance_squared_to(p_points[2]) < CMP_EPSILON2) { - r_weights[0] = 0; - r_weights[1] = 0; - r_weights[2] = 1; - return; - } - - Vector2 v0 = p_points[1] - p_points[0]; - Vector2 v1 = p_points[2] - p_points[0]; - Vector2 v2 = p_pos - p_points[0]; - - float d00 = v0.dot(v0); - float d01 = v0.dot(v1); - float d11 = v1.dot(v1); - float d20 = v2.dot(v0); - float d21 = v2.dot(v1); - float denom = (d00 * d11 - d01 * d01); - if (denom == 0) { - r_weights[0] = 1; - r_weights[1] = 0; - r_weights[2] = 0; - return; - } - float v = (d11 * d20 - d01 * d21) / denom; - float w = (d00 * d21 - d01 * d20) / denom; - float u = 1.0f - v - w; - - r_weights[0] = u; - r_weights[1] = v; - r_weights[2] = w; -} - -float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { - - _update_triangles(); - - if (triangles.size() == 0) - return 0; - - Vector2 best_point; - bool first = true; - int blend_triangle = -1; - float blend_weights[3] = { 0, 0, 0 }; - - for (int i = 0; i < triangles.size(); i++) { - Vector2 points[3]; - for (int j = 0; j < 3; j++) { - points[j] = get_blend_point_position(get_triangle_point(i, j)); - } - - if (Geometry::is_point_in_triangle(blend_pos, points[0], points[1], points[2])) { - - blend_triangle = i; - _blend_triangle(blend_pos, points, blend_weights); - break; - } - - for (int j = 0; j < 3; j++) { - Vector2 s[2] = { - points[j], - points[(j + 1) % 3] - }; - Vector2 closest = Geometry::get_closest_point_to_segment_2d(blend_pos, s); - if (first || closest.distance_to(blend_pos) < best_point.distance_to(blend_pos)) { - best_point = closest; - blend_triangle = i; - first = false; - float d = s[0].distance_to(s[1]); - if (d == 0.0) { - blend_weights[j] = 1.0; - blend_weights[(j + 1) % 3] = 0.0; - blend_weights[(j + 2) % 3] = 0.0; - } else { - float c = s[0].distance_to(closest) / d; - - blend_weights[j] = 1.0 - c; - blend_weights[(j + 1) % 3] = c; - blend_weights[(j + 2) % 3] = 0.0; - } - } - } - } - - ERR_FAIL_COND_V(blend_triangle == -1, 0); //should never reach here - - int triangle_points[3]; - for (int j = 0; j < 3; j++) { - triangle_points[j] = get_triangle_point(blend_triangle, j); - } - - first = true; - float mind; - for (int i = 0; i < blend_points_used; i++) { - - bool found = false; - for (int j = 0; j < 3; j++) { - if (i == triangle_points[j]) { - //blend with the given weight - float t = blend_node(blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false); - if (first || t < mind) { - mind = t; - first = false; - } - found = true; - break; - } - } - - if (!found) { - //ignore - blend_node(blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false); - } - } - return mind; -} - -String AnimationNodeBlendSpace2D::get_caption() const { - return "BlendSpace2D"; -} - -void AnimationNodeBlendSpace2D::_validate_property(PropertyInfo &property) const { - if (property.name.begins_with("blend_point_")) { - String left = property.name.get_slicec('/', 0); - int idx = left.get_slicec('_', 2).to_int(); - if (idx >= blend_points_used) { - property.usage = 0; - } - } - AnimationRootNode::_validate_property(property); -} - -void AnimationNodeBlendSpace2D::set_auto_triangles(bool p_enable) { - auto_triangles = p_enable; - if (auto_triangles) { - trianges_dirty = true; - } -} - -bool AnimationNodeBlendSpace2D::get_auto_triangles() const { - return auto_triangles; -} - -void AnimationNodeBlendSpace2D::_bind_methods() { - - ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace2D::set_blend_point_position); - ClassDB::bind_method(D_METHOD("get_blend_point_position", "point"), &AnimationNodeBlendSpace2D::get_blend_point_position); - ClassDB::bind_method(D_METHOD("set_blend_point_node", "point", "node"), &AnimationNodeBlendSpace2D::set_blend_point_node); - ClassDB::bind_method(D_METHOD("get_blend_point_node", "point"), &AnimationNodeBlendSpace2D::get_blend_point_node); - ClassDB::bind_method(D_METHOD("remove_blend_point", "point"), &AnimationNodeBlendSpace2D::remove_blend_point); - ClassDB::bind_method(D_METHOD("get_blend_point_count"), &AnimationNodeBlendSpace2D::get_blend_point_count); - - ClassDB::bind_method(D_METHOD("add_triangle", "x", "y", "z", "at_index"), &AnimationNodeBlendSpace2D::add_triangle, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("get_triangle_point", "triangle", "point"), &AnimationNodeBlendSpace2D::get_triangle_point); - ClassDB::bind_method(D_METHOD("remove_triangle", "triangle"), &AnimationNodeBlendSpace2D::remove_triangle); - ClassDB::bind_method(D_METHOD("get_triangle_count"), &AnimationNodeBlendSpace2D::get_triangle_count); - - ClassDB::bind_method(D_METHOD("set_min_space", "min_space"), &AnimationNodeBlendSpace2D::set_min_space); - ClassDB::bind_method(D_METHOD("get_min_space"), &AnimationNodeBlendSpace2D::get_min_space); - - ClassDB::bind_method(D_METHOD("set_max_space", "max_space"), &AnimationNodeBlendSpace2D::set_max_space); - ClassDB::bind_method(D_METHOD("get_max_space"), &AnimationNodeBlendSpace2D::get_max_space); - - ClassDB::bind_method(D_METHOD("set_snap", "snap"), &AnimationNodeBlendSpace2D::set_snap); - ClassDB::bind_method(D_METHOD("get_snap"), &AnimationNodeBlendSpace2D::get_snap); - - ClassDB::bind_method(D_METHOD("set_blend_position", "pos"), &AnimationNodeBlendSpace2D::set_blend_position); - ClassDB::bind_method(D_METHOD("get_blend_position"), &AnimationNodeBlendSpace2D::get_blend_position); - - ClassDB::bind_method(D_METHOD("set_x_label", "text"), &AnimationNodeBlendSpace2D::set_x_label); - ClassDB::bind_method(D_METHOD("get_x_label"), &AnimationNodeBlendSpace2D::get_x_label); - - ClassDB::bind_method(D_METHOD("set_y_label", "text"), &AnimationNodeBlendSpace2D::set_y_label); - ClassDB::bind_method(D_METHOD("get_y_label"), &AnimationNodeBlendSpace2D::get_y_label); - - ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace2D::_add_blend_point); - - ClassDB::bind_method(D_METHOD("_set_triangles", "triangles"), &AnimationNodeBlendSpace2D::_set_triangles); - ClassDB::bind_method(D_METHOD("_get_triangles"), &AnimationNodeBlendSpace2D::_get_triangles); - - ClassDB::bind_method(D_METHOD("set_auto_triangles", "enable"), &AnimationNodeBlendSpace2D::set_auto_triangles); - ClassDB::bind_method(D_METHOD("get_auto_triangles"), &AnimationNodeBlendSpace2D::get_auto_triangles); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_auto_triangles", "get_auto_triangles"); - - for (int i = 0; i < MAX_BLEND_POINTS; i++) { - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "_add_blend_point", "get_blend_point_node", i); - ADD_PROPERTYI(PropertyInfo(Variant::VECTOR2, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i); - } - - ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_triangles", "_get_triangles"); - - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "blend_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_blend_position", "get_blend_position"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_x_label", "get_x_label"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_y_label", "get_y_label"); -} - -AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() { - - auto_triangles = true; - blend_points_used = 0; - max_space = Vector2(1, 1); - min_space = Vector2(-1, -1); - snap = Vector2(0.1, 0.1); - x_label = "x"; - y_label = "y"; - trianges_dirty = false; -} - -AnimationNodeBlendSpace2D::~AnimationNodeBlendSpace2D() { - - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_parent(this); - blend_points[i].node->set_graph_player(get_graph_player()); - } -} diff --git a/scene/animation/animation_blend_space2d.h b/scene/animation/animation_blend_space2d.h deleted file mode 100644 index 9c992578d6..0000000000 --- a/scene/animation/animation_blend_space2d.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef ANIMATION_BLEND_SPACE2D_H -#define ANIMATION_BLEND_SPACE2D_H - -#include "scene/animation/animation_graph_player.h" - -class AnimationNodeBlendSpace2D : public AnimationRootNode { - GDCLASS(AnimationNodeBlendSpace2D, AnimationRootNode) - - enum { - MAX_BLEND_POINTS = 64 - }; - - struct BlendPoint { - Ref node; - Vector2 position; - }; - - BlendPoint blend_points[MAX_BLEND_POINTS]; - int blend_points_used; - - struct BlendTriangle { - int points[3]; - }; - - Vector triangles; - - Vector2 blend_pos; - Vector2 max_space; - Vector2 min_space; - Vector2 snap; - String x_label; - String y_label; - - void _add_blend_point(int p_index, const Ref &p_node); - void _set_triangles(const Vector &p_triangles); - Vector _get_triangles() const; - - void _blend_triangle(const Vector2 &p_pos, const Vector2 *p_points, float *r_weights); - - bool auto_triangles; - bool trianges_dirty; - - void _update_triangles(); - -protected: - virtual void _validate_property(PropertyInfo &property) const; - static void _bind_methods(); - -public: - void add_blend_point(const Ref &p_node, const Vector2 &p_position, int p_at_index = -1); - void set_blend_point_position(int p_point, const Vector2 &p_position); - void set_blend_point_node(int p_point, const Ref &p_node); - Vector2 get_blend_point_position(int p_point) const; - Ref get_blend_point_node(int p_point) const; - void remove_blend_point(int p_point); - int get_blend_point_count() const; - - bool has_triangle(int p_x, int p_y, int p_z) const; - void add_triangle(int p_x, int p_y, int p_z, int p_at_index = -1); - int get_triangle_point(int p_triangle, int p_point); - void remove_triangle(int p_triangle); - int get_triangle_count() const; - - void set_min_space(const Vector2 &p_min); - Vector2 get_min_space() const; - - void set_max_space(const Vector2 &p_max); - Vector2 get_max_space() const; - - void set_snap(const Vector2 &p_snap); - Vector2 get_snap() const; - - void set_blend_position(const Vector2 &p_pos); - Vector2 get_blend_position() const; - - void set_x_label(const String &p_label); - String get_x_label() const; - - void set_y_label(const String &p_label); - String get_y_label() const; - - virtual float process(float p_time, bool p_seek); - virtual String get_caption() const; - - Vector2 get_closest_point(const Vector2 &p_point); - - void set_auto_triangles(bool p_enable); - bool get_auto_triangles() const; - - AnimationNodeBlendSpace2D(); - ~AnimationNodeBlendSpace2D(); -}; - -#endif // ANIMATION_BLEND_SPACE_H diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp new file mode 100644 index 0000000000..6f659cc2bb --- /dev/null +++ b/scene/animation/animation_blend_space_1d.cpp @@ -0,0 +1,272 @@ +#include "animation_blend_space_1d.h" + +void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const { + if (property.name.begins_with("blend_point_")) { + String left = property.name.get_slicec('/', 0); + int idx = left.get_slicec('_', 2).to_int(); + if (idx >= blend_points_used) { + property.usage = 0; + } + } + AnimationRootNode::_validate_property(property); +} + +void AnimationNodeBlendSpace1D::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace1D::add_blend_point, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace1D::set_blend_point_position); + ClassDB::bind_method(D_METHOD("get_blend_point_position", "point"), &AnimationNodeBlendSpace1D::get_blend_point_position); + ClassDB::bind_method(D_METHOD("set_blend_point_node", "point", "node"), &AnimationNodeBlendSpace1D::set_blend_point_node); + ClassDB::bind_method(D_METHOD("get_blend_point_node", "point"), &AnimationNodeBlendSpace1D::get_blend_point_node); + ClassDB::bind_method(D_METHOD("remove_blend_point", "point"), &AnimationNodeBlendSpace1D::remove_blend_point); + ClassDB::bind_method(D_METHOD("get_blend_point_count"), &AnimationNodeBlendSpace1D::get_blend_point_count); + + ClassDB::bind_method(D_METHOD("set_min_space", "min_space"), &AnimationNodeBlendSpace1D::set_min_space); + ClassDB::bind_method(D_METHOD("get_min_space"), &AnimationNodeBlendSpace1D::get_min_space); + + ClassDB::bind_method(D_METHOD("set_max_space", "max_space"), &AnimationNodeBlendSpace1D::set_max_space); + ClassDB::bind_method(D_METHOD("get_max_space"), &AnimationNodeBlendSpace1D::get_max_space); + + ClassDB::bind_method(D_METHOD("set_snap", "snap"), &AnimationNodeBlendSpace1D::set_snap); + ClassDB::bind_method(D_METHOD("get_snap"), &AnimationNodeBlendSpace1D::get_snap); + + ClassDB::bind_method(D_METHOD("set_blend_pos", "pos"), &AnimationNodeBlendSpace1D::set_blend_pos); + ClassDB::bind_method(D_METHOD("get_blend_pos"), &AnimationNodeBlendSpace1D::get_blend_pos); + + ClassDB::bind_method(D_METHOD("set_value_label", "text"), &AnimationNodeBlendSpace1D::set_value_label); + ClassDB::bind_method(D_METHOD("get_value_label"), &AnimationNodeBlendSpace1D::get_value_label); + + ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace1D::_add_blend_point); + + for (int i = 0; i < MAX_BLEND_POINTS; i++) { + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "_add_blend_point", "get_blend_point_node", i); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i); + } + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "blend_pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_blend_pos", "get_blend_pos"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_value_label", "get_value_label"); +} + +void AnimationNodeBlendSpace1D::add_blend_point(const Ref &p_node, float p_position, int p_at_index) { + ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); + ERR_FAIL_COND(p_node.is_null()); + ERR_FAIL_COND(p_node->get_parent().is_valid()); + ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used); + + if (p_at_index == -1 || p_at_index == blend_points_used) { + p_at_index = blend_points_used; + } else { + for (int i = blend_points_used - 1; i > p_at_index; i++) { + blend_points[i] = blend_points[i - 1]; + } + } + + blend_points[p_at_index].node = p_node; + blend_points[p_at_index].position = p_position; + + blend_points[p_at_index].node->set_parent(this); + blend_points[p_at_index].node->set_graph_player(get_graph_player()); + + blend_points_used++; +} + +void AnimationNodeBlendSpace1D::set_blend_point_position(int p_point, float p_position) { + ERR_FAIL_INDEX(p_point, blend_points_used); + + blend_points[p_point].position = p_position; +} + +void AnimationNodeBlendSpace1D::set_blend_point_node(int p_point, const Ref &p_node) { + ERR_FAIL_INDEX(p_point, blend_points_used); + ERR_FAIL_COND(p_node.is_null()); + + if (blend_points[p_point].node.is_valid()) { + blend_points[p_point].node->set_parent(NULL); + blend_points[p_point].node->set_graph_player(NULL); + } + + blend_points[p_point].node = p_node; + blend_points[p_point].node->set_parent(this); + blend_points[p_point].node->set_graph_player(get_graph_player()); +} + +float AnimationNodeBlendSpace1D::get_blend_point_position(int p_point) const { + ERR_FAIL_INDEX_V(p_point, blend_points_used, 0); + return blend_points[p_point].position; +} + +Ref AnimationNodeBlendSpace1D::get_blend_point_node(int p_point) const { + ERR_FAIL_INDEX_V(p_point, blend_points_used, Ref()); + return blend_points[p_point].node; +} + +void AnimationNodeBlendSpace1D::remove_blend_point(int p_point) { + ERR_FAIL_INDEX(p_point, blend_points_used); + + blend_points[p_point].node->set_parent(NULL); + blend_points[p_point].node->set_graph_player(NULL); + + for (int i = p_point; i < blend_points_used - 1; i++) { + blend_points[i] = blend_points[i + 1]; + } + + blend_points_used--; +} + +int AnimationNodeBlendSpace1D::get_blend_point_count() const { + + return blend_points_used; +} + +void AnimationNodeBlendSpace1D::set_min_space(float p_min) { + min_space = p_min; + + if (min_space >= max_space) { + min_space = max_space - 1; + } +} + +float AnimationNodeBlendSpace1D::get_min_space() const { + return min_space; +} + +void AnimationNodeBlendSpace1D::set_max_space(float p_max) { + max_space = p_max; + + if (max_space <= min_space) { + max_space = min_space + 1; + } +} + +float AnimationNodeBlendSpace1D::get_max_space() const { + return max_space; +} + +void AnimationNodeBlendSpace1D::set_snap(float p_snap) { + snap = p_snap; +} + +float AnimationNodeBlendSpace1D::get_snap() const { + return snap; +} + +void AnimationNodeBlendSpace1D::set_blend_pos(float p_pos) { + blend_pos = p_pos; +} + +float AnimationNodeBlendSpace1D::get_blend_pos() const { + return blend_pos; +} + +void AnimationNodeBlendSpace1D::set_value_label(const String &p_label) { + value_label = p_label; +} + +String AnimationNodeBlendSpace1D::get_value_label() const { + return value_label; +} + +void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref &p_node) { + if (p_index == blend_points_used) { + add_blend_point(p_node, 0); + } else { + set_blend_point_node(p_index, p_node); + } +} + +float AnimationNodeBlendSpace1D::process(float p_time, bool p_seek) { + + if (blend_points_used == 0) { + return 0.0; + } + + if (blend_points_used == 1) { + // only one point available, just play that animation + return blend_node(blend_points[0].node, p_time, p_seek, 1.0, FILTER_IGNORE, false); + } + + int point_lower = -1; + float pos_lower = 0.0; + int point_higher = -1; + float pos_higher = 0.0; + + // find the closest two points to blend between + for (int i = 0; i < blend_points_used; i++) { + + float pos = blend_points[i].position; + + if (pos <= blend_pos) { + if (point_lower == -1) { + point_lower = i; + pos_lower = pos; + } else if ((blend_pos - pos) < (blend_pos - pos_lower)) { + point_lower = i; + pos_lower = pos; + } + } else { + if (point_higher == -1) { + point_higher = i; + pos_higher = pos; + } else if ((pos - blend_pos) < (pos_higher - blend_pos)) { + point_higher = i; + pos_higher = pos; + } + } + } + + if (point_lower == -1) { + // we are on the left side, no other point to the left + // we just play the next point. + + return blend_node(blend_points[point_higher].node, p_time, p_seek, 1.0, FILTER_IGNORE, false); + } else if (point_higher == -1) { + // we are on the right side, no other point to the right + // we just play the previous point + return blend_node(blend_points[point_lower].node, p_time, p_seek, 1.0, FILTER_IGNORE, false); + } else { + + //w we are between two points. + // figure out weights, then blend the animations + + float distance_between_points = pos_higher - pos_lower; + + float current_pos_inbetween = blend_pos - pos_lower; + + float blend_percentage = current_pos_inbetween / distance_between_points; + + float blend_lower = 1.0 - blend_percentage; + float blend_higher = blend_percentage; + + float time_remaining_lower = 0.0; + float time_remaining_higher = 0.0; + + time_remaining_lower = blend_node(blend_points[point_lower].node, p_time, p_seek, blend_lower, FILTER_IGNORE, false); + time_remaining_higher = blend_node(blend_points[point_higher].node, p_time, p_seek, blend_higher, FILTER_IGNORE, false); + + return MAX(time_remaining_lower, time_remaining_higher); + } +} + +String AnimationNodeBlendSpace1D::get_caption() const { + return "BlendSpace1D"; +} + +AnimationNodeBlendSpace1D::AnimationNodeBlendSpace1D() { + + blend_points_used = 0; + max_space = 1; + min_space = -1; + + snap = 0.1; + value_label = "value"; +} + +AnimationNodeBlendSpace1D::~AnimationNodeBlendSpace1D() { + + for (int i = 0; i < blend_points_used; i++) { + blend_points[i].node->set_parent(this); + blend_points[i].node->set_graph_player(get_graph_player()); + } +} diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h new file mode 100644 index 0000000000..447c948e93 --- /dev/null +++ b/scene/animation/animation_blend_space_1d.h @@ -0,0 +1,68 @@ +#ifndef ANIMATION_BLEND_SPACE_1D_H +#define ANIMATION_BLEND_SPACE_1D_H + +#include "scene/animation/animation_graph_player.h" + +class AnimationNodeBlendSpace1D : public AnimationRootNode { + GDCLASS(AnimationNodeBlendSpace1D, AnimationRootNode) + + enum { + MAX_BLEND_POINTS = 64 + }; + + struct BlendPoint { + Ref node; + float position; + }; + + BlendPoint blend_points[MAX_BLEND_POINTS]; + int blend_points_used; + + float blend_pos; + + float max_space; + float min_space; + + float snap; + + String value_label; + + void _add_blend_point(int p_index, const Ref &p_node); + +protected: + virtual void _validate_property(PropertyInfo &property) const; + static void _bind_methods(); + +public: + void add_blend_point(const Ref &p_node, float p_position, int p_at_index = -1); + void set_blend_point_position(int p_point, float p_position); + void set_blend_point_node(int p_point, const Ref &p_node); + + float get_blend_point_position(int p_point) const; + Ref get_blend_point_node(int p_point) const; + void remove_blend_point(int p_point); + int get_blend_point_count() const; + + void set_min_space(float p_min); + float get_min_space() const; + + void set_max_space(float p_max); + float get_max_space() const; + + void set_snap(float p_snap); + float get_snap() const; + + void set_blend_pos(float p_pos); + float get_blend_pos() const; + + void set_value_label(const String &p_label); + String get_value_label() const; + + float process(float p_time, bool p_seek); + String get_caption() const; + + AnimationNodeBlendSpace1D(); + ~AnimationNodeBlendSpace1D(); +}; + +#endif // ANIMATION_BLEND_SPACE_1D_H diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp new file mode 100644 index 0000000000..675285b075 --- /dev/null +++ b/scene/animation/animation_blend_space_2d.cpp @@ -0,0 +1,558 @@ +#include "animation_blend_space_2d.h" +#include "math/delaunay.h" + +void AnimationNodeBlendSpace2D::add_blend_point(const Ref &p_node, const Vector2 &p_position, int p_at_index) { + ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); + ERR_FAIL_COND(p_node.is_null()); + ERR_FAIL_COND(p_node->get_parent().is_valid()); + ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used); + + if (p_at_index == -1 || p_at_index == blend_points_used) { + p_at_index = blend_points_used; + } else { + for (int i = blend_points_used - 1; i > p_at_index; i--) { + blend_points[i] = blend_points[i - 1]; + } + for (int i = 0; i < triangles.size(); i++) { + for (int j = 0; j < 3; j++) { + if (triangles[i].points[j] >= p_at_index) { + triangles[i].points[j]++; + } + } + } + } + blend_points[p_at_index].node = p_node; + blend_points[p_at_index].position = p_position; + + blend_points[p_at_index].node->set_parent(this); + blend_points[p_at_index].node->set_graph_player(get_graph_player()); + blend_points_used++; + + if (auto_triangles) { + trianges_dirty = true; + } +} + +void AnimationNodeBlendSpace2D::set_blend_point_position(int p_point, const Vector2 &p_position) { + ERR_FAIL_INDEX(p_point, blend_points_used); + blend_points[p_point].position = p_position; + if (auto_triangles) { + trianges_dirty = true; + } +} +void AnimationNodeBlendSpace2D::set_blend_point_node(int p_point, const Ref &p_node) { + ERR_FAIL_INDEX(p_point, blend_points_used); + ERR_FAIL_COND(p_node.is_null()); + + if (blend_points[p_point].node.is_valid()) { + blend_points[p_point].node->set_parent(NULL); + blend_points[p_point].node->set_graph_player(NULL); + } + blend_points[p_point].node = p_node; + blend_points[p_point].node->set_parent(this); + blend_points[p_point].node->set_graph_player(get_graph_player()); +} +Vector2 AnimationNodeBlendSpace2D::get_blend_point_position(int p_point) const { + ERR_FAIL_INDEX_V(p_point, blend_points_used, Vector2()); + return blend_points[p_point].position; +} +Ref AnimationNodeBlendSpace2D::get_blend_point_node(int p_point) const { + ERR_FAIL_INDEX_V(p_point, blend_points_used, Ref()); + return blend_points[p_point].node; +} +void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) { + ERR_FAIL_INDEX(p_point, blend_points_used); + + blend_points[p_point].node->set_parent(NULL); + blend_points[p_point].node->set_graph_player(NULL); + + for (int i = 0; i < triangles.size(); i++) { + bool erase = false; + for (int j = 0; j < 3; j++) { + if (triangles[i].points[j] == p_point) { + erase = true; + break; + } else if (triangles[i].points[j] > p_point) { + triangles[i].points[j]--; + } + } + if (erase) { + triangles.remove(i); + + i--; + } + } + + for (int i = p_point; i < blend_points_used - 1; i++) { + blend_points[i] = blend_points[i + 1]; + } + blend_points_used--; +} + +int AnimationNodeBlendSpace2D::get_blend_point_count() const { + + return blend_points_used; +} + +bool AnimationNodeBlendSpace2D::has_triangle(int p_x, int p_y, int p_z) const { + + ERR_FAIL_INDEX_V(p_x, blend_points_used, false); + ERR_FAIL_INDEX_V(p_y, blend_points_used, false); + ERR_FAIL_INDEX_V(p_z, blend_points_used, false); + + BlendTriangle t; + t.points[0] = p_x; + t.points[1] = p_y; + t.points[2] = p_z; + + SortArray sort; + sort.sort(t.points, 3); + + for (int i = 0; i < triangles.size(); i++) { + bool all_equal = true; + for (int j = 0; j < 3; j++) { + if (triangles[i].points[j] != t.points[j]) { + all_equal = false; + break; + } + } + if (all_equal) + return true; + } + + return false; +} + +void AnimationNodeBlendSpace2D::add_triangle(int p_x, int p_y, int p_z, int p_at_index) { + + ERR_FAIL_INDEX(p_x, blend_points_used); + ERR_FAIL_INDEX(p_y, blend_points_used); + ERR_FAIL_INDEX(p_z, blend_points_used); + + _update_triangles(); + + BlendTriangle t; + t.points[0] = p_x; + t.points[1] = p_y; + t.points[2] = p_z; + + SortArray sort; + sort.sort(t.points, 3); + + for (int i = 0; i < triangles.size(); i++) { + bool all_equal = true; + for (int j = 0; j < 3; j++) { + if (triangles[i].points[j] != t.points[j]) { + all_equal = false; + break; + } + } + ERR_FAIL_COND(all_equal); + } + + if (p_at_index == -1 || p_at_index == triangles.size()) { + triangles.push_back(t); + } else { + triangles.insert(p_at_index, t); + } +} +int AnimationNodeBlendSpace2D::get_triangle_point(int p_triangle, int p_point) { + + _update_triangles(); + + ERR_FAIL_INDEX_V(p_point, 3, -1); + ERR_FAIL_INDEX_V(p_triangle, triangles.size(), -1); + return triangles[p_triangle].points[p_point]; +} +void AnimationNodeBlendSpace2D::remove_triangle(int p_triangle) { + ERR_FAIL_INDEX(p_triangle, triangles.size()); + + triangles.remove(p_triangle); +} + +int AnimationNodeBlendSpace2D::get_triangle_count() const { + return triangles.size(); +} + +void AnimationNodeBlendSpace2D::set_min_space(const Vector2 &p_min) { + + min_space = p_min; + if (min_space.x >= max_space.x) { + min_space.x = max_space.x - 1; + } + if (min_space.y >= max_space.y) { + min_space.y = max_space.y - 1; + } +} +Vector2 AnimationNodeBlendSpace2D::get_min_space() const { + return min_space; +} + +void AnimationNodeBlendSpace2D::set_max_space(const Vector2 &p_max) { + + max_space = p_max; + if (max_space.x <= min_space.x) { + max_space.x = min_space.x + 1; + } + if (max_space.y <= min_space.y) { + max_space.y = min_space.y + 1; + } +} +Vector2 AnimationNodeBlendSpace2D::get_max_space() const { + return max_space; +} + +void AnimationNodeBlendSpace2D::set_snap(const Vector2 &p_snap) { + snap = p_snap; +} +Vector2 AnimationNodeBlendSpace2D::get_snap() const { + return snap; +} + +void AnimationNodeBlendSpace2D::set_blend_position(const Vector2 &p_pos) { + blend_pos = p_pos; +} +Vector2 AnimationNodeBlendSpace2D::get_blend_position() const { + return blend_pos; +} + +void AnimationNodeBlendSpace2D::set_x_label(const String &p_label) { + x_label = p_label; +} +String AnimationNodeBlendSpace2D::get_x_label() const { + return x_label; +} + +void AnimationNodeBlendSpace2D::set_y_label(const String &p_label) { + y_label = p_label; +} +String AnimationNodeBlendSpace2D::get_y_label() const { + return y_label; +} + +void AnimationNodeBlendSpace2D::_add_blend_point(int p_index, const Ref &p_node) { + if (p_index == blend_points_used) { + add_blend_point(p_node, Vector2()); + } else { + set_blend_point_node(p_index, p_node); + } +} + +void AnimationNodeBlendSpace2D::_set_triangles(const Vector &p_triangles) { + + if (auto_triangles) + return; + ERR_FAIL_COND(p_triangles.size() % 3 != 0); + for (int i = 0; i < p_triangles.size(); i += 3) { + add_triangle(p_triangles[i + 0], p_triangles[i + 1], p_triangles[i + 2]); + } +} + +Vector AnimationNodeBlendSpace2D::_get_triangles() const { + + Vector t; + if (auto_triangles && trianges_dirty) + return t; + + t.resize(triangles.size() * 3); + for (int i = 0; i < triangles.size(); i++) { + t[i * 3 + 0] = triangles[i].points[0]; + t[i * 3 + 1] = triangles[i].points[1]; + t[i * 3 + 2] = triangles[i].points[2]; + } + return t; +} + +void AnimationNodeBlendSpace2D::_update_triangles() { + + if (!auto_triangles || !trianges_dirty) + return; + + trianges_dirty = false; + triangles.clear(); + if (blend_points_used < 3) + return; + + Vector points; + points.resize(blend_points_used); + for (int i = 0; i < blend_points_used; i++) { + points[i] = blend_points[i].position; + } + + Vector triangles = Delaunay2D::triangulate(points); + + for (int i = 0; i < triangles.size(); i++) { + add_triangle(triangles[i].points[0], triangles[i].points[1], triangles[i].points[2]); + } +} + +Vector2 AnimationNodeBlendSpace2D::get_closest_point(const Vector2 &p_point) { + + _update_triangles(); + + if (triangles.size() == 0) + return Vector2(); + + Vector2 best_point; + bool first = true; + + for (int i = 0; i < triangles.size(); i++) { + Vector2 points[3]; + for (int j = 0; j < 3; j++) { + points[j] = get_blend_point_position(get_triangle_point(i, j)); + } + + if (Geometry::is_point_in_triangle(p_point, points[0], points[1], points[2])) { + + return p_point; + } + + for (int j = 0; j < 3; j++) { + Vector2 s[2] = { + points[j], + points[(j + 1) % 3] + }; + Vector2 closest = Geometry::get_closest_point_to_segment_2d(p_point, s); + if (first || closest.distance_to(p_point) < best_point.distance_to(p_point)) { + best_point = closest; + first = false; + } + } + } + + return best_point; +} + +void AnimationNodeBlendSpace2D::_blend_triangle(const Vector2 &p_pos, const Vector2 *p_points, float *r_weights) { + + if (p_pos.distance_squared_to(p_points[0]) < CMP_EPSILON2) { + r_weights[0] = 1; + r_weights[1] = 0; + r_weights[2] = 0; + return; + } + if (p_pos.distance_squared_to(p_points[1]) < CMP_EPSILON2) { + r_weights[0] = 0; + r_weights[1] = 1; + r_weights[2] = 0; + return; + } + if (p_pos.distance_squared_to(p_points[2]) < CMP_EPSILON2) { + r_weights[0] = 0; + r_weights[1] = 0; + r_weights[2] = 1; + return; + } + + Vector2 v0 = p_points[1] - p_points[0]; + Vector2 v1 = p_points[2] - p_points[0]; + Vector2 v2 = p_pos - p_points[0]; + + float d00 = v0.dot(v0); + float d01 = v0.dot(v1); + float d11 = v1.dot(v1); + float d20 = v2.dot(v0); + float d21 = v2.dot(v1); + float denom = (d00 * d11 - d01 * d01); + if (denom == 0) { + r_weights[0] = 1; + r_weights[1] = 0; + r_weights[2] = 0; + return; + } + float v = (d11 * d20 - d01 * d21) / denom; + float w = (d00 * d21 - d01 * d20) / denom; + float u = 1.0f - v - w; + + r_weights[0] = u; + r_weights[1] = v; + r_weights[2] = w; +} + +float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { + + _update_triangles(); + + if (triangles.size() == 0) + return 0; + + Vector2 best_point; + bool first = true; + int blend_triangle = -1; + float blend_weights[3] = { 0, 0, 0 }; + + for (int i = 0; i < triangles.size(); i++) { + Vector2 points[3]; + for (int j = 0; j < 3; j++) { + points[j] = get_blend_point_position(get_triangle_point(i, j)); + } + + if (Geometry::is_point_in_triangle(blend_pos, points[0], points[1], points[2])) { + + blend_triangle = i; + _blend_triangle(blend_pos, points, blend_weights); + break; + } + + for (int j = 0; j < 3; j++) { + Vector2 s[2] = { + points[j], + points[(j + 1) % 3] + }; + Vector2 closest = Geometry::get_closest_point_to_segment_2d(blend_pos, s); + if (first || closest.distance_to(blend_pos) < best_point.distance_to(blend_pos)) { + best_point = closest; + blend_triangle = i; + first = false; + float d = s[0].distance_to(s[1]); + if (d == 0.0) { + blend_weights[j] = 1.0; + blend_weights[(j + 1) % 3] = 0.0; + blend_weights[(j + 2) % 3] = 0.0; + } else { + float c = s[0].distance_to(closest) / d; + + blend_weights[j] = 1.0 - c; + blend_weights[(j + 1) % 3] = c; + blend_weights[(j + 2) % 3] = 0.0; + } + } + } + } + + ERR_FAIL_COND_V(blend_triangle == -1, 0); //should never reach here + + int triangle_points[3]; + for (int j = 0; j < 3; j++) { + triangle_points[j] = get_triangle_point(blend_triangle, j); + } + + first = true; + float mind; + for (int i = 0; i < blend_points_used; i++) { + + bool found = false; + for (int j = 0; j < 3; j++) { + if (i == triangle_points[j]) { + //blend with the given weight + float t = blend_node(blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false); + if (first || t < mind) { + mind = t; + first = false; + } + found = true; + break; + } + } + + if (!found) { + //ignore + blend_node(blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false); + } + } + return mind; +} + +String AnimationNodeBlendSpace2D::get_caption() const { + return "BlendSpace2D"; +} + +void AnimationNodeBlendSpace2D::_validate_property(PropertyInfo &property) const { + if (property.name.begins_with("blend_point_")) { + String left = property.name.get_slicec('/', 0); + int idx = left.get_slicec('_', 2).to_int(); + if (idx >= blend_points_used) { + property.usage = 0; + } + } + AnimationRootNode::_validate_property(property); +} + +void AnimationNodeBlendSpace2D::set_auto_triangles(bool p_enable) { + auto_triangles = p_enable; + if (auto_triangles) { + trianges_dirty = true; + } +} + +bool AnimationNodeBlendSpace2D::get_auto_triangles() const { + return auto_triangles; +} + +void AnimationNodeBlendSpace2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace2D::set_blend_point_position); + ClassDB::bind_method(D_METHOD("get_blend_point_position", "point"), &AnimationNodeBlendSpace2D::get_blend_point_position); + ClassDB::bind_method(D_METHOD("set_blend_point_node", "point", "node"), &AnimationNodeBlendSpace2D::set_blend_point_node); + ClassDB::bind_method(D_METHOD("get_blend_point_node", "point"), &AnimationNodeBlendSpace2D::get_blend_point_node); + ClassDB::bind_method(D_METHOD("remove_blend_point", "point"), &AnimationNodeBlendSpace2D::remove_blend_point); + ClassDB::bind_method(D_METHOD("get_blend_point_count"), &AnimationNodeBlendSpace2D::get_blend_point_count); + + ClassDB::bind_method(D_METHOD("add_triangle", "x", "y", "z", "at_index"), &AnimationNodeBlendSpace2D::add_triangle, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_triangle_point", "triangle", "point"), &AnimationNodeBlendSpace2D::get_triangle_point); + ClassDB::bind_method(D_METHOD("remove_triangle", "triangle"), &AnimationNodeBlendSpace2D::remove_triangle); + ClassDB::bind_method(D_METHOD("get_triangle_count"), &AnimationNodeBlendSpace2D::get_triangle_count); + + ClassDB::bind_method(D_METHOD("set_min_space", "min_space"), &AnimationNodeBlendSpace2D::set_min_space); + ClassDB::bind_method(D_METHOD("get_min_space"), &AnimationNodeBlendSpace2D::get_min_space); + + ClassDB::bind_method(D_METHOD("set_max_space", "max_space"), &AnimationNodeBlendSpace2D::set_max_space); + ClassDB::bind_method(D_METHOD("get_max_space"), &AnimationNodeBlendSpace2D::get_max_space); + + ClassDB::bind_method(D_METHOD("set_snap", "snap"), &AnimationNodeBlendSpace2D::set_snap); + ClassDB::bind_method(D_METHOD("get_snap"), &AnimationNodeBlendSpace2D::get_snap); + + ClassDB::bind_method(D_METHOD("set_blend_position", "pos"), &AnimationNodeBlendSpace2D::set_blend_position); + ClassDB::bind_method(D_METHOD("get_blend_position"), &AnimationNodeBlendSpace2D::get_blend_position); + + ClassDB::bind_method(D_METHOD("set_x_label", "text"), &AnimationNodeBlendSpace2D::set_x_label); + ClassDB::bind_method(D_METHOD("get_x_label"), &AnimationNodeBlendSpace2D::get_x_label); + + ClassDB::bind_method(D_METHOD("set_y_label", "text"), &AnimationNodeBlendSpace2D::set_y_label); + ClassDB::bind_method(D_METHOD("get_y_label"), &AnimationNodeBlendSpace2D::get_y_label); + + ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace2D::_add_blend_point); + + ClassDB::bind_method(D_METHOD("_set_triangles", "triangles"), &AnimationNodeBlendSpace2D::_set_triangles); + ClassDB::bind_method(D_METHOD("_get_triangles"), &AnimationNodeBlendSpace2D::_get_triangles); + + ClassDB::bind_method(D_METHOD("set_auto_triangles", "enable"), &AnimationNodeBlendSpace2D::set_auto_triangles); + ClassDB::bind_method(D_METHOD("get_auto_triangles"), &AnimationNodeBlendSpace2D::get_auto_triangles); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_auto_triangles", "get_auto_triangles"); + + for (int i = 0; i < MAX_BLEND_POINTS; i++) { + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "_add_blend_point", "get_blend_point_node", i); + ADD_PROPERTYI(PropertyInfo(Variant::VECTOR2, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i); + } + + ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_triangles", "_get_triangles"); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "blend_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_blend_position", "get_blend_position"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_x_label", "get_x_label"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_y_label", "get_y_label"); +} + +AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() { + + auto_triangles = true; + blend_points_used = 0; + max_space = Vector2(1, 1); + min_space = Vector2(-1, -1); + snap = Vector2(0.1, 0.1); + x_label = "x"; + y_label = "y"; + trianges_dirty = false; +} + +AnimationNodeBlendSpace2D::~AnimationNodeBlendSpace2D() { + + for (int i = 0; i < blend_points_used; i++) { + blend_points[i].node->set_parent(this); + blend_points[i].node->set_graph_player(get_graph_player()); + } +} diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h new file mode 100644 index 0000000000..f8f2440088 --- /dev/null +++ b/scene/animation/animation_blend_space_2d.h @@ -0,0 +1,94 @@ +#ifndef ANIMATION_BLEND_SPACE_2D_H +#define ANIMATION_BLEND_SPACE_2D_H + +#include "scene/animation/animation_graph_player.h" + +class AnimationNodeBlendSpace2D : public AnimationRootNode { + GDCLASS(AnimationNodeBlendSpace2D, AnimationRootNode) + + enum { + MAX_BLEND_POINTS = 64 + }; + + struct BlendPoint { + Ref node; + Vector2 position; + }; + + BlendPoint blend_points[MAX_BLEND_POINTS]; + int blend_points_used; + + struct BlendTriangle { + int points[3]; + }; + + Vector triangles; + + Vector2 blend_pos; + Vector2 max_space; + Vector2 min_space; + Vector2 snap; + String x_label; + String y_label; + + void _add_blend_point(int p_index, const Ref &p_node); + void _set_triangles(const Vector &p_triangles); + Vector _get_triangles() const; + + void _blend_triangle(const Vector2 &p_pos, const Vector2 *p_points, float *r_weights); + + bool auto_triangles; + bool trianges_dirty; + + void _update_triangles(); + +protected: + virtual void _validate_property(PropertyInfo &property) const; + static void _bind_methods(); + +public: + void add_blend_point(const Ref &p_node, const Vector2 &p_position, int p_at_index = -1); + void set_blend_point_position(int p_point, const Vector2 &p_position); + void set_blend_point_node(int p_point, const Ref &p_node); + Vector2 get_blend_point_position(int p_point) const; + Ref get_blend_point_node(int p_point) const; + void remove_blend_point(int p_point); + int get_blend_point_count() const; + + bool has_triangle(int p_x, int p_y, int p_z) const; + void add_triangle(int p_x, int p_y, int p_z, int p_at_index = -1); + int get_triangle_point(int p_triangle, int p_point); + void remove_triangle(int p_triangle); + int get_triangle_count() const; + + void set_min_space(const Vector2 &p_min); + Vector2 get_min_space() const; + + void set_max_space(const Vector2 &p_max); + Vector2 get_max_space() const; + + void set_snap(const Vector2 &p_snap); + Vector2 get_snap() const; + + void set_blend_position(const Vector2 &p_pos); + Vector2 get_blend_position() const; + + void set_x_label(const String &p_label); + String get_x_label() const; + + void set_y_label(const String &p_label); + String get_y_label() const; + + virtual float process(float p_time, bool p_seek); + virtual String get_caption() const; + + Vector2 get_closest_point(const Vector2 &p_point); + + void set_auto_triangles(bool p_enable); + bool get_auto_triangles() const; + + AnimationNodeBlendSpace2D(); + ~AnimationNodeBlendSpace2D(); +}; + +#endif // ANIMATION_BLEND_SPACE_2D_H diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 9fbdf675ec..e7cde8ddb9 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -63,8 +63,8 @@ #include "scene/2d/tile_map.h" #include "scene/2d/visibility_notifier_2d.h" #include "scene/2d/y_sort.h" -#include "scene/animation/animation_blend_space1d.h" -#include "scene/animation/animation_blend_space2d.h" +#include "scene/animation/animation_blend_space_1d.h" +#include "scene/animation/animation_blend_space_2d.h" #include "scene/animation/animation_blend_tree.h" #include "scene/animation/animation_graph_player.h" #include "scene/animation/animation_node_state_machine.h" -- cgit v1.2.3