summaryrefslogtreecommitdiff
path: root/scene/animation
diff options
context:
space:
mode:
Diffstat (limited to 'scene/animation')
-rw-r--r--scene/animation/animation_blend_space_1d.cpp200
-rw-r--r--scene/animation/animation_blend_space_1d.h24
-rw-r--r--scene/animation/animation_blend_space_2d.cpp26
-rw-r--r--scene/animation/animation_blend_space_2d.h7
-rw-r--r--scene/animation/animation_blend_tree.cpp382
-rw-r--r--scene/animation/animation_blend_tree.h74
-rw-r--r--scene/animation/animation_node_state_machine.cpp204
-rw-r--r--scene/animation/animation_node_state_machine.h36
-rw-r--r--scene/animation/animation_player.cpp439
-rw-r--r--scene/animation/animation_player.h38
-rw-r--r--scene/animation/animation_tree.cpp601
-rw-r--r--scene/animation/animation_tree.h70
-rw-r--r--scene/animation/root_motion_view.cpp10
-rw-r--r--scene/animation/tween.cpp52
-rw-r--r--scene/animation/tween.h5
15 files changed, 1498 insertions, 670 deletions
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
index a2028b8de8..0e9e02f247 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -30,12 +30,20 @@
#include "animation_blend_space_1d.h"
+#include "animation_blend_tree.h"
+
void AnimationNodeBlendSpace1D::get_parameter_list(List<PropertyInfo> *r_list) const {
r_list->push_back(PropertyInfo(Variant::FLOAT, blend_position));
+ r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
+ r_list->push_back(PropertyInfo(Variant::FLOAT, length_internal, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
}
Variant AnimationNodeBlendSpace1D::get_parameter_default_value(const StringName &p_parameter) const {
- return 0;
+ if (p_parameter == closest) {
+ return -1;
+ } else {
+ return 0;
+ }
}
Ref<AnimationNode> AnimationNodeBlendSpace1D::get_child_by_name(const StringName &p_name) {
@@ -53,7 +61,15 @@ void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &p_property) con
}
void AnimationNodeBlendSpace1D::_tree_changed() {
- emit_signal(SNAME("tree_changed"));
+ AnimationRootNode::_tree_changed();
+}
+
+void AnimationNodeBlendSpace1D::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
+ AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
+}
+
+void AnimationNodeBlendSpace1D::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
+ AnimationRootNode::_animation_node_removed(p_oid, p_node);
}
void AnimationNodeBlendSpace1D::_bind_methods() {
@@ -77,6 +93,9 @@ void AnimationNodeBlendSpace1D::_bind_methods() {
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("set_blend_mode", "mode"), &AnimationNodeBlendSpace1D::set_blend_mode);
+ ClassDB::bind_method(D_METHOD("get_blend_mode"), &AnimationNodeBlendSpace1D::get_blend_mode);
+
ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlendSpace1D::set_use_sync);
ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlendSpace1D::is_using_sync);
@@ -91,7 +110,12 @@ void AnimationNodeBlendSpace1D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_space", "get_max_space");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_value_label", "get_value_label");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NO_EDITOR), "set_blend_mode", "get_blend_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_sync", "is_using_sync");
+
+ BIND_ENUM_CONSTANT(BLEND_MODE_INTERPOLATED);
+ BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE);
+ BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE_CARRY);
}
void AnimationNodeBlendSpace1D::get_child_nodes(List<ChildNode> *r_child_nodes) {
@@ -121,6 +145,8 @@ void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_
blend_points[p_at_index].position = p_position;
blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_at_index].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_at_index].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
blend_points_used++;
emit_signal(SNAME("tree_changed"));
@@ -138,10 +164,14 @@ void AnimationNodeBlendSpace1D::set_blend_point_node(int p_point, const Ref<Anim
if (blend_points[p_point].node.is_valid()) {
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed));
+ blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed));
+ blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed));
}
blend_points[p_point].node = p_node;
blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_point].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_point].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
emit_signal(SNAME("tree_changed"));
}
@@ -161,12 +191,16 @@ void AnimationNodeBlendSpace1D::remove_blend_point(int p_point) {
ERR_FAIL_COND(blend_points[p_point].node.is_null());
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace1D::_tree_changed));
+ blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_renamed));
+ blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace1D::_animation_node_removed));
for (int i = p_point; i < blend_points_used - 1; i++) {
blend_points[i] = blend_points[i + 1];
}
blend_points_used--;
+
+ emit_signal(SNAME("animation_node_removed"), get_instance_id(), itos(p_point));
emit_signal(SNAME("tree_changed"));
}
@@ -214,6 +248,14 @@ String AnimationNodeBlendSpace1D::get_value_label() const {
return value_label;
}
+void AnimationNodeBlendSpace1D::set_blend_mode(BlendMode p_blend_mode) {
+ blend_mode = p_blend_mode;
+}
+
+AnimationNodeBlendSpace1D::BlendMode AnimationNodeBlendSpace1D::get_blend_mode() const {
+ return blend_mode;
+}
+
void AnimationNodeBlendSpace1D::set_use_sync(bool p_sync) {
sync = p_sync;
}
@@ -241,79 +283,125 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_is_
}
double blend_pos = get_parameter(blend_position);
+ int cur_closest = get_parameter(closest);
+ double cur_length_internal = get_parameter(length_internal);
+ double max_time_remaining = 0.0;
- float weights[MAX_BLEND_POINTS] = {};
+ if (blend_mode == BLEND_MODE_INTERPOLATED) {
+ float weights[MAX_BLEND_POINTS] = {};
+
+ int point_lower = -1;
+ float pos_lower = 0.0;
+ int point_higher = -1;
+ float pos_higher = 0.0;
+
+ // find the closest two points to blend between
+ for (int i = 0; i < blend_points_used; i++) {
+ float pos = blend_points[i].position;
+
+ if (pos <= blend_pos) {
+ if (point_lower == -1) {
+ point_lower = i;
+ pos_lower = pos;
+ } else if ((blend_pos - pos) < (blend_pos - pos_lower)) {
+ point_lower = i;
+ pos_lower = pos;
+ }
+ } else {
+ if (point_higher == -1) {
+ point_higher = i;
+ pos_higher = pos;
+ } else if ((pos - blend_pos) < (pos_higher - blend_pos)) {
+ point_higher = i;
+ pos_higher = pos;
+ }
+ }
+ }
- int point_lower = -1;
- float pos_lower = 0.0;
- int point_higher = -1;
- float pos_higher = 0.0;
+ // fill in weights
- // find the closest two points to blend between
- for (int i = 0; i < blend_points_used; i++) {
- float pos = blend_points[i].position;
-
- if (pos <= blend_pos) {
- if (point_lower == -1) {
- point_lower = i;
- pos_lower = pos;
- } else if ((blend_pos - pos) < (blend_pos - pos_lower)) {
- point_lower = i;
- pos_lower = pos;
- }
+ if (point_lower == -1 && point_higher != -1) {
+ // we are on the left side, no other point to the left
+ // we just play the next point.
+
+ weights[point_higher] = 1.0;
+ } else if (point_higher == -1) {
+ // we are on the right side, no other point to the right
+ // we just play the previous point
+
+ weights[point_lower] = 1.0;
} else {
- if (point_higher == -1) {
- point_higher = i;
- pos_higher = pos;
- } else if ((pos - blend_pos) < (pos_higher - blend_pos)) {
- point_higher = i;
- pos_higher = pos;
- }
- }
- }
+ // we are between two points.
+ // figure out weights, then blend the animations
- // fill in weights
+ float distance_between_points = pos_higher - pos_lower;
- if (point_lower == -1 && point_higher != -1) {
- // we are on the left side, no other point to the left
- // we just play the next point.
+ float current_pos_inbetween = blend_pos - pos_lower;
- weights[point_higher] = 1.0;
- } else if (point_higher == -1) {
- // we are on the right side, no other point to the right
- // we just play the previous point
+ float blend_percentage = current_pos_inbetween / distance_between_points;
- weights[point_lower] = 1.0;
- } else {
- // we are between two points.
- // figure out weights, then blend the animations
+ float blend_lower = 1.0 - blend_percentage;
+ float blend_higher = blend_percentage;
- float distance_between_points = pos_higher - pos_lower;
+ weights[point_lower] = blend_lower;
+ weights[point_higher] = blend_higher;
+ }
- float current_pos_inbetween = blend_pos - pos_lower;
+ // actually blend the animations now
- float blend_percentage = current_pos_inbetween / distance_between_points;
+ for (int i = 0; i < blend_points_used; i++) {
+ if (i == point_lower || i == point_higher) {
+ double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, weights[i], FILTER_IGNORE, true);
+ max_time_remaining = MAX(max_time_remaining, remaining);
+ } else if (sync) {
+ blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true);
+ }
+ }
+ } else {
+ int new_closest = -1;
+ double new_closest_dist = 1e20;
+
+ for (int i = 0; i < blend_points_used; i++) {
+ double d = abs(blend_points[i].position - blend_pos);
+ if (d < new_closest_dist) {
+ new_closest = i;
+ new_closest_dist = d;
+ }
+ }
- float blend_lower = 1.0 - blend_percentage;
- float blend_higher = blend_percentage;
+ if (new_closest != cur_closest && new_closest != -1) {
+ double from = 0.0;
+ if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) {
+ //for ping-pong loop
+ Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node);
+ Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node);
+ if (!na_c.is_null() && !na_n.is_null()) {
+ na_n->set_backward(na_c->is_backward());
+ }
+ //see how much animation remains
+ from = cur_length_internal - blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, false, p_is_external_seeking, 0.0, FILTER_IGNORE, true);
+ }
- weights[point_lower] = blend_lower;
- weights[point_higher] = blend_higher;
- }
+ max_time_remaining = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
+ cur_length_internal = from + max_time_remaining;
- // actually blend the animations now
+ cur_closest = new_closest;
- double max_time_remaining = 0.0;
+ } else {
+ max_time_remaining = blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
+ }
- for (int i = 0; i < blend_points_used; i++) {
- if (i == point_lower || i == point_higher) {
- double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, weights[i], FILTER_IGNORE, true);
- max_time_remaining = MAX(max_time_remaining, remaining);
- } else if (sync) {
- blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true);
+ if (sync) {
+ for (int i = 0; i < blend_points_used; i++) {
+ if (i != cur_closest) {
+ blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true);
+ }
+ }
}
}
+ set_parameter(this->closest, cur_closest);
+ set_parameter(this->length_internal, cur_length_internal);
return max_time_remaining;
}
diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h
index af93783c0d..4007df0ded 100644
--- a/scene/animation/animation_blend_space_1d.h
+++ b/scene/animation/animation_blend_space_1d.h
@@ -36,6 +36,14 @@
class AnimationNodeBlendSpace1D : public AnimationRootNode {
GDCLASS(AnimationNodeBlendSpace1D, AnimationRootNode);
+public:
+ enum BlendMode {
+ BLEND_MODE_INTERPOLATED,
+ BLEND_MODE_DISCRETE,
+ BLEND_MODE_DISCRETE_CARRY,
+ };
+
+protected:
enum {
MAX_BLEND_POINTS = 64
};
@@ -58,16 +66,21 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode {
void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node);
- void _tree_changed();
-
StringName blend_position = "blend_position";
+ StringName closest = "closest";
+ StringName length_internal = "length_internal";
+
+ BlendMode blend_mode = BLEND_MODE_INTERPOLATED;
-protected:
bool sync = false;
void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
+ virtual void _tree_changed() override;
+ virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
+ virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
+
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -95,6 +108,9 @@ public:
void set_value_label(const String &p_label);
String get_value_label() const;
+ void set_blend_mode(BlendMode p_blend_mode);
+ BlendMode get_blend_mode() const;
+
void set_use_sync(bool p_sync);
bool is_using_sync() const;
@@ -107,4 +123,6 @@ public:
~AnimationNodeBlendSpace1D();
};
+VARIANT_ENUM_CAST(AnimationNodeBlendSpace1D::BlendMode)
+
#endif // ANIMATION_BLEND_SPACE_1D_H
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index c37d54961e..ae5b0d5779 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -81,6 +81,8 @@ void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_
blend_points[p_at_index].position = p_position;
blend_points[p_at_index].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_at_index].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_at_index].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
blend_points_used++;
_queue_auto_triangles();
@@ -100,9 +102,13 @@ void AnimationNodeBlendSpace2D::set_blend_point_node(int p_point, const Ref<Anim
if (blend_points[p_point].node.is_valid()) {
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed));
+ blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed));
+ blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed));
}
blend_points[p_point].node = p_node;
blend_points[p_point].node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_point].node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ blend_points[p_point].node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
emit_signal(SNAME("tree_changed"));
}
@@ -122,6 +128,8 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) {
ERR_FAIL_COND(blend_points[p_point].node.is_null());
blend_points[p_point].node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendSpace2D::_tree_changed));
+ blend_points[p_point].node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_renamed));
+ blend_points[p_point].node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendSpace2D::_animation_node_removed));
for (int i = 0; i < triangles.size(); i++) {
bool erase = false;
@@ -144,6 +152,8 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) {
blend_points[i] = blend_points[i + 1];
}
blend_points_used--;
+
+ emit_signal(SNAME("animation_node_removed"), get_instance_id(), itos(p_point));
emit_signal(SNAME("tree_changed"));
}
@@ -598,10 +608,6 @@ Ref<AnimationNode> AnimationNodeBlendSpace2D::get_child_by_name(const StringName
return get_blend_point_node(p_name.operator String().to_int());
}
-void AnimationNodeBlendSpace2D::_tree_changed() {
- emit_signal(SNAME("tree_changed"));
-}
-
void AnimationNodeBlendSpace2D::set_blend_mode(BlendMode p_blend_mode) {
blend_mode = p_blend_mode;
}
@@ -618,6 +624,18 @@ bool AnimationNodeBlendSpace2D::is_using_sync() const {
return sync;
}
+void AnimationNodeBlendSpace2D::_tree_changed() {
+ AnimationRootNode::_tree_changed();
+}
+
+void AnimationNodeBlendSpace2D::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
+ AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
+}
+
+void AnimationNodeBlendSpace2D::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
+ AnimationRootNode::_animation_node_removed(p_oid, p_node);
+}
+
void AnimationNodeBlendSpace2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace2D::set_blend_point_position);
diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h
index 044c93d9f6..a770bf01ee 100644
--- a/scene/animation/animation_blend_space_2d.h
+++ b/scene/animation/animation_blend_space_2d.h
@@ -85,14 +85,15 @@ protected:
void _update_triangles();
void _queue_auto_triangles();
- void _tree_changed();
-
-protected:
bool sync = false;
void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
+ virtual void _tree_changed() override;
+ virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
+ virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
+
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 1ef0774828..3567738366 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -229,15 +229,17 @@ AnimationNodeSync::AnimationNodeSync() {
////////////////////////////////////////////////////////
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, "", PROPERTY_USAGE_NONE));
+ r_list->push_back(PropertyInfo(Variant::BOOL, active, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY));
+ r_list->push_back(PropertyInfo(Variant::INT, request, PROPERTY_HINT_ENUM, ",Fire,Abort"));
r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, remaining, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, time_to_restart, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
}
Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_parameter) const {
- if (p_parameter == active || p_parameter == prev_active) {
+ if (p_parameter == request) {
+ return ONE_SHOT_REQUEST_NONE;
+ } else if (p_parameter == active) {
return false;
} else if (p_parameter == time_to_restart) {
return -1;
@@ -246,6 +248,13 @@ Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_pa
}
}
+bool AnimationNodeOneShot::is_parameter_read_only(const StringName &p_parameter) const {
+ if (p_parameter == active) {
+ return true;
+ }
+ return false;
+}
+
void AnimationNodeOneShot::set_fadein_time(double p_time) {
fade_in = p_time;
}
@@ -303,50 +312,51 @@ bool AnimationNodeOneShot::has_filter() const {
}
double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_external_seeking) {
+ OneShotRequest cur_request = static_cast<OneShotRequest>((int)get_parameter(request));
bool cur_active = get_parameter(active);
- bool cur_prev_active = get_parameter(prev_active);
double cur_time = get_parameter(time);
double cur_remaining = get_parameter(remaining);
double cur_time_to_restart = get_parameter(time_to_restart);
- if (!cur_active) {
- //make it as if this node doesn't exist, pass input 0 by.
- if (cur_prev_active) {
- set_parameter(prev_active, false);
- }
+ set_parameter(request, ONE_SHOT_REQUEST_NONE);
+
+ bool do_start = cur_request == ONE_SHOT_REQUEST_FIRE;
+ if (cur_request == ONE_SHOT_REQUEST_ABORT) {
+ set_parameter(active, false);
+ set_parameter(time_to_restart, -1);
+ return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync);
+ } else if (!do_start && !cur_active) {
if (cur_time_to_restart >= 0.0 && !p_seek) {
cur_time_to_restart -= p_time;
if (cur_time_to_restart < 0) {
- //restart
- set_parameter(active, true);
- cur_active = true;
+ do_start = true; // Restart.
}
set_parameter(time_to_restart, cur_time_to_restart);
}
-
- return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync);
+ if (!do_start) {
+ return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync);
+ }
}
bool os_seek = p_seek;
-
if (p_seek) {
cur_time = p_time;
}
- bool do_start = !cur_prev_active;
if (do_start) {
cur_time = 0;
os_seek = true;
- set_parameter(prev_active, true);
+ set_parameter(request, ONE_SHOT_REQUEST_NONE);
+ set_parameter(active, true);
}
real_t blend;
-
+ bool use_fade_in = fade_in > 0;
if (cur_time < fade_in) {
- if (fade_in > 0) {
+ if (use_fade_in) {
blend = cur_time / fade_in;
} else {
- blend = 0;
+ blend = 0; // Should not happen.
}
} else if (!do_start && cur_remaining <= fade_out) {
if (fade_out > 0) {
@@ -358,10 +368,10 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_exter
blend = 1.0;
}
- double main_rem;
+ double main_rem = 0.0;
if (mix == MIX_MODE_ADD) {
main_rem = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync);
- } else {
+ } else if (use_fade_in) {
main_rem = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0 - blend, FILTER_BLEND, sync); // Unlike below, processing this edge is a corner case.
}
double os_rem = blend_input(1, os_seek ? cur_time : p_time, os_seek, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_PASS, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
@@ -375,7 +385,6 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_exter
cur_remaining = os_rem;
if (cur_remaining <= 0) {
set_parameter(active, false);
- set_parameter(prev_active, false);
if (autorestart) {
double restart_sec = autorestart_delay + Math::randd() * autorestart_random_delay;
set_parameter(time_to_restart, restart_sec);
@@ -419,6 +428,10 @@ void AnimationNodeOneShot::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_delay", "get_autorestart_delay");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_random_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_autorestart_random_delay", "get_autorestart_random_delay");
+ BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_NONE);
+ BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_FIRE);
+ BIND_ENUM_CONSTANT(ONE_SHOT_REQUEST_ABORT);
+
BIND_ENUM_CONSTANT(MIX_MODE_BLEND);
BIND_ENUM_CONSTANT(MIX_MODE_ADD);
}
@@ -505,7 +518,7 @@ void AnimationNodeBlend2::get_parameter_list(List<PropertyInfo> *r_list) const {
}
Variant AnimationNodeBlend2::get_parameter_default_value(const StringName &p_parameter) const {
- return 0; //for blend amount
+ return 0; // For blend amount.
}
String AnimationNodeBlend2::get_caption() const {
@@ -518,7 +531,7 @@ double AnimationNodeBlend2::process(double p_time, bool p_seek, bool p_is_extern
double rem0 = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0 - amount, FILTER_BLEND, sync);
double rem1 = blend_input(1, p_time, p_seek, p_is_external_seeking, amount, FILTER_PASS, sync);
- return amount > 0.5 ? rem1 : rem0; //hacky but good enough
+ return amount > 0.5 ? rem1 : rem0; // Hacky but good enough.
}
bool AnimationNodeBlend2::has_filter() const {
@@ -540,7 +553,7 @@ void AnimationNodeBlend3::get_parameter_list(List<PropertyInfo> *r_list) const {
}
Variant AnimationNodeBlend3::get_parameter_default_value(const StringName &p_parameter) const {
- return 0; //for blend amount
+ return 0; // For blend amount.
}
String AnimationNodeBlend3::get_caption() const {
@@ -553,7 +566,7 @@ double AnimationNodeBlend3::process(double p_time, bool p_seek, bool p_is_extern
double rem1 = blend_input(1, p_time, p_seek, p_is_external_seeking, 1.0 - ABS(amount), FILTER_IGNORE, sync);
double rem2 = blend_input(2, p_time, p_seek, p_is_external_seeking, MAX(0, amount), FILTER_IGNORE, sync);
- return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); //hacky but good enough
+ return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); // Hacky but good enough.
}
void AnimationNodeBlend3::_bind_methods() {
@@ -572,7 +585,7 @@ void AnimationNodeTimeScale::get_parameter_list(List<PropertyInfo> *r_list) cons
}
Variant AnimationNodeTimeScale::get_parameter_default_value(const StringName &p_parameter) const {
- return 1.0; //initial timescale
+ return 1.0; // Initial timescale.
}
String AnimationNodeTimeScale::get_caption() const {
@@ -598,24 +611,24 @@ AnimationNodeTimeScale::AnimationNodeTimeScale() {
////////////////////////////////////
void AnimationNodeTimeSeek::get_parameter_list(List<PropertyInfo> *r_list) const {
- r_list->push_back(PropertyInfo(Variant::FLOAT, seek_pos, PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater"));
+ r_list->push_back(PropertyInfo(Variant::FLOAT, seek_pos_request, PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater")); // It will be reset to -1 after seeking the position immediately.
}
Variant AnimationNodeTimeSeek::get_parameter_default_value(const StringName &p_parameter) const {
- return 1.0; //initial timescale
+ return -1.0; // Initial seek request.
}
String AnimationNodeTimeSeek::get_caption() const {
- return "Seek";
+ return "TimeSeek";
}
double AnimationNodeTimeSeek::process(double p_time, bool p_seek, bool p_is_external_seeking) {
- double cur_seek_pos = get_parameter(seek_pos);
+ double cur_seek_pos = get_parameter(seek_pos_request);
if (p_seek) {
return blend_input(0, p_time, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
} else if (cur_seek_pos >= 0) {
double ret = blend_input(0, cur_seek_pos, true, true, 1.0, FILTER_IGNORE, true);
- set_parameter(seek_pos, -1.0); //reset
+ set_parameter(seek_pos_request, -1.0); // Reset.
return ret;
} else {
return blend_input(0, p_time, false, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
@@ -631,18 +644,76 @@ AnimationNodeTimeSeek::AnimationNodeTimeSeek() {
/////////////////////////////////////////////////
+bool AnimationNodeTransition::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (!path.begins_with("input_")) {
+ return false;
+ }
+
+ int which = path.get_slicec('/', 0).get_slicec('_', 1).to_int();
+ String what = path.get_slicec('/', 1);
+
+ if (which == get_input_count() && what == "name") {
+ if (add_input(p_value)) {
+ return true;
+ }
+ return false;
+ }
+
+ ERR_FAIL_INDEX_V(which, get_input_count(), false);
+
+ if (what == "name") {
+ set_input_name(which, p_value);
+ } else if (what == "auto_advance") {
+ set_input_as_auto_advance(which, p_value);
+ } else if (what == "reset") {
+ set_input_reset(which, p_value);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool AnimationNodeTransition::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (!path.begins_with("input_")) {
+ return false;
+ }
+
+ int which = path.get_slicec('/', 0).get_slicec('_', 1).to_int();
+ String what = path.get_slicec('/', 1);
+
+ ERR_FAIL_INDEX_V(which, get_input_count(), false);
+
+ if (what == "name") {
+ r_ret = get_input_name(which);
+ } else if (what == "auto_advance") {
+ r_ret = is_input_set_as_auto_advance(which);
+ } else if (what == "reset") {
+ r_ret = is_input_reset(which);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) const {
String anims;
- for (int i = 0; i < enabled_inputs; i++) {
+ for (int i = 0; i < get_input_count(); 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, "", PROPERTY_USAGE_NONE));
- r_list->push_back(PropertyInfo(Variant::INT, prev, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
+ r_list->push_back(PropertyInfo(Variant::STRING, current_state, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); // For interface.
+ r_list->push_back(PropertyInfo(Variant::STRING, transition_request, PROPERTY_HINT_ENUM, anims)); // For transition request. It will be cleared after setting the value immediately.
+ r_list->push_back(PropertyInfo(Variant::INT, current_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_READ_ONLY)); // To avoid finding the index every frame, use this internally.
+ r_list->push_back(PropertyInfo(Variant::INT, prev_index, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, time, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
r_list->push_back(PropertyInfo(Variant::FLOAT, prev_xfading, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE));
}
@@ -650,56 +721,74 @@ void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) con
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) {
+ } else if (p_parameter == prev_index || p_parameter == current_index) {
return -1;
} else {
- return 0;
+ return String();
}
}
+bool AnimationNodeTransition::is_parameter_read_only(const StringName &p_parameter) const {
+ if (p_parameter == current_state || p_parameter == current_index) {
+ return true;
+ }
+ return false;
+}
+
String AnimationNodeTransition::get_caption() const {
return "Transition";
}
-void AnimationNodeTransition::_update_inputs() {
- while (get_input_count() < enabled_inputs) {
- add_input(inputs[get_input_count()].name);
+void AnimationNodeTransition::set_input_count(int p_inputs) {
+ for (int i = get_input_count(); i < p_inputs; i++) {
+ add_input("state_" + itos(i));
}
-
- while (get_input_count() > enabled_inputs) {
+ while (get_input_count() > p_inputs) {
remove_input(get_input_count() - 1);
}
+
+ pending_update = true;
+
+ emit_signal(SNAME("tree_changed")); // For updating connect activity map.
+ notify_property_list_changed();
+}
+
+bool AnimationNodeTransition::add_input(const String &p_name) {
+ if (AnimationNode::add_input(p_name)) {
+ input_data.push_back(InputData());
+ return true;
+ }
+ return false;
}
-void AnimationNodeTransition::set_enabled_inputs(int p_inputs) {
- ERR_FAIL_INDEX(p_inputs, MAX_INPUTS);
- enabled_inputs = p_inputs;
- _update_inputs();
+void AnimationNodeTransition::remove_input(int p_index) {
+ input_data.remove_at(p_index);
+ AnimationNode::remove_input(p_index);
}
-int AnimationNodeTransition::get_enabled_inputs() {
- return enabled_inputs;
+bool AnimationNodeTransition::set_input_name(int p_input, const String &p_name) {
+ pending_update = true;
+ return AnimationNode::set_input_name(p_input, p_name);
}
void AnimationNodeTransition::set_input_as_auto_advance(int p_input, bool p_enable) {
- ERR_FAIL_INDEX(p_input, MAX_INPUTS);
- inputs[p_input].auto_advance = p_enable;
+ ERR_FAIL_INDEX(p_input, get_input_count());
+ input_data.write[p_input].auto_advance = p_enable;
}
bool AnimationNodeTransition::is_input_set_as_auto_advance(int p_input) const {
- ERR_FAIL_INDEX_V(p_input, MAX_INPUTS, false);
- return inputs[p_input].auto_advance;
+ ERR_FAIL_INDEX_V(p_input, get_input_count(), false);
+ return input_data[p_input].auto_advance;
}
-void AnimationNodeTransition::set_input_caption(int p_input, const String &p_name) {
- ERR_FAIL_INDEX(p_input, MAX_INPUTS);
- inputs[p_input].name = p_name;
- set_input_name(p_input, p_name);
+void AnimationNodeTransition::set_input_reset(int p_input, bool p_enable) {
+ ERR_FAIL_INDEX(p_input, get_input_count());
+ input_data.write[p_input].reset = p_enable;
}
-String AnimationNodeTransition::get_input_caption(int p_input) const {
- ERR_FAIL_INDEX_V(p_input, MAX_INPUTS, String());
- return inputs[p_input].name;
+bool AnimationNodeTransition::is_input_reset(int p_input) const {
+ ERR_FAIL_INDEX_V(p_input, get_input_count(), true);
+ return input_data[p_input].reset;
}
void AnimationNodeTransition::set_xfade_time(double p_fade) {
@@ -718,51 +807,96 @@ Ref<Curve> AnimationNodeTransition::get_xfade_curve() const {
return xfade_curve;
}
-void AnimationNodeTransition::set_from_start(bool p_from_start) {
- from_start = p_from_start;
+void AnimationNodeTransition::set_allow_transition_to_self(bool p_enable) {
+ allow_transition_to_self = p_enable;
}
-bool AnimationNodeTransition::is_from_start() const {
- return from_start;
+bool AnimationNodeTransition::is_allow_transition_to_self() const {
+ return allow_transition_to_self;
}
double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_external_seeking) {
- int cur_current = get_parameter(current);
- int cur_prev = get_parameter(prev);
- int cur_prev_current = get_parameter(prev_current);
+ String cur_transition_request = get_parameter(transition_request);
+ int cur_current_index = get_parameter(current_index);
+ int cur_prev_index = get_parameter(prev_index);
double cur_time = get_parameter(time);
double cur_prev_xfading = get_parameter(prev_xfading);
- bool switched = cur_current != cur_prev_current;
+ bool switched = false;
+ bool restart = false;
- if (switched) {
- set_parameter(prev_current, cur_current);
- set_parameter(prev, cur_prev_current);
+ if (pending_update) {
+ if (cur_current_index < 0 || cur_current_index >= get_input_count()) {
+ set_parameter(prev_index, -1);
+ if (get_input_count() > 0) {
+ set_parameter(current_index, 0);
+ set_parameter(current_state, get_input_name(0));
+ } else {
+ set_parameter(current_index, -1);
+ set_parameter(current_state, StringName());
+ }
+ } else {
+ set_parameter(current_state, get_input_name(cur_current_index));
+ }
+ pending_update = false;
+ }
- cur_prev = cur_prev_current;
+ if (!cur_transition_request.is_empty()) {
+ int new_idx = find_input(cur_transition_request);
+ if (new_idx >= 0) {
+ if (cur_current_index == new_idx) {
+ if (allow_transition_to_self) {
+ // Transition to same state.
+ restart = input_data[cur_current_index].reset;
+ cur_prev_xfading = 0;
+ set_parameter(prev_xfading, 0);
+ cur_prev_index = -1;
+ set_parameter(prev_index, -1);
+ }
+ } else {
+ switched = true;
+ cur_prev_index = cur_current_index;
+ set_parameter(prev_index, cur_current_index);
+ cur_current_index = new_idx;
+ set_parameter(current_index, cur_current_index);
+ set_parameter(current_state, cur_transition_request);
+ }
+ } else {
+ ERR_PRINT("No such input: '" + cur_transition_request + "'");
+ }
+ cur_transition_request = String();
+ set_parameter(transition_request, cur_transition_request);
+ }
+
+ // Special case for restart.
+ if (restart) {
+ set_parameter(time, 0);
+ return blend_input(cur_current_index, 0, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
+ }
+
+ if (switched) {
cur_prev_xfading = xfade_time;
cur_time = 0;
- switched = true;
}
- if (cur_current < 0 || cur_current >= enabled_inputs || cur_prev >= enabled_inputs) {
+ if (cur_current_index < 0 || cur_current_index >= get_input_count() || cur_prev_index >= get_input_count()) {
return 0;
}
double rem = 0.0;
if (sync) {
- for (int i = 0; i < enabled_inputs; i++) {
- if (i != cur_current && i != cur_prev) {
+ for (int i = 0; i < get_input_count(); i++) {
+ if (i != cur_current_index && i != cur_prev_index) {
blend_input(i, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true);
}
}
}
- if (cur_prev < 0) { // process current animation, check for transition
+ if (cur_prev_index < 0) { // Process current animation, check for transition.
- rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
+ rem = blend_input(cur_current_index, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true);
if (p_seek) {
cur_time = p_time;
@@ -770,34 +904,39 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex
cur_time += p_time;
}
- if (inputs[cur_current].auto_advance && rem <= xfade_time) {
- set_parameter(current, (cur_current + 1) % enabled_inputs);
+ if (input_data[cur_current_index].auto_advance && rem <= xfade_time) {
+ set_parameter(transition_request, get_input_name((cur_current_index + 1) % get_input_count()));
}
- } else { // cross-fading from prev to current
+ } else { // Cross-fading from prev to current.
- real_t blend = xfade_time == 0 ? 0 : (cur_prev_xfading / xfade_time);
+ bool use_blend = xfade_time > 0;
+ real_t blend = !use_blend ? 0 : (cur_prev_xfading / xfade_time);
if (xfade_curve.is_valid()) {
blend = xfade_curve->sample(blend);
}
// Blend values must be more than CMP_EPSILON to process discrete keys in edge.
real_t blend_inv = 1.0 - blend;
- if (from_start && !p_seek && switched) { //just switched, seek to start of current
- rem = blend_input(cur_current, 0, true, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true);
+ if (input_data[cur_current_index].reset && !p_seek && switched) { // Just switched, seek to start of current.
+ rem = blend_input(cur_current_index, 0, true, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true);
} else {
- rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true);
+ rem = blend_input(cur_current_index, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(blend_inv) ? CMP_EPSILON : blend_inv, FILTER_IGNORE, true);
}
if (p_seek) {
- blend_input(cur_prev, p_time, true, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true);
+ if (use_blend) {
+ blend_input(cur_prev_index, p_time, true, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true);
+ }
cur_time = p_time;
} else {
- blend_input(cur_prev, p_time, false, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true);
+ if (use_blend) {
+ blend_input(cur_prev_index, p_time, false, p_is_external_seeking, Math::is_zero_approx(blend) ? CMP_EPSILON : blend, FILTER_IGNORE, true);
+ }
cur_time += p_time;
cur_prev_xfading -= p_time;
if (cur_prev_xfading < 0) {
- set_parameter(prev, -1);
+ set_parameter(prev_index, -1);
}
}
}
@@ -808,27 +947,22 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_ex
return rem;
}
-void AnimationNodeTransition::_validate_property(PropertyInfo &p_property) const {
- if (p_property.name.begins_with("input_")) {
- String n = p_property.name.get_slicec('/', 0).get_slicec('_', 1);
- if (n != "count") {
- int idx = n.to_int();
- if (idx >= enabled_inputs) {
- p_property.usage = PROPERTY_USAGE_NONE;
- }
- }
+void AnimationNodeTransition::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < get_input_count(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/auto_advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/reset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL));
}
}
void AnimationNodeTransition::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_enabled_inputs", "amount"), &AnimationNodeTransition::set_enabled_inputs);
- ClassDB::bind_method(D_METHOD("get_enabled_inputs"), &AnimationNodeTransition::get_enabled_inputs);
+ ClassDB::bind_method(D_METHOD("set_input_count", "input_count"), &AnimationNodeTransition::set_input_count);
ClassDB::bind_method(D_METHOD("set_input_as_auto_advance", "input", "enable"), &AnimationNodeTransition::set_input_as_auto_advance);
ClassDB::bind_method(D_METHOD("is_input_set_as_auto_advance", "input"), &AnimationNodeTransition::is_input_set_as_auto_advance);
- 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_input_reset", "input", "enable"), &AnimationNodeTransition::set_input_reset);
+ ClassDB::bind_method(D_METHOD("is_input_reset", "input"), &AnimationNodeTransition::is_input_reset);
ClassDB::bind_method(D_METHOD("set_xfade_time", "time"), &AnimationNodeTransition::set_xfade_time);
ClassDB::bind_method(D_METHOD("get_xfade_time"), &AnimationNodeTransition::get_xfade_time);
@@ -836,24 +970,16 @@ void AnimationNodeTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeTransition::set_xfade_curve);
ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeTransition::get_xfade_curve);
- ClassDB::bind_method(D_METHOD("set_from_start", "from_start"), &AnimationNodeTransition::set_from_start);
- ClassDB::bind_method(D_METHOD("is_from_start"), &AnimationNodeTransition::is_from_start);
+ ClassDB::bind_method(D_METHOD("set_allow_transition_to_self", "enable"), &AnimationNodeTransition::set_allow_transition_to_self);
+ ClassDB::bind_method(D_METHOD("is_allow_transition_to_self"), &AnimationNodeTransition::is_allow_transition_to_self);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_inputs", 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::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01,suffix:s"), "set_xfade_time", "get_xfade_time");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "from_start"), "set_from_start", "is_from_start");
-
- for (int i = 0; i < MAX_INPUTS; i++) {
- ADD_PROPERTYI(PropertyInfo(Variant::STRING, "input_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_input_caption", "get_input_caption", i);
- ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "input_" + itos(i) + "/auto_advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_input_as_auto_advance", "is_input_set_as_auto_advance", i);
- }
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_transition_to_self"), "set_allow_transition_to_self", "is_allow_transition_to_self");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED, "Inputs,input_"), "set_input_count", "get_input_count");
}
AnimationNodeTransition::AnimationNodeTransition() {
- for (int i = 0; i < MAX_INPUTS; i++) {
- inputs[i].name = "state " + itos(i);
- }
}
/////////////////////
@@ -887,6 +1013,8 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNod
emit_signal(SNAME("tree_changed"));
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("animation_node_removed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
p_node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_name), CONNECT_REFERENCE_COUNTED);
}
@@ -949,12 +1077,14 @@ void AnimationNodeBlendTree::remove_node(const StringName &p_name) {
{
Ref<AnimationNode> node = nodes[p_name].node;
node->disconnect("tree_changed", callable_mp(this, &AnimationNodeBlendTree::_tree_changed));
+ node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_renamed));
+ node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeBlendTree::_animation_node_removed));
node->disconnect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed));
}
nodes.erase(p_name);
- //erase connections to name
+ // Erase connections to name.
for (KeyValue<StringName, Node> &E : nodes) {
for (int i = 0; i < E.value.connections.size(); i++) {
if (E.value.connections[i] == p_name) {
@@ -963,6 +1093,7 @@ void AnimationNodeBlendTree::remove_node(const StringName &p_name) {
}
}
+ emit_signal(SNAME("animation_node_removed"), get_instance_id(), p_name);
emit_changed();
emit_signal(SNAME("tree_changed"));
}
@@ -978,7 +1109,7 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN
nodes[p_new_name] = nodes[p_name];
nodes.erase(p_name);
- //rename connections
+ // Rename connections.
for (KeyValue<StringName, Node> &E : nodes) {
for (int i = 0; i < E.value.connections.size(); i++) {
if (E.value.connections[i] == p_name) {
@@ -986,9 +1117,10 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN
}
}
}
- //connection must be done with new name
+ // Connection must be done with new name.
nodes[p_new_name].node->connect("changed", callable_mp(this, &AnimationNodeBlendTree::_node_changed).bind(p_new_name), CONNECT_REFERENCE_COUNTED);
+ emit_signal(SNAME("animation_node_renamed"), get_instance_id(), p_name, p_new_name);
emit_signal(SNAME("tree_changed"));
}
@@ -1189,6 +1321,18 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons
p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
+void AnimationNodeBlendTree::_tree_changed() {
+ AnimationRootNode::_tree_changed();
+}
+
+void AnimationNodeBlendTree::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
+ AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
+}
+
+void AnimationNodeBlendTree::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
+ AnimationRootNode::_animation_node_removed(p_oid, p_node);
+}
+
void AnimationNodeBlendTree::reset_state() {
graph_offset = Vector2();
nodes.clear();
@@ -1197,10 +1341,6 @@ void AnimationNodeBlendTree::reset_state() {
emit_signal(SNAME("tree_changed"));
}
-void AnimationNodeBlendTree::_tree_changed() {
- emit_signal(SNAME("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());
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index e72471202e..d4827180bb 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -96,6 +96,12 @@ class AnimationNodeOneShot : public AnimationNodeSync {
GDCLASS(AnimationNodeOneShot, AnimationNodeSync);
public:
+ enum OneShotRequest {
+ ONE_SHOT_REQUEST_NONE,
+ ONE_SHOT_REQUEST_FIRE,
+ ONE_SHOT_REQUEST_ABORT,
+ };
+
enum MixMode {
MIX_MODE_BLEND,
MIX_MODE_ADD
@@ -110,13 +116,8 @@ private:
double autorestart_random_delay = 0.0;
MixMode mix = MIX_MODE_BLEND;
- /* bool active;
- bool do_start;
- double time;
- double remaining;*/
-
+ StringName request = PNAME("request");
StringName active = PNAME("active");
- StringName prev_active = "prev_active";
StringName time = "time";
StringName remaining = "remaining";
StringName time_to_restart = "time_to_restart";
@@ -127,6 +128,7 @@ protected:
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
+ virtual bool is_parameter_read_only(const StringName &p_parameter) const override;
virtual String get_caption() const override;
@@ -153,6 +155,7 @@ public:
AnimationNodeOneShot();
};
+VARIANT_ENUM_CAST(AnimationNodeOneShot::OneShotRequest)
VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode)
class AnimationNodeAdd2 : public AnimationNodeSync {
@@ -254,7 +257,7 @@ public:
class AnimationNodeTimeSeek : public AnimationNode {
GDCLASS(AnimationNodeTimeSeek, AnimationNode);
- StringName seek_pos = PNAME("seek_position");
+ StringName seek_pos_request = PNAME("seek_request");
protected:
static void _bind_methods();
@@ -273,54 +276,52 @@ public:
class AnimationNodeTransition : public AnimationNodeSync {
GDCLASS(AnimationNodeTransition, AnimationNodeSync);
- enum {
- MAX_INPUTS = 32
- };
struct InputData {
- String name;
bool auto_advance = false;
+ bool reset = true;
};
+ Vector<InputData> input_data;
- InputData inputs[MAX_INPUTS];
- int enabled_inputs = 0;
-
- /*
- double prev_xfading;
- int prev;
- double time;
- int current;
- int prev_current; */
-
- StringName prev_xfading = "prev_xfading";
- StringName prev = "prev";
StringName time = "time";
- StringName current = PNAME("current");
- StringName prev_current = "prev_current";
+ StringName prev_xfading = "prev_xfading";
+ StringName prev_index = "prev_index";
+ StringName current_index = PNAME("current_index");
+ StringName current_state = PNAME("current_state");
+ StringName transition_request = PNAME("transition_request");
+
+ StringName prev_frame_current = "pf_current";
+ StringName prev_frame_current_idx = "pf_current_idx";
double xfade_time = 0.0;
Ref<Curve> xfade_curve;
- bool from_start = true;
+ bool allow_transition_to_self = false;
- void _update_inputs();
+ bool pending_update = false;
protected:
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ bool _set(const StringName &p_path, const Variant &p_value);
static void _bind_methods();
- void _validate_property(PropertyInfo &p_property) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
+ virtual bool is_parameter_read_only(const StringName &p_parameter) const override;
virtual String get_caption() const override;
- void set_enabled_inputs(int p_inputs);
- int get_enabled_inputs();
+ void set_input_count(int p_inputs);
+
+ virtual bool add_input(const String &p_name) override;
+ virtual void remove_input(int p_index) override;
+ virtual bool set_input_name(int p_input, const String &p_name) override;
void set_input_as_auto_advance(int p_input, bool p_enable);
bool is_input_set_as_auto_advance(int p_input) const;
- void set_input_caption(int p_input, const String &p_name);
- String get_input_caption(int p_input) const;
+ void set_input_reset(int p_input, bool p_enable);
+ bool is_input_reset(int p_input) const;
void set_xfade_time(double p_fade);
double get_xfade_time() const;
@@ -328,8 +329,8 @@ public:
void set_xfade_curve(const Ref<Curve> &p_curve);
Ref<Curve> get_xfade_curve() const;
- void set_from_start(bool p_from_start);
- bool is_from_start() const;
+ void set_allow_transition_to_self(bool p_enable);
+ bool is_allow_transition_to_self() const;
double process(double p_time, bool p_seek, bool p_is_external_seeking) override;
@@ -360,7 +361,6 @@ class AnimationNodeBlendTree : public AnimationRootNode {
Vector2 graph_offset;
- void _tree_changed();
void _node_changed(const StringName &p_node);
void _initialize_node_tree();
@@ -371,6 +371,10 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual void _tree_changed() override;
+ virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
+ virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
+
virtual void reset_state() override;
public:
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index f5df64dbdd..d19d3cc7a3 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -107,6 +107,15 @@ Ref<Curve> AnimationNodeStateMachineTransition::get_xfade_curve() const {
return xfade_curve;
}
+void AnimationNodeStateMachineTransition::set_reset(bool p_reset) {
+ reset = p_reset;
+ emit_changed();
+}
+
+bool AnimationNodeStateMachineTransition::is_reset() const {
+ return reset;
+}
+
void AnimationNodeStateMachineTransition::set_priority(int p_priority) {
priority = p_priority;
emit_changed();
@@ -132,6 +141,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeStateMachineTransition::set_xfade_curve);
ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeStateMachineTransition::get_xfade_curve);
+ ClassDB::bind_method(D_METHOD("set_reset", "reset"), &AnimationNodeStateMachineTransition::set_reset);
+ ClassDB::bind_method(D_METHOD("is_reset"), &AnimationNodeStateMachineTransition::is_reset);
+
ClassDB::bind_method(D_METHOD("set_priority", "priority"), &AnimationNodeStateMachineTransition::set_priority);
ClassDB::bind_method(D_METHOD("get_priority"), &AnimationNodeStateMachineTransition::get_priority);
@@ -140,6 +152,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01,suffix:s"), "set_xfade_time", "get_xfade_time");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset"), "set_reset", "is_reset");
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority");
ADD_GROUP("Switch", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,At End"), "set_switch_mode", "get_switch_mode");
@@ -164,18 +179,27 @@ AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() {
////////////////////////////////////////////////////////
-void AnimationNodeStateMachinePlayback::travel(const StringName &p_state) {
- start_request_travel = true;
- start_request = p_state;
+void AnimationNodeStateMachinePlayback::travel(const StringName &p_state, bool p_reset_on_teleport) {
+ travel_request = p_state;
+ reset_request_on_teleport = p_reset_on_teleport;
stop_request = false;
}
-void AnimationNodeStateMachinePlayback::start(const StringName &p_state) {
- start_request_travel = false;
+void AnimationNodeStateMachinePlayback::start(const StringName &p_state, bool p_reset) {
+ travel_request = StringName();
+ reset_request = p_reset;
+ _start(p_state);
+}
+
+void AnimationNodeStateMachinePlayback::_start(const StringName &p_state) {
start_request = p_state;
stop_request = false;
}
+void AnimationNodeStateMachinePlayback::next() {
+ next_request = true;
+}
+
void AnimationNodeStateMachinePlayback::stop() {
stop_request = true;
}
@@ -188,7 +212,7 @@ StringName AnimationNodeStateMachinePlayback::get_current_node() const {
return current;
}
-StringName AnimationNodeStateMachinePlayback::get_blend_from_node() const {
+StringName AnimationNodeStateMachinePlayback::get_fading_from_node() const {
return fading_from;
}
@@ -204,6 +228,22 @@ float AnimationNodeStateMachinePlayback::get_current_length() const {
return len_current;
}
+float AnimationNodeStateMachinePlayback::get_fade_from_play_pos() const {
+ return pos_fade_from;
+}
+
+float AnimationNodeStateMachinePlayback::get_fade_from_length() const {
+ return len_fade_from;
+}
+
+float AnimationNodeStateMachinePlayback::get_fading_time() const {
+ return fading_time;
+}
+
+float AnimationNodeStateMachinePlayback::get_fading_pos() const {
+ return fading_pos;
+}
+
bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel) {
ERR_FAIL_COND_V(!playing, false);
ERR_FAIL_COND_V(!p_state_machine->states.has(p_travel), false);
@@ -212,7 +252,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
path.clear(); //a new one will be needed
if (current == p_travel) {
- return true; //nothing to do
+ return !p_state_machine->is_allow_transition_to_self();
}
Vector2 current_pos = p_state_machine->states[current].position;
@@ -323,6 +363,15 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
}
double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking) {
+ double rem = _process(p_state_machine, p_time, p_seek, p_is_external_seeking);
+ start_request = StringName();
+ next_request = false;
+ stop_request = false;
+ reset_request_on_teleport = false;
+ return rem;
+}
+
+double AnimationNodeStateMachinePlayback::_process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking) {
if (p_time == -1) {
Ref<AnimationNodeStateMachine> anodesm = p_state_machine->states[current].node;
if (anodesm.is_valid()) {
@@ -335,14 +384,13 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
//if not playing and it can restart, then restart
if (!playing && start_request == StringName()) {
if (!stop_request && p_state_machine->start_node) {
- start(p_state_machine->start_node);
+ _start(p_state_machine->start_node);
} else {
return 0;
}
}
if (playing && stop_request) {
- stop_request = false;
playing = false;
return 0;
}
@@ -350,42 +398,47 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
bool play_start = false;
if (start_request != StringName()) {
- if (start_request_travel) {
- if (!playing) {
- if (!stop_request && p_state_machine->start_node) {
- // can restart, just postpone traveling
- path.clear();
- current = p_state_machine->start_node;
- playing = true;
- play_start = true;
- } else {
- // stopped, invalid state
- String node_name = start_request;
- start_request = StringName(); //clear start request
- ERR_FAIL_V_MSG(0, "Can't travel to '" + node_name + "' if state machine is not playing. Maybe you need to enable Autoplay on Load for one of the nodes in your state machine or call .start() first?");
- }
- } else {
- if (!_travel(p_state_machine, start_request)) {
- // can't travel, then teleport
- path.clear();
- current = start_request;
- play_start = true;
- }
- start_request = StringName(); //clear start request
- }
+ // teleport to start
+ if (p_state_machine->states.has(start_request)) {
+ path.clear();
+ current = start_request;
+ playing = true;
+ play_start = true;
} else {
- // teleport to start
- if (p_state_machine->states.has(start_request)) {
+ StringName node = start_request;
+ ERR_FAIL_V_MSG(0, "No such node: '" + node + "'");
+ }
+ } else if (travel_request != StringName()) {
+ if (!playing) {
+ if (!stop_request && p_state_machine->start_node) {
+ // can restart, just postpone traveling
path.clear();
- current = start_request;
+ current = p_state_machine->start_node;
playing = true;
play_start = true;
- start_request = StringName(); //clear start request
} else {
- StringName node = start_request;
- start_request = StringName(); //clear start request
- ERR_FAIL_V_MSG(0, "No such node: '" + node + "'");
+ // stopped, invalid state
+ String node_name = travel_request;
+ travel_request = StringName();
+ ERR_FAIL_V_MSG(0, "Can't travel to '" + node_name + "' if state machine is not playing. Maybe you need to enable Autoplay on Load for one of the nodes in your state machine or call .start() first?");
+ }
+ } else {
+ if (!_travel(p_state_machine, travel_request)) {
+ // can't travel, then teleport
+ if (p_state_machine->states.has(travel_request)) {
+ path.clear();
+ if (current != travel_request || reset_request_on_teleport) {
+ current = travel_request;
+ play_start = true;
+ reset_request = reset_request_on_teleport;
+ }
+ } else {
+ StringName node = travel_request;
+ travel_request = StringName();
+ ERR_FAIL_V_MSG(0, "No such node: '" + node + "'");
+ }
}
+ travel_request = StringName();
}
}
@@ -396,8 +449,11 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
current = p_state_machine->start_node;
}
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 1.0, AnimationNode::FILTER_IGNORE, true);
- pos_current = 0;
+ if (reset_request) {
+ len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 1.0, AnimationNode::FILTER_IGNORE, true);
+ pos_current = 0;
+ reset_request = false;
+ }
}
if (!p_state_machine->states.has(current)) {
@@ -421,11 +477,22 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
if (current_curve.is_valid()) {
fade_blend = current_curve->sample(fade_blend);
}
- double rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend) ? CMP_EPSILON : fade_blend, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
+
+ double rem = do_start ? len_current : p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend) ? CMP_EPSILON : fade_blend, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
if (fading_from != StringName()) {
double fade_blend_inv = 1.0 - fade_blend;
- p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend_inv) ? CMP_EPSILON : fade_blend_inv, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
+ float fading_from_rem = 0.0;
+ fading_from_rem = p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, Math::is_zero_approx(fade_blend_inv) ? CMP_EPSILON : fade_blend_inv, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
+ //guess playback position
+ if (fading_from_rem > len_fade_from) { // weird but ok
+ len_fade_from = fading_from_rem;
+ }
+
+ { //advance and loop check
+ float next_pos = len_fade_from - fading_from_rem;
+ pos_fade_from = next_pos; //looped
+ }
if (fade_blend >= 1.0) {
fading_from = StringName();
}
@@ -457,6 +524,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
next_xfade = p_state_machine->transitions[i].transition->get_xfade_time();
current_curve = p_state_machine->transitions[i].transition->get_xfade_curve();
switch_mode = p_state_machine->transitions[i].transition->get_switch_mode();
+ reset_request = p_state_machine->transitions[i].transition->is_reset();
next = path[0];
}
}
@@ -513,6 +581,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
current_curve = p_state_machine->transitions[auto_advance_to].transition->get_xfade_curve();
next_xfade = p_state_machine->transitions[auto_advance_to].transition->get_xfade_time();
switch_mode = p_state_machine->transitions[auto_advance_to].transition->get_switch_mode();
+ reset_request = p_state_machine->transitions[auto_advance_to].transition->is_reset();
}
}
@@ -567,7 +636,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
goto_next = fading_from == StringName();
}
- if (goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped
+ if (next_request || goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped
if (next_xfade) {
//time to fade, baby
fading_from = current;
@@ -590,8 +659,12 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
current = next;
+ pos_fade_from = pos_current;
+ len_fade_from = len_current;
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, CMP_EPSILON, AnimationNode::FILTER_IGNORE, true); // Process next node's first key in here.
+ if (reset_request) {
+ len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, CMP_EPSILON, AnimationNode::FILTER_IGNORE, true); // Process next node's first key in here.
+ }
if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) {
pos_current = MIN(pos_current, len_current);
p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true);
@@ -652,13 +725,15 @@ bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<Anima
}
void AnimationNodeStateMachinePlayback::_bind_methods() {
- ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachinePlayback::travel);
- ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachinePlayback::start);
+ ClassDB::bind_method(D_METHOD("travel", "to_node", "reset_on_teleport"), &AnimationNodeStateMachinePlayback::travel, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("start", "node", "reset"), &AnimationNodeStateMachinePlayback::start, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("next"), &AnimationNodeStateMachinePlayback::next);
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_current_play_position"), &AnimationNodeStateMachinePlayback::get_current_play_pos);
ClassDB::bind_method(D_METHOD("get_current_length"), &AnimationNodeStateMachinePlayback::get_current_length);
+ ClassDB::bind_method(D_METHOD("get_fading_from_node"), &AnimationNodeStateMachinePlayback::get_fading_from_node);
ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachinePlayback::get_travel_path);
}
@@ -669,7 +744,7 @@ AnimationNodeStateMachinePlayback::AnimationNodeStateMachinePlayback() {
///////////////////////////////////////////////////////
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));
+ r_list->push_back(PropertyInfo(Variant::OBJECT, playback, PROPERTY_HINT_RESOURCE_TYPE, "AnimationNodeStateMachinePlayback", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE));
List<StringName> advance_conditions;
for (int i = 0; i < transitions.size(); i++) {
StringName ac = transitions[i].transition->get_advance_condition_name();
@@ -716,6 +791,8 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
emit_signal(SNAME("tree_changed"));
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("animation_node_removed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
}
void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<AnimationNode> p_node) {
@@ -727,6 +804,8 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima
Ref<AnimationNode> node = states[p_name].node;
if (node.is_valid()) {
node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
+ node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_renamed));
+ node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_removed));
}
}
@@ -736,6 +815,16 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima
emit_signal(SNAME("tree_changed"));
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("animation_node_renamed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_renamed), CONNECT_REFERENCE_COUNTED);
+ p_node->connect("animation_node_removed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_removed), CONNECT_REFERENCE_COUNTED);
+}
+
+void AnimationNodeStateMachine::set_allow_transition_to_self(bool p_enable) {
+ allow_transition_to_self = p_enable;
+}
+
+bool AnimationNodeStateMachine::is_allow_transition_to_self() const {
+ return allow_transition_to_self;
}
bool AnimationNodeStateMachine::can_edit_node(const StringName &p_name) const {
@@ -801,10 +890,13 @@ void AnimationNodeStateMachine::remove_node(const StringName &p_name) {
Ref<AnimationNode> node = states[p_name].node;
ERR_FAIL_COND(node.is_null());
node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
+ node->disconnect("animation_node_renamed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_renamed));
+ node->disconnect("animation_node_removed", callable_mp(this, &AnimationNodeStateMachine::_animation_node_removed));
}
states.erase(p_name);
+ emit_signal(SNAME("animation_node_removed"), get_instance_id(), p_name);
emit_changed();
emit_signal(SNAME("tree_changed"));
}
@@ -824,6 +916,7 @@ void AnimationNodeStateMachine::rename_node(const StringName &p_name, const Stri
_rename_transitions(p_name, p_new_name);
+ emit_signal(SNAME("animation_node_renamed"), get_instance_id(), p_name, p_new_name);
emit_changed();
emit_signal(SNAME("tree_changed"));
}
@@ -1282,7 +1375,15 @@ Vector2 AnimationNodeStateMachine::get_node_position(const StringName &p_name) c
void AnimationNodeStateMachine::_tree_changed() {
emit_changed();
- emit_signal(SNAME("tree_changed"));
+ AnimationRootNode::_tree_changed();
+}
+
+void AnimationNodeStateMachine::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
+ AnimationRootNode::_animation_node_renamed(p_oid, p_old_name, p_new_name);
+}
+
+void AnimationNodeStateMachine::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
+ AnimationRootNode::_animation_node_removed(p_oid, p_node);
}
void AnimationNodeStateMachine::_bind_methods() {
@@ -1308,6 +1409,11 @@ void AnimationNodeStateMachine::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeStateMachine::set_graph_offset);
ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeStateMachine::get_graph_offset);
+
+ ClassDB::bind_method(D_METHOD("set_allow_transition_to_self", "enable"), &AnimationNodeStateMachine::set_allow_transition_to_self);
+ ClassDB::bind_method(D_METHOD("is_allow_transition_to_self"), &AnimationNodeStateMachine::is_allow_transition_to_self);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_transition_to_self"), "set_allow_transition_to_self", "is_allow_transition_to_self");
}
AnimationNodeStateMachine::AnimationNodeStateMachine() {
diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h
index 116589eb2f..5867b6c65a 100644
--- a/scene/animation/animation_node_state_machine.h
+++ b/scene/animation/animation_node_state_machine.h
@@ -57,6 +57,7 @@ private:
StringName advance_condition_name;
float xfade_time = 0.0;
Ref<Curve> xfade_curve;
+ bool reset = true;
int priority = 1;
String advance_expression;
@@ -84,6 +85,9 @@ public:
void set_xfade_time(float p_xfade);
float get_xfade_time() const;
+ void set_reset(bool p_reset);
+ bool is_reset() const;
+
void set_xfade_curve(const Ref<Curve> &p_curve);
Ref<Curve> get_xfade_curve() const;
@@ -114,6 +118,9 @@ class AnimationNodeStateMachinePlayback : public Resource {
StringName next;
};
+ double len_fade_from = 0.0;
+ double pos_fade_from = 0.0;
+
double len_current = 0.0;
double pos_current = 0.0;
bool end_loop = false;
@@ -131,10 +138,15 @@ class AnimationNodeStateMachinePlayback : public Resource {
bool playing = false;
StringName start_request;
- bool start_request_travel = false;
+ StringName travel_request;
+ bool reset_request = false;
+ bool reset_request_on_teleport = false;
+ bool next_request = false;
bool stop_request = false;
bool _travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel);
+ void _start(const StringName &p_state);
+ double _process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking);
double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking);
@@ -144,16 +156,23 @@ protected:
static void _bind_methods();
public:
- void travel(const StringName &p_state);
- void start(const StringName &p_state);
+ void travel(const StringName &p_state, bool p_reset_on_teleport = true);
+ void start(const StringName &p_state, bool p_reset = true);
+ void next();
void stop();
bool is_playing() const;
StringName get_current_node() const;
- StringName get_blend_from_node() const;
+ StringName get_fading_from_node() const;
Vector<StringName> get_travel_path() const;
float get_current_play_pos() const;
float get_current_length() const;
+ float get_fade_from_play_pos() const;
+ float get_fade_from_length() const;
+
+ float get_fading_time() const;
+ float get_fading_pos() const;
+
AnimationNodeStateMachinePlayback();
};
@@ -169,6 +188,7 @@ private:
};
HashMap<StringName, State> states;
+ bool allow_transition_to_self = false;
struct Transition {
StringName from;
@@ -187,7 +207,6 @@ private:
Vector2 graph_offset;
- void _tree_changed();
void _remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition);
void _rename_transitions(const StringName &p_name, const StringName &p_new_name);
bool _can_connect(const StringName &p_name, Vector<AnimationNodeStateMachine *> p_parents = Vector<AnimationNodeStateMachine *>());
@@ -201,6 +220,10 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
+ virtual void _tree_changed() override;
+ virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) override;
+ virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node) override;
+
virtual void reset_state() override;
public:
@@ -235,6 +258,9 @@ public:
void remove_transition_by_index(const int p_transition);
void remove_transition(const StringName &p_from, const StringName &p_to);
+ void set_allow_transition_to_self(bool p_enable);
+ bool is_allow_transition_to_self() const;
+
bool can_edit_node(const StringName &p_name) const;
AnimationNodeStateMachine *get_prev_state_machine() const;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 7f42c8fac3..fc3a3d306f 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -143,8 +143,8 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const {
} else if (name.begins_with("libraries")) {
Dictionary d;
- for (uint32_t i = 0; i < animation_libraries.size(); i++) {
- d[animation_libraries[i].name] = animation_libraries[i].library;
+ for (const AnimationLibraryData &lib : animation_libraries) {
+ d[lib.name] = lib.library;
}
r_ret = d;
@@ -199,7 +199,7 @@ void AnimationPlayer::_validate_property(PropertyInfo &p_property) const {
void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> anim_names;
- anim_names.push_back(PropertyInfo(Variant::DICTIONARY, "libraries"));
+ anim_names.push_back(PropertyInfo(Variant::DICTIONARY, PNAME("libraries")));
for (const KeyValue<StringName, AnimationData> &E : animation_set) {
if (E.value.next != StringName()) {
@@ -431,6 +431,17 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
}
}
+ if (a->track_get_type(i) == Animation::TYPE_AUDIO) {
+ if (!node_cache->audio_anim.has(a->track_get_path(i).get_concatenated_names())) {
+ TrackNodeCache::AudioAnim aa;
+ aa.object = (Object *)child;
+ aa.audio_stream.instantiate();
+ aa.audio_stream->set_polyphony(audio_max_polyphony);
+
+ node_cache->audio_anim[a->track_get_path(i).get_concatenated_names()] = aa;
+ }
+ }
+
node_cache->last_setup_pass = setup_pass;
}
}
@@ -451,6 +462,15 @@ static void _call_object(Object *p_object, const StringName &p_method, const Vec
}
}
+Variant AnimationPlayer::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) {
+ Variant res;
+ if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, const_cast<Object *>(p_object), p_object_idx, res)) {
+ return res;
+ }
+
+ return _post_process_key_value(p_anim, p_track, p_value, p_object, p_object_idx);
+}
+
Variant AnimationPlayer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) {
switch (p_anim->track_get_type(p_track)) {
#ifndef _3D_DISABLED
@@ -473,7 +493,9 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count());
Animation *a = p_anim->animation.operator->();
+#ifdef TOOLS_ENABLED
bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint();
+#endif // TOOLS_ENABLED
bool backward = signbit(p_delta);
for (int i = 0; i < a->get_track_count(); i++) {
@@ -512,7 +534,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (err != OK) {
continue;
}
- loc = _post_process_key_value(a, i, loc, nc->node_3d, nc->bone_idx);
+ loc = post_process_key_value(a, i, loc, nc->node_3d, nc->bone_idx);
if (nc->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
@@ -540,7 +562,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (err != OK) {
continue;
}
- rot = _post_process_key_value(a, i, rot, nc->node_3d, nc->bone_idx);
+ rot = post_process_key_value(a, i, rot, nc->node_3d, nc->bone_idx);
if (nc->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
@@ -568,7 +590,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (err != OK) {
continue;
}
- scale = _post_process_key_value(a, i, scale, nc->node_3d, nc->bone_idx);
+ scale = post_process_key_value(a, i, scale, nc->node_3d, nc->bone_idx);
if (nc->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
@@ -596,7 +618,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (err != OK) {
continue;
}
- blend = _post_process_key_value(a, i, blend, nc->node_blend_shape, nc->blend_shape_idx);
+ blend = post_process_key_value(a, i, blend, nc->node_blend_shape, nc->blend_shape_idx);
if (nc->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
@@ -649,7 +671,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (p_time < first_key_time) {
double c = Math::ease(p_time / first_key_time, transition);
Variant first_value = a->track_get_key_value(i, first_key);
- first_value = _post_process_key_value(a, i, first_value, nc->node);
+ first_value = post_process_key_value(a, i, first_value, nc->node);
Variant interp_value = Animation::interpolate_variant(pa->capture, first_value, c);
if (pa->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX);
@@ -670,7 +692,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (value == Variant()) {
continue;
}
- value = _post_process_key_value(a, i, value, nc->node);
+ value = post_process_key_value(a, i, value, nc->node);
if (pa->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX);
@@ -701,7 +723,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
for (int &F : indices) {
Variant value = a->track_get_key_value(i, F);
- value = _post_process_key_value(a, i, value, nc->node);
+ value = post_process_key_value(a, i, value, nc->node);
switch (pa->special) {
case SP_NONE: {
bool valid;
@@ -745,11 +767,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} break;
case Animation::TYPE_METHOD: {
- if (!nc->node) {
+#ifdef TOOLS_ENABLED
+ if (!can_call) {
continue;
}
- if (!p_is_current) {
- break;
+#endif // TOOLS_ENABLED
+ if (!p_is_current || !nc->node || is_stopping) {
+ continue;
}
List<int> indices;
@@ -772,16 +796,12 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
for (int &E : indices) {
StringName method = a->method_track_get_name(i, E);
Vector<Variant> params = a->method_track_get_params(i, E);
-
#ifdef DEBUG_ENABLED
if (!nc->node->has_method(method)) {
ERR_PRINT("Invalid method call '" + method + "'. '" + a->get_name() + "' at node '" + get_path() + "'.");
}
#endif
-
- if (can_call) {
- _call_object(nc->node, method, params, method_call_mode == ANIMATION_METHOD_CALL_DEFERRED);
- }
+ _call_object(nc->node, method, params, method_call_mode == ANIMATION_METHOD_CALL_DEFERRED);
}
} break;
@@ -796,7 +816,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
TrackNodeCache::BezierAnim *ba = &E->value;
real_t bezier = a->bezier_track_interpolate(i, p_time);
- bezier = _post_process_key_value(a, i, bezier, nc->node);
+ bezier = post_process_key_value(a, i, bezier, nc->node);
if (ba->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_bezier_size >= NODE_CACHE_UPDATE_MAX);
cache_update_bezier[cache_update_bezier_size++] = ba;
@@ -808,113 +828,100 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} break;
case Animation::TYPE_AUDIO: {
- if (!nc->node) {
+ if (!nc->node || is_stopping) {
continue;
}
+#ifdef TOOLS_ENABLED
+ if (p_seeked && !can_call) {
+ continue; // To avoid spamming the preview in editor.
+ }
+#endif // TOOLS_ENABLED
+ HashMap<StringName, TrackNodeCache::AudioAnim>::Iterator E = nc->audio_anim.find(a->track_get_path(i).get_concatenated_names());
+ ERR_CONTINUE(!E); //should it continue, or create a new one?
- if (p_seeked) {
- //find whatever should be playing
- int idx = a->track_find_key(i, p_time);
- if (idx < 0) {
- continue;
- }
-
- Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
- if (!stream.is_valid()) {
- nc->node->call(SNAME("stop"));
- nc->audio_playing = false;
- playing_caches.erase(nc);
- } else {
- float start_ofs = a->audio_track_get_key_start_offset(i, idx);
- start_ofs += p_time - a->track_get_key_time(i, idx);
- float end_ofs = a->audio_track_get_key_end_offset(i, idx);
- float len = stream->get_length();
-
- if (start_ofs > len - end_ofs) {
- nc->node->call(SNAME("stop"));
- nc->audio_playing = false;
- playing_caches.erase(nc);
- continue;
- }
-
- nc->node->call(SNAME("set_stream"), stream);
- nc->node->call(SNAME("play"), start_ofs);
-
- nc->audio_playing = true;
- playing_caches.insert(nc);
- if (len && end_ofs > 0) { //force an end at a time
- nc->audio_len = len - start_ofs - end_ofs;
- } else {
- nc->audio_len = 0;
- }
+ TrackNodeCache::AudioAnim *aa = &E->value;
+ Node *asp = Object::cast_to<Node>(aa->object);
+ if (!asp) {
+ continue;
+ }
+ aa->length = a->get_length();
+ aa->time = p_time;
+ aa->loop = a->get_loop_mode() != Animation::LOOP_NONE;
+ aa->backward = backward;
+ if (aa->accum_pass != accum_pass) {
+ ERR_CONTINUE(cache_update_audio_size >= NODE_CACHE_UPDATE_MAX);
+ cache_update_audio[cache_update_audio_size++] = aa;
+ aa->accum_pass = accum_pass;
+ }
- nc->audio_start = p_time;
+ HashMap<int, TrackNodeCache::PlayingAudioStreamInfo> &map = aa->playing_streams;
+ // Find stream.
+ int idx = -1;
+ if (p_seeked || p_started) {
+ idx = a->track_find_key(i, p_time);
+ // Discard previous stream when seeking.
+ if (map.has(idx)) {
+ aa->audio_stream_playback->stop_stream(map[idx].index);
+ map.erase(idx);
}
-
} else {
- //find stuff to play
List<int> to_play;
- if (p_started) {
- int first_key = a->track_find_key(i, p_prev_time, Animation::FIND_MODE_EXACT);
- if (first_key >= 0) {
- to_play.push_back(first_key);
- }
- }
+
a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_looped_flag);
if (to_play.size()) {
- int idx = to_play.back()->get();
-
- Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
- if (!stream.is_valid()) {
- nc->node->call(SNAME("stop"));
- nc->audio_playing = false;
- playing_caches.erase(nc);
- } else {
- float start_ofs = a->audio_track_get_key_start_offset(i, idx);
- float end_ofs = a->audio_track_get_key_end_offset(i, idx);
- float len = stream->get_length();
-
- nc->node->call(SNAME("set_stream"), stream);
- nc->node->call(SNAME("play"), start_ofs);
-
- nc->audio_playing = true;
- playing_caches.insert(nc);
- if (len && end_ofs > 0) { //force an end at a time
- nc->audio_len = len - start_ofs - end_ofs;
- } else {
- nc->audio_len = 0;
- }
-
- nc->audio_start = p_time;
- }
- } else if (nc->audio_playing) {
- bool loop = a->get_loop_mode() != Animation::LOOP_NONE;
+ idx = to_play.back()->get();
+ }
+ }
+ if (idx < 0) {
+ continue;
+ }
- bool stop = false;
+ // Play stream.
+ Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
+ if (stream.is_valid()) {
+ double start_ofs = a->audio_track_get_key_start_offset(i, idx);
+ double end_ofs = a->audio_track_get_key_end_offset(i, idx);
+ double len = stream->get_length();
- if (!loop) {
- if ((p_time < nc->audio_start && !backward) || (p_time > nc->audio_start && backward)) {
- stop = true;
- }
- } else if (nc->audio_len > 0) {
- float len = nc->audio_start > p_time ? (a->get_length() - nc->audio_start) + p_time : p_time - nc->audio_start;
+ if (p_seeked || p_started) {
+ start_ofs += p_time - a->track_get_key_time(i, idx);
+ }
- if (len > nc->audio_len) {
- stop = true;
- }
+ if (aa->object->call(SNAME("get_stream")) != aa->audio_stream) {
+ aa->object->call(SNAME("set_stream"), aa->audio_stream);
+ aa->audio_stream_playback.unref();
+ if (!playing_audio_stream_players.has(asp)) {
+ playing_audio_stream_players.push_back(asp);
}
+ }
+ if (!aa->object->call(SNAME("is_playing"))) {
+ aa->object->call(SNAME("play"));
+ }
+ if (!aa->object->call(SNAME("has_stream_playback"))) {
+ aa->audio_stream_playback.unref();
+ continue;
+ }
+ if (aa->audio_stream_playback.is_null()) {
+ aa->audio_stream_playback = aa->object->call(SNAME("get_stream_playback"));
+ }
- if (stop) {
- //time to stop
- nc->node->call(SNAME("stop"));
- nc->audio_playing = false;
- playing_caches.erase(nc);
- }
+ TrackNodeCache::PlayingAudioStreamInfo pasi;
+ pasi.index = aa->audio_stream_playback->play_stream(stream, start_ofs);
+ pasi.start = p_time;
+ if (len && end_ofs > 0) { // Force an end at a time.
+ pasi.len = len - start_ofs - end_ofs;
+ } else {
+ pasi.len = 0;
}
+ map[idx] = pasi;
}
} break;
case Animation::TYPE_ANIMATION: {
+ if (is_stopping) {
+ continue;
+ }
+
AnimationPlayer *player = Object::cast_to<AnimationPlayer>(nc->node);
if (!player) {
continue;
@@ -1206,6 +1213,53 @@ void AnimationPlayer::_animation_update_transforms() {
ERR_CONTINUE(ba->accum_pass != accum_pass);
ba->object->set_indexed(ba->bezier_property, ba->bezier_accum);
}
+
+ for (int i = 0; i < cache_update_audio_size; i++) {
+ TrackNodeCache::AudioAnim *aa = cache_update_audio[i];
+
+ ERR_CONTINUE(aa->accum_pass != accum_pass);
+
+ // Audio ending process.
+ LocalVector<int> erase_list;
+ for (const KeyValue<int, TrackNodeCache::PlayingAudioStreamInfo> &K : aa->playing_streams) {
+ TrackNodeCache::PlayingAudioStreamInfo pasi = K.value;
+
+ bool stop = false;
+ if (!aa->audio_stream_playback->is_stream_playing(pasi.index)) {
+ stop = true;
+ }
+ if (!aa->loop) {
+ if (!aa->backward) {
+ if (aa->time < pasi.start) {
+ stop = true;
+ }
+ } else {
+ if (aa->time > pasi.start) {
+ stop = true;
+ }
+ }
+ }
+ if (pasi.len > 0) {
+ double len = 0.0;
+ if (!aa->backward) {
+ len = pasi.start > aa->time ? (aa->length - pasi.start) + aa->time : aa->time - pasi.start;
+ } else {
+ len = pasi.start < aa->time ? (aa->length - aa->time) + pasi.start : pasi.start - aa->time;
+ }
+ if (len > pasi.len) {
+ stop = true;
+ }
+ }
+ if (stop) {
+ // Time to stop.
+ aa->audio_stream_playback->stop_stream(pasi.index);
+ erase_list.push_back(K.key);
+ }
+ }
+ for (uint32_t erase_idx = 0; erase_idx < erase_list.size(); erase_idx++) {
+ aa->playing_streams.erase(erase_list[erase_idx]);
+ }
+ }
}
void AnimationPlayer::_animation_process(double p_delta) {
@@ -1221,6 +1275,7 @@ void AnimationPlayer::_animation_process(double p_delta) {
cache_update_size = 0;
cache_update_prop_size = 0;
cache_update_bezier_size = 0;
+ cache_update_audio_size = 0;
AnimationData *prev_from = playback.current.from;
_animation_process2(p_delta, started);
@@ -1230,6 +1285,8 @@ void AnimationPlayer::_animation_process(double p_delta) {
_animation_update_transforms();
if (end_reached) {
+ _clear_audio_streams();
+ _stop_playing_caches(false);
if (queued.size()) {
String old = playback.assigned;
play(queued.front()->get());
@@ -1265,13 +1322,13 @@ void AnimationPlayer::_animation_set_cache_update() {
bool clear_cache_needed = false;
// Update changed and add otherwise
- for (uint32_t i = 0; i < animation_libraries.size(); i++) {
- for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) {
- StringName key = animation_libraries[i].name == StringName() ? K.key : StringName(String(animation_libraries[i].name) + "/" + String(K.key));
+ for (const AnimationLibraryData &lib : animation_libraries) {
+ for (const KeyValue<StringName, Ref<Animation>> &K : lib.library->animations) {
+ StringName key = lib.name == StringName() ? K.key : StringName(String(lib.name) + "/" + String(K.key));
if (!animation_set.has(key)) {
AnimationData ad;
ad.animation = K.value;
- ad.animation_library = animation_libraries[i].name;
+ ad.animation_library = lib.name;
ad.name = key;
ad.last_update = animation_set_update_pass;
animation_set.insert(ad.name, ad);
@@ -1279,11 +1336,11 @@ void AnimationPlayer::_animation_set_cache_update() {
AnimationData &ad = animation_set[key];
if (ad.last_update != animation_set_update_pass) {
// Was not updated, update. If the animation is duplicated, the second one will be ignored.
- if (ad.animation != K.value || ad.animation_library != animation_libraries[i].name) {
+ if (ad.animation != K.value || ad.animation_library != lib.name) {
// Animation changed, update and clear caches.
clear_cache_needed = true;
ad.animation = K.value;
- ad.animation_library = animation_libraries[i].name;
+ ad.animation_library = lib.name;
}
ad.last_update = animation_set_update_pass;
@@ -1401,11 +1458,11 @@ Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref
int insert_pos = 0;
- for (uint32_t i = 0; i < animation_libraries.size(); i++) {
- ERR_FAIL_COND_V_MSG(animation_libraries[i].name == p_name, ERR_ALREADY_EXISTS, "Can't add animation library twice with name: " + String(p_name));
- ERR_FAIL_COND_V_MSG(animation_libraries[i].library == p_animation_library, ERR_ALREADY_EXISTS, "Can't add animation library twice (adding as '" + p_name.operator String() + "', exists as '" + animation_libraries[i].name.operator String() + "'.");
+ for (const AnimationLibraryData &lib : animation_libraries) {
+ ERR_FAIL_COND_V_MSG(lib.name == p_name, ERR_ALREADY_EXISTS, "Can't add animation library twice with name: " + String(p_name));
+ ERR_FAIL_COND_V_MSG(lib.library == p_animation_library, ERR_ALREADY_EXISTS, "Can't add animation library twice (adding as '" + p_name.operator String() + "', exists as '" + lib.name.operator String() + "'.");
- if (animation_libraries[i].name.operator String() >= p_name.operator String()) {
+ if (lib.name.operator String() >= p_name.operator String()) {
break;
}
@@ -1464,21 +1521,21 @@ void AnimationPlayer::rename_animation_library(const StringName &p_name, const S
#endif
bool found = false;
- for (uint32_t i = 0; i < animation_libraries.size(); i++) {
- ERR_FAIL_COND_MSG(animation_libraries[i].name == p_new_name, "Can't rename animation library to another existing name: " + String(p_new_name));
- if (animation_libraries[i].name == p_name) {
+ for (AnimationLibraryData &lib : animation_libraries) {
+ ERR_FAIL_COND_MSG(lib.name == p_new_name, "Can't rename animation library to another existing name: " + String(p_new_name));
+ if (lib.name == p_name) {
found = true;
- animation_libraries[i].name = p_new_name;
+ lib.name = p_new_name;
// rename connections
- animation_libraries[i].library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added));
- animation_libraries[i].library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed));
- animation_libraries[i].library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed));
+ lib.library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added));
+ lib.library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed));
+ lib.library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed));
- animation_libraries[i].library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_new_name));
- animation_libraries[i].library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed).bind(p_new_name));
- animation_libraries[i].library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_new_name));
+ lib.library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_new_name));
+ lib.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed).bind(p_new_name));
+ lib.library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_new_name));
- for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) {
+ for (const KeyValue<StringName, Ref<Animation>> &K : lib.library->animations) {
StringName old_name = p_name == StringName() ? K.key : StringName(String(p_name) + "/" + String(K.key));
StringName new_name = p_new_name == StringName() ? K.key : StringName(String(p_new_name) + "/" + String(K.key));
_rename_animation(old_name, new_name);
@@ -1498,8 +1555,8 @@ void AnimationPlayer::rename_animation_library(const StringName &p_name, const S
}
bool AnimationPlayer::has_animation_library(const StringName &p_name) const {
- for (uint32_t i = 0; i < animation_libraries.size(); i++) {
- if (animation_libraries[i].name == p_name) {
+ for (const AnimationLibraryData &lib : animation_libraries) {
+ if (lib.name == p_name) {
return true;
}
}
@@ -1508,9 +1565,9 @@ bool AnimationPlayer::has_animation_library(const StringName &p_name) const {
}
Ref<AnimationLibrary> AnimationPlayer::get_animation_library(const StringName &p_name) const {
- for (uint32_t i = 0; i < animation_libraries.size(); i++) {
- if (animation_libraries[i].name == p_name) {
- return animation_libraries[i].library;
+ for (const AnimationLibraryData &lib : animation_libraries) {
+ if (lib.name == p_name) {
+ return lib.library;
}
}
ERR_FAIL_V(Ref<AnimationLibrary>());
@@ -1518,15 +1575,15 @@ Ref<AnimationLibrary> AnimationPlayer::get_animation_library(const StringName &p
TypedArray<StringName> AnimationPlayer::_get_animation_library_list() const {
TypedArray<StringName> ret;
- for (uint32_t i = 0; i < animation_libraries.size(); i++) {
- ret.push_back(animation_libraries[i].name);
+ for (const AnimationLibraryData &lib : animation_libraries) {
+ ret.push_back(lib.name);
}
return ret;
}
void AnimationPlayer::get_animation_library_list(List<StringName> *p_libraries) const {
- for (uint32_t i = 0; i < animation_libraries.size(); i++) {
- p_libraries->push_back(animation_libraries[i].name);
+ for (const AnimationLibraryData &lib : animation_libraries) {
+ p_libraries->push_back(lib.name);
}
}
@@ -1658,7 +1715,8 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa
}
if (get_current_animation() != p_name) {
- _stop_playing_caches();
+ _clear_audio_streams();
+ _stop_playing_caches(false);
}
c.current.from = &animation_set[name];
@@ -1705,8 +1763,11 @@ bool AnimationPlayer::is_playing() const {
void AnimationPlayer::set_current_animation(const String &p_anim) {
if (p_anim == "[stop]" || p_anim.is_empty()) {
stop();
- } else if (!is_playing() || playback.assigned != p_anim) {
+ } else if (!is_playing()) {
play(p_anim);
+ } else if (playback.assigned != p_anim) {
+ float speed = get_playing_speed();
+ play(p_anim, -1.0, speed, signbit(speed));
} else {
// Same animation, do not replay from start
}
@@ -1718,7 +1779,8 @@ String AnimationPlayer::get_current_animation() const {
void AnimationPlayer::set_assigned_animation(const String &p_anim) {
if (is_playing()) {
- play(p_anim);
+ float speed = get_playing_speed();
+ play(p_anim, -1.0, speed, signbit(speed));
} else {
ERR_FAIL_COND_MSG(!animation_set.has(p_anim), vformat("Animation not found: %s.", p_anim));
playback.current.pos = 0;
@@ -1731,18 +1793,12 @@ String AnimationPlayer::get_assigned_animation() const {
return playback.assigned;
}
-void AnimationPlayer::stop(bool p_reset) {
- _stop_playing_caches();
- Playback &c = playback;
- c.blend.clear();
- if (p_reset) {
- c.current.from = nullptr;
- c.current.speed_scale = 1;
- c.current.pos = 0;
- }
- _set_process(false);
- queued.clear();
- playing = false;
+void AnimationPlayer::pause() {
+ _stop_internal(false, false);
+}
+
+void AnimationPlayer::stop(bool p_keep_state) {
+ _stop_internal(true, p_keep_state);
}
void AnimationPlayer::set_speed_scale(float p_speed) {
@@ -1761,15 +1817,18 @@ float AnimationPlayer::get_playing_speed() const {
}
void AnimationPlayer::seek(double p_time, bool p_update) {
+ playback.current.pos = p_time;
+
if (!playback.current.from) {
if (playback.assigned) {
ERR_FAIL_COND_MSG(!animation_set.has(playback.assigned), vformat("Animation not found: %s.", playback.assigned));
playback.current.from = &animation_set[playback.assigned];
}
- ERR_FAIL_COND(!playback.current.from);
+ if (!playback.current.from) {
+ return; // There is no animation.
+ }
}
- playback.current.pos = p_time;
playback.seeked = true;
if (p_update) {
_animation_process(0);
@@ -1777,20 +1836,22 @@ void AnimationPlayer::seek(double p_time, bool p_update) {
}
void AnimationPlayer::seek_delta(double p_time, double p_delta) {
+ playback.current.pos = p_time - p_delta;
+
if (!playback.current.from) {
if (playback.assigned) {
ERR_FAIL_COND_MSG(!animation_set.has(playback.assigned), vformat("Animation not found: %s.", playback.assigned));
playback.current.from = &animation_set[playback.assigned];
}
- ERR_FAIL_COND(!playback.current.from);
+ if (!playback.current.from) {
+ return; // There is no animation.
+ }
}
- playback.current.pos = p_time - p_delta;
if (speed_scale != 0.0) {
p_delta /= speed_scale;
}
_animation_process(p_delta);
- //playback.current.pos=p_time;
}
bool AnimationPlayer::is_valid() const {
@@ -1814,7 +1875,7 @@ void AnimationPlayer::_animation_changed(const StringName &p_name) {
}
}
-void AnimationPlayer::_stop_playing_caches() {
+void AnimationPlayer::_stop_playing_caches(bool p_reset) {
for (TrackNodeCache *E : playing_caches) {
if (E->node && E->audio_playing) {
E->node->call(SNAME("stop"));
@@ -1824,7 +1885,12 @@ void AnimationPlayer::_stop_playing_caches() {
if (!player) {
continue;
}
- player->stop();
+
+ if (p_reset) {
+ player->stop();
+ } else {
+ player->pause();
+ }
}
}
@@ -1836,7 +1902,8 @@ void AnimationPlayer::_node_removed(Node *p_node) {
}
void AnimationPlayer::clear_caches() {
- _stop_playing_caches();
+ _clear_audio_streams();
+ _stop_playing_caches(true);
node_cache_map.clear();
@@ -1847,10 +1914,19 @@ void AnimationPlayer::clear_caches() {
cache_update_size = 0;
cache_update_prop_size = 0;
cache_update_bezier_size = 0;
+ cache_update_audio_size = 0;
emit_signal(SNAME("caches_cleared"));
}
+void AnimationPlayer::_clear_audio_streams() {
+ for (int i = 0; i < playing_audio_stream_players.size(); i++) {
+ playing_audio_stream_players[i]->call(SNAME("stop"));
+ playing_audio_stream_players[i]->call(SNAME("set_stream"), Ref<AudioStream>());
+ }
+ playing_audio_stream_players.clear();
+}
+
void AnimationPlayer::set_active(bool p_active) {
if (active == p_active) {
return;
@@ -1930,6 +2006,15 @@ AnimationPlayer::AnimationMethodCallMode AnimationPlayer::get_method_call_mode()
return method_call_mode;
}
+void AnimationPlayer::set_audio_max_polyphony(int p_audio_max_polyphony) {
+ ERR_FAIL_COND(p_audio_max_polyphony < 0 || p_audio_max_polyphony > 128);
+ audio_max_polyphony = p_audio_max_polyphony;
+}
+
+int AnimationPlayer::get_audio_max_polyphony() const {
+ return audio_max_polyphony;
+}
+
void AnimationPlayer::set_movie_quit_on_finish_enabled(bool p_enabled) {
movie_quit_on_finish = p_enabled;
}
@@ -1957,6 +2042,27 @@ void AnimationPlayer::_set_process(bool p_process, bool p_force) {
processing = p_process;
}
+void AnimationPlayer::_stop_internal(bool p_reset, bool p_keep_state) {
+ _clear_audio_streams();
+ _stop_playing_caches(p_reset);
+ Playback &c = playback;
+ c.blend.clear();
+ if (p_reset) {
+ if (p_keep_state) {
+ c.current.pos = 0;
+ } else {
+ is_stopping = true;
+ seek(0, true);
+ is_stopping = false;
+ }
+ c.current.from = nullptr;
+ c.current.speed_scale = 1;
+ }
+ _set_process(false);
+ queued.clear();
+ playing = false;
+}
+
void AnimationPlayer::animation_set_next(const StringName &p_animation, const StringName &p_next) {
ERR_FAIL_COND_MSG(!animation_set.has(p_animation), vformat("Animation not found: %s.", p_animation));
animation_set[p_animation].next = p_next;
@@ -2081,7 +2187,7 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) {
Ref<AnimatedValuesBackup> new_values = aux_player->backup_animated_values();
old_values->restore();
- Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo();
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Animation Apply Reset"));
ur->add_do_method(new_values.ptr(), "restore");
ur->add_undo_method(old_values.ptr(), "restore");
@@ -2119,7 +2225,8 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play, DEFVAL(""), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::play_backwards, DEFVAL(""), DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("stop", "reset"), &AnimationPlayer::stop, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("pause"), &AnimationPlayer::pause);
+ ClassDB::bind_method(D_METHOD("stop", "keep_state"), &AnimationPlayer::stop, DEFVAL(false));
ClassDB::bind_method(D_METHOD("is_playing"), &AnimationPlayer::is_playing);
ClassDB::bind_method(D_METHOD("set_current_animation", "anim"), &AnimationPlayer::set_current_animation);
@@ -2157,6 +2264,9 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_method_call_mode", "mode"), &AnimationPlayer::set_method_call_mode);
ClassDB::bind_method(D_METHOD("get_method_call_mode"), &AnimationPlayer::get_method_call_mode);
+ ClassDB::bind_method(D_METHOD("set_audio_max_polyphony", "max_polyphony"), &AnimationPlayer::set_audio_max_polyphony);
+ ClassDB::bind_method(D_METHOD("get_audio_max_polyphony"), &AnimationPlayer::get_audio_max_polyphony);
+
ClassDB::bind_method(D_METHOD("set_movie_quit_on_finish_enabled", "enabled"), &AnimationPlayer::set_movie_quit_on_finish_enabled);
ClassDB::bind_method(D_METHOD("is_movie_quit_on_finish_enabled"), &AnimationPlayer::is_movie_quit_on_finish_enabled);
@@ -2166,6 +2276,8 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("seek", "seconds", "update"), &AnimationPlayer::seek, DEFVAL(false));
ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationPlayer::advance);
+ GDVIRTUAL_BIND(_post_process_key_value, "animation", "track", "value", "object", "object_idx");
+
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_node"), "set_root", "get_root");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR), "set_current_animation", "get_current_animation");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "assigned_animation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_assigned_animation", "get_assigned_animation");
@@ -2178,8 +2290,9 @@ void AnimationPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_process_callback", "get_process_callback");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_default_blend_time", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:s"), "set_default_blend_time", "get_default_blend_time");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playback_active", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_active", "is_active");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::INT, "method_call_mode", PROPERTY_HINT_ENUM, "Deferred,Immediate"), "set_method_call_mode", "get_method_call_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_max_polyphony", PROPERTY_HINT_RANGE, "1,127,1"), "set_audio_max_polyphony", "get_audio_max_polyphony");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "movie_quit_on_finish"), "set_movie_quit_on_finish_enabled", "is_movie_quit_on_finish_enabled");
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index f431253876..b0975fbead 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -37,6 +37,7 @@
#include "scene/3d/skeleton_3d.h"
#include "scene/resources/animation.h"
#include "scene/resources/animation_library.h"
+#include "scene/resources/audio_stream_polyphonic.h"
#ifdef TOOLS_ENABLED
class AnimatedValuesBackup : public RefCounted {
@@ -147,6 +148,26 @@ private:
HashMap<StringName, BezierAnim> bezier_anim;
+ struct PlayingAudioStreamInfo {
+ AudioStreamPlaybackPolyphonic::ID index = -1;
+ double start = 0.0;
+ double len = 0.0;
+ };
+
+ struct AudioAnim {
+ Ref<AudioStreamPolyphonic> audio_stream;
+ Ref<AudioStreamPlaybackPolyphonic> audio_stream_playback;
+ HashMap<int, PlayingAudioStreamInfo> playing_streams;
+ Object *object = nullptr;
+ uint64_t accum_pass = 0;
+ double length = 0.0;
+ double time = 0.0;
+ bool loop = false;
+ bool backward = false;
+ };
+
+ HashMap<StringName, AudioAnim> audio_anim;
+
uint32_t last_setup_pass = 0;
TrackNodeCache() {}
};
@@ -187,11 +208,15 @@ private:
int cache_update_prop_size = 0;
TrackNodeCache::BezierAnim *cache_update_bezier[NODE_CACHE_UPDATE_MAX];
int cache_update_bezier_size = 0;
+ TrackNodeCache::AudioAnim *cache_update_audio[NODE_CACHE_UPDATE_MAX];
+ int cache_update_audio_size = 0;
HashSet<TrackNodeCache *> playing_caches;
+ Vector<Node *> playing_audio_stream_players;
uint64_t accum_pass = 1;
float speed_scale = 1.0;
double default_blend_time = 0.0;
+ bool is_stopping = false;
struct AnimationData {
String name;
@@ -262,6 +287,7 @@ private:
bool reset_on_save = true;
AnimationProcessCallback process_callback = ANIMATION_PROCESS_IDLE;
AnimationMethodCallMode method_call_mode = ANIMATION_METHOD_CALL_DEFERRED;
+ int audio_max_polyphony = 32;
bool movie_quit_on_finish = false;
bool processing = false;
bool active = true;
@@ -277,7 +303,8 @@ private:
void _animation_process(double p_delta);
void _node_removed(Node *p_node);
- void _stop_playing_caches();
+ void _clear_audio_streams();
+ void _stop_playing_caches(bool p_reset);
// bind helpers
Vector<String> _get_animation_list() const {
@@ -294,6 +321,7 @@ private:
void _animation_changed(const StringName &p_name);
void _set_process(bool p_process, bool p_force = false);
+ void _stop_internal(bool p_reset, bool p_keep_state);
bool playing = false;
@@ -315,6 +343,8 @@ protected:
static void _bind_methods();
+ GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, Object *, int);
+ Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1);
virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1);
public:
@@ -346,7 +376,8 @@ public:
void queue(const StringName &p_name);
Vector<String> get_queue();
void clear_queue();
- void stop(bool p_reset = true);
+ void pause();
+ void stop(bool p_keep_state = false);
bool is_playing() const;
String get_current_animation() const;
void set_current_animation(const String &p_anim);
@@ -372,6 +403,9 @@ public:
void set_method_call_mode(AnimationMethodCallMode p_mode);
AnimationMethodCallMode get_method_call_mode() const;
+ void set_audio_max_polyphony(int p_audio_max_polyphony);
+ int get_audio_max_polyphony() const;
+
void set_movie_quit_on_finish_enabled(bool p_enabled);
bool is_movie_quit_on_finish_enabled() const;
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index ab341797c7..9f9916c1c6 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -54,13 +54,19 @@ Variant AnimationNode::get_parameter_default_value(const StringName &p_parameter
return ret;
}
+bool AnimationNode::is_parameter_read_only(const StringName &p_parameter) const {
+ bool ret = false;
+ GDVIRTUAL_CALL(_is_parameter_read_only, p_parameter, ret);
+ return ret;
+}
+
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;
+ state->tree->property_map[path].first = p_value;
}
Variant AnimationNode::get_parameter(const StringName &p_name) const {
@@ -69,7 +75,7 @@ Variant AnimationNode::get_parameter(const StringName &p_name) const {
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];
+ return state->tree->property_map[path].first;
}
void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {
@@ -297,36 +303,21 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_is_external_seeking, p_connections);
}
-int AnimationNode::get_input_count() const {
- return inputs.size();
-}
-
-String AnimationNode::get_input_name(int p_input) {
- ERR_FAIL_INDEX_V(p_input, inputs.size(), String());
- return inputs[p_input].name;
-}
-
String AnimationNode::get_caption() const {
String ret = "Node";
GDVIRTUAL_CALL(_get_caption, ret);
return ret;
}
-void AnimationNode::add_input(const String &p_name) {
+bool AnimationNode::add_input(const String &p_name) {
//root nodes can't add inputs
- ERR_FAIL_COND(Object::cast_to<AnimationRootNode>(this) != nullptr);
+ ERR_FAIL_COND_V(Object::cast_to<AnimationRootNode>(this) != nullptr, false);
Input input;
- ERR_FAIL_COND(p_name.contains(".") || p_name.contains("/"));
+ ERR_FAIL_COND_V(p_name.contains(".") || p_name.contains("/"), false);
input.name = p_name;
inputs.push_back(input);
emit_changed();
-}
-
-void AnimationNode::set_input_name(int p_input, const String &p_name) {
- ERR_FAIL_INDEX(p_input, inputs.size());
- ERR_FAIL_COND(p_name.contains(".") || p_name.contains("/"));
- inputs.write[p_input].name = p_name;
- emit_changed();
+ return true;
}
void AnimationNode::remove_input(int p_index) {
@@ -335,6 +326,34 @@ void AnimationNode::remove_input(int p_index) {
emit_changed();
}
+bool AnimationNode::set_input_name(int p_input, const String &p_name) {
+ ERR_FAIL_INDEX_V(p_input, inputs.size(), false);
+ ERR_FAIL_COND_V(p_name.contains(".") || p_name.contains("/"), false);
+ inputs.write[p_input].name = p_name;
+ emit_changed();
+ return true;
+}
+
+String AnimationNode::get_input_name(int p_input) const {
+ ERR_FAIL_INDEX_V(p_input, inputs.size(), String());
+ return inputs[p_input].name;
+}
+
+int AnimationNode::get_input_count() const {
+ return inputs.size();
+}
+
+int AnimationNode::find_input(const String &p_name) const {
+ int idx = -1;
+ for (int i = 0; i < inputs.size(); i++) {
+ if (inputs[i].name == p_name) {
+ idx = i;
+ break;
+ }
+ }
+ return idx;
+}
+
double AnimationNode::process(double p_time, bool p_seek, bool p_is_external_seeking) {
double ret = 0;
GDVIRTUAL_CALL(_process, p_time, p_seek, p_is_external_seeking, ret);
@@ -398,11 +417,12 @@ Ref<AnimationNode> AnimationNode::get_child_by_name(const StringName &p_name) {
}
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("add_input", "name"), &AnimationNode::add_input);
ClassDB::bind_method(D_METHOD("remove_input", "index"), &AnimationNode::remove_input);
+ ClassDB::bind_method(D_METHOD("set_input_name", "input", "name"), &AnimationNode::set_input_name);
+ ClassDB::bind_method(D_METHOD("get_input_name", "input"), &AnimationNode::get_input_name);
+ ClassDB::bind_method(D_METHOD("get_input_count"), &AnimationNode::get_input_count);
+ ClassDB::bind_method(D_METHOD("find_input", "name"), &AnimationNode::find_input);
ClassDB::bind_method(D_METHOD("set_filter_path", "path", "enable"), &AnimationNode::set_filter_path);
ClassDB::bind_method(D_METHOD("is_path_filtered", "path"), &AnimationNode::is_path_filtered);
@@ -427,11 +447,14 @@ void AnimationNode::_bind_methods() {
GDVIRTUAL_BIND(_get_parameter_list);
GDVIRTUAL_BIND(_get_child_by_name, "name");
GDVIRTUAL_BIND(_get_parameter_default_value, "parameter");
+ GDVIRTUAL_BIND(_is_parameter_read_only, "parameter");
GDVIRTUAL_BIND(_process, "time", "seek", "is_external_seeking");
GDVIRTUAL_BIND(_get_caption);
GDVIRTUAL_BIND(_has_filter);
ADD_SIGNAL(MethodInfo("tree_changed"));
+ ADD_SIGNAL(MethodInfo("animation_node_renamed", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name")));
+ ADD_SIGNAL(MethodInfo("animation_node_removed", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "name")));
BIND_ENUM_CONSTANT(FILTER_IGNORE);
BIND_ENUM_CONSTANT(FILTER_PASS);
@@ -444,15 +467,33 @@ AnimationNode::AnimationNode() {
////////////////////
+void AnimationRootNode::_tree_changed() {
+ emit_signal(SNAME("tree_changed"));
+}
+
+void AnimationRootNode::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
+ emit_signal(SNAME("animation_node_renamed"), p_oid, p_old_name, p_new_name);
+}
+
+void AnimationRootNode::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
+ emit_signal(SNAME("animation_node_removed"), p_oid, p_node);
+}
+
+////////////////////
+
void AnimationTree::set_tree_root(const Ref<AnimationNode> &p_root) {
if (root.is_valid()) {
root->disconnect("tree_changed", callable_mp(this, &AnimationTree::_tree_changed));
+ root->disconnect("animation_node_renamed", callable_mp(this, &AnimationTree::_animation_node_renamed));
+ root->disconnect("animation_node_removed", callable_mp(this, &AnimationTree::_animation_node_removed));
}
root = p_root;
if (root.is_valid()) {
root->connect("tree_changed", callable_mp(this, &AnimationTree::_tree_changed));
+ root->connect("animation_node_renamed", callable_mp(this, &AnimationTree::_animation_node_renamed));
+ root->connect("animation_node_removed", callable_mp(this, &AnimationTree::_animation_node_removed));
}
properties_dirty = true;
@@ -479,13 +520,7 @@ void AnimationTree::set_active(bool p_active) {
}
if (!active && is_inside_tree()) {
- for (const TrackCache *E : playing_caches) {
- if (ObjectDB::get_instance(E->object_id)) {
- E->object->call(SNAME("stop"));
- }
- }
-
- playing_caches.clear();
+ _clear_caches();
}
}
@@ -524,6 +559,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
if (!player->has_node(player->get_root())) {
ERR_PRINT("AnimationTree: AnimationPlayer root is invalid.");
set_active(false);
+ _clear_caches();
return false;
}
Node *parent = player->get_node(player->get_root());
@@ -531,6 +567,10 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
List<StringName> sname;
player->get_animation_list(&sname);
+ root_motion_cache.loc = Vector3(0, 0, 0);
+ root_motion_cache.rot = Quaternion(0, 0, 0, 1);
+ root_motion_cache.scale = Vector3(1, 1, 1);
+
Ref<Animation> reset_anim;
bool has_reset_anim = player->has_animation(SceneStringNames::get_singleton()->RESET);
if (has_reset_anim) {
@@ -747,7 +787,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
if (has_reset_anim) {
int rt = reset_anim->find_track(path, track_type);
if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) {
- track_bezier->init_value = reset_anim->track_get_key_value(rt, 0);
+ track_bezier->init_value = (reset_anim->track_get_key_value(rt, 0).operator Array())[0];
}
}
} break;
@@ -756,6 +796,8 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_audio->object = child;
track_audio->object_id = track_audio->object->get_instance_id();
+ track_audio->audio_stream.instantiate();
+ track_audio->audio_stream->set_polyphony(audio_max_polyphony);
track = track_audio;
@@ -853,14 +895,32 @@ void AnimationTree::_animation_player_changed() {
}
void AnimationTree::_clear_caches() {
+ _clear_audio_streams();
+ _clear_playing_caches();
for (KeyValue<NodePath, TrackCache *> &K : track_cache) {
memdelete(K.value);
}
- playing_caches.clear();
track_cache.clear();
cache_valid = false;
}
+void AnimationTree::_clear_audio_streams() {
+ for (int i = 0; i < playing_audio_stream_players.size(); i++) {
+ playing_audio_stream_players[i]->call(SNAME("stop"));
+ playing_audio_stream_players[i]->call(SNAME("set_stream"), Ref<AudioStream>());
+ }
+ playing_audio_stream_players.clear();
+}
+
+void AnimationTree::_clear_playing_caches() {
+ for (const TrackCache *E : playing_caches) {
+ if (ObjectDB::get_instance(E->object_id)) {
+ E->object->call(SNAME("stop"));
+ }
+ }
+ playing_caches.clear();
+}
+
static void _call_object(Object *p_object, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred) {
// Separate function to use alloca() more efficiently
const Variant **argptrs = (const Variant **)alloca(sizeof(const Variant **) * p_params.size());
@@ -979,14 +1039,13 @@ void AnimationTree::_process_graph(double p_delta) {
case Animation::TYPE_POSITION_3D: {
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (track->root_motion) {
- t->loc = Vector3(0, 0, 0);
- t->rot = Quaternion(0, 0, 0, 1);
- t->scale = Vector3(1, 1, 1);
- } else {
- t->loc = t->init_loc;
- t->rot = t->init_rot;
- t->scale = t->init_scale;
+ root_motion_cache.loc = Vector3(0, 0, 0);
+ root_motion_cache.rot = Quaternion(0, 0, 0, 1);
+ root_motion_cache.scale = Vector3(1, 1, 1);
}
+ t->loc = t->init_loc;
+ t->rot = t->init_rot;
+ t->scale = t->init_scale;
} break;
case Animation::TYPE_BLEND_SHAPE: {
TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track);
@@ -1000,6 +1059,13 @@ void AnimationTree::_process_graph(double p_delta) {
TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
t->value = t->init_value;
} break;
+ case Animation::TYPE_AUDIO: {
+ TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
+ for (KeyValue<ObjectID, PlayingAudioTrackInfo> &L : t->playing_streams) {
+ PlayingAudioTrackInfo &track_info = L.value;
+ track_info.volume = 0.0;
+ }
+ } break;
default: {
} break;
}
@@ -1008,8 +1074,9 @@ void AnimationTree::_process_graph(double p_delta) {
// Apply value/transform/blend/bezier blends to track caches and execute method/audio/animation tracks.
{
+#ifdef TOOLS_ENABLED
bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint();
-
+#endif // TOOLS_ENABLED
for (const AnimationNode::AnimationState &as : state.animation_states) {
Ref<Animation> a = as.animation;
double time = as.time;
@@ -1018,8 +1085,8 @@ void AnimationTree::_process_graph(double p_delta) {
bool seeked = as.seeked;
Animation::LoopedFlag looped_flag = as.looped_flag;
bool is_external_seeking = as.is_external_seeking;
+ bool backward = signbit(delta); // This flag is used by the root motion calculates or detecting the end of audio stream.
#ifndef _3D_DISABLED
- bool backward = signbit(delta); // This flag is required only for the root motion since it calculates the difference between the previous and current frames.
bool calc_root = !seeked || is_external_seeking;
#endif // _3D_DISABLED
@@ -1038,9 +1105,6 @@ void AnimationTree::_process_graph(double p_delta) {
int blend_idx = state.track_map[path];
ERR_CONTINUE(blend_idx < 0 || blend_idx >= state.track_count);
real_t blend = (*as.track_blends)[blend_idx] * weight;
- if (Math::is_zero_approx(blend)) {
- continue; // Nothing to blend.
- }
Animation::TrackType ttype = a->track_get_type(i);
if (ttype != Animation::TYPE_POSITION_3D && ttype != Animation::TYPE_ROTATION_3D && ttype != Animation::TYPE_SCALE_3D && track->type != ttype) {
@@ -1052,7 +1116,11 @@ void AnimationTree::_process_graph(double p_delta) {
switch (ttype) {
case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
+
if (track->root_motion && calc_root) {
double prev_time = time - delta;
if (!backward) {
@@ -1097,10 +1165,10 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
- loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
+ loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
a->position_track_interpolate(i, (double)a->get_length(), &loc[1]);
- loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
- t->loc += (loc[1] - loc[0]) * blend;
+ loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
+ root_motion_cache.loc += (loc[1] - loc[0]) * blend;
prev_time = 0;
}
} else {
@@ -1109,10 +1177,10 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
- loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
+ loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
a->position_track_interpolate(i, 0, &loc[1]);
- loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
- t->loc += (loc[1] - loc[0]) * blend;
+ loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
+ root_motion_cache.loc += (loc[1] - loc[0]) * blend;
prev_time = (double)a->get_length();
}
}
@@ -1121,21 +1189,21 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
- loc[0] = _post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
-
+ loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx);
a->position_track_interpolate(i, time, &loc[1]);
- loc[1] = _post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
- t->loc += (loc[1] - loc[0]) * blend;
+ loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx);
+ root_motion_cache.loc += (loc[1] - loc[0]) * blend;
prev_time = !backward ? 0 : (double)a->get_length();
+ }
- } else {
+ {
Vector3 loc;
Error err = a->position_track_interpolate(i, time, &loc);
if (err != OK) {
continue;
}
- loc = _post_process_key_value(a, i, loc, t->object, t->bone_idx);
+ loc = post_process_key_value(a, i, loc, t->object, t->bone_idx);
t->loc += (loc - t->init_loc) * blend;
}
@@ -1143,7 +1211,11 @@ void AnimationTree::_process_graph(double p_delta) {
} break;
case Animation::TYPE_ROTATION_3D: {
#ifndef _3D_DISABLED
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
+
if (track->root_motion && calc_root) {
double prev_time = time - delta;
if (!backward) {
@@ -1188,10 +1260,10 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
- rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
+ rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
a->rotation_track_interpolate(i, (double)a->get_length(), &rot[1]);
- rot[1] = _post_process_key_value(a, i, rot[1], t->object, t->bone_idx);
- t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
+ rot[1] = post_process_key_value(a, i, rot[1], t->object, t->bone_idx);
+ root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = 0;
}
} else {
@@ -1200,9 +1272,9 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
- rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
+ rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
a->rotation_track_interpolate(i, 0, &rot[1]);
- t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
+ root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = (double)a->get_length();
}
}
@@ -1211,21 +1283,22 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
- rot[0] = _post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
+ rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx);
a->rotation_track_interpolate(i, time, &rot[1]);
- rot[1] = _post_process_key_value(a, i, rot[1], t->object, t->bone_idx);
- t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
+ rot[1] = post_process_key_value(a, i, rot[1], t->object, t->bone_idx);
+ root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = !backward ? 0 : (double)a->get_length();
+ }
- } else {
+ {
Quaternion rot;
Error err = a->rotation_track_interpolate(i, time, &rot);
if (err != OK) {
continue;
}
- rot = _post_process_key_value(a, i, rot, t->object, t->bone_idx);
+ rot = post_process_key_value(a, i, rot, t->object, t->bone_idx);
t->rot = (t->rot * Quaternion().slerp(t->init_rot.inverse() * rot, blend)).normalized();
}
@@ -1233,7 +1306,11 @@ void AnimationTree::_process_graph(double p_delta) {
} break;
case Animation::TYPE_SCALE_3D: {
#ifndef _3D_DISABLED
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
+
if (track->root_motion && calc_root) {
double prev_time = time - delta;
if (!backward) {
@@ -1278,10 +1355,10 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
- scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
+ scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
a->scale_track_interpolate(i, (double)a->get_length(), &scale[1]);
- t->scale += (scale[1] - scale[0]) * blend;
- scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
+ root_motion_cache.scale += (scale[1] - scale[0]) * blend;
+ scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
prev_time = 0;
}
} else {
@@ -1290,10 +1367,10 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
- scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
+ scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
a->scale_track_interpolate(i, 0, &scale[1]);
- scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
- t->scale += (scale[1] - scale[0]) * blend;
+ scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
+ root_motion_cache.scale += (scale[1] - scale[0]) * blend;
prev_time = (double)a->get_length();
}
}
@@ -1302,21 +1379,22 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
- scale[0] = _post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
+ scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx);
a->scale_track_interpolate(i, time, &scale[1]);
- scale[1] = _post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
- t->scale += (scale[1] - scale[0]) * blend;
+ scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx);
+ root_motion_cache.scale += (scale[1] - scale[0]) * blend;
prev_time = !backward ? 0 : (double)a->get_length();
+ }
- } else {
+ {
Vector3 scale;
Error err = a->scale_track_interpolate(i, time, &scale);
if (err != OK) {
continue;
}
- scale = _post_process_key_value(a, i, scale, t->object, t->bone_idx);
+ scale = post_process_key_value(a, i, scale, t->object, t->bone_idx);
t->scale += (scale - t->init_scale) * blend;
}
@@ -1324,6 +1402,9 @@ void AnimationTree::_process_graph(double p_delta) {
} break;
case Animation::TYPE_BLEND_SHAPE: {
#ifndef _3D_DISABLED
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track);
float value;
@@ -1334,19 +1415,22 @@ void AnimationTree::_process_graph(double p_delta) {
if (err != OK) {
continue;
}
- value = _post_process_key_value(a, i, value, t->object, t->shape_index);
+ value = post_process_key_value(a, i, value, t->object, t->shape_index);
t->value += (value - t->init_value) * blend;
#endif // _3D_DISABLED
} break;
case Animation::TYPE_VALUE: {
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
Animation::UpdateMode update_mode = a->value_track_get_update_mode(i);
if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) {
Variant value = a->value_track_interpolate(i, time);
- value = _post_process_key_value(a, i, value, t->object);
+ value = post_process_key_value(a, i, value, t->object);
if (value == Variant()) {
continue;
@@ -1386,14 +1470,14 @@ void AnimationTree::_process_graph(double p_delta) {
continue;
}
Variant value = a->track_get_key_value(i, idx);
- value = _post_process_key_value(a, i, value, t->object);
+ value = post_process_key_value(a, i, value, t->object);
t->object->set_indexed(t->subpath, value);
} else {
List<int> indices;
a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag);
for (int &F : indices) {
Variant value = a->track_get_key_value(i, F);
- value = _post_process_key_value(a, i, value, t->object);
+ value = post_process_key_value(a, i, value, t->object);
t->object->set_indexed(t->subpath, value);
}
}
@@ -1401,6 +1485,14 @@ void AnimationTree::_process_graph(double p_delta) {
} break;
case Animation::TYPE_METHOD: {
+#ifdef TOOLS_ENABLED
+ if (!can_call) {
+ continue;
+ }
+#endif // TOOLS_ENABLED
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track);
if (seeked) {
@@ -1410,137 +1502,116 @@ void AnimationTree::_process_graph(double p_delta) {
}
StringName method = a->method_track_get_name(i, idx);
Vector<Variant> params = a->method_track_get_params(i, idx);
- if (can_call) {
- _call_object(t->object, method, params, false);
- }
+ _call_object(t->object, method, params, false);
} else {
List<int> indices;
a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag);
for (int &F : indices) {
StringName method = a->method_track_get_name(i, F);
Vector<Variant> params = a->method_track_get_params(i, F);
- if (can_call) {
- _call_object(t->object, method, params, true);
- }
+ _call_object(t->object, method, params, true);
}
}
} break;
case Animation::TYPE_BEZIER: {
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
real_t bezier = a->bezier_track_interpolate(i, time);
- bezier = _post_process_key_value(a, i, bezier, t->object);
+ bezier = post_process_key_value(a, i, bezier, t->object);
t->value += (bezier - t->init_value) * blend;
} break;
case Animation::TYPE_AUDIO: {
TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
- if (seeked) {
- //find whatever should be playing
- int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT);
- if (idx < 0) {
- continue;
- }
-
- Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
- if (!stream.is_valid()) {
- t->object->call(SNAME("stop"));
- t->playing = false;
- playing_caches.erase(t);
- } else {
- double start_ofs = a->audio_track_get_key_start_offset(i, idx);
- start_ofs += time - a->track_get_key_time(i, idx);
- double end_ofs = a->audio_track_get_key_end_offset(i, idx);
- double len = stream->get_length();
-
- if (start_ofs > len - end_ofs) {
- t->object->call(SNAME("stop"));
- t->playing = false;
- playing_caches.erase(t);
- continue;
- }
-
- t->object->call(SNAME("set_stream"), stream);
- t->object->call(SNAME("play"), start_ofs);
-
- t->playing = true;
- playing_caches.insert(t);
- if (len && end_ofs > 0) { //force an end at a time
- t->len = len - start_ofs - end_ofs;
- } else {
- t->len = 0;
- }
+ Node *asp = Object::cast_to<Node>(t->object);
+ if (!asp) {
+ t->playing_streams.clear();
+ continue;
+ }
- t->start = time;
+ ObjectID oid = a->get_instance_id();
+ if (!t->playing_streams.has(oid)) {
+ t->playing_streams[oid] = PlayingAudioTrackInfo();
+ }
+ // The end of audio should be observed even if the blend value is 0, build up the information and store to the cache for that.
+ PlayingAudioTrackInfo &track_info = t->playing_streams[oid];
+ track_info.length = a->get_length();
+ track_info.time = time;
+ track_info.volume += blend;
+ track_info.loop = a->get_loop_mode() != Animation::LOOP_NONE;
+ track_info.backward = backward;
+ track_info.use_blend = a->audio_track_is_use_blend(i);
+
+ HashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
+ // Find stream.
+ int idx = -1;
+ if (seeked) {
+ idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT);
+ // Discard previous stream when seeking.
+ if (map.has(idx)) {
+ t->audio_stream_playback->stop_stream(map[idx].index);
+ map.erase(idx);
}
-
} else {
- //find stuff to play
List<int> to_play;
a->track_get_key_indices_in_range(i, time, delta, &to_play, looped_flag);
if (to_play.size()) {
- int idx = to_play.back()->get();
-
- Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
- if (!stream.is_valid()) {
- t->object->call(SNAME("stop"));
- t->playing = false;
- playing_caches.erase(t);
- } else {
- double start_ofs = a->audio_track_get_key_start_offset(i, idx);
- double end_ofs = a->audio_track_get_key_end_offset(i, idx);
- double len = stream->get_length();
-
- t->object->call(SNAME("set_stream"), stream);
- t->object->call(SNAME("play"), start_ofs);
+ idx = to_play.back()->get();
+ }
+ }
+ if (idx < 0) {
+ continue;
+ }
- t->playing = true;
- playing_caches.insert(t);
- if (len && end_ofs > 0) { //force an end at a time
- t->len = len - start_ofs - end_ofs;
- } else {
- t->len = 0;
- }
+ // Play stream.
+ Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
+ if (stream.is_valid()) {
+ double start_ofs = a->audio_track_get_key_start_offset(i, idx);
+ double end_ofs = a->audio_track_get_key_end_offset(i, idx);
+ double len = stream->get_length();
- t->start = time;
- }
- } else if (t->playing) {
- bool loop = a->get_loop_mode() != Animation::LOOP_NONE;
-
- bool stop = false;
-
- if (!loop) {
- if (delta > 0) {
- if (time < t->start) {
- stop = true;
- }
- } else if (delta < 0) {
- if (time > t->start) {
- stop = true;
- }
- }
- } else if (t->len > 0) {
- double len = t->start > time ? (a->get_length() - t->start) + time : time - t->start;
+ if (seeked) {
+ start_ofs += time - a->track_get_key_time(i, idx);
+ }
- if (len > t->len) {
- stop = true;
- }
+ if (t->object->call(SNAME("get_stream")) != t->audio_stream) {
+ t->object->call(SNAME("set_stream"), t->audio_stream);
+ t->audio_stream_playback.unref();
+ if (!playing_audio_stream_players.has(asp)) {
+ playing_audio_stream_players.push_back(asp);
}
+ }
+ if (!t->object->call(SNAME("is_playing"))) {
+ t->object->call(SNAME("play"));
+ }
+ if (!t->object->call(SNAME("has_stream_playback"))) {
+ t->audio_stream_playback.unref();
+ continue;
+ }
+ if (t->audio_stream_playback.is_null()) {
+ t->audio_stream_playback = t->object->call(SNAME("get_stream_playback"));
+ }
- if (stop) {
- //time to stop
- t->object->call(SNAME("stop"));
- t->playing = false;
- playing_caches.erase(t);
- }
+ PlayingAudioStreamInfo pasi;
+ pasi.index = t->audio_stream_playback->play_stream(stream, start_ofs);
+ pasi.start = time;
+ if (len && end_ofs > 0) { // Force an end at a time.
+ pasi.len = len - start_ofs - end_ofs;
+ } else {
+ pasi.len = 0;
}
+ map[idx] = pasi;
}
- real_t db = Math::linear_to_db(MAX(blend, 0.00001));
- t->object->call(SNAME("set_volume_db"), db);
} break;
case Animation::TYPE_ANIMATION: {
+ if (Math::is_zero_approx(blend)) {
+ continue; // Nothing to blend.
+ }
TrackCacheAnimation *t = static_cast<TrackCacheAnimation *>(track);
AnimationPlayer *player2 = Object::cast_to<AnimationPlayer>(t->object);
@@ -1629,10 +1700,12 @@ void AnimationTree::_process_graph(double p_delta) {
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (t->root_motion) {
- root_motion_position = t->loc;
- root_motion_rotation = t->rot;
- root_motion_scale = t->scale - Vector3(1, 1, 1);
-
+ root_motion_position = root_motion_cache.loc;
+ root_motion_rotation = root_motion_cache.rot;
+ root_motion_scale = root_motion_cache.scale - Vector3(1, 1, 1);
+ root_motion_position_accumulator = t->loc;
+ root_motion_rotation_accumulator = t->rot;
+ root_motion_scale_accumulator = t->scale;
} else if (t->skeleton && t->bone_idx >= 0) {
if (t->loc_used) {
t->skeleton->set_bone_pose_position(t->bone_idx, t->loc);
@@ -1686,6 +1759,64 @@ void AnimationTree::_process_graph(double p_delta) {
t->object->set_indexed(t->subpath, t->value);
} break;
+ case Animation::TYPE_AUDIO: {
+ TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
+
+ // Audio ending process.
+ LocalVector<ObjectID> erase_maps;
+ for (KeyValue<ObjectID, PlayingAudioTrackInfo> &L : t->playing_streams) {
+ PlayingAudioTrackInfo &track_info = L.value;
+ float db = Math::linear_to_db(track_info.use_blend ? track_info.volume : 1.0);
+ LocalVector<int> erase_streams;
+ HashMap<int, PlayingAudioStreamInfo> &map = track_info.stream_info;
+ for (const KeyValue<int, PlayingAudioStreamInfo> &M : map) {
+ PlayingAudioStreamInfo pasi = M.value;
+
+ bool stop = false;
+ if (!t->audio_stream_playback->is_stream_playing(pasi.index)) {
+ stop = true;
+ }
+ if (!track_info.loop) {
+ if (!track_info.backward) {
+ if (track_info.time < pasi.start) {
+ stop = true;
+ }
+ } else if (track_info.backward) {
+ if (track_info.time > pasi.start) {
+ stop = true;
+ }
+ }
+ }
+ if (pasi.len > 0) {
+ double len = 0.0;
+ if (!track_info.backward) {
+ len = pasi.start > track_info.time ? (track_info.length - pasi.start) + track_info.time : track_info.time - pasi.start;
+ } else {
+ len = pasi.start < track_info.time ? (track_info.length - track_info.time) + pasi.start : pasi.start - track_info.time;
+ }
+ if (len > pasi.len) {
+ stop = true;
+ }
+ }
+ if (stop) {
+ // Time to stop.
+ t->audio_stream_playback->stop_stream(pasi.index);
+ erase_streams.push_back(M.key);
+ } else {
+ t->audio_stream_playback->set_stream_volume(pasi.index, db);
+ }
+ }
+ for (uint32_t erase_idx = 0; erase_idx < erase_streams.size(); erase_idx++) {
+ map.erase(erase_streams[erase_idx]);
+ }
+ if (map.size() == 0) {
+ erase_maps.push_back(L.key);
+ }
+ }
+ for (uint32_t erase_idx = 0; erase_idx < erase_maps.size(); erase_idx++) {
+ t->playing_streams.erase(erase_maps[erase_idx]);
+ }
+ } break;
default: {
} //the rest don't matter
}
@@ -1693,6 +1824,15 @@ void AnimationTree::_process_graph(double p_delta) {
}
}
+Variant AnimationTree::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) {
+ Variant res;
+ if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, const_cast<Object *>(p_object), p_object_idx, res)) {
+ return res;
+ }
+
+ return _post_process_key_value(p_anim, p_track, p_value, p_object, p_object_idx);
+}
+
Variant AnimationTree::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) {
switch (p_anim->track_get_type(p_track)) {
#ifndef _3D_DISABLED
@@ -1755,6 +1895,8 @@ void AnimationTree::_setup_animation_player() {
return;
}
+ cache_valid = false;
+
AnimationPlayer *new_player = nullptr;
if (!animation_player.is_empty()) {
new_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player));
@@ -1802,6 +1944,15 @@ NodePath AnimationTree::get_advance_expression_base_node() const {
return advance_expression_base_node;
}
+void AnimationTree::set_audio_max_polyphony(int p_audio_max_polyphony) {
+ ERR_FAIL_COND(p_audio_max_polyphony < 0 || p_audio_max_polyphony > 128);
+ audio_max_polyphony = p_audio_max_polyphony;
+}
+
+int AnimationTree::get_audio_max_polyphony() const {
+ return audio_max_polyphony;
+}
+
bool AnimationTree::is_state_invalid() const {
return !state.valid;
}
@@ -1856,6 +2007,18 @@ Vector3 AnimationTree::get_root_motion_scale() const {
return root_motion_scale;
}
+Vector3 AnimationTree::get_root_motion_position_accumulator() const {
+ return root_motion_position_accumulator;
+}
+
+Quaternion AnimationTree::get_root_motion_rotation_accumulator() const {
+ return root_motion_rotation_accumulator;
+}
+
+Vector3 AnimationTree::get_root_motion_scale_accumulator() const {
+ return root_motion_scale_accumulator;
+}
+
void AnimationTree::_tree_changed() {
if (properties_dirty) {
return;
@@ -1865,11 +2028,46 @@ void AnimationTree::_tree_changed() {
properties_dirty = true;
}
+void AnimationTree::_animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name) {
+ ERR_FAIL_COND(!property_reference_map.has(p_oid));
+ String base_path = property_reference_map[p_oid];
+ String old_base = base_path + p_old_name;
+ String new_base = base_path + p_new_name;
+ for (const PropertyInfo &E : properties) {
+ if (E.name.begins_with(old_base)) {
+ String new_name = E.name.replace_first(old_base, new_base);
+ property_map[new_name] = property_map[E.name];
+ property_map.erase(E.name);
+ }
+ }
+
+ //update tree second
+ properties_dirty = true;
+ _update_properties();
+}
+
+void AnimationTree::_animation_node_removed(const ObjectID &p_oid, const StringName &p_node) {
+ ERR_FAIL_COND(!property_reference_map.has(p_oid));
+ String base_path = String(property_reference_map[p_oid]) + String(p_node);
+ for (const PropertyInfo &E : properties) {
+ if (E.name.begins_with(base_path)) {
+ property_map.erase(E.name);
+ }
+ }
+
+ //update tree second
+ properties_dirty = true;
+ _update_properties();
+}
+
void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<AnimationNode> node) {
ERR_FAIL_COND(node.is_null());
if (!property_parent_map.has(p_base_path)) {
property_parent_map[p_base_path] = HashMap<StringName, StringName>();
}
+ if (!property_reference_map.has(node->get_instance_id())) {
+ property_reference_map[node->get_instance_id()] = p_base_path;
+ }
if (node->get_input_count() && !input_activity_map.has(p_base_path)) {
Vector<Activity> activity;
@@ -1889,7 +2087,10 @@ void AnimationTree::_update_properties_for_node(const String &p_base_path, Ref<A
StringName key = pinfo.name;
if (!property_map.has(p_base_path + key)) {
- property_map[p_base_path + key] = node->get_parameter_default_value(key);
+ Pair<Variant, bool> param;
+ param.first = node->get_parameter_default_value(key);
+ param.second = node->is_parameter_read_only(key);
+ property_map[p_base_path + key] = param;
}
property_parent_map[p_base_path][key] = p_base_path + key;
@@ -1912,6 +2113,7 @@ void AnimationTree::_update_properties() {
}
properties.clear();
+ property_reference_map.clear();
property_parent_map.clear();
input_activity_map.clear();
input_activity_map_get.clear();
@@ -1931,7 +2133,10 @@ bool AnimationTree::_set(const StringName &p_name, const Variant &p_value) {
}
if (property_map.has(p_name)) {
- property_map[p_name] = p_value;
+ if (is_inside_tree() && property_map[p_name].second) {
+ return false; // Prevent to set property by user.
+ }
+ property_map[p_name].first = p_value;
return true;
}
@@ -1944,7 +2149,7 @@ bool AnimationTree::_get(const StringName &p_name, Variant &r_ret) const {
}
if (property_map.has(p_name)) {
- r_ret = property_map[p_name];
+ r_ret = property_map[p_name].first;
return true;
}
@@ -1961,20 +2166,6 @@ void AnimationTree::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
-void AnimationTree::rename_parameter(const String &p_base, const String &p_new_base) {
- //rename values first
- for (const PropertyInfo &E : properties) {
- if (E.name.begins_with(p_base)) {
- String new_name = E.name.replace_first(p_base, p_new_base);
- property_map[new_name] = property_map[E.name];
- }
- }
-
- //update tree second
- properties_dirty = true;
- _update_properties();
-}
-
real_t AnimationTree::get_connection_activity(const StringName &p_path, int p_connection) const {
if (!input_activity_map_get.has(p_path)) {
return 0;
@@ -2011,22 +2202,30 @@ void AnimationTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_root_motion_track", "path"), &AnimationTree::set_root_motion_track);
ClassDB::bind_method(D_METHOD("get_root_motion_track"), &AnimationTree::get_root_motion_track);
+ ClassDB::bind_method(D_METHOD("set_audio_max_polyphony", "max_polyphony"), &AnimationTree::set_audio_max_polyphony);
+ ClassDB::bind_method(D_METHOD("get_audio_max_polyphony"), &AnimationTree::get_audio_max_polyphony);
+
ClassDB::bind_method(D_METHOD("get_root_motion_position"), &AnimationTree::get_root_motion_position);
ClassDB::bind_method(D_METHOD("get_root_motion_rotation"), &AnimationTree::get_root_motion_rotation);
ClassDB::bind_method(D_METHOD("get_root_motion_scale"), &AnimationTree::get_root_motion_scale);
+ ClassDB::bind_method(D_METHOD("get_root_motion_position_accumulator"), &AnimationTree::get_root_motion_position_accumulator);
+ ClassDB::bind_method(D_METHOD("get_root_motion_rotation_accumulator"), &AnimationTree::get_root_motion_rotation_accumulator);
+ ClassDB::bind_method(D_METHOD("get_root_motion_scale_accumulator"), &AnimationTree::get_root_motion_scale_accumulator);
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);
+ GDVIRTUAL_BIND(_post_process_key_value, "animation", "track", "value", "object", "object_idx");
+
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::NODE_PATH, "advance_expression_base_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node"), "set_advance_expression_base_node", "get_advance_expression_base_node");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_process_callback", "get_process_callback");
+ ADD_GROUP("Audio", "audio_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_max_polyphony", PROPERTY_HINT_RANGE, "1,127,1"), "set_audio_max_polyphony", "get_audio_max_polyphony");
ADD_GROUP("Root Motion", "root_motion_");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_motion_track"), "set_root_motion_track", "get_root_motion_track");
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 2c1be6199c..c68cae56ea 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -35,6 +35,7 @@
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/resources/animation.h"
+#include "scene/resources/audio_stream_polyphonic.h"
class AnimationNodeBlendTree;
class AnimationNodeStartState;
@@ -117,6 +118,7 @@ protected:
GDVIRTUAL0RC(Array, _get_parameter_list)
GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName)
GDVIRTUAL1RC(Variant, _get_parameter_default_value, StringName)
+ GDVIRTUAL1RC(bool, _is_parameter_read_only, StringName)
GDVIRTUAL3RC(double, _process, double, bool, bool)
GDVIRTUAL0RC(String, _get_caption)
GDVIRTUAL0RC(bool, _has_filter)
@@ -124,6 +126,7 @@ protected:
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const;
+ virtual bool is_parameter_read_only(const StringName &p_parameter) const;
void set_parameter(const StringName &p_name, const Variant &p_value);
Variant get_parameter(const StringName &p_name) const;
@@ -138,12 +141,12 @@ public:
virtual double process(double p_time, bool p_seek, bool p_is_external_seeking);
virtual String get_caption() const;
+ virtual bool add_input(const String &p_name);
+ virtual void remove_input(int p_index);
+ virtual bool set_input_name(int p_input, const String &p_name);
+ virtual String get_input_name(int p_input) const;
int get_input_count() const;
- String get_input_name(int p_input);
-
- void add_input(const String &p_name);
- void set_input_name(int p_input, const String &p_name);
- void remove_input(int p_index);
+ int find_input(const String &p_name) const;
void set_filter_path(const NodePath &p_path, bool p_enable);
bool is_path_filtered(const NodePath &p_path) const;
@@ -164,6 +167,11 @@ VARIANT_ENUM_CAST(AnimationNode::FilterAction)
class AnimationRootNode : public AnimationNode {
GDCLASS(AnimationRootNode, AnimationNode);
+protected:
+ virtual void _tree_changed();
+ virtual void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name);
+ virtual void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node);
+
public:
AnimationRootNode() {}
};
@@ -220,6 +228,12 @@ private:
}
};
+ struct RootMotionCache {
+ Vector3 loc = Vector3(0, 0, 0);
+ Quaternion rot = Quaternion(0, 0, 0, 1);
+ Vector3 scale = Vector3(1, 1, 1);
+ };
+
struct TrackCacheBlendShape : public TrackCache {
MeshInstance3D *mesh_3d = nullptr;
float init_value = 0;
@@ -250,10 +264,28 @@ private:
}
};
- struct TrackCacheAudio : public TrackCache {
- bool playing = false;
+ // Audio stream information for each audio stream placed on the track.
+ struct PlayingAudioStreamInfo {
+ AudioStreamPlaybackPolyphonic::ID index = -1; // ID retrieved from AudioStreamPlaybackPolyphonic.
double start = 0.0;
double len = 0.0;
+ };
+
+ // Audio track information for mixng and ending.
+ struct PlayingAudioTrackInfo {
+ HashMap<int, PlayingAudioStreamInfo> stream_info;
+ double length = 0.0;
+ double time = 0.0;
+ real_t volume = 0.0;
+ bool loop = false;
+ bool backward = false;
+ bool use_blend = false;
+ };
+
+ struct TrackCacheAudio : public TrackCache {
+ Ref<AudioStreamPolyphonic> audio_stream;
+ Ref<AudioStreamPlaybackPolyphonic> audio_stream_playback;
+ HashMap<ObjectID, PlayingAudioTrackInfo> playing_streams; // Key is Animation resource ObjectID.
TrackCacheAudio() {
type = Animation::TYPE_AUDIO;
@@ -268,8 +300,10 @@ private:
}
};
+ RootMotionCache root_motion_cache;
HashMap<NodePath, TrackCache *> track_cache;
HashSet<TrackCache *> playing_caches;
+ Vector<Node *> playing_audio_stream_players;
Ref<AnimationNode> root;
NodePath advance_expression_base_node = NodePath(String("."));
@@ -277,6 +311,7 @@ private:
AnimationProcessCallback process_callback = ANIMATION_PROCESS_IDLE;
bool active = false;
NodePath animation_player;
+ int audio_max_polyphony = 32;
AnimationNode::State state;
bool cache_valid = false;
@@ -285,6 +320,8 @@ private:
void _setup_animation_player();
void _animation_player_changed();
void _clear_caches();
+ void _clear_playing_caches();
+ void _clear_audio_streams();
bool _update_caches(AnimationPlayer *player);
void _process_graph(double p_delta);
@@ -297,14 +334,20 @@ private:
Vector3 root_motion_position = Vector3(0, 0, 0);
Quaternion root_motion_rotation = Quaternion(0, 0, 0, 1);
Vector3 root_motion_scale = Vector3(0, 0, 0);
+ Vector3 root_motion_position_accumulator = Vector3(0, 0, 0);
+ Quaternion root_motion_rotation_accumulator = Quaternion(0, 0, 0, 1);
+ Vector3 root_motion_scale_accumulator = Vector3(1, 1, 1);
friend class AnimationNode;
bool properties_dirty = true;
void _tree_changed();
+ void _animation_node_renamed(const ObjectID &p_oid, const String &p_old_name, const String &p_new_name);
+ void _animation_node_removed(const ObjectID &p_oid, const StringName &p_node);
void _update_properties();
List<PropertyInfo> properties;
HashMap<StringName, HashMap<StringName, StringName>> property_parent_map;
- HashMap<StringName, Variant> property_map;
+ HashMap<ObjectID, StringName> property_reference_map;
+ HashMap<StringName, Pair<Variant, bool>> property_map; // Property value and read-only flag.
struct Activity {
uint64_t last_pass = 0;
@@ -326,6 +369,8 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, Object *, int);
+ Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1);
virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1);
public:
@@ -344,6 +389,9 @@ public:
void set_advance_expression_base_node(const NodePath &p_advance_expression_base_node);
NodePath get_advance_expression_base_node() const;
+ void set_audio_max_polyphony(int p_audio_max_polyphony);
+ int get_audio_max_polyphony() const;
+
PackedStringArray get_configuration_warnings() const override;
bool is_state_invalid() const;
@@ -356,11 +404,13 @@ public:
Quaternion get_root_motion_rotation() const;
Vector3 get_root_motion_scale() const;
+ Vector3 get_root_motion_position_accumulator() const;
+ Quaternion get_root_motion_rotation_accumulator() const;
+ Vector3 get_root_motion_scale_accumulator() const;
+
real_t get_connection_activity(const StringName &p_path, int p_connection) const;
void advance(double p_time);
- void rename_parameter(const String &p_base, const String &p_new_base);
-
uint64_t get_last_process_pass() const;
AnimationTree();
~AnimationTree();
diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp
index e6b258df3e..fc758b9456 100644
--- a/scene/animation/root_motion_view.cpp
+++ b/scene/animation/root_motion_view.cpp
@@ -80,13 +80,15 @@ bool RootMotionView::get_zero_y() const {
void RootMotionView::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- immediate_material = StandardMaterial3D::get_material_for_2d(false, true, false, false, false);
+ immediate_material = StandardMaterial3D::get_material_for_2d(false, BaseMaterial3D::TRANSPARENCY_ALPHA, false);
+
first = true;
} break;
case NOTIFICATION_INTERNAL_PROCESS:
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
Transform3D transform;
+ Basis diff;
if (has_node(path)) {
Node *node = get_node(path);
@@ -102,9 +104,9 @@ void RootMotionView::_notification(int p_what) {
set_process_internal(true);
set_physics_process_internal(false);
}
-
transform.origin = tree->get_root_motion_position();
transform.basis = tree->get_root_motion_rotation(); // Scale is meaningless.
+ diff = tree->get_root_motion_rotation_accumulator();
}
}
@@ -114,8 +116,10 @@ void RootMotionView::_notification(int p_what) {
first = false;
- accumulated.origin += transform.origin;
accumulated.basis *= transform.basis;
+ transform.origin = (diff.inverse() * accumulated.basis).xform(transform.origin);
+ accumulated.origin += transform.origin;
+
accumulated.origin.x = Math::fposmod(accumulated.origin.x, cell_size);
if (zero_y) {
accumulated.origin.y = 0;
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index b88695427e..abc7814877 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -60,7 +60,7 @@ void Tweener::_bind_methods() {
ADD_SIGNAL(MethodInfo("finished"));
}
-void Tween::start_tweeners() {
+void Tween::_start_tweeners() {
if (tweeners.is_empty()) {
dead = true;
ERR_FAIL_MSG("Tween without commands, aborting.");
@@ -71,6 +71,15 @@ void Tween::start_tweeners() {
}
}
+void Tween::_stop_internal(bool p_reset) {
+ running = false;
+ if (p_reset) {
+ started = false;
+ dead = false;
+ total_time = 0;
+ }
+}
+
Ref<PropertyTweener> Tween::tween_property(Object *p_target, NodePath p_property, Variant p_to, double p_duration) {
ERR_FAIL_NULL_V(p_target, nullptr);
ERR_FAIL_COND_V_MSG(!valid, nullptr, "Tween invalid. Either finished or created outside scene tree.");
@@ -135,14 +144,11 @@ void Tween::append(Ref<Tweener> p_tweener) {
}
void Tween::stop() {
- started = false;
- running = false;
- dead = false;
- total_time = 0;
+ _stop_internal(true);
}
void Tween::pause() {
- running = false;
+ _stop_internal(false);
}
void Tween::play() {
@@ -274,11 +280,20 @@ bool Tween::step(double p_delta) {
}
if (!started) {
- ERR_FAIL_COND_V_MSG(tweeners.is_empty(), false, "Tween started, but has no Tweeners.");
+ if (tweeners.is_empty()) {
+ String tween_id;
+ Node *node = get_bound_node();
+ if (node) {
+ tween_id = vformat("Tween (bound to %s)", node->is_inside_tree() ? (String)node->get_path() : (String)node->get_name());
+ } else {
+ tween_id = to_string();
+ }
+ ERR_FAIL_V_MSG(false, tween_id + ": started with no Tweeners.");
+ }
current_step = 0;
loops_done = 0;
total_time = 0;
- start_tweeners();
+ _start_tweeners();
started = true;
}
@@ -319,7 +334,7 @@ bool Tween::step(double p_delta) {
} else {
emit_signal(SNAME("loop_finished"), loops_done);
current_step = 0;
- start_tweeners();
+ _start_tweeners();
#ifdef DEBUG_ENABLED
if (loops <= 0 && Math::is_equal_approx(rem_delta, initial_delta)) {
if (!potential_infinite) {
@@ -332,7 +347,7 @@ bool Tween::step(double p_delta) {
#endif
}
} else {
- start_tweeners();
+ _start_tweeners();
}
}
}
@@ -387,6 +402,15 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, d
return ret;
}
+String Tween::to_string() {
+ String ret = Object::to_string();
+ Node *node = get_bound_node();
+ if (node) {
+ ret += vformat(" (bound to %s)", node->get_name());
+ }
+ return ret;
+}
+
void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("tween_property", "object", "property", "final_val", "duration"), &Tween::tween_property);
ClassDB::bind_method(D_METHOD("tween_interval", "time"), &Tween::tween_interval);
@@ -570,7 +594,7 @@ PropertyTweener::PropertyTweener(Object *p_target, NodePath p_property, Variant
}
PropertyTweener::PropertyTweener() {
- ERR_FAIL_MSG("Can't create empty PropertyTweener. Use get_tree().tween_property() or tween_property() instead.");
+ ERR_FAIL_MSG("PropertyTweener can't be created directly. Use the tween_property() method in Tween.");
}
void IntervalTweener::start() {
@@ -601,7 +625,7 @@ IntervalTweener::IntervalTweener(double p_time) {
}
IntervalTweener::IntervalTweener() {
- ERR_FAIL_MSG("Can't create empty IntervalTweener. Use get_tree().tween_interval() instead.");
+ ERR_FAIL_MSG("IntervalTweener can't be created directly. Use the tween_interval() method in Tween.");
}
Ref<CallbackTweener> CallbackTweener::set_delay(double p_delay) {
@@ -652,7 +676,7 @@ CallbackTweener::CallbackTweener(Callable p_callback) {
}
CallbackTweener::CallbackTweener() {
- ERR_FAIL_MSG("Can't create empty CallbackTweener. Use get_tree().tween_callback() instead.");
+ ERR_FAIL_MSG("CallbackTweener can't be created directly. Use the tween_callback() method in Tween.");
}
Ref<MethodTweener> MethodTweener::set_delay(double p_delay) {
@@ -745,5 +769,5 @@ MethodTweener::MethodTweener(Callable p_callback, Variant p_from, Variant p_to,
}
MethodTweener::MethodTweener() {
- ERR_FAIL_MSG("Can't create empty MethodTweener. Use get_tree().tween_method() instead.");
+ ERR_FAIL_MSG("MethodTweener can't be created directly. Use the tween_method() method in Tween.");
}
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index 8f65416e71..58217db535 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -123,12 +123,15 @@ private:
typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d);
static interpolater interpolaters[TRANS_MAX][EASE_MAX];
- void start_tweeners();
+ void _start_tweeners();
+ void _stop_internal(bool p_reset);
protected:
static void _bind_methods();
public:
+ virtual String to_string() override;
+
Ref<PropertyTweener> tween_property(Object *p_target, NodePath p_property, Variant p_to, double p_duration);
Ref<IntervalTweener> tween_interval(double p_time);
Ref<CallbackTweener> tween_callback(Callable p_callback);