Path: blob/master/editor/animation/animation_blend_space_2d_editor.cpp
21151 views
/**************************************************************************/1/* animation_blend_space_2d_editor.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "animation_blend_space_2d_editor.h"3132#include "core/io/resource_loader.h"33#include "core/math/geometry_2d.h"34#include "core/os/keyboard.h"35#include "editor/editor_node.h"36#include "editor/editor_string_names.h"37#include "editor/editor_undo_redo_manager.h"38#include "editor/gui/editor_file_dialog.h"39#include "editor/settings/editor_settings.h"40#include "editor/themes/editor_scale.h"41#include "scene/animation/animation_blend_tree.h"42#include "scene/animation/animation_player.h"43#include "scene/gui/button.h"44#include "scene/gui/check_box.h"45#include "scene/gui/grid_container.h"46#include "scene/gui/line_edit.h"47#include "scene/gui/menu_button.h"48#include "scene/gui/option_button.h"49#include "scene/gui/panel.h"50#include "scene/gui/panel_container.h"51#include "scene/gui/separator.h"52#include "scene/gui/spin_box.h"53#include "scene/main/window.h"5455bool AnimationNodeBlendSpace2DEditor::can_edit(const Ref<AnimationNode> &p_node) {56Ref<AnimationNodeBlendSpace2D> bs2d = p_node;57return bs2d.is_valid();58}5960void AnimationNodeBlendSpace2DEditor::_blend_space_changed() {61blend_space_draw->queue_redraw();62}6364void AnimationNodeBlendSpace2DEditor::edit(const Ref<AnimationNode> &p_node) {65if (blend_space.is_valid()) {66blend_space->disconnect("triangles_updated", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_blend_space_changed));67}68blend_space = p_node;69read_only = false;7071if (blend_space.is_valid()) {72read_only = EditorNode::get_singleton()->is_resource_read_only(blend_space);7374blend_space->connect("triangles_updated", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_blend_space_changed));75_update_space();76}7778tool_create->set_disabled(read_only);79max_x_value->set_editable(!read_only);80min_x_value->set_editable(!read_only);81max_y_value->set_editable(!read_only);82min_y_value->set_editable(!read_only);83label_x->set_editable(!read_only);84label_y->set_editable(!read_only);85edit_x->set_editable(!read_only);86edit_y->set_editable(!read_only);87tool_triangle->set_disabled(read_only);88auto_triangles->set_disabled(read_only);89sync->set_disabled(read_only);90interpolation->set_disabled(read_only);91}9293StringName AnimationNodeBlendSpace2DEditor::get_blend_position_path() const {94StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + "blend_position";95return path;96}9798void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) {99AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();100if (!tree) {101return;102}103104Ref<InputEventKey> k = p_event;105if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && !k->is_echo()) {106if (selected_point != -1 || selected_triangle != -1) {107if (!read_only) {108_erase_selected();109}110accept_event();111}112}113114Ref<InputEventMouseButton> mb = p_event;115116if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) || (mb->get_button_index() == MouseButton::LEFT && tool_create->is_pressed()))) {117if (!read_only) {118menu->clear(false);119animations_menu->clear();120animations_to_add.clear();121122LocalVector<StringName> classes;123ClassDB::get_inheriters_from_class("AnimationRootNode", classes);124classes.sort_custom<StringName::AlphCompare>();125126menu->add_submenu_node_item(TTR("Add Animation"), animations_menu);127128List<StringName> names;129tree->get_animation_list(&names);130for (const StringName &E : names) {131animations_menu->add_icon_item(get_editor_theme_icon(SNAME("Animation")), E);132animations_to_add.push_back(E);133}134135for (const StringName &E : classes) {136String name = String(E).replace_first("AnimationNode", "");137if (name == "Animation" || name == "StartState" || name == "EndState") {138continue; // nope139}140int idx = menu->get_item_count();141menu->add_item(vformat(TTR("Add %s"), name), idx);142menu->set_item_metadata(idx, E);143}144145Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard();146if (clipb.is_valid()) {147menu->add_separator();148menu->add_item(TTR("Paste"), MENU_PASTE);149}150menu->add_separator();151menu->add_item(TTR("Load..."), MENU_LOAD_FILE);152153menu->set_position(blend_space_draw->get_screen_position() + mb->get_position());154menu->reset_size();155menu->popup();156add_point_pos = (mb->get_position() / blend_space_draw->get_size());157add_point_pos.y = 1.0 - add_point_pos.y;158add_point_pos *= (blend_space->get_max_space() - blend_space->get_min_space());159add_point_pos += blend_space->get_min_space();160161if (snap->is_pressed()) {162add_point_pos = add_point_pos.snapped(blend_space->get_snap());163}164}165}166167if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && !mb->is_shift_pressed() && !mb->is_command_or_control_pressed() && mb->get_button_index() == MouseButton::LEFT) {168blend_space_draw->queue_redraw(); //update anyway169//try to see if a point can be selected170selected_point = -1;171selected_triangle = -1;172_update_tool_erase();173174for (int i = 0; i < points.size(); i++) {175if (points[i].distance_to(mb->get_position()) < 10 * EDSCALE) {176selected_point = i;177Ref<AnimationNode> node = blend_space->get_blend_point_node(i);178EditorNode::get_singleton()->push_item(node.ptr(), "", true);179180if (mb->is_double_click() && AnimationTreeEditor::get_singleton()->can_edit(node)) {181_open_editor();182return;183}184185dragging_selected_attempt = true;186drag_from = mb->get_position();187_update_tool_erase();188_update_edited_point_pos();189return;190}191}192193//then try to see if a triangle can be selected194if (!blend_space->get_auto_triangles()) { //if autotriangles use, disable this195for (int i = 0; i < blend_space->get_triangle_count(); i++) {196Vector<Vector2> triangle;197198for (int j = 0; j < 3; j++) {199int idx = blend_space->get_triangle_point(i, j);200ERR_FAIL_INDEX(idx, points.size());201triangle.push_back(points[idx]);202}203204if (Geometry2D::is_point_in_triangle(mb->get_position(), triangle[0], triangle[1], triangle[2])) {205selected_triangle = i;206_update_tool_erase();207return;208}209}210}211212// If no point or triangle was selected, select host BlendSpace2D node.213if (selected_point == -1 && selected_triangle == -1) {214EditorNode::get_singleton()->push_item(blend_space.ptr(), "", true);215}216}217218if (mb.is_valid() && mb->is_pressed() && tool_triangle->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {219blend_space_draw->queue_redraw(); //update anyway220//try to see if a point can be selected221selected_point = -1;222223for (int i = 0; i < points.size(); i++) {224if (making_triangle.has(i)) {225continue;226}227228if (points[i].distance_to(mb->get_position()) < 10 * EDSCALE) {229making_triangle.push_back(i);230if (making_triangle.size() == 3) {231//add triangle!232if (blend_space->has_triangle(making_triangle[0], making_triangle[1], making_triangle[2])) {233making_triangle.clear();234EditorNode::get_singleton()->show_warning(TTR("Triangle already exists."));235return;236}237238updating = true;239EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();240undo_redo->create_action(TTR("Add Triangle"));241undo_redo->add_do_method(blend_space.ptr(), "add_triangle", making_triangle[0], making_triangle[1], making_triangle[2]);242undo_redo->add_undo_method(blend_space.ptr(), "remove_triangle", blend_space->get_triangle_count());243undo_redo->add_do_method(this, "_update_space");244undo_redo->add_undo_method(this, "_update_space");245undo_redo->commit_action();246updating = false;247making_triangle.clear();248}249return;250}251}252}253254if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == MouseButton::LEFT) {255if (dragging_selected) {256//move257Vector2 point = blend_space->get_blend_point_position(selected_point);258point += drag_ofs;259if (snap->is_pressed()) {260point = point.snapped(blend_space->get_snap());261}262263if (!read_only) {264updating = true;265EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();266undo_redo->create_action(TTR("Move Node Point"));267undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point);268undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));269undo_redo->add_do_method(this, "_update_space");270undo_redo->add_undo_method(this, "_update_space");271undo_redo->add_do_method(this, "_update_edited_point_pos");272undo_redo->add_undo_method(this, "_update_edited_point_pos");273undo_redo->commit_action();274updating = false;275}276}277dragging_selected_attempt = false;278dragging_selected = false;279blend_space_draw->queue_redraw();280}281282if (mb.is_valid() && mb->is_pressed() && !dragging_selected_attempt && ((tool_select->is_pressed() && mb->is_shift_pressed()) || tool_blend->is_pressed()) && mb->get_button_index() == MouseButton::LEFT) {283Vector2 blend_pos = (mb->get_position() / blend_space_draw->get_size());284blend_pos.y = 1.0 - blend_pos.y;285blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space());286blend_pos += blend_space->get_min_space();287288tree->set(get_blend_position_path(), blend_pos);289290dragging_blend_position = true;291blend_space_draw->queue_redraw();292}293294if (mb.is_valid() && !mb->is_pressed() && dragging_blend_position && mb->get_button_index() == MouseButton::LEFT) {295dragging_blend_position = false;296}297298Ref<InputEventMouseMotion> mm = p_event;299300if (mm.is_valid() && dragging_selected_attempt) {301dragging_selected = true;302if (!read_only) {303drag_ofs = ((mm->get_position() - drag_from) / blend_space_draw->get_size()) * (blend_space->get_max_space() - blend_space->get_min_space()) * Vector2(1, -1);304}305blend_space_draw->queue_redraw();306_update_edited_point_pos();307}308309if (mm.is_valid() && tool_triangle->is_pressed() && making_triangle.size()) {310blend_space_draw->queue_redraw();311}312313if (mm.is_valid() && !tool_triangle->is_pressed() && making_triangle.size()) {314making_triangle.clear();315blend_space_draw->queue_redraw();316}317318if (mm.is_valid() && dragging_blend_position && !dragging_selected_attempt && ((tool_select->is_pressed() && mm->is_shift_pressed()) || tool_blend->is_pressed()) && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {319Vector2 blend_pos = (mm->get_position() / blend_space_draw->get_size());320blend_pos.y = 1.0 - blend_pos.y;321blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space());322blend_pos += blend_space->get_min_space();323324tree->set(get_blend_position_path(), blend_pos);325326blend_space_draw->queue_redraw();327}328}329330void AnimationNodeBlendSpace2DEditor::_file_opened(const String &p_file) {331file_loaded = ResourceLoader::load(p_file);332if (file_loaded.is_valid()) {333_add_menu_type(MENU_LOAD_FILE_CONFIRM);334} else {335EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only animation nodes are allowed."));336}337}338339void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) {340Ref<AnimationRootNode> node;341if (p_index == MENU_LOAD_FILE) {342open_file->clear_filters();343List<String> filters;344ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters);345for (const String &E : filters) {346open_file->add_filter("*." + E);347}348open_file->popup_file_dialog();349return;350} else if (p_index == MENU_LOAD_FILE_CONFIRM) {351node = file_loaded;352file_loaded.unref();353} else if (p_index == MENU_PASTE) {354node = EditorSettings::get_singleton()->get_resource_clipboard();355} else {356String type = menu->get_item_metadata(p_index);357358Object *obj = ClassDB::instantiate(type);359ERR_FAIL_NULL(obj);360AnimationNode *an = Object::cast_to<AnimationNode>(obj);361ERR_FAIL_NULL(an);362363node = Ref<AnimationNode>(an);364}365366if (node.is_null()) {367EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed."));368return;369}370371updating = true;372EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();373undo_redo->create_action(TTR("Add Node Point"));374undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos);375undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());376undo_redo->add_do_method(this, "_update_space");377undo_redo->add_undo_method(this, "_update_space");378undo_redo->commit_action();379updating = false;380381blend_space_draw->queue_redraw();382}383384void AnimationNodeBlendSpace2DEditor::_add_animation_type(int p_index) {385Ref<AnimationNodeAnimation> anim;386anim.instantiate();387388anim->set_animation(animations_to_add[p_index]);389390updating = true;391EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();392undo_redo->create_action(TTR("Add Animation Point"));393undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos);394undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count());395undo_redo->add_do_method(this, "_update_space");396undo_redo->add_undo_method(this, "_update_space");397undo_redo->commit_action();398updating = false;399400blend_space_draw->queue_redraw();401}402403void AnimationNodeBlendSpace2DEditor::_update_tool_erase() {404tool_erase->set_disabled(405(!(selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) && !(selected_triangle >= 0 && selected_triangle < blend_space->get_triangle_count())) ||406read_only);407408if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {409Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point);410if (AnimationTreeEditor::get_singleton()->can_edit(an)) {411open_editor->show();412} else {413open_editor->hide();414}415if (!read_only) {416edit_hb->show();417} else {418edit_hb->hide();419}420} else {421edit_hb->hide();422}423}424425void AnimationNodeBlendSpace2DEditor::_tool_switch(int p_tool) {426making_triangle.clear();427428if (p_tool == 3) {429Vector<Vector2> bl_points;430for (int i = 0; i < blend_space->get_blend_point_count(); i++) {431bl_points.push_back(blend_space->get_blend_point_position(i));432}433Vector<Delaunay2D::Triangle> tr = Delaunay2D::triangulate(bl_points);434for (int i = 0; i < tr.size(); i++) {435blend_space->add_triangle(tr[i].points[0], tr[i].points[1], tr[i].points[2]);436}437}438439if (p_tool == 0) {440tool_erase->show();441tool_erase_sep->show();442} else {443tool_erase->hide();444tool_erase_sep->hide();445}446_update_tool_erase();447blend_space_draw->queue_redraw();448}449450void AnimationNodeBlendSpace2DEditor::_blend_space_draw() {451AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();452if (!tree) {453return;454}455456Color linecolor = get_theme_color(SceneStringName(font_color), SNAME("Label"));457Color linecolor_soft = linecolor;458linecolor_soft.a *= 0.5;459Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));460int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));461Ref<Texture2D> icon = get_editor_theme_icon(SNAME("KeyValue"));462Ref<Texture2D> icon_selected = get_editor_theme_icon(SNAME("KeySelected"));463464Size2 s = blend_space_draw->get_size();465466if (blend_space_draw->has_focus(true)) {467Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));468blend_space_draw->draw_rect(Rect2(Point2(), s), color, false);469}470blend_space_draw->draw_line(Point2(1, 0), Point2(1, s.height - 1), linecolor, Math::round(EDSCALE));471blend_space_draw->draw_line(Point2(1, s.height - 1), Point2(s.width - 1, s.height - 1), linecolor, Math::round(EDSCALE));472473blend_space_draw->draw_line(Point2(0, 0), Point2(5 * EDSCALE, 0), linecolor, Math::round(EDSCALE));474if (blend_space->get_min_space().y < 0) {475int y = (blend_space->get_max_space().y / (blend_space->get_max_space().y - blend_space->get_min_space().y)) * s.height;476blend_space_draw->draw_line(Point2(0, y), Point2(5 * EDSCALE, y), linecolor, Math::round(EDSCALE));477blend_space_draw->draw_string(font, Point2(2 * EDSCALE, y - font->get_height(font_size) + font->get_ascent(font_size)), "0", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, linecolor);478blend_space_draw->draw_line(Point2(5 * EDSCALE, y), Point2(s.width, y), linecolor_soft, Math::round(EDSCALE));479}480481if (blend_space->get_min_space().x < 0) {482int x = (-blend_space->get_min_space().x / (blend_space->get_max_space().x - blend_space->get_min_space().x)) * s.width;483blend_space_draw->draw_line(Point2(x, s.height - 1), Point2(x, s.height - 5 * EDSCALE), linecolor, Math::round(EDSCALE));484blend_space_draw->draw_string(font, Point2(x + 2 * EDSCALE, s.height - 2 * EDSCALE - font->get_height(font_size) + font->get_ascent(font_size)), "0", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, linecolor);485blend_space_draw->draw_line(Point2(x, s.height - 5 * EDSCALE), Point2(x, 0), linecolor_soft, Math::round(EDSCALE));486}487488if (snap->is_pressed()) {489linecolor_soft.a = linecolor.a * 0.1;490491if (blend_space->get_snap().x > 0) {492int prev_idx = 0;493for (int i = 0; i < s.x; i++) {494float v = blend_space->get_min_space().x + i * (blend_space->get_max_space().x - blend_space->get_min_space().x) / s.x;495int idx = int(v / blend_space->get_snap().x);496497if (i > 0 && prev_idx != idx) {498blend_space_draw->draw_line(Point2(i, 0), Point2(i, s.height), linecolor_soft, Math::round(EDSCALE));499}500501prev_idx = idx;502}503}504505if (blend_space->get_snap().y > 0) {506int prev_idx = 0;507for (int i = 0; i < s.y; i++) {508float v = blend_space->get_max_space().y - i * (blend_space->get_max_space().y - blend_space->get_min_space().y) / s.y;509int idx = int(v / blend_space->get_snap().y);510511if (i > 0 && prev_idx != idx) {512blend_space_draw->draw_line(Point2(0, i), Point2(s.width, i), linecolor_soft, Math::round(EDSCALE));513}514515prev_idx = idx;516}517}518}519520//triangles first521for (int i = 0; i < blend_space->get_triangle_count(); i++) {522Vector<Vector2> bl_points;523bl_points.resize(3);524525for (int j = 0; j < 3; j++) {526int point_idx = blend_space->get_triangle_point(i, j);527Vector2 point = blend_space->get_blend_point_position(point_idx);528if (dragging_selected && selected_point == point_idx) {529point += drag_ofs;530if (snap->is_pressed()) {531point = point.snapped(blend_space->get_snap());532}533}534point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());535point *= s;536point.y = s.height - point.y;537bl_points.write[j] = point;538}539540for (int j = 0; j < 3; j++) {541blend_space_draw->draw_line(bl_points[j], bl_points[(j + 1) % 3], linecolor, Math::round(EDSCALE), true);542}543544Color color;545if (i == selected_triangle) {546color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));547color.a *= 0.5;548} else {549color = linecolor;550color.a *= 0.2;551}552553Vector<Color> colors = {554color,555color,556color557};558blend_space_draw->draw_primitive(bl_points, colors, Vector<Vector2>());559}560561points.clear();562for (int i = 0; i < blend_space->get_blend_point_count(); i++) {563Vector2 point = blend_space->get_blend_point_position(i);564if (!read_only) {565if (dragging_selected && selected_point == i) {566point += drag_ofs;567if (snap->is_pressed()) {568point = point.snapped(blend_space->get_snap());569}570}571}572point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());573point *= s;574point.y = s.height - point.y;575576points.push_back(point);577point -= (icon->get_size() / 2);578point = point.floor();579580if (i == selected_point) {581blend_space_draw->draw_texture(icon_selected, point);582} else {583blend_space_draw->draw_texture(icon, point);584}585}586587if (making_triangle.size()) {588Vector<Vector2> bl_points;589for (int i = 0; i < making_triangle.size(); i++) {590Vector2 point = blend_space->get_blend_point_position(making_triangle[i]);591point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());592point *= s;593point.y = s.height - point.y;594bl_points.push_back(point);595}596597for (int i = 0; i < bl_points.size() - 1; i++) {598blend_space_draw->draw_line(bl_points[i], bl_points[i + 1], linecolor, Math::round(2 * EDSCALE), true);599}600blend_space_draw->draw_line(bl_points[bl_points.size() - 1], blend_space_draw->get_local_mouse_position(), linecolor, Math::round(2 * EDSCALE), true);601}602603///draw cursor position604605{606Color color;607if (tool_blend->is_pressed()) {608color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));609} else {610color = linecolor;611color.a *= 0.5;612}613614Vector2 blend_pos = tree->get(get_blend_position_path());615Vector2 point = blend_pos;616617point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());618point *= s;619point.y = s.height - point.y;620621if (blend_space->get_triangle_count()) {622Vector2 closest = blend_space->get_closest_point(blend_pos);623closest = (closest - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());624closest *= s;625closest.y = s.height - closest.y;626627Color lcol = color;628lcol.a *= 0.4;629blend_space_draw->draw_line(point, closest, lcol, Math::round(2 * EDSCALE), true);630}631632float mind = 5 * EDSCALE;633float maxd = 15 * EDSCALE;634blend_space_draw->draw_line(point + Vector2(mind, 0), point + Vector2(maxd, 0), color, Math::round(2 * EDSCALE));635blend_space_draw->draw_line(point + Vector2(-mind, 0), point + Vector2(-maxd, 0), color, Math::round(2 * EDSCALE));636blend_space_draw->draw_line(point + Vector2(0, mind), point + Vector2(0, maxd), color, Math::round(2 * EDSCALE));637blend_space_draw->draw_line(point + Vector2(0, -mind), point + Vector2(0, -maxd), color, Math::round(2 * EDSCALE));638}639}640641void AnimationNodeBlendSpace2DEditor::_snap_toggled() {642blend_space_draw->queue_redraw();643}644645void AnimationNodeBlendSpace2DEditor::_update_space() {646if (updating) {647return;648}649650updating = true;651652if (blend_space->get_auto_triangles()) {653tool_triangle->hide();654} else {655tool_triangle->show();656}657658auto_triangles->set_pressed(blend_space->get_auto_triangles());659660sync->set_pressed(blend_space->is_using_sync());661interpolation->select(blend_space->get_blend_mode());662663max_x_value->set_value(blend_space->get_max_space().x);664max_y_value->set_value(blend_space->get_max_space().y);665666min_x_value->set_value(blend_space->get_min_space().x);667min_y_value->set_value(blend_space->get_min_space().y);668669label_x->set_text(blend_space->get_x_label());670label_y->set_text(blend_space->get_y_label());671672snap_x->set_value(blend_space->get_snap().x);673snap_y->set_value(blend_space->get_snap().y);674675blend_space_draw->queue_redraw();676677updating = false;678}679680void AnimationNodeBlendSpace2DEditor::_config_changed(double) {681if (updating) {682return;683}684685updating = true;686EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();687undo_redo->create_action(TTR("Change BlendSpace2D Config"));688undo_redo->add_do_method(blend_space.ptr(), "set_max_space", Vector2(max_x_value->get_value(), max_y_value->get_value()));689undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space());690undo_redo->add_do_method(blend_space.ptr(), "set_min_space", Vector2(min_x_value->get_value(), min_y_value->get_value()));691undo_redo->add_undo_method(blend_space.ptr(), "set_min_space", blend_space->get_min_space());692undo_redo->add_do_method(blend_space.ptr(), "set_snap", Vector2(snap_x->get_value(), snap_y->get_value()));693undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap());694undo_redo->add_do_method(blend_space.ptr(), "set_use_sync", sync->is_pressed());695undo_redo->add_undo_method(blend_space.ptr(), "set_use_sync", blend_space->is_using_sync());696undo_redo->add_do_method(blend_space.ptr(), "set_blend_mode", interpolation->get_selected());697undo_redo->add_undo_method(blend_space.ptr(), "set_blend_mode", blend_space->get_blend_mode());698undo_redo->add_do_method(this, "_update_space");699undo_redo->add_undo_method(this, "_update_space");700undo_redo->commit_action();701updating = false;702703blend_space_draw->queue_redraw();704}705706void AnimationNodeBlendSpace2DEditor::_labels_changed(String) {707if (updating) {708return;709}710711updating = true;712EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();713undo_redo->create_action(TTR("Change BlendSpace2D Labels"), UndoRedo::MERGE_ENDS);714undo_redo->add_do_method(blend_space.ptr(), "set_x_label", label_x->get_text());715undo_redo->add_undo_method(blend_space.ptr(), "set_x_label", blend_space->get_x_label());716undo_redo->add_do_method(blend_space.ptr(), "set_y_label", label_y->get_text());717undo_redo->add_undo_method(blend_space.ptr(), "set_y_label", blend_space->get_y_label());718undo_redo->add_do_method(this, "_update_space");719undo_redo->add_undo_method(this, "_update_space");720undo_redo->commit_action();721updating = false;722}723724void AnimationNodeBlendSpace2DEditor::_erase_selected() {725EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();726if (selected_point != -1) {727updating = true;728undo_redo->create_action(TTR("Remove BlendSpace2D Point"));729undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point);730undo_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);731732//restore triangles using this point733for (int i = 0; i < blend_space->get_triangle_count(); i++) {734for (int j = 0; j < 3; j++) {735if (blend_space->get_triangle_point(i, j) == selected_point) {736undo_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);737break;738}739}740}741742undo_redo->add_do_method(this, "_update_space");743undo_redo->add_undo_method(this, "_update_space");744undo_redo->commit_action();745746// Return selection to host BlendSpace2D node.747EditorNode::get_singleton()->push_item(blend_space.ptr(), "", true);748749updating = false;750_update_tool_erase();751752blend_space_draw->queue_redraw();753} else if (selected_triangle != -1) {754updating = true;755undo_redo->create_action(TTR("Remove BlendSpace2D Triangle"));756undo_redo->add_do_method(blend_space.ptr(), "remove_triangle", selected_triangle);757undo_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);758759undo_redo->add_do_method(this, "_update_space");760undo_redo->add_undo_method(this, "_update_space");761undo_redo->commit_action();762763selected_triangle = -1;764765updating = false;766_update_tool_erase();767768blend_space_draw->queue_redraw();769}770}771772void AnimationNodeBlendSpace2DEditor::_update_edited_point_pos() {773if (updating) {774return;775}776777if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {778Vector2 pos = blend_space->get_blend_point_position(selected_point);779if (dragging_selected) {780pos += drag_ofs;781if (snap->is_pressed()) {782pos = pos.snapped(blend_space->get_snap());783}784}785updating = true;786edit_x->set_value(pos.x);787edit_y->set_value(pos.y);788updating = false;789}790}791792void AnimationNodeBlendSpace2DEditor::_edit_point_pos(double) {793if (updating) {794return;795}796updating = true;797EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();798undo_redo->create_action(TTR("Move Node Point"));799undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, Vector2(edit_x->get_value(), edit_y->get_value()));800undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point));801undo_redo->add_do_method(this, "_update_space");802undo_redo->add_undo_method(this, "_update_space");803undo_redo->add_do_method(this, "_update_edited_point_pos");804undo_redo->add_undo_method(this, "_update_edited_point_pos");805undo_redo->commit_action();806updating = false;807808blend_space_draw->queue_redraw();809}810811void AnimationNodeBlendSpace2DEditor::_notification(int p_what) {812switch (p_what) {813case NOTIFICATION_THEME_CHANGED: {814error_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));815error_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));816panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));817tool_blend->set_button_icon(get_editor_theme_icon(SNAME("EditPivot")));818tool_select->set_button_icon(get_editor_theme_icon(SNAME("ToolSelect")));819tool_create->set_button_icon(get_editor_theme_icon(SNAME("EditKey")));820tool_triangle->set_button_icon(get_editor_theme_icon(SNAME("ToolTriangle")));821tool_erase->set_button_icon(get_editor_theme_icon(SNAME("Remove")));822snap->set_button_icon(get_editor_theme_icon(SNAME("SnapGrid")));823open_editor->set_button_icon(get_editor_theme_icon(SNAME("Edit")));824auto_triangles->set_button_icon(get_editor_theme_icon(SNAME("AutoTriangle")));825interpolation->clear();826interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous")), TTR("Continuous"), 0);827interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete")), TTR("Discrete"), 1);828interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackCapture")), TTR("Capture"), 2);829} break;830831case NOTIFICATION_PROCESS: {832AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();833if (!tree) {834return;835}836837String error;838839error = tree->get_editor_error_message();840841if (error.is_empty() && blend_space->get_triangle_count() == 0) {842error = TTR("No triangles exist, so no blending can take place.");843}844845if (error != error_label->get_text()) {846error_label->set_text(error);847if (!error.is_empty()) {848error_panel->show();849} else {850error_panel->hide();851}852}853} break;854855case NOTIFICATION_VISIBILITY_CHANGED: {856set_process(is_visible_in_tree());857} break;858}859}860861void AnimationNodeBlendSpace2DEditor::_open_editor() {862if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) {863Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point);864ERR_FAIL_COND(an.is_null());865AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point));866}867}868869void AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled() {870EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();871undo_redo->create_action(TTR("Toggle Auto Triangles"));872undo_redo->add_do_method(blend_space.ptr(), "set_auto_triangles", auto_triangles->is_pressed());873undo_redo->add_undo_method(blend_space.ptr(), "set_auto_triangles", blend_space->get_auto_triangles());874undo_redo->add_do_method(this, "_update_space");875undo_redo->add_undo_method(this, "_update_space");876undo_redo->commit_action();877}878879void AnimationNodeBlendSpace2DEditor::_bind_methods() {880ClassDB::bind_method("_update_space", &AnimationNodeBlendSpace2DEditor::_update_space);881ClassDB::bind_method("_update_tool_erase", &AnimationNodeBlendSpace2DEditor::_update_tool_erase);882883ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace2DEditor::_update_edited_point_pos);884}885886AnimationNodeBlendSpace2DEditor *AnimationNodeBlendSpace2DEditor::singleton = nullptr;887888AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {889singleton = this;890updating = false;891892HBoxContainer *top_hb = memnew(HBoxContainer);893add_child(top_hb);894895Ref<ButtonGroup> bg;896bg.instantiate();897898tool_select = memnew(Button);899tool_select->set_theme_type_variation(SceneStringName(FlatButton));900tool_select->set_toggle_mode(true);901tool_select->set_button_group(bg);902top_hb->add_child(tool_select);903tool_select->set_pressed(true);904tool_select->set_tooltip_text(TTR("Select and move points.\nRMB: Create point at position clicked.\nShift+LMB+Drag: Set the blending position within the space."));905tool_select->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_tool_switch).bind(0));906907tool_create = memnew(Button);908tool_create->set_theme_type_variation(SceneStringName(FlatButton));909tool_create->set_toggle_mode(true);910tool_create->set_button_group(bg);911top_hb->add_child(tool_create);912tool_create->set_tooltip_text(TTR("Create points."));913tool_create->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_tool_switch).bind(1));914915tool_blend = memnew(Button);916tool_blend->set_theme_type_variation(SceneStringName(FlatButton));917tool_blend->set_toggle_mode(true);918tool_blend->set_button_group(bg);919top_hb->add_child(tool_blend);920tool_blend->set_tooltip_text(TTR("Set the blending position within the space."));921tool_blend->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_tool_switch).bind(2));922923tool_triangle = memnew(Button);924tool_triangle->set_theme_type_variation(SceneStringName(FlatButton));925tool_triangle->set_toggle_mode(true);926tool_triangle->set_button_group(bg);927top_hb->add_child(tool_triangle);928tool_triangle->set_tooltip_text(TTR("Create triangles by connecting points."));929tool_triangle->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_tool_switch).bind(3));930931tool_erase_sep = memnew(VSeparator);932top_hb->add_child(tool_erase_sep);933tool_erase = memnew(Button);934tool_erase->set_theme_type_variation(SceneStringName(FlatButton));935top_hb->add_child(tool_erase);936tool_erase->set_tooltip_text(TTR("Erase points and triangles."));937tool_erase->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_erase_selected));938tool_erase->set_disabled(true);939940top_hb->add_child(memnew(VSeparator));941942auto_triangles = memnew(Button);943auto_triangles->set_theme_type_variation(SceneStringName(FlatButton));944top_hb->add_child(auto_triangles);945auto_triangles->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled));946auto_triangles->set_toggle_mode(true);947auto_triangles->set_tooltip_text(TTR("Generate blend triangles automatically (instead of manually)"));948949top_hb->add_child(memnew(VSeparator));950951snap = memnew(Button);952snap->set_theme_type_variation(SceneStringName(FlatButton));953snap->set_toggle_mode(true);954top_hb->add_child(snap);955snap->set_pressed(true);956snap->set_tooltip_text(TTR("Enable snap and show grid."));957snap->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_snap_toggled));958959snap_x = memnew(SpinBox);960top_hb->add_child(snap_x);961snap_x->set_prefix("x:");962snap_x->set_min(0.01);963snap_x->set_step(0.01);964snap_x->set_max(1000);965snap_x->set_accessibility_name(TTRC("Grid X Step"));966967snap_y = memnew(SpinBox);968top_hb->add_child(snap_y);969snap_y->set_prefix("y:");970snap_y->set_min(0.01);971snap_y->set_step(0.01);972snap_y->set_max(1000);973snap_y->set_accessibility_name(TTRC("Grid Y Step"));974975top_hb->add_child(memnew(VSeparator));976977top_hb->add_child(memnew(Label(TTR("Sync:"))));978sync = memnew(CheckBox);979top_hb->add_child(sync);980sync->connect(SceneStringName(toggled), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));981982top_hb->add_child(memnew(VSeparator));983984top_hb->add_child(memnew(Label(TTR("Blend:"))));985interpolation = memnew(OptionButton);986top_hb->add_child(interpolation);987interpolation->connect(SceneStringName(item_selected), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));988989edit_hb = memnew(HBoxContainer);990top_hb->add_child(edit_hb);991edit_hb->add_child(memnew(VSeparator));992edit_hb->add_child(memnew(Label(TTR("Point"))));993edit_x = memnew(SpinBox);994edit_hb->add_child(edit_x);995edit_x->set_min(-1000);996edit_x->set_step(0.01);997edit_x->set_max(1000);998edit_x->set_accessibility_name(TTRC("Blend X Value"));999edit_x->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_edit_point_pos));1000edit_y = memnew(SpinBox);1001edit_hb->add_child(edit_y);1002edit_y->set_min(-1000);1003edit_y->set_step(0.01);1004edit_y->set_max(1000);1005edit_y->set_accessibility_name(TTRC("Blend Y Value"));1006edit_y->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_edit_point_pos));1007open_editor = memnew(Button);1008edit_hb->add_child(open_editor);1009open_editor->set_text(TTR("Open Editor"));1010open_editor->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_open_editor), CONNECT_DEFERRED);1011edit_hb->hide();1012open_editor->hide();10131014HBoxContainer *main_hb = memnew(HBoxContainer);1015add_child(main_hb);1016main_hb->set_v_size_flags(SIZE_EXPAND_FILL);10171018GridContainer *main_grid = memnew(GridContainer);1019main_grid->set_columns(2);1020main_hb->add_child(main_grid);1021main_grid->set_h_size_flags(SIZE_EXPAND_FILL);1022{1023VBoxContainer *left_vbox = memnew(VBoxContainer);1024main_grid->add_child(left_vbox);1025left_vbox->set_v_size_flags(SIZE_EXPAND_FILL);1026max_y_value = memnew(SpinBox);1027max_y_value->set_accessibility_name(TTRC("Max Y"));1028left_vbox->add_child(max_y_value);1029left_vbox->add_spacer();1030label_y = memnew(LineEdit);1031label_y->set_accessibility_name(TTRC("Y Value"));1032left_vbox->add_child(label_y);1033label_y->set_expand_to_text_length_enabled(true);1034left_vbox->add_spacer();1035min_y_value = memnew(SpinBox);1036min_y_value->set_accessibility_name(TTRC("Min Y"));1037left_vbox->add_child(min_y_value);10381039max_y_value->set_max(10000);1040max_y_value->set_min(0.01);1041max_y_value->set_step(0.01);10421043min_y_value->set_min(-10000);1044min_y_value->set_max(0);1045min_y_value->set_step(0.01);1046}10471048panel = memnew(PanelContainer);1049panel->set_clip_contents(true);1050main_grid->add_child(panel);1051panel->set_h_size_flags(SIZE_EXPAND_FILL);10521053blend_space_draw = memnew(Control);1054blend_space_draw->connect(SceneStringName(gui_input), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_blend_space_gui_input));1055blend_space_draw->connect(SceneStringName(draw), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_blend_space_draw));1056blend_space_draw->set_focus_mode(FOCUS_ALL);10571058panel->add_child(blend_space_draw);1059main_grid->add_child(memnew(Control)); //empty bottom left10601061{1062HBoxContainer *bottom_vbox = memnew(HBoxContainer);1063main_grid->add_child(bottom_vbox);1064bottom_vbox->set_h_size_flags(SIZE_EXPAND_FILL);1065min_x_value = memnew(SpinBox);1066min_x_value->set_accessibility_name(TTRC("Min X"));1067bottom_vbox->add_child(min_x_value);1068bottom_vbox->add_spacer();1069label_x = memnew(LineEdit);1070label_x->set_accessibility_name(TTRC("X Value"));1071bottom_vbox->add_child(label_x);1072label_x->set_expand_to_text_length_enabled(true);1073bottom_vbox->add_spacer();1074max_x_value = memnew(SpinBox);1075max_x_value->set_accessibility_name(TTRC("Max X"));1076bottom_vbox->add_child(max_x_value);10771078max_x_value->set_max(10000);1079max_x_value->set_min(0.01);1080max_x_value->set_step(0.01);10811082min_x_value->set_min(-10000);1083min_x_value->set_max(0);1084min_x_value->set_step(0.01);1085}10861087snap_x->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));1088snap_y->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));1089max_x_value->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));1090min_x_value->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));1091max_y_value->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));1092min_y_value->connect(SceneStringName(value_changed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_config_changed));1093label_x->connect(SceneStringName(text_changed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_labels_changed));1094label_y->connect(SceneStringName(text_changed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_labels_changed));10951096error_panel = memnew(PanelContainer);1097add_child(error_panel);1098error_label = memnew(Label);1099error_label->set_focus_mode(FOCUS_ACCESSIBILITY);1100error_panel->add_child(error_label);1101error_panel->hide();11021103set_custom_minimum_size(Size2(0, 300 * EDSCALE));11041105menu = memnew(PopupMenu);1106add_child(menu);1107menu->connect(SceneStringName(id_pressed), callable_mp(this, &AnimationNodeBlendSpace2DEditor::_add_menu_type));11081109animations_menu = memnew(PopupMenu);1110animations_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);1111menu->add_child(animations_menu);1112animations_menu->connect("index_pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_add_animation_type));11131114open_file = memnew(EditorFileDialog);1115add_child(open_file);1116open_file->set_title(TTR("Open Animation Node"));1117open_file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);1118open_file->connect("file_selected", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_file_opened));11191120selected_point = -1;1121selected_triangle = -1;11221123dragging_selected = false;1124dragging_selected_attempt = false;1125dragging_blend_position = false;1126}112711281129