diff options
Diffstat (limited to 'scene/animation')
-rw-r--r-- | scene/animation/animation_blend_space_1d.cpp | 80 | ||||
-rw-r--r-- | scene/animation/animation_blend_space_1d.h | 19 | ||||
-rw-r--r-- | scene/animation/animation_blend_space_2d.cpp | 78 | ||||
-rw-r--r-- | scene/animation/animation_blend_space_2d.h | 17 | ||||
-rw-r--r-- | scene/animation/animation_blend_tree.cpp | 517 | ||||
-rw-r--r-- | scene/animation/animation_blend_tree.h | 111 | ||||
-rw-r--r-- | scene/animation/animation_node_state_machine.cpp | 836 | ||||
-rw-r--r-- | scene/animation/animation_node_state_machine.h | 109 | ||||
-rw-r--r-- | scene/animation/animation_tree.cpp | 325 | ||||
-rw-r--r-- | scene/animation/animation_tree.h | 57 |
10 files changed, 1300 insertions, 849 deletions
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index 1bc9fa4b12..2bde8a16c9 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -1,12 +1,15 @@ #include "animation_blend_space_1d.h" -void AnimationNodeBlendSpace1D::set_tree(AnimationTree *p_player) { - AnimationRootNode::set_tree(p_player); +void AnimationNodeBlendSpace1D::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL,blend_position)); +} +Variant AnimationNodeBlendSpace1D::get_parameter_default_value(const StringName &p_parameter) const { + return 0; +} - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_tree(p_player); - } +Ref<AnimationNode> AnimationNodeBlendSpace1D::get_child_by_name(const StringName &p_name) { + return get_blend_point_node(p_name.operator String().to_int()); } void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const { @@ -20,6 +23,10 @@ void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const AnimationRootNode::_validate_property(property); } +void AnimationNodeBlendSpace1D::_tree_changed() { + emit_signal("tree_changed"); +} + void AnimationNodeBlendSpace1D::_bind_methods() { ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace1D::add_blend_point, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace1D::set_blend_point_position); @@ -38,30 +45,37 @@ void AnimationNodeBlendSpace1D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_snap", "snap"), &AnimationNodeBlendSpace1D::set_snap); ClassDB::bind_method(D_METHOD("get_snap"), &AnimationNodeBlendSpace1D::get_snap); - ClassDB::bind_method(D_METHOD("set_blend_pos", "pos"), &AnimationNodeBlendSpace1D::set_blend_pos); - ClassDB::bind_method(D_METHOD("get_blend_pos"), &AnimationNodeBlendSpace1D::get_blend_pos); - ClassDB::bind_method(D_METHOD("set_value_label", "text"), &AnimationNodeBlendSpace1D::set_value_label); ClassDB::bind_method(D_METHOD("get_value_label"), &AnimationNodeBlendSpace1D::get_value_label); ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace1D::_add_blend_point); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeBlendSpace1D::_tree_changed); + for (int i = 0; i < MAX_BLEND_POINTS; i++) { - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "_add_blend_point", "get_blend_point_node", i); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i); } ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "blend_pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_blend_pos", "get_blend_pos"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_value_label", "get_value_label"); } +void AnimationNodeBlendSpace1D::get_child_nodes(List<ChildNode> *r_child_nodes) { + for(int i=0;i<blend_points_used;i++) { + ChildNode cn; + cn.name=itos(i); + cn.node=blend_points[i].node; + r_child_nodes->push_back(cn); + } +} + void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index) { ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(p_node->get_parent().is_valid()); + ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used); if (p_at_index == -1 || p_at_index == blend_points_used) { @@ -75,10 +89,12 @@ void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_ blend_points[p_at_index].node = p_node; blend_points[p_at_index].position = p_position; - blend_points[p_at_index].node->set_parent(this); - blend_points[p_at_index].node->set_tree(get_tree()); + blend_points[p_at_index].node->connect("tree_changed",this,"_tree_changed",varray(),CONNECT_REFERENCE_COUNTED); blend_points_used++; + emit_signal("tree_changed"); + + } void AnimationNodeBlendSpace1D::set_blend_point_position(int p_point, float p_position) { @@ -92,13 +108,14 @@ void AnimationNodeBlendSpace1D::set_blend_point_node(int p_point, const Ref<Anim ERR_FAIL_COND(p_node.is_null()); if (blend_points[p_point].node.is_valid()) { - blend_points[p_point].node->set_parent(NULL); - blend_points[p_point].node->set_tree(NULL); + blend_points[p_point].node->disconnect("tree_changed",this,"_tree_changed"); } blend_points[p_point].node = p_node; - blend_points[p_point].node->set_parent(this); - blend_points[p_point].node->set_tree(get_tree()); + blend_points[p_point].node->connect("tree_changed",this,"_tree_changed",varray(),CONNECT_REFERENCE_COUNTED); + + emit_signal("tree_changed"); + } float AnimationNodeBlendSpace1D::get_blend_point_position(int p_point) const { @@ -114,14 +131,16 @@ Ref<AnimationRootNode> AnimationNodeBlendSpace1D::get_blend_point_node(int p_poi void AnimationNodeBlendSpace1D::remove_blend_point(int p_point) { ERR_FAIL_INDEX(p_point, blend_points_used); - blend_points[p_point].node->set_parent(NULL); - blend_points[p_point].node->set_tree(NULL); + blend_points[p_point].node->disconnect("tree_changed",this,"_tree_changed"); for (int i = p_point; i < blend_points_used - 1; i++) { blend_points[i] = blend_points[i + 1]; } + blend_points_used--; + emit_signal("tree_changed"); + } int AnimationNodeBlendSpace1D::get_blend_point_count() const { @@ -161,13 +180,6 @@ float AnimationNodeBlendSpace1D::get_snap() const { return snap; } -void AnimationNodeBlendSpace1D::set_blend_pos(float p_pos) { - blend_pos = p_pos; -} - -float AnimationNodeBlendSpace1D::get_blend_pos() const { - return blend_pos; -} void AnimationNodeBlendSpace1D::set_value_label(const String &p_label) { value_label = p_label; @@ -191,11 +203,14 @@ float AnimationNodeBlendSpace1D::process(float p_time, bool p_seek) { return 0.0; } + if (blend_points_used == 1) { // only one point available, just play that animation - return blend_node(blend_points[0].node, p_time, p_seek, 1.0, FILTER_IGNORE, false); + return blend_node(blend_points[0].name,blend_points[0].node, p_time, p_seek, 1.0, FILTER_IGNORE, false); } + float blend_pos = get_parameter(blend_position); + float weights[MAX_BLEND_POINTS] = {}; int point_lower = -1; @@ -262,7 +277,7 @@ float AnimationNodeBlendSpace1D::process(float p_time, bool p_seek) { float max_time_remaining = 0.0; for (int i = 0; i < blend_points_used; i++) { - float remaining = blend_node(blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false); + float remaining = blend_node(blend_points[i].name,blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false); max_time_remaining = MAX(max_time_remaining, remaining); } @@ -276,18 +291,19 @@ String AnimationNodeBlendSpace1D::get_caption() const { AnimationNodeBlendSpace1D::AnimationNodeBlendSpace1D() { + for(int i=0;i<MAX_BLEND_POINTS;i++) { + blend_points[i].name=itos(i); + } blend_points_used = 0; max_space = 1; min_space = -1; snap = 0.1; value_label = "value"; + + blend_position="blend_position"; } AnimationNodeBlendSpace1D::~AnimationNodeBlendSpace1D() { - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_parent(this); - blend_points[i].node->set_tree(get_tree()); - } } diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index d1ed4c6a1f..b47accf973 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -11,6 +11,7 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { }; struct BlendPoint { + StringName name; Ref<AnimationRootNode> node; float position; }; @@ -18,8 +19,6 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { BlendPoint blend_points[MAX_BLEND_POINTS]; int blend_points_used; - float blend_pos; - float max_space; float min_space; @@ -29,12 +28,21 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node); + void _tree_changed(); + + StringName blend_position; + protected: virtual void _validate_property(PropertyInfo &property) const; static void _bind_methods(); public: - virtual void set_tree(AnimationTree *p_player); + + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); + void add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index = -1); void set_blend_point_position(int p_point, float p_position); @@ -54,15 +62,14 @@ public: void set_snap(float p_snap); float get_snap() const; - void set_blend_pos(float p_pos); - float get_blend_pos() const; - void set_value_label(const String &p_label); String get_value_label() const; float process(float p_time, bool p_seek); String get_caption() const; + Ref<AnimationNode> get_child_by_name(const StringName &p_name); + AnimationNodeBlendSpace1D(); ~AnimationNodeBlendSpace1D(); }; diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 3c93a0c8ec..bce1477d4e 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -1,18 +1,27 @@ #include "animation_blend_space_2d.h" #include "math/delaunay.h" -void AnimationNodeBlendSpace2D::set_tree(AnimationTree *p_player) { - AnimationRootNode::set_tree(p_player); - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_tree(p_player); +void AnimationNodeBlendSpace2D::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::VECTOR2,blend_position)); +} +Variant AnimationNodeBlendSpace2D::get_parameter_default_value(const StringName &p_parameter) const { + return Vector2(); +} + + +void AnimationNodeBlendSpace2D::get_child_nodes(List<ChildNode> *r_child_nodes) { + for(int i=0;i<blend_points_used;i++) { + ChildNode cn; + cn.name=itos(i); + cn.node=blend_points[i].node; + r_child_nodes->push_back(cn); } } void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index) { ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(p_node->get_parent().is_valid()); ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used); if (p_at_index == -1 || p_at_index == blend_points_used) { @@ -32,13 +41,15 @@ void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_ blend_points[p_at_index].node = p_node; blend_points[p_at_index].position = p_position; - blend_points[p_at_index].node->set_parent(this); - blend_points[p_at_index].node->set_tree(get_tree()); + blend_points[p_at_index].node->connect("tree_changed",this,"_tree_changed",varray(),CONNECT_REFERENCE_COUNTED); blend_points_used++; + if (auto_triangles) { trianges_dirty = true; } + emit_signal("tree_changed"); + } void AnimationNodeBlendSpace2D::set_blend_point_position(int p_point, const Vector2 &p_position) { @@ -53,12 +64,13 @@ void AnimationNodeBlendSpace2D::set_blend_point_node(int p_point, const Ref<Anim ERR_FAIL_COND(p_node.is_null()); if (blend_points[p_point].node.is_valid()) { - blend_points[p_point].node->set_parent(NULL); - blend_points[p_point].node->set_tree(NULL); + blend_points[p_point].node->disconnect("tree_changed",this,"_tree_changed"); } blend_points[p_point].node = p_node; - blend_points[p_point].node->set_parent(this); - blend_points[p_point].node->set_tree(get_tree()); + blend_points[p_point].node->connect("tree_changed",this,"_tree_changed",varray(),CONNECT_REFERENCE_COUNTED); + + emit_signal("tree_changed"); + } Vector2 AnimationNodeBlendSpace2D::get_blend_point_position(int p_point) const { ERR_FAIL_INDEX_V(p_point, blend_points_used, Vector2()); @@ -71,8 +83,7 @@ Ref<AnimationRootNode> AnimationNodeBlendSpace2D::get_blend_point_node(int p_poi void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) { ERR_FAIL_INDEX(p_point, blend_points_used); - blend_points[p_point].node->set_parent(NULL); - blend_points[p_point].node->set_tree(NULL); + blend_points[p_point].node->disconnect("tree_changed",this,"_tree_changed"); for (int i = 0; i < triangles.size(); i++) { bool erase = false; @@ -95,6 +106,8 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) { blend_points[i] = blend_points[i + 1]; } blend_points_used--; + emit_signal("tree_changed"); + } int AnimationNodeBlendSpace2D::get_blend_point_count() const { @@ -217,13 +230,6 @@ Vector2 AnimationNodeBlendSpace2D::get_snap() const { return snap; } -void AnimationNodeBlendSpace2D::set_blend_position(const Vector2 &p_pos) { - blend_pos = p_pos; -} -Vector2 AnimationNodeBlendSpace2D::get_blend_position() const { - return blend_pos; -} - void AnimationNodeBlendSpace2D::set_x_label(const String &p_label) { x_label = p_label; } @@ -381,6 +387,8 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { _update_triangles(); + Vector2 blend_pos = get_parameter(blend_position); + if (triangles.size() == 0) return 0; @@ -443,7 +451,7 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { for (int j = 0; j < 3; j++) { if (i == triangle_points[j]) { //blend with the given weight - float t = blend_node(blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false); + float t = blend_node(blend_points[i].name,blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false); if (first || t < mind) { mind = t; first = false; @@ -455,7 +463,7 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { if (!found) { //ignore - blend_node(blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false); + blend_node(blend_points[i].name,blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false); } } return mind; @@ -483,10 +491,19 @@ void AnimationNodeBlendSpace2D::set_auto_triangles(bool p_enable) { } } + bool AnimationNodeBlendSpace2D::get_auto_triangles() const { return auto_triangles; } +Ref<AnimationNode> AnimationNodeBlendSpace2D::get_child_by_name(const StringName &p_name) { + return get_blend_point_node(p_name.operator String().to_int()); +} + +void AnimationNodeBlendSpace2D::_tree_changed() { + emit_signal("tree_changed"); +} + void AnimationNodeBlendSpace2D::_bind_methods() { ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1)); @@ -511,9 +528,6 @@ void AnimationNodeBlendSpace2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_snap", "snap"), &AnimationNodeBlendSpace2D::set_snap); ClassDB::bind_method(D_METHOD("get_snap"), &AnimationNodeBlendSpace2D::get_snap); - ClassDB::bind_method(D_METHOD("set_blend_position", "pos"), &AnimationNodeBlendSpace2D::set_blend_position); - ClassDB::bind_method(D_METHOD("get_blend_position"), &AnimationNodeBlendSpace2D::get_blend_position); - ClassDB::bind_method(D_METHOD("set_x_label", "text"), &AnimationNodeBlendSpace2D::set_x_label); ClassDB::bind_method(D_METHOD("get_x_label"), &AnimationNodeBlendSpace2D::get_x_label); @@ -528,10 +542,12 @@ void AnimationNodeBlendSpace2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_triangles", "enable"), &AnimationNodeBlendSpace2D::set_auto_triangles); ClassDB::bind_method(D_METHOD("get_auto_triangles"), &AnimationNodeBlendSpace2D::get_auto_triangles); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeBlendSpace2D::_tree_changed); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_auto_triangles", "get_auto_triangles"); for (int i = 0; i < MAX_BLEND_POINTS; i++) { - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "_add_blend_point", "get_blend_point_node", i); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i); ADD_PROPERTYI(PropertyInfo(Variant::VECTOR2, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i); } @@ -540,13 +556,15 @@ void AnimationNodeBlendSpace2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "blend_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_blend_position", "get_blend_position"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_x_label", "get_x_label"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_y_label", "get_y_label"); } AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() { + for(int i=0;i<MAX_BLEND_POINTS;i++) { + blend_points[i].name=itos(i); + } auto_triangles = true; blend_points_used = 0; max_space = Vector2(1, 1); @@ -555,12 +573,10 @@ AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() { x_label = "x"; y_label = "y"; trianges_dirty = false; + blend_position = "blend_position"; } AnimationNodeBlendSpace2D::~AnimationNodeBlendSpace2D() { - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_parent(this); - blend_points[i].node->set_tree(get_tree()); - } + } diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h index 74d20b6013..342909531b 100644 --- a/scene/animation/animation_blend_space_2d.h +++ b/scene/animation/animation_blend_space_2d.h @@ -11,6 +11,7 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode { }; struct BlendPoint { + StringName name; Ref<AnimationRootNode> node; Vector2 position; }; @@ -24,7 +25,7 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode { Vector<BlendTriangle> triangles; - Vector2 blend_pos; + StringName blend_position; Vector2 max_space; Vector2 min_space; Vector2 snap; @@ -42,12 +43,19 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode { void _update_triangles(); + void _tree_changed(); + + protected: virtual void _validate_property(PropertyInfo &property) const; static void _bind_methods(); public: - virtual void set_tree(AnimationTree *p_player); + + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); void add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index = -1); void set_blend_point_position(int p_point, const Vector2 &p_position); @@ -72,9 +80,6 @@ public: void set_snap(const Vector2 &p_snap); Vector2 get_snap() const; - void set_blend_position(const Vector2 &p_pos); - Vector2 get_blend_position() const; - void set_x_label(const String &p_label); String get_x_label() const; @@ -89,6 +94,8 @@ public: void set_auto_triangles(bool p_enable); bool get_auto_triangles() const; + virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name); + AnimationNodeBlendSpace2D(); ~AnimationNodeBlendSpace2D(); }; diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 65904410d3..66a9c5babd 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -3,6 +3,7 @@ void AnimationNodeAnimation::set_animation(const StringName &p_name) { animation = p_name; + _change_notify("animation"); } StringName AnimationNodeAnimation::get_animation() const { @@ -13,43 +14,36 @@ float AnimationNodeAnimation::get_playback_time() const { return time; } +Vector<String> (*AnimationNodeAnimation::get_editable_animation_list)() = NULL; + void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const { - if (property.name == "animation") { - AnimationTree *gp = get_tree(); - if (gp && gp->has_node(gp->get_animation_player())) { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); - if (ap) { - List<StringName> names; - ap->get_animation_list(&names); - String anims; - for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - if (E != names.front()) { - anims += ","; - } - anims += String(E->get()); - } - if (anims != String()) { - property.hint = PROPERTY_HINT_ENUM; - property.hint_string = anims; - } + if (property.name == "animation" && get_editable_animation_list) { + Vector<String> names = get_editable_animation_list(); + String anims; + for (int i = 0; i < names.size(); i++) { + + if (i > 0) { + anims += ","; } + anims += String(names[i]); + } + if (anims != String()) { + property.hint = PROPERTY_HINT_ENUM; + property.hint_string = anims; } } - - AnimationRootNode::_validate_property(property); } float AnimationNodeAnimation::process(float p_time, bool p_seek) { - AnimationPlayer *ap = get_player(); + AnimationPlayer *ap = state->player; ERR_FAIL_COND_V(!ap, 0); - Ref<Animation> anim = ap->get_animation(animation); - if (!anim.is_valid()) { + if (!ap->has_animation(animation)) { - Ref<AnimationNodeBlendTree> tree = get_parent(); - if (tree.is_valid()) { + AnimationNodeBlendTree *tree = Object::cast_to<AnimationNodeBlendTree>(parent); + if (tree) { String name = tree->get_node_name(Ref<AnimationNodeAnimation>(this)); make_invalid(vformat(RTR("On BlendTree node '%s', animation not found: '%s'"), name, animation)); @@ -60,6 +54,8 @@ float AnimationNodeAnimation::process(float p_time, bool p_seek) { return 0; } + Ref<Animation> anim = ap->get_animation(animation); + if (p_seek) { time = p_time; step = 0; @@ -108,6 +104,20 @@ AnimationNodeAnimation::AnimationNodeAnimation() { //////////////////////////////////////////////////////// +void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::BOOL, active)); + r_list->push_back(PropertyInfo(Variant::BOOL, prev_active, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::REAL, time, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::REAL, remaining, PROPERTY_HINT_NONE, "", 0)); +} +Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_parameter) const { + if (p_parameter == active || p_parameter == prev_active) { + return false; + } else { + return 0.0; + } +} + void AnimationNodeOneShot::set_fadein_time(float p_time) { fade_in = p_time; @@ -162,18 +172,6 @@ AnimationNodeOneShot::MixMode AnimationNodeOneShot::get_mix_mode() const { return mix; } -void AnimationNodeOneShot::start() { - active = true; - do_start = true; -} -void AnimationNodeOneShot::stop() { - active = false; -} -bool AnimationNodeOneShot::is_active() const { - - return active; -} - String AnimationNodeOneShot::get_caption() const { return "OneShot"; } @@ -184,8 +182,16 @@ bool AnimationNodeOneShot::has_filter() const { float AnimationNodeOneShot::process(float p_time, bool p_seek) { + bool active = get_parameter(this->active); + bool prev_active = get_parameter(this->prev_active); + float time = get_parameter(this->time); + float remaining = get_parameter(this->remaining); + if (!active) { //make it as if this node doesn't exist, pass input 0 by. + if (prev_active) { + set_parameter(this->prev_active, false); + } return blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); } @@ -193,9 +199,12 @@ float AnimationNodeOneShot::process(float p_time, bool p_seek) { if (p_seek) time = p_time; + bool do_start = !prev_active; + if (do_start) { time = 0; os_seek = true; + set_parameter(this->prev_active, true); } float blend; @@ -233,10 +242,15 @@ float AnimationNodeOneShot::process(float p_time, bool p_seek) { if (!p_seek) { time += p_time; remaining = os_rem; - if (remaining <= 0) - active = false; + if (remaining <= 0) { + set_parameter(this->active, false); + set_parameter(this->prev_active, false); + } } + set_parameter(this->time, time); + set_parameter(this->remaining, remaining); + return MAX(main_rem, remaining); } void AnimationNodeOneShot::set_use_sync(bool p_sync) { @@ -269,10 +283,6 @@ void AnimationNodeOneShot::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mix_mode", "mode"), &AnimationNodeOneShot::set_mix_mode); ClassDB::bind_method(D_METHOD("get_mix_mode"), &AnimationNodeOneShot::get_mix_mode); - ClassDB::bind_method(D_METHOD("start"), &AnimationNodeOneShot::start); - ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeOneShot::stop); - ClassDB::bind_method(D_METHOD("is_active"), &AnimationNodeOneShot::is_active); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeOneShot::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeOneShot::is_using_sync); @@ -297,26 +307,27 @@ AnimationNodeOneShot::AnimationNodeOneShot() { add_input("in"); add_input("shot"); - time = 0; fade_in = 0.1; fade_out = 0.1; autorestart = false; autorestart_delay = 1; - autorestart_remaining = 0; + mix = MIX_MODE_BLEND; - active = false; - do_start = false; sync = false; + + active = "active"; + prev_active = "prev_active"; + time = "time"; + remaining = "remaining"; } //////////////////////////////////////////////// -void AnimationNodeAdd2::set_amount(float p_amount) { - amount = p_amount; +void AnimationNodeAdd2::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, add_amount, PROPERTY_HINT_RANGE, "0,1,0.01")); } - -float AnimationNodeAdd2::get_amount() const { - return amount; +Variant AnimationNodeAdd2::get_parameter_default_value(const StringName &p_parameter) const { + return 0; } String AnimationNodeAdd2::get_caption() const { @@ -339,6 +350,7 @@ bool AnimationNodeAdd2::has_filter() const { float AnimationNodeAdd2::process(float p_time, bool p_seek) { + float amount = get_parameter(add_amount); float rem0 = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync); @@ -347,32 +359,27 @@ float AnimationNodeAdd2::process(float p_time, bool p_seek) { void AnimationNodeAdd2::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeAdd2::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeAdd2::get_amount); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd2::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd2::is_using_sync); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeAdd2::AnimationNodeAdd2() { + add_amount = "add_amount"; add_input("in"); add_input("add"); - amount = 0; sync = false; } //////////////////////////////////////////////// -void AnimationNodeAdd3::set_amount(float p_amount) { - amount = p_amount; +void AnimationNodeAdd3::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, add_amount, PROPERTY_HINT_RANGE, "-1,1,0.01")); } - -float AnimationNodeAdd3::get_amount() const { - return amount; +Variant AnimationNodeAdd3::get_parameter_default_value(const StringName &p_parameter) const { + return 0; } String AnimationNodeAdd3::get_caption() const { @@ -395,6 +402,7 @@ bool AnimationNodeAdd3::has_filter() const { float AnimationNodeAdd3::process(float p_time, bool p_seek) { + float amount = get_parameter(add_amount); blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_PASS, !sync); float rem0 = blend_input(1, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_PASS, !sync); @@ -404,39 +412,37 @@ float AnimationNodeAdd3::process(float p_time, bool p_seek) { void AnimationNodeAdd3::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeAdd3::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeAdd3::get_amount); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd3::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd3::is_using_sync); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeAdd3::AnimationNodeAdd3() { + add_amount = "add_amount"; add_input("-add"); add_input("in"); add_input("+add"); - amount = 0; sync = false; } ///////////////////////////////////////////// -void AnimationNodeBlend2::set_amount(float p_amount) { - amount = p_amount; +void AnimationNodeBlend2::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, blend_amount, PROPERTY_HINT_RANGE, "0,1,0.01")); } - -float AnimationNodeBlend2::get_amount() const { - return amount; +Variant AnimationNodeBlend2::get_parameter_default_value(const StringName &p_parameter) const { + return 0; //for blend amount } + String AnimationNodeBlend2::get_caption() const { return "Blend2"; } float AnimationNodeBlend2::process(float p_time, bool p_seek) { + float amount = get_parameter(blend_amount); + float rem0 = blend_input(0, p_time, p_seek, 1.0 - amount, FILTER_BLEND, !sync); float rem1 = blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync); @@ -459,31 +465,25 @@ bool AnimationNodeBlend2::has_filter() const { } void AnimationNodeBlend2::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeBlend2::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeBlend2::get_amount); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlend2::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlend2::is_using_sync); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeBlend2::AnimationNodeBlend2() { + blend_amount = "blend_amount"; add_input("in"); add_input("blend"); sync = false; - - amount = 0; } ////////////////////////////////////// -void AnimationNodeBlend3::set_amount(float p_amount) { - amount = p_amount; +void AnimationNodeBlend3::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, blend_amount, PROPERTY_HINT_RANGE, "-1,1,0.01")); } - -float AnimationNodeBlend3::get_amount() const { - return amount; +Variant AnimationNodeBlend3::get_parameter_default_value(const StringName &p_parameter) const { + return 0; //for blend amount } String AnimationNodeBlend3::get_caption() const { @@ -502,6 +502,7 @@ bool AnimationNodeBlend3::is_using_sync() const { float AnimationNodeBlend3::process(float p_time, bool p_seek) { + float amount = get_parameter(blend_amount); float rem0 = blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_IGNORE, !sync); float rem1 = blend_input(1, p_time, p_seek, 1.0 - ABS(amount), FILTER_IGNORE, !sync); float rem2 = blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_IGNORE, !sync); @@ -511,31 +512,26 @@ float AnimationNodeBlend3::process(float p_time, bool p_seek) { void AnimationNodeBlend3::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeBlend3::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeBlend3::get_amount); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlend3::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlend3::is_using_sync); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeBlend3::AnimationNodeBlend3() { + blend_amount = "blend_amount"; add_input("-blend"); add_input("in"); add_input("+blend"); sync = false; - amount = 0; } ///////////////////////////////// -void AnimationNodeTimeScale::set_scale(float p_scale) { - scale = p_scale; +void AnimationNodeTimeScale::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, scale, PROPERTY_HINT_RANGE, "0,32,0.01,or_greater")); } - -float AnimationNodeTimeScale::get_scale() const { - return scale; +Variant AnimationNodeTimeScale::get_parameter_default_value(const StringName &p_parameter) const { + return 1.0; //initial timescale } String AnimationNodeTimeScale::get_caption() const { @@ -544,6 +540,7 @@ String AnimationNodeTimeScale::get_caption() const { float AnimationNodeTimeScale::process(float p_time, bool p_seek) { + float scale = get_parameter(this->scale); if (p_seek) { return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false); } else { @@ -552,25 +549,19 @@ float AnimationNodeTimeScale::process(float p_time, bool p_seek) { } void AnimationNodeTimeScale::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_scale", "scale"), &AnimationNodeTimeScale::set_scale); - ClassDB::bind_method(D_METHOD("get_scale"), &AnimationNodeTimeScale::get_scale); - - ADD_PROPERTY(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,32,0.01,or_greater"), "set_scale", "get_scale"); } AnimationNodeTimeScale::AnimationNodeTimeScale() { + scale = "scale"; add_input("in"); - scale = 1.0; } //////////////////////////////////// -void AnimationNodeTimeSeek::set_seek_pos(float p_seek_pos) { - seek_pos = p_seek_pos; +void AnimationNodeTimeSeek::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, seek_pos, PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater")); } - -float AnimationNodeTimeSeek::get_seek_pos() const { - return seek_pos; +Variant AnimationNodeTimeSeek::get_parameter_default_value(const StringName &p_parameter) const { + return 1.0; //initial timescale } String AnimationNodeTimeSeek::get_caption() const { @@ -579,11 +570,12 @@ String AnimationNodeTimeSeek::get_caption() const { float AnimationNodeTimeSeek::process(float p_time, bool p_seek) { + float seek_pos = get_parameter(this->seek_pos); if (p_seek) { return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false); } else if (seek_pos >= 0) { float ret = blend_input(0, seek_pos, true, 1.0, FILTER_IGNORE, false); - seek_pos = -1; + set_parameter(this->seek_pos, -1.0); //reset _change_notify("seek_pos"); return ret; } else { @@ -592,19 +584,41 @@ float AnimationNodeTimeSeek::process(float p_time, bool p_seek) { } void AnimationNodeTimeSeek::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_seek_pos", "seek_pos"), &AnimationNodeTimeSeek::set_seek_pos); - ClassDB::bind_method(D_METHOD("get_seek_pos"), &AnimationNodeTimeSeek::get_seek_pos); - - ADD_PROPERTY(PropertyInfo(Variant::REAL, "seek_pos", PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater"), "set_seek_pos", "get_seek_pos"); } + AnimationNodeTimeSeek::AnimationNodeTimeSeek() { add_input("in"); - seek_pos = -1; + seek_pos = "seek_position"; } ///////////////////////////////////////////////// +void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) const { + + String anims; + for (int i = 0; i < enabled_inputs; i++) { + if (i > 0) { + anims += ","; + } + anims += inputs[i].name; + } + + r_list->push_back(PropertyInfo(Variant::INT, current, PROPERTY_HINT_ENUM, anims)); + r_list->push_back(PropertyInfo(Variant::INT, prev_current, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::INT, prev, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::REAL, time, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::REAL, prev_xfading, PROPERTY_HINT_NONE, "", 0)); +} +Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const { + if (p_parameter == time || p_parameter == prev_xfading) { + return 0.0; + } else if (p_parameter == prev || p_parameter == prev_current) { + return -1; + } else { + return 0; + } +} + String AnimationNodeTransition::get_caption() const { return "Transition"; } @@ -650,18 +664,12 @@ String AnimationNodeTransition::get_input_caption(int p_input) const { return inputs[p_input].name; } -void AnimationNodeTransition::set_current(int p_current) { - - if (current == p_current) - return; - ERR_FAIL_INDEX(p_current, enabled_inputs); - +#if 0 Ref<AnimationNodeBlendTree> tree = get_parent(); if (tree.is_valid() && current >= 0) { prev = current; - prev_xfading = xfade; - prev_time = time; + prev_xfading = xfade; time = 0; current = p_current; switched = true; @@ -669,11 +677,8 @@ void AnimationNodeTransition::set_current(int p_current) { } else { current = p_current; } -} +#endif -int AnimationNodeTransition::get_current() const { - return current; -} void AnimationNodeTransition::set_cross_fade_time(float p_fade) { xfade = p_fade; } @@ -684,9 +689,34 @@ float AnimationNodeTransition::get_cross_fade_time() const { float AnimationNodeTransition::process(float p_time, bool p_seek) { + int current = get_parameter(this->current); + int prev = get_parameter(this->prev); + int prev_current = get_parameter(this->prev_current); + + float time = get_parameter(this->time); + float prev_xfading = get_parameter(this->prev_xfading); + + bool switched = current != prev_current; + + if (switched) { + set_parameter(this->prev_current, current); + set_parameter(this->prev, prev_current); + + prev = prev_current; + prev_xfading = xfade; + time = 0; + switched = true; + } + + if (current < 0 || current >= enabled_inputs || prev >= enabled_inputs) { + return 0; + } + + float rem = 0; + if (prev < 0) { // process current animation, check for transition - float rem = blend_input(current, p_time, p_seek, 1.0, FILTER_IGNORE, false); + rem = blend_input(current, p_time, p_seek, 1.0, FILTER_IGNORE, false); if (p_seek) time = p_time; @@ -695,16 +725,13 @@ float AnimationNodeTransition::process(float p_time, bool p_seek) { if (inputs[current].auto_advance && rem <= xfade) { - set_current((current + 1) % enabled_inputs); + set_parameter(this->current, (current + 1) % enabled_inputs); } - return rem; } else { // cross-fading from prev to current float blend = xfade ? (prev_xfading / xfade) : 1; - float rem; - if (!p_seek && switched) { //just switched, seek to start of current rem = blend_input(current, 0, true, 1.0 - blend, FILTER_IGNORE, false); @@ -723,28 +750,19 @@ float AnimationNodeTransition::process(float p_time, bool p_seek) { time += p_time; prev_xfading -= p_time; if (prev_xfading < 0) { - prev = -1; + set_parameter(this->prev, -1); } } - - return rem; } + + set_parameter(this->time, time); + set_parameter(this->prev_xfading, prev_xfading); + + return rem; } void AnimationNodeTransition::_validate_property(PropertyInfo &property) const { - if (property.name == "current" && enabled_inputs > 0) { - property.hint = PROPERTY_HINT_ENUM; - String anims; - for (int i = 0; i < enabled_inputs; i++) { - if (i > 0) { - anims += ","; - } - anims += inputs[i].name; - } - property.hint_string = anims; - } - if (property.name.begins_with("input_")) { String n = property.name.get_slicec('/', 0).get_slicec('_', 1); if (n != "count") { @@ -769,14 +787,10 @@ void AnimationNodeTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_input_caption", "input", "caption"), &AnimationNodeTransition::set_input_caption); ClassDB::bind_method(D_METHOD("get_input_caption", "input"), &AnimationNodeTransition::get_input_caption); - ClassDB::bind_method(D_METHOD("set_current", "index"), &AnimationNodeTransition::set_current); - ClassDB::bind_method(D_METHOD("get_current"), &AnimationNodeTransition::get_current); - ClassDB::bind_method(D_METHOD("set_cross_fade_time", "time"), &AnimationNodeTransition::set_cross_fade_time); ClassDB::bind_method(D_METHOD("get_cross_fade_time"), &AnimationNodeTransition::get_cross_fade_time); ADD_PROPERTY(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,64,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "current", PROPERTY_HINT_RANGE, "0,64,1"), "set_current", "get_current"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01"), "set_cross_fade_time", "get_cross_fade_time"); for (int i = 0; i < MAX_INPUTS; i++) { @@ -786,13 +800,15 @@ void AnimationNodeTransition::_bind_methods() { } AnimationNodeTransition::AnimationNodeTransition() { + + prev_xfading = "prev_xfading"; + prev = "prev"; + time = "time"; + current = "current"; + prev_current = "prev_current"; + ; + enabled_inputs = 0; - xfade = 0; - current = -1; - prev = -1; - prev_time = 0; - prev_xfading = 0; - switched = false; for (int i = 0; i < MAX_INPUTS; i++) { inputs[i].auto_advance = false; inputs[i].name = itos(i + 1); @@ -814,69 +830,102 @@ AnimationNodeOutput::AnimationNodeOutput() { } /////////////////////////////////////////////////////// -void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNode> p_node) { +void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position) { ERR_FAIL_COND(nodes.has(p_name)); ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(p_node->get_parent().is_valid()); - ERR_FAIL_COND(p_node->get_tree() != NULL); ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output); ERR_FAIL_COND(String(p_name).find("/") != -1); - nodes[p_name] = p_node; - p_node->set_parent(this); - p_node->set_tree(get_tree()); + Node n; + n.node = p_node; + n.position = p_position; + n.connections.resize(n.node->get_input_count()); + nodes[p_name] = n; emit_changed(); + emit_signal("tree_changed"); + + p_node->connect("tree_changed", this, "_tree_changed", varray(), CONNECT_REFERENCE_COUNTED); + p_node->connect("changed", this, "_node_changed", varray(p_name), CONNECT_REFERENCE_COUNTED); } Ref<AnimationNode> AnimationNodeBlendTree::get_node(const StringName &p_name) const { ERR_FAIL_COND_V(!nodes.has(p_name), Ref<AnimationNode>()); - return nodes[p_name]; + return nodes[p_name].node; } StringName AnimationNodeBlendTree::get_node_name(const Ref<AnimationNode> &p_node) const { - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - if (E->get() == p_node) { + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + if (E->get().node == p_node) { return E->key(); } } ERR_FAIL_V(StringName()); } + +void AnimationNodeBlendTree::set_node_position(const StringName &p_node, const Vector2 &p_position) { + ERR_FAIL_COND(!nodes.has(p_node)); + nodes[p_node].position = p_position; +} + +Vector2 AnimationNodeBlendTree::get_node_position(const StringName &p_node) const { + ERR_FAIL_COND_V(!nodes.has(p_node), Vector2()); + return nodes[p_node].position; +} + +void AnimationNodeBlendTree::get_child_nodes(List<ChildNode> *r_child_nodes) { + Vector<StringName> ns; + + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + ns.push_back(E->key()); + } + + ns.sort_custom<StringName::AlphCompare>(); + + for (int i = 0; i < ns.size(); i++) { + ChildNode cn; + cn.name = ns[i]; + cn.node = nodes[cn.name].node; + r_child_nodes->push_back(cn); + } +} + bool AnimationNodeBlendTree::has_node(const StringName &p_name) const { return nodes.has(p_name); } +Vector<StringName> AnimationNodeBlendTree::get_node_connection_array(const StringName &p_name) const { + + ERR_FAIL_COND_V(!nodes.has(p_name), Vector<StringName>()); + return nodes[p_name].connections; +} void AnimationNodeBlendTree::remove_node(const StringName &p_name) { ERR_FAIL_COND(!nodes.has(p_name)); ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output); //can't delete output { - //erase node connections - Ref<AnimationNode> node = nodes[p_name]; - for (int i = 0; i < node->get_input_count(); i++) { - node->set_input_connection(i, StringName()); - } - node->set_parent(NULL); - node->set_tree(NULL); + Ref<AnimationNode> node = nodes[p_name].node; + node->disconnect("tree_changed", this, "_tree_changed"); + node->disconnect("changed", this, "_node_changed"); } nodes.erase(p_name); //erase connections to name - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - if (node->get_input_connection(i) == p_name) { - node->set_input_connection(i, StringName()); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().connections.size(); i++) { + if (E->get().connections[i] == p_name) { + E->get().connections.write[i] = StringName(); } } } emit_changed(); + emit_signal("tree_changed"); } void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringName &p_new_name) { @@ -886,18 +935,24 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output); ERR_FAIL_COND(p_new_name == SceneStringNames::get_singleton()->output); + nodes[p_name].node->disconnect("changed", this, "_node_changed"); + nodes[p_new_name] = nodes[p_name]; nodes.erase(p_name); //rename connections - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - if (node->get_input_connection(i) == p_name) { - node->set_input_connection(i, p_new_name); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + + for (int i = 0; i < E->get().connections.size(); i++) { + if (E->get().connections[i] == p_name) { + E->get().connections.write[i] = p_new_name; } } } + //connection must be done with new name + nodes[p_new_name].node->connect("changed", this, "_node_changed", varray(p_new_name), CONNECT_REFERENCE_COUNTED); + + emit_signal("tree_changed"); } void AnimationNodeBlendTree::connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node) { @@ -907,18 +962,18 @@ void AnimationNodeBlendTree::connect_node(const StringName &p_input_node, int p_ ERR_FAIL_COND(p_output_node == SceneStringNames::get_singleton()->output); ERR_FAIL_COND(p_input_node == p_output_node); - Ref<AnimationNode> input = nodes[p_input_node]; - ERR_FAIL_INDEX(p_input_index, input->get_input_count()); + Ref<AnimationNode> input = nodes[p_input_node].node; + ERR_FAIL_INDEX(p_input_index, nodes[p_input_node].connections.size()); - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - StringName output = node->get_input_connection(i); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().connections.size(); i++) { + StringName output = E->get().connections[i]; ERR_FAIL_COND(output == p_output_node); } } - input->set_input_connection(p_input_index, p_output_node); + nodes[p_input_node].connections.write[p_input_index] = p_output_node; + emit_changed(); } @@ -926,20 +981,21 @@ void AnimationNodeBlendTree::disconnect_node(const StringName &p_node, int p_inp ERR_FAIL_COND(!nodes.has(p_node)); - Ref<AnimationNode> input = nodes[p_node]; - ERR_FAIL_INDEX(p_input_index, input->get_input_count()); + Ref<AnimationNode> input = nodes[p_node].node; + ERR_FAIL_INDEX(p_input_index, nodes[p_node].connections.size()); - input->set_input_connection(p_input_index, StringName()); + nodes[p_node].connections.write[p_input_index] = StringName(); } float AnimationNodeBlendTree::get_connection_activity(const StringName &p_input_node, int p_input_index) const { ERR_FAIL_COND_V(!nodes.has(p_input_node), 0); - Ref<AnimationNode> input = nodes[p_input_node]; - ERR_FAIL_INDEX_V(p_input_index, input->get_input_count(), 0); + Ref<AnimationNode> input = nodes[p_input_node].node; + ERR_FAIL_INDEX_V(p_input_index, nodes[p_input_node].connections.size(), 0); - return input->get_input_activity(p_input_index); + //return input->get_input_activity(p_input_index); + return 0; } AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node) const { @@ -956,20 +1012,19 @@ AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node return CONNECTION_ERROR_SAME_NODE; } - Ref<AnimationNode> input = nodes[p_input_node]; + Ref<AnimationNode> input = nodes[p_input_node].node; - if (p_input_index < 0 || p_input_index >= input->get_input_count()) { + if (p_input_index < 0 || p_input_index >= nodes[p_input_node].connections.size()) { return CONNECTION_ERROR_NO_INPUT_INDEX; } - if (input->get_input_connection(p_input_index) != StringName()) { + if (nodes[p_input_node].connections[p_input_index] != StringName()) { return CONNECTION_ERROR_CONNECTION_EXISTS; } - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - StringName output = node->get_input_connection(i); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().connections.size(); i++) { + StringName output = E->get().connections[i]; if (output == p_output_node) { return CONNECTION_ERROR_CONNECTION_EXISTS; } @@ -980,10 +1035,9 @@ AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node void AnimationNodeBlendTree::get_node_connections(List<NodeConnection> *r_connections) const { - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - StringName output = node->get_input_connection(i); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().connections.size(); i++) { + StringName output = E->get().connections[i]; if (output != StringName()) { NodeConnection nc; nc.input_node = E->key(); @@ -1001,13 +1055,13 @@ String AnimationNodeBlendTree::get_caption() const { float AnimationNodeBlendTree::process(float p_time, bool p_seek) { - Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output]; - return blend_node(output, p_time, p_seek, 1.0); + Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output].node; + return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, 1.0); } void AnimationNodeBlendTree::get_node_list(List<StringName> *r_list) { - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { r_list->push_back(E->key()); } } @@ -1022,14 +1076,8 @@ Vector2 AnimationNodeBlendTree::get_graph_offset() const { return graph_offset; } -void AnimationNodeBlendTree::set_tree(AnimationTree *p_player) { - - AnimationNode::set_tree(p_player); - - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - node->set_tree(p_player); - } +Ref<AnimationNode> AnimationNodeBlendTree::get_child_by_name(const StringName &p_name) { + return get_node(p_name); } bool AnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_value) { @@ -1051,7 +1099,7 @@ bool AnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_val if (what == "position") { if (nodes.has(node_name)) { - nodes[node_name]->set_position(p_value); + nodes[node_name].position = p_value; } return true; } @@ -1078,7 +1126,7 @@ bool AnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_ret) cons if (what == "node") { if (nodes.has(node_name)) { - r_ret = nodes[node_name]; + r_ret = nodes[node_name].node; return true; } } @@ -1086,7 +1134,7 @@ bool AnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_ret) cons if (what == "position") { if (nodes.has(node_name)) { - r_ret = nodes[node_name]->get_position(); + r_ret = nodes[node_name].position; return true; } } @@ -1113,7 +1161,7 @@ bool AnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_ret) cons void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) const { List<StringName> names; - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { names.push_back(E->key()); } names.sort_custom<StringName::AlphCompare>(); @@ -1121,7 +1169,7 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons for (List<StringName>::Element *E = names.front(); E; E = E->next()) { String name = E->get(); if (name != "output") { - p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR)); } p_list->push_back(PropertyInfo(Variant::VECTOR2, "nodes/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } @@ -1129,9 +1177,19 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } +void AnimationNodeBlendTree::_tree_changed() { + emit_signal("tree_changed"); +} + +void AnimationNodeBlendTree::_node_changed(const StringName &p_node) { + + ERR_FAIL_COND(!nodes.has(p_node)); + nodes[p_node].connections.resize(nodes[p_node].node->get_input_count()); +} + void AnimationNodeBlendTree::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_node", "name", "node"), &AnimationNodeBlendTree::add_node); + ClassDB::bind_method(D_METHOD("add_node", "name", "node", "position"), &AnimationNodeBlendTree::add_node, DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("get_node", "name"), &AnimationNodeBlendTree::get_node); ClassDB::bind_method(D_METHOD("remove_node", "name"), &AnimationNodeBlendTree::remove_node); ClassDB::bind_method(D_METHOD("rename_node", "name", "new_name"), &AnimationNodeBlendTree::rename_node); @@ -1139,9 +1197,15 @@ void AnimationNodeBlendTree::_bind_methods() { ClassDB::bind_method(D_METHOD("connect_node", "input_node", "input_index", "output_node"), &AnimationNodeBlendTree::connect_node); ClassDB::bind_method(D_METHOD("disconnect_node", "input_node", "input_index"), &AnimationNodeBlendTree::disconnect_node); + ClassDB::bind_method(D_METHOD("set_node_position", "name", "position"), &AnimationNodeBlendTree::set_node_position); + ClassDB::bind_method(D_METHOD("get_node_position", "name"), &AnimationNodeBlendTree::get_node_position); + ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeBlendTree::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeBlendTree::get_graph_offset); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeBlendTree::_tree_changed); + ClassDB::bind_method(D_METHOD("_node_changed", "node"), &AnimationNodeBlendTree::_node_changed); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset"); BIND_CONSTANT(CONNECTION_OK); @@ -1156,15 +1220,12 @@ AnimationNodeBlendTree::AnimationNodeBlendTree() { Ref<AnimationNodeOutput> output; output.instance(); - output->set_position(Vector2(300, 150)); - output->set_parent(this); - nodes["output"] = output; + Node n; + n.node = output; + n.position = Vector2(300, 150); + n.connections.resize(1); + nodes["output"] = n; } AnimationNodeBlendTree::~AnimationNodeBlendTree() { - - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - E->get()->set_parent(NULL); - E->get()->set_tree(NULL); - } } diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index e86cc2e823..37bd45c74a 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -20,6 +20,8 @@ protected: static void _bind_methods(); public: + static Vector<String> (*get_editable_animation_list)(); + virtual String get_caption() const; virtual float process(float p_time, bool p_seek); @@ -41,8 +43,6 @@ public: }; private: - bool active; - bool do_start; float fade_in; float fade_out; @@ -51,15 +51,25 @@ private: float autorestart_random_delay; MixMode mix; - float time; - float remaining; - float autorestart_remaining; bool sync; + /* bool active; + bool do_start; + float time; + float remaining;*/ + + StringName active; + StringName prev_active; + StringName time; + StringName remaining; + protected: static void _bind_methods(); public: + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + virtual String get_caption() const; void set_fadein_time(float p_time); @@ -79,10 +89,6 @@ public: void set_mix_mode(MixMode p_mix); MixMode get_mix_mode() const; - void start(); - void stop(); - bool is_active() const; - void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -97,17 +103,17 @@ VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode) class AnimationNodeAdd2 : public AnimationNode { GDCLASS(AnimationNodeAdd2, AnimationNode); - float amount; + StringName add_amount; bool sync; protected: static void _bind_methods(); public: - virtual String get_caption() const; + void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_amount(float p_amount); - float get_amount() const; + virtual String get_caption() const; void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -121,17 +127,17 @@ public: class AnimationNodeAdd3 : public AnimationNode { GDCLASS(AnimationNodeAdd3, AnimationNode); - float amount; + StringName add_amount; bool sync; protected: static void _bind_methods(); public: - virtual String get_caption() const; + void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_amount(float p_amount); - float get_amount() const; + virtual String get_caption() const; void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -145,19 +151,19 @@ public: class AnimationNodeBlend2 : public AnimationNode { GDCLASS(AnimationNodeBlend2, AnimationNode); - float amount; + StringName blend_amount; bool sync; protected: static void _bind_methods(); public: + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + virtual String get_caption() const; virtual float process(float p_time, bool p_seek); - void set_amount(float p_amount); - float get_amount() const; - void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -168,17 +174,17 @@ public: class AnimationNodeBlend3 : public AnimationNode { GDCLASS(AnimationNodeBlend3, AnimationNode); - float amount; + StringName blend_amount; bool sync; protected: static void _bind_methods(); public: - virtual String get_caption() const; + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_amount(float p_amount); - float get_amount() const; + virtual String get_caption() const; void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -190,16 +196,16 @@ public: class AnimationNodeTimeScale : public AnimationNode { GDCLASS(AnimationNodeTimeScale, AnimationNode); - float scale; + StringName scale; protected: static void _bind_methods(); public: - virtual String get_caption() const; + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_scale(float p_scale); - float get_scale() const; + virtual String get_caption() const; float process(float p_time, bool p_seek); @@ -209,16 +215,16 @@ public: class AnimationNodeTimeSeek : public AnimationNode { GDCLASS(AnimationNodeTimeSeek, AnimationNode); - float seek_pos; + StringName seek_pos; protected: static void _bind_methods(); public: - virtual String get_caption() const; + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_seek_pos(float p_sec); - float get_seek_pos() const; + virtual String get_caption() const; float process(float p_time, bool p_seek); @@ -241,13 +247,18 @@ class AnimationNodeTransition : public AnimationNode { InputData inputs[MAX_INPUTS]; int enabled_inputs; - float prev_time; + /* float prev_xfading; int prev; - bool switched; - float time; int current; + int prev_current; */ + + StringName prev_xfading; + StringName prev; + StringName time; + StringName current; + StringName prev_current; float xfade; @@ -258,6 +269,9 @@ protected: void _validate_property(PropertyInfo &property) const; public: + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + virtual String get_caption() const; void set_enabled_inputs(int p_inputs); @@ -269,9 +283,6 @@ public: void set_input_caption(int p_input, const String &p_name); String get_input_caption(int p_input) const; - void set_current(int p_current); - int get_current() const; - void set_cross_fade_time(float p_fade); float get_cross_fade_time() const; @@ -293,10 +304,19 @@ public: class AnimationNodeBlendTree : public AnimationRootNode { GDCLASS(AnimationNodeBlendTree, AnimationRootNode) - Map<StringName, Ref<AnimationNode> > nodes; + struct Node { + Ref<AnimationNode> node; + Vector2 position; + Vector<StringName> connections; + }; + + Map<StringName, Node> nodes; Vector2 graph_offset; + void _tree_changed(); + void _node_changed(const StringName &p_node); + protected: static void _bind_methods(); bool _set(const StringName &p_name, const Variant &p_value); @@ -314,12 +334,18 @@ public: //no need to check for cycles due to tree topology }; - void add_node(const StringName &p_name, Ref<AnimationNode> p_node); + void add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position = Vector2()); Ref<AnimationNode> get_node(const StringName &p_name) const; void remove_node(const StringName &p_name); void rename_node(const StringName &p_name, const StringName &p_new_name); bool has_node(const StringName &p_name) const; StringName get_node_name(const Ref<AnimationNode> &p_node) const; + Vector<StringName> get_node_connection_array(const StringName &p_name) const; + + void set_node_position(const StringName &p_node, const Vector2 &p_position); + Vector2 get_node_position(const StringName &p_node) const; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); void connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node); void disconnect_node(const StringName &p_node, int p_input_index); @@ -342,7 +368,8 @@ public: void set_graph_offset(const Vector2 &p_graph_offset); Vector2 get_graph_offset() const; - virtual void set_tree(AnimationTree *p_player); + virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name); + AnimationNodeBlendTree(); ~AnimationNodeBlendTree(); }; diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index f478112a36..cbbed2efcc 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -20,6 +20,26 @@ bool AnimationNodeStateMachineTransition::has_auto_advance() const { return auto_advance; } +void AnimationNodeStateMachineTransition::set_advance_condition(const StringName &p_condition) { + String cs = p_condition; + ERR_FAIL_COND(cs.find("/") != -1 || cs.find(":") != -1); + advance_condition = p_condition; + if (cs != String()) { + advance_condition_name = "conditions/" + cs; + } else { + advance_condition_name = StringName(); + } + emit_signal("advance_condition_changed"); +} + +StringName AnimationNodeStateMachineTransition::get_advance_condition() const { + return advance_condition; +} + +StringName AnimationNodeStateMachineTransition::get_advance_condition_name() const { + return advance_condition_name; +} + void AnimationNodeStateMachineTransition::set_xfade_time(float p_xfade) { ERR_FAIL_COND(p_xfade < 0); @@ -56,6 +76,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_advance", "auto_advance"), &AnimationNodeStateMachineTransition::set_auto_advance); ClassDB::bind_method(D_METHOD("has_auto_advance"), &AnimationNodeStateMachineTransition::has_auto_advance); + ClassDB::bind_method(D_METHOD("set_advance_condition", "name"), &AnimationNodeStateMachineTransition::set_advance_condition); + ClassDB::bind_method(D_METHOD("get_advance_condition"), &AnimationNodeStateMachineTransition::get_advance_condition); + ClassDB::bind_method(D_METHOD("set_xfade_time", "secs"), &AnimationNodeStateMachineTransition::set_xfade_time); ClassDB::bind_method(D_METHOD("get_xfade_time"), &AnimationNodeStateMachineTransition::get_xfade_time); @@ -67,6 +90,7 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,AtEnd"), "set_switch_mode", "get_switch_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_advance"), "set_auto_advance", "has_auto_advance"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "advance_condition"), "set_advance_condition", "get_advance_condition"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01"), "set_xfade_time", "get_xfade_time"); ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); @@ -74,6 +98,8 @@ void AnimationNodeStateMachineTransition::_bind_methods() { BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE); BIND_ENUM_CONSTANT(SWITCH_MODE_SYNC); BIND_ENUM_CONSTANT(SWITCH_MODE_AT_END); + + ADD_SIGNAL(MethodInfo("advance_condition_changed")); } AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { @@ -85,277 +111,241 @@ AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { priority = 1; } -/////////////////////////////////////////////////////// -void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node) { - - ERR_FAIL_COND(states.has(p_name)); - ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(p_node->get_parent().is_valid()); - ERR_FAIL_COND(p_node->get_tree() != NULL); - ERR_FAIL_COND(String(p_name).find("/") != -1); - states[p_name] = p_node; +//////////////////////////////////////////////////////// - p_node->set_parent(this); - p_node->set_tree(get_tree()); +void AnimationNodeStateMachinePlayback::travel(const StringName &p_state) { - emit_changed(); + start_request_travel = true; + start_request = p_state; + stop_request = false; } -Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const { - - ERR_FAIL_COND_V(!states.has(p_name), Ref<AnimationNode>()); - - return states[p_name]; +void AnimationNodeStateMachinePlayback::start(const StringName &p_state) { + start_request_travel = false; + start_request = p_state; + stop_request = false; + print_line("wants start"); } +void AnimationNodeStateMachinePlayback::stop() { -StringName AnimationNodeStateMachine::get_node_name(const Ref<AnimationNode> &p_node) const { - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { - if (E->get() == p_node) { - return E->key(); - } - } - - ERR_FAIL_V(StringName()); + stop_request = true; } - -bool AnimationNodeStateMachine::has_node(const StringName &p_name) const { - return states.has(p_name); +bool AnimationNodeStateMachinePlayback::is_playing() const { + return playing; +} +StringName AnimationNodeStateMachinePlayback::get_current_node() const { + return current; +} +StringName AnimationNodeStateMachinePlayback::get_blend_from_node() const { + return fading_from; +} +Vector<StringName> AnimationNodeStateMachinePlayback::get_travel_path() const { + return path; +} +float AnimationNodeStateMachinePlayback::get_current_play_pos() const { + return pos_current; +} +float AnimationNodeStateMachinePlayback::get_current_length() const { + return len_current; } -void AnimationNodeStateMachine::remove_node(const StringName &p_name) { - - ERR_FAIL_COND(!states.has(p_name)); - - { - //erase node connections - Ref<AnimationNode> node = states[p_name]; - for (int i = 0; i < node->get_input_count(); i++) { - node->set_input_connection(i, StringName()); - } - node->set_parent(NULL); - node->set_tree(NULL); - } - states.erase(p_name); - path.erase(p_name); +bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *sm, const StringName &p_travel) { - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_name || transitions[i].to == p_name) { - transitions.remove(i); - i--; - } - } + ERR_FAIL_COND_V(!playing, false); + ERR_FAIL_COND_V(!sm->states.has(p_travel), false); + ERR_FAIL_COND_V(!sm->states.has(current), false); - if (start_node == p_name) { - start_node = StringName(); - } + path.clear(); //a new one will be needed - if (end_node == p_name) { - end_node = StringName(); - } + if (current == p_travel) + return true; //nothing to do - if (playing && current == p_name) { - stop(); - } - emit_changed(); -} + loops_current = 0; // reset loops, so fade does not happen immediately -void AnimationNodeStateMachine::rename_node(const StringName &p_name, const StringName &p_new_name) { + Vector2 current_pos = sm->states[current].position; + Vector2 target_pos = sm->states[p_travel].position; - ERR_FAIL_COND(!states.has(p_name)); - ERR_FAIL_COND(states.has(p_new_name)); + Map<StringName, AStarCost> cost_map; - states[p_new_name] = states[p_name]; - states.erase(p_name); + List<int> open_list; - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_name) { - transitions.write[i].from = p_new_name; - } + //build open list + for (int i = 0; i < sm->transitions.size(); i++) { + if (sm->transitions[i].from == current) { + open_list.push_back(i); + float cost = sm->states[sm->transitions[i].to].position.distance_to(current_pos); + cost *= sm->transitions[i].transition->get_priority(); + AStarCost ap; + ap.prev = current; + ap.distance = cost; + cost_map[sm->transitions[i].to] = ap; - if (transitions[i].to == p_name) { - transitions.write[i].to = p_new_name; + if (sm->transitions[i].to == p_travel) { //prematurely found it! :D + path.push_back(p_travel); + return true; + } } } - if (start_node == p_name) { - start_node = p_new_name; - } + //begin astar + bool found_route = false; + while (!found_route) { - if (end_node == p_name) { - end_node = p_new_name; - } + if (open_list.size() == 0) { + return false; //no path found + } - if (playing && current == p_name) { - current = p_new_name; - } + //find the last cost transition + List<int>::Element *least_cost_transition = NULL; + float least_cost = 1e20; - path.clear(); //clear path -} + for (List<int>::Element *E = open_list.front(); E; E = E->next()) { -void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { + float cost = cost_map[sm->transitions[E->get()].to].distance; + cost += sm->states[sm->transitions[E->get()].to].position.distance_to(target_pos); - List<StringName> nodes; - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { - nodes.push_back(E->key()); - } - nodes.sort_custom<StringName::AlphCompare>(); + if (cost < least_cost) { + least_cost_transition = E; + } + } - for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) { - r_nodes->push_back(E->get()); - } -} + StringName transition_prev = sm->transitions[least_cost_transition->get()].from; + StringName transition = sm->transitions[least_cost_transition->get()].to; -bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const StringName &p_to) const { + for (int i = 0; i < sm->transitions.size(); i++) { + if (sm->transitions[i].from != transition || sm->transitions[i].to == transition_prev) { + continue; //not interested on those + } - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) - return true; - } - return false; -} + float distance = sm->states[sm->transitions[i].from].position.distance_to(sm->states[sm->transitions[i].to].position); + distance *= sm->transitions[i].transition->get_priority(); + distance += cost_map[sm->transitions[i].from].distance; -int AnimationNodeStateMachine::find_transition(const StringName &p_from, const StringName &p_to) const { + if (cost_map.has(sm->transitions[i].to)) { + //oh this was visited already, can we win the cost? + if (distance < cost_map[sm->transitions[i].to].distance) { + cost_map[sm->transitions[i].to].distance = distance; + cost_map[sm->transitions[i].to].prev = sm->transitions[i].from; + } + } else { + //add to open list + AStarCost ac; + ac.prev = sm->transitions[i].from; + ac.distance = distance; + cost_map[sm->transitions[i].to] = ac; - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) - return i; - } - return -1; -} + open_list.push_back(i); -void AnimationNodeStateMachine::add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition) { + if (sm->transitions[i].to == p_travel) { + found_route = true; + break; + } + } + } - ERR_FAIL_COND(p_from == p_to); - ERR_FAIL_COND(!states.has(p_from)); - ERR_FAIL_COND(!states.has(p_to)); - ERR_FAIL_COND(p_transition.is_null()); + if (found_route) { + break; + } - for (int i = 0; i < transitions.size(); i++) { - ERR_FAIL_COND(transitions[i].from == p_from && transitions[i].to == p_to); + open_list.erase(least_cost_transition); } - Transition tr; - tr.from = p_from; - tr.to = p_to; - tr.transition = p_transition; - - transitions.push_back(tr); -} - -Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_transition(int p_transition) const { - ERR_FAIL_INDEX_V(p_transition, transitions.size(), Ref<AnimationNodeStateMachineTransition>()); - return transitions[p_transition].transition; -} -StringName AnimationNodeStateMachine::get_transition_from(int p_transition) const { + //make path + StringName at = p_travel; + while (at != current) { + path.push_back(at); + at = cost_map[at].prev; + } - ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); - return transitions[p_transition].from; -} -StringName AnimationNodeStateMachine::get_transition_to(int p_transition) const { + path.invert(); - ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); - return transitions[p_transition].to; + return true; } -int AnimationNodeStateMachine::get_transition_count() const { - - return transitions.size(); -} -void AnimationNodeStateMachine::remove_transition(const StringName &p_from, const StringName &p_to) { +float AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *sm, float p_time, bool p_seek) { - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) { - transitions.remove(i); - return; + //if not playing and it can restart, then restart + if (!playing && start_request == StringName()) { + if (!stop_request && sm->start_node) { + start(sm->start_node); + } else { + return 0; } } - if (playing) { - path.clear(); - } -} - -void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) { - - transitions.remove(p_transition); - if (playing) { - path.clear(); + if (playing && stop_request) { + stop_request = false; + playing = false; + return 0; } -} - -void AnimationNodeStateMachine::set_start_node(const StringName &p_node) { - - ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); - start_node = p_node; -} - -String AnimationNodeStateMachine::get_start_node() const { - - return start_node; -} - -void AnimationNodeStateMachine::set_end_node(const StringName &p_node) { - - ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); - end_node = p_node; -} - -String AnimationNodeStateMachine::get_end_node() const { - - return end_node; -} -void AnimationNodeStateMachine::set_graph_offset(const Vector2 &p_offset) { - graph_offset = p_offset; -} + bool play_start = false; -Vector2 AnimationNodeStateMachine::get_graph_offset() const { - return graph_offset; -} + if (start_request != StringName()) { -float AnimationNodeStateMachine::process(float p_time, bool p_seek) { + if (start_request_travel) { + if (!playing) { + start_request = StringName(); + ERR_EXPLAIN("Can't travel to '" + String(start_request) + "' if state machine is not active."); + ERR_FAIL_V(0); + } - //if not playing and it can restart, then restart - if (!playing) { - if (start_node) { - start(start_node); + if (!_travel(sm, start_request)) { + //cant travel, then teleport + path.clear(); + current = start_request; + } } else { - return 0; + path.clear(); + current = start_request; + playing = true; + play_start = true; } + + start_request = StringName(); //clear start request } bool do_start = (p_seek && p_time == 0) || play_start || current == StringName(); if (do_start) { - if (start_node != StringName() && p_seek && p_time == 0) { - current = start_node; + if (sm->start_node != StringName() && p_seek && p_time == 0) { + current = sm->start_node; } - len_current = blend_node(states[current], 0, true, 1.0, FILTER_IGNORE, false); + len_current = sm->blend_node(current, sm->states[current].node, 0, true, 1.0, AnimationNode::FILTER_IGNORE, false); pos_current = 0; loops_current = 0; play_start = false; } + if (!sm->states.has(current)) { + playing = false; //current does not exist + current = StringName(); + return 0; + } float fade_blend = 1.0; if (fading_from != StringName()) { - if (!p_seek) { - fading_pos += p_time; - } - fade_blend = MIN(1.0, fading_pos / fading_time); - if (fade_blend >= 1.0) { + if (!sm->states.has(fading_from)) { fading_from = StringName(); + } else { + if (!p_seek) { + fading_pos += p_time; + } + fade_blend = MIN(1.0, fading_pos / fading_time); + if (fade_blend >= 1.0) { + fading_from = StringName(); + } } } - float rem = blend_node(states[current], p_time, p_seek, fade_blend, FILTER_IGNORE, false); + float rem = sm->blend_node(current, sm->states[current].node, p_time, p_seek, fade_blend, AnimationNode::FILTER_IGNORE, false); if (fading_from != StringName()) { - blend_node(states[fading_from], p_time, p_seek, 1.0 - fade_blend, FILTER_IGNORE, false); + sm->blend_node(current, sm->states[fading_from].node, p_time, p_seek, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false); } //guess playback position @@ -380,29 +370,39 @@ float AnimationNodeStateMachine::process(float p_time, bool p_seek) { if (path.size()) { - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == current && transitions[i].to == path[0]) { - next_xfade = transitions[i].transition->get_xfade_time(); - switch_mode = transitions[i].transition->get_switch_mode(); + for (int i = 0; i < sm->transitions.size(); i++) { + if (sm->transitions[i].from == current && sm->transitions[i].to == path[0]) { + next_xfade = sm->transitions[i].transition->get_xfade_time(); + switch_mode = sm->transitions[i].transition->get_switch_mode(); next = path[0]; } } } else { float priority_best = 1e20; int auto_advance_to = -1; - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == current && transitions[i].transition->has_auto_advance()) { + for (int i = 0; i < sm->transitions.size(); i++) { + + bool auto_advance = false; + if (sm->transitions[i].transition->has_auto_advance()) { + auto_advance = true; + } + StringName advance_condition_name = sm->transitions[i].transition->get_advance_condition_name(); + if (advance_condition_name != StringName() && bool(sm->get_parameter(advance_condition_name))) { + auto_advance = true; + } + + if (sm->transitions[i].from == current && sm->transitions[i].transition->has_auto_advance()) { - if (transitions[i].transition->get_priority() < priority_best) { + if (sm->transitions[i].transition->get_priority() < priority_best) { auto_advance_to = i; } } } if (auto_advance_to != -1) { - next = transitions[auto_advance_to].to; - next_xfade = transitions[auto_advance_to].transition->get_xfade_time(); - switch_mode = transitions[auto_advance_to].transition->get_switch_mode(); + next = sm->transitions[auto_advance_to].to; + next_xfade = sm->transitions[auto_advance_to].transition->get_xfade_time(); + switch_mode = sm->transitions[auto_advance_to].transition->get_switch_mode(); } } @@ -437,12 +437,12 @@ float AnimationNodeStateMachine::process(float p_time, bool p_seek) { } current = next; if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) { - len_current = blend_node(states[current], 0, true, 0, FILTER_IGNORE, false); + len_current = sm->blend_node(current, sm->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); pos_current = MIN(pos_current, len_current); - blend_node(states[current], pos_current, true, 0, FILTER_IGNORE, false); + sm->blend_node(current, sm->states[current].node, pos_current, true, 0, AnimationNode::FILTER_IGNORE, false); } else { - len_current = blend_node(states[current], 0, true, 0, FILTER_IGNORE, false); + len_current = sm->blend_node(current, sm->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); pos_current = 0; } @@ -453,169 +453,321 @@ float AnimationNodeStateMachine::process(float p_time, bool p_seek) { //compute time left for transitions by using the end node - if (end_node != StringName() && end_node != current) { + if (sm->end_node != StringName() && sm->end_node != current) { - rem = blend_node(states[end_node], 0, true, 0, FILTER_IGNORE, false); + rem = sm->blend_node(current, sm->states[sm->end_node].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); } return rem; } -bool AnimationNodeStateMachine::travel(const StringName &p_state) { - ERR_FAIL_COND_V(!playing, false); - ERR_FAIL_COND_V(!states.has(p_state), false); - ERR_FAIL_COND_V(!states.has(current), false); +void AnimationNodeStateMachinePlayback::_bind_methods() { - path.clear(); //a new one will be needed + ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachinePlayback::travel); + ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachinePlayback::start); + ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachinePlayback::stop); + ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachinePlayback::is_playing); + ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachinePlayback::get_current_node); + ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachinePlayback::get_travel_path); +} - if (current == p_state) - return true; //nothing to do +AnimationNodeStateMachinePlayback::AnimationNodeStateMachinePlayback() { + set_local_to_scene(true); //only one per instanced scene - loops_current = 0; // reset loops, so fade does not happen immediately + playing = false; + len_current = 0; + fading_time = 0; + stop_request = false; +} - Vector2 current_pos = states[current]->get_position(); - Vector2 target_pos = states[p_state]->get_position(); +/////////////////////////////////////////////////////// - Map<StringName, AStarCost> cost_map; +void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::OBJECT, playback, PROPERTY_HINT_RESOURCE_TYPE, "AnimationNodeStateMachinePlayback", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + List<StringName> advance_conditions; + for (int i = 0; i < transitions.size(); i++) { + StringName ac = transitions[i].transition->get_advance_condition_name(); + if (ac != StringName() && advance_conditions.find(ac) == NULL) { + advance_conditions.push_back(ac); + } + } - List<int> open_list; + advance_conditions.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = advance_conditions.front(); E; E = E->next()) { + r_list->push_back(PropertyInfo(Variant::BOOL, E->get())); + } +} - //build open list - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == current) { - open_list.push_back(i); - float cost = states[transitions[i].to]->get_position().distance_to(current_pos); - cost *= transitions[i].transition->get_priority(); - AStarCost ap; - ap.prev = current; - ap.distance = cost; - cost_map[transitions[i].to] = ap; +Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName &p_parameter) const { - if (transitions[i].to == p_state) { //prematurely found it! :D - path.push_back(p_state); - return true; - } - } + if (p_parameter == playback) { + Ref<AnimationNodeStateMachinePlayback> p; + p.instance(); + return p; + } else { + return false; //advance condition } +} - //begin astar - bool found_route = false; - while (!found_route) { +void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position) { - if (open_list.size() == 0) { - return false; //no path found + ERR_FAIL_COND(states.has(p_name)); + ERR_FAIL_COND(p_node.is_null()); + ERR_FAIL_COND(String(p_name).find("/") != -1); + + State state; + state.node = p_node; + state.position = p_position; + + states[p_name] = state; + + emit_changed(); + emit_signal("tree_changed"); + + p_node->connect("tree_changed", this, "_tree_changed", varray(), CONNECT_REFERENCE_COUNTED); +} + +Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const { + + ERR_FAIL_COND_V(!states.has(p_name), Ref<AnimationNode>()); + + return states[p_name].node; +} + +StringName AnimationNodeStateMachine::get_node_name(const Ref<AnimationNode> &p_node) const { + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { + if (E->get().node == p_node) { + return E->key(); } + } - //find the last cost transition - List<int>::Element *least_cost_transition = NULL; - float least_cost = 1e20; + ERR_FAIL_V(StringName()); +} - for (List<int>::Element *E = open_list.front(); E; E = E->next()) { +void AnimationNodeStateMachine::get_child_nodes(List<ChildNode> *r_child_nodes) { + Vector<StringName> nodes; - float cost = cost_map[transitions[E->get()].to].distance; - cost += states[transitions[E->get()].to]->get_position().distance_to(target_pos); + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { + nodes.push_back(E->key()); + } - if (cost < least_cost) { - least_cost_transition = E; - } + nodes.sort_custom<StringName::AlphCompare>(); + + for (int i = 0; i < nodes.size(); i++) { + ChildNode cn; + cn.name = nodes[i]; + cn.node = states[cn.name].node; + r_child_nodes->push_back(cn); + } +} + +bool AnimationNodeStateMachine::has_node(const StringName &p_name) const { + return states.has(p_name); +} +void AnimationNodeStateMachine::remove_node(const StringName &p_name) { + + ERR_FAIL_COND(!states.has(p_name)); + + { + Ref<AnimationNode> node = states[p_name].node; + node->disconnect("tree_changed", this, "_tree_changed"); + } + + states.erase(p_name); + //path.erase(p_name); + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_name || transitions[i].to == p_name) { + transitions.write[i].transition->disconnect("advance_condition_changed", this, "_tree_changed"); + transitions.remove(i); + i--; } + } - StringName transition_prev = transitions[least_cost_transition->get()].from; - StringName transition = transitions[least_cost_transition->get()].to; + if (start_node == p_name) { + start_node = StringName(); + } - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from != transition || transitions[i].to == transition_prev) { - continue; //not interested on those - } + if (end_node == p_name) { + end_node = StringName(); + } - float distance = states[transitions[i].from]->get_position().distance_to(states[transitions[i].to]->get_position()); - distance *= transitions[i].transition->get_priority(); - distance += cost_map[transitions[i].from].distance; + /*if (playing && current == p_name) { + stop(); + }*/ - if (cost_map.has(transitions[i].to)) { - //oh this was visited already, can we win the cost? - if (distance < cost_map[transitions[i].to].distance) { - cost_map[transitions[i].to].distance = distance; - cost_map[transitions[i].to].prev = transitions[i].from; - } - } else { - //add to open list - AStarCost ac; - ac.prev = transitions[i].from; - ac.distance = distance; - cost_map[transitions[i].to] = ac; + emit_changed(); + emit_signal("tree_changed"); +} - open_list.push_back(i); +void AnimationNodeStateMachine::rename_node(const StringName &p_name, const StringName &p_new_name) { - if (transitions[i].to == p_state) { - found_route = true; - break; - } - } + ERR_FAIL_COND(!states.has(p_name)); + ERR_FAIL_COND(states.has(p_new_name)); + + states[p_new_name] = states[p_name]; + states.erase(p_name); + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_name) { + transitions.write[i].from = p_new_name; } - if (found_route) { - break; + if (transitions[i].to == p_name) { + transitions.write[i].to = p_new_name; } + } - open_list.erase(least_cost_transition); + if (start_node == p_name) { + start_node = p_new_name; } - //make path - StringName at = p_state; - while (at != current) { - path.push_back(at); - at = cost_map[at].prev; + if (end_node == p_name) { + end_node = p_new_name; } - path.invert(); + /*if (playing && current == p_name) { + current = p_new_name; + }*/ - return true; + //path.clear(); //clear path + emit_signal("tree_changed"); } -void AnimationNodeStateMachine::start(const StringName &p_state) { +void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { - ERR_FAIL_COND(!states.has(p_state)); - path.clear(); - current = p_state; - playing = true; - play_start = true; + List<StringName> nodes; + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { + nodes.push_back(E->key()); + } + nodes.sort_custom<StringName::AlphCompare>(); + + for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) { + r_nodes->push_back(E->get()); + } } -void AnimationNodeStateMachine::stop() { - playing = false; - play_start = false; - current = StringName(); + +bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const StringName &p_to) const { + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_from && transitions[i].to == p_to) + return true; + } + return false; } -bool AnimationNodeStateMachine::is_playing() const { - return playing; +int AnimationNodeStateMachine::find_transition(const StringName &p_from, const StringName &p_to) const { + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_from && transitions[i].to == p_to) + return i; + } + return -1; } -StringName AnimationNodeStateMachine::get_current_node() const { - if (!playing) { - return StringName(); + +void AnimationNodeStateMachine::add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition) { + + ERR_FAIL_COND(p_from == p_to); + ERR_FAIL_COND(!states.has(p_from)); + ERR_FAIL_COND(!states.has(p_to)); + ERR_FAIL_COND(p_transition.is_null()); + + for (int i = 0; i < transitions.size(); i++) { + ERR_FAIL_COND(transitions[i].from == p_from && transitions[i].to == p_to); } - return current; + Transition tr; + tr.from = p_from; + tr.to = p_to; + tr.transition = p_transition; + + tr.transition->connect("advance_condition_changed", this, "_tree_changed", varray(), CONNECT_REFERENCE_COUNTED); + + transitions.push_back(tr); +} + +Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_transition(int p_transition) const { + ERR_FAIL_INDEX_V(p_transition, transitions.size(), Ref<AnimationNodeStateMachineTransition>()); + return transitions[p_transition].transition; } +StringName AnimationNodeStateMachine::get_transition_from(int p_transition) const { + + ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); + return transitions[p_transition].from; +} +StringName AnimationNodeStateMachine::get_transition_to(int p_transition) const { + + ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); + return transitions[p_transition].to; +} + +int AnimationNodeStateMachine::get_transition_count() const { + + return transitions.size(); +} +void AnimationNodeStateMachine::remove_transition(const StringName &p_from, const StringName &p_to) { -StringName AnimationNodeStateMachine::get_blend_from_node() const { - if (!playing) { - return StringName(); + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_from && transitions[i].to == p_to) { + transitions.write[i].transition->disconnect("advance_condition_changed", this, "_tree_changed"); + transitions.remove(i); + return; + } } - return fading_from; + /*if (playing) { + path.clear(); + }*/ } -float AnimationNodeStateMachine::get_current_play_pos() const { - return pos_current; +void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) { + + ERR_FAIL_INDEX(p_transition, transitions.size()); + transitions.write[p_transition].transition->disconnect("advance_condition_changed", this, "_tree_changed"); + transitions.remove(p_transition); + /*if (playing) { + path.clear(); + }*/ } -float AnimationNodeStateMachine::get_current_length() const { - return len_current; + +void AnimationNodeStateMachine::set_start_node(const StringName &p_node) { + + ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); + start_node = p_node; } -Vector<StringName> AnimationNodeStateMachine::get_travel_path() const { - return path; +String AnimationNodeStateMachine::get_start_node() const { + + return start_node; +} + +void AnimationNodeStateMachine::set_end_node(const StringName &p_node) { + + ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); + end_node = p_node; +} + +String AnimationNodeStateMachine::get_end_node() const { + + return end_node; +} + +void AnimationNodeStateMachine::set_graph_offset(const Vector2 &p_offset) { + graph_offset = p_offset; +} + +Vector2 AnimationNodeStateMachine::get_graph_offset() const { + return graph_offset; +} + +float AnimationNodeStateMachine::process(float p_time, bool p_seek) { + + Ref<AnimationNodeStateMachinePlayback> playback = get_parameter(this->playback); + ERR_FAIL_COND_V(playback.is_null(), 0.0); + + return playback->process(this, p_time, p_seek); } + String AnimationNodeStateMachine::get_caption() const { return "StateMachine"; } @@ -623,14 +775,8 @@ String AnimationNodeStateMachine::get_caption() const { void AnimationNodeStateMachine::_notification(int p_what) { } -void AnimationNodeStateMachine::set_tree(AnimationTree *p_player) { - - AnimationNode::set_tree(p_player); - - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { - Ref<AnimationRootNode> node = E->get(); - node->set_tree(p_player); - } +Ref<AnimationNode> AnimationNodeStateMachine::get_child_by_name(const StringName &p_name) { + return get_node(p_name); } bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_value) { @@ -651,7 +797,7 @@ bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_ if (what == "position") { if (states.has(node_name)) { - states[node_name]->set_position(p_value); + states[node_name].position = p_value; } return true; } @@ -687,7 +833,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c if (what == "node") { if (states.has(node_name)) { - r_ret = states[node_name]; + r_ret = states[node_name].node; return true; } } @@ -695,7 +841,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c if (what == "position") { if (states.has(node_name)) { - r_ret = states[node_name]->get_position(); + r_ret = states[node_name].position; return true; } } @@ -727,14 +873,14 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) const { List<StringName> names; - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { names.push_back(E->key()); } names.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { String name = E->get(); - p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::VECTOR2, "states/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } @@ -744,15 +890,33 @@ void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) c p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } +void AnimationNodeStateMachine::set_node_position(const StringName &p_name, const Vector2 &p_position) { + ERR_FAIL_COND(!states.has(p_name)); + states[p_name].position = p_position; +} + +Vector2 AnimationNodeStateMachine::get_node_position(const StringName &p_name) const { + + ERR_FAIL_COND_V(!states.has(p_name), Vector2()); + return states[p_name].position; +} + +void AnimationNodeStateMachine::_tree_changed() { + emit_signal("tree_changed"); +} + void AnimationNodeStateMachine::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_node", "name", "node"), &AnimationNodeStateMachine::add_node); + ClassDB::bind_method(D_METHOD("add_node", "name", "node", "position"), &AnimationNodeStateMachine::add_node, DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("get_node", "name"), &AnimationNodeStateMachine::get_node); ClassDB::bind_method(D_METHOD("remove_node", "name"), &AnimationNodeStateMachine::remove_node); ClassDB::bind_method(D_METHOD("rename_node", "name", "new_name"), &AnimationNodeStateMachine::rename_node); ClassDB::bind_method(D_METHOD("has_node", "name"), &AnimationNodeStateMachine::has_node); ClassDB::bind_method(D_METHOD("get_node_name", "node"), &AnimationNodeStateMachine::get_node_name); + ClassDB::bind_method(D_METHOD("set_node_position", "name", "position"), &AnimationNodeStateMachine::set_node_position); + ClassDB::bind_method(D_METHOD("get_node_position", "name"), &AnimationNodeStateMachine::get_node_position); + ClassDB::bind_method(D_METHOD("has_transition", "from", "to"), &AnimationNodeStateMachine::add_transition); ClassDB::bind_method(D_METHOD("add_transition", "from", "to", "transition"), &AnimationNodeStateMachine::add_transition); ClassDB::bind_method(D_METHOD("get_transition", "idx"), &AnimationNodeStateMachine::get_transition); @@ -771,20 +935,10 @@ void AnimationNodeStateMachine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_graph_offset", "name"), &AnimationNodeStateMachine::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeStateMachine::get_graph_offset); - ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachine::travel); - ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachine::start); - ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachine::stop); - ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachine::is_playing); - ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachine::get_current_node); - ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachine::get_travel_path); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeStateMachine::_tree_changed); } AnimationNodeStateMachine::AnimationNodeStateMachine() { - play_start = false; - - playing = false; - len_current = 0; - - fading_time = 0; + playback = "playback"; } diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index e7357e09ea..5d633d6334 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -15,6 +15,8 @@ public: private: SwitchMode switch_mode; bool auto_advance; + StringName advance_condition; + StringName advance_condition_name; float xfade; bool disabled; int priority; @@ -29,6 +31,11 @@ public: void set_auto_advance(bool p_enable); bool has_auto_advance() const; + void set_advance_condition(const StringName &p_condition); + StringName get_advance_condition() const; + + StringName get_advance_condition_name() const; + void set_xfade_time(float p_xfade); float get_xfade_time() const; @@ -43,39 +50,24 @@ public: VARIANT_ENUM_CAST(AnimationNodeStateMachineTransition::SwitchMode) -class AnimationNodeStateMachine : public AnimationRootNode { - - GDCLASS(AnimationNodeStateMachine, AnimationRootNode); +class AnimationNodeStateMachine; -private: - Map<StringName, Ref<AnimationRootNode> > states; +class AnimationNodeStateMachinePlayback : public Resource { + GDCLASS(AnimationNodeStateMachinePlayback, Resource); - struct Transition { - - StringName from; - StringName to; - Ref<AnimationNodeStateMachineTransition> transition; - }; + friend class AnimationNodeStateMachine; struct AStarCost { float distance; StringName prev; }; - Vector<Transition> transitions; - float len_total; float len_current; float pos_current; int loops_current; - bool play_start; - StringName start_node; - StringName end_node; - - Vector2 graph_offset; - StringName current; StringName fading_from; @@ -85,6 +77,63 @@ private: Vector<StringName> path; bool playing; + StringName start_request; + bool start_request_travel; + bool stop_request; + + bool _travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel); + + float process(AnimationNodeStateMachine *p_state_machine, float p_time, bool p_seek); + +protected: + static void _bind_methods(); + +public: + void travel(const StringName &p_state); + void start(const StringName &p_state); + void stop(); + bool is_playing() const; + StringName get_current_node() const; + StringName get_blend_from_node() const; + Vector<StringName> get_travel_path() const; + float get_current_play_pos() const; + float get_current_length() const; + + AnimationNodeStateMachinePlayback(); +}; + +class AnimationNodeStateMachine : public AnimationRootNode { + + GDCLASS(AnimationNodeStateMachine, AnimationRootNode); + +private: + friend class AnimationNodeStateMachinePlayback; + + struct State { + Ref<AnimationRootNode> node; + Vector2 position; + }; + + Map<StringName, State> states; + + struct Transition { + + StringName from; + StringName to; + Ref<AnimationNodeStateMachineTransition> transition; + }; + + Vector<Transition> transitions; + + StringName playback; + + StringName start_node; + StringName end_node; + + Vector2 graph_offset; + + void _tree_changed(); + protected: void _notification(int p_what); static void _bind_methods(); @@ -94,7 +143,10 @@ protected: void _get_property_list(List<PropertyInfo> *p_list) const; public: - void add_node(const StringName &p_name, Ref<AnimationNode> p_node); + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + + void add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position = Vector2()); Ref<AnimationNode> get_node(const StringName &p_name) const; void remove_node(const StringName &p_name); void rename_node(const StringName &p_name, const StringName &p_new_name); @@ -102,6 +154,11 @@ public: StringName get_node_name(const Ref<AnimationNode> &p_node) const; void get_node_list(List<StringName> *r_nodes) const; + void set_node_position(const StringName &p_name, const Vector2 &p_position); + Vector2 get_node_position(const StringName &p_name) const; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); + bool has_transition(const StringName &p_from, const StringName &p_to) const; int find_transition(const StringName &p_from, const StringName &p_to) const; void add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition); @@ -124,17 +181,7 @@ public: virtual float process(float p_time, bool p_seek); virtual String get_caption() const; - bool travel(const StringName &p_state); - void start(const StringName &p_state); - void stop(); - bool is_playing() const; - StringName get_current_node() const; - StringName get_blend_from_node() const; - Vector<StringName> get_travel_path() const; - float get_current_play_pos() const; - float get_current_length() const; - - virtual void set_tree(AnimationTree *p_player); + virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name); AnimationNodeStateMachine(); }; diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 8bbc05eed3..68b0602c67 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -5,6 +5,38 @@ #include "scene/scene_string_names.h" #include "servers/audio/audio_stream.h" + + +void AnimationNode::get_parameter_list(List<PropertyInfo> *r_list) const { + +} + +Variant AnimationNode::get_parameter_default_value(const StringName &p_parameter) const { + return Variant(); +} + +void AnimationNode::set_parameter(const StringName& p_name, const Variant& p_value) { + ERR_FAIL_COND(!state); + ERR_FAIL_COND(!state->tree->property_parent_map.has(base_path)); + ERR_FAIL_COND(!state->tree->property_parent_map[base_path].has(p_name)); + StringName path = state->tree->property_parent_map[base_path][p_name]; + + state->tree->property_map[path]=p_value; +} + +Variant AnimationNode::get_parameter(const StringName& p_name) const { + ERR_FAIL_COND_V(!state,Variant()); + ERR_FAIL_COND_V(!state->tree->property_parent_map.has(base_path),Variant()); + ERR_FAIL_COND_V(!state->tree->property_parent_map[base_path].has(p_name),Variant()); + + StringName path = state->tree->property_parent_map[base_path][p_name]; + return state->tree->property_map[path]; +} + +void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) { + +} + void AnimationNode::blend_animation(const StringName &p_animation, float p_time, float p_delta, bool p_seeked, float p_blend) { ERR_FAIL_COND(!state); @@ -14,8 +46,8 @@ void AnimationNode::blend_animation(const StringName &p_animation, float p_time, if (animation.is_null()) { - Ref<AnimationNodeBlendTree> btree = get_parent(); - if (btree.is_valid()) { + AnimationNodeBlendTree* btree = Object::cast_to<AnimationNodeBlendTree>(parent); + if (btree) { String name = btree->get_node_name(Ref<AnimationNodeAnimation>(this)); make_invalid(vformat(RTR("In node '%s', invalid animation: '%s'."), name, p_animation)); } else { @@ -37,10 +69,20 @@ void AnimationNode::blend_animation(const StringName &p_animation, float p_time, state->animation_states.push_back(anim_state); } -float AnimationNode::_pre_process(State *p_state, float p_time, bool p_seek) { +float AnimationNode::_pre_process(const StringName& p_base_path, AnimationNode *p_parent, State *p_state, float p_time, bool p_seek, const Vector<StringName>& p_connections) { + + base_path = p_base_path; + parent = p_parent; + connections=p_connections; state = p_state; + float t = process(p_time, p_seek); + state = NULL; + parent = NULL; + base_path = StringName(); + connections.clear(); + return t; } @@ -56,39 +98,31 @@ void AnimationNode::make_invalid(const String &p_reason) { float AnimationNode::blend_input(int p_input, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) { ERR_FAIL_INDEX_V(p_input, inputs.size(), 0); ERR_FAIL_COND_V(!state, 0); - ERR_FAIL_COND_V(!get_tree(), 0); //should not happen, but used to catch bugs - - Ref<AnimationNodeBlendTree> tree = get_parent(); - - if (!tree.is_valid() && get_tree()->get_tree_root().ptr() != this) { - make_invalid(RTR("Can't blend input because node is not in a tree")); - return 0; - } - ERR_FAIL_COND_V(!tree.is_valid(), 0); //should not happen + AnimationNodeBlendTree* blend_tree = Object::cast_to<AnimationNodeBlendTree>(parent); + ERR_FAIL_COND_V(!blend_tree,0); - StringName anim_name = inputs[p_input].connected_to; + StringName node_name = connections[p_input]; - Ref<AnimationNode> node = tree->get_node(anim_name); - - if (node.is_null()) { - - String name = tree->get_node_name(Ref<AnimationNodeAnimation>(this)); + if (!blend_tree->has_node(node_name)) { + String name = blend_tree->get_node_name(Ref<AnimationNode>(this)); make_invalid(vformat(RTR("Nothing connected to input '%s' of node '%s'."), get_input_name(p_input), name)); return 0; } - inputs.write[p_input].last_pass = state->last_pass; + Ref<AnimationNode> node = blend_tree->get_node(node_name); - return _blend_node(node, p_time, p_seek, p_blend, p_filter, p_optimize, &inputs.write[p_input].activity); + //inputs.write[p_input].last_pass = state->last_pass; + float activity; + return _blend_node(node_name,blend_tree->get_node_connection_array(node_name),NULL,node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity); } -float AnimationNode::blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) { +float AnimationNode::blend_node(const StringName& p_sub_path,Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) { - return _blend_node(p_node, p_time, p_seek, p_blend, p_filter, p_optimize); + return _blend_node(p_sub_path,Vector<StringName>(),this,p_node, p_time, p_seek, p_blend, p_filter, p_optimize); } -float AnimationNode::_blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize, float *r_max) { +float AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName>& p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize, float *r_max) { ERR_FAIL_COND_V(!p_node.is_valid(), 0); ERR_FAIL_COND_V(!state, 0); @@ -189,7 +223,19 @@ float AnimationNode::_blend_node(Ref<AnimationNode> p_node, float p_time, bool p if (!p_seek && p_optimize && !any_valid) //pointless to go on, all are zero return 0; - return p_node->_pre_process(state, p_time, p_seek); + String new_path; + AnimationNode *new_parent; + + //this is the slowest part of processing, but as strings process in powers of 2, and the paths always exist, it will not result in that many allocations + if (p_new_parent) { + new_parent = p_new_parent; + new_path = String(base_path)+String(p_subpath)+"/"; + } else { + ERR_FAIL_COND_V(!parent,0); + new_parent = parent; + new_path = String(parent->base_path) + String(p_subpath)+"/"; + } + return p_node->_pre_process(new_path,new_parent,state, p_time, p_seek, p_connections); } int AnimationNode::get_input_count() const { @@ -201,28 +247,6 @@ String AnimationNode::get_input_name(int p_input) { return inputs[p_input].name; } -float AnimationNode::get_input_activity(int p_input) const { - - ERR_FAIL_INDEX_V(p_input, inputs.size(), 0); - if (!get_tree()) - return 0; - - if (get_tree()->get_last_process_pass() != inputs[p_input].last_pass) { - return 0; - } - return inputs[p_input].activity; -} -StringName AnimationNode::get_input_connection(int p_input) { - - ERR_FAIL_INDEX_V(p_input, inputs.size(), StringName()); - return inputs[p_input].connected_to; -} - -void AnimationNode::set_input_connection(int p_input, const StringName &p_connection) { - - ERR_FAIL_INDEX(p_input, inputs.size()); - inputs.write[p_input].connected_to = p_connection; -} String AnimationNode::get_caption() const { @@ -239,8 +263,6 @@ void AnimationNode::add_input(const String &p_name) { Input input; ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1); input.name = p_name; - input.activity = 0; - input.last_pass = 0; inputs.push_back(input); emit_changed(); } @@ -258,35 +280,6 @@ void AnimationNode::remove_input(int p_index) { emit_changed(); } -void AnimationNode::_set_parent(Object *p_parent) { - set_parent(Object::cast_to<AnimationNode>(p_parent)); -} - -void AnimationNode::set_parent(AnimationNode *p_parent) { - parent = p_parent; //do not use ref because parent contains children - if (get_script_instance()) { - get_script_instance()->call("_parent_set", p_parent); - } -} - -Ref<AnimationNode> AnimationNode::get_parent() const { - if (parent) { - return Ref<AnimationNode>(parent); - } - - return Ref<AnimationNode>(); -} - -AnimationTree *AnimationNode::get_tree() const { - - return player; -} - -AnimationPlayer *AnimationNode::get_player() const { - ERR_FAIL_COND_V(!state, NULL); - return state->player; -} - float AnimationNode::process(float p_time, bool p_seek) { if (get_script_instance()) { @@ -320,21 +313,7 @@ bool AnimationNode::has_filter() const { return false; } -void AnimationNode::set_position(const Vector2 &p_position) { - position = p_position; -} - -Vector2 AnimationNode::get_position() const { - return position; -} - -void AnimationNode::set_tree(AnimationTree *p_player) { - if (player != NULL && p_player == NULL) { - emit_signal("removed_from_graph"); - } - player = p_player; -} Array AnimationNode::_get_filters() const { @@ -361,12 +340,14 @@ void AnimationNode::_validate_property(PropertyInfo &property) const { } } +Ref<AnimationNode> AnimationNode::get_child_by_name(const StringName &p_name) { + return Ref<AnimationNode>(); +} + void AnimationNode::_bind_methods() { ClassDB::bind_method(D_METHOD("get_input_count"), &AnimationNode::get_input_count); ClassDB::bind_method(D_METHOD("get_input_name", "input"), &AnimationNode::get_input_name); - ClassDB::bind_method(D_METHOD("get_input_connection", "input"), &AnimationNode::get_input_connection); - ClassDB::bind_method(D_METHOD("get_input_activity", "input"), &AnimationNode::get_input_activity); ClassDB::bind_method(D_METHOD("add_input", "name"), &AnimationNode::add_input); ClassDB::bind_method(D_METHOD("remove_input", "index"), &AnimationNode::remove_input); @@ -377,19 +358,16 @@ void AnimationNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_filter_enabled", "enable"), &AnimationNode::set_filter_enabled); ClassDB::bind_method(D_METHOD("is_filter_enabled"), &AnimationNode::is_filter_enabled); - ClassDB::bind_method(D_METHOD("set_position", "position"), &AnimationNode::set_position); - ClassDB::bind_method(D_METHOD("get_position"), &AnimationNode::get_position); ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters); ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters); ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "blend"), &AnimationNode::blend_animation); - ClassDB::bind_method(D_METHOD("blend_node", "node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("blend_node", "name","node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true)); ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("set_parent", "parent"), &AnimationNode::_set_parent); - ClassDB::bind_method(D_METHOD("get_parent"), &AnimationNode::get_parent); - ClassDB::bind_method(D_METHOD("get_tree"), &AnimationNode::get_tree); + ClassDB::bind_method(D_METHOD("set_parameter","name","value"), &AnimationNode::set_parameter); + ClassDB::bind_method(D_METHOD("get_parameter","name"), &AnimationNode::get_parameter); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_filter_enabled", "is_filter_enabled"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "filters", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_filters", "_get_filters"); @@ -397,9 +375,11 @@ void AnimationNode::_bind_methods() { BIND_VMETHOD(MethodInfo("process", PropertyInfo(Variant::REAL, "time"), PropertyInfo(Variant::BOOL, "seek"))); BIND_VMETHOD(MethodInfo(Variant::STRING, "get_caption")); BIND_VMETHOD(MethodInfo(Variant::STRING, "has_filter")); - BIND_VMETHOD(MethodInfo("_parent_set", PropertyInfo(Variant::OBJECT, "parent"))); ADD_SIGNAL(MethodInfo("removed_from_graph")); + + ADD_SIGNAL(MethodInfo("tree_changed")); + BIND_ENUM_CONSTANT(FILTER_IGNORE); BIND_ENUM_CONSTANT(FILTER_PASS); BIND_ENUM_CONSTANT(FILTER_STOP); @@ -410,8 +390,6 @@ AnimationNode::AnimationNode() { state = NULL; parent = NULL; - player = NULL; - set_local_to_scene(true); filter_enabled = false; } @@ -420,18 +398,17 @@ AnimationNode::AnimationNode() { void AnimationTree::set_tree_root(const Ref<AnimationNode> &p_root) { if (root.is_valid()) { - root->set_tree(NULL); - } - if (p_root.is_valid()) { - ERR_EXPLAIN("root node already set to another player"); - ERR_FAIL_COND(p_root->player); + root->disconnect("tree_changed",this,"_tree_changed"); } + root = p_root; if (root.is_valid()) { - root->set_tree(this); + root->connect("tree_changed",this,"_tree_changed"); } + properties_dirty=true; + update_configuration_warning(); } @@ -699,7 +676,10 @@ void AnimationTree::_clear_caches() { void AnimationTree::_process_graph(float p_delta) { + _update_properties(); //if properties need updating, update them + //check all tracks, see if they need modification + root_motion_transform = Transform(); if (!root.is_valid()) { @@ -741,6 +721,7 @@ void AnimationTree::_process_graph(float p_delta) { state.valid = true; state.player = player; state.last_pass = process_pass; + state.tree = this; // root source blends @@ -757,11 +738,11 @@ void AnimationTree::_process_graph(float p_delta) { if (started) { //if started, seek - root->_pre_process(&state, 0, true); + root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path,NULL,&state, 0, true,Vector<StringName>()); started = false; } - root->_pre_process(&state, p_delta, false); + root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path,NULL,&state, p_delta, false,Vector<StringName>()); } if (!state.valid) { @@ -1297,6 +1278,117 @@ Transform AnimationTree::get_root_motion_transform() const { return root_motion_transform; } +void AnimationTree::_tree_changed() { + if (properties_dirty) { + return; + } + + call_deferred("_update_properties"); + properties_dirty=true; +} + +void AnimationTree::_update_properties_for_node(const String& p_base_path,Ref<AnimationNode> node) { + + if (!property_parent_map.has(p_base_path)) { + property_parent_map[p_base_path]=HashMap<StringName, StringName>(); + } + + List<PropertyInfo> plist; + node->get_parameter_list(&plist); + for(List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) { + PropertyInfo pinfo = E->get(); + + StringName key = pinfo.name; + + if (!property_map.has(p_base_path +key)) { + property_map[p_base_path + key] = node->get_parameter_default_value(key); + } + + property_parent_map[p_base_path][key]=p_base_path+key; + + pinfo.name = p_base_path + key; + properties.push_back(pinfo); + } + + List<AnimationNode::ChildNode> children; + node->get_child_nodes(&children); + + for (List<AnimationNode::ChildNode>::Element *E=children.front();E;E=E->next()) { + _update_properties_for_node(p_base_path+E->get().name+"/",E->get().node); + } +} + +void AnimationTree::_update_properties() { + if (!properties_dirty) { + return; + } + + properties.clear(); + property_parent_map.clear(); + + if (root.is_valid()) { + _update_properties_for_node(SceneStringNames::get_singleton()->parameters_base_path,root); + } + + properties_dirty = false; + + _change_notify(); +} + +bool AnimationTree::_set(const StringName &p_name, const Variant &p_value) { + if (properties_dirty) { + _update_properties(); + } + + if (property_map.has(p_name)) { + property_map[p_name]=p_value; +#ifdef TOOLS_ENABLED + _change_notify(p_name.operator String().utf8().get_data()); +#endif + return true; + } + + return false; +} + +bool AnimationTree::_get(const StringName &p_name, Variant &r_ret) const { + if (properties_dirty) { + const_cast<AnimationTree*>(this)->_update_properties(); + } + + if (property_map.has(p_name)) { + r_ret=property_map[p_name]; + return true; + } + + return false; + +} +void AnimationTree::_get_property_list(List<PropertyInfo> *p_list) const { + if (properties_dirty) { + const_cast<AnimationTree*>(this)->_update_properties(); + } + + for (const List<PropertyInfo>::Element *E=properties.front();E;E=E->next()) { + p_list->push_back(E->get()); + } +} + +void AnimationTree::rename_parameter(const String& p_base,const String& p_new_base) { + + //rename values first + for (const List<PropertyInfo>::Element *E=properties.front();E;E=E->next()) { + if (E->get().name.begins_with(p_base)) { + String new_name = E->get().name.replace_first(p_base,p_new_base); + property_map[new_name]=property_map[E->get().name]; + } + } + + //update tree second + properties_dirty=true; + _update_properties(); +} + void AnimationTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_active", "active"), &AnimationTree::set_active); ClassDB::bind_method(D_METHOD("is_active"), &AnimationTree::is_active); @@ -1315,11 +1407,17 @@ void AnimationTree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_root_motion_transform"), &AnimationTree::get_root_motion_transform); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationTree::_tree_changed); + ClassDB::bind_method(D_METHOD("_update_properties"), &AnimationTree::_update_properties); + + ClassDB::bind_method(D_METHOD("rename_parameter","old_name","new_name"), &AnimationTree::rename_parameter); + + ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationTree::advance); ClassDB::bind_method(D_METHOD("_node_removed"), &AnimationTree::_node_removed); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_tree_root", "get_tree_root"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode"), "set_tree_root", "get_tree_root"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active"); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_process_mode", "get_process_mode"); @@ -1338,10 +1436,9 @@ AnimationTree::AnimationTree() { cache_valid = false; setup_pass = 1; started = true; + properties_dirty = true; } AnimationTree::~AnimationTree() { - if (root.is_valid()) { - root->player = NULL; - } + } diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 87092a4a0e..3c615b2f92 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -23,9 +23,6 @@ public: struct Input { String name; - StringName connected_to; - float activity; - uint64_t last_pass; }; Vector<Input> inputs; @@ -51,30 +48,33 @@ public: List<AnimationState> animation_states; bool valid; AnimationPlayer *player; + AnimationTree *tree; String invalid_reasons; uint64_t last_pass; }; Vector<float> blends; State *state; - float _pre_process(State *p_state, float p_time, bool p_seek); + String path; + float _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, float p_time, bool p_seek, const Vector<StringName> &p_connections); void _pre_update_animations(HashMap<NodePath, int> *track_map); - Vector2 position; + //all this is temporary + StringName base_path; + Vector<StringName> connections; AnimationNode *parent; - AnimationTree *player; - - float _blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, float *r_max = NULL); HashMap<NodePath, bool> filter; bool filter_enabled; Array _get_filters() const; void _set_filters(const Array &p_filters); + friend class AnimationNodeBlendTree; + float _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, float *r_max = NULL); protected: void blend_animation(const StringName &p_animation, float p_time, float p_delta, bool p_seeked, float p_blend); - float blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); + float blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); float blend_input(int p_input, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); void make_invalid(const String &p_reason); @@ -85,20 +85,24 @@ protected: void _set_parent(Object *p_parent); public: - void set_parent(AnimationNode *p_parent); - Ref<AnimationNode> get_parent() const; - virtual void set_tree(AnimationTree *p_player); - AnimationTree *get_tree() const; - AnimationPlayer *get_player() const; + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + + void set_parameter(const StringName &p_name, const Variant &p_value); + Variant get_parameter(const StringName &p_name) const; + + struct ChildNode { + StringName name; + Ref<AnimationNode> node; + }; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); virtual float process(float p_time, bool p_seek); virtual String get_caption() const; int get_input_count() const; String get_input_name(int p_input); - StringName get_input_connection(int p_input); - void set_input_connection(int p_input, const StringName &p_connection); - float get_input_activity(int p_input) const; void add_input(const String &p_name); void set_input_name(int p_input, const String &p_name); @@ -112,8 +116,7 @@ public: virtual bool has_filter() const; - void set_position(const Vector2 &p_position); - Vector2 get_position() const; + virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name); AnimationNode(); }; @@ -245,7 +248,21 @@ private: NodePath root_motion_track; Transform root_motion_transform; + friend class AnimationNode; + bool properties_dirty; + void _tree_changed(); + void _update_properties(); + List<PropertyInfo> properties; + HashMap<StringName, HashMap<StringName, StringName> > property_parent_map; + HashMap<StringName, Variant> property_map; + + void _update_properties_for_node(const String &p_base_path, Ref<AnimationNode> node); + protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + void _notification(int p_what); static void _bind_methods(); @@ -274,6 +291,8 @@ public: void advance(float p_time); + void rename_parameter(const String &p_base, const String &p_new_base); + uint64_t get_last_process_pass() const; AnimationTree(); ~AnimationTree(); |