Browse Source

Massive rewrite to AnimationTree. Many APIs changed in order to:
-Reuse resources
-Expose properties in AnimationTree

Juan Linietsky 7 years ago
parent
commit
c7e4527a88
34 changed files with 3815 additions and 2889 deletions
  1. 20 3
      core/object.cpp
  2. 4 1
      core/object.h
  3. 2 1
      editor/editor_inspector.cpp
  4. 2 4
      editor/editor_node.cpp
  5. 13 2
      editor/inspector_dock.cpp
  6. 2 3
      editor/inspector_dock.h
  7. 88 88
      editor/plugins/animation_blend_space_1d_editor.cpp
  8. 16 25
      editor/plugins/animation_blend_space_1d_editor.h
  9. 92 93
      editor/plugins/animation_blend_space_2d_editor.cpp
  10. 17 24
      editor/plugins/animation_blend_space_2d_editor.h
  11. 164 129
      editor/plugins/animation_blend_tree_editor_plugin.cpp
  12. 19 26
      editor/plugins/animation_blend_tree_editor_plugin.h
  13. 167 135
      editor/plugins/animation_state_machine_editor.cpp
  14. 17 27
      editor/plugins/animation_state_machine_editor.h
  15. 151 1322
      editor/plugins/animation_tree_editor_plugin.cpp
  16. 48 150
      editor/plugins/animation_tree_editor_plugin.h
  17. 1446 0
      editor/plugins/animation_tree_player_editor_plugin.cpp
  18. 187 0
      editor/plugins/animation_tree_player_editor_plugin.h
  19. 3 7
      scene/3d/physics_body.cpp
  20. 48 32
      scene/animation/animation_blend_space_1d.cpp
  21. 13 6
      scene/animation/animation_blend_space_1d.h
  22. 47 31
      scene/animation/animation_blend_space_2d.cpp
  23. 12 5
      scene/animation/animation_blend_space_2d.h
  24. 273 220
      scene/animation/animation_blend_tree.cpp
  25. 69 42
      scene/animation/animation_blend_tree.h
  26. 504 340
      scene/animation/animation_node_state_machine.cpp
  27. 78 31
      scene/animation/animation_node_state_machine.h
  28. 211 114
      scene/animation/animation_tree.cpp
  29. 38 19
      scene/animation/animation_tree.h
  30. 55 8
      scene/gui/graph_edit.cpp
  31. 3 1
      scene/gui/graph_edit.h
  32. 2 0
      scene/register_scene_types.cpp
  33. 2 0
      scene/scene_string_names.cpp
  34. 2 0
      scene/scene_string_names.h

+ 20 - 3
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) {

+ 4 - 1
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;

+ 2 - 1
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();
 				}

+ 2 - 4
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)));

+ 13 - 2
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) {

+ 2 - 3
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,

+ 88 - 88
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;

+ 16 - 25
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

+ 92 - 93
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() {
-}

+ 17 - 24
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 <[email protected]>
 */
 
-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

+ 164 - 129
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() {
-}

+ 19 - 26
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 <[email protected]>
 */
 
-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

+ 167 - 135
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() {
-}

+ 17 - 27
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

File diff suppressed because it is too large
+ 151 - 1322
editor/plugins/animation_tree_editor_plugin.cpp


+ 48 - 150
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 <[email protected]>
-*/
-
-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;

+ 1446 - 0
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() {
+}

+ 187 - 0
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 <[email protected]>
+*/
+
+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

+ 3 - 7
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");
 }

+ 48 - 32
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());
-	}
 }

+ 13 - 6
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();
 };

+ 47 - 31
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());
-	}
+
 }

+ 12 - 5
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();
 };

File diff suppressed because it is too large
+ 273 - 220
scene/animation/animation_blend_tree.cpp


+ 69 - 42
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();
 };

File diff suppressed because it is too large
+ 504 - 340
scene/animation/animation_node_state_machine.cpp


+ 78 - 31
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();
 };

+ 211 - 114
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;
-	}
+
 }

+ 38 - 19
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();

+ 55 - 8
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>

+ 3 - 1
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);

+ 2 - 0
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>();

+ 2 - 0
scene/scene_string_names.cpp

@@ -201,4 +201,6 @@ SceneStringNames::SceneStringNames() {
 	}
 
 	_mesh_changed = StaticCString::create("_mesh_changed");
+
+	parameters_base_path = "parameters/";
 }

+ 2 - 0
scene/scene_string_names.h

@@ -203,6 +203,8 @@ public:
 
 	StringName output;
 
+	StringName parameters_base_path;
+
 	enum {
 		MAX_MATERIALS = 32
 	};

Some files were not shown because too many files changed in this diff