diff options
author | Juan Linietsky <reduzio@gmail.com> | 2018-08-20 13:38:18 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2018-08-20 13:39:16 -0300 |
commit | c7e4527a88b90062e1d5fdea50c1b85cea3c8c6c (patch) | |
tree | ee365bb27589b1e3646cee42fa1594038a25bfe9 | |
parent | 1b66b08fdb3adc838810ce79823c70ac391c8a21 (diff) |
Massive rewrite to AnimationTree. Many APIs changed in order to:
-Reuse resources
-Expose properties in AnimationTree
34 files changed, 3819 insertions, 2900 deletions
diff --git a/core/object.cpp b/core/object.cpp index ba8b710a84..4b2869b373 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -1476,8 +1476,13 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str Signal::Target target(p_to_object->get_instance_id(), p_to_method); if (s->slot_map.has(target)) { - ERR_EXPLAIN("Signal '" + p_signal + "' is already connected to given method '" + p_to_method + "' in that object."); - ERR_FAIL_COND_V(s->slot_map.has(target), ERR_INVALID_PARAMETER); + if (p_flags & CONNECT_REFERENCE_COUNTED) { + s->slot_map[target].reference_count++; + return OK; + } else { + ERR_EXPLAIN("Signal '" + p_signal + "' is already connected to given method '" + p_to_method + "' in that object."); + ERR_FAIL_COND_V(s->slot_map.has(target), ERR_INVALID_PARAMETER); + } } Signal::Slot slot; @@ -1491,6 +1496,10 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str conn.binds = p_binds; slot.conn = conn; slot.cE = p_to_object->connections.push_back(conn); + if (p_flags & CONNECT_REFERENCE_COUNTED) { + slot.reference_count = 1; + } + s->slot_map[target] = slot; return OK; @@ -1539,7 +1548,14 @@ void Object::disconnect(const StringName &p_signal, Object *p_to_object, const S ERR_FAIL(); } - p_to_object->connections.erase(s->slot_map[target].cE); + Signal::Slot *slot = &s->slot_map[target]; + + slot->reference_count--; // by default is zero, if it was not referenced it will go below it + if (slot->reference_count >= 0) { + return; + } + + p_to_object->connections.erase(slot->cE); s->slot_map.erase(target); if (s->slot_map.empty() && ClassDB::has_signal(get_class_name(), p_signal)) { @@ -1761,6 +1777,7 @@ void Object::_bind_methods() { BIND_ENUM_CONSTANT(CONNECT_DEFERRED); BIND_ENUM_CONSTANT(CONNECT_PERSIST); BIND_ENUM_CONSTANT(CONNECT_ONESHOT); + BIND_ENUM_CONSTANT(CONNECT_REFERENCE_COUNTED); } void Object::call_deferred(const StringName &p_method, VARIANT_ARG_DECLARE) { diff --git a/core/object.h b/core/object.h index 806cf8160c..52c9c509ab 100644 --- a/core/object.h +++ b/core/object.h @@ -392,7 +392,8 @@ public: CONNECT_DEFERRED = 1, CONNECT_PERSIST = 2, // hint for scene to save this connection - CONNECT_ONESHOT = 4 + CONNECT_ONESHOT = 4, + CONNECT_REFERENCE_COUNTED = 8, }; struct Connection { @@ -443,8 +444,10 @@ private: struct Slot { + int reference_count; Connection conn; List<Connection>::Element *cE; + Slot() { reference_count = 0; } }; MethodInfo user; diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 99a2b2aa67..d3ef737542 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1533,9 +1533,10 @@ void EditorInspector::update_tree() { if (capitalize_paths) path_name = path_name.capitalize(); + Color c = sscolor; c.a /= level; - section->setup(path_name, acc_path, object, c, use_folding); + section->setup(path_name, path_name, object, c, use_folding); item_path[acc_path] = section->get_vbox(); } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 0be6ee8759..975c7f8345 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -74,6 +74,7 @@ #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/animation_state_machine_editor.h" #include "editor/plugins/animation_tree_editor_plugin.h" +#include "editor/plugins/animation_tree_player_editor_plugin.h" #include "editor/plugins/asset_library_editor_plugin.h" #include "editor/plugins/audio_stream_editor_plugin.h" #include "editor/plugins/baked_lightmap_editor_plugin.h" @@ -5589,16 +5590,13 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(ShaderEditorPlugin(this))); add_editor_plugin(memnew(VisualShaderEditorPlugin(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))); add_editor_plugin(memnew(CameraEditorPlugin(this))); add_editor_plugin(memnew(ThemeEditorPlugin(this))); add_editor_plugin(memnew(MultiMeshEditorPlugin(this))); add_editor_plugin(memnew(MeshInstanceEditorPlugin(this))); add_editor_plugin(memnew(AnimationTreeEditorPlugin(this))); + add_editor_plugin(memnew(AnimationTreePlayerEditorPlugin(this))); add_editor_plugin(memnew(MeshLibraryEditorPlugin(this))); add_editor_plugin(memnew(StyleBoxEditorPlugin(this))); add_editor_plugin(memnew(SpriteEditorPlugin(this))); diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 615e9df043..0335053162 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -36,6 +36,16 @@ void InspectorDock::_menu_option(int p_option) { switch (p_option) { + case RESOURCE_MAKE_BUILT_IN: { + _unref_resource(); + } break; + case RESOURCE_COPY: { + _copy_resource(); + } break; + case RESOURCE_EDIT_CLIPBOARD: { + _paste_resource(); + } break; + case RESOURCE_SAVE: { _save_resource(false); } break; @@ -400,10 +410,11 @@ void InspectorDock::update(Object *p_object) { p->add_shortcut(ED_SHORTCUT("property_editor/copy_params", TTR("Copy Params")), OBJECT_COPY_PARAMS); p->add_shortcut(ED_SHORTCUT("property_editor/paste_params", TTR("Paste Params")), OBJECT_PASTE_PARAMS); p->add_separator(); - p->add_shortcut(ED_SHORTCUT("property_editor/paste_resource", TTR("Paste Resource")), RESOURCE_PASTE); + + p->add_shortcut(ED_SHORTCUT("property_editor/paste_resource", TTR("Edit Resource Clipboard")), RESOURCE_EDIT_CLIPBOARD); if (is_resource) { p->add_shortcut(ED_SHORTCUT("property_editor/copy_resource", TTR("Copy Resource")), RESOURCE_COPY); - p->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTR("Make Built-In")), RESOURCE_UNREF); + p->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTR("Make Built-In")), RESOURCE_MAKE_BUILT_IN); } if (is_resource || is_node) { diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h index f347056158..97ef6899dc 100644 --- a/editor/inspector_dock.h +++ b/editor/inspector_dock.h @@ -51,13 +51,12 @@ class InspectorDock : public VBoxContainer { GDCLASS(InspectorDock, VBoxContainer); enum MenuOptions { - RESOURCE_NEW, RESOURCE_LOAD, RESOURCE_SAVE, RESOURCE_SAVE_AS, - RESOURCE_UNREF, + RESOURCE_MAKE_BUILT_IN, RESOURCE_COPY, - RESOURCE_PASTE, + RESOURCE_EDIT_CLIPBOARD, OBJECT_COPY_PARAMS, OBJECT_PASTE_PARAMS, OBJECT_UNIQUE_RESOURCES, diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index 2e128db883..1106464edf 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -3,41 +3,11 @@ #include "os/keyboard.h" #include "scene/animation/animation_blend_tree.h" -void AnimationNodeBlendSpace1DEditorPlugin::edit(Object *p_object) { - anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendSpace1D>(p_object)); +StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const { + StringName path = AnimationTreeEditor::get_singleton()->get_base_path()+"blend_position"; + return path; } -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<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; @@ -62,7 +32,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven menu->add_submenu_item(TTR("Add Animation"), "animations"); - AnimationTree *gp = blend_space->get_tree(); + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); ERR_FAIL_COND(!gp); if (gp->has_node(gp->get_animation_player())) { @@ -85,10 +55,18 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven continue; int idx = menu->get_item_count(); - menu->add_item(vformat("Add %s", name)); + menu->add_item(vformat("Add %s", name),idx); menu->set_item_metadata(idx, E->get()); } + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } + menu->add_separator(); + menu->add_item(TTR("Load.."), MENU_LOAD_FILE); + menu->set_global_position(blend_space_draw->get_global_transform().xform(mb->get_position())); menu->popup(); @@ -158,7 +136,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven 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); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(),blend_pos); blend_space_draw->update(); } @@ -181,7 +159,8 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven 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); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(),blend_pos); + blend_space_draw->update(); } } @@ -277,7 +256,9 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_draw() { color.a *= 0.5; } - float point = blend_space->get_blend_pos(); + float point = AnimationTreeEditor::get_singleton()->get_tree()->get(get_blend_position_path()); + + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); point *= s.width; @@ -299,12 +280,6 @@ void AnimationNodeBlendSpace1DEditor::_update_space() { 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()); @@ -355,15 +330,47 @@ void AnimationNodeBlendSpace1DEditor::_snap_toggled() { blend_space_draw->update(); } +void AnimationNodeBlendSpace1DEditor::_file_opened(const String &p_file) { + + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_menu_type(MENU_LOAD_FILE_CONFIRM); + } +} + void AnimationNodeBlendSpace1DEditor::_add_menu_type(int p_index) { - String type = menu->get_item_metadata(p_index); + Ref<AnimationRootNode> node; + if (p_index == MENU_LOAD_FILE) { + + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_index == MENU_LOAD_FILE_CONFIRM) { + node = file_loaded; + file_loaded.unref(); + } else if (p_index == MENU_PASTE) { + + node = EditorSettings::get_singleton()->get_resource_clipboard(); + } else { + String type = menu->get_item_metadata(p_index); - Object *obj = ClassDB::instance(type); - ERR_FAIL_COND(!obj); - AnimationNode *an = Object::cast_to<AnimationNode>(obj); - ERR_FAIL_COND(!an); + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to<AnimationNode>(obj); + ERR_FAIL_COND(!an); - Ref<AnimationNode> node(an); + node = Ref<AnimationNode>(an); + } + + if (!node.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed.")); + return; + } updating = true; undo_redo->create_action("Add Node Point"); @@ -438,7 +445,7 @@ void AnimationNodeBlendSpace1DEditor::_update_tool_erase() { if (point_valid) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); - if (EditorNode::get_singleton()->item_has_editor(an.ptr())) { + if (AnimationTreeEditor::get_singleton()->can_edit(an)) { open_editor->show(); } else { open_editor->hide(); @@ -490,17 +497,11 @@ void AnimationNodeBlendSpace1DEditor::_open_editor() { if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); ERR_FAIL_COND(an.is_null()); - EditorNode::get_singleton()->edit_item(an.ptr()); + AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point)); } } -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) { @@ -513,18 +514,16 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { 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_tree()) { - error = TTR("BlendSpace1D does not belong to an AnimationTree node."); - } else if (!blend_space->get_tree()->is_active()) { + if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (blend_space->get_tree()->is_state_invalid()) { - error = blend_space->get_tree()->get_invalid_state_reason(); + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); } if (error != error_label->get_text()) { @@ -536,6 +535,10 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { } } } + + if (p_what==NOTIFICATION_VISIBILITY_CHANGED) { + set_process(is_visible_in_tree()); + } } void AnimationNodeBlendSpace1DEditor::_bind_methods() { @@ -556,28 +559,26 @@ void AnimationNodeBlendSpace1DEditor::_bind_methods() { 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); + ClassDB::bind_method("_file_opened", &AnimationNodeBlendSpace1DEditor::_file_opened); + + + } -void AnimationNodeBlendSpace1DEditor::edit(AnimationNodeBlendSpace1D *p_blend_space) { +bool AnimationNodeBlendSpace1DEditor::can_edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_valid()) { - blend_space->disconnect("removed_from_graph", this, "_removed_from_graph"); - } + Ref<AnimationNodeBlendSpace1D> b1d=p_node; + return b1d.is_valid(); +} - if (p_blend_space) { - blend_space = Ref<AnimationNodeBlendSpace1D>(p_blend_space); - } else { - blend_space.unref(); - } +void AnimationNodeBlendSpace1DEditor::edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_null()) { - hide(); - } else { - blend_space->connect("removed_from_graph", this, "_removed_from_graph"); + + blend_space=p_node; + + if (!blend_space.is_null()) { _update_space(); } } @@ -594,14 +595,6 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { Ref<ButtonGroup> 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); @@ -726,13 +719,20 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { menu = memnew(PopupMenu); add_child(menu); - menu->connect("index_pressed", this, "_add_menu_type"); + menu->connect("id_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"); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + selected_point = -1; dragging_selected = false; dragging_selected_attempt = false; diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h index 52139626e6..f040f6dcab 100644 --- a/editor/plugins/animation_blend_space_1d_editor.h +++ b/editor/plugins/animation_blend_space_1d_editor.h @@ -9,10 +9,11 @@ #include "scene/gui/graph_edit.h" #include "scene/gui/popup.h" #include "scene/gui/tree.h" +#include "editor/plugins/animation_tree_editor_plugin.h" -class AnimationNodeBlendSpace1DEditor : public VBoxContainer { +class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeBlendSpace1DEditor, VBoxContainer) + GDCLASS(AnimationNodeBlendSpace1DEditor, AnimationTreeNodeEditorPlugin) Ref<AnimationNodeBlendSpace1D> blend_space; @@ -81,7 +82,17 @@ class AnimationNodeBlendSpace1DEditor : public VBoxContainer { void _goto_parent(); - void _removed_from_graph(); + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + + StringName get_blend_position_path() const; protected: void _notification(int p_what); @@ -89,29 +100,9 @@ protected: public: static AnimationNodeBlendSpace1DEditor *get_singleton() { return singleton; } - void edit(AnimationNodeBlendSpace1D *p_blend_space); + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); 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 index 27df60f87a..e008971e5c 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -11,27 +11,26 @@ #include "scene/gui/panel.h" #include "scene/main/viewport.h" -void AnimationNodeBlendSpace2DEditor::edit(AnimationNodeBlendSpace2D *p_blend_space) { +bool AnimationNodeBlendSpace2DEditor::can_edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_valid()) { - blend_space->disconnect("removed_from_graph", this, "_removed_from_graph"); - } + Ref<AnimationNodeBlendSpace2D> bs2d=p_node; + return bs2d.is_valid(); +} - if (p_blend_space) { - blend_space = Ref<AnimationNodeBlendSpace2D>(p_blend_space); - } else { - blend_space.unref(); - } +void AnimationNodeBlendSpace2DEditor::edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_null()) { - hide(); - } else { - blend_space->connect("removed_from_graph", this, "_removed_from_graph"); + blend_space = p_node; + if (!blend_space.is_null()) { _update_space(); } } +StringName AnimationNodeBlendSpace2DEditor::get_blend_position_path() const { + StringName path = AnimationTreeEditor::get_singleton()->get_base_path()+"blend_position"; + return path; +} + void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; @@ -54,7 +53,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); menu->add_submenu_item(TTR("Add Animation"), "animations"); - AnimationTree *gp = blend_space->get_tree(); + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); ERR_FAIL_COND(!gp); if (gp && gp->has_node(gp->get_animation_player())) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); @@ -74,10 +73,19 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven if (name == "Animation") continue; // nope int idx = menu->get_item_count(); - menu->add_item(vformat("Add %s", name)); + menu->add_item(vformat("Add %s", name),idx); menu->set_item_metadata(idx, E->get()); } + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } + menu->add_separator(); + menu->add_item(TTR("Load.."), MENU_LOAD_FILE); + + 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()); @@ -203,7 +211,8 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven 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); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(),blend_pos); + blend_space_draw->update(); } @@ -237,21 +246,54 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven 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); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(),blend_pos); + blend_space_draw->update(); } } +void AnimationNodeBlendSpace2DEditor::_file_opened(const String &p_file) { + + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_menu_type(MENU_LOAD_FILE_CONFIRM); + } +} + void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) { - String type = menu->get_item_metadata(p_index); + Ref<AnimationRootNode> node; + if (p_index == MENU_LOAD_FILE) { + + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_index == MENU_LOAD_FILE_CONFIRM) { + node = file_loaded; + file_loaded.unref(); + } else if (p_index == MENU_PASTE) { + + node = EditorSettings::get_singleton()->get_resource_clipboard(); + } else { + String type = menu->get_item_metadata(p_index); + + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to<AnimationNode>(obj); + ERR_FAIL_COND(!an); - Object *obj = ClassDB::instance(type); - ERR_FAIL_COND(!obj); - AnimationNode *an = Object::cast_to<AnimationNode>(obj); - ERR_FAIL_COND(!an); + node = Ref<AnimationNode>(an); + } - Ref<AnimationNode> node(an); + if (!node.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed.")); + return; + } updating = true; undo_redo->create_action("Add Node Point"); @@ -288,7 +330,7 @@ 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<AnimationNode> an = blend_space->get_blend_point_node(selected_point); - if (EditorNode::get_singleton()->item_has_editor(an.ptr())) { + if (AnimationTreeEditor::get_singleton()->can_edit(an)) { open_editor->show(); } else { open_editor->hide(); @@ -490,13 +532,15 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() { color.a *= 0.5; } - Vector2 point = blend_space->get_blend_position(); + Vector2 blend_pos = AnimationTreeEditor::get_singleton()->get_tree()->get(get_blend_position_path()); + Vector2 point = blend_pos; + 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()); + Vector2 closest = blend_space->get_closest_point(blend_pos); 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; @@ -527,12 +571,6 @@ void AnimationNodeBlendSpace2DEditor::_update_space() { 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 { @@ -685,7 +723,6 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { 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")); } @@ -693,12 +730,12 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { String error; - if (!blend_space->get_tree()) { + if (!AnimationTreeEditor::get_singleton()->get_tree()) { error = TTR("BlendSpace2D does not belong to an AnimationTree node."); - } else if (!blend_space->get_tree()->is_active()) { + } else if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (blend_space->get_tree()->is_state_invalid()) { - error = blend_space->get_tree()->get_invalid_state_reason(); + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); } else if (blend_space->get_triangle_count() == 0) { error = TTR("No triangles exist, so no blending can take place."); } @@ -712,22 +749,22 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { } } } + + if (p_what==NOTIFICATION_VISIBILITY_CHANGED) { + set_process(is_visible_in_tree()); + } } + void AnimationNodeBlendSpace2DEditor::_open_editor() { if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); - ERR_FAIL_COND(!an.is_valid()); - EditorNode::get_singleton()->edit_item(an.ptr()); + ERR_FAIL_COND(an.is_null()); + AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point)); } } -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); } @@ -761,11 +798,13 @@ void AnimationNodeBlendSpace2DEditor::_bind_methods() { 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); + + ClassDB::bind_method("_file_opened", &AnimationNodeBlendSpace2DEditor::_file_opened); + } AnimationNodeBlendSpace2DEditor *AnimationNodeBlendSpace2DEditor::singleton = NULL; @@ -781,14 +820,6 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { Ref<ButtonGroup> 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); @@ -968,13 +999,20 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { menu = memnew(PopupMenu); add_child(menu); - menu->connect("index_pressed", this, "_add_menu_type"); + menu->connect("id_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"); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + selected_point = -1; selected_triangle = -1; @@ -982,42 +1020,3 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { dragging_selected_attempt = false; } -void AnimationNodeBlendSpace2DEditorPlugin::edit(Object *p_object) { - - anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendSpace2D>(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 index a0e497804e..ae684985f3 100644 --- a/editor/plugins/animation_blend_space_2d_editor.h +++ b/editor/plugins/animation_blend_space_2d_editor.h @@ -9,18 +9,17 @@ #include "scene/gui/graph_edit.h" #include "scene/gui/popup.h" #include "scene/gui/tree.h" +#include "editor/plugins/animation_tree_editor_plugin.h" /** @author Juan Linietsky <reduzio@gmail.com> */ -class AnimationNodeBlendSpace2DEditor : public VBoxContainer { +class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeBlendSpace2DEditor, VBoxContainer); + GDCLASS(AnimationNodeBlendSpace2DEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeBlendSpace2D> blend_space; - HBoxContainer *goto_parent_hb; - ToolButton *goto_parent; PanelContainer *panel; ToolButton *tool_blend; @@ -93,38 +92,32 @@ class AnimationNodeBlendSpace2DEditor : public VBoxContainer { void _edit_point_pos(double); void _open_editor(); - void _goto_parent(); - void _removed_from_graph(); void _auto_triangles_toggled(); + StringName get_blend_position_path() const; + + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + protected: void _notification(int p_what); static void _bind_methods(); public: static AnimationNodeBlendSpace2DEditor *get_singleton() { return singleton; } - void edit(AnimationNodeBlendSpace2D *p_blend_space); + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); 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/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index c00ad451fa..42e32b9788 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -2,6 +2,7 @@ #include "core/io/resource_loader.h" #include "core/project_settings.h" +#include "editor/editor_inspector.h" #include "os/input.h" #include "os/keyboard.h" #include "scene/animation/animation_player.h" @@ -9,27 +10,6 @@ #include "scene/gui/panel.h" #include "scene/main/viewport.h" -void AnimationNodeBlendTreeEditor::edit(AnimationNodeBlendTree *p_blend_tree) { - - if (blend_tree.is_valid()) { - blend_tree->disconnect("removed_from_graph", this, "_removed_from_graph"); - } - - if (p_blend_tree) { - blend_tree = Ref<AnimationNodeBlendTree>(p_blend_tree); - } else { - blend_tree.unref(); - } - - if (blend_tree.is_null()) { - hide(); - } else { - blend_tree->connect("removed_from_graph", this, "_removed_from_graph"); - - _update_graph(); - } -} - void AnimationNodeBlendTreeEditor::add_custom_type(const String &p_name, const Ref<Script> &p_script) { for (int i = 0; i < add_options.size(); i++) { @@ -58,10 +38,19 @@ void AnimationNodeBlendTreeEditor::remove_custom_type(const Ref<Script> &p_scrip void AnimationNodeBlendTreeEditor::_update_options_menu() { + print_line("update options"); add_node->get_popup()->clear(); for (int i = 0; i < add_options.size(); i++) { - add_node->get_popup()->add_item(add_options[i].name); + add_node->get_popup()->add_item(add_options[i].name, i); } + + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + add_node->get_popup()->add_separator(); + add_node->get_popup()->add_item(TTR("Paste"), MENU_PASTE); + } + add_node->get_popup()->add_separator(); + add_node->get_popup()->add_item(TTR("Load.."), MENU_LOAD_FILE); } Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const { @@ -69,18 +58,28 @@ Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const { return Size2(10, 200); } +void AnimationNodeBlendTreeEditor::_property_changed(const StringName &p_property, const Variant &p_value) { + + AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_tree(); + updating = true; + undo_redo->create_action("Parameter Changed: " + String(p_property), UndoRedo::MERGE_ENDS); + undo_redo->add_do_property(tree, p_property, p_value); + undo_redo->add_undo_property(tree, p_property, tree->get(p_property)); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + updating = false; +} + void AnimationNodeBlendTreeEditor::_update_graph() { if (updating) return; + visible_properties.clear(); + graph->set_scroll_ofs(blend_tree->get_graph_offset() * EDSCALE); - if (blend_tree->get_parent().is_valid()) { - goto_parent->show(); - } else { - goto_parent->hide(); - } graph->clear_connections(); //erase all nodes for (int i = 0; i < graph->get_child_count(); i++) { @@ -107,7 +106,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { agnode->connect("changed", this, "_node_changed", varray(agnode->get_instance_id()), CONNECT_DEFERRED); } - node->set_offset(agnode->get_position() * EDSCALE); + node->set_offset(blend_tree->get_node_position(E->get()) * EDSCALE); node->set_title(agnode->get_caption()); node->set_name(E->get()); @@ -133,9 +132,28 @@ void AnimationNodeBlendTreeEditor::_update_graph() { node->set_slot(base + i, true, 0, get_color("font_color", "Label"), false, 0, Color()); } - node->connect("dragged", this, "_node_dragged", varray(agnode)); + List<PropertyInfo> pinfo; + agnode->get_parameter_list(&pinfo); + for (List<PropertyInfo>::Element *F = pinfo.front(); F; F = F->next()) { + + if (!(F->get().usage & PROPERTY_USAGE_EDITOR)) { + continue; + } + String base_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E->get()) + "/" + F->get().name; + EditorProperty *prop = EditorInspector::instantiate_property_editor(AnimationTreeEditor::get_singleton()->get_tree(), F->get().type, base_path, F->get().hint, F->get().hint_string, F->get().usage); + if (prop) { + prop->set_object_and_property(AnimationTreeEditor::get_singleton()->get_tree(), base_path); + prop->update_property(); + prop->set_name_split_ratio(0); + prop->connect("property_changed", this, "_property_changed"); + node->add_child(prop); + visible_properties.push_back(prop); + } + } + + node->connect("dragged", this, "_node_dragged", varray(E->get())); - if (EditorNode::get_singleton()->item_has_editor(agnode.ptr())) { + if (AnimationTreeEditor::get_singleton()->can_edit(agnode)) { node->add_child(memnew(HSeparator)); Button *open_in_editor = memnew(Button); open_in_editor->set_text(TTR("Open Editor")); @@ -169,7 +187,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { ProgressBar *pb = memnew(ProgressBar); - AnimationTree *player = anim->get_tree(); + AnimationTree *player = AnimationTreeEditor::get_singleton()->get_tree(); if (player->has_node(player->get_animation_player())) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(player->get_node(player->get_animation_player())); if (ap) { @@ -194,6 +212,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { mb->get_popup()->connect("index_pressed", this, "_anim_selected", varray(options, E->get()), CONNECT_DEFERRED); } + /* should be no longer necesary, as the boolean works Ref<AnimationNodeOneShot> oneshot = agnode; if (oneshot.is_valid()) { @@ -209,7 +228,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { play_stop->add_child(stop); play_stop->add_spacer(); node->add_child(play_stop); - } + } */ } List<AnimationNodeBlendTree::NodeConnection> connections; @@ -225,16 +244,44 @@ void AnimationNodeBlendTreeEditor::_update_graph() { } } -void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { +void AnimationNodeBlendTreeEditor::_file_opened(const String &p_file) { - ERR_FAIL_INDEX(p_idx, add_options.size()); + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_node(MENU_LOAD_FILE_CONFIRM); + } +} + +void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { Ref<AnimationNode> anode; - if (add_options[p_idx].type != String()) { + String base_name; + + if (p_idx == MENU_LOAD_FILE) { + + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_idx == MENU_LOAD_FILE_CONFIRM) { + anode = file_loaded; + file_loaded.unref(); + base_name = anode->get_class(); + } else if (p_idx == MENU_PASTE) { + + anode = EditorSettings::get_singleton()->get_resource_clipboard(); + ERR_FAIL_COND(!anode.is_valid()); + base_name = anode->get_class(); + } else if (add_options[p_idx].type != String()) { AnimationNode *an = Object::cast_to<AnimationNode>(ClassDB::instance(add_options[p_idx].type)); ERR_FAIL_COND(!an); anode = Ref<AnimationNode>(an); + base_name = add_options[p_idx].name; } else { ERR_FAIL_COND(add_options[p_idx].script.is_null()); String base_type = add_options[p_idx].script->get_instance_base_type(); @@ -242,13 +289,16 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { ERR_FAIL_COND(!an); anode = Ref<AnimationNode>(an); anode->set_script(add_options[p_idx].script.get_ref_ptr()); + base_name = add_options[p_idx].name; } + Ref<AnimationNodeOutput> out = anode; + if (out.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("Output node can't be added to the blend tree.")); + return; + } Point2 instance_pos = graph->get_scroll_ofs() + graph->get_size() * 0.5; - anode->set_position(instance_pos / EDSCALE); - - String base_name = add_options[p_idx].name; int base = 1; String name = base_name; while (blend_tree->has_node(name)) { @@ -257,19 +307,19 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { } undo_redo->create_action("Add Node to BlendTree"); - undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode); + undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode, instance_pos / EDSCALE); undo_redo->add_undo_method(blend_tree.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); } -void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, Ref<AnimationNode> p_node) { +void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which) { updating = true; undo_redo->create_action("Node Moved"); - undo_redo->add_do_method(p_node.ptr(), "set_position", p_to / EDSCALE); - undo_redo->add_undo_method(p_node.ptr(), "set_position", p_from / EDSCALE); + undo_redo->add_do_method(blend_tree.ptr(), "set_node_position", p_which, p_to / EDSCALE); + undo_redo->add_undo_method(blend_tree.ptr(), "set_node_position", p_which, p_from / EDSCALE); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -342,20 +392,6 @@ void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) { undo_redo->commit_action(); } -void AnimationNodeBlendTreeEditor::_oneshot_start(const StringName &p_name) { - - Ref<AnimationNodeOneShot> os = blend_tree->get_node(p_name); - ERR_FAIL_COND(!os.is_valid()); - os->start(); -} - -void AnimationNodeBlendTreeEditor::_oneshot_stop(const StringName &p_name) { - - Ref<AnimationNodeOneShot> os = blend_tree->get_node(p_name); - ERR_FAIL_COND(!os.is_valid()); - os->stop(); -} - void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) { GraphNode *gn = Object::cast_to<GraphNode>(p_node); @@ -373,13 +409,7 @@ void AnimationNodeBlendTreeEditor::_open_in_editor(const String &p_which) { Ref<AnimationNode> an = blend_tree->get_node(p_which); ERR_FAIL_COND(!an.is_valid()) - EditorNode::get_singleton()->edit_item(an.ptr()); -} - -void AnimationNodeBlendTreeEditor::_open_parent() { - if (blend_tree->get_parent().is_valid()) { - EditorNode::get_singleton()->edit_item(blend_tree->get_parent().ptr()); - } + AnimationTreeEditor::get_singleton()->enter_editor(p_which); } void AnimationNodeBlendTreeEditor::_filter_toggled() { @@ -417,14 +447,14 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano if (updating || _filter_edit != anode) return false; - NodePath player_path = anode->get_tree()->get_animation_player(); + NodePath player_path = AnimationTreeEditor::get_singleton()->get_tree()->get_animation_player(); - if (!anode->get_tree()->has_node(player_path)) { + if (!AnimationTreeEditor::get_singleton()->get_tree()->has_node(player_path)) { EditorNode::get_singleton()->show_warning(TTR("No animation player set, so unable to retrieve track names.")); return false; } - AnimationPlayer *player = Object::cast_to<AnimationPlayer>(anode->get_tree()->get_node(player_path)); + AnimationPlayer *player = Object::cast_to<AnimationPlayer>(AnimationTreeEditor::get_singleton()->get_tree()->get_node(player_path)); if (!player) { EditorNode::get_singleton()->show_warning(TTR("Player path set is invalid, so unable to retrieve track names.")); return false; @@ -593,8 +623,6 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); - error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); error_label->add_color_override("font_color", get_color("error_color", "Editor")); } @@ -603,12 +631,10 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { String error; - if (!blend_tree->get_tree()) { - error = TTR("BlendTree does not belong to an AnimationTree node."); - } else if (!blend_tree->get_tree()->is_active()) { + if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (blend_tree->get_tree()->is_state_invalid()) { - error = blend_tree->get_tree()->get_invalid_state_reason(); + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); } if (error != error_label->get_text()) { @@ -624,13 +650,13 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { blend_tree->get_node_connections(&conns); for (List<AnimationNodeBlendTree::NodeConnection>::Element *E = conns.front(); E; E = E->next()) { float activity = 0; - if (blend_tree->get_tree() && !blend_tree->get_tree()->is_state_invalid()) { + if (AnimationTreeEditor::get_singleton()->get_tree() && !AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { activity = blend_tree->get_connection_activity(E->get().input_node, E->get().input_index); } graph->set_connection_activity(E->get().output_node, 0, E->get().input_node, E->get().input_index, activity); } - AnimationTree *graph_player = blend_tree->get_tree(); + AnimationTree *graph_player = AnimationTreeEditor::get_singleton()->get_tree(); AnimationPlayer *player = NULL; if (graph_player->has_node(graph_player->get_animation_player())) { player = Object::cast_to<AnimationPlayer>(graph_player->get_node(graph_player->get_animation_player())); @@ -650,6 +676,14 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { } } } + + for (int i = 0; i < visible_properties.size(); i++) { + visible_properties[i]->update_property(); + } + } + + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + set_process(is_visible_in_tree()); } } @@ -664,9 +698,9 @@ void AnimationNodeBlendTreeEditor::_scroll_changed(const Vector2 &p_scroll) { void AnimationNodeBlendTreeEditor::_node_changed(ObjectID p_node) { AnimationNode *an = Object::cast_to<AnimationNode>(ObjectDB::get_instance(p_node)); - if (an && an->get_parent() == blend_tree) { - _update_graph(); - } + //if (an && an->get_parent() == blend_tree) { + _update_graph(); + //} } void AnimationNodeBlendTreeEditor::_bind_methods() { @@ -680,17 +714,17 @@ void AnimationNodeBlendTreeEditor::_bind_methods() { ClassDB::bind_method("_disconnection_request", &AnimationNodeBlendTreeEditor::_disconnection_request); ClassDB::bind_method("_node_selected", &AnimationNodeBlendTreeEditor::_node_selected); ClassDB::bind_method("_open_in_editor", &AnimationNodeBlendTreeEditor::_open_in_editor); - ClassDB::bind_method("_open_parent", &AnimationNodeBlendTreeEditor::_open_parent); ClassDB::bind_method("_scroll_changed", &AnimationNodeBlendTreeEditor::_scroll_changed); ClassDB::bind_method("_delete_request", &AnimationNodeBlendTreeEditor::_delete_request); ClassDB::bind_method("_edit_filters", &AnimationNodeBlendTreeEditor::_edit_filters); ClassDB::bind_method("_update_filters", &AnimationNodeBlendTreeEditor::_update_filters); ClassDB::bind_method("_filter_edited", &AnimationNodeBlendTreeEditor::_filter_edited); ClassDB::bind_method("_filter_toggled", &AnimationNodeBlendTreeEditor::_filter_toggled); - ClassDB::bind_method("_oneshot_start", &AnimationNodeBlendTreeEditor::_oneshot_start); - ClassDB::bind_method("_oneshot_stop", &AnimationNodeBlendTreeEditor::_oneshot_stop); ClassDB::bind_method("_node_changed", &AnimationNodeBlendTreeEditor::_node_changed); ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendTreeEditor::_removed_from_graph); + ClassDB::bind_method("_property_changed", &AnimationNodeBlendTreeEditor::_property_changed); + ClassDB::bind_method("_file_opened", &AnimationNodeBlendTreeEditor::_file_opened); + ClassDB::bind_method("_update_options_menu", &AnimationNodeBlendTreeEditor::_update_options_menu); ClassDB::bind_method("_anim_selected", &AnimationNodeBlendTreeEditor::_anim_selected); } @@ -708,7 +742,9 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1) - ERR_FAIL_COND(new_name == prev_name); + if (new_name == prev_name) { + return; //nothing to do + } String base_name = new_name; int base = 1; @@ -718,22 +754,61 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima name = base_name + " " + itos(base); } + String base_path = AnimationTreeEditor::get_singleton()->get_base_path(); + updating = true; undo_redo->create_action("Node Renamed"); undo_redo->add_do_method(blend_tree.ptr(), "rename_node", prev_name, name); undo_redo->add_undo_method(blend_tree.ptr(), "rename_node", name, prev_name); + undo_redo->add_do_method(AnimationTreeEditor::get_singleton()->get_tree(), "rename_parameter", base_path + prev_name, base_path + name); + undo_redo->add_undo_method(AnimationTreeEditor::get_singleton()->get_tree(), "rename_parameter", base_path + name, base_path + prev_name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); updating = false; gn->set_name(new_name); gn->set_size(gn->get_minimum_size()); + + //change editors accordingly + for (int i = 0; i < visible_properties.size(); i++) { + String pname = visible_properties[i]->get_edited_property().operator String(); + if (pname.begins_with(base_path + prev_name)) { + String new_name = pname.replace_first(base_path + prev_name, base_path + name); + visible_properties[i]->set_object_and_property(visible_properties[i]->get_edited_object(), new_name); + } + } } void AnimationNodeBlendTreeEditor::_node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node) { _node_renamed(le->call("get_text"), p_node); } +bool AnimationNodeBlendTreeEditor::can_edit(const Ref<AnimationNode> &p_node) { + Ref<AnimationNodeBlendTree> bt = p_node; + return bt.is_valid(); +} + +void AnimationNodeBlendTreeEditor::edit(const Ref<AnimationNode> &p_node) { + + if (blend_tree.is_valid()) { + blend_tree->disconnect("removed_from_graph", this, "_removed_from_graph"); + } + + if (p_node.is_valid()) { + blend_tree = p_node; + } else { + blend_tree.unref(); + } + + if (blend_tree.is_null()) { + hide(); + } else { + blend_tree->connect("removed_from_graph", this, "_removed_from_graph"); + + _update_graph(); + } +} + AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { singleton = this; @@ -757,13 +832,8 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { graph->get_zoom_hbox()->add_child(add_node); add_node->set_text(TTR("Add Node..")); graph->get_zoom_hbox()->move_child(add_node, 0); - add_node->get_popup()->connect("index_pressed", this, "_add_node"); - - goto_parent = memnew(Button); - graph->get_zoom_hbox()->add_child(goto_parent); - graph->get_zoom_hbox()->move_child(goto_parent, 0); - goto_parent->hide(); - goto_parent->connect("pressed", this, "_open_parent"); + add_node->get_popup()->connect("id_pressed", this, "_add_node"); + add_node->connect("about_to_show", this, "_update_options_menu"); add_options.push_back(AddOption("Animation", "AnimationNodeAnimation")); add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot")); @@ -804,45 +874,10 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { filters->set_hide_root(true); filters->connect("item_edited", this, "_filter_edited"); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); undo_redo = EditorNode::get_singleton()->get_undo_redo(); } - -void AnimationNodeBlendTreeEditorPlugin::edit(Object *p_object) { - - anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendTree>(p_object)); -} - -bool AnimationNodeBlendTreeEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("AnimationNodeBlendTree"); -} - -void AnimationNodeBlendTreeEditorPlugin::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); - } -} - -AnimationNodeBlendTreeEditorPlugin::AnimationNodeBlendTreeEditorPlugin(EditorNode *p_node) { - - editor = p_node; - anim_tree_editor = memnew(AnimationNodeBlendTreeEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); - - button = editor->add_bottom_panel_item(TTR("BlendTree"), anim_tree_editor); - button->hide(); -} - -AnimationNodeBlendTreeEditorPlugin::~AnimationNodeBlendTreeEditorPlugin() { -} diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index deba3b2b0e..35ecc32979 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -3,6 +3,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" +#include "editor/plugins/animation_tree_editor_plugin.h" #include "editor/property_editor.h" #include "scene/animation/animation_blend_tree.h" #include "scene/gui/button.h" @@ -13,14 +14,13 @@ @author Juan Linietsky <reduzio@gmail.com> */ -class AnimationNodeBlendTreeEditor : public VBoxContainer { +class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeBlendTreeEditor, VBoxContainer); + GDCLASS(AnimationNodeBlendTreeEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeBlendTree> blend_tree; GraphEdit *graph; MenuButton *add_node; - Button *goto_parent; PanelContainer *error_panel; Label *error_label; @@ -32,6 +32,7 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { CheckBox *filter_enabled; Map<StringName, ProgressBar *> animations; + Vector<EditorProperty *> visible_properties; void _update_graph(); @@ -52,7 +53,7 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { static AnimationNodeBlendTreeEditor *singleton; - void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, Ref<AnimationNode> p_node); + void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which); void _node_renamed(const String &p_text, Ref<AnimationNode> p_node); void _node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node); @@ -64,11 +65,8 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { void _scroll_changed(const Vector2 &p_scroll); void _node_selected(Object *p_node); void _open_in_editor(const String &p_which); - void _open_parent(); void _anim_selected(int p_index, Array p_options, const String &p_node); void _delete_request(const String &p_which); - void _oneshot_start(const StringName &p_name); - void _oneshot_stop(const StringName &p_name); bool _update_filters(const Ref<AnimationNode> &anode); void _edit_filters(const String &p_which); @@ -78,8 +76,19 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { void _node_changed(ObjectID p_node); + void _property_changed(const StringName &p_property, const Variant &p_value); void _removed_from_graph(); + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + protected: void _notification(int p_what); static void _bind_methods(); @@ -91,27 +100,11 @@ public: void remove_custom_type(const Ref<Script> &p_script); virtual Size2 get_minimum_size() const; - void edit(AnimationNodeBlendTree *p_blend_tree); - AnimationNodeBlendTreeEditor(); -}; -class AnimationNodeBlendTreeEditorPlugin : public EditorPlugin { + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); - GDCLASS(AnimationNodeBlendTreeEditorPlugin, EditorPlugin); - - AnimationNodeBlendTreeEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "BlendTree"; } - 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); - - AnimationNodeBlendTreeEditorPlugin(EditorNode *p_node); - ~AnimationNodeBlendTreeEditorPlugin(); + AnimationNodeBlendTreeEditor(); }; #endif // ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index ee450333c8..3a65cb9b38 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -11,22 +11,17 @@ #include "scene/gui/panel.h" #include "scene/main/viewport.h" -void AnimationNodeStateMachineEditor::edit(AnimationNodeStateMachine *p_state_machine) { +bool AnimationNodeStateMachineEditor::can_edit(const Ref<AnimationNode> &p_node) { - if (state_machine.is_valid()) { - state_machine->disconnect("removed_from_graph", this, "_removed_from_graph"); - } + Ref<AnimationNodeStateMachine> ansm = p_node; + return ansm.is_valid(); +} - if (p_state_machine) { - state_machine = Ref<AnimationNodeStateMachine>(p_state_machine); - } else { - state_machine.unref(); - } +void AnimationNodeStateMachineEditor::edit(const Ref<AnimationNode> &p_node) { - if (state_machine.is_null()) { - hide(); - } else { - state_machine->connect("removed_from_graph", this, "_removed_from_graph"); + state_machine = p_node; + + if (state_machine.is_valid()) { selected_transition_from = StringName(); selected_transition_to = StringName(); @@ -38,6 +33,10 @@ void AnimationNodeStateMachineEditor::edit(AnimationNodeStateMachine *p_state_ma void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEvent> &p_event) { + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + if (playback.is_null()) + return; + Ref<InputEventKey> k = p_event; if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && !k->is_echo()) { if (selected_node != StringName() || selected_transition_to != StringName() || selected_transition_from != StringName()) { @@ -59,7 +58,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); menu->add_submenu_item(TTR("Add Animation"), "animations"); - AnimationTree *gp = state_machine->get_tree(); + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); ERR_FAIL_COND(!gp); if (gp && gp->has_node(gp->get_animation_player())) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); @@ -79,9 +78,17 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv if (name == "Animation") continue; // nope int idx = menu->get_item_count(); - menu->add_item(vformat("Add %s", name)); + menu->add_item(vformat("Add %s", name), idx); menu->set_item_metadata(idx, E->get()); } + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } + menu->add_separator(); + menu->add_item(TTR("Load.."), MENU_LOAD_FILE); menu->set_global_position(state_machine_draw->get_global_transform().xform(mb->get_position())); menu->popup(); @@ -98,18 +105,12 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order if (node_rects[i].play.has_point(mb->get_position())) { //edit name - if (play_mode->get_selected() == 1 || !state_machine->is_playing()) { + if (play_mode->get_selected() == 1 || !playback->is_playing()) { //start - state_machine->start(node_rects[i].node_name); + playback->start(node_rects[i].node_name); } else { //travel - if (!state_machine->travel(node_rects[i].node_name)) { - - state_machine->start(node_rects[i].node_name); - //removing this due to usability.. - //error_time = 5; - //error_text = vformat(TTR("No path found from '%s' to '%s'."), state_machine->get_current_node(), node_rects[i].node_name); - } + playback->travel(node_rects[i].node_name); } state_machine_draw->update(); return; @@ -196,8 +197,8 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv Ref<AnimationNode> an = state_machine->get_node(selected_node); updating = true; undo_redo->create_action("Move Node"); - undo_redo->add_do_method(an.ptr(), "set_position", an->get_position() + drag_ofs / EDSCALE); - undo_redo->add_undo_method(an.ptr(), "set_position", an->get_position()); + undo_redo->add_do_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node) + drag_ofs / EDSCALE); + undo_redo->add_undo_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node)); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -293,7 +294,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv snap_y = StringName(); { //snap - Vector2 cpos = state_machine->get_node(selected_node)->get_position() + drag_ofs / EDSCALE; + Vector2 cpos = state_machine->get_node_position(selected_node) + drag_ofs / EDSCALE; List<StringName> nodes; state_machine->get_node_list(&nodes); @@ -303,7 +304,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) { if (E->get() == selected_node) continue; - Vector2 npos = state_machine->get_node(E->get())->get_position(); + Vector2 npos = state_machine->get_node_position(E->get()); float d_x = ABS(npos.x - cpos.x); if (d_x < MIN(5, best_d_x)) { @@ -372,19 +373,58 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv } } +void AnimationNodeStateMachineEditor::_file_opened(const String &p_file) { + + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_menu_type(MENU_LOAD_FILE_CONFIRM); + } +} + void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) { - String type = menu->get_item_metadata(p_index); + String base_name; + Ref<AnimationRootNode> node; - Object *obj = ClassDB::instance(type); - ERR_FAIL_COND(!obj); - AnimationNode *an = Object::cast_to<AnimationNode>(obj); - ERR_FAIL_COND(!an); + if (p_index == MENU_LOAD_FILE) { - Ref<AnimationNode> node(an); - node->set_position(add_node_pos); + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_index == MENU_LOAD_FILE_CONFIRM) { + node = file_loaded; + file_loaded.unref(); + } else if (p_index == MENU_PASTE) { + + node = EditorSettings::get_singleton()->get_resource_clipboard(); + + } else { + String type = menu->get_item_metadata(p_index); + + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to<AnimationNode>(obj); + ERR_FAIL_COND(!an); + + node = Ref<AnimationNode>(an); + base_name = type.replace_first("AnimationNode", ""); + } + + if (!node.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed.")); + return; + } + + if (base_name == String()) { + + base_name = node->get_class().replace_first("AnimationNode", ""); + } - String base_name = type.replace_first("AnimationNode", ""); int base = 1; String name = base_name; while (state_machine->has_node(name)) { @@ -394,7 +434,7 @@ void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) { updating = true; undo_redo->create_action("Add Node"); - undo_redo->add_do_method(state_machine.ptr(), "add_node", name, node); + undo_redo->add_do_method(state_machine.ptr(), "add_node", name, node, add_node_pos); undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -419,11 +459,9 @@ void AnimationNodeStateMachineEditor::_add_animation_type(int p_index) { name = base_name + " " + itos(base); } - anim->set_position(add_node_pos); - updating = true; undo_redo->create_action("Add Node"); - undo_redo->add_do_method(state_machine.ptr(), "add_node", name, anim); + undo_redo->add_do_method(state_machine.ptr(), "add_node", name, anim, add_node_pos); undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -502,6 +540,8 @@ void AnimationNodeStateMachineEditor::_clip_dst_line_to_rect(Vector2 &r_from, Ve void AnimationNodeStateMachineEditor::_state_machine_draw() { + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + Ref<StyleBox> style = get_stylebox("frame", "GraphNode"); Ref<StyleBox> style_selected = get_stylebox("selectedframe", "GraphNode"); @@ -515,10 +555,17 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { linecolor.a *= 0.3; Ref<StyleBox> playing_overlay = get_stylebox("position", "GraphNode"); - bool playing = state_machine->is_playing(); - StringName current = state_machine->get_current_node(); - StringName blend_from = state_machine->get_blend_from_node(); - Vector<StringName> travel_path = state_machine->get_travel_path(); + bool playing = false; + StringName current; + StringName blend_from; + Vector<StringName> travel_path; + + if (playback.is_valid()) { + playing = playback->is_playing(); + current = playback->get_current_node(); + blend_from = playback->get_blend_from_node(); + travel_path = playback->get_travel_path(); + } if (state_machine_draw->has_focus()) { state_machine_draw->draw_rect(Rect2(Point2(), state_machine_draw->get_size()), accent, false); @@ -534,13 +581,13 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { //snap lines if (dragging_selected) { - Vector2 from = (state_machine->get_node(selected_node)->get_position() * EDSCALE) + drag_ofs - state_machine->get_graph_offset() * EDSCALE; + Vector2 from = (state_machine->get_node_position(selected_node) * EDSCALE) + drag_ofs - state_machine->get_graph_offset() * EDSCALE; if (snap_x != StringName()) { - Vector2 to = (state_machine->get_node(snap_x)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + Vector2 to = (state_machine->get_node_position(snap_x) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; state_machine_draw->draw_line(from, to, linecolor, 2); } if (snap_y != StringName()) { - Vector2 to = (state_machine->get_node(snap_y)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + Vector2 to = (state_machine->get_node_position(snap_y) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; state_machine_draw->draw_line(from, to, linecolor, 2); } } @@ -563,7 +610,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } Vector2 offset; - offset += anode->get_position() * EDSCALE; + offset += state_machine->get_node_position(E->get()) * EDSCALE; if (selected_node == E->get() && dragging_selected) { offset += drag_ofs; } @@ -588,10 +635,10 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { //draw conecting line for potential new transition if (connecting) { - Vector2 from = (state_machine->get_node(connecting_from)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + Vector2 from = (state_machine->get_node_position(connecting_from) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; Vector2 to; if (connecting_to_node != StringName()) { - to = (state_machine->get_node(connecting_to_node)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + to = (state_machine->get_node_position(connecting_to_node) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; } else { to = connecting_to; } @@ -617,15 +664,17 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { TransitionLine tl; tl.from_node = state_machine->get_transition_from(i); Vector2 ofs_from = (dragging_selected && tl.from_node == selected_node) ? drag_ofs : Vector2(); - tl.from = (state_machine->get_node(tl.from_node)->get_position() * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE; + tl.from = (state_machine->get_node_position(tl.from_node) * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE; tl.to_node = state_machine->get_transition_to(i); Vector2 ofs_to = (dragging_selected && tl.to_node == selected_node) ? drag_ofs : Vector2(); - tl.to = (state_machine->get_node(tl.to_node)->get_position() * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE; + tl.to = (state_machine->get_node_position(tl.to_node) * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE; Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i); tl.disabled = tr->is_disabled(); tl.auto_advance = tr->has_auto_advance(); + tl.advance_condition_name = tr->get_advance_condition_name(); + tl.advance_condition_state = false; tl.mode = tr->get_switch_mode(); tl.width = tr_bidi_offset; @@ -665,7 +714,14 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } } } - _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, selected, travel, tl.auto_advance); + + bool auto_advance = tl.auto_advance; + StringName fullpath = AnimationTreeEditor::get_singleton()->get_base_path() + String(tl.advance_condition_name); + if (tl.advance_condition_name != StringName() && bool(AnimationTreeEditor::get_singleton()->get_tree()->get(fullpath))) { + tl.advance_condition_state = true; + auto_advance = true; + } + _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, selected, travel, auto_advance); transition_lines.push_back(tl); } @@ -675,7 +731,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { String name = node_rects[i].node_name; Ref<AnimationNode> anode = state_machine->get_node(name); - bool needs_editor = EditorNode::get_singleton()->item_has_editor(anode.ptr()); + bool needs_editor = AnimationTreeEditor::get_singleton()->can_edit(anode); Ref<StyleBox> sb = name == selected_node ? style_selected : style; int strsize = font->get_string_size(name).width; @@ -757,12 +813,14 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { - if (!state_machine->is_playing()) + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + + if (!playback.is_valid() || !playback->is_playing()) return; int idx = -1; for (int i = 0; node_rects.size(); i++) { - if (node_rects[i].node_name == state_machine->get_current_node()) { + if (node_rects[i].node_name == playback->get_current_node()) { idx = i; break; } @@ -785,9 +843,9 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { } to.y = from.y; - float len = MAX(0.0001, state_machine->get_current_length()); + float len = MAX(0.0001, playback->get_current_length()); - float pos = CLAMP(state_machine->get_current_play_pos(), 0, len); + float pos = CLAMP(playback->get_current_play_pos(), 0, len); float c = pos / len; Color fg = get_color("font_color", "Label"); Color bg = fg; @@ -807,12 +865,6 @@ void AnimationNodeStateMachineEditor::_update_graph() { updating = true; - if (state_machine->get_parent().is_valid()) { - goto_parent_hbox->show(); - } else { - goto_parent_hbox->hide(); - } - state_machine_draw->update(); updating = false; @@ -824,7 +876,6 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { 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")); - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); tool_select->set_icon(get_icon("ToolSelect", "EditorIcons")); tool_create->set_icon(get_icon("ToolAddNode", "EditorIcons")); @@ -856,19 +907,21 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { String error; + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + if (error_time > 0) { error = error_text; error_time -= get_process_delta_time(); - } else if (!state_machine->get_tree()) { - error = TTR("StateMachine does not belong to an AnimationTree node."); - } else if (!state_machine->get_tree()->is_active()) { + } else if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (state_machine->get_tree()->is_state_invalid()) { - error = state_machine->get_tree()->get_invalid_state_reason(); - } else if (state_machine->get_parent().is_valid() && state_machine->get_parent()->is_class("AnimationNodeStateMachine")) { + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); + /*} else if (state_machine->get_parent().is_valid() && state_machine->get_parent()->is_class("AnimationNodeStateMachine")) { if (state_machine->get_start_node() == StringName() || state_machine->get_end_node() == StringName()) { error = TTR("Start and end nodes are needed for a sub-transition."); - } + }*/ + } else if (playback.is_null()) { + error = vformat(TTR("No playback resource set at path: %s."), AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); } if (error != error_label->get_text()) { @@ -904,14 +957,38 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { break; } + if (transition_lines[i].advance_condition_name != state_machine->get_transition(tidx)->get_advance_condition_name()) { + state_machine_draw->update(); + break; + } + if (transition_lines[i].mode != state_machine->get_transition(tidx)->get_switch_mode()) { state_machine_draw->update(); break; } + + bool acstate = transition_lines[i].advance_condition_name != StringName() && bool(AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + String(transition_lines[i].advance_condition_name))); + + if (transition_lines[i].advance_condition_state != acstate) { + state_machine_draw->update(); + break; + } } bool same_travel_path = true; - Vector<StringName> tp = state_machine->get_travel_path(); + Vector<StringName> tp; + bool is_playing = false; + StringName current_node; + StringName blend_from_node; + float play_pos = 0; + + if (playback.is_valid()) { + tp = playback->get_travel_path(); + is_playing = playback->is_playing(); + current_node = playback->get_current_node(); + blend_from_node = playback->get_blend_from_node(); + play_pos = playback->get_current_play_pos(); + } { @@ -928,37 +1005,32 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { } //update if travel state changed - if (!same_travel_path || last_active != state_machine->is_playing() || last_current_node != state_machine->get_current_node() || last_blend_from_node != state_machine->get_blend_from_node()) { + if (!same_travel_path || last_active != is_playing || last_current_node != current_node || last_blend_from_node != blend_from_node) { state_machine_draw->update(); last_travel_path = tp; - last_current_node = state_machine->get_current_node(); - last_active = state_machine->is_playing(); - last_blend_from_node = state_machine->get_blend_from_node(); + last_current_node = current_node; + last_active = is_playing; + last_blend_from_node = blend_from_node; state_machine_play_pos->update(); } - if (last_play_pos != state_machine->get_current_play_pos()) { + if (last_play_pos != play_pos) { - last_play_pos = state_machine->get_current_play_pos(); + last_play_pos = play_pos; state_machine_play_pos->update(); } } if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { over_node = StringName(); + set_process(is_visible_in_tree()); } } void AnimationNodeStateMachineEditor::_open_editor(const String &p_name) { - Ref<AnimationNode> an = state_machine->get_node(p_name); - ERR_FAIL_COND(!an.is_valid()); - EditorNode::get_singleton()->edit_item(an.ptr()); -} - -void AnimationNodeStateMachineEditor::_goto_parent() { - EditorNode::get_singleton()->edit_item(state_machine->get_parent().ptr()); + AnimationTreeEditor::get_singleton()->enter_editor(p_name); } void AnimationNodeStateMachineEditor::_removed_from_graph() { @@ -1114,7 +1186,6 @@ void AnimationNodeStateMachineEditor::_bind_methods() { ClassDB::bind_method("_name_edited", &AnimationNodeStateMachineEditor::_name_edited); - ClassDB::bind_method("_goto_parent", &AnimationNodeStateMachineEditor::_goto_parent); ClassDB::bind_method("_removed_from_graph", &AnimationNodeStateMachineEditor::_removed_from_graph); ClassDB::bind_method("_open_editor", &AnimationNodeStateMachineEditor::_open_editor); @@ -1124,6 +1195,7 @@ void AnimationNodeStateMachineEditor::_bind_methods() { ClassDB::bind_method("_autoplay_selected", &AnimationNodeStateMachineEditor::_autoplay_selected); ClassDB::bind_method("_end_selected", &AnimationNodeStateMachineEditor::_end_selected); ClassDB::bind_method("_update_mode", &AnimationNodeStateMachineEditor::_update_mode); + ClassDB::bind_method("_file_opened", &AnimationNodeStateMachineEditor::_file_opened); } AnimationNodeStateMachineEditor *AnimationNodeStateMachineEditor::singleton = NULL; @@ -1136,13 +1208,6 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { HBoxContainer *top_hb = memnew(HBoxContainer); add_child(top_hb); - goto_parent_hbox = memnew(HBoxContainer); - goto_parent = memnew(ToolButton); - goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED); - goto_parent_hbox->add_child(goto_parent); - goto_parent_hbox->add_child(memnew(VSeparator)); - top_hb->add_child(goto_parent_hbox); - Ref<ButtonGroup> bg; bg.instance(); @@ -1248,7 +1313,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { menu = memnew(PopupMenu); add_child(menu); - menu->connect("index_pressed", this, "_add_menu_type"); + menu->connect("id_pressed", this, "_add_menu_type"); animations_menu = memnew(PopupMenu); menu->add_child(animations_menu); @@ -1261,6 +1326,13 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { name_edit->connect("text_entered", this, "_name_edited"); name_edit->set_as_toplevel(true); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + over_text = false; over_node_what = -1; @@ -1271,43 +1343,3 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { error_time = 0; } - -void AnimationNodeStateMachineEditorPlugin::edit(Object *p_object) { - - anim_tree_editor->edit(Object::cast_to<AnimationNodeStateMachine>(p_object)); -} - -bool AnimationNodeStateMachineEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("AnimationNodeStateMachine"); -} - -void AnimationNodeStateMachineEditorPlugin::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); - } -} - -AnimationNodeStateMachineEditorPlugin::AnimationNodeStateMachineEditorPlugin(EditorNode *p_node) { - - editor = p_node; - anim_tree_editor = memnew(AnimationNodeStateMachineEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); - - button = editor->add_bottom_panel_item(TTR("StateMachine"), anim_tree_editor); - button->hide(); -} - -AnimationNodeStateMachineEditorPlugin::~AnimationNodeStateMachineEditorPlugin() { -} diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index efd3de7415..49d08607cf 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -3,6 +3,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" +#include "editor/plugins/animation_tree_editor_plugin.h" #include "editor/property_editor.h" #include "scene/animation/animation_node_state_machine.h" #include "scene/gui/button.h" @@ -10,9 +11,9 @@ #include "scene/gui/popup.h" #include "scene/gui/tree.h" -class AnimationNodeStateMachineEditor : public VBoxContainer { +class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeStateMachineEditor, VBoxContainer); + GDCLASS(AnimationNodeStateMachineEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeStateMachine> state_machine; @@ -29,9 +30,6 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { OptionButton *transition_mode; OptionButton *play_mode; - HBoxContainer *goto_parent_hbox; - ToolButton *goto_parent; - PanelContainer *panel; StringName selected_node; @@ -79,8 +77,6 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { void _add_menu_type(int p_index); void _add_animation_type(int p_index); - void _goto_parent(); - void _removed_from_graph(); struct NodeRect { @@ -99,6 +95,8 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { Vector2 from; Vector2 to; AnimationNodeStateMachineTransition::SwitchMode mode; + StringName advance_condition_name; + bool advance_condition_state; bool disabled; bool auto_advance; float width; @@ -135,33 +133,25 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { float error_time; String error_text; + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + protected: void _notification(int p_what); static void _bind_methods(); public: static AnimationNodeStateMachineEditor *get_singleton() { return singleton; } - void edit(AnimationNodeStateMachine *p_state_machine); + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); AnimationNodeStateMachineEditor(); }; -class AnimationNodeStateMachineEditorPlugin : public EditorPlugin { - - GDCLASS(AnimationNodeStateMachineEditorPlugin, EditorPlugin); - - AnimationNodeStateMachineEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "StateMachine"; } - 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); - - AnimationNodeStateMachineEditorPlugin(EditorNode *p_node); - ~AnimationNodeStateMachineEditorPlugin(); -}; - #endif // ANIMATION_STATE_MACHINE_EDITOR_H diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index 25582ae0b9..19921ef54f 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -1,1418 +1,247 @@ -/*************************************************************************/ -/* animation_tree_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - #include "animation_tree_editor_plugin.h" +#include "animation_blend_space_1d_editor.h" +#include "animation_blend_space_2d_editor.h" +#include "animation_blend_tree_editor_plugin.h" +#include "animation_state_machine_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" +#include "scene/scene_string_names.h" -void AnimationTreeEditor::edit(AnimationTreePlayer *p_anim_tree) { +void AnimationTreeEditor::edit(AnimationTree *p_tree) { - anim_tree = p_anim_tree; + if (tree == p_tree) + return; - if (!anim_tree) { - hide(); - } else { - order.clear(); - p_anim_tree->get_node_list(&order); - /* - for(List<StringName>::Element* E=order.front();E;E=E->next()) { + tree = p_tree; - if (E->get() >= (int)last_id) - last_id=E->get()+1; - }*/ - play_button->set_pressed(p_anim_tree->is_active()); - //read the orders + Vector<String> path; + if (tree->has_meta("_tree_edit_path")) { + path = tree->get_meta("_tree_edit_path"); + edit_path(path); + } else { + current_root = 0; } } -Size2 AnimationTreeEditor::_get_maximum_size() { - - Size2 max; - - for (List<StringName>::Element *E = order.front(); E; E = E->next()) { +void AnimationTreeEditor::_path_button_pressed(int p_path) { - Point2 pos = anim_tree->node_get_position(E->get()); - - if (click_type == CLICK_NODE && click_node == E->get()) { + Ref<AnimationNode> node = tree->get_tree_root(); + if (node.is_null()) + return; - pos += click_motion - click_pos; + edited_path.clear(); + if (p_path >= 0) { + for (int i = 0; i <= p_path; i++) { + Ref<AnimationNode> child = node->get_child_by_name(button_path[i]); + ERR_BREAK(child.is_null()); + node = child; + edited_path.push_back(button_path[i]); } - pos += get_node_size(E->get()); - if (pos.x > max.x) - max.x = pos.x; - if (pos.y > max.y) - max.y = pos.y; } - return max; -} - -const char *AnimationTreeEditor::_node_type_names[] = { "Output", "Animation", "OneShot", "Mix", "Blend2", "Blend3", "Blend4", "TimeScale", "TimeSeek", "Transition" }; - -Size2 AnimationTreeEditor::get_node_size(const StringName &p_node) const { - - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - - Size2 size = style->get_minimum_size(); - - int count = 2; // title and name - int inputs = anim_tree->node_get_input_count(p_node); - count += inputs ? inputs : 1; - String name = p_node; - - float name_w = font->get_string_size(name).width; - float type_w = font->get_string_size(String(_node_type_names[type])).width; - float max_w = MAX(name_w, type_w); - - switch (type) { - case AnimationTreePlayer::NODE_TIMESEEK: - case AnimationTreePlayer::NODE_OUTPUT: { - } break; - case AnimationTreePlayer::NODE_ANIMATION: - case AnimationTreePlayer::NODE_ONESHOT: - case AnimationTreePlayer::NODE_MIX: - case AnimationTreePlayer::NODE_BLEND2: - case AnimationTreePlayer::NODE_BLEND3: - case AnimationTreePlayer::NODE_BLEND4: - case AnimationTreePlayer::NODE_TIMESCALE: - case AnimationTreePlayer::NODE_TRANSITION: { - - size.height += font->get_height(); - } break; - case AnimationTreePlayer::NODE_MAX: { + for (int i = 0; i < editors.size(); i++) { + if (editors[i]->can_edit(node)) { + editors[i]->edit(node); + editors[i]->show(); + } else { + editors[i]->edit(Ref<AnimationNode>()); + editors[i]->hide(); } } - - size.x += max_w + 20; - size.y += count * (font->get_height() + get_constant("vseparation", "PopupMenu")); - - return size; } -void AnimationTreeEditor::_edit_dialog_changede(String) { - - edit_dialog->hide(); -} - -void AnimationTreeEditor::_edit_dialog_changeds(String s) { - - _edit_dialog_changed(); -} - -void AnimationTreeEditor::_edit_dialog_changedf(float) { - - _edit_dialog_changed(); -} - -void AnimationTreeEditor::_edit_dialog_changed() { - - if (updating_edit) - return; - - if (renaming_edit) { - - if (anim_tree->node_rename(edited_node, edit_line[0]->get_text()) == OK) { - for (List<StringName>::Element *E = order.front(); E; E = E->next()) { - - if (E->get() == edited_node) - E->get() = edit_line[0]->get_text(); - } - edited_node = edit_line[0]->get_text(); - } - update(); - return; +void AnimationTreeEditor::_update_path() { + while (path_hb->get_child_count()) { + memdelete(path_hb->get_child(0)); } - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); - - switch (type) { - - case AnimationTreePlayer::NODE_TIMESCALE: - anim_tree->timescale_node_set_scale(edited_node, edit_line[0]->get_text().to_double()); - break; - case AnimationTreePlayer::NODE_ONESHOT: - anim_tree->oneshot_node_set_fadein_time(edited_node, edit_line[0]->get_text().to_double()); - anim_tree->oneshot_node_set_fadeout_time(edited_node, edit_line[1]->get_text().to_double()); - anim_tree->oneshot_node_set_autorestart_delay(edited_node, edit_line[2]->get_text().to_double()); - anim_tree->oneshot_node_set_autorestart_random_delay(edited_node, edit_line[3]->get_text().to_double()); - anim_tree->oneshot_node_set_autorestart(edited_node, edit_check->is_pressed()); - anim_tree->oneshot_node_set_mix_mode(edited_node, edit_option->get_selected()); - - break; - - case AnimationTreePlayer::NODE_MIX: - - anim_tree->mix_node_set_amount(edited_node, edit_scroll[0]->get_value()); - break; - case AnimationTreePlayer::NODE_BLEND2: - anim_tree->blend2_node_set_amount(edited_node, edit_scroll[0]->get_value()); - - break; - - case AnimationTreePlayer::NODE_BLEND3: - anim_tree->blend3_node_set_amount(edited_node, edit_scroll[0]->get_value()); - - break; - case AnimationTreePlayer::NODE_BLEND4: - - anim_tree->blend4_node_set_amount(edited_node, Point2(edit_scroll[0]->get_value(), edit_scroll[1]->get_value())); - - break; - - case AnimationTreePlayer::NODE_TRANSITION: { - anim_tree->transition_node_set_xfade_time(edited_node, edit_line[0]->get_text().to_double()); - if (anim_tree->transition_node_get_current(edited_node) != edit_option->get_selected()) - anim_tree->transition_node_set_current(edited_node, edit_option->get_selected()); - } break; - default: {} - } -} - -void AnimationTreeEditor::_edit_dialog_animation_changed() { - - Ref<Animation> anim = property_editor->get_variant().operator RefPtr(); - anim_tree->animation_node_set_animation(edited_node, anim); - update(); -} - -void AnimationTreeEditor::_edit_dialog_edit_animation() { - - if (Engine::get_singleton()->is_editor_hint()) { - get_tree()->get_root()->get_child(0)->call("_resource_selected", property_editor->get_variant().operator RefPtr()); - }; -}; - -void AnimationTreeEditor::_edit_oneshot_start() { - - anim_tree->oneshot_node_start(edited_node); -} - -void AnimationTreeEditor::_play_toggled() { - - anim_tree->set_active(play_button->is_pressed()); -} - -void AnimationTreeEditor::_master_anim_menu_item(int p_item) { - - if (p_item == 0) - _edit_filters(); - else { - - String str = master_anim_popup->get_item_text(p_item); - anim_tree->animation_node_set_master_animation(edited_node, str); + Ref<ButtonGroup> group; + group.instance(); + + Button *b = memnew(Button); + b->set_text("root"); + b->set_toggle_mode(true); + b->set_button_group(group); + b->set_pressed(true); + b->set_focus_mode(FOCUS_NONE); + b->connect("pressed", this, "_path_button_pressed", varray(-1)); + path_hb->add_child(b); + for (int i = 0; i < button_path.size(); i++) { + b = memnew(Button); + b->set_text(button_path[i]); + b->set_toggle_mode(true); + b->set_button_group(group); + path_hb->add_child(b); + b->set_pressed(true); + b->set_focus_mode(FOCUS_NONE); + b->connect("pressed", this, "_path_button_pressed", varray(i)); } - update(); } -void AnimationTreeEditor::_popup_edit_dialog() { - - updating_edit = true; - - for (int i = 0; i < 2; i++) - edit_scroll[i]->hide(); - - for (int i = 0; i < 4; i++) { - - edit_line[i]->hide(); - edit_label[i]->hide(); - } - - edit_option->hide(); - edit_button->hide(); - filter_button->hide(); - edit_check->hide(); - - Point2 pos = anim_tree->node_get_position(edited_node) - Point2(h_scroll->get_value(), v_scroll->get_value()); - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Size2 size = get_node_size(edited_node); - Point2 popup_pos(pos.x + style->get_margin(MARGIN_LEFT), pos.y + size.y - style->get_margin(MARGIN_BOTTOM)); - popup_pos += get_global_position(); - - if (renaming_edit) { - - edit_label[0]->set_text(TTR("New name:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(edited_node); - edit_line[0]->show(); - edit_dialog->set_size(Size2(150, 50)); - - } else { - - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); - - switch (type) { - - case AnimationTreePlayer::NODE_ANIMATION: - - if (anim_tree->get_master_player() != NodePath() && anim_tree->has_node(anim_tree->get_master_player()) && Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player()))) { - - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player())); - master_anim_popup->clear(); - master_anim_popup->add_item(TTR("Edit Filters")); - master_anim_popup->add_separator(); - List<StringName> sn; - ap->get_animation_list(&sn); - sn.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *E = sn.front(); E; E = E->next()) { - master_anim_popup->add_item(E->get()); - } - - master_anim_popup->set_position(popup_pos); - master_anim_popup->popup(); - } else { - property_editor->edit(this, "", Variant::OBJECT, anim_tree->animation_node_get_animation(edited_node), PROPERTY_HINT_RESOURCE_TYPE, "Animation"); - property_editor->set_position(popup_pos); - property_editor->popup(); - updating_edit = false; - } - return; - case AnimationTreePlayer::NODE_TIMESCALE: - edit_label[0]->set_text(TTR("Scale:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(rtos(anim_tree->timescale_node_get_scale(edited_node))); - edit_line[0]->show(); - edit_dialog->set_size(Size2(150, 50)); - break; - case AnimationTreePlayer::NODE_ONESHOT: - edit_label[0]->set_text(TTR("Fade In (s):")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(rtos(anim_tree->oneshot_node_get_fadein_time(edited_node))); - edit_line[0]->show(); - edit_label[1]->set_text(TTR("Fade Out (s):")); - edit_label[1]->set_position(Point2(5, 55)); - edit_label[1]->show(); - edit_line[1]->set_begin(Point2(15, 75)); - edit_line[1]->set_text(rtos(anim_tree->oneshot_node_get_fadeout_time(edited_node))); - edit_line[1]->show(); - - edit_option->clear(); - edit_option->add_item(TTR("Blend"), 0); - edit_option->add_item(TTR("Mix"), 1); - edit_option->set_begin(Point2(15, 105)); - - edit_option->select(anim_tree->oneshot_node_get_mix_mode(edited_node)); - edit_option->show(); - - edit_check->set_text(TTR("Auto Restart:")); - edit_check->set_begin(Point2(15, 125)); - edit_check->set_pressed(anim_tree->oneshot_node_has_autorestart(edited_node)); - edit_check->show(); - - edit_label[2]->set_text(TTR("Restart (s):")); - edit_label[2]->set_position(Point2(5, 145)); - edit_label[2]->show(); - edit_line[2]->set_begin(Point2(15, 165)); - edit_line[2]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_delay(edited_node))); - edit_line[2]->show(); - edit_label[3]->set_text(TTR("Random Restart (s):")); - edit_label[3]->set_position(Point2(5, 195)); - edit_label[3]->show(); - edit_line[3]->set_begin(Point2(15, 215)); - edit_line[3]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_random_delay(edited_node))); - edit_line[3]->show(); - - filter_button->set_begin(Point2(10, 245)); - filter_button->show(); - - edit_button->set_begin(Point2(10, 268)); - edit_button->set_text(TTR("Start!")); - - edit_button->show(); +void AnimationTreeEditor::edit_path(const Vector<String> &p_path) { - edit_dialog->set_size(Size2(180, 293)); + button_path.clear(); - break; + Ref<AnimationNode> node = tree->get_tree_root(); - case AnimationTreePlayer::NODE_MIX: + if (node.is_valid()) { + current_root = node->get_instance_id(); - edit_label[0]->set_text(TTR("Amount:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(0); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->mix_node_get_amount(edited_node)); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - edit_dialog->set_size(Size2(150, 50)); + for (int i = 0; i < p_path.size(); i++) { - break; - case AnimationTreePlayer::NODE_BLEND2: - edit_label[0]->set_text(TTR("Blend:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(0); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->blend2_node_get_amount(edited_node)); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - filter_button->set_begin(Point2(10, 47)); - filter_button->show(); - edit_dialog->set_size(Size2(150, 74)); - - break; - - case AnimationTreePlayer::NODE_BLEND3: - edit_label[0]->set_text(TTR("Blend:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(-1); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->blend3_node_get_amount(edited_node)); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - edit_dialog->set_size(Size2(150, 50)); - - break; - case AnimationTreePlayer::NODE_BLEND4: - - edit_label[0]->set_text(TTR("Blend 0:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(0); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->blend4_node_get_amount(edited_node).x); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - edit_label[1]->set_text(TTR("Blend 1:")); - edit_label[1]->set_position(Point2(5, 55)); - edit_label[1]->show(); - edit_scroll[1]->set_min(0); - edit_scroll[1]->set_max(1); - edit_scroll[1]->set_step(0.01); - edit_scroll[1]->set_value(anim_tree->blend4_node_get_amount(edited_node).y); - edit_scroll[1]->set_begin(Point2(15, 75)); - edit_scroll[1]->show(); - edit_dialog->set_size(Size2(150, 100)); - - break; - - case AnimationTreePlayer::NODE_TRANSITION: { - - edit_label[0]->set_text(TTR("X-Fade Time (s):")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(rtos(anim_tree->transition_node_get_xfade_time(edited_node))); - edit_line[0]->show(); - - edit_label[1]->set_text(TTR("Current:")); - edit_label[1]->set_position(Point2(5, 55)); - edit_label[1]->show(); - edit_option->set_begin(Point2(15, 75)); - - edit_option->clear(); - - for (int i = 0; i < anim_tree->transition_node_get_input_count(edited_node); i++) { - edit_option->add_item(itos(i), i); - } - - edit_option->select(anim_tree->transition_node_get_current(edited_node)); - edit_option->show(); - edit_dialog->set_size(Size2(150, 100)); - - } break; - default: {} + Ref<AnimationNode> child = node->get_child_by_name(p_path[i]); + ERR_BREAK(child.is_null()); + node = child; + button_path.push_back(p_path[i]); } - } - - edit_dialog->set_position(popup_pos); - edit_dialog->popup(); - - updating_edit = false; -} - -void AnimationTreeEditor::_draw_node(const StringName &p_node) { - - RID ci = get_canvas_item(); - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - Color font_color = get_color("font_color", "PopupMenu"); - Color font_color_title = get_color("font_color_hover", "PopupMenu"); - font_color_title.a *= 0.8; - Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); - - Size2 size = get_node_size(p_node); - Point2 pos = anim_tree->node_get_position(p_node); - if (click_type == CLICK_NODE && click_node == p_node) { - - pos += click_motion - click_pos; - if (pos.x < 5) - pos.x = 5; - if (pos.y < 5) - pos.y = 5; - } - - pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); - style->draw(ci, Rect2(pos, size)); - - float w = size.width - style->get_minimum_size().width; - float h = font->get_height() + get_constant("vseparation", "PopupMenu"); - - Point2 ofs = style->get_offset() + pos; - Point2 ascofs(0, font->get_ascent()); - - Color bx = font_color_title; - bx.a *= 0.1; - draw_rect(Rect2(ofs, Size2(size.width - style->get_minimum_size().width, font->get_height())), bx); - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, String(_node_type_names[type]), font_color_title); - - ofs.y += h; - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, p_node, font_color); - ofs.y += h; - - int count = 2; // title and name - int inputs = anim_tree->node_get_input_count(p_node); - count += inputs ? inputs : 1; - - float icon_h_ofs = Math::floor((font->get_height() - slot_icon->get_height()) / 2.0) + 1; - - if (type != AnimationTreePlayer::NODE_OUTPUT) - slot_icon->draw(ci, ofs + Point2(w, icon_h_ofs)); //output - - if (inputs) { - for (int i = 0; i < inputs; i++) { - - slot_icon->draw(ci, ofs + Point2(-slot_icon->get_width(), icon_h_ofs)); - String text; - switch (type) { - - case AnimationTreePlayer::NODE_TIMESCALE: - case AnimationTreePlayer::NODE_TIMESEEK: text = "in"; break; - case AnimationTreePlayer::NODE_OUTPUT: text = "out"; break; - case AnimationTreePlayer::NODE_ANIMATION: break; - case AnimationTreePlayer::NODE_ONESHOT: text = (i == 0 ? "in" : "add"); break; - case AnimationTreePlayer::NODE_BLEND2: - case AnimationTreePlayer::NODE_MIX: text = (i == 0 ? "a" : "b"); break; - case AnimationTreePlayer::NODE_BLEND3: - switch (i) { - case 0: text = "b-"; break; - case 1: text = "a"; break; - case 2: text = "b+"; break; - } - break; - - case AnimationTreePlayer::NODE_BLEND4: - switch (i) { - case 0: text = "a0"; break; - case 1: text = "b0"; break; - case 2: text = "a1"; break; - case 3: text = "b1"; break; - } - break; - - case AnimationTreePlayer::NODE_TRANSITION: - text = itos(i); - if (anim_tree->transition_node_has_input_auto_advance(p_node, i)) - text += "->"; - - break; - default: {} + for (int i = 0; i < editors.size(); i++) { + if (editors[i]->can_edit(node)) { + editors[i]->edit(node); + editors[i]->show(); + } else { + editors[i]->edit(Ref<AnimationNode>()); + editors[i]->hide(); } - font->draw(ci, ofs + ascofs + Point2(3, 0), text, font_color); - - ofs.y += h; } } else { - ofs.y += h; + current_root = 0; } - Ref<StyleBox> pg_bg = get_stylebox("bg", "ProgressBar"); - Ref<StyleBox> pg_fill = get_stylebox("fill", "ProgressBar"); - Rect2 pg_rect(ofs, Size2(w, h)); - - bool editable = true; - switch (type) { - case AnimationTreePlayer::NODE_ANIMATION: { - - Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); - String text; - if (anim_tree->animation_node_get_master_animation(p_node) != "") - text = anim_tree->animation_node_get_master_animation(p_node); - else if (anim.is_null()) - text = "load..."; - else - text = anim->get_name(); - - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, text, font_color_title); + edited_path = button_path; - } break; - case AnimationTreePlayer::NODE_ONESHOT: - case AnimationTreePlayer::NODE_MIX: - case AnimationTreePlayer::NODE_BLEND2: - case AnimationTreePlayer::NODE_BLEND3: - case AnimationTreePlayer::NODE_BLEND4: - case AnimationTreePlayer::NODE_TIMESCALE: - case AnimationTreePlayer::NODE_TRANSITION: { - - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit...", font_color_title); - } break; - default: editable = false; - } - - if (editable) { - - Ref<Texture> arrow = get_icon("GuiDropdown", "EditorIcons"); - Point2 arrow_ofs(w - arrow->get_width(), Math::floor((h - arrow->get_height()) / 2)); - arrow->draw(ci, ofs + arrow_ofs); - } + _update_path(); } -AnimationTreeEditor::ClickType AnimationTreeEditor::_locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const { - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - - float h = (font->get_height() + get_constant("vseparation", "PopupMenu")); - - for (const List<StringName>::Element *E = order.back(); E; E = E->prev()) { - - StringName node = E->get(); - - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(node); - - Point2 pos = anim_tree->node_get_position(node); - Size2 size = get_node_size(node); - - pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); - - if (!Rect2(pos, size).has_point(p_click)) - continue; - - if (p_node_id) - *p_node_id = node; - - pos = p_click - pos; - - float y = pos.y - style->get_offset().height; - - if (y < 2 * h) - return CLICK_NODE; - y -= 2 * h; - - int inputs = anim_tree->node_get_input_count(node); - int count = MAX(inputs, 1); - - if (inputs == 0 || (pos.x > size.width / 2 && type != AnimationTreePlayer::NODE_OUTPUT)) { - - if (y < count * h) { - - if (p_slot_index) - *p_slot_index = 0; - return CLICK_OUTPUT_SLOT; - } - } - - for (int i = 0; i < count; i++) { - - if (y < h) { - if (p_slot_index) - *p_slot_index = i; - return CLICK_INPUT_SLOT; - } - y -= h; - } - - bool has_parameters = type != AnimationTreePlayer::NODE_OUTPUT && type != AnimationTreePlayer::NODE_TIMESEEK; - return has_parameters ? CLICK_PARAMETER : CLICK_NODE; - } - - return CLICK_NONE; -} - -Point2 AnimationTreeEditor::_get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot) { - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); - - Size2 size = get_node_size(p_node_id); - Point2 pos = anim_tree->node_get_position(p_node_id); - - if (click_type == CLICK_NODE && click_node == p_node_id) { - - pos += click_motion - click_pos; - if (pos.x < 5) - pos.x = 5; - if (pos.y < 5) - pos.y = 5; - } - - pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); - - float w = size.width - style->get_minimum_size().width; - float h = font->get_height() + get_constant("vseparation", "PopupMenu"); - - pos += style->get_offset(); - - pos.y += h * 2; - - pos.y += h * p_slot; - - pos += Point2(-slot_icon->get_width() / 2.0, h / 2.0).floor(); - - if (!p_input) { - pos.x += w + slot_icon->get_width(); - } - - return pos; +Vector<String> AnimationTreeEditor::get_edited_path() const { + return button_path; } -void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) { - - Ref<InputEventMouseButton> mb = p_event; - - if (mb.is_valid()) { - - if (mb->is_pressed()) { +void AnimationTreeEditor::enter_editor(const String &p_path) { - if (mb->get_button_index() == 1) { - click_pos = Point2(mb->get_position().x, mb->get_position().y); - click_motion = click_pos; - click_type = _locate_click(click_pos, &click_node, &click_slot); - if (click_type != CLICK_NONE) { - - order.erase(click_node); - order.push_back(click_node); - update(); - } - - switch (click_type) { - case CLICK_INPUT_SLOT: { - click_pos = _get_slot_pos(click_node, true, click_slot); - } break; - case CLICK_OUTPUT_SLOT: { - click_pos = _get_slot_pos(click_node, false, click_slot); - } break; - case CLICK_PARAMETER: { - - edited_node = click_node; - renaming_edit = false; - _popup_edit_dialog(); - //open editor - //_node_edit_property(click_node); - } break; - default: {} - } - } - if (mb->get_button_index() == 2) { - - if (click_type != CLICK_NONE) { - click_type = CLICK_NONE; - update(); - } else { - // try to disconnect/remove - - Point2 rclick_pos = Point2(mb->get_position().x, mb->get_position().y); - rclick_type = _locate_click(rclick_pos, &rclick_node, &rclick_slot); - if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) { - - node_popup->clear(); - node_popup->set_size(Size2(1, 1)); - node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT); - if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) { - node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); - if (rclick_type == CLICK_INPUT_SLOT) { - if (anim_tree->transition_node_has_input_auto_advance(rclick_node, rclick_slot)) - node_popup->add_item(TTR("Clear Auto-Advance"), NODE_CLEAR_AUTOADVANCE); - else - node_popup->add_item(TTR("Set Auto-Advance"), NODE_SET_AUTOADVANCE); - node_popup->add_item(TTR("Delete Input"), NODE_DELETE_INPUT); - } - } - - node_popup->set_position(rclick_pos + get_global_position()); - node_popup->popup(); - } - - if (rclick_type == CLICK_NODE) { - node_popup->clear(); - node_popup->set_size(Size2(1, 1)); - node_popup->add_item(TTR("Rename"), NODE_RENAME); - node_popup->add_item(TTR("Remove"), NODE_ERASE); - if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) - node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); - node_popup->set_position(rclick_pos + get_global_position()); - node_popup->popup(); - } - } - } - } else { - - if (mb->get_button_index() == 1 && click_type != CLICK_NONE) { - - switch (click_type) { - case CLICK_INPUT_SLOT: - case CLICK_OUTPUT_SLOT: { - - Point2 dst_click_pos = Point2(mb->get_position().x, mb->get_position().y); - StringName id; - int slot; - ClickType dst_click_type = _locate_click(dst_click_pos, &id, &slot); - - if (dst_click_type == CLICK_INPUT_SLOT && click_type == CLICK_OUTPUT_SLOT) { - - anim_tree->connect_nodes(click_node, id, slot); - } - if (click_type == CLICK_INPUT_SLOT && dst_click_type == CLICK_OUTPUT_SLOT) { - - anim_tree->connect_nodes(id, click_node, click_slot); - } - - } break; - case CLICK_NODE: { - Point2 new_pos = anim_tree->node_get_position(click_node) + (click_motion - click_pos); - if (new_pos.x < 5) - new_pos.x = 5; - if (new_pos.y < 5) - new_pos.y = 5; - anim_tree->node_set_position(click_node, new_pos); - - } break; - default: {} - } - - click_type = CLICK_NONE; - update(); - } - } - } - - Ref<InputEventMouseMotion> mm = p_event; - - if (mm.is_valid()) { - - if (mm->get_button_mask() & 1 && click_type != CLICK_NONE) { - - click_motion = Point2(mm->get_position().x, mm->get_position().y); - update(); - } - if ((mm->get_button_mask() & 4 || Input::get_singleton()->is_key_pressed(KEY_SPACE))) { - - h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x); - v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y); - update(); - } - } + Vector<String> path = edited_path; + path.push_back(p_path); + edit_path(path); } -void AnimationTreeEditor::_draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color) { - - static const int steps = 20; - - Rect2 r; - r.position = p_from; - r.expand_to(p_to); - Vector2 sign = Vector2((p_from.x < p_to.x) ? 1 : -1, (p_from.y < p_to.y) ? 1 : -1); - bool flip = sign.x * sign.y < 0; - - Vector2 prev; - for (int i = 0; i <= steps; i++) { - - float d = i / float(steps); - float c = -Math::cos(d * Math_PI) * 0.5 + 0.5; - if (flip) - c = 1.0 - c; - Vector2 p = r.position + Vector2(d * r.size.width, c * r.size.height); - - if (i > 0) { - - draw_line(prev, p, p_color, 2); - } - - prev = p; - } +void AnimationTreeEditor::_about_to_show_root() { } void AnimationTreeEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_PROCESS) { + ObjectID root = 0; + if (tree && tree->get_tree_root().is_valid()) { + root = tree->get_tree_root()->get_instance_id(); + } - switch (p_what) { - - case NOTIFICATION_ENTER_TREE: { - - play_button->set_icon(get_icon("Play", "EditorIcons")); - add_menu->set_icon(get_icon("Add", "EditorIcons")); - } break; - case NOTIFICATION_DRAW: { - - _update_scrollbars(); - //VisualServer::get_singleton()->canvas_item_add_rect(get_canvas_item(),Rect2(Point2(),get_size()),Color(0,0,0,1)); - get_stylebox("bg", "Tree")->draw(get_canvas_item(), Rect2(Point2(), get_size())); - - for (List<StringName>::Element *E = order.front(); E; E = E->next()) { - - _draw_node(E->get()); - } - - if (click_type == CLICK_INPUT_SLOT || click_type == CLICK_OUTPUT_SLOT) { - - _draw_cos_line(click_pos, click_motion, Color(0.5, 1, 0.5, 0.8)); - } - - List<AnimationTreePlayer::Connection> connections; - anim_tree->get_connection_list(&connections); - - for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { - - const AnimationTreePlayer::Connection &c = E->get(); - Point2 source = _get_slot_pos(c.src_node, false, 0); - Point2 dest = _get_slot_pos(c.dst_node, true, c.dst_input); - Color col = Color(1, 1, 0.5, 0.8); - /* - if (click_type==CLICK_NODE && click_node==c.src_node) { - - source+=click_motion-click_pos; - } - - if (click_type==CLICK_NODE && click_node==c.dst_node) { - - dest+=click_motion-click_pos; - }*/ - - _draw_cos_line(source, dest, col); - } - - switch (anim_tree->get_last_error()) { - - case AnimationTreePlayer::CONNECT_OK: { - - Ref<Font> f = get_font("font", "Label"); - f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is valid."), Color(0, 1, 0.6, 0.8)); - } break; - default: { - - Ref<Font> f = get_font("font", "Label"); - f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is invalid."), Color(1, 0.6, 0.0, 0.8)); - } break; - } - - } break; + if (root != current_root) { + edit_path(Vector<String>()); + } } } -void AnimationTreeEditor::_update_scrollbars() { - - Size2 size = get_size(); - Size2 hmin = h_scroll->get_combined_minimum_size(); - Size2 vmin = v_scroll->get_combined_minimum_size(); - - v_scroll->set_begin(Point2(size.width - vmin.width, 0)); - v_scroll->set_end(Point2(size.width, size.height)); - - h_scroll->set_begin(Point2(0, size.height - hmin.height)); - h_scroll->set_end(Point2(size.width - vmin.width, size.height)); - - Size2 min = _get_maximum_size(); - - if (min.height < size.height - hmin.height) { - - v_scroll->hide(); - offset.y = 0; - } else { - - v_scroll->show(); - v_scroll->set_max(min.height); - v_scroll->set_page(size.height - hmin.height); - offset.y = v_scroll->get_value(); - } - - if (min.width < size.width - vmin.width) { - - h_scroll->hide(); - offset.x = 0; - } else { - - h_scroll->show(); - h_scroll->set_max(min.width); - h_scroll->set_page(size.width - vmin.width); - offset.x = h_scroll->get_value(); - } +void AnimationTreeEditor::_bind_methods() { + ClassDB::bind_method("_path_button_pressed", &AnimationTreeEditor::_path_button_pressed); } -void AnimationTreeEditor::_scroll_moved(float) { +AnimationTreeEditor *AnimationTreeEditor::singleton = NULL; - offset.x = h_scroll->get_value(); - offset.y = v_scroll->get_value(); - update(); +void AnimationTreeEditor::add_plugin(AnimationTreeNodeEditorPlugin *p_editor) { + ERR_FAIL_COND(p_editor->get_parent()); + editor_base->add_child(p_editor); + editors.push_back(p_editor); + p_editor->set_h_size_flags(SIZE_EXPAND_FILL); + p_editor->set_v_size_flags(SIZE_EXPAND_FILL); + p_editor->hide(); } -void AnimationTreeEditor::_node_menu_item(int p_item) { - - switch (p_item) { - - case NODE_DISCONNECT: { - - if (rclick_type == CLICK_INPUT_SLOT) { - - anim_tree->disconnect_nodes(rclick_node, rclick_slot); - update(); - } - - if (rclick_type == CLICK_OUTPUT_SLOT) { - - List<AnimationTreePlayer::Connection> connections; - anim_tree->get_connection_list(&connections); - - for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { - - const AnimationTreePlayer::Connection &c = E->get(); - if (c.dst_node == rclick_node) { - - anim_tree->disconnect_nodes(c.dst_node, c.dst_input); - } - } - update(); - } - - } break; - case NODE_RENAME: { - - renaming_edit = true; - edited_node = rclick_node; - _popup_edit_dialog(); - - } break; - case NODE_ADD_INPUT: { - - anim_tree->transition_node_set_input_count(rclick_node, anim_tree->transition_node_get_input_count(rclick_node) + 1); - update(); - } break; - case NODE_DELETE_INPUT: { - - anim_tree->transition_node_delete_input(rclick_node, rclick_slot); - update(); - } break; - case NODE_SET_AUTOADVANCE: { - - anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, true); - update(); - - } break; - case NODE_CLEAR_AUTOADVANCE: { - - anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, false); - update(); - - } break; - - case NODE_ERASE: { - - if (rclick_node == "out") - break; - order.erase(rclick_node); - anim_tree->remove_node(rclick_node); - update(); - } break; - } +void AnimationTreeEditor::remove_plugin(AnimationTreeNodeEditorPlugin *p_editor) { + ERR_FAIL_COND(p_editor->get_parent() != editor_base); + editor_base->remove_child(p_editor); + editors.erase(p_editor); } -StringName AnimationTreeEditor::_add_node(int p_item) { - - static const char *bname[] = { - "out", - "anim", - "oneshot", - "mix", - "blend2", - "blend3", - "blend4", - "scale", - "seek", - "transition" - }; - - String name; - int idx = 1; - - while (true) { - - name = bname[p_item]; - if (idx > 1) - name += " " + itos(idx); - if (anim_tree->node_exists(name)) - idx++; - else - break; +String AnimationTreeEditor::get_base_path() { + String path = SceneStringNames::get_singleton()->parameters_base_path; + for (int i = 0; i < edited_path.size(); i++) { + path += edited_path[i] + "/"; } - - anim_tree->add_node((AnimationTreePlayer::NodeType)p_item, name); - anim_tree->node_set_position(name, Point2(last_x, last_y)); - order.push_back(name); - last_x += 10; - last_y += 10; - last_x = last_x % (int)get_size().width; - last_y = last_y % (int)get_size().height; - update(); - - return name; -}; - -void AnimationTreeEditor::_file_dialog_selected(String p_path) { - - switch (file_op) { - - case MENU_IMPORT_ANIMATIONS: { - Vector<String> files = file_dialog->get_selected_files(); - - for (int i = 0; i < files.size(); i++) { - - StringName node = _add_node(AnimationTreePlayer::NODE_ANIMATION); - - RES anim = ResourceLoader::load(files[i]); - anim_tree->animation_node_set_animation(node, anim); - //anim_tree->node_set_name(node, files[i].get_file()); - }; - } break; - - default: - break; - }; -}; - -void AnimationTreeEditor::_add_menu_item(int p_item) { - - if (p_item == MENU_GRAPH_CLEAR) { - - //clear - } else if (p_item == MENU_IMPORT_ANIMATIONS) { - - file_op = MENU_IMPORT_ANIMATIONS; - file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); - file_dialog->popup_centered_ratio(); - - } else { - - _add_node(p_item); - } -} - -Size2 AnimationTreeEditor::get_minimum_size() const { - - return Size2(10, 200); + return path; } -void AnimationTreeEditor::_find_paths_for_filter(const StringName &p_node, Set<String> &paths) { - - ERR_FAIL_COND(!anim_tree->node_exists(p_node)); - - for (int i = 0; i < anim_tree->node_get_input_count(p_node); i++) { - - StringName port = anim_tree->node_get_input_source(p_node, i); - if (port == StringName()) - continue; - _find_paths_for_filter(port, paths); - } - - if (anim_tree->node_get_type(p_node) == AnimationTreePlayer::NODE_ANIMATION) { - - Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); - if (anim.is_valid()) { - - for (int i = 0; i < anim->get_track_count(); i++) { - paths.insert(anim->track_get_path(i)); - } +bool AnimationTreeEditor::can_edit(const Ref<AnimationNode> &p_node) const { + for (int i = 0; i < editors.size(); i++) { + if (editors[i]->can_edit(p_node)) { + return true; } } + return false; } -void AnimationTreeEditor::_filter_edited() { - - TreeItem *ed = filter->get_edited(); - if (!ed) - return; +Vector<String> AnimationTreeEditor::get_animation_list() { - if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { - anim_tree->oneshot_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { - anim_tree->blend2_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { - anim_tree->animation_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + if (!singleton->is_visible()) { + return Vector<String>(); } -} - -void AnimationTreeEditor::_edit_filters() { - - filter_dialog->popup_centered_ratio(); - filter->clear(); - - Set<String> npb; - _find_paths_for_filter(edited_node, npb); - - TreeItem *root = filter->create_item(); - filter->set_hide_root(true); - Map<String, TreeItem *> pm; - - Node *base = anim_tree->get_node(anim_tree->get_base_path()); - - for (Set<String>::Element *E = npb.front(); E; E = E->next()) { - - TreeItem *parent = root; - String descr = E->get(); - if (base) { - NodePath np = E->get(); - if (np.get_subname_count() == 1) { - Node *n = base->get_node(np); - Skeleton *s = Object::cast_to<Skeleton>(n); - if (s) { + AnimationTree *tree = singleton->tree; + if (!tree || !tree->has_node(tree->get_animation_player())) + return Vector<String>(); - String skelbase = E->get().substr(0, E->get().find(":")); + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(tree->get_node(tree->get_animation_player())); - int bidx = s->find_bone(np.get_subname(0)); + if (!ap) + return Vector<String>(); - if (bidx != -1) { - int bparent = s->get_bone_parent(bidx); - // - if (bparent != -1) { - - String bpn = skelbase + ":" + s->get_bone_name(bparent); - if (pm.has(bpn)) { - parent = pm[bpn]; - descr = np.get_subname(0); - } - } else { - - if (pm.has(skelbase)) { - parent = pm[skelbase]; - } - } - } - } - } - } - - TreeItem *it = filter->create_item(parent); - it->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); - it->set_text(0, descr); - it->set_metadata(0, NodePath(E->get())); - it->set_editable(0, true); - if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { - it->set_checked(0, anim_tree->oneshot_node_is_path_filtered(edited_node, E->get())); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { - it->set_checked(0, anim_tree->blend2_node_is_path_filtered(edited_node, E->get())); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { - it->set_checked(0, anim_tree->animation_node_is_path_filtered(edited_node, E->get())); - } - pm[E->get()] = it; + List<StringName> anims; + ap->get_animation_list(&anims); + Vector<String> ret; + for (List<StringName>::Element *E = anims.front(); E; E = E->next()) { + ret.push_back(E->get()); } -} - -void AnimationTreeEditor::_bind_methods() { - ClassDB::bind_method("_add_menu_item", &AnimationTreeEditor::_add_menu_item); - ClassDB::bind_method("_node_menu_item", &AnimationTreeEditor::_node_menu_item); - ClassDB::bind_method("_gui_input", &AnimationTreeEditor::_gui_input); - //ClassDB::bind_method( "_node_param_changed", &AnimationTreeEditor::_node_param_changed ); - ClassDB::bind_method("_scroll_moved", &AnimationTreeEditor::_scroll_moved); - ClassDB::bind_method("_edit_dialog_changeds", &AnimationTreeEditor::_edit_dialog_changeds); - ClassDB::bind_method("_edit_dialog_changede", &AnimationTreeEditor::_edit_dialog_changede); - ClassDB::bind_method("_edit_dialog_changedf", &AnimationTreeEditor::_edit_dialog_changedf); - ClassDB::bind_method("_edit_dialog_changed", &AnimationTreeEditor::_edit_dialog_changed); - ClassDB::bind_method("_edit_dialog_animation_changed", &AnimationTreeEditor::_edit_dialog_animation_changed); - ClassDB::bind_method("_edit_dialog_edit_animation", &AnimationTreeEditor::_edit_dialog_edit_animation); - ClassDB::bind_method("_play_toggled", &AnimationTreeEditor::_play_toggled); - ClassDB::bind_method("_edit_oneshot_start", &AnimationTreeEditor::_edit_oneshot_start); - ClassDB::bind_method("_file_dialog_selected", &AnimationTreeEditor::_file_dialog_selected); - ClassDB::bind_method("_master_anim_menu_item", &AnimationTreeEditor::_master_anim_menu_item); - ClassDB::bind_method("_edit_filters", &AnimationTreeEditor::_edit_filters); - ClassDB::bind_method("_filter_edited", &AnimationTreeEditor::_filter_edited); + return ret; } AnimationTreeEditor::AnimationTreeEditor() { - set_focus_mode(FOCUS_ALL); - - PopupMenu *p; - List<PropertyInfo> defaults; - - add_menu = memnew(MenuButton); - //add_menu->set_ - add_menu->set_position(Point2(0, 0)); - add_menu->set_size(Point2(25, 15)); - add_child(add_menu); - - p = add_menu->get_popup(); - p->add_item(TTR("Animation Node"), AnimationTreePlayer::NODE_ANIMATION); - p->add_item(TTR("OneShot Node"), AnimationTreePlayer::NODE_ONESHOT); - p->add_item(TTR("Mix Node"), AnimationTreePlayer::NODE_MIX); - p->add_item(TTR("Blend2 Node"), AnimationTreePlayer::NODE_BLEND2); - p->add_item(TTR("Blend3 Node"), AnimationTreePlayer::NODE_BLEND3); - p->add_item(TTR("Blend4 Node"), AnimationTreePlayer::NODE_BLEND4); - p->add_item(TTR("TimeScale Node"), AnimationTreePlayer::NODE_TIMESCALE); - p->add_item(TTR("TimeSeek Node"), AnimationTreePlayer::NODE_TIMESEEK); - p->add_item(TTR("Transition Node"), AnimationTreePlayer::NODE_TRANSITION); - p->add_separator(); - p->add_item(TTR("Import Animations..."), MENU_IMPORT_ANIMATIONS); // wtf - p->add_separator(); - p->add_item(TTR("Clear"), MENU_GRAPH_CLEAR); - - p->connect("id_pressed", this, "_add_menu_item"); - - play_button = memnew(Button); - play_button->set_position(Point2(25, 0)); - play_button->set_size(Point2(25, 15)); - add_child(play_button); - play_button->set_toggle_mode(true); - play_button->connect("pressed", this, "_play_toggled"); - - last_x = 50; - last_y = 50; - - property_editor = memnew(CustomPropertyEditor); - add_child(property_editor); - property_editor->connect("variant_changed", this, "_edit_dialog_animation_changed"); - property_editor->connect("resource_edit_request", this, "_edit_dialog_edit_animation"); - - h_scroll = memnew(HScrollBar); - v_scroll = memnew(VScrollBar); - - add_child(h_scroll); - add_child(v_scroll); - - h_scroll->connect("value_changed", this, "_scroll_moved"); - v_scroll->connect("value_changed", this, "_scroll_moved"); - - node_popup = memnew(PopupMenu); - add_child(node_popup); - node_popup->set_as_toplevel(true); - - master_anim_popup = memnew(PopupMenu); - add_child(master_anim_popup); - master_anim_popup->connect("id_pressed", this, "_master_anim_menu_item"); - - node_popup->connect("id_pressed", this, "_node_menu_item"); - - updating_edit = false; - - edit_dialog = memnew(PopupPanel); - //edit_dialog->get_ok()->hide(); - //edit_dialog->get_cancel()->hide(); - add_child(edit_dialog); - - edit_option = memnew(OptionButton); - edit_option->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_option->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_option); - edit_option->connect("item_selected", this, "_edit_dialog_changedf"); - edit_option->hide(); - - for (int i = 0; i < 2; i++) { - edit_scroll[i] = memnew(HSlider); - edit_scroll[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_scroll[i]->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_scroll[i]); - edit_scroll[i]->hide(); - edit_scroll[i]->connect("value_changed", this, "_edit_dialog_changedf"); - } - for (int i = 0; i < 4; i++) { - edit_line[i] = memnew(LineEdit); - edit_line[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_line[i]->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_line[i]); - edit_line[i]->hide(); - edit_line[i]->connect("text_changed", this, "_edit_dialog_changeds"); - edit_line[i]->connect("text_entered", this, "_edit_dialog_changede"); - edit_label[i] = memnew(Label); - edit_dialog->add_child(edit_label[i]); - edit_label[i]->hide(); - } - - edit_button = memnew(Button); - edit_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_button->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_button); - edit_button->hide(); - edit_button->connect("pressed", this, "_edit_oneshot_start"); - - edit_check = memnew(CheckButton); - edit_check->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_check->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_check); - edit_check->hide(); - edit_check->connect("pressed", this, "_edit_dialog_changed"); - - file_dialog = memnew(EditorFileDialog); - file_dialog->set_enable_multiple_selection(true); - file_dialog->set_current_dir(ProjectSettings::get_singleton()->get_resource_path()); - add_child(file_dialog); - file_dialog->connect("file_selected", this, "_file_dialog_selected"); - - filter_dialog = memnew(AcceptDialog); - filter_dialog->set_title(TTR("Edit Node Filters")); - add_child(filter_dialog); - - filter = memnew(Tree); - filter_dialog->add_child(filter); - //filter_dialog->set_child_rect(filter); - filter->connect("item_edited", this, "_filter_edited"); + AnimationNodeAnimation::get_editable_animation_list = get_animation_list; + path_edit = memnew(ScrollContainer); + add_child(path_edit); + path_edit->set_enable_h_scroll(true); + path_edit->set_enable_v_scroll(false); + path_hb = memnew(HBoxContainer); + path_edit->add_child(path_hb); - filter_button = memnew(Button); - filter_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); - filter_button->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(filter_button); - filter_button->hide(); - filter_button->set_text(TTR("Filters...")); - filter_button->connect("pressed", this, "_edit_filters"); + current_root = 0; + singleton = this; + editor_base = memnew(PanelContainer); + editor_base->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(editor_base); - set_clip_contents(true); + add_plugin(memnew(AnimationNodeBlendTreeEditor)); + add_plugin(memnew(AnimationNodeBlendSpace1DEditor)); + add_plugin(memnew(AnimationNodeBlendSpace2DEditor)); + add_plugin(memnew(AnimationNodeStateMachineEditor)); } void AnimationTreeEditorPlugin::edit(Object *p_object) { - anim_tree_editor->edit(Object::cast_to<AnimationTreePlayer>(p_object)); + anim_tree_editor->edit(Object::cast_to<AnimationTree>(p_object)); } bool AnimationTreeEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("AnimationTreePlayer"); + return p_object->is_class("AnimationTree"); } void AnimationTreeEditorPlugin::make_visible(bool p_visible) { @@ -1422,13 +251,13 @@ void AnimationTreeEditorPlugin::make_visible(bool p_visible) { //editor->animation_panel_make_visible(true); button->show(); editor->make_bottom_panel_item_visible(anim_tree_editor); - anim_tree_editor->set_physics_process(true); + 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_physics_process(false); + anim_tree_editor->set_process(false); } } diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h index aeb5b1744f..b12054bb62 100644 --- a/editor/plugins/animation_tree_editor_plugin.h +++ b/editor/plugins/animation_tree_editor_plugin.h @@ -1,167 +1,65 @@ -/*************************************************************************/ -/* animation_tree_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - #ifndef ANIMATION_TREE_EDITOR_PLUGIN_H #define ANIMATION_TREE_EDITOR_PLUGIN_H #include "editor/editor_node.h" #include "editor/editor_plugin.h" #include "editor/property_editor.h" -#include "scene/animation/animation_tree_player.h" +#include "scene/animation/animation_tree.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 <reduzio@gmail.com> -*/ - -class AnimationTreeEditor : public Control { - - GDCLASS(AnimationTreeEditor, Control); - - static const char *_node_type_names[]; - - enum ClickType { - CLICK_NONE, - CLICK_NAME, - CLICK_NODE, - CLICK_INPUT_SLOT, - CLICK_OUTPUT_SLOT, - CLICK_PARAMETER - }; - - enum { - - MENU_GRAPH_CLEAR = 100, - MENU_IMPORT_ANIMATIONS = 101, - NODE_DISCONNECT, - NODE_RENAME, - NODE_ERASE, - NODE_ADD_INPUT, - NODE_DELETE_INPUT, - NODE_SET_AUTOADVANCE, - NODE_CLEAR_AUTOADVANCE - }; - - bool renaming_edit; - StringName edited_node; - bool updating_edit; - Popup *edit_dialog; - HSlider *edit_scroll[2]; - LineEdit *edit_line[4]; - OptionButton *edit_option; - Label *edit_label[4]; - Button *edit_button; - Button *filter_button; - CheckButton *edit_check; - EditorFileDialog *file_dialog; - int file_op; - - void _popup_edit_dialog(); - - void _setup_edit_dialog(const StringName &p_node); - PopupMenu *master_anim_popup; - PopupMenu *node_popup; - PopupMenu *add_popup; - HScrollBar *h_scroll; - VScrollBar *v_scroll; - MenuButton *add_menu; - - CustomPropertyEditor *property_editor; - - AnimationTreePlayer *anim_tree; - List<StringName> order; - Set<StringName> active_nodes; - - int last_x, last_y; - - Point2 offset; - ClickType click_type; - Point2 click_pos; - StringName click_node; - int click_slot; - Point2 click_motion; - ClickType rclick_type; - StringName rclick_node; - int rclick_slot; - - Button *play_button; - - Size2 _get_maximum_size(); - Size2 get_node_size(const StringName &p_node) const; - void _draw_node(const StringName &p_node); - - AcceptDialog *filter_dialog; - Tree *filter; - - void _draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color); - void _update_scrollbars(); - void _scroll_moved(float); - void _play_toggled(); - /* - void _node_param_changed(); - void _node_add_callback(); - void _node_add(VisualServer::AnimationTreeNodeType p_type); - void _node_edit_property(const StringName& p_node); -*/ - - void _master_anim_menu_item(int p_item); - void _node_menu_item(int p_item); - void _add_menu_item(int p_item); - - void _filter_edited(); - void _find_paths_for_filter(const StringName &p_node, Set<String> &paths); - void _edit_filters(); - - void _edit_oneshot_start(); - void _edit_dialog_animation_changed(); - void _edit_dialog_edit_animation(); - void _edit_dialog_changeds(String); - void _edit_dialog_changede(String); - void _edit_dialog_changedf(float); - void _edit_dialog_changed(); - void _dialog_changed() const; - ClickType _locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const; - Point2 _get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot); - - StringName _add_node(int p_item); - void _file_dialog_selected(String p_path); + +class AnimationTreeNodeEditorPlugin : public VBoxContainer { + GDCLASS(AnimationTreeNodeEditorPlugin, VBoxContainer) +public: + virtual bool can_edit(const Ref<AnimationNode> &p_node) = 0; + virtual void edit(const Ref<AnimationNode> &p_node) = 0; +}; + +class AnimationTreeEditor : public VBoxContainer { + + GDCLASS(AnimationTreeEditor, VBoxContainer); + + ScrollContainer *path_edit; + HBoxContainer *path_hb; + + AnimationTree *tree; + PanelContainer *editor_base; + + Vector<String> button_path; + Vector<String> edited_path; + Vector<AnimationTreeNodeEditorPlugin *> editors; + + void _update_path(); + void _about_to_show_root(); + ObjectID current_root; + + void _path_button_pressed(int p_path); + + static Vector<String> get_animation_list(); protected: void _notification(int p_what); - void _gui_input(Ref<InputEvent> p_event); static void _bind_methods(); + static AnimationTreeEditor *singleton; + public: - virtual Size2 get_minimum_size() const; - void edit(AnimationTreePlayer *p_anim_tree); + AnimationTree *get_tree() { return tree; } + void add_plugin(AnimationTreeNodeEditorPlugin *p_editor); + void remove_plugin(AnimationTreeNodeEditorPlugin *p_editor); + + String get_base_path(); + + bool can_edit(const Ref<AnimationNode> &p_node) const; + + void edit_path(const Vector<String> &p_path); + Vector<String> get_edited_path() const; + + void enter_editor(const String &p_path = ""); + static AnimationTreeEditor *get_singleton() { return singleton; } + void edit(AnimationTree *p_tree); AnimationTreeEditor(); }; @@ -174,7 +72,7 @@ class AnimationTreeEditorPlugin : public EditorPlugin { Button *button; public: - virtual String get_name() const { return "AnimTree"; } + virtual String get_name() const { return "AnimationTree"; } bool has_main_screen() const { return false; } virtual void edit(Object *p_object); virtual bool handles(Object *p_object) const; diff --git a/editor/plugins/animation_tree_player_editor_plugin.cpp b/editor/plugins/animation_tree_player_editor_plugin.cpp new file mode 100644 index 0000000000..36d10ab99e --- /dev/null +++ b/editor/plugins/animation_tree_player_editor_plugin.cpp @@ -0,0 +1,1446 @@ +/*************************************************************************/ +/* animation_tree_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "animation_tree_player_editor_plugin.h" + +#include "core/io/resource_loader.h" +#include "core/project_settings.h" +#include "os/input.h" +#include "os/keyboard.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/panel.h" +#include "scene/main/viewport.h" + +void AnimationTreePlayerEditor::edit(AnimationTreePlayer *p_anim_tree) { + + anim_tree = p_anim_tree; + + if (!anim_tree) { + hide(); + } else { + order.clear(); + p_anim_tree->get_node_list(&order); + /* + for(List<StringName>::Element* E=order.front();E;E=E->next()) { + + if (E->get() >= (int)last_id) + last_id=E->get()+1; + }*/ + play_button->set_pressed(p_anim_tree->is_active()); + //read the orders + } +} + +Size2 AnimationTreePlayerEditor::_get_maximum_size() { + + Size2 max; + + for (List<StringName>::Element *E = order.front(); E; E = E->next()) { + + Point2 pos = anim_tree->node_get_position(E->get()); + + if (click_type == CLICK_NODE && click_node == E->get()) { + + pos += click_motion - click_pos; + } + pos += get_node_size(E->get()); + if (pos.x > max.x) + max.x = pos.x; + if (pos.y > max.y) + max.y = pos.y; + } + + return max; +} + +const char *AnimationTreePlayerEditor::_node_type_names[] = { "Output", "Animation", "OneShot", "Mix", "Blend2", "Blend3", "Blend4", "TimeScale", "TimeSeek", "Transition" }; + +Size2 AnimationTreePlayerEditor::get_node_size(const StringName &p_node) const { + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + + Size2 size = style->get_minimum_size(); + + int count = 2; // title and name + int inputs = anim_tree->node_get_input_count(p_node); + count += inputs ? inputs : 1; + String name = p_node; + + float name_w = font->get_string_size(name).width; + float type_w = font->get_string_size(String(_node_type_names[type])).width; + float max_w = MAX(name_w, type_w); + + switch (type) { + case AnimationTreePlayer::NODE_TIMESEEK: + case AnimationTreePlayer::NODE_OUTPUT: { + } break; + case AnimationTreePlayer::NODE_ANIMATION: + case AnimationTreePlayer::NODE_ONESHOT: + case AnimationTreePlayer::NODE_MIX: + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_BLEND3: + case AnimationTreePlayer::NODE_BLEND4: + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TRANSITION: { + + size.height += font->get_height(); + } break; + case AnimationTreePlayer::NODE_MAX: { + } + } + + size.x += max_w + 20; + size.y += count * (font->get_height() + get_constant("vseparation", "PopupMenu")); + + return size; +} + +void AnimationTreePlayerEditor::_edit_dialog_changede(String) { + + edit_dialog->hide(); +} + +void AnimationTreePlayerEditor::_edit_dialog_changeds(String s) { + + _edit_dialog_changed(); +} + +void AnimationTreePlayerEditor::_edit_dialog_changedf(float) { + + _edit_dialog_changed(); +} + +void AnimationTreePlayerEditor::_edit_dialog_changed() { + + if (updating_edit) + return; + + if (renaming_edit) { + + if (anim_tree->node_rename(edited_node, edit_line[0]->get_text()) == OK) { + for (List<StringName>::Element *E = order.front(); E; E = E->next()) { + + if (E->get() == edited_node) + E->get() = edit_line[0]->get_text(); + } + edited_node = edit_line[0]->get_text(); + } + update(); + return; + } + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); + + switch (type) { + + case AnimationTreePlayer::NODE_TIMESCALE: + anim_tree->timescale_node_set_scale(edited_node, edit_line[0]->get_text().to_double()); + break; + case AnimationTreePlayer::NODE_ONESHOT: + anim_tree->oneshot_node_set_fadein_time(edited_node, edit_line[0]->get_text().to_double()); + anim_tree->oneshot_node_set_fadeout_time(edited_node, edit_line[1]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart_delay(edited_node, edit_line[2]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart_random_delay(edited_node, edit_line[3]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart(edited_node, edit_check->is_pressed()); + anim_tree->oneshot_node_set_mix_mode(edited_node, edit_option->get_selected()); + + break; + + case AnimationTreePlayer::NODE_MIX: + + anim_tree->mix_node_set_amount(edited_node, edit_scroll[0]->get_value()); + break; + case AnimationTreePlayer::NODE_BLEND2: + anim_tree->blend2_node_set_amount(edited_node, edit_scroll[0]->get_value()); + + break; + + case AnimationTreePlayer::NODE_BLEND3: + anim_tree->blend3_node_set_amount(edited_node, edit_scroll[0]->get_value()); + + break; + case AnimationTreePlayer::NODE_BLEND4: + + anim_tree->blend4_node_set_amount(edited_node, Point2(edit_scroll[0]->get_value(), edit_scroll[1]->get_value())); + + break; + + case AnimationTreePlayer::NODE_TRANSITION: { + anim_tree->transition_node_set_xfade_time(edited_node, edit_line[0]->get_text().to_double()); + if (anim_tree->transition_node_get_current(edited_node) != edit_option->get_selected()) + anim_tree->transition_node_set_current(edited_node, edit_option->get_selected()); + } break; + default: {} + } +} + +void AnimationTreePlayerEditor::_edit_dialog_animation_changed() { + + Ref<Animation> anim = property_editor->get_variant().operator RefPtr(); + anim_tree->animation_node_set_animation(edited_node, anim); + update(); +} + +void AnimationTreePlayerEditor::_edit_dialog_edit_animation() { + + if (Engine::get_singleton()->is_editor_hint()) { + get_tree()->get_root()->get_child(0)->call("_resource_selected", property_editor->get_variant().operator RefPtr()); + }; +}; + +void AnimationTreePlayerEditor::_edit_oneshot_start() { + + anim_tree->oneshot_node_start(edited_node); +} + +void AnimationTreePlayerEditor::_play_toggled() { + + anim_tree->set_active(play_button->is_pressed()); +} + +void AnimationTreePlayerEditor::_master_anim_menu_item(int p_item) { + + if (p_item == 0) + _edit_filters(); + else { + + String str = master_anim_popup->get_item_text(p_item); + anim_tree->animation_node_set_master_animation(edited_node, str); + } + update(); +} + +void AnimationTreePlayerEditor::_popup_edit_dialog() { + + updating_edit = true; + + for (int i = 0; i < 2; i++) + edit_scroll[i]->hide(); + + for (int i = 0; i < 4; i++) { + + edit_line[i]->hide(); + edit_label[i]->hide(); + } + + edit_option->hide(); + edit_button->hide(); + filter_button->hide(); + edit_check->hide(); + + Point2 pos = anim_tree->node_get_position(edited_node) - Point2(h_scroll->get_value(), v_scroll->get_value()); + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Size2 size = get_node_size(edited_node); + Point2 popup_pos(pos.x + style->get_margin(MARGIN_LEFT), pos.y + size.y - style->get_margin(MARGIN_BOTTOM)); + popup_pos += get_global_position(); + + if (renaming_edit) { + + edit_label[0]->set_text(TTR("New name:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(edited_node); + edit_line[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + + } else { + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); + + switch (type) { + + case AnimationTreePlayer::NODE_ANIMATION: + + if (anim_tree->get_master_player() != NodePath() && anim_tree->has_node(anim_tree->get_master_player()) && Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player()))) { + + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player())); + master_anim_popup->clear(); + master_anim_popup->add_item(TTR("Edit Filters")); + master_anim_popup->add_separator(); + List<StringName> sn; + ap->get_animation_list(&sn); + sn.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = sn.front(); E; E = E->next()) { + master_anim_popup->add_item(E->get()); + } + + master_anim_popup->set_position(popup_pos); + master_anim_popup->popup(); + } else { + property_editor->edit(this, "", Variant::OBJECT, anim_tree->animation_node_get_animation(edited_node), PROPERTY_HINT_RESOURCE_TYPE, "Animation"); + property_editor->set_position(popup_pos); + property_editor->popup(); + updating_edit = false; + } + return; + case AnimationTreePlayer::NODE_TIMESCALE: + edit_label[0]->set_text(TTR("Scale:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(rtos(anim_tree->timescale_node_get_scale(edited_node))); + edit_line[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + break; + case AnimationTreePlayer::NODE_ONESHOT: + edit_label[0]->set_text(TTR("Fade In (s):")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(rtos(anim_tree->oneshot_node_get_fadein_time(edited_node))); + edit_line[0]->show(); + edit_label[1]->set_text(TTR("Fade Out (s):")); + edit_label[1]->set_position(Point2(5, 55)); + edit_label[1]->show(); + edit_line[1]->set_begin(Point2(15, 75)); + edit_line[1]->set_text(rtos(anim_tree->oneshot_node_get_fadeout_time(edited_node))); + edit_line[1]->show(); + + edit_option->clear(); + edit_option->add_item(TTR("Blend"), 0); + edit_option->add_item(TTR("Mix"), 1); + edit_option->set_begin(Point2(15, 105)); + + edit_option->select(anim_tree->oneshot_node_get_mix_mode(edited_node)); + edit_option->show(); + + edit_check->set_text(TTR("Auto Restart:")); + edit_check->set_begin(Point2(15, 125)); + edit_check->set_pressed(anim_tree->oneshot_node_has_autorestart(edited_node)); + edit_check->show(); + + edit_label[2]->set_text(TTR("Restart (s):")); + edit_label[2]->set_position(Point2(5, 145)); + edit_label[2]->show(); + edit_line[2]->set_begin(Point2(15, 165)); + edit_line[2]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_delay(edited_node))); + edit_line[2]->show(); + edit_label[3]->set_text(TTR("Random Restart (s):")); + edit_label[3]->set_position(Point2(5, 195)); + edit_label[3]->show(); + edit_line[3]->set_begin(Point2(15, 215)); + edit_line[3]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_random_delay(edited_node))); + edit_line[3]->show(); + + filter_button->set_begin(Point2(10, 245)); + filter_button->show(); + + edit_button->set_begin(Point2(10, 268)); + edit_button->set_text(TTR("Start!")); + + edit_button->show(); + + edit_dialog->set_size(Size2(180, 293)); + + break; + + case AnimationTreePlayer::NODE_MIX: + + edit_label[0]->set_text(TTR("Amount:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->mix_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + + break; + case AnimationTreePlayer::NODE_BLEND2: + edit_label[0]->set_text(TTR("Blend:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->blend2_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + filter_button->set_begin(Point2(10, 47)); + filter_button->show(); + edit_dialog->set_size(Size2(150, 74)); + + break; + + case AnimationTreePlayer::NODE_BLEND3: + edit_label[0]->set_text(TTR("Blend:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(-1); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->blend3_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + + break; + case AnimationTreePlayer::NODE_BLEND4: + + edit_label[0]->set_text(TTR("Blend 0:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->blend4_node_get_amount(edited_node).x); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + edit_label[1]->set_text(TTR("Blend 1:")); + edit_label[1]->set_position(Point2(5, 55)); + edit_label[1]->show(); + edit_scroll[1]->set_min(0); + edit_scroll[1]->set_max(1); + edit_scroll[1]->set_step(0.01); + edit_scroll[1]->set_value(anim_tree->blend4_node_get_amount(edited_node).y); + edit_scroll[1]->set_begin(Point2(15, 75)); + edit_scroll[1]->show(); + edit_dialog->set_size(Size2(150, 100)); + + break; + + case AnimationTreePlayer::NODE_TRANSITION: { + + edit_label[0]->set_text(TTR("X-Fade Time (s):")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(rtos(anim_tree->transition_node_get_xfade_time(edited_node))); + edit_line[0]->show(); + + edit_label[1]->set_text(TTR("Current:")); + edit_label[1]->set_position(Point2(5, 55)); + edit_label[1]->show(); + edit_option->set_begin(Point2(15, 75)); + + edit_option->clear(); + + for (int i = 0; i < anim_tree->transition_node_get_input_count(edited_node); i++) { + edit_option->add_item(itos(i), i); + } + + edit_option->select(anim_tree->transition_node_get_current(edited_node)); + edit_option->show(); + edit_dialog->set_size(Size2(150, 100)); + + } break; + default: {} + } + } + + edit_dialog->set_position(popup_pos); + edit_dialog->popup(); + + updating_edit = false; +} + +void AnimationTreePlayerEditor::_draw_node(const StringName &p_node) { + + RID ci = get_canvas_item(); + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + Color font_color = get_color("font_color", "PopupMenu"); + Color font_color_title = get_color("font_color_hover", "PopupMenu"); + font_color_title.a *= 0.8; + Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); + + Size2 size = get_node_size(p_node); + Point2 pos = anim_tree->node_get_position(p_node); + if (click_type == CLICK_NODE && click_node == p_node) { + + pos += click_motion - click_pos; + if (pos.x < 5) + pos.x = 5; + if (pos.y < 5) + pos.y = 5; + } + + pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); + + style->draw(ci, Rect2(pos, size)); + + float w = size.width - style->get_minimum_size().width; + float h = font->get_height() + get_constant("vseparation", "PopupMenu"); + + Point2 ofs = style->get_offset() + pos; + Point2 ascofs(0, font->get_ascent()); + + Color bx = font_color_title; + bx.a *= 0.1; + draw_rect(Rect2(ofs, Size2(size.width - style->get_minimum_size().width, font->get_height())), bx); + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, String(_node_type_names[type]), font_color_title); + + ofs.y += h; + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, p_node, font_color); + ofs.y += h; + + int count = 2; // title and name + int inputs = anim_tree->node_get_input_count(p_node); + count += inputs ? inputs : 1; + + float icon_h_ofs = Math::floor((font->get_height() - slot_icon->get_height()) / 2.0) + 1; + + if (type != AnimationTreePlayer::NODE_OUTPUT) + slot_icon->draw(ci, ofs + Point2(w, icon_h_ofs)); //output + + if (inputs) { + for (int i = 0; i < inputs; i++) { + + slot_icon->draw(ci, ofs + Point2(-slot_icon->get_width(), icon_h_ofs)); + String text; + switch (type) { + + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TIMESEEK: text = "in"; break; + case AnimationTreePlayer::NODE_OUTPUT: text = "out"; break; + case AnimationTreePlayer::NODE_ANIMATION: break; + case AnimationTreePlayer::NODE_ONESHOT: text = (i == 0 ? "in" : "add"); break; + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_MIX: text = (i == 0 ? "a" : "b"); break; + case AnimationTreePlayer::NODE_BLEND3: + switch (i) { + case 0: text = "b-"; break; + case 1: text = "a"; break; + case 2: text = "b+"; break; + } + break; + + case AnimationTreePlayer::NODE_BLEND4: + switch (i) { + case 0: text = "a0"; break; + case 1: text = "b0"; break; + case 2: text = "a1"; break; + case 3: text = "b1"; break; + } + break; + + case AnimationTreePlayer::NODE_TRANSITION: + text = itos(i); + if (anim_tree->transition_node_has_input_auto_advance(p_node, i)) + text += "->"; + + break; + default: {} + } + font->draw(ci, ofs + ascofs + Point2(3, 0), text, font_color); + + ofs.y += h; + } + } else { + ofs.y += h; + } + + Ref<StyleBox> pg_bg = get_stylebox("bg", "ProgressBar"); + Ref<StyleBox> pg_fill = get_stylebox("fill", "ProgressBar"); + Rect2 pg_rect(ofs, Size2(w, h)); + + bool editable = true; + switch (type) { + case AnimationTreePlayer::NODE_ANIMATION: { + + Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); + String text; + if (anim_tree->animation_node_get_master_animation(p_node) != "") + text = anim_tree->animation_node_get_master_animation(p_node); + else if (anim.is_null()) + text = "load..."; + else + text = anim->get_name(); + + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, text, font_color_title); + + } break; + case AnimationTreePlayer::NODE_ONESHOT: + case AnimationTreePlayer::NODE_MIX: + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_BLEND3: + case AnimationTreePlayer::NODE_BLEND4: + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TRANSITION: { + + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit...", font_color_title); + } break; + default: editable = false; + } + + if (editable) { + + Ref<Texture> arrow = get_icon("GuiDropdown", "EditorIcons"); + Point2 arrow_ofs(w - arrow->get_width(), Math::floor((h - arrow->get_height()) / 2)); + arrow->draw(ci, ofs + arrow_ofs); + } +} + +AnimationTreePlayerEditor::ClickType AnimationTreePlayerEditor::_locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const { + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + + float h = (font->get_height() + get_constant("vseparation", "PopupMenu")); + + for (const List<StringName>::Element *E = order.back(); E; E = E->prev()) { + + StringName node = E->get(); + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(node); + + Point2 pos = anim_tree->node_get_position(node); + Size2 size = get_node_size(node); + + pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); + + if (!Rect2(pos, size).has_point(p_click)) + continue; + + if (p_node_id) + *p_node_id = node; + + pos = p_click - pos; + + float y = pos.y - style->get_offset().height; + + if (y < 2 * h) + return CLICK_NODE; + y -= 2 * h; + + int inputs = anim_tree->node_get_input_count(node); + int count = MAX(inputs, 1); + + if (inputs == 0 || (pos.x > size.width / 2 && type != AnimationTreePlayer::NODE_OUTPUT)) { + + if (y < count * h) { + + if (p_slot_index) + *p_slot_index = 0; + return CLICK_OUTPUT_SLOT; + } + } + + for (int i = 0; i < count; i++) { + + if (y < h) { + if (p_slot_index) + *p_slot_index = i; + return CLICK_INPUT_SLOT; + } + y -= h; + } + + bool has_parameters = type != AnimationTreePlayer::NODE_OUTPUT && type != AnimationTreePlayer::NODE_TIMESEEK; + return has_parameters ? CLICK_PARAMETER : CLICK_NODE; + } + + return CLICK_NONE; +} + +Point2 AnimationTreePlayerEditor::_get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot) { + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); + + Size2 size = get_node_size(p_node_id); + Point2 pos = anim_tree->node_get_position(p_node_id); + + if (click_type == CLICK_NODE && click_node == p_node_id) { + + pos += click_motion - click_pos; + if (pos.x < 5) + pos.x = 5; + if (pos.y < 5) + pos.y = 5; + } + + pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); + + float w = size.width - style->get_minimum_size().width; + float h = font->get_height() + get_constant("vseparation", "PopupMenu"); + + pos += style->get_offset(); + + pos.y += h * 2; + + pos.y += h * p_slot; + + pos += Point2(-slot_icon->get_width() / 2.0, h / 2.0).floor(); + + if (!p_input) { + pos.x += w + slot_icon->get_width(); + } + + return pos; +} + +void AnimationTreePlayerEditor::_gui_input(Ref<InputEvent> p_event) { + + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid()) { + + if (mb->is_pressed()) { + + if (mb->get_button_index() == 1) { + click_pos = Point2(mb->get_position().x, mb->get_position().y); + click_motion = click_pos; + click_type = _locate_click(click_pos, &click_node, &click_slot); + if (click_type != CLICK_NONE) { + + order.erase(click_node); + order.push_back(click_node); + update(); + } + + switch (click_type) { + case CLICK_INPUT_SLOT: { + click_pos = _get_slot_pos(click_node, true, click_slot); + } break; + case CLICK_OUTPUT_SLOT: { + click_pos = _get_slot_pos(click_node, false, click_slot); + } break; + case CLICK_PARAMETER: { + + edited_node = click_node; + renaming_edit = false; + _popup_edit_dialog(); + //open editor + //_node_edit_property(click_node); + } break; + default: {} + } + } + if (mb->get_button_index() == 2) { + + if (click_type != CLICK_NONE) { + click_type = CLICK_NONE; + update(); + } else { + // try to disconnect/remove + + Point2 rclick_pos = Point2(mb->get_position().x, mb->get_position().y); + rclick_type = _locate_click(rclick_pos, &rclick_node, &rclick_slot); + if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) { + + node_popup->clear(); + node_popup->set_size(Size2(1, 1)); + node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT); + if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) { + node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); + if (rclick_type == CLICK_INPUT_SLOT) { + if (anim_tree->transition_node_has_input_auto_advance(rclick_node, rclick_slot)) + node_popup->add_item(TTR("Clear Auto-Advance"), NODE_CLEAR_AUTOADVANCE); + else + node_popup->add_item(TTR("Set Auto-Advance"), NODE_SET_AUTOADVANCE); + node_popup->add_item(TTR("Delete Input"), NODE_DELETE_INPUT); + } + } + + node_popup->set_position(rclick_pos + get_global_position()); + node_popup->popup(); + } + + if (rclick_type == CLICK_NODE) { + node_popup->clear(); + node_popup->set_size(Size2(1, 1)); + node_popup->add_item(TTR("Rename"), NODE_RENAME); + node_popup->add_item(TTR("Remove"), NODE_ERASE); + if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) + node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); + node_popup->set_position(rclick_pos + get_global_position()); + node_popup->popup(); + } + } + } + } else { + + if (mb->get_button_index() == 1 && click_type != CLICK_NONE) { + + switch (click_type) { + case CLICK_INPUT_SLOT: + case CLICK_OUTPUT_SLOT: { + + Point2 dst_click_pos = Point2(mb->get_position().x, mb->get_position().y); + StringName id; + int slot; + ClickType dst_click_type = _locate_click(dst_click_pos, &id, &slot); + + if (dst_click_type == CLICK_INPUT_SLOT && click_type == CLICK_OUTPUT_SLOT) { + + anim_tree->connect_nodes(click_node, id, slot); + } + if (click_type == CLICK_INPUT_SLOT && dst_click_type == CLICK_OUTPUT_SLOT) { + + anim_tree->connect_nodes(id, click_node, click_slot); + } + + } break; + case CLICK_NODE: { + Point2 new_pos = anim_tree->node_get_position(click_node) + (click_motion - click_pos); + if (new_pos.x < 5) + new_pos.x = 5; + if (new_pos.y < 5) + new_pos.y = 5; + anim_tree->node_set_position(click_node, new_pos); + + } break; + default: {} + } + + click_type = CLICK_NONE; + update(); + } + } + } + + Ref<InputEventMouseMotion> mm = p_event; + + if (mm.is_valid()) { + + if (mm->get_button_mask() & 1 && click_type != CLICK_NONE) { + + click_motion = Point2(mm->get_position().x, mm->get_position().y); + update(); + } + if ((mm->get_button_mask() & 4 || Input::get_singleton()->is_key_pressed(KEY_SPACE))) { + + h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x); + v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y); + update(); + } + } +} + +void AnimationTreePlayerEditor::_draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color) { + + static const int steps = 20; + + Rect2 r; + r.position = p_from; + r.expand_to(p_to); + Vector2 sign = Vector2((p_from.x < p_to.x) ? 1 : -1, (p_from.y < p_to.y) ? 1 : -1); + bool flip = sign.x * sign.y < 0; + + Vector2 prev; + for (int i = 0; i <= steps; i++) { + + float d = i / float(steps); + float c = -Math::cos(d * Math_PI) * 0.5 + 0.5; + if (flip) + c = 1.0 - c; + Vector2 p = r.position + Vector2(d * r.size.width, c * r.size.height); + + if (i > 0) { + + draw_line(prev, p, p_color, 2); + } + + prev = p; + } +} + +void AnimationTreePlayerEditor::_notification(int p_what) { + + switch (p_what) { + + case NOTIFICATION_ENTER_TREE: { + + play_button->set_icon(get_icon("Play", "EditorIcons")); + add_menu->set_icon(get_icon("Add", "EditorIcons")); + } break; + case NOTIFICATION_DRAW: { + + _update_scrollbars(); + //VisualServer::get_singleton()->canvas_item_add_rect(get_canvas_item(),Rect2(Point2(),get_size()),Color(0,0,0,1)); + get_stylebox("bg", "Tree")->draw(get_canvas_item(), Rect2(Point2(), get_size())); + + for (List<StringName>::Element *E = order.front(); E; E = E->next()) { + + _draw_node(E->get()); + } + + if (click_type == CLICK_INPUT_SLOT || click_type == CLICK_OUTPUT_SLOT) { + + _draw_cos_line(click_pos, click_motion, Color(0.5, 1, 0.5, 0.8)); + } + + List<AnimationTreePlayer::Connection> connections; + anim_tree->get_connection_list(&connections); + + for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { + + const AnimationTreePlayer::Connection &c = E->get(); + Point2 source = _get_slot_pos(c.src_node, false, 0); + Point2 dest = _get_slot_pos(c.dst_node, true, c.dst_input); + Color col = Color(1, 1, 0.5, 0.8); + /* + if (click_type==CLICK_NODE && click_node==c.src_node) { + + source+=click_motion-click_pos; + } + + if (click_type==CLICK_NODE && click_node==c.dst_node) { + + dest+=click_motion-click_pos; + }*/ + + _draw_cos_line(source, dest, col); + } + + switch (anim_tree->get_last_error()) { + + case AnimationTreePlayer::CONNECT_OK: { + + Ref<Font> f = get_font("font", "Label"); + f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is valid."), Color(0, 1, 0.6, 0.8)); + } break; + default: { + + Ref<Font> f = get_font("font", "Label"); + f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is invalid."), Color(1, 0.6, 0.0, 0.8)); + } break; + } + + } break; + } +} + +void AnimationTreePlayerEditor::_update_scrollbars() { + + Size2 size = get_size(); + Size2 hmin = h_scroll->get_combined_minimum_size(); + Size2 vmin = v_scroll->get_combined_minimum_size(); + + v_scroll->set_begin(Point2(size.width - vmin.width, 0)); + v_scroll->set_end(Point2(size.width, size.height)); + + h_scroll->set_begin(Point2(0, size.height - hmin.height)); + h_scroll->set_end(Point2(size.width - vmin.width, size.height)); + + Size2 min = _get_maximum_size(); + + if (min.height < size.height - hmin.height) { + + v_scroll->hide(); + offset.y = 0; + } else { + + v_scroll->show(); + v_scroll->set_max(min.height); + v_scroll->set_page(size.height - hmin.height); + offset.y = v_scroll->get_value(); + } + + if (min.width < size.width - vmin.width) { + + h_scroll->hide(); + offset.x = 0; + } else { + + h_scroll->show(); + h_scroll->set_max(min.width); + h_scroll->set_page(size.width - vmin.width); + offset.x = h_scroll->get_value(); + } +} + +void AnimationTreePlayerEditor::_scroll_moved(float) { + + offset.x = h_scroll->get_value(); + offset.y = v_scroll->get_value(); + update(); +} + +void AnimationTreePlayerEditor::_node_menu_item(int p_item) { + + switch (p_item) { + + case NODE_DISCONNECT: { + + if (rclick_type == CLICK_INPUT_SLOT) { + + anim_tree->disconnect_nodes(rclick_node, rclick_slot); + update(); + } + + if (rclick_type == CLICK_OUTPUT_SLOT) { + + List<AnimationTreePlayer::Connection> connections; + anim_tree->get_connection_list(&connections); + + for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { + + const AnimationTreePlayer::Connection &c = E->get(); + if (c.dst_node == rclick_node) { + + anim_tree->disconnect_nodes(c.dst_node, c.dst_input); + } + } + update(); + } + + } break; + case NODE_RENAME: { + + renaming_edit = true; + edited_node = rclick_node; + _popup_edit_dialog(); + + } break; + case NODE_ADD_INPUT: { + + anim_tree->transition_node_set_input_count(rclick_node, anim_tree->transition_node_get_input_count(rclick_node) + 1); + update(); + } break; + case NODE_DELETE_INPUT: { + + anim_tree->transition_node_delete_input(rclick_node, rclick_slot); + update(); + } break; + case NODE_SET_AUTOADVANCE: { + + anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, true); + update(); + + } break; + case NODE_CLEAR_AUTOADVANCE: { + + anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, false); + update(); + + } break; + + case NODE_ERASE: { + + if (rclick_node == "out") + break; + order.erase(rclick_node); + anim_tree->remove_node(rclick_node); + update(); + } break; + } +} + +StringName AnimationTreePlayerEditor::_add_node(int p_item) { + + static const char *bname[] = { + "out", + "anim", + "oneshot", + "mix", + "blend2", + "blend3", + "blend4", + "scale", + "seek", + "transition" + }; + + String name; + int idx = 1; + + while (true) { + + name = bname[p_item]; + if (idx > 1) + name += " " + itos(idx); + if (anim_tree->node_exists(name)) + idx++; + else + break; + } + + anim_tree->add_node((AnimationTreePlayer::NodeType)p_item, name); + anim_tree->node_set_position(name, Point2(last_x, last_y)); + order.push_back(name); + last_x += 10; + last_y += 10; + last_x = last_x % (int)get_size().width; + last_y = last_y % (int)get_size().height; + update(); + + return name; +}; + +void AnimationTreePlayerEditor::_file_dialog_selected(String p_path) { + + switch (file_op) { + + case MENU_IMPORT_ANIMATIONS: { + Vector<String> files = file_dialog->get_selected_files(); + + for (int i = 0; i < files.size(); i++) { + + StringName node = _add_node(AnimationTreePlayer::NODE_ANIMATION); + + RES anim = ResourceLoader::load(files[i]); + anim_tree->animation_node_set_animation(node, anim); + //anim_tree->node_set_name(node, files[i].get_file()); + }; + } break; + + default: + break; + }; +}; + +void AnimationTreePlayerEditor::_add_menu_item(int p_item) { + + if (p_item == MENU_GRAPH_CLEAR) { + + //clear + } else if (p_item == MENU_IMPORT_ANIMATIONS) { + + file_op = MENU_IMPORT_ANIMATIONS; + file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); + file_dialog->popup_centered_ratio(); + + } else { + + _add_node(p_item); + } +} + +Size2 AnimationTreePlayerEditor::get_minimum_size() const { + + return Size2(10, 200); +} + +void AnimationTreePlayerEditor::_find_paths_for_filter(const StringName &p_node, Set<String> &paths) { + + ERR_FAIL_COND(!anim_tree->node_exists(p_node)); + + for (int i = 0; i < anim_tree->node_get_input_count(p_node); i++) { + + StringName port = anim_tree->node_get_input_source(p_node, i); + if (port == StringName()) + continue; + _find_paths_for_filter(port, paths); + } + + if (anim_tree->node_get_type(p_node) == AnimationTreePlayer::NODE_ANIMATION) { + + Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); + if (anim.is_valid()) { + + for (int i = 0; i < anim->get_track_count(); i++) { + paths.insert(anim->track_get_path(i)); + } + } + } +} + +void AnimationTreePlayerEditor::_filter_edited() { + + TreeItem *ed = filter->get_edited(); + if (!ed) + return; + + if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { + anim_tree->oneshot_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { + anim_tree->blend2_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { + anim_tree->animation_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + } +} + +void AnimationTreePlayerEditor::_edit_filters() { + + filter_dialog->popup_centered_ratio(); + filter->clear(); + + Set<String> npb; + _find_paths_for_filter(edited_node, npb); + + TreeItem *root = filter->create_item(); + filter->set_hide_root(true); + Map<String, TreeItem *> pm; + + Node *base = anim_tree->get_node(anim_tree->get_base_path()); + + for (Set<String>::Element *E = npb.front(); E; E = E->next()) { + + TreeItem *parent = root; + String descr = E->get(); + if (base) { + NodePath np = E->get(); + + if (np.get_subname_count() == 1) { + Node *n = base->get_node(np); + Skeleton *s = Object::cast_to<Skeleton>(n); + if (s) { + + String skelbase = E->get().substr(0, E->get().find(":")); + + int bidx = s->find_bone(np.get_subname(0)); + + if (bidx != -1) { + int bparent = s->get_bone_parent(bidx); + // + if (bparent != -1) { + + String bpn = skelbase + ":" + s->get_bone_name(bparent); + if (pm.has(bpn)) { + parent = pm[bpn]; + descr = np.get_subname(0); + } + } else { + + if (pm.has(skelbase)) { + parent = pm[skelbase]; + } + } + } + } + } + } + + TreeItem *it = filter->create_item(parent); + it->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + it->set_text(0, descr); + it->set_metadata(0, NodePath(E->get())); + it->set_editable(0, true); + if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { + it->set_checked(0, anim_tree->oneshot_node_is_path_filtered(edited_node, E->get())); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { + it->set_checked(0, anim_tree->blend2_node_is_path_filtered(edited_node, E->get())); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { + it->set_checked(0, anim_tree->animation_node_is_path_filtered(edited_node, E->get())); + } + pm[E->get()] = it; + } +} + +void AnimationTreePlayerEditor::_bind_methods() { + + ClassDB::bind_method("_add_menu_item", &AnimationTreePlayerEditor::_add_menu_item); + ClassDB::bind_method("_node_menu_item", &AnimationTreePlayerEditor::_node_menu_item); + ClassDB::bind_method("_gui_input", &AnimationTreePlayerEditor::_gui_input); + //ClassDB::bind_method( "_node_param_changed", &AnimationTreeEditor::_node_param_changed ); + ClassDB::bind_method("_scroll_moved", &AnimationTreePlayerEditor::_scroll_moved); + ClassDB::bind_method("_edit_dialog_changeds", &AnimationTreePlayerEditor::_edit_dialog_changeds); + ClassDB::bind_method("_edit_dialog_changede", &AnimationTreePlayerEditor::_edit_dialog_changede); + ClassDB::bind_method("_edit_dialog_changedf", &AnimationTreePlayerEditor::_edit_dialog_changedf); + ClassDB::bind_method("_edit_dialog_changed", &AnimationTreePlayerEditor::_edit_dialog_changed); + ClassDB::bind_method("_edit_dialog_animation_changed", &AnimationTreePlayerEditor::_edit_dialog_animation_changed); + ClassDB::bind_method("_edit_dialog_edit_animation", &AnimationTreePlayerEditor::_edit_dialog_edit_animation); + ClassDB::bind_method("_play_toggled", &AnimationTreePlayerEditor::_play_toggled); + ClassDB::bind_method("_edit_oneshot_start", &AnimationTreePlayerEditor::_edit_oneshot_start); + ClassDB::bind_method("_file_dialog_selected", &AnimationTreePlayerEditor::_file_dialog_selected); + ClassDB::bind_method("_master_anim_menu_item", &AnimationTreePlayerEditor::_master_anim_menu_item); + ClassDB::bind_method("_edit_filters", &AnimationTreePlayerEditor::_edit_filters); + ClassDB::bind_method("_filter_edited", &AnimationTreePlayerEditor::_filter_edited); +} + +AnimationTreePlayerEditor::AnimationTreePlayerEditor() { + + set_focus_mode(FOCUS_ALL); + + PopupMenu *p; + List<PropertyInfo> defaults; + + add_menu = memnew(MenuButton); + //add_menu->set_ + add_menu->set_position(Point2(0, 0)); + add_menu->set_size(Point2(25, 15)); + add_child(add_menu); + + p = add_menu->get_popup(); + p->add_item(TTR("Animation Node"), AnimationTreePlayer::NODE_ANIMATION); + p->add_item(TTR("OneShot Node"), AnimationTreePlayer::NODE_ONESHOT); + p->add_item(TTR("Mix Node"), AnimationTreePlayer::NODE_MIX); + p->add_item(TTR("Blend2 Node"), AnimationTreePlayer::NODE_BLEND2); + p->add_item(TTR("Blend3 Node"), AnimationTreePlayer::NODE_BLEND3); + p->add_item(TTR("Blend4 Node"), AnimationTreePlayer::NODE_BLEND4); + p->add_item(TTR("TimeScale Node"), AnimationTreePlayer::NODE_TIMESCALE); + p->add_item(TTR("TimeSeek Node"), AnimationTreePlayer::NODE_TIMESEEK); + p->add_item(TTR("Transition Node"), AnimationTreePlayer::NODE_TRANSITION); + p->add_separator(); + p->add_item(TTR("Import Animations..."), MENU_IMPORT_ANIMATIONS); // wtf + p->add_separator(); + p->add_item(TTR("Clear"), MENU_GRAPH_CLEAR); + + p->connect("id_pressed", this, "_add_menu_item"); + + play_button = memnew(Button); + play_button->set_position(Point2(25, 0)); + play_button->set_size(Point2(25, 15)); + add_child(play_button); + play_button->set_toggle_mode(true); + play_button->connect("pressed", this, "_play_toggled"); + + last_x = 50; + last_y = 50; + + property_editor = memnew(CustomPropertyEditor); + add_child(property_editor); + property_editor->connect("variant_changed", this, "_edit_dialog_animation_changed"); + property_editor->connect("resource_edit_request", this, "_edit_dialog_edit_animation"); + + h_scroll = memnew(HScrollBar); + v_scroll = memnew(VScrollBar); + + add_child(h_scroll); + add_child(v_scroll); + + h_scroll->connect("value_changed", this, "_scroll_moved"); + v_scroll->connect("value_changed", this, "_scroll_moved"); + + node_popup = memnew(PopupMenu); + add_child(node_popup); + node_popup->set_as_toplevel(true); + + master_anim_popup = memnew(PopupMenu); + add_child(master_anim_popup); + master_anim_popup->connect("id_pressed", this, "_master_anim_menu_item"); + + node_popup->connect("id_pressed", this, "_node_menu_item"); + + updating_edit = false; + + edit_dialog = memnew(PopupPanel); + //edit_dialog->get_ok()->hide(); + //edit_dialog->get_cancel()->hide(); + add_child(edit_dialog); + + edit_option = memnew(OptionButton); + edit_option->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_option->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_option); + edit_option->connect("item_selected", this, "_edit_dialog_changedf"); + edit_option->hide(); + + for (int i = 0; i < 2; i++) { + edit_scroll[i] = memnew(HSlider); + edit_scroll[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_scroll[i]->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_scroll[i]); + edit_scroll[i]->hide(); + edit_scroll[i]->connect("value_changed", this, "_edit_dialog_changedf"); + } + for (int i = 0; i < 4; i++) { + edit_line[i] = memnew(LineEdit); + edit_line[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_line[i]->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_line[i]); + edit_line[i]->hide(); + edit_line[i]->connect("text_changed", this, "_edit_dialog_changeds"); + edit_line[i]->connect("text_entered", this, "_edit_dialog_changede"); + edit_label[i] = memnew(Label); + edit_dialog->add_child(edit_label[i]); + edit_label[i]->hide(); + } + + edit_button = memnew(Button); + edit_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_button->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_button); + edit_button->hide(); + edit_button->connect("pressed", this, "_edit_oneshot_start"); + + edit_check = memnew(CheckButton); + edit_check->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_check->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_check); + edit_check->hide(); + edit_check->connect("pressed", this, "_edit_dialog_changed"); + + file_dialog = memnew(EditorFileDialog); + file_dialog->set_enable_multiple_selection(true); + file_dialog->set_current_dir(ProjectSettings::get_singleton()->get_resource_path()); + add_child(file_dialog); + file_dialog->connect("file_selected", this, "_file_dialog_selected"); + + filter_dialog = memnew(AcceptDialog); + filter_dialog->set_title(TTR("Edit Node Filters")); + add_child(filter_dialog); + + filter = memnew(Tree); + filter_dialog->add_child(filter); + //filter_dialog->set_child_rect(filter); + filter->connect("item_edited", this, "_filter_edited"); + + filter_button = memnew(Button); + filter_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); + filter_button->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(filter_button); + filter_button->hide(); + filter_button->set_text(TTR("Filters...")); + filter_button->connect("pressed", this, "_edit_filters"); + + set_clip_contents(true); +} + +void AnimationTreePlayerEditorPlugin::edit(Object *p_object) { + + anim_tree_editor->edit(Object::cast_to<AnimationTreePlayer>(p_object)); +} + +bool AnimationTreePlayerEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("AnimationTreePlayer"); +} + +void AnimationTreePlayerEditorPlugin::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_physics_process(true); + } else { + + if (anim_tree_editor->is_visible_in_tree()) + editor->hide_bottom_panel(); + button->hide(); + anim_tree_editor->set_physics_process(false); + } +} + +AnimationTreePlayerEditorPlugin::AnimationTreePlayerEditorPlugin(EditorNode *p_node) { + + editor = p_node; + anim_tree_editor = memnew(AnimationTreePlayerEditor); + anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); + + button = editor->add_bottom_panel_item(TTR("AnimationTree"), anim_tree_editor); + button->hide(); +} + +AnimationTreePlayerEditorPlugin::~AnimationTreePlayerEditorPlugin() { +} diff --git a/editor/plugins/animation_tree_player_editor_plugin.h b/editor/plugins/animation_tree_player_editor_plugin.h new file mode 100644 index 0000000000..d1c5f395e4 --- /dev/null +++ b/editor/plugins/animation_tree_player_editor_plugin.h @@ -0,0 +1,187 @@ +/*************************************************************************/ +/* animation_tree_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ANIMATION_TREE_PLAYER_EDITOR_PLUGIN_H +#define ANIMATION_TREE_PLAYER_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "editor/property_editor.h" +#include "scene/animation/animation_tree_player.h" +#include "scene/gui/button.h" +#include "scene/gui/popup.h" +#include "scene/gui/tree.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class AnimationTreePlayerEditor : public Control { + + GDCLASS(AnimationTreePlayerEditor, Control); + + static const char *_node_type_names[]; + + enum ClickType { + CLICK_NONE, + CLICK_NAME, + CLICK_NODE, + CLICK_INPUT_SLOT, + CLICK_OUTPUT_SLOT, + CLICK_PARAMETER + }; + + enum { + + MENU_GRAPH_CLEAR = 100, + MENU_IMPORT_ANIMATIONS = 101, + NODE_DISCONNECT, + NODE_RENAME, + NODE_ERASE, + NODE_ADD_INPUT, + NODE_DELETE_INPUT, + NODE_SET_AUTOADVANCE, + NODE_CLEAR_AUTOADVANCE + }; + + bool renaming_edit; + StringName edited_node; + bool updating_edit; + Popup *edit_dialog; + HSlider *edit_scroll[2]; + LineEdit *edit_line[4]; + OptionButton *edit_option; + Label *edit_label[4]; + Button *edit_button; + Button *filter_button; + CheckButton *edit_check; + EditorFileDialog *file_dialog; + int file_op; + + void _popup_edit_dialog(); + + void _setup_edit_dialog(const StringName &p_node); + PopupMenu *master_anim_popup; + PopupMenu *node_popup; + PopupMenu *add_popup; + HScrollBar *h_scroll; + VScrollBar *v_scroll; + MenuButton *add_menu; + + CustomPropertyEditor *property_editor; + + AnimationTreePlayer *anim_tree; + List<StringName> order; + Set<StringName> active_nodes; + + int last_x, last_y; + + Point2 offset; + ClickType click_type; + Point2 click_pos; + StringName click_node; + int click_slot; + Point2 click_motion; + ClickType rclick_type; + StringName rclick_node; + int rclick_slot; + + Button *play_button; + + Size2 _get_maximum_size(); + Size2 get_node_size(const StringName &p_node) const; + void _draw_node(const StringName &p_node); + + AcceptDialog *filter_dialog; + Tree *filter; + + void _draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color); + void _update_scrollbars(); + void _scroll_moved(float); + void _play_toggled(); + /* + void _node_param_changed(); + void _node_add_callback(); + void _node_add(VisualServer::AnimationTreeNodeType p_type); + void _node_edit_property(const StringName& p_node); +*/ + + void _master_anim_menu_item(int p_item); + void _node_menu_item(int p_item); + void _add_menu_item(int p_item); + + void _filter_edited(); + void _find_paths_for_filter(const StringName &p_node, Set<String> &paths); + void _edit_filters(); + + void _edit_oneshot_start(); + void _edit_dialog_animation_changed(); + void _edit_dialog_edit_animation(); + void _edit_dialog_changeds(String); + void _edit_dialog_changede(String); + void _edit_dialog_changedf(float); + void _edit_dialog_changed(); + void _dialog_changed() const; + ClickType _locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const; + Point2 _get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot); + + StringName _add_node(int p_item); + void _file_dialog_selected(String p_path); + +protected: + void _notification(int p_what); + void _gui_input(Ref<InputEvent> p_event); + static void _bind_methods(); + +public: + virtual Size2 get_minimum_size() const; + void edit(AnimationTreePlayer *p_anim_tree); + AnimationTreePlayerEditor(); +}; + +class AnimationTreePlayerEditorPlugin : public EditorPlugin { + + GDCLASS(AnimationTreePlayerEditorPlugin, EditorPlugin); + + AnimationTreePlayerEditor *anim_tree_editor; + EditorNode *editor; + Button *button; + +public: + virtual String get_name() const { return "AnimTree"; } + 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); + + AnimationTreePlayerEditorPlugin(EditorNode *p_node); + ~AnimationTreePlayerEditorPlugin(); +}; + +#endif // ANIMATION_TREE_EDITOR_PLUGIN_H diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index d56ba5d403..2df6ef7c8a 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -1394,13 +1394,9 @@ void KinematicBody::_bind_methods() { ClassDB::bind_method(D_METHOD("get_slide_count"), &KinematicBody::get_slide_count); ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &KinematicBody::_get_slide_collision); - ADD_GROUP("Axis Lock", "axis_lock_"); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_x"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_X); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_y"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_Y); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_z"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_Z); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_x"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_ANGULAR_X); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_y"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_ANGULAR_Y); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_z"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_ANGULAR_Z); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "move_lock_x", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_X); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "move_lock_y", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_Y); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "move_lock_z", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_Z); ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); } diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index 1bc9fa4b12..2bde8a16c9 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -1,12 +1,15 @@ #include "animation_blend_space_1d.h" -void AnimationNodeBlendSpace1D::set_tree(AnimationTree *p_player) { - AnimationRootNode::set_tree(p_player); +void AnimationNodeBlendSpace1D::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL,blend_position)); +} +Variant AnimationNodeBlendSpace1D::get_parameter_default_value(const StringName &p_parameter) const { + return 0; +} - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_tree(p_player); - } +Ref<AnimationNode> AnimationNodeBlendSpace1D::get_child_by_name(const StringName &p_name) { + return get_blend_point_node(p_name.operator String().to_int()); } void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const { @@ -20,6 +23,10 @@ void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const AnimationRootNode::_validate_property(property); } +void AnimationNodeBlendSpace1D::_tree_changed() { + emit_signal("tree_changed"); +} + 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); @@ -38,30 +45,37 @@ void AnimationNodeBlendSpace1D::_bind_methods() { 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); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeBlendSpace1D::_tree_changed); + 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::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_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::get_child_nodes(List<ChildNode> *r_child_nodes) { + for(int i=0;i<blend_points_used;i++) { + ChildNode cn; + cn.name=itos(i); + cn.node=blend_points[i].node; + r_child_nodes->push_back(cn); + } +} + void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &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) { @@ -75,10 +89,12 @@ void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_ 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_tree(get_tree()); + blend_points[p_at_index].node->connect("tree_changed",this,"_tree_changed",varray(),CONNECT_REFERENCE_COUNTED); blend_points_used++; + emit_signal("tree_changed"); + + } void AnimationNodeBlendSpace1D::set_blend_point_position(int p_point, float p_position) { @@ -92,13 +108,14 @@ void AnimationNodeBlendSpace1D::set_blend_point_node(int p_point, const Ref<Anim 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_tree(NULL); + blend_points[p_point].node->disconnect("tree_changed",this,"_tree_changed"); } blend_points[p_point].node = p_node; - blend_points[p_point].node->set_parent(this); - blend_points[p_point].node->set_tree(get_tree()); + blend_points[p_point].node->connect("tree_changed",this,"_tree_changed",varray(),CONNECT_REFERENCE_COUNTED); + + emit_signal("tree_changed"); + } float AnimationNodeBlendSpace1D::get_blend_point_position(int p_point) const { @@ -114,14 +131,16 @@ Ref<AnimationRootNode> AnimationNodeBlendSpace1D::get_blend_point_node(int p_poi 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_tree(NULL); + blend_points[p_point].node->disconnect("tree_changed",this,"_tree_changed"); for (int i = p_point; i < blend_points_used - 1; i++) { blend_points[i] = blend_points[i + 1]; } + blend_points_used--; + emit_signal("tree_changed"); + } int AnimationNodeBlendSpace1D::get_blend_point_count() const { @@ -161,13 +180,6 @@ 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; @@ -191,11 +203,14 @@ float AnimationNodeBlendSpace1D::process(float p_time, bool p_seek) { 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); + return blend_node(blend_points[0].name,blend_points[0].node, p_time, p_seek, 1.0, FILTER_IGNORE, false); } + float blend_pos = get_parameter(blend_position); + float weights[MAX_BLEND_POINTS] = {}; int point_lower = -1; @@ -262,7 +277,7 @@ float AnimationNodeBlendSpace1D::process(float p_time, bool p_seek) { float max_time_remaining = 0.0; for (int i = 0; i < blend_points_used; i++) { - float remaining = blend_node(blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false); + float remaining = blend_node(blend_points[i].name,blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false); max_time_remaining = MAX(max_time_remaining, remaining); } @@ -276,18 +291,19 @@ String AnimationNodeBlendSpace1D::get_caption() const { AnimationNodeBlendSpace1D::AnimationNodeBlendSpace1D() { + for(int i=0;i<MAX_BLEND_POINTS;i++) { + blend_points[i].name=itos(i); + } blend_points_used = 0; max_space = 1; min_space = -1; snap = 0.1; value_label = "value"; + + blend_position="blend_position"; } AnimationNodeBlendSpace1D::~AnimationNodeBlendSpace1D() { - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_parent(this); - blend_points[i].node->set_tree(get_tree()); - } } diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index d1ed4c6a1f..b47accf973 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -11,6 +11,7 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { }; struct BlendPoint { + StringName name; Ref<AnimationRootNode> node; float position; }; @@ -18,8 +19,6 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { BlendPoint blend_points[MAX_BLEND_POINTS]; int blend_points_used; - float blend_pos; - float max_space; float min_space; @@ -29,12 +28,21 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node); + void _tree_changed(); + + StringName blend_position; + protected: virtual void _validate_property(PropertyInfo &property) const; static void _bind_methods(); public: - virtual void set_tree(AnimationTree *p_player); + + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); + void add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index = -1); void set_blend_point_position(int p_point, float p_position); @@ -54,15 +62,14 @@ public: 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; + Ref<AnimationNode> get_child_by_name(const StringName &p_name); + AnimationNodeBlendSpace1D(); ~AnimationNodeBlendSpace1D(); }; diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 3c93a0c8ec..bce1477d4e 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -1,18 +1,27 @@ #include "animation_blend_space_2d.h" #include "math/delaunay.h" -void AnimationNodeBlendSpace2D::set_tree(AnimationTree *p_player) { - AnimationRootNode::set_tree(p_player); - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_tree(p_player); +void AnimationNodeBlendSpace2D::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::VECTOR2,blend_position)); +} +Variant AnimationNodeBlendSpace2D::get_parameter_default_value(const StringName &p_parameter) const { + return Vector2(); +} + + +void AnimationNodeBlendSpace2D::get_child_nodes(List<ChildNode> *r_child_nodes) { + for(int i=0;i<blend_points_used;i++) { + ChildNode cn; + cn.name=itos(i); + cn.node=blend_points[i].node; + r_child_nodes->push_back(cn); } } void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &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) { @@ -32,13 +41,15 @@ void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_ 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_tree(get_tree()); + blend_points[p_at_index].node->connect("tree_changed",this,"_tree_changed",varray(),CONNECT_REFERENCE_COUNTED); blend_points_used++; + if (auto_triangles) { trianges_dirty = true; } + emit_signal("tree_changed"); + } void AnimationNodeBlendSpace2D::set_blend_point_position(int p_point, const Vector2 &p_position) { @@ -53,12 +64,13 @@ void AnimationNodeBlendSpace2D::set_blend_point_node(int p_point, const Ref<Anim 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_tree(NULL); + blend_points[p_point].node->disconnect("tree_changed",this,"_tree_changed"); } blend_points[p_point].node = p_node; - blend_points[p_point].node->set_parent(this); - blend_points[p_point].node->set_tree(get_tree()); + blend_points[p_point].node->connect("tree_changed",this,"_tree_changed",varray(),CONNECT_REFERENCE_COUNTED); + + emit_signal("tree_changed"); + } Vector2 AnimationNodeBlendSpace2D::get_blend_point_position(int p_point) const { ERR_FAIL_INDEX_V(p_point, blend_points_used, Vector2()); @@ -71,8 +83,7 @@ Ref<AnimationRootNode> AnimationNodeBlendSpace2D::get_blend_point_node(int p_poi 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_tree(NULL); + blend_points[p_point].node->disconnect("tree_changed",this,"_tree_changed"); for (int i = 0; i < triangles.size(); i++) { bool erase = false; @@ -95,6 +106,8 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) { blend_points[i] = blend_points[i + 1]; } blend_points_used--; + emit_signal("tree_changed"); + } int AnimationNodeBlendSpace2D::get_blend_point_count() const { @@ -217,13 +230,6 @@ 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; } @@ -381,6 +387,8 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { _update_triangles(); + Vector2 blend_pos = get_parameter(blend_position); + if (triangles.size() == 0) return 0; @@ -443,7 +451,7 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { 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); + float t = blend_node(blend_points[i].name,blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false); if (first || t < mind) { mind = t; first = false; @@ -455,7 +463,7 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { if (!found) { //ignore - blend_node(blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false); + blend_node(blend_points[i].name,blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false); } } return mind; @@ -483,10 +491,19 @@ void AnimationNodeBlendSpace2D::set_auto_triangles(bool p_enable) { } } + bool AnimationNodeBlendSpace2D::get_auto_triangles() const { return auto_triangles; } +Ref<AnimationNode> AnimationNodeBlendSpace2D::get_child_by_name(const StringName &p_name) { + return get_blend_point_node(p_name.operator String().to_int()); +} + +void AnimationNodeBlendSpace2D::_tree_changed() { + emit_signal("tree_changed"); +} + void AnimationNodeBlendSpace2D::_bind_methods() { ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1)); @@ -511,9 +528,6 @@ void AnimationNodeBlendSpace2D::_bind_methods() { 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); @@ -528,10 +542,12 @@ void AnimationNodeBlendSpace2D::_bind_methods() { 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); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeBlendSpace2D::_tree_changed); + 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::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_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); } @@ -540,13 +556,15 @@ void AnimationNodeBlendSpace2D::_bind_methods() { 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() { + for(int i=0;i<MAX_BLEND_POINTS;i++) { + blend_points[i].name=itos(i); + } auto_triangles = true; blend_points_used = 0; max_space = Vector2(1, 1); @@ -555,12 +573,10 @@ AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() { x_label = "x"; y_label = "y"; trianges_dirty = false; + blend_position = "blend_position"; } AnimationNodeBlendSpace2D::~AnimationNodeBlendSpace2D() { - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_parent(this); - blend_points[i].node->set_tree(get_tree()); - } + } diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h index 74d20b6013..342909531b 100644 --- a/scene/animation/animation_blend_space_2d.h +++ b/scene/animation/animation_blend_space_2d.h @@ -11,6 +11,7 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode { }; struct BlendPoint { + StringName name; Ref<AnimationRootNode> node; Vector2 position; }; @@ -24,7 +25,7 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode { Vector<BlendTriangle> triangles; - Vector2 blend_pos; + StringName blend_position; Vector2 max_space; Vector2 min_space; Vector2 snap; @@ -42,12 +43,19 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode { void _update_triangles(); + void _tree_changed(); + + protected: virtual void _validate_property(PropertyInfo &property) const; static void _bind_methods(); public: - virtual void set_tree(AnimationTree *p_player); + + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); void add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index = -1); void set_blend_point_position(int p_point, const Vector2 &p_position); @@ -72,9 +80,6 @@ public: 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; @@ -89,6 +94,8 @@ public: void set_auto_triangles(bool p_enable); bool get_auto_triangles() const; + virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name); + AnimationNodeBlendSpace2D(); ~AnimationNodeBlendSpace2D(); }; diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 65904410d3..31ee31745a 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -3,6 +3,7 @@ void AnimationNodeAnimation::set_animation(const StringName &p_name) { animation = p_name; + _change_notify("animation"); } StringName AnimationNodeAnimation::get_animation() const { @@ -13,43 +14,36 @@ float AnimationNodeAnimation::get_playback_time() const { return time; } +Vector<String> (*AnimationNodeAnimation::get_editable_animation_list)() = NULL; + void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const { - if (property.name == "animation") { - AnimationTree *gp = get_tree(); - if (gp && gp->has_node(gp->get_animation_player())) { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); - if (ap) { - List<StringName> names; - ap->get_animation_list(&names); - String anims; - for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - if (E != names.front()) { - anims += ","; - } - anims += String(E->get()); - } - if (anims != String()) { - property.hint = PROPERTY_HINT_ENUM; - property.hint_string = anims; - } + if (property.name == "animation" && get_editable_animation_list) { + Vector<String> names = get_editable_animation_list(); + String anims; + for (int i = 0; i < names.size(); i++) { + + if (i > 0) { + anims += ","; } + anims += String(names[i]); + } + if (anims != String()) { + property.hint = PROPERTY_HINT_ENUM; + property.hint_string = anims; } } - - AnimationRootNode::_validate_property(property); } float AnimationNodeAnimation::process(float p_time, bool p_seek) { - AnimationPlayer *ap = get_player(); + AnimationPlayer *ap = state->player; ERR_FAIL_COND_V(!ap, 0); - Ref<Animation> anim = ap->get_animation(animation); - if (!anim.is_valid()) { + if (!ap->has_animation(animation)) { - Ref<AnimationNodeBlendTree> tree = get_parent(); - if (tree.is_valid()) { + AnimationNodeBlendTree *tree = Object::cast_to<AnimationNodeBlendTree>(parent); + if (tree) { String name = tree->get_node_name(Ref<AnimationNodeAnimation>(this)); make_invalid(vformat(RTR("On BlendTree node '%s', animation not found: '%s'"), name, animation)); @@ -60,6 +54,8 @@ float AnimationNodeAnimation::process(float p_time, bool p_seek) { return 0; } + Ref<Animation> anim = ap->get_animation(animation); + if (p_seek) { time = p_time; step = 0; @@ -108,6 +104,20 @@ AnimationNodeAnimation::AnimationNodeAnimation() { //////////////////////////////////////////////////////// +void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::BOOL, active)); + r_list->push_back(PropertyInfo(Variant::BOOL, prev_active, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::REAL, time, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::REAL, remaining, PROPERTY_HINT_NONE, "", 0)); +} +Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_parameter) const { + if (p_parameter == active || p_parameter == prev_active) { + return false; + } else { + return 0.0; + } +} + void AnimationNodeOneShot::set_fadein_time(float p_time) { fade_in = p_time; @@ -162,18 +172,6 @@ AnimationNodeOneShot::MixMode AnimationNodeOneShot::get_mix_mode() const { return mix; } -void AnimationNodeOneShot::start() { - active = true; - do_start = true; -} -void AnimationNodeOneShot::stop() { - active = false; -} -bool AnimationNodeOneShot::is_active() const { - - return active; -} - String AnimationNodeOneShot::get_caption() const { return "OneShot"; } @@ -184,8 +182,16 @@ bool AnimationNodeOneShot::has_filter() const { float AnimationNodeOneShot::process(float p_time, bool p_seek) { + bool active = get_parameter(this->active); + bool prev_active = get_parameter(this->prev_active); + float time = get_parameter(this->time); + float remaining = get_parameter(this->remaining); + if (!active) { //make it as if this node doesn't exist, pass input 0 by. + if (prev_active) { + set_parameter(this->prev_active, false); + } return blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); } @@ -193,9 +199,12 @@ float AnimationNodeOneShot::process(float p_time, bool p_seek) { if (p_seek) time = p_time; + bool do_start = !prev_active; + if (do_start) { time = 0; os_seek = true; + set_parameter(this->prev_active, true); } float blend; @@ -233,10 +242,15 @@ float AnimationNodeOneShot::process(float p_time, bool p_seek) { if (!p_seek) { time += p_time; remaining = os_rem; - if (remaining <= 0) - active = false; + if (remaining <= 0) { + set_parameter(this->active, false); + set_parameter(this->prev_active, false); + } } + set_parameter(this->time, time); + set_parameter(this->remaining, remaining); + return MAX(main_rem, remaining); } void AnimationNodeOneShot::set_use_sync(bool p_sync) { @@ -269,10 +283,6 @@ void AnimationNodeOneShot::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mix_mode", "mode"), &AnimationNodeOneShot::set_mix_mode); ClassDB::bind_method(D_METHOD("get_mix_mode"), &AnimationNodeOneShot::get_mix_mode); - ClassDB::bind_method(D_METHOD("start"), &AnimationNodeOneShot::start); - ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeOneShot::stop); - ClassDB::bind_method(D_METHOD("is_active"), &AnimationNodeOneShot::is_active); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeOneShot::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeOneShot::is_using_sync); @@ -297,26 +307,22 @@ AnimationNodeOneShot::AnimationNodeOneShot() { add_input("in"); add_input("shot"); - time = 0; fade_in = 0.1; fade_out = 0.1; autorestart = false; autorestart_delay = 1; - autorestart_remaining = 0; + mix = MIX_MODE_BLEND; - active = false; - do_start = false; sync = false; } //////////////////////////////////////////////// -void AnimationNodeAdd2::set_amount(float p_amount) { - amount = p_amount; +void AnimationNodeAdd2::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, add_amount, PROPERTY_HINT_RANGE, "0,1,0.01")); } - -float AnimationNodeAdd2::get_amount() const { - return amount; +Variant AnimationNodeAdd2::get_parameter_default_value(const StringName &p_parameter) const { + return 0; } String AnimationNodeAdd2::get_caption() const { @@ -339,6 +345,7 @@ bool AnimationNodeAdd2::has_filter() const { float AnimationNodeAdd2::process(float p_time, bool p_seek) { + float amount = get_parameter(add_amount); float rem0 = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync); @@ -347,32 +354,27 @@ float AnimationNodeAdd2::process(float p_time, bool p_seek) { void AnimationNodeAdd2::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeAdd2::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeAdd2::get_amount); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd2::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd2::is_using_sync); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeAdd2::AnimationNodeAdd2() { + add_amount = "add_amount"; add_input("in"); add_input("add"); - amount = 0; sync = false; } //////////////////////////////////////////////// -void AnimationNodeAdd3::set_amount(float p_amount) { - amount = p_amount; +void AnimationNodeAdd3::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, add_amount, PROPERTY_HINT_RANGE, "-1,1,0.01")); } - -float AnimationNodeAdd3::get_amount() const { - return amount; +Variant AnimationNodeAdd3::get_parameter_default_value(const StringName &p_parameter) const { + return 0; } String AnimationNodeAdd3::get_caption() const { @@ -395,6 +397,7 @@ bool AnimationNodeAdd3::has_filter() const { float AnimationNodeAdd3::process(float p_time, bool p_seek) { + float amount = get_parameter(add_amount); blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_PASS, !sync); float rem0 = blend_input(1, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_PASS, !sync); @@ -404,39 +407,37 @@ float AnimationNodeAdd3::process(float p_time, bool p_seek) { void AnimationNodeAdd3::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeAdd3::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeAdd3::get_amount); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd3::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd3::is_using_sync); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeAdd3::AnimationNodeAdd3() { + add_amount = "add_amount"; add_input("-add"); add_input("in"); add_input("+add"); - amount = 0; sync = false; } ///////////////////////////////////////////// -void AnimationNodeBlend2::set_amount(float p_amount) { - amount = p_amount; +void AnimationNodeBlend2::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, blend_amount, PROPERTY_HINT_RANGE, "0,1,0.01")); } - -float AnimationNodeBlend2::get_amount() const { - return amount; +Variant AnimationNodeBlend2::get_parameter_default_value(const StringName &p_parameter) const { + return 0; //for blend amount } + String AnimationNodeBlend2::get_caption() const { return "Blend2"; } float AnimationNodeBlend2::process(float p_time, bool p_seek) { + float amount = get_parameter(blend_amount); + float rem0 = blend_input(0, p_time, p_seek, 1.0 - amount, FILTER_BLEND, !sync); float rem1 = blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync); @@ -459,31 +460,25 @@ bool AnimationNodeBlend2::has_filter() const { } void AnimationNodeBlend2::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeBlend2::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeBlend2::get_amount); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlend2::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlend2::is_using_sync); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeBlend2::AnimationNodeBlend2() { + blend_amount = "blend_amount"; add_input("in"); add_input("blend"); sync = false; - - amount = 0; } ////////////////////////////////////// -void AnimationNodeBlend3::set_amount(float p_amount) { - amount = p_amount; +void AnimationNodeBlend3::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, blend_amount, PROPERTY_HINT_RANGE, "-1,1,0.01")); } - -float AnimationNodeBlend3::get_amount() const { - return amount; +Variant AnimationNodeBlend3::get_parameter_default_value(const StringName &p_parameter) const { + return 0; //for blend amount } String AnimationNodeBlend3::get_caption() const { @@ -502,6 +497,7 @@ bool AnimationNodeBlend3::is_using_sync() const { float AnimationNodeBlend3::process(float p_time, bool p_seek) { + float amount = get_parameter(blend_amount); float rem0 = blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_IGNORE, !sync); float rem1 = blend_input(1, p_time, p_seek, 1.0 - ABS(amount), FILTER_IGNORE, !sync); float rem2 = blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_IGNORE, !sync); @@ -511,31 +507,26 @@ float AnimationNodeBlend3::process(float p_time, bool p_seek) { void AnimationNodeBlend3::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeBlend3::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeBlend3::get_amount); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlend3::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlend3::is_using_sync); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeBlend3::AnimationNodeBlend3() { + blend_amount = "blend_amount"; add_input("-blend"); add_input("in"); add_input("+blend"); sync = false; - amount = 0; } ///////////////////////////////// -void AnimationNodeTimeScale::set_scale(float p_scale) { - scale = p_scale; +void AnimationNodeTimeScale::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, scale, PROPERTY_HINT_RANGE, "0,32,0.01,or_greater")); } - -float AnimationNodeTimeScale::get_scale() const { - return scale; +Variant AnimationNodeTimeScale::get_parameter_default_value(const StringName &p_parameter) const { + return 1.0; //initial timescale } String AnimationNodeTimeScale::get_caption() const { @@ -544,6 +535,7 @@ String AnimationNodeTimeScale::get_caption() const { float AnimationNodeTimeScale::process(float p_time, bool p_seek) { + float scale = get_parameter(this->scale); if (p_seek) { return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false); } else { @@ -552,25 +544,19 @@ float AnimationNodeTimeScale::process(float p_time, bool p_seek) { } void AnimationNodeTimeScale::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_scale", "scale"), &AnimationNodeTimeScale::set_scale); - ClassDB::bind_method(D_METHOD("get_scale"), &AnimationNodeTimeScale::get_scale); - - ADD_PROPERTY(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,32,0.01,or_greater"), "set_scale", "get_scale"); } AnimationNodeTimeScale::AnimationNodeTimeScale() { + scale = "scale"; add_input("in"); - scale = 1.0; } //////////////////////////////////// -void AnimationNodeTimeSeek::set_seek_pos(float p_seek_pos) { - seek_pos = p_seek_pos; +void AnimationNodeTimeSeek::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, seek_pos, PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater")); } - -float AnimationNodeTimeSeek::get_seek_pos() const { - return seek_pos; +Variant AnimationNodeTimeSeek::get_parameter_default_value(const StringName &p_parameter) const { + return 1.0; //initial timescale } String AnimationNodeTimeSeek::get_caption() const { @@ -579,11 +565,12 @@ String AnimationNodeTimeSeek::get_caption() const { float AnimationNodeTimeSeek::process(float p_time, bool p_seek) { + float seek_pos = get_parameter(this->seek_pos); if (p_seek) { return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false); } else if (seek_pos >= 0) { float ret = blend_input(0, seek_pos, true, 1.0, FILTER_IGNORE, false); - seek_pos = -1; + set_parameter(this->seek_pos, -1.0); //reset _change_notify("seek_pos"); return ret; } else { @@ -592,19 +579,41 @@ float AnimationNodeTimeSeek::process(float p_time, bool p_seek) { } void AnimationNodeTimeSeek::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_seek_pos", "seek_pos"), &AnimationNodeTimeSeek::set_seek_pos); - ClassDB::bind_method(D_METHOD("get_seek_pos"), &AnimationNodeTimeSeek::get_seek_pos); - - ADD_PROPERTY(PropertyInfo(Variant::REAL, "seek_pos", PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater"), "set_seek_pos", "get_seek_pos"); } + AnimationNodeTimeSeek::AnimationNodeTimeSeek() { add_input("in"); - seek_pos = -1; + seek_pos = "seek_position"; } ///////////////////////////////////////////////// +void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) const { + + String anims; + for (int i = 0; i < enabled_inputs; i++) { + if (i > 0) { + anims += ","; + } + anims += inputs[i].name; + } + + r_list->push_back(PropertyInfo(Variant::INT, current, PROPERTY_HINT_ENUM, anims)); + r_list->push_back(PropertyInfo(Variant::INT, prev_current, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::INT, prev, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::REAL, time, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::REAL, prev_xfading, PROPERTY_HINT_NONE, "", 0)); +} +Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const { + if (p_parameter == time || p_parameter == prev_xfading) { + return 0.0; + } else if (p_parameter == prev || p_parameter == prev_current) { + return -1; + } else { + return 0; + } +} + String AnimationNodeTransition::get_caption() const { return "Transition"; } @@ -650,18 +659,12 @@ String AnimationNodeTransition::get_input_caption(int p_input) const { return inputs[p_input].name; } -void AnimationNodeTransition::set_current(int p_current) { - - if (current == p_current) - return; - ERR_FAIL_INDEX(p_current, enabled_inputs); - +#if 0 Ref<AnimationNodeBlendTree> tree = get_parent(); if (tree.is_valid() && current >= 0) { prev = current; - prev_xfading = xfade; - prev_time = time; + prev_xfading = xfade; time = 0; current = p_current; switched = true; @@ -669,11 +672,8 @@ void AnimationNodeTransition::set_current(int p_current) { } else { current = p_current; } -} +#endif -int AnimationNodeTransition::get_current() const { - return current; -} void AnimationNodeTransition::set_cross_fade_time(float p_fade) { xfade = p_fade; } @@ -684,9 +684,34 @@ float AnimationNodeTransition::get_cross_fade_time() const { float AnimationNodeTransition::process(float p_time, bool p_seek) { + int current = get_parameter(this->current); + int prev = get_parameter(this->prev); + int prev_current = get_parameter(this->prev_current); + + float time = get_parameter(this->time); + float prev_xfading = get_parameter(this->prev_xfading); + + bool switched = current != prev_current; + + if (switched) { + set_parameter(this->prev_current, current); + set_parameter(this->prev, prev_current); + + prev = prev_current; + prev_xfading = xfade; + time = 0; + switched = true; + } + + if (current < 0 || current >= enabled_inputs || prev >= enabled_inputs) { + return 0; + } + + float rem = 0; + if (prev < 0) { // process current animation, check for transition - float rem = blend_input(current, p_time, p_seek, 1.0, FILTER_IGNORE, false); + rem = blend_input(current, p_time, p_seek, 1.0, FILTER_IGNORE, false); if (p_seek) time = p_time; @@ -695,16 +720,13 @@ float AnimationNodeTransition::process(float p_time, bool p_seek) { if (inputs[current].auto_advance && rem <= xfade) { - set_current((current + 1) % enabled_inputs); + set_parameter(this->current, (current + 1) % enabled_inputs); } - return rem; } else { // cross-fading from prev to current float blend = xfade ? (prev_xfading / xfade) : 1; - float rem; - if (!p_seek && switched) { //just switched, seek to start of current rem = blend_input(current, 0, true, 1.0 - blend, FILTER_IGNORE, false); @@ -723,28 +745,19 @@ float AnimationNodeTransition::process(float p_time, bool p_seek) { time += p_time; prev_xfading -= p_time; if (prev_xfading < 0) { - prev = -1; + set_parameter(this->prev, -1); } } - - return rem; } + + set_parameter(this->time, time); + set_parameter(this->prev_xfading, prev_xfading); + + return rem; } void AnimationNodeTransition::_validate_property(PropertyInfo &property) const { - if (property.name == "current" && enabled_inputs > 0) { - property.hint = PROPERTY_HINT_ENUM; - String anims; - for (int i = 0; i < enabled_inputs; i++) { - if (i > 0) { - anims += ","; - } - anims += inputs[i].name; - } - property.hint_string = anims; - } - if (property.name.begins_with("input_")) { String n = property.name.get_slicec('/', 0).get_slicec('_', 1); if (n != "count") { @@ -769,14 +782,10 @@ void AnimationNodeTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_input_caption", "input", "caption"), &AnimationNodeTransition::set_input_caption); ClassDB::bind_method(D_METHOD("get_input_caption", "input"), &AnimationNodeTransition::get_input_caption); - ClassDB::bind_method(D_METHOD("set_current", "index"), &AnimationNodeTransition::set_current); - ClassDB::bind_method(D_METHOD("get_current"), &AnimationNodeTransition::get_current); - ClassDB::bind_method(D_METHOD("set_cross_fade_time", "time"), &AnimationNodeTransition::set_cross_fade_time); ClassDB::bind_method(D_METHOD("get_cross_fade_time"), &AnimationNodeTransition::get_cross_fade_time); ADD_PROPERTY(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,64,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "current", PROPERTY_HINT_RANGE, "0,64,1"), "set_current", "get_current"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01"), "set_cross_fade_time", "get_cross_fade_time"); for (int i = 0; i < MAX_INPUTS; i++) { @@ -786,13 +795,15 @@ void AnimationNodeTransition::_bind_methods() { } AnimationNodeTransition::AnimationNodeTransition() { + + prev_xfading = "prev_xfading"; + prev = "prev"; + time = "time"; + current = "current"; + prev_current = "prev_current"; + ; + enabled_inputs = 0; - xfade = 0; - current = -1; - prev = -1; - prev_time = 0; - prev_xfading = 0; - switched = false; for (int i = 0; i < MAX_INPUTS; i++) { inputs[i].auto_advance = false; inputs[i].name = itos(i + 1); @@ -814,69 +825,102 @@ AnimationNodeOutput::AnimationNodeOutput() { } /////////////////////////////////////////////////////// -void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNode> p_node) { +void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position) { ERR_FAIL_COND(nodes.has(p_name)); ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(p_node->get_parent().is_valid()); - ERR_FAIL_COND(p_node->get_tree() != NULL); ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output); ERR_FAIL_COND(String(p_name).find("/") != -1); - nodes[p_name] = p_node; - p_node->set_parent(this); - p_node->set_tree(get_tree()); + Node n; + n.node = p_node; + n.position = p_position; + n.connections.resize(n.node->get_input_count()); + nodes[p_name] = n; emit_changed(); + emit_signal("tree_changed"); + + p_node->connect("tree_changed", this, "_tree_changed", varray(), CONNECT_REFERENCE_COUNTED); + p_node->connect("changed", this, "_node_changed", varray(p_name), CONNECT_REFERENCE_COUNTED); } Ref<AnimationNode> AnimationNodeBlendTree::get_node(const StringName &p_name) const { ERR_FAIL_COND_V(!nodes.has(p_name), Ref<AnimationNode>()); - return nodes[p_name]; + return nodes[p_name].node; } StringName AnimationNodeBlendTree::get_node_name(const Ref<AnimationNode> &p_node) const { - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - if (E->get() == p_node) { + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + if (E->get().node == p_node) { return E->key(); } } ERR_FAIL_V(StringName()); } + +void AnimationNodeBlendTree::set_node_position(const StringName &p_node, const Vector2 &p_position) { + ERR_FAIL_COND(!nodes.has(p_node)); + nodes[p_node].position = p_position; +} + +Vector2 AnimationNodeBlendTree::get_node_position(const StringName &p_node) const { + ERR_FAIL_COND_V(!nodes.has(p_node), Vector2()); + return nodes[p_node].position; +} + +void AnimationNodeBlendTree::get_child_nodes(List<ChildNode> *r_child_nodes) { + Vector<StringName> ns; + + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + ns.push_back(E->key()); + } + + ns.sort_custom<StringName::AlphCompare>(); + + for (int i = 0; i < ns.size(); i++) { + ChildNode cn; + cn.name = ns[i]; + cn.node = nodes[cn.name].node; + r_child_nodes->push_back(cn); + } +} + bool AnimationNodeBlendTree::has_node(const StringName &p_name) const { return nodes.has(p_name); } +Vector<StringName> AnimationNodeBlendTree::get_node_connection_array(const StringName &p_name) const { + + ERR_FAIL_COND_V(!nodes.has(p_name), Vector<StringName>()); + return nodes[p_name].connections; +} void AnimationNodeBlendTree::remove_node(const StringName &p_name) { ERR_FAIL_COND(!nodes.has(p_name)); ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output); //can't delete output { - //erase node connections - Ref<AnimationNode> node = nodes[p_name]; - for (int i = 0; i < node->get_input_count(); i++) { - node->set_input_connection(i, StringName()); - } - node->set_parent(NULL); - node->set_tree(NULL); + Ref<AnimationNode> node = nodes[p_name].node; + node->disconnect("tree_changed", this, "_tree_changed"); + node->disconnect("changed", this, "_node_changed"); } nodes.erase(p_name); //erase connections to name - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - if (node->get_input_connection(i) == p_name) { - node->set_input_connection(i, StringName()); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().connections.size(); i++) { + if (E->get().connections[i] == p_name) { + E->get().connections.write[i] = StringName(); } } } emit_changed(); + emit_signal("tree_changed"); } void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringName &p_new_name) { @@ -886,18 +930,24 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output); ERR_FAIL_COND(p_new_name == SceneStringNames::get_singleton()->output); + nodes[p_name].node->disconnect("changed", this, "_node_changed"); + nodes[p_new_name] = nodes[p_name]; nodes.erase(p_name); //rename connections - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - if (node->get_input_connection(i) == p_name) { - node->set_input_connection(i, p_new_name); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + + for (int i = 0; i < E->get().connections.size(); i++) { + if (E->get().connections[i] == p_name) { + E->get().connections.write[i] = p_new_name; } } } + //connection must be done with new name + nodes[p_new_name].node->connect("changed", this, "_node_changed", varray(p_new_name), CONNECT_REFERENCE_COUNTED); + + emit_signal("tree_changed"); } void AnimationNodeBlendTree::connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node) { @@ -907,18 +957,18 @@ void AnimationNodeBlendTree::connect_node(const StringName &p_input_node, int p_ ERR_FAIL_COND(p_output_node == SceneStringNames::get_singleton()->output); ERR_FAIL_COND(p_input_node == p_output_node); - Ref<AnimationNode> input = nodes[p_input_node]; - ERR_FAIL_INDEX(p_input_index, input->get_input_count()); + Ref<AnimationNode> input = nodes[p_input_node].node; + ERR_FAIL_INDEX(p_input_index, nodes[p_input_node].connections.size()); - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - StringName output = node->get_input_connection(i); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().connections.size(); i++) { + StringName output = E->get().connections[i]; ERR_FAIL_COND(output == p_output_node); } } - input->set_input_connection(p_input_index, p_output_node); + nodes[p_input_node].connections.write[p_input_index] = p_output_node; + emit_changed(); } @@ -926,20 +976,21 @@ void AnimationNodeBlendTree::disconnect_node(const StringName &p_node, int p_inp ERR_FAIL_COND(!nodes.has(p_node)); - Ref<AnimationNode> input = nodes[p_node]; - ERR_FAIL_INDEX(p_input_index, input->get_input_count()); + Ref<AnimationNode> input = nodes[p_node].node; + ERR_FAIL_INDEX(p_input_index, nodes[p_node].connections.size()); - input->set_input_connection(p_input_index, StringName()); + nodes[p_node].connections.write[p_input_index] = StringName(); } float AnimationNodeBlendTree::get_connection_activity(const StringName &p_input_node, int p_input_index) const { ERR_FAIL_COND_V(!nodes.has(p_input_node), 0); - Ref<AnimationNode> input = nodes[p_input_node]; - ERR_FAIL_INDEX_V(p_input_index, input->get_input_count(), 0); + Ref<AnimationNode> input = nodes[p_input_node].node; + ERR_FAIL_INDEX_V(p_input_index, nodes[p_input_node].connections.size(), 0); - return input->get_input_activity(p_input_index); + //return input->get_input_activity(p_input_index); + return 0; } AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node) const { @@ -956,20 +1007,19 @@ AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node return CONNECTION_ERROR_SAME_NODE; } - Ref<AnimationNode> input = nodes[p_input_node]; + Ref<AnimationNode> input = nodes[p_input_node].node; - if (p_input_index < 0 || p_input_index >= input->get_input_count()) { + if (p_input_index < 0 || p_input_index >= nodes[p_input_node].connections.size()) { return CONNECTION_ERROR_NO_INPUT_INDEX; } - if (input->get_input_connection(p_input_index) != StringName()) { + if (nodes[p_input_node].connections[p_input_index] != StringName()) { return CONNECTION_ERROR_CONNECTION_EXISTS; } - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - StringName output = node->get_input_connection(i); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().connections.size(); i++) { + StringName output = E->get().connections[i]; if (output == p_output_node) { return CONNECTION_ERROR_CONNECTION_EXISTS; } @@ -980,10 +1030,9 @@ AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node void AnimationNodeBlendTree::get_node_connections(List<NodeConnection> *r_connections) const { - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - StringName output = node->get_input_connection(i); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().connections.size(); i++) { + StringName output = E->get().connections[i]; if (output != StringName()) { NodeConnection nc; nc.input_node = E->key(); @@ -1001,13 +1050,13 @@ String AnimationNodeBlendTree::get_caption() const { float AnimationNodeBlendTree::process(float p_time, bool p_seek) { - Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output]; - return blend_node(output, p_time, p_seek, 1.0); + Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output].node; + return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, 1.0); } void AnimationNodeBlendTree::get_node_list(List<StringName> *r_list) { - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { r_list->push_back(E->key()); } } @@ -1022,14 +1071,8 @@ Vector2 AnimationNodeBlendTree::get_graph_offset() const { return graph_offset; } -void AnimationNodeBlendTree::set_tree(AnimationTree *p_player) { - - AnimationNode::set_tree(p_player); - - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - node->set_tree(p_player); - } +Ref<AnimationNode> AnimationNodeBlendTree::get_child_by_name(const StringName &p_name) { + return get_node(p_name); } bool AnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_value) { @@ -1051,7 +1094,7 @@ bool AnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_val if (what == "position") { if (nodes.has(node_name)) { - nodes[node_name]->set_position(p_value); + nodes[node_name].position = p_value; } return true; } @@ -1078,7 +1121,7 @@ bool AnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_ret) cons if (what == "node") { if (nodes.has(node_name)) { - r_ret = nodes[node_name]; + r_ret = nodes[node_name].node; return true; } } @@ -1086,7 +1129,7 @@ bool AnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_ret) cons if (what == "position") { if (nodes.has(node_name)) { - r_ret = nodes[node_name]->get_position(); + r_ret = nodes[node_name].position; return true; } } @@ -1113,7 +1156,7 @@ bool AnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_ret) cons void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) const { List<StringName> names; - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { names.push_back(E->key()); } names.sort_custom<StringName::AlphCompare>(); @@ -1121,7 +1164,7 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons for (List<StringName>::Element *E = names.front(); E; E = E->next()) { String name = E->get(); if (name != "output") { - p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR)); } p_list->push_back(PropertyInfo(Variant::VECTOR2, "nodes/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } @@ -1129,9 +1172,19 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } +void AnimationNodeBlendTree::_tree_changed() { + emit_signal("tree_changed"); +} + +void AnimationNodeBlendTree::_node_changed(const StringName &p_node) { + + ERR_FAIL_COND(!nodes.has(p_node)); + nodes[p_node].connections.resize(nodes[p_node].node->get_input_count()); +} + void AnimationNodeBlendTree::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_node", "name", "node"), &AnimationNodeBlendTree::add_node); + ClassDB::bind_method(D_METHOD("add_node", "name", "node", "position"), &AnimationNodeBlendTree::add_node, DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("get_node", "name"), &AnimationNodeBlendTree::get_node); ClassDB::bind_method(D_METHOD("remove_node", "name"), &AnimationNodeBlendTree::remove_node); ClassDB::bind_method(D_METHOD("rename_node", "name", "new_name"), &AnimationNodeBlendTree::rename_node); @@ -1139,9 +1192,15 @@ void AnimationNodeBlendTree::_bind_methods() { ClassDB::bind_method(D_METHOD("connect_node", "input_node", "input_index", "output_node"), &AnimationNodeBlendTree::connect_node); ClassDB::bind_method(D_METHOD("disconnect_node", "input_node", "input_index"), &AnimationNodeBlendTree::disconnect_node); + ClassDB::bind_method(D_METHOD("set_node_position", "name", "position"), &AnimationNodeBlendTree::set_node_position); + ClassDB::bind_method(D_METHOD("get_node_position", "name"), &AnimationNodeBlendTree::get_node_position); + ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeBlendTree::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeBlendTree::get_graph_offset); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeBlendTree::_tree_changed); + ClassDB::bind_method(D_METHOD("_node_changed", "node"), &AnimationNodeBlendTree::_node_changed); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset"); BIND_CONSTANT(CONNECTION_OK); @@ -1156,15 +1215,12 @@ AnimationNodeBlendTree::AnimationNodeBlendTree() { Ref<AnimationNodeOutput> output; output.instance(); - output->set_position(Vector2(300, 150)); - output->set_parent(this); - nodes["output"] = output; + Node n; + n.node = output; + n.position = Vector2(300, 150); + n.connections.resize(1); + nodes["output"] = n; } AnimationNodeBlendTree::~AnimationNodeBlendTree() { - - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - E->get()->set_parent(NULL); - E->get()->set_tree(NULL); - } } diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index e86cc2e823..37bd45c74a 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -20,6 +20,8 @@ protected: static void _bind_methods(); public: + static Vector<String> (*get_editable_animation_list)(); + virtual String get_caption() const; virtual float process(float p_time, bool p_seek); @@ -41,8 +43,6 @@ public: }; private: - bool active; - bool do_start; float fade_in; float fade_out; @@ -51,15 +51,25 @@ private: float autorestart_random_delay; MixMode mix; - float time; - float remaining; - float autorestart_remaining; bool sync; + /* bool active; + bool do_start; + float time; + float remaining;*/ + + StringName active; + StringName prev_active; + StringName time; + StringName remaining; + protected: static void _bind_methods(); public: + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + virtual String get_caption() const; void set_fadein_time(float p_time); @@ -79,10 +89,6 @@ public: void set_mix_mode(MixMode p_mix); MixMode get_mix_mode() const; - void start(); - void stop(); - bool is_active() const; - void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -97,17 +103,17 @@ VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode) class AnimationNodeAdd2 : public AnimationNode { GDCLASS(AnimationNodeAdd2, AnimationNode); - float amount; + StringName add_amount; bool sync; protected: static void _bind_methods(); public: - virtual String get_caption() const; + void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_amount(float p_amount); - float get_amount() const; + virtual String get_caption() const; void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -121,17 +127,17 @@ public: class AnimationNodeAdd3 : public AnimationNode { GDCLASS(AnimationNodeAdd3, AnimationNode); - float amount; + StringName add_amount; bool sync; protected: static void _bind_methods(); public: - virtual String get_caption() const; + void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_amount(float p_amount); - float get_amount() const; + virtual String get_caption() const; void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -145,19 +151,19 @@ public: class AnimationNodeBlend2 : public AnimationNode { GDCLASS(AnimationNodeBlend2, AnimationNode); - float amount; + StringName blend_amount; bool sync; protected: static void _bind_methods(); public: + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + virtual String get_caption() const; virtual float process(float p_time, bool p_seek); - void set_amount(float p_amount); - float get_amount() const; - void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -168,17 +174,17 @@ public: class AnimationNodeBlend3 : public AnimationNode { GDCLASS(AnimationNodeBlend3, AnimationNode); - float amount; + StringName blend_amount; bool sync; protected: static void _bind_methods(); public: - virtual String get_caption() const; + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_amount(float p_amount); - float get_amount() const; + virtual String get_caption() const; void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -190,16 +196,16 @@ public: class AnimationNodeTimeScale : public AnimationNode { GDCLASS(AnimationNodeTimeScale, AnimationNode); - float scale; + StringName scale; protected: static void _bind_methods(); public: - virtual String get_caption() const; + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_scale(float p_scale); - float get_scale() const; + virtual String get_caption() const; float process(float p_time, bool p_seek); @@ -209,16 +215,16 @@ public: class AnimationNodeTimeSeek : public AnimationNode { GDCLASS(AnimationNodeTimeSeek, AnimationNode); - float seek_pos; + StringName seek_pos; protected: static void _bind_methods(); public: - virtual String get_caption() const; + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_seek_pos(float p_sec); - float get_seek_pos() const; + virtual String get_caption() const; float process(float p_time, bool p_seek); @@ -241,13 +247,18 @@ class AnimationNodeTransition : public AnimationNode { InputData inputs[MAX_INPUTS]; int enabled_inputs; - float prev_time; + /* float prev_xfading; int prev; - bool switched; - float time; int current; + int prev_current; */ + + StringName prev_xfading; + StringName prev; + StringName time; + StringName current; + StringName prev_current; float xfade; @@ -258,6 +269,9 @@ protected: void _validate_property(PropertyInfo &property) const; public: + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + virtual String get_caption() const; void set_enabled_inputs(int p_inputs); @@ -269,9 +283,6 @@ public: void set_input_caption(int p_input, const String &p_name); String get_input_caption(int p_input) const; - void set_current(int p_current); - int get_current() const; - void set_cross_fade_time(float p_fade); float get_cross_fade_time() const; @@ -293,10 +304,19 @@ public: class AnimationNodeBlendTree : public AnimationRootNode { GDCLASS(AnimationNodeBlendTree, AnimationRootNode) - Map<StringName, Ref<AnimationNode> > nodes; + struct Node { + Ref<AnimationNode> node; + Vector2 position; + Vector<StringName> connections; + }; + + Map<StringName, Node> nodes; Vector2 graph_offset; + void _tree_changed(); + void _node_changed(const StringName &p_node); + protected: static void _bind_methods(); bool _set(const StringName &p_name, const Variant &p_value); @@ -314,12 +334,18 @@ public: //no need to check for cycles due to tree topology }; - void add_node(const StringName &p_name, Ref<AnimationNode> p_node); + void add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position = Vector2()); Ref<AnimationNode> get_node(const StringName &p_name) const; void remove_node(const StringName &p_name); void rename_node(const StringName &p_name, const StringName &p_new_name); bool has_node(const StringName &p_name) const; StringName get_node_name(const Ref<AnimationNode> &p_node) const; + Vector<StringName> get_node_connection_array(const StringName &p_name) const; + + void set_node_position(const StringName &p_node, const Vector2 &p_position); + Vector2 get_node_position(const StringName &p_node) const; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); void connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node); void disconnect_node(const StringName &p_node, int p_input_index); @@ -342,7 +368,8 @@ public: void set_graph_offset(const Vector2 &p_graph_offset); Vector2 get_graph_offset() const; - virtual void set_tree(AnimationTree *p_player); + virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name); + AnimationNodeBlendTree(); ~AnimationNodeBlendTree(); }; diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index f478112a36..cbbed2efcc 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -20,6 +20,26 @@ bool AnimationNodeStateMachineTransition::has_auto_advance() const { return auto_advance; } +void AnimationNodeStateMachineTransition::set_advance_condition(const StringName &p_condition) { + String cs = p_condition; + ERR_FAIL_COND(cs.find("/") != -1 || cs.find(":") != -1); + advance_condition = p_condition; + if (cs != String()) { + advance_condition_name = "conditions/" + cs; + } else { + advance_condition_name = StringName(); + } + emit_signal("advance_condition_changed"); +} + +StringName AnimationNodeStateMachineTransition::get_advance_condition() const { + return advance_condition; +} + +StringName AnimationNodeStateMachineTransition::get_advance_condition_name() const { + return advance_condition_name; +} + void AnimationNodeStateMachineTransition::set_xfade_time(float p_xfade) { ERR_FAIL_COND(p_xfade < 0); @@ -56,6 +76,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_advance", "auto_advance"), &AnimationNodeStateMachineTransition::set_auto_advance); ClassDB::bind_method(D_METHOD("has_auto_advance"), &AnimationNodeStateMachineTransition::has_auto_advance); + ClassDB::bind_method(D_METHOD("set_advance_condition", "name"), &AnimationNodeStateMachineTransition::set_advance_condition); + ClassDB::bind_method(D_METHOD("get_advance_condition"), &AnimationNodeStateMachineTransition::get_advance_condition); + ClassDB::bind_method(D_METHOD("set_xfade_time", "secs"), &AnimationNodeStateMachineTransition::set_xfade_time); ClassDB::bind_method(D_METHOD("get_xfade_time"), &AnimationNodeStateMachineTransition::get_xfade_time); @@ -67,6 +90,7 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,AtEnd"), "set_switch_mode", "get_switch_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_advance"), "set_auto_advance", "has_auto_advance"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "advance_condition"), "set_advance_condition", "get_advance_condition"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01"), "set_xfade_time", "get_xfade_time"); ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); @@ -74,6 +98,8 @@ void AnimationNodeStateMachineTransition::_bind_methods() { BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE); BIND_ENUM_CONSTANT(SWITCH_MODE_SYNC); BIND_ENUM_CONSTANT(SWITCH_MODE_AT_END); + + ADD_SIGNAL(MethodInfo("advance_condition_changed")); } AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { @@ -85,277 +111,241 @@ AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { priority = 1; } -/////////////////////////////////////////////////////// -void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node) { - - ERR_FAIL_COND(states.has(p_name)); - ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(p_node->get_parent().is_valid()); - ERR_FAIL_COND(p_node->get_tree() != NULL); - ERR_FAIL_COND(String(p_name).find("/") != -1); - states[p_name] = p_node; +//////////////////////////////////////////////////////// - p_node->set_parent(this); - p_node->set_tree(get_tree()); +void AnimationNodeStateMachinePlayback::travel(const StringName &p_state) { - emit_changed(); + start_request_travel = true; + start_request = p_state; + stop_request = false; } -Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const { - - ERR_FAIL_COND_V(!states.has(p_name), Ref<AnimationNode>()); - - return states[p_name]; +void AnimationNodeStateMachinePlayback::start(const StringName &p_state) { + start_request_travel = false; + start_request = p_state; + stop_request = false; + print_line("wants start"); } +void AnimationNodeStateMachinePlayback::stop() { -StringName AnimationNodeStateMachine::get_node_name(const Ref<AnimationNode> &p_node) const { - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { - if (E->get() == p_node) { - return E->key(); - } - } - - ERR_FAIL_V(StringName()); + stop_request = true; } - -bool AnimationNodeStateMachine::has_node(const StringName &p_name) const { - return states.has(p_name); +bool AnimationNodeStateMachinePlayback::is_playing() const { + return playing; +} +StringName AnimationNodeStateMachinePlayback::get_current_node() const { + return current; +} +StringName AnimationNodeStateMachinePlayback::get_blend_from_node() const { + return fading_from; +} +Vector<StringName> AnimationNodeStateMachinePlayback::get_travel_path() const { + return path; +} +float AnimationNodeStateMachinePlayback::get_current_play_pos() const { + return pos_current; +} +float AnimationNodeStateMachinePlayback::get_current_length() const { + return len_current; } -void AnimationNodeStateMachine::remove_node(const StringName &p_name) { - - ERR_FAIL_COND(!states.has(p_name)); - - { - //erase node connections - Ref<AnimationNode> node = states[p_name]; - for (int i = 0; i < node->get_input_count(); i++) { - node->set_input_connection(i, StringName()); - } - node->set_parent(NULL); - node->set_tree(NULL); - } - states.erase(p_name); - path.erase(p_name); +bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *sm, const StringName &p_travel) { - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_name || transitions[i].to == p_name) { - transitions.remove(i); - i--; - } - } + ERR_FAIL_COND_V(!playing, false); + ERR_FAIL_COND_V(!sm->states.has(p_travel), false); + ERR_FAIL_COND_V(!sm->states.has(current), false); - if (start_node == p_name) { - start_node = StringName(); - } + path.clear(); //a new one will be needed - if (end_node == p_name) { - end_node = StringName(); - } + if (current == p_travel) + return true; //nothing to do - if (playing && current == p_name) { - stop(); - } - emit_changed(); -} + loops_current = 0; // reset loops, so fade does not happen immediately -void AnimationNodeStateMachine::rename_node(const StringName &p_name, const StringName &p_new_name) { + Vector2 current_pos = sm->states[current].position; + Vector2 target_pos = sm->states[p_travel].position; - ERR_FAIL_COND(!states.has(p_name)); - ERR_FAIL_COND(states.has(p_new_name)); + Map<StringName, AStarCost> cost_map; - states[p_new_name] = states[p_name]; - states.erase(p_name); + List<int> open_list; - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_name) { - transitions.write[i].from = p_new_name; - } + //build open list + for (int i = 0; i < sm->transitions.size(); i++) { + if (sm->transitions[i].from == current) { + open_list.push_back(i); + float cost = sm->states[sm->transitions[i].to].position.distance_to(current_pos); + cost *= sm->transitions[i].transition->get_priority(); + AStarCost ap; + ap.prev = current; + ap.distance = cost; + cost_map[sm->transitions[i].to] = ap; - if (transitions[i].to == p_name) { - transitions.write[i].to = p_new_name; + if (sm->transitions[i].to == p_travel) { //prematurely found it! :D + path.push_back(p_travel); + return true; + } } } - if (start_node == p_name) { - start_node = p_new_name; - } + //begin astar + bool found_route = false; + while (!found_route) { - if (end_node == p_name) { - end_node = p_new_name; - } + if (open_list.size() == 0) { + return false; //no path found + } - if (playing && current == p_name) { - current = p_new_name; - } + //find the last cost transition + List<int>::Element *least_cost_transition = NULL; + float least_cost = 1e20; - path.clear(); //clear path -} + for (List<int>::Element *E = open_list.front(); E; E = E->next()) { -void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { + float cost = cost_map[sm->transitions[E->get()].to].distance; + cost += sm->states[sm->transitions[E->get()].to].position.distance_to(target_pos); - List<StringName> nodes; - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { - nodes.push_back(E->key()); - } - nodes.sort_custom<StringName::AlphCompare>(); + if (cost < least_cost) { + least_cost_transition = E; + } + } - for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) { - r_nodes->push_back(E->get()); - } -} + StringName transition_prev = sm->transitions[least_cost_transition->get()].from; + StringName transition = sm->transitions[least_cost_transition->get()].to; -bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const StringName &p_to) const { + for (int i = 0; i < sm->transitions.size(); i++) { + if (sm->transitions[i].from != transition || sm->transitions[i].to == transition_prev) { + continue; //not interested on those + } - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) - return true; - } - return false; -} + float distance = sm->states[sm->transitions[i].from].position.distance_to(sm->states[sm->transitions[i].to].position); + distance *= sm->transitions[i].transition->get_priority(); + distance += cost_map[sm->transitions[i].from].distance; -int AnimationNodeStateMachine::find_transition(const StringName &p_from, const StringName &p_to) const { + if (cost_map.has(sm->transitions[i].to)) { + //oh this was visited already, can we win the cost? + if (distance < cost_map[sm->transitions[i].to].distance) { + cost_map[sm->transitions[i].to].distance = distance; + cost_map[sm->transitions[i].to].prev = sm->transitions[i].from; + } + } else { + //add to open list + AStarCost ac; + ac.prev = sm->transitions[i].from; + ac.distance = distance; + cost_map[sm->transitions[i].to] = ac; - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) - return i; - } - return -1; -} + open_list.push_back(i); -void AnimationNodeStateMachine::add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition) { + if (sm->transitions[i].to == p_travel) { + found_route = true; + break; + } + } + } - ERR_FAIL_COND(p_from == p_to); - ERR_FAIL_COND(!states.has(p_from)); - ERR_FAIL_COND(!states.has(p_to)); - ERR_FAIL_COND(p_transition.is_null()); + if (found_route) { + break; + } - for (int i = 0; i < transitions.size(); i++) { - ERR_FAIL_COND(transitions[i].from == p_from && transitions[i].to == p_to); + open_list.erase(least_cost_transition); } - Transition tr; - tr.from = p_from; - tr.to = p_to; - tr.transition = p_transition; - - transitions.push_back(tr); -} - -Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_transition(int p_transition) const { - ERR_FAIL_INDEX_V(p_transition, transitions.size(), Ref<AnimationNodeStateMachineTransition>()); - return transitions[p_transition].transition; -} -StringName AnimationNodeStateMachine::get_transition_from(int p_transition) const { + //make path + StringName at = p_travel; + while (at != current) { + path.push_back(at); + at = cost_map[at].prev; + } - ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); - return transitions[p_transition].from; -} -StringName AnimationNodeStateMachine::get_transition_to(int p_transition) const { + path.invert(); - ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); - return transitions[p_transition].to; + return true; } -int AnimationNodeStateMachine::get_transition_count() const { - - return transitions.size(); -} -void AnimationNodeStateMachine::remove_transition(const StringName &p_from, const StringName &p_to) { +float AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *sm, float p_time, bool p_seek) { - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) { - transitions.remove(i); - return; + //if not playing and it can restart, then restart + if (!playing && start_request == StringName()) { + if (!stop_request && sm->start_node) { + start(sm->start_node); + } else { + return 0; } } - if (playing) { - path.clear(); - } -} - -void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) { - - transitions.remove(p_transition); - if (playing) { - path.clear(); + if (playing && stop_request) { + stop_request = false; + playing = false; + return 0; } -} - -void AnimationNodeStateMachine::set_start_node(const StringName &p_node) { - - ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); - start_node = p_node; -} - -String AnimationNodeStateMachine::get_start_node() const { - - return start_node; -} - -void AnimationNodeStateMachine::set_end_node(const StringName &p_node) { - - ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); - end_node = p_node; -} - -String AnimationNodeStateMachine::get_end_node() const { - - return end_node; -} -void AnimationNodeStateMachine::set_graph_offset(const Vector2 &p_offset) { - graph_offset = p_offset; -} + bool play_start = false; -Vector2 AnimationNodeStateMachine::get_graph_offset() const { - return graph_offset; -} + if (start_request != StringName()) { -float AnimationNodeStateMachine::process(float p_time, bool p_seek) { + if (start_request_travel) { + if (!playing) { + start_request = StringName(); + ERR_EXPLAIN("Can't travel to '" + String(start_request) + "' if state machine is not active."); + ERR_FAIL_V(0); + } - //if not playing and it can restart, then restart - if (!playing) { - if (start_node) { - start(start_node); + if (!_travel(sm, start_request)) { + //cant travel, then teleport + path.clear(); + current = start_request; + } } else { - return 0; + path.clear(); + current = start_request; + playing = true; + play_start = true; } + + start_request = StringName(); //clear start request } bool do_start = (p_seek && p_time == 0) || play_start || current == StringName(); if (do_start) { - if (start_node != StringName() && p_seek && p_time == 0) { - current = start_node; + if (sm->start_node != StringName() && p_seek && p_time == 0) { + current = sm->start_node; } - len_current = blend_node(states[current], 0, true, 1.0, FILTER_IGNORE, false); + len_current = sm->blend_node(current, sm->states[current].node, 0, true, 1.0, AnimationNode::FILTER_IGNORE, false); pos_current = 0; loops_current = 0; play_start = false; } + if (!sm->states.has(current)) { + playing = false; //current does not exist + current = StringName(); + return 0; + } float fade_blend = 1.0; if (fading_from != StringName()) { - if (!p_seek) { - fading_pos += p_time; - } - fade_blend = MIN(1.0, fading_pos / fading_time); - if (fade_blend >= 1.0) { + if (!sm->states.has(fading_from)) { fading_from = StringName(); + } else { + if (!p_seek) { + fading_pos += p_time; + } + fade_blend = MIN(1.0, fading_pos / fading_time); + if (fade_blend >= 1.0) { + fading_from = StringName(); + } } } - float rem = blend_node(states[current], p_time, p_seek, fade_blend, FILTER_IGNORE, false); + float rem = sm->blend_node(current, sm->states[current].node, p_time, p_seek, fade_blend, AnimationNode::FILTER_IGNORE, false); if (fading_from != StringName()) { - blend_node(states[fading_from], p_time, p_seek, 1.0 - fade_blend, FILTER_IGNORE, false); + sm->blend_node(current, sm->states[fading_from].node, p_time, p_seek, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false); } //guess playback position @@ -380,29 +370,39 @@ float AnimationNodeStateMachine::process(float p_time, bool p_seek) { if (path.size()) { - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == current && transitions[i].to == path[0]) { - next_xfade = transitions[i].transition->get_xfade_time(); - switch_mode = transitions[i].transition->get_switch_mode(); + for (int i = 0; i < sm->transitions.size(); i++) { + if (sm->transitions[i].from == current && sm->transitions[i].to == path[0]) { + next_xfade = sm->transitions[i].transition->get_xfade_time(); + switch_mode = sm->transitions[i].transition->get_switch_mode(); next = path[0]; } } } else { float priority_best = 1e20; int auto_advance_to = -1; - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == current && transitions[i].transition->has_auto_advance()) { + for (int i = 0; i < sm->transitions.size(); i++) { + + bool auto_advance = false; + if (sm->transitions[i].transition->has_auto_advance()) { + auto_advance = true; + } + StringName advance_condition_name = sm->transitions[i].transition->get_advance_condition_name(); + if (advance_condition_name != StringName() && bool(sm->get_parameter(advance_condition_name))) { + auto_advance = true; + } + + if (sm->transitions[i].from == current && sm->transitions[i].transition->has_auto_advance()) { - if (transitions[i].transition->get_priority() < priority_best) { + if (sm->transitions[i].transition->get_priority() < priority_best) { auto_advance_to = i; } } } if (auto_advance_to != -1) { - next = transitions[auto_advance_to].to; - next_xfade = transitions[auto_advance_to].transition->get_xfade_time(); - switch_mode = transitions[auto_advance_to].transition->get_switch_mode(); + next = sm->transitions[auto_advance_to].to; + next_xfade = sm->transitions[auto_advance_to].transition->get_xfade_time(); + switch_mode = sm->transitions[auto_advance_to].transition->get_switch_mode(); } } @@ -437,12 +437,12 @@ float AnimationNodeStateMachine::process(float p_time, bool p_seek) { } current = next; if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) { - len_current = blend_node(states[current], 0, true, 0, FILTER_IGNORE, false); + len_current = sm->blend_node(current, sm->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); pos_current = MIN(pos_current, len_current); - blend_node(states[current], pos_current, true, 0, FILTER_IGNORE, false); + sm->blend_node(current, sm->states[current].node, pos_current, true, 0, AnimationNode::FILTER_IGNORE, false); } else { - len_current = blend_node(states[current], 0, true, 0, FILTER_IGNORE, false); + len_current = sm->blend_node(current, sm->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); pos_current = 0; } @@ -453,169 +453,321 @@ float AnimationNodeStateMachine::process(float p_time, bool p_seek) { //compute time left for transitions by using the end node - if (end_node != StringName() && end_node != current) { + if (sm->end_node != StringName() && sm->end_node != current) { - rem = blend_node(states[end_node], 0, true, 0, FILTER_IGNORE, false); + rem = sm->blend_node(current, sm->states[sm->end_node].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); } return rem; } -bool AnimationNodeStateMachine::travel(const StringName &p_state) { - ERR_FAIL_COND_V(!playing, false); - ERR_FAIL_COND_V(!states.has(p_state), false); - ERR_FAIL_COND_V(!states.has(current), false); +void AnimationNodeStateMachinePlayback::_bind_methods() { - path.clear(); //a new one will be needed + ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachinePlayback::travel); + ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachinePlayback::start); + ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachinePlayback::stop); + ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachinePlayback::is_playing); + ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachinePlayback::get_current_node); + ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachinePlayback::get_travel_path); +} - if (current == p_state) - return true; //nothing to do +AnimationNodeStateMachinePlayback::AnimationNodeStateMachinePlayback() { + set_local_to_scene(true); //only one per instanced scene - loops_current = 0; // reset loops, so fade does not happen immediately + playing = false; + len_current = 0; + fading_time = 0; + stop_request = false; +} - Vector2 current_pos = states[current]->get_position(); - Vector2 target_pos = states[p_state]->get_position(); +/////////////////////////////////////////////////////// - Map<StringName, AStarCost> cost_map; +void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::OBJECT, playback, PROPERTY_HINT_RESOURCE_TYPE, "AnimationNodeStateMachinePlayback", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + List<StringName> advance_conditions; + for (int i = 0; i < transitions.size(); i++) { + StringName ac = transitions[i].transition->get_advance_condition_name(); + if (ac != StringName() && advance_conditions.find(ac) == NULL) { + advance_conditions.push_back(ac); + } + } - List<int> open_list; + advance_conditions.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = advance_conditions.front(); E; E = E->next()) { + r_list->push_back(PropertyInfo(Variant::BOOL, E->get())); + } +} - //build open list - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == current) { - open_list.push_back(i); - float cost = states[transitions[i].to]->get_position().distance_to(current_pos); - cost *= transitions[i].transition->get_priority(); - AStarCost ap; - ap.prev = current; - ap.distance = cost; - cost_map[transitions[i].to] = ap; +Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName &p_parameter) const { - if (transitions[i].to == p_state) { //prematurely found it! :D - path.push_back(p_state); - return true; - } - } + if (p_parameter == playback) { + Ref<AnimationNodeStateMachinePlayback> p; + p.instance(); + return p; + } else { + return false; //advance condition } +} - //begin astar - bool found_route = false; - while (!found_route) { +void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position) { - if (open_list.size() == 0) { - return false; //no path found + ERR_FAIL_COND(states.has(p_name)); + ERR_FAIL_COND(p_node.is_null()); + ERR_FAIL_COND(String(p_name).find("/") != -1); + + State state; + state.node = p_node; + state.position = p_position; + + states[p_name] = state; + + emit_changed(); + emit_signal("tree_changed"); + + p_node->connect("tree_changed", this, "_tree_changed", varray(), CONNECT_REFERENCE_COUNTED); +} + +Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const { + + ERR_FAIL_COND_V(!states.has(p_name), Ref<AnimationNode>()); + + return states[p_name].node; +} + +StringName AnimationNodeStateMachine::get_node_name(const Ref<AnimationNode> &p_node) const { + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { + if (E->get().node == p_node) { + return E->key(); } + } - //find the last cost transition - List<int>::Element *least_cost_transition = NULL; - float least_cost = 1e20; + ERR_FAIL_V(StringName()); +} - for (List<int>::Element *E = open_list.front(); E; E = E->next()) { +void AnimationNodeStateMachine::get_child_nodes(List<ChildNode> *r_child_nodes) { + Vector<StringName> nodes; - float cost = cost_map[transitions[E->get()].to].distance; - cost += states[transitions[E->get()].to]->get_position().distance_to(target_pos); + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { + nodes.push_back(E->key()); + } - if (cost < least_cost) { - least_cost_transition = E; - } + nodes.sort_custom<StringName::AlphCompare>(); + + for (int i = 0; i < nodes.size(); i++) { + ChildNode cn; + cn.name = nodes[i]; + cn.node = states[cn.name].node; + r_child_nodes->push_back(cn); + } +} + +bool AnimationNodeStateMachine::has_node(const StringName &p_name) const { + return states.has(p_name); +} +void AnimationNodeStateMachine::remove_node(const StringName &p_name) { + + ERR_FAIL_COND(!states.has(p_name)); + + { + Ref<AnimationNode> node = states[p_name].node; + node->disconnect("tree_changed", this, "_tree_changed"); + } + + states.erase(p_name); + //path.erase(p_name); + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_name || transitions[i].to == p_name) { + transitions.write[i].transition->disconnect("advance_condition_changed", this, "_tree_changed"); + transitions.remove(i); + i--; } + } - StringName transition_prev = transitions[least_cost_transition->get()].from; - StringName transition = transitions[least_cost_transition->get()].to; + if (start_node == p_name) { + start_node = StringName(); + } - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from != transition || transitions[i].to == transition_prev) { - continue; //not interested on those - } + if (end_node == p_name) { + end_node = StringName(); + } - float distance = states[transitions[i].from]->get_position().distance_to(states[transitions[i].to]->get_position()); - distance *= transitions[i].transition->get_priority(); - distance += cost_map[transitions[i].from].distance; + /*if (playing && current == p_name) { + stop(); + }*/ - if (cost_map.has(transitions[i].to)) { - //oh this was visited already, can we win the cost? - if (distance < cost_map[transitions[i].to].distance) { - cost_map[transitions[i].to].distance = distance; - cost_map[transitions[i].to].prev = transitions[i].from; - } - } else { - //add to open list - AStarCost ac; - ac.prev = transitions[i].from; - ac.distance = distance; - cost_map[transitions[i].to] = ac; + emit_changed(); + emit_signal("tree_changed"); +} - open_list.push_back(i); +void AnimationNodeStateMachine::rename_node(const StringName &p_name, const StringName &p_new_name) { - if (transitions[i].to == p_state) { - found_route = true; - break; - } - } + ERR_FAIL_COND(!states.has(p_name)); + ERR_FAIL_COND(states.has(p_new_name)); + + states[p_new_name] = states[p_name]; + states.erase(p_name); + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_name) { + transitions.write[i].from = p_new_name; } - if (found_route) { - break; + if (transitions[i].to == p_name) { + transitions.write[i].to = p_new_name; } + } - open_list.erase(least_cost_transition); + if (start_node == p_name) { + start_node = p_new_name; } - //make path - StringName at = p_state; - while (at != current) { - path.push_back(at); - at = cost_map[at].prev; + if (end_node == p_name) { + end_node = p_new_name; } - path.invert(); + /*if (playing && current == p_name) { + current = p_new_name; + }*/ - return true; + //path.clear(); //clear path + emit_signal("tree_changed"); } -void AnimationNodeStateMachine::start(const StringName &p_state) { +void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { - ERR_FAIL_COND(!states.has(p_state)); - path.clear(); - current = p_state; - playing = true; - play_start = true; + List<StringName> nodes; + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { + nodes.push_back(E->key()); + } + nodes.sort_custom<StringName::AlphCompare>(); + + for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) { + r_nodes->push_back(E->get()); + } } -void AnimationNodeStateMachine::stop() { - playing = false; - play_start = false; - current = StringName(); + +bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const StringName &p_to) const { + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_from && transitions[i].to == p_to) + return true; + } + return false; } -bool AnimationNodeStateMachine::is_playing() const { - return playing; +int AnimationNodeStateMachine::find_transition(const StringName &p_from, const StringName &p_to) const { + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_from && transitions[i].to == p_to) + return i; + } + return -1; } -StringName AnimationNodeStateMachine::get_current_node() const { - if (!playing) { - return StringName(); + +void AnimationNodeStateMachine::add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition) { + + ERR_FAIL_COND(p_from == p_to); + ERR_FAIL_COND(!states.has(p_from)); + ERR_FAIL_COND(!states.has(p_to)); + ERR_FAIL_COND(p_transition.is_null()); + + for (int i = 0; i < transitions.size(); i++) { + ERR_FAIL_COND(transitions[i].from == p_from && transitions[i].to == p_to); } - return current; + Transition tr; + tr.from = p_from; + tr.to = p_to; + tr.transition = p_transition; + + tr.transition->connect("advance_condition_changed", this, "_tree_changed", varray(), CONNECT_REFERENCE_COUNTED); + + transitions.push_back(tr); +} + +Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_transition(int p_transition) const { + ERR_FAIL_INDEX_V(p_transition, transitions.size(), Ref<AnimationNodeStateMachineTransition>()); + return transitions[p_transition].transition; } +StringName AnimationNodeStateMachine::get_transition_from(int p_transition) const { + + ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); + return transitions[p_transition].from; +} +StringName AnimationNodeStateMachine::get_transition_to(int p_transition) const { + + ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); + return transitions[p_transition].to; +} + +int AnimationNodeStateMachine::get_transition_count() const { + + return transitions.size(); +} +void AnimationNodeStateMachine::remove_transition(const StringName &p_from, const StringName &p_to) { -StringName AnimationNodeStateMachine::get_blend_from_node() const { - if (!playing) { - return StringName(); + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_from && transitions[i].to == p_to) { + transitions.write[i].transition->disconnect("advance_condition_changed", this, "_tree_changed"); + transitions.remove(i); + return; + } } - return fading_from; + /*if (playing) { + path.clear(); + }*/ } -float AnimationNodeStateMachine::get_current_play_pos() const { - return pos_current; +void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) { + + ERR_FAIL_INDEX(p_transition, transitions.size()); + transitions.write[p_transition].transition->disconnect("advance_condition_changed", this, "_tree_changed"); + transitions.remove(p_transition); + /*if (playing) { + path.clear(); + }*/ } -float AnimationNodeStateMachine::get_current_length() const { - return len_current; + +void AnimationNodeStateMachine::set_start_node(const StringName &p_node) { + + ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); + start_node = p_node; } -Vector<StringName> AnimationNodeStateMachine::get_travel_path() const { - return path; +String AnimationNodeStateMachine::get_start_node() const { + + return start_node; +} + +void AnimationNodeStateMachine::set_end_node(const StringName &p_node) { + + ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); + end_node = p_node; +} + +String AnimationNodeStateMachine::get_end_node() const { + + return end_node; +} + +void AnimationNodeStateMachine::set_graph_offset(const Vector2 &p_offset) { + graph_offset = p_offset; +} + +Vector2 AnimationNodeStateMachine::get_graph_offset() const { + return graph_offset; +} + +float AnimationNodeStateMachine::process(float p_time, bool p_seek) { + + Ref<AnimationNodeStateMachinePlayback> playback = get_parameter(this->playback); + ERR_FAIL_COND_V(playback.is_null(), 0.0); + + return playback->process(this, p_time, p_seek); } + String AnimationNodeStateMachine::get_caption() const { return "StateMachine"; } @@ -623,14 +775,8 @@ String AnimationNodeStateMachine::get_caption() const { void AnimationNodeStateMachine::_notification(int p_what) { } -void AnimationNodeStateMachine::set_tree(AnimationTree *p_player) { - - AnimationNode::set_tree(p_player); - - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { - Ref<AnimationRootNode> node = E->get(); - node->set_tree(p_player); - } +Ref<AnimationNode> AnimationNodeStateMachine::get_child_by_name(const StringName &p_name) { + return get_node(p_name); } bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_value) { @@ -651,7 +797,7 @@ bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_ if (what == "position") { if (states.has(node_name)) { - states[node_name]->set_position(p_value); + states[node_name].position = p_value; } return true; } @@ -687,7 +833,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c if (what == "node") { if (states.has(node_name)) { - r_ret = states[node_name]; + r_ret = states[node_name].node; return true; } } @@ -695,7 +841,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c if (what == "position") { if (states.has(node_name)) { - r_ret = states[node_name]->get_position(); + r_ret = states[node_name].position; return true; } } @@ -727,14 +873,14 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) const { List<StringName> names; - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { names.push_back(E->key()); } names.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { String name = E->get(); - p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::VECTOR2, "states/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } @@ -744,15 +890,33 @@ void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) c p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } +void AnimationNodeStateMachine::set_node_position(const StringName &p_name, const Vector2 &p_position) { + ERR_FAIL_COND(!states.has(p_name)); + states[p_name].position = p_position; +} + +Vector2 AnimationNodeStateMachine::get_node_position(const StringName &p_name) const { + + ERR_FAIL_COND_V(!states.has(p_name), Vector2()); + return states[p_name].position; +} + +void AnimationNodeStateMachine::_tree_changed() { + emit_signal("tree_changed"); +} + void AnimationNodeStateMachine::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_node", "name", "node"), &AnimationNodeStateMachine::add_node); + ClassDB::bind_method(D_METHOD("add_node", "name", "node", "position"), &AnimationNodeStateMachine::add_node, DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("get_node", "name"), &AnimationNodeStateMachine::get_node); ClassDB::bind_method(D_METHOD("remove_node", "name"), &AnimationNodeStateMachine::remove_node); ClassDB::bind_method(D_METHOD("rename_node", "name", "new_name"), &AnimationNodeStateMachine::rename_node); ClassDB::bind_method(D_METHOD("has_node", "name"), &AnimationNodeStateMachine::has_node); ClassDB::bind_method(D_METHOD("get_node_name", "node"), &AnimationNodeStateMachine::get_node_name); + ClassDB::bind_method(D_METHOD("set_node_position", "name", "position"), &AnimationNodeStateMachine::set_node_position); + ClassDB::bind_method(D_METHOD("get_node_position", "name"), &AnimationNodeStateMachine::get_node_position); + ClassDB::bind_method(D_METHOD("has_transition", "from", "to"), &AnimationNodeStateMachine::add_transition); ClassDB::bind_method(D_METHOD("add_transition", "from", "to", "transition"), &AnimationNodeStateMachine::add_transition); ClassDB::bind_method(D_METHOD("get_transition", "idx"), &AnimationNodeStateMachine::get_transition); @@ -771,20 +935,10 @@ void AnimationNodeStateMachine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_graph_offset", "name"), &AnimationNodeStateMachine::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeStateMachine::get_graph_offset); - ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachine::travel); - ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachine::start); - ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachine::stop); - ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachine::is_playing); - ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachine::get_current_node); - ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachine::get_travel_path); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeStateMachine::_tree_changed); } AnimationNodeStateMachine::AnimationNodeStateMachine() { - play_start = false; - - playing = false; - len_current = 0; - - fading_time = 0; + playback = "playback"; } diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index e7357e09ea..5d633d6334 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -15,6 +15,8 @@ public: private: SwitchMode switch_mode; bool auto_advance; + StringName advance_condition; + StringName advance_condition_name; float xfade; bool disabled; int priority; @@ -29,6 +31,11 @@ public: void set_auto_advance(bool p_enable); bool has_auto_advance() const; + void set_advance_condition(const StringName &p_condition); + StringName get_advance_condition() const; + + StringName get_advance_condition_name() const; + void set_xfade_time(float p_xfade); float get_xfade_time() const; @@ -43,39 +50,24 @@ public: VARIANT_ENUM_CAST(AnimationNodeStateMachineTransition::SwitchMode) -class AnimationNodeStateMachine : public AnimationRootNode { - - GDCLASS(AnimationNodeStateMachine, AnimationRootNode); +class AnimationNodeStateMachine; -private: - Map<StringName, Ref<AnimationRootNode> > states; +class AnimationNodeStateMachinePlayback : public Resource { + GDCLASS(AnimationNodeStateMachinePlayback, Resource); - struct Transition { - - StringName from; - StringName to; - Ref<AnimationNodeStateMachineTransition> transition; - }; + friend class AnimationNodeStateMachine; struct AStarCost { float distance; StringName prev; }; - Vector<Transition> transitions; - float len_total; float len_current; float pos_current; int loops_current; - bool play_start; - StringName start_node; - StringName end_node; - - Vector2 graph_offset; - StringName current; StringName fading_from; @@ -85,6 +77,63 @@ private: Vector<StringName> path; bool playing; + StringName start_request; + bool start_request_travel; + bool stop_request; + + bool _travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel); + + float process(AnimationNodeStateMachine *p_state_machine, float p_time, bool p_seek); + +protected: + static void _bind_methods(); + +public: + void travel(const StringName &p_state); + void start(const StringName &p_state); + void stop(); + bool is_playing() const; + StringName get_current_node() const; + StringName get_blend_from_node() const; + Vector<StringName> get_travel_path() const; + float get_current_play_pos() const; + float get_current_length() const; + + AnimationNodeStateMachinePlayback(); +}; + +class AnimationNodeStateMachine : public AnimationRootNode { + + GDCLASS(AnimationNodeStateMachine, AnimationRootNode); + +private: + friend class AnimationNodeStateMachinePlayback; + + struct State { + Ref<AnimationRootNode> node; + Vector2 position; + }; + + Map<StringName, State> states; + + struct Transition { + + StringName from; + StringName to; + Ref<AnimationNodeStateMachineTransition> transition; + }; + + Vector<Transition> transitions; + + StringName playback; + + StringName start_node; + StringName end_node; + + Vector2 graph_offset; + + void _tree_changed(); + protected: void _notification(int p_what); static void _bind_methods(); @@ -94,7 +143,10 @@ protected: void _get_property_list(List<PropertyInfo> *p_list) const; public: - void add_node(const StringName &p_name, Ref<AnimationNode> p_node); + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + + void add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position = Vector2()); Ref<AnimationNode> get_node(const StringName &p_name) const; void remove_node(const StringName &p_name); void rename_node(const StringName &p_name, const StringName &p_new_name); @@ -102,6 +154,11 @@ public: StringName get_node_name(const Ref<AnimationNode> &p_node) const; void get_node_list(List<StringName> *r_nodes) const; + void set_node_position(const StringName &p_name, const Vector2 &p_position); + Vector2 get_node_position(const StringName &p_name) const; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); + bool has_transition(const StringName &p_from, const StringName &p_to) const; int find_transition(const StringName &p_from, const StringName &p_to) const; void add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition); @@ -124,17 +181,7 @@ public: virtual float process(float p_time, bool p_seek); virtual String get_caption() const; - bool travel(const StringName &p_state); - void start(const StringName &p_state); - void stop(); - bool is_playing() const; - StringName get_current_node() const; - StringName get_blend_from_node() const; - Vector<StringName> get_travel_path() const; - float get_current_play_pos() const; - float get_current_length() const; - - virtual void set_tree(AnimationTree *p_player); + virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name); AnimationNodeStateMachine(); }; diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 8bbc05eed3..68b0602c67 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -5,6 +5,38 @@ #include "scene/scene_string_names.h" #include "servers/audio/audio_stream.h" + + +void AnimationNode::get_parameter_list(List<PropertyInfo> *r_list) const { + +} + +Variant AnimationNode::get_parameter_default_value(const StringName &p_parameter) const { + return Variant(); +} + +void AnimationNode::set_parameter(const StringName& p_name, const Variant& p_value) { + ERR_FAIL_COND(!state); + ERR_FAIL_COND(!state->tree->property_parent_map.has(base_path)); + ERR_FAIL_COND(!state->tree->property_parent_map[base_path].has(p_name)); + StringName path = state->tree->property_parent_map[base_path][p_name]; + + state->tree->property_map[path]=p_value; +} + +Variant AnimationNode::get_parameter(const StringName& p_name) const { + ERR_FAIL_COND_V(!state,Variant()); + ERR_FAIL_COND_V(!state->tree->property_parent_map.has(base_path),Variant()); + ERR_FAIL_COND_V(!state->tree->property_parent_map[base_path].has(p_name),Variant()); + + StringName path = state->tree->property_parent_map[base_path][p_name]; + return state->tree->property_map[path]; +} + +void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) { + +} + void AnimationNode::blend_animation(const StringName &p_animation, float p_time, float p_delta, bool p_seeked, float p_blend) { ERR_FAIL_COND(!state); @@ -14,8 +46,8 @@ void AnimationNode::blend_animation(const StringName &p_animation, float p_time, if (animation.is_null()) { - Ref<AnimationNodeBlendTree> btree = get_parent(); - if (btree.is_valid()) { + AnimationNodeBlendTree* btree = Object::cast_to<AnimationNodeBlendTree>(parent); + if (btree) { String name = btree->get_node_name(Ref<AnimationNodeAnimation>(this)); make_invalid(vformat(RTR("In node '%s', invalid animation: '%s'."), name, p_animation)); } else { @@ -37,10 +69,20 @@ void AnimationNode::blend_animation(const StringName &p_animation, float p_time, state->animation_states.push_back(anim_state); } -float AnimationNode::_pre_process(State *p_state, float p_time, bool p_seek) { +float AnimationNode::_pre_process(const StringName& p_base_path, AnimationNode *p_parent, State *p_state, float p_time, bool p_seek, const Vector<StringName>& p_connections) { + + base_path = p_base_path; + parent = p_parent; + connections=p_connections; state = p_state; + float t = process(p_time, p_seek); + state = NULL; + parent = NULL; + base_path = StringName(); + connections.clear(); + return t; } @@ -56,39 +98,31 @@ void AnimationNode::make_invalid(const String &p_reason) { float AnimationNode::blend_input(int p_input, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) { ERR_FAIL_INDEX_V(p_input, inputs.size(), 0); ERR_FAIL_COND_V(!state, 0); - ERR_FAIL_COND_V(!get_tree(), 0); //should not happen, but used to catch bugs - - Ref<AnimationNodeBlendTree> tree = get_parent(); - - if (!tree.is_valid() && get_tree()->get_tree_root().ptr() != this) { - make_invalid(RTR("Can't blend input because node is not in a tree")); - return 0; - } - ERR_FAIL_COND_V(!tree.is_valid(), 0); //should not happen + AnimationNodeBlendTree* blend_tree = Object::cast_to<AnimationNodeBlendTree>(parent); + ERR_FAIL_COND_V(!blend_tree,0); - StringName anim_name = inputs[p_input].connected_to; + StringName node_name = connections[p_input]; - Ref<AnimationNode> node = tree->get_node(anim_name); - - if (node.is_null()) { - - String name = tree->get_node_name(Ref<AnimationNodeAnimation>(this)); + if (!blend_tree->has_node(node_name)) { + String name = blend_tree->get_node_name(Ref<AnimationNode>(this)); make_invalid(vformat(RTR("Nothing connected to input '%s' of node '%s'."), get_input_name(p_input), name)); return 0; } - inputs.write[p_input].last_pass = state->last_pass; + Ref<AnimationNode> node = blend_tree->get_node(node_name); - return _blend_node(node, p_time, p_seek, p_blend, p_filter, p_optimize, &inputs.write[p_input].activity); + //inputs.write[p_input].last_pass = state->last_pass; + float activity; + return _blend_node(node_name,blend_tree->get_node_connection_array(node_name),NULL,node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity); } -float AnimationNode::blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) { +float AnimationNode::blend_node(const StringName& p_sub_path,Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) { - return _blend_node(p_node, p_time, p_seek, p_blend, p_filter, p_optimize); + return _blend_node(p_sub_path,Vector<StringName>(),this,p_node, p_time, p_seek, p_blend, p_filter, p_optimize); } -float AnimationNode::_blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize, float *r_max) { +float AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName>& p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize, float *r_max) { ERR_FAIL_COND_V(!p_node.is_valid(), 0); ERR_FAIL_COND_V(!state, 0); @@ -189,7 +223,19 @@ float AnimationNode::_blend_node(Ref<AnimationNode> p_node, float p_time, bool p if (!p_seek && p_optimize && !any_valid) //pointless to go on, all are zero return 0; - return p_node->_pre_process(state, p_time, p_seek); + String new_path; + AnimationNode *new_parent; + + //this is the slowest part of processing, but as strings process in powers of 2, and the paths always exist, it will not result in that many allocations + if (p_new_parent) { + new_parent = p_new_parent; + new_path = String(base_path)+String(p_subpath)+"/"; + } else { + ERR_FAIL_COND_V(!parent,0); + new_parent = parent; + new_path = String(parent->base_path) + String(p_subpath)+"/"; + } + return p_node->_pre_process(new_path,new_parent,state, p_time, p_seek, p_connections); } int AnimationNode::get_input_count() const { @@ -201,28 +247,6 @@ String AnimationNode::get_input_name(int p_input) { return inputs[p_input].name; } -float AnimationNode::get_input_activity(int p_input) const { - - ERR_FAIL_INDEX_V(p_input, inputs.size(), 0); - if (!get_tree()) - return 0; - - if (get_tree()->get_last_process_pass() != inputs[p_input].last_pass) { - return 0; - } - return inputs[p_input].activity; -} -StringName AnimationNode::get_input_connection(int p_input) { - - ERR_FAIL_INDEX_V(p_input, inputs.size(), StringName()); - return inputs[p_input].connected_to; -} - -void AnimationNode::set_input_connection(int p_input, const StringName &p_connection) { - - ERR_FAIL_INDEX(p_input, inputs.size()); - inputs.write[p_input].connected_to = p_connection; -} String AnimationNode::get_caption() const { @@ -239,8 +263,6 @@ void AnimationNode::add_input(const String &p_name) { Input input; ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1); input.name = p_name; - input.activity = 0; - input.last_pass = 0; inputs.push_back(input); emit_changed(); } @@ -258,35 +280,6 @@ void AnimationNode::remove_input(int p_index) { emit_changed(); } -void AnimationNode::_set_parent(Object *p_parent) { - set_parent(Object::cast_to<AnimationNode>(p_parent)); -} - -void AnimationNode::set_parent(AnimationNode *p_parent) { - parent = p_parent; //do not use ref because parent contains children - if (get_script_instance()) { - get_script_instance()->call("_parent_set", p_parent); - } -} - -Ref<AnimationNode> AnimationNode::get_parent() const { - if (parent) { - return Ref<AnimationNode>(parent); - } - - return Ref<AnimationNode>(); -} - -AnimationTree *AnimationNode::get_tree() const { - - return player; -} - -AnimationPlayer *AnimationNode::get_player() const { - ERR_FAIL_COND_V(!state, NULL); - return state->player; -} - float AnimationNode::process(float p_time, bool p_seek) { if (get_script_instance()) { @@ -320,21 +313,7 @@ bool AnimationNode::has_filter() const { return false; } -void AnimationNode::set_position(const Vector2 &p_position) { - position = p_position; -} - -Vector2 AnimationNode::get_position() const { - return position; -} - -void AnimationNode::set_tree(AnimationTree *p_player) { - if (player != NULL && p_player == NULL) { - emit_signal("removed_from_graph"); - } - player = p_player; -} Array AnimationNode::_get_filters() const { @@ -361,12 +340,14 @@ void AnimationNode::_validate_property(PropertyInfo &property) const { } } +Ref<AnimationNode> AnimationNode::get_child_by_name(const StringName &p_name) { + return Ref<AnimationNode>(); +} + void AnimationNode::_bind_methods() { ClassDB::bind_method(D_METHOD("get_input_count"), &AnimationNode::get_input_count); ClassDB::bind_method(D_METHOD("get_input_name", "input"), &AnimationNode::get_input_name); - ClassDB::bind_method(D_METHOD("get_input_connection", "input"), &AnimationNode::get_input_connection); - ClassDB::bind_method(D_METHOD("get_input_activity", "input"), &AnimationNode::get_input_activity); ClassDB::bind_method(D_METHOD("add_input", "name"), &AnimationNode::add_input); ClassDB::bind_method(D_METHOD("remove_input", "index"), &AnimationNode::remove_input); @@ -377,19 +358,16 @@ void AnimationNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_filter_enabled", "enable"), &AnimationNode::set_filter_enabled); ClassDB::bind_method(D_METHOD("is_filter_enabled"), &AnimationNode::is_filter_enabled); - ClassDB::bind_method(D_METHOD("set_position", "position"), &AnimationNode::set_position); - ClassDB::bind_method(D_METHOD("get_position"), &AnimationNode::get_position); ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters); ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters); ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "blend"), &AnimationNode::blend_animation); - ClassDB::bind_method(D_METHOD("blend_node", "node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("blend_node", "name","node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true)); ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("set_parent", "parent"), &AnimationNode::_set_parent); - ClassDB::bind_method(D_METHOD("get_parent"), &AnimationNode::get_parent); - ClassDB::bind_method(D_METHOD("get_tree"), &AnimationNode::get_tree); + ClassDB::bind_method(D_METHOD("set_parameter","name","value"), &AnimationNode::set_parameter); + ClassDB::bind_method(D_METHOD("get_parameter","name"), &AnimationNode::get_parameter); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_filter_enabled", "is_filter_enabled"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "filters", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_filters", "_get_filters"); @@ -397,9 +375,11 @@ void AnimationNode::_bind_methods() { BIND_VMETHOD(MethodInfo("process", PropertyInfo(Variant::REAL, "time"), PropertyInfo(Variant::BOOL, "seek"))); BIND_VMETHOD(MethodInfo(Variant::STRING, "get_caption")); BIND_VMETHOD(MethodInfo(Variant::STRING, "has_filter")); - BIND_VMETHOD(MethodInfo("_parent_set", PropertyInfo(Variant::OBJECT, "parent"))); ADD_SIGNAL(MethodInfo("removed_from_graph")); + + ADD_SIGNAL(MethodInfo("tree_changed")); + BIND_ENUM_CONSTANT(FILTER_IGNORE); BIND_ENUM_CONSTANT(FILTER_PASS); BIND_ENUM_CONSTANT(FILTER_STOP); @@ -410,8 +390,6 @@ AnimationNode::AnimationNode() { state = NULL; parent = NULL; - player = NULL; - set_local_to_scene(true); filter_enabled = false; } @@ -420,18 +398,17 @@ AnimationNode::AnimationNode() { void AnimationTree::set_tree_root(const Ref<AnimationNode> &p_root) { if (root.is_valid()) { - root->set_tree(NULL); - } - if (p_root.is_valid()) { - ERR_EXPLAIN("root node already set to another player"); - ERR_FAIL_COND(p_root->player); + root->disconnect("tree_changed",this,"_tree_changed"); } + root = p_root; if (root.is_valid()) { - root->set_tree(this); + root->connect("tree_changed",this,"_tree_changed"); } + properties_dirty=true; + update_configuration_warning(); } @@ -699,7 +676,10 @@ void AnimationTree::_clear_caches() { void AnimationTree::_process_graph(float p_delta) { + _update_properties(); //if properties need updating, update them + //check all tracks, see if they need modification + root_motion_transform = Transform(); if (!root.is_valid()) { @@ -741,6 +721,7 @@ void AnimationTree::_process_graph(float p_delta) { state.valid = true; state.player = player; state.last_pass = process_pass; + state.tree = this; // root source blends @@ -757,11 +738,11 @@ void AnimationTree::_process_graph(float p_delta) { if (started) { //if started, seek - root->_pre_process(&state, 0, true); + root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path,NULL,&state, 0, true,Vector<StringName>()); started = false; } - root->_pre_process(&state, p_delta, false); + root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path,NULL,&state, p_delta, false,Vector<StringName>()); } if (!state.valid) { @@ -1297,6 +1278,117 @@ Transform AnimationTree::get_root_motion_transform() const { return root_motion_transform; } +void AnimationTree::_tree_changed() { + if (properties_dirty) { + return; + } + + call_deferred("_update_properties"); + properties_dirty=true; +} + +void AnimationTree::_update_properties_for_node(const String& p_base_path,Ref<AnimationNode> node) { + + if (!property_parent_map.has(p_base_path)) { + property_parent_map[p_base_path]=HashMap<StringName, StringName>(); + } + + List<PropertyInfo> plist; + node->get_parameter_list(&plist); + for(List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) { + PropertyInfo pinfo = E->get(); + + StringName key = pinfo.name; + + if (!property_map.has(p_base_path +key)) { + property_map[p_base_path + key] = node->get_parameter_default_value(key); + } + + property_parent_map[p_base_path][key]=p_base_path+key; + + pinfo.name = p_base_path + key; + properties.push_back(pinfo); + } + + List<AnimationNode::ChildNode> children; + node->get_child_nodes(&children); + + for (List<AnimationNode::ChildNode>::Element *E=children.front();E;E=E->next()) { + _update_properties_for_node(p_base_path+E->get().name+"/",E->get().node); + } +} + +void AnimationTree::_update_properties() { + if (!properties_dirty) { + return; + } + + properties.clear(); + property_parent_map.clear(); + + if (root.is_valid()) { + _update_properties_for_node(SceneStringNames::get_singleton()->parameters_base_path,root); + } + + properties_dirty = false; + + _change_notify(); +} + +bool AnimationTree::_set(const StringName &p_name, const Variant &p_value) { + if (properties_dirty) { + _update_properties(); + } + + if (property_map.has(p_name)) { + property_map[p_name]=p_value; +#ifdef TOOLS_ENABLED + _change_notify(p_name.operator String().utf8().get_data()); +#endif + return true; + } + + return false; +} + +bool AnimationTree::_get(const StringName &p_name, Variant &r_ret) const { + if (properties_dirty) { + const_cast<AnimationTree*>(this)->_update_properties(); + } + + if (property_map.has(p_name)) { + r_ret=property_map[p_name]; + return true; + } + + return false; + +} +void AnimationTree::_get_property_list(List<PropertyInfo> *p_list) const { + if (properties_dirty) { + const_cast<AnimationTree*>(this)->_update_properties(); + } + + for (const List<PropertyInfo>::Element *E=properties.front();E;E=E->next()) { + p_list->push_back(E->get()); + } +} + +void AnimationTree::rename_parameter(const String& p_base,const String& p_new_base) { + + //rename values first + for (const List<PropertyInfo>::Element *E=properties.front();E;E=E->next()) { + if (E->get().name.begins_with(p_base)) { + String new_name = E->get().name.replace_first(p_base,p_new_base); + property_map[new_name]=property_map[E->get().name]; + } + } + + //update tree second + properties_dirty=true; + _update_properties(); +} + void AnimationTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_active", "active"), &AnimationTree::set_active); ClassDB::bind_method(D_METHOD("is_active"), &AnimationTree::is_active); @@ -1315,11 +1407,17 @@ void AnimationTree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_root_motion_transform"), &AnimationTree::get_root_motion_transform); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationTree::_tree_changed); + ClassDB::bind_method(D_METHOD("_update_properties"), &AnimationTree::_update_properties); + + ClassDB::bind_method(D_METHOD("rename_parameter","old_name","new_name"), &AnimationTree::rename_parameter); + + ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationTree::advance); ClassDB::bind_method(D_METHOD("_node_removed"), &AnimationTree::_node_removed); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_tree_root", "get_tree_root"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode"), "set_tree_root", "get_tree_root"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active"); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_process_mode", "get_process_mode"); @@ -1338,10 +1436,9 @@ AnimationTree::AnimationTree() { cache_valid = false; setup_pass = 1; started = true; + properties_dirty = true; } AnimationTree::~AnimationTree() { - if (root.is_valid()) { - root->player = NULL; - } + } diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 87092a4a0e..3c615b2f92 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -23,9 +23,6 @@ public: struct Input { String name; - StringName connected_to; - float activity; - uint64_t last_pass; }; Vector<Input> inputs; @@ -51,30 +48,33 @@ public: List<AnimationState> animation_states; bool valid; AnimationPlayer *player; + AnimationTree *tree; String invalid_reasons; uint64_t last_pass; }; Vector<float> blends; State *state; - float _pre_process(State *p_state, float p_time, bool p_seek); + String path; + float _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, float p_time, bool p_seek, const Vector<StringName> &p_connections); void _pre_update_animations(HashMap<NodePath, int> *track_map); - Vector2 position; + //all this is temporary + StringName base_path; + Vector<StringName> connections; AnimationNode *parent; - AnimationTree *player; - - float _blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, float *r_max = NULL); HashMap<NodePath, bool> filter; bool filter_enabled; Array _get_filters() const; void _set_filters(const Array &p_filters); + friend class AnimationNodeBlendTree; + float _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, float *r_max = NULL); protected: void blend_animation(const StringName &p_animation, float p_time, float p_delta, bool p_seeked, float p_blend); - float blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); + float blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); float blend_input(int p_input, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); void make_invalid(const String &p_reason); @@ -85,20 +85,24 @@ protected: void _set_parent(Object *p_parent); public: - void set_parent(AnimationNode *p_parent); - Ref<AnimationNode> get_parent() const; - virtual void set_tree(AnimationTree *p_player); - AnimationTree *get_tree() const; - AnimationPlayer *get_player() const; + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + + void set_parameter(const StringName &p_name, const Variant &p_value); + Variant get_parameter(const StringName &p_name) const; + + struct ChildNode { + StringName name; + Ref<AnimationNode> node; + }; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); virtual float process(float p_time, bool p_seek); virtual String get_caption() const; int get_input_count() const; String get_input_name(int p_input); - StringName get_input_connection(int p_input); - void set_input_connection(int p_input, const StringName &p_connection); - float get_input_activity(int p_input) const; void add_input(const String &p_name); void set_input_name(int p_input, const String &p_name); @@ -112,8 +116,7 @@ public: virtual bool has_filter() const; - void set_position(const Vector2 &p_position); - Vector2 get_position() const; + virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name); AnimationNode(); }; @@ -245,7 +248,21 @@ private: NodePath root_motion_track; Transform root_motion_transform; + friend class AnimationNode; + bool properties_dirty; + void _tree_changed(); + void _update_properties(); + List<PropertyInfo> properties; + HashMap<StringName, HashMap<StringName, StringName> > property_parent_map; + HashMap<StringName, Variant> property_map; + + void _update_properties_for_node(const String &p_base_path, Ref<AnimationNode> node); + protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + void _notification(int p_what); static void _bind_methods(); @@ -274,6 +291,8 @@ public: void advance(float p_time); + void rename_parameter(const String &p_base, const String &p_new_base); + uint64_t get_last_process_pass() const; AnimationTree(); ~AnimationTree(); diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index d95ec9e495..a7163adbe6 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -356,14 +356,14 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (create_hot_zone(pos).has_point(p_point)) + if (is_in_hot_zone(pos, p_point)) return true; } for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - if (create_hot_zone(pos).has_point(p_point)) { + if (is_in_hot_zone(pos, p_point)) { return true; } } @@ -388,7 +388,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (create_hot_zone(pos).has_point(mpos)) { + if (is_in_hot_zone(pos, mpos)) { if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) { //check disconnect @@ -435,7 +435,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - if (create_hot_zone(pos).has_point(mpos)) { + if (is_in_hot_zone(pos, mpos)) { if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) { //check disconnect @@ -502,7 +502,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); int type = gn->get_connection_output_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && create_hot_zone(pos).has_point(mpos)) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos, mpos)) { connecting_target = true; connecting_to = pos; @@ -517,7 +517,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); int type = gn->get_connection_input_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && create_hot_zone(pos).has_point(mpos)) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos, mpos)) { connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -557,8 +557,55 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } } -Rect2 GraphEdit::create_hot_zone(const Vector2 &pos) { - return Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2); +bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &pos) { + + if (p_control->is_set_as_toplevel() || !p_control->is_visible()) + return false; + + if (!p_control->has_point(pos) || p_control->get_mouse_filter() == MOUSE_FILTER_IGNORE) { + //test children + for (int i = 0; i < p_control->get_child_count(); i++) { + Control *subchild = Object::cast_to<Control>(p_control->get_child(i)); + if (!subchild) + continue; + if (_check_clickable_control(subchild, pos - subchild->get_position())) { + return true; + } + } + + return false; + } else { + return true; + } +} + +bool GraphEdit::is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos) { + if (!Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2).has_point(p_mouse_pos)) + return false; + + for (int i = 0; i < get_child_count(); i++) { + Control *child = Object::cast_to<Control>(get_child(i)); + if (!child) + continue; + Rect2 rect = child->get_rect(); + if (rect.has_point(p_mouse_pos)) { + + //check sub-controls + Vector2 subpos = p_mouse_pos - rect.position; + + for (int j = 0; j < child->get_child_count(); j++) { + Control *subchild = Object::cast_to<Control>(child->get_child(j)); + if (!subchild) + continue; + + if (_check_clickable_control(subchild, subpos - subchild->get_position())) { + return false; + } + } + } + } + + return true; } template <class Vector2> diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 64ba18681e..31a449eb59 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -131,7 +131,7 @@ private: GraphEditFilter *top_layer; void _top_layer_input(const Ref<InputEvent> &p_ev); - Rect2 create_hot_zone(const Vector2 &pos); + bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos); void _top_layer_draw(); void _connections_layer_draw(); @@ -172,6 +172,8 @@ private: void _snap_toggled(); void _snap_value_changed(double); + bool _check_clickable_control(Control *p_control, const Vector2 &pos); + protected: static void _bind_methods(); virtual void add_child_notify(Node *p_child); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index d066814069..a3400a32f7 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -412,6 +412,8 @@ void register_scene_types() { ClassDB::register_class<AnimationNodeBlendSpace1D>(); ClassDB::register_class<AnimationNodeBlendSpace2D>(); ClassDB::register_class<AnimationNodeStateMachine>(); + ClassDB::register_class<AnimationNodeStateMachinePlayback>(); + ClassDB::register_class<AnimationNodeStateMachineTransition>(); ClassDB::register_class<AnimationNodeOutput>(); ClassDB::register_class<AnimationNodeOneShot>(); diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 661606c7ef..e468b3dab4 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -201,4 +201,6 @@ SceneStringNames::SceneStringNames() { } _mesh_changed = StaticCString::create("_mesh_changed"); + + parameters_base_path = "parameters/"; } diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 817158f9f3..dbbcf79b9f 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -203,6 +203,8 @@ public: StringName output; + StringName parameters_base_path; + enum { MAX_MATERIALS = 32 }; |