summaryrefslogtreecommitdiff
path: root/scene/animation
diff options
context:
space:
mode:
Diffstat (limited to 'scene/animation')
-rw-r--r--scene/animation/animation_blend_space_1d.cpp6
-rw-r--r--scene/animation/animation_blend_space_1d.h2
-rw-r--r--scene/animation/animation_blend_space_2d.cpp12
-rw-r--r--scene/animation/animation_blend_space_2d.h2
-rw-r--r--scene/animation/animation_blend_tree.cpp99
-rw-r--r--scene/animation/animation_blend_tree.h44
-rw-r--r--scene/animation/animation_node_state_machine.cpp595
-rw-r--r--scene/animation/animation_node_state_machine.h43
-rw-r--r--scene/animation/animation_player.cpp410
-rw-r--r--scene/animation/animation_player.h73
-rw-r--r--scene/animation/animation_tree.cpp245
-rw-r--r--scene/animation/animation_tree.h33
-rw-r--r--scene/animation/root_motion_view.cpp13
-rw-r--r--scene/animation/tween.cpp161
-rw-r--r--scene/animation/tween.h14
15 files changed, 1223 insertions, 529 deletions
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
index 849316c568..594f98410e 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -219,14 +219,14 @@ void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref<Animatio
}
}
-double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek) {
+double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_seek_root) {
if (blend_points_used == 0) {
return 0.0;
}
if (blend_points_used == 1) {
// only one point available, just play that animation
- return blend_node(blend_points[0].name, blend_points[0].node, p_time, p_seek, 1.0, FILTER_IGNORE, false);
+ return blend_node(blend_points[0].name, blend_points[0].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false);
}
double blend_pos = get_parameter(blend_position);
@@ -295,7 +295,7 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek) {
double max_time_remaining = 0.0;
for (int i = 0; i < blend_points_used; i++) {
- double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false);
+ double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, weights[i], FILTER_IGNORE, false);
max_time_remaining = MAX(max_time_remaining, remaining);
}
diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h
index 7038cece06..b2075c8c93 100644
--- a/scene/animation/animation_blend_space_1d.h
+++ b/scene/animation/animation_blend_space_1d.h
@@ -93,7 +93,7 @@ public:
void set_value_label(const String &p_label);
String get_value_label() const;
- double process(double p_time, bool p_seek) override;
+ double process(double p_time, bool p_seek, bool p_seek_root) override;
String get_caption() const override;
Ref<AnimationNode> get_child_by_name(const StringName &p_name) override;
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index a3aa3f6cc8..acdce2d7de 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -432,7 +432,7 @@ void AnimationNodeBlendSpace2D::_blend_triangle(const Vector2 &p_pos, const Vect
r_weights[2] = w;
}
-double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
+double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_seek_root) {
_update_triangles();
Vector2 blend_pos = get_parameter(blend_position);
@@ -502,7 +502,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
for (int j = 0; j < 3; j++) {
if (i == triangle_points[j]) {
//blend with the given weight
- double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false);
+ double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, blend_weights[j], FILTER_IGNORE, false);
if (first || t < mind) {
mind = t;
first = false;
@@ -514,7 +514,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
if (!found) {
//ignore
- blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false);
+ blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, false);
}
}
} else {
@@ -539,16 +539,16 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
na_n->set_backward(na_c->is_backward());
}
//see how much animation remains
- from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, 0.0, FILTER_IGNORE, false);
+ from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, p_seek_root, 0.0, FILTER_IGNORE, false);
}
- mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, 1.0, FILTER_IGNORE, false);
+ mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_seek_root, 1.0, FILTER_IGNORE, false);
length_internal = from + mind;
closest = new_closest;
} else {
- mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, 1.0, FILTER_IGNORE, false);
+ mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false);
}
}
diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h
index 1356656bf8..01f53ed25a 100644
--- a/scene/animation/animation_blend_space_2d.h
+++ b/scene/animation/animation_blend_space_2d.h
@@ -126,7 +126,7 @@ public:
void set_y_label(const String &p_label);
String get_y_label() const;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
virtual String get_caption() const override;
Vector2 get_closest_point(const Vector2 &p_point);
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 433f21f91f..1d537837bd 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -64,7 +64,7 @@ void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const {
}
}
-double AnimationNodeAnimation::process(double p_time, bool p_seek) {
+double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_seek_root) {
AnimationPlayer *ap = state->player;
ERR_FAIL_COND_V(!ap, 0);
@@ -101,8 +101,8 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek) {
}
}
- if (anim->get_loop_mode() == Animation::LoopMode::LOOP_PINGPONG) {
- if (anim_size) {
+ if (anim->get_loop_mode() == Animation::LOOP_PINGPONG) {
+ if (!Math::is_zero_approx(anim_size)) {
if ((int)Math::floor(abs(time - prev_time) / anim_size) % 2 == 0) {
if (prev_time > 0 && time <= 0) {
backward = !backward;
@@ -116,22 +116,24 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek) {
time = Math::pingpong(time, anim_size);
}
} else {
- if (anim->get_loop_mode() == Animation::LoopMode::LOOP_LINEAR) {
- if (anim_size) {
+ if (anim->get_loop_mode() == Animation::LOOP_LINEAR) {
+ if (!Math::is_zero_approx(anim_size)) {
time = Math::fposmod(time, anim_size);
}
} else if (time < 0) {
+ step += time;
time = 0;
} else if (time > anim_size) {
+ step += anim_size - time;
time = anim_size;
}
backward = false;
}
if (play_mode == PLAY_MODE_FORWARD) {
- blend_animation(animation, time, step, p_seek, 1.0, pingponged);
+ blend_animation(animation, time, step, p_seek, p_seek_root, 1.0, pingponged);
} else {
- blend_animation(animation, anim_size - time, -step, p_seek, 1.0, pingponged);
+ blend_animation(animation, anim_size - time, -step, p_seek, p_seek_root, 1.0, pingponged);
}
set_parameter(this->time, time);
@@ -251,7 +253,7 @@ bool AnimationNodeOneShot::has_filter() const {
return true;
}
-double AnimationNodeOneShot::process(double p_time, bool p_seek) {
+double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_seek_root) {
bool active = get_parameter(this->active);
bool prev_active = get_parameter(this->prev_active);
double time = get_parameter(this->time);
@@ -274,7 +276,7 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek) {
}
if (!active) {
- return blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
+ return blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync);
}
}
@@ -311,12 +313,12 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek) {
double main_rem;
if (mix == MIX_MODE_ADD) {
- main_rem = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
+ main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync);
} else {
- main_rem = blend_input(0, p_time, p_seek, 1.0 - blend, FILTER_BLEND, !sync);
+ main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_BLEND, !sync);
}
- double os_rem = blend_input(1, os_seek ? time : p_time, os_seek, blend, FILTER_PASS, false);
+ double os_rem = blend_input(1, os_seek ? time : p_time, os_seek, p_seek_root, blend, FILTER_PASS, false);
if (do_start) {
remaining = os_rem;
@@ -373,14 +375,14 @@ void AnimationNodeOneShot::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_mode", PROPERTY_HINT_ENUM, "Blend,Add"), "set_mix_mode", "get_mix_mode");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadein_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadein_time", "get_fadein_time");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadeout_time", "get_fadeout_time");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadein_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_fadein_time", "get_fadein_time");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater,suffix:s"), "set_fadeout_time", "get_fadeout_time");
ADD_GROUP("Auto Restart", "autorestart_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autorestart"), "set_autorestart", "has_autorestart");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_autorestart_delay", "get_autorestart_delay");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "autorestart_random_delay", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_autorestart_random_delay", "get_autorestart_random_delay");
+ 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");
ADD_GROUP("", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync");
@@ -420,10 +422,10 @@ bool AnimationNodeAdd2::has_filter() const {
return true;
}
-double AnimationNodeAdd2::process(double p_time, bool p_seek) {
+double AnimationNodeAdd2::process(double p_time, bool p_seek, bool p_seek_root) {
double amount = get_parameter(add_amount);
- double rem0 = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
- blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync);
+ double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync);
+ blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, !sync);
return rem0;
}
@@ -466,11 +468,11 @@ bool AnimationNodeAdd3::has_filter() const {
return true;
}
-double AnimationNodeAdd3::process(double p_time, bool p_seek) {
+double AnimationNodeAdd3::process(double p_time, bool p_seek, bool p_seek_root) {
double amount = get_parameter(add_amount);
- blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_PASS, !sync);
- double rem0 = blend_input(1, p_time, p_seek, 1.0, FILTER_IGNORE, !sync);
- blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_PASS, !sync);
+ blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_PASS, !sync);
+ double rem0 = blend_input(1, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync);
+ blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_PASS, !sync);
return rem0;
}
@@ -502,11 +504,11 @@ String AnimationNodeBlend2::get_caption() const {
return "Blend2";
}
-double AnimationNodeBlend2::process(double p_time, bool p_seek) {
+double AnimationNodeBlend2::process(double p_time, bool p_seek, bool p_seek_root) {
double amount = get_parameter(blend_amount);
- double rem0 = blend_input(0, p_time, p_seek, 1.0 - amount, FILTER_BLEND, !sync);
- double rem1 = blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync);
+ double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - amount, FILTER_BLEND, !sync);
+ double rem1 = blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, !sync);
return amount > 0.5 ? rem1 : rem0; //hacky but good enough
}
@@ -557,11 +559,11 @@ bool AnimationNodeBlend3::is_using_sync() const {
return sync;
}
-double AnimationNodeBlend3::process(double p_time, bool p_seek) {
+double AnimationNodeBlend3::process(double p_time, bool p_seek, bool p_seek_root) {
double amount = get_parameter(blend_amount);
- double rem0 = blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_IGNORE, !sync);
- double rem1 = blend_input(1, p_time, p_seek, 1.0 - ABS(amount), FILTER_IGNORE, !sync);
- double rem2 = blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_IGNORE, !sync);
+ double rem0 = blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_IGNORE, !sync);
+ double rem1 = blend_input(1, p_time, p_seek, p_seek_root, 1.0 - ABS(amount), FILTER_IGNORE, !sync);
+ double rem2 = blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_IGNORE, !sync);
return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); //hacky but good enough
}
@@ -574,7 +576,6 @@ void AnimationNodeBlend3::_bind_methods() {
}
AnimationNodeBlend3::AnimationNodeBlend3() {
- blend_amount = "blend_amount";
add_input("-blend");
add_input("in");
add_input("+blend");
@@ -595,12 +596,12 @@ String AnimationNodeTimeScale::get_caption() const {
return "TimeScale";
}
-double AnimationNodeTimeScale::process(double p_time, bool p_seek) {
+double AnimationNodeTimeScale::process(double p_time, bool p_seek, bool p_seek_root) {
double scale = get_parameter(this->scale);
if (p_seek) {
- return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false);
+ return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, false);
} else {
- return blend_input(0, p_time * scale, false, 1.0, FILTER_IGNORE, false);
+ return blend_input(0, p_time * scale, false, p_seek_root, 1.0, FILTER_IGNORE, false);
}
}
@@ -625,16 +626,16 @@ String AnimationNodeTimeSeek::get_caption() const {
return "Seek";
}
-double AnimationNodeTimeSeek::process(double p_time, bool p_seek) {
+double AnimationNodeTimeSeek::process(double p_time, bool p_seek, bool p_seek_root) {
double seek_pos = get_parameter(this->seek_pos);
if (p_seek) {
- return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false);
+ return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, false);
} else if (seek_pos >= 0) {
- double ret = blend_input(0, seek_pos, true, 1.0, FILTER_IGNORE, false);
+ double ret = blend_input(0, seek_pos, true, true, 1.0, FILTER_IGNORE, false);
set_parameter(this->seek_pos, -1.0); //reset
return ret;
} else {
- return blend_input(0, p_time, false, 1.0, FILTER_IGNORE, false);
+ return blend_input(0, p_time, false, p_seek_root, 1.0, FILTER_IGNORE, false);
}
}
@@ -726,7 +727,7 @@ float AnimationNodeTransition::get_cross_fade_time() const {
return xfade;
}
-double AnimationNodeTransition::process(double p_time, bool p_seek) {
+double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_root) {
int current = get_parameter(this->current);
int prev = get_parameter(this->prev);
int prev_current = get_parameter(this->prev_current);
@@ -754,7 +755,7 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) {
if (prev < 0) { // process current animation, check for transition
- rem = blend_input(current, p_time, p_seek, 1.0, FILTER_IGNORE, false);
+ rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false);
if (p_seek) {
time = p_time;
@@ -772,16 +773,16 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) {
if (!p_seek && switched) { //just switched, seek to start of current
- rem = blend_input(current, 0, true, 1.0 - blend, FILTER_IGNORE, false);
+ rem = blend_input(current, 0, true, p_seek_root, 1.0 - blend, FILTER_IGNORE, false);
} else {
- rem = blend_input(current, p_time, p_seek, 1.0 - blend, FILTER_IGNORE, false);
+ rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_IGNORE, false);
}
if (p_seek) { // don't seek prev animation
- blend_input(prev, 0, false, blend, FILTER_IGNORE, false);
+ blend_input(prev, 0, false, p_seek_root, blend, FILTER_IGNORE, false);
time = p_time;
} else {
- blend_input(prev, p_time, false, blend, FILTER_IGNORE, false);
+ blend_input(prev, p_time, false, p_seek_root, blend, FILTER_IGNORE, false);
time += p_time;
prev_xfading -= p_time;
if (prev_xfading < 0) {
@@ -824,7 +825,7 @@ void AnimationNodeTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_cross_fade_time"), &AnimationNodeTransition::get_cross_fade_time);
ADD_PROPERTY(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,64,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01"), "set_cross_fade_time", "get_cross_fade_time");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01,suffix:s"), "set_cross_fade_time", "get_cross_fade_time");
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);
@@ -844,8 +845,8 @@ String AnimationNodeOutput::get_caption() const {
return "Output";
}
-double AnimationNodeOutput::process(double p_time, bool p_seek) {
- return blend_input(0, p_time, p_seek, 1.0);
+double AnimationNodeOutput::process(double p_time, bool p_seek, bool p_seek_root) {
+ return blend_input(0, p_time, p_seek, p_seek_root, 1.0);
}
AnimationNodeOutput::AnimationNodeOutput() {
@@ -1057,9 +1058,9 @@ String AnimationNodeBlendTree::get_caption() const {
return "BlendTree";
}
-double AnimationNodeBlendTree::process(double p_time, bool p_seek) {
+double AnimationNodeBlendTree::process(double p_time, bool p_seek, bool p_seek_root) {
Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output].node;
- return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, 1.0);
+ return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, p_seek_root, 1.0);
}
void AnimationNodeBlendTree::get_node_list(List<StringName> *r_list) {
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index 2acacd7396..0a2305b8d6 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -53,7 +53,7 @@ public:
static Vector<String> (*get_editable_animation_list)();
virtual String get_caption() const override;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
void set_animation(const StringName &p_name);
StringName get_animation() const;
@@ -87,8 +87,8 @@ public:
};
private:
- float fade_in = 0.1;
- float fade_out = 0.1;
+ float fade_in = 0.0;
+ float fade_out = 0.0;
bool autorestart = false;
float autorestart_delay = 1.0;
@@ -102,7 +102,7 @@ private:
float time;
float remaining;*/
- StringName active = "active";
+ StringName active = PNAME("active");
StringName prev_active = "prev_active";
StringName time = "time";
StringName remaining = "remaining";
@@ -138,7 +138,7 @@ public:
bool is_using_sync() const;
virtual bool has_filter() const override;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeOneShot();
};
@@ -148,7 +148,7 @@ VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode)
class AnimationNodeAdd2 : public AnimationNode {
GDCLASS(AnimationNodeAdd2, AnimationNode);
- StringName add_amount = "add_amount";
+ StringName add_amount = PNAME("add_amount");
bool sync = false;
protected:
@@ -164,7 +164,7 @@ public:
bool is_using_sync() const;
virtual bool has_filter() const override;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeAdd2();
};
@@ -172,7 +172,7 @@ public:
class AnimationNodeAdd3 : public AnimationNode {
GDCLASS(AnimationNodeAdd3, AnimationNode);
- StringName add_amount = "add_amount";
+ StringName add_amount = PNAME("add_amount");
bool sync = false;
protected:
@@ -188,7 +188,7 @@ public:
bool is_using_sync() const;
virtual bool has_filter() const override;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeAdd3();
};
@@ -196,7 +196,7 @@ public:
class AnimationNodeBlend2 : public AnimationNode {
GDCLASS(AnimationNodeBlend2, AnimationNode);
- StringName blend_amount = "blend_amount";
+ StringName blend_amount = PNAME("blend_amount");
bool sync = false;
protected:
@@ -207,7 +207,7 @@ public:
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
virtual String get_caption() const override;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
void set_use_sync(bool p_sync);
bool is_using_sync() const;
@@ -219,7 +219,7 @@ public:
class AnimationNodeBlend3 : public AnimationNode {
GDCLASS(AnimationNodeBlend3, AnimationNode);
- StringName blend_amount;
+ StringName blend_amount = PNAME("blend_amount");
bool sync;
protected:
@@ -234,14 +234,14 @@ public:
void set_use_sync(bool p_sync);
bool is_using_sync() const;
- double process(double p_time, bool p_seek) override;
+ double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeBlend3();
};
class AnimationNodeTimeScale : public AnimationNode {
GDCLASS(AnimationNodeTimeScale, AnimationNode);
- StringName scale = "scale";
+ StringName scale = PNAME("scale");
protected:
static void _bind_methods();
@@ -252,7 +252,7 @@ public:
virtual String get_caption() const override;
- double process(double p_time, bool p_seek) override;
+ double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeTimeScale();
};
@@ -260,7 +260,7 @@ public:
class AnimationNodeTimeSeek : public AnimationNode {
GDCLASS(AnimationNodeTimeSeek, AnimationNode);
- StringName seek_pos = "seek_position";
+ StringName seek_pos = PNAME("seek_position");
protected:
static void _bind_methods();
@@ -271,7 +271,7 @@ public:
virtual String get_caption() const override;
- double process(double p_time, bool p_seek) override;
+ double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeTimeSeek();
};
@@ -300,7 +300,7 @@ class AnimationNodeTransition : public AnimationNode {
StringName prev_xfading = "prev_xfading";
StringName prev = "prev";
StringName time = "time";
- StringName current = "current";
+ StringName current = PNAME("current");
StringName prev_current = "prev_current";
float xfade = 0.0;
@@ -329,7 +329,7 @@ public:
void set_cross_fade_time(float p_fade);
float get_cross_fade_time() const;
- double process(double p_time, bool p_seek) override;
+ double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeTransition();
};
@@ -339,7 +339,7 @@ class AnimationNodeOutput : public AnimationNode {
public:
virtual String get_caption() const override;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
AnimationNodeOutput();
};
@@ -354,7 +354,7 @@ class AnimationNodeBlendTree : public AnimationRootNode {
Vector<StringName> connections;
};
- Map<StringName, Node> nodes;
+ HashMap<StringName, Node> nodes;
Vector2 graph_offset;
@@ -408,7 +408,7 @@ public:
void get_node_connections(List<NodeConnection> *r_connections) const;
virtual String get_caption() const override;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
void get_node_list(List<StringName> *r_list);
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 5ea7f4b7d9..2c7d021427 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -118,7 +118,7 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,At End"), "set_switch_mode", "get_switch_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_advance"), "set_auto_advance", "has_auto_advance");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "advance_condition"), "set_advance_condition", "get_advance_condition");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01"), "set_xfade_time", "get_xfade_time");
+ 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::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
@@ -185,27 +185,29 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
return true; //nothing to do
}
- loops_current = 0; // reset loops, so fade does not happen immediately
-
Vector2 current_pos = p_state_machine->states[current].position;
Vector2 target_pos = p_state_machine->states[p_travel].position;
- Map<StringName, AStarCost> cost_map;
+ HashMap<StringName, AStarCost> cost_map;
List<int> open_list;
//build open list
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- if (p_state_machine->transitions[i].from == current) {
+ if (p_state_machine->transitions[i].transition->is_disabled()) {
+ continue;
+ }
+
+ if (p_state_machine->transitions[i].local_from == current) {
open_list.push_back(i);
- float cost = p_state_machine->states[p_state_machine->transitions[i].to].position.distance_to(current_pos);
+ float cost = p_state_machine->states[p_state_machine->transitions[i].local_to].position.distance_to(current_pos);
cost *= p_state_machine->transitions[i].transition->get_priority();
AStarCost ap;
ap.prev = current;
ap.distance = cost;
- cost_map[p_state_machine->transitions[i].to] = ap;
+ cost_map[p_state_machine->transitions[i].local_to] = ap;
- if (p_state_machine->transitions[i].to == p_travel) { //prematurely found it! :D
+ if (p_state_machine->transitions[i].local_to == p_travel) { //prematurely found it! :D
path.push_back(p_travel);
return true;
}
@@ -224,8 +226,8 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
float least_cost = 1e20;
for (List<int>::Element *E = open_list.front(); E; E = E->next()) {
- float cost = cost_map[p_state_machine->transitions[E->get()].to].distance;
- cost += p_state_machine->states[p_state_machine->transitions[E->get()].to].position.distance_to(target_pos);
+ float cost = cost_map[p_state_machine->transitions[E->get()].local_to].distance;
+ cost += p_state_machine->states[p_state_machine->transitions[E->get()].local_to].position.distance_to(target_pos);
if (cost < least_cost) {
least_cost_transition = E;
@@ -233,34 +235,38 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
}
}
- StringName transition_prev = p_state_machine->transitions[least_cost_transition->get()].from;
- StringName transition = p_state_machine->transitions[least_cost_transition->get()].to;
+ StringName transition_prev = p_state_machine->transitions[least_cost_transition->get()].local_from;
+ StringName transition = p_state_machine->transitions[least_cost_transition->get()].local_to;
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- if (p_state_machine->transitions[i].from != transition || p_state_machine->transitions[i].to == transition_prev) {
+ if (p_state_machine->transitions[i].transition->is_disabled()) {
+ continue;
+ }
+
+ if (p_state_machine->transitions[i].local_from != transition || p_state_machine->transitions[i].local_to == transition_prev) {
continue; //not interested on those
}
- float distance = p_state_machine->states[p_state_machine->transitions[i].from].position.distance_to(p_state_machine->states[p_state_machine->transitions[i].to].position);
+ float distance = p_state_machine->states[p_state_machine->transitions[i].local_from].position.distance_to(p_state_machine->states[p_state_machine->transitions[i].local_to].position);
distance *= p_state_machine->transitions[i].transition->get_priority();
- distance += cost_map[p_state_machine->transitions[i].from].distance;
+ distance += cost_map[p_state_machine->transitions[i].local_from].distance;
- if (cost_map.has(p_state_machine->transitions[i].to)) {
+ if (cost_map.has(p_state_machine->transitions[i].local_to)) {
//oh this was visited already, can we win the cost?
- if (distance < cost_map[p_state_machine->transitions[i].to].distance) {
- cost_map[p_state_machine->transitions[i].to].distance = distance;
- cost_map[p_state_machine->transitions[i].to].prev = p_state_machine->transitions[i].from;
+ if (distance < cost_map[p_state_machine->transitions[i].local_to].distance) {
+ cost_map[p_state_machine->transitions[i].local_to].distance = distance;
+ cost_map[p_state_machine->transitions[i].local_to].prev = p_state_machine->transitions[i].local_from;
}
} else {
//add to open list
AStarCost ac;
- ac.prev = p_state_machine->transitions[i].from;
+ ac.prev = p_state_machine->transitions[i].local_from;
ac.distance = distance;
- cost_map[p_state_machine->transitions[i].to] = ac;
+ cost_map[p_state_machine->transitions[i].local_to] = ac;
open_list.push_back(i);
- if (p_state_machine->transitions[i].to == p_travel) {
+ if (p_state_machine->transitions[i].local_to == p_travel) {
found_route = true;
break;
}
@@ -286,7 +292,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
return true;
}
-double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek) {
+double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root) {
//if not playing and it can restart, then restart
if (!playing && start_request == StringName()) {
if (!stop_request && p_state_machine->start_node) {
@@ -350,9 +356,8 @@ 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, 1.0, AnimationNode::FILTER_IGNORE, false);
+ len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 1.0, AnimationNode::FILTER_IGNORE, false);
pos_current = 0;
- loops_current = 0;
}
if (!p_state_machine->states.has(current)) {
@@ -376,10 +381,10 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
}
- float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, fade_blend, AnimationNode::FILTER_IGNORE, false);
+ float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_seek_root, fade_blend, AnimationNode::FILTER_IGNORE, false);
if (fading_from != StringName()) {
- p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false);
+ p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_seek_root, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false);
}
//guess playback position
@@ -388,12 +393,8 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
{ //advance and loop check
-
float next_pos = len_current - rem;
-
- if (next_pos < pos_current) {
- loops_current++;
- }
+ end_loop = next_pos < pos_current;
pos_current = next_pos; //looped
}
@@ -404,7 +405,11 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
if (path.size()) {
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- if (p_state_machine->transitions[i].from == current && p_state_machine->transitions[i].to == path[0]) {
+ if (p_state_machine->transitions[i].transition->is_disabled()) {
+ continue;
+ }
+
+ if (p_state_machine->transitions[i].local_from == current && p_state_machine->transitions[i].local_to == path[0]) {
next_xfade = p_state_machine->transitions[i].transition->get_xfade_time();
switch_mode = p_state_machine->transitions[i].transition->get_switch_mode();
next = path[0];
@@ -413,17 +418,39 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
} else {
float priority_best = 1e20;
int auto_advance_to = -1;
+
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
- bool auto_advance = false;
- if (p_state_machine->transitions[i].transition->has_auto_advance()) {
- auto_advance = true;
+ if (p_state_machine->transitions[i].transition->is_disabled()) {
+ continue;
}
- StringName advance_condition_name = p_state_machine->transitions[i].transition->get_advance_condition_name();
- if (advance_condition_name != StringName() && bool(p_state_machine->get_parameter(advance_condition_name))) {
- auto_advance = true;
+
+ // handles end_node: when end_node is reached in a sub state machine, find and activate the current_transition
+ if (force_auto_advance) {
+ if (p_state_machine->transitions[i].from == current_transition.from && p_state_machine->transitions[i].to == current_transition.to) {
+ auto_advance_to = i;
+ force_auto_advance = false;
+ break;
+ }
}
- if (p_state_machine->transitions[i].from == current && auto_advance) {
+ // handles start_node: if previous state machine is pointing to a node inside the current state machine, starts the current machine from start_node to prev_local_to
+ if (p_state_machine->start_node == current && p_state_machine->transitions[i].local_from == current) {
+ if (p_state_machine->prev_state_machine.is_valid()) {
+ Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback");
+
+ if (prev_playback.is_valid()) {
+ StringName prev_local_to = String(prev_playback->current_transition.next).replace_first(String(p_state_machine->state_machine_name) + "/", "");
+
+ if (p_state_machine->transitions[i].to == prev_local_to) {
+ auto_advance_to = i;
+ prev_playback->current_transition.next = StringName();
+ break;
+ }
+ }
+ }
+ }
+
+ if (p_state_machine->transitions[i].from == current && _check_advance_condition(p_state_machine, p_state_machine->transitions[i].transition)) {
if (p_state_machine->transitions[i].transition->get_priority() <= priority_best) {
priority_best = p_state_machine->transitions[i].transition->get_priority();
auto_advance_to = i;
@@ -432,26 +459,69 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
if (auto_advance_to != -1) {
- next = p_state_machine->transitions[auto_advance_to].to;
+ next = p_state_machine->transitions[auto_advance_to].local_to;
+ Transition tr;
+ tr.from = String(p_state_machine->state_machine_name) + "/" + String(p_state_machine->transitions[auto_advance_to].from);
+ tr.to = String(p_state_machine->transitions[auto_advance_to].to).replace_first("../", "");
+ tr.next = p_state_machine->transitions[auto_advance_to].to;
+ current_transition = tr;
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();
}
}
+ if (next == p_state_machine->end_node) {
+ Ref<AnimationNodeStateMachine> prev_state_machine = p_state_machine->prev_state_machine;
+
+ if (prev_state_machine.is_valid()) {
+ Ref<AnimationNodeStateMachinePlayback> prev_playback = prev_state_machine->get_parameter("playback");
+
+ if (prev_playback.is_valid()) {
+ if (next_xfade) {
+ prev_playback->current_transition = current_transition;
+ prev_playback->force_auto_advance = true;
+
+ return rem;
+ }
+ float priority_best = 1e20;
+ int auto_advance_to = -1;
+
+ for (int i = 0; i < prev_state_machine->transitions.size(); i++) {
+ if (prev_state_machine->transitions[i].transition->is_disabled()) {
+ continue;
+ }
+
+ if (current_transition.next == prev_state_machine->end_node && _check_advance_condition(prev_state_machine, prev_state_machine->transitions[i].transition)) {
+ if (prev_state_machine->transitions[i].transition->get_priority() <= priority_best) {
+ priority_best = prev_state_machine->transitions[i].transition->get_priority();
+ auto_advance_to = i;
+ }
+ }
+ }
+
+ if (auto_advance_to != -1) {
+ if (prev_state_machine->transitions[auto_advance_to].transition->get_xfade_time()) {
+ return rem;
+ }
+ }
+ }
+ }
+ }
+
//if next, see when to transition
if (next != StringName()) {
bool goto_next = false;
if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_AT_END) {
- goto_next = next_xfade >= (len_current - pos_current) || loops_current > 0;
- if (loops_current > 0) {
+ goto_next = next_xfade >= (len_current - pos_current) || end_loop;
+ if (end_loop) {
next_xfade = 0;
}
} else {
goto_next = fading_from == StringName();
}
- if (goto_next) { //loops should be used because fade time may be too small or zero and animation may have looped
+ if (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
@@ -468,28 +538,48 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
current = next;
if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) {
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false);
+ len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false);
pos_current = MIN(pos_current, len_current);
- p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, 0, AnimationNode::FILTER_IGNORE, false);
+ p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false);
} else {
- len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false);
+ len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false);
pos_current = 0;
}
rem = len_current; //so it does not show 0 on transition
- loops_current = 0;
}
}
- //compute time left for transitions by using the end node
- if (p_state_machine->end_node != StringName() && p_state_machine->end_node != current) {
- rem = p_state_machine->blend_node(p_state_machine->end_node, p_state_machine->states[p_state_machine->end_node].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false);
+ // time left must always be 1 because the end node don't length to compute
+ if (p_state_machine->end_node != current) {
+ rem = 1;
+ } else {
+ Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback");
+
+ if (prev_playback.is_valid()) {
+ prev_playback->current_transition = current_transition;
+ prev_playback->force_auto_advance = true;
+ }
}
return rem;
}
+bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<AnimationNodeStateMachine> state_machine, const Ref<AnimationNodeStateMachineTransition> transition) const {
+ if (transition->has_auto_advance()) {
+ return true;
+ }
+
+ StringName advance_condition_name = transition->get_advance_condition_name();
+
+ if (advance_condition_name != StringName() && bool(state_machine->get_parameter(advance_condition_name))) {
+ return true;
+ }
+
+ return false;
+}
+
void AnimationNodeStateMachinePlayback::_bind_methods() {
ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachinePlayback::travel);
ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachinePlayback::start);
@@ -521,6 +611,23 @@ void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) c
for (const StringName &E : advance_conditions) {
r_list->push_back(PropertyInfo(Variant::BOOL, E));
}
+
+ // for (const KeyValue<StringName, State> &E : states) {
+ // if (E->node == ansm) {
+ // for (int i = 0; i < E->node->transitions.size(); i++) {
+ // StringName ac = E->node->transitions[i].transition->get_advance_condition_name();
+ // if (ac != StringName() && advance_conditions.find(ac) == nullptr) {
+ // advance_conditions.push_back(ac);
+ // }
+ // }
+
+ // advance_conditions.sort_custom<StringName::AlphCompare>();
+
+ // for (const StringName &E : advance_conditions) {
+ // r_list->push_back(PropertyInfo(Variant::BOOL, E));
+ // }
+ // }
+ // }
}
Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName &p_parameter) const {
@@ -544,6 +651,13 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
states[p_name] = state;
+ Ref<AnimationNodeStateMachine> anodesm = p_node;
+
+ if (anodesm.is_valid()) {
+ anodesm->state_machine_name = p_name;
+ anodesm->prev_state_machine = (Ref<AnimationNodeStateMachine>)this;
+ }
+
emit_changed();
emit_signal(SNAME("tree_changed"));
@@ -570,6 +684,14 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima
p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
}
+bool AnimationNodeStateMachine::can_edit_node(const StringName &p_name) const {
+ if (states.has(p_name)) {
+ return !(states[p_name].node->is_class("AnimationNodeStartState") || states[p_name].node->is_class("AnimationNodeEndState"));
+ }
+
+ return true;
+}
+
Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const {
ERR_FAIL_COND_V(!states.has(p_name), Ref<AnimationNode>());
@@ -610,36 +732,24 @@ bool AnimationNodeStateMachine::has_node(const StringName &p_name) const {
void AnimationNodeStateMachine::remove_node(const StringName &p_name) {
ERR_FAIL_COND(!states.has(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));
+ if (!can_edit_node(p_name)) {
+ return;
}
- states.erase(p_name);
- //path.erase(p_name);
-
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_name || transitions[i].to == p_name) {
- transitions.write[i].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
- transitions.remove_at(i);
+ if (transitions[i].local_from == p_name || transitions[i].local_to == p_name) {
+ remove_transition_by_index(i);
i--;
}
}
- if (start_node == p_name) {
- start_node = StringName();
- }
-
- if (end_node == p_name) {
- end_node = StringName();
+ {
+ Ref<AnimationNode> node = states[p_name].node;
+ ERR_FAIL_COND(node.is_null());
+ node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
}
- /*if (playing && current == p_name) {
- stop();
- }*/
+ states.erase(p_name);
emit_changed();
emit_signal(SNAME("tree_changed"));
@@ -648,39 +758,73 @@ void AnimationNodeStateMachine::remove_node(const StringName &p_name) {
void AnimationNodeStateMachine::rename_node(const StringName &p_name, const StringName &p_new_name) {
ERR_FAIL_COND(!states.has(p_name));
ERR_FAIL_COND(states.has(p_new_name));
+ ERR_FAIL_COND(!can_edit_node(p_name));
states[p_new_name] = states[p_name];
states.erase(p_name);
+ Ref<AnimationNodeStateMachine> anodesm = states[p_new_name].node;
+ if (anodesm.is_valid()) {
+ anodesm->state_machine_name = p_new_name;
+ }
+
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_name) {
- transitions.write[i].from = p_new_name;
+ if (transitions[i].local_from == p_name) {
+ _rename_transition(transitions[i].from, String(transitions[i].from).replace_first(p_name, p_new_name));
}
- if (transitions[i].to == p_name) {
- transitions.write[i].to = p_new_name;
+ if (transitions[i].local_to == p_name) {
+ _rename_transition(transitions[i].to, String(transitions[i].to).replace_first(p_name, p_new_name));
}
}
- if (start_node == p_name) {
- start_node = p_new_name;
- }
+ emit_signal("tree_changed");
+}
- if (end_node == p_name) {
- end_node = p_new_name;
+void AnimationNodeStateMachine::_rename_transition(const StringName &p_name, const StringName &p_new_name) {
+ if (updating_transitions) {
+ return;
}
- /*if (playing && current == p_name) {
- current = p_new_name;
- }*/
+ updating_transitions = true;
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].from == p_name) {
+ Vector<String> path = String(transitions[i].to).split("/");
+ if (path.size() > 1) {
+ if (path[0] == "..") {
+ prev_state_machine->_rename_transition(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name);
+ } else {
+ ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_to].node)->_rename_transition("../" + p_name, "../" + p_new_name);
+ }
+ }
+
+ transitions.write[i].from = p_new_name;
+ }
- //path.clear(); //clear path
- emit_signal(SNAME("tree_changed"));
+ if (transitions[i].to == p_name) {
+ Vector<String> path = String(transitions[i].from).split("/");
+ if (path.size() > 1) {
+ if (path[0] == "..") {
+ prev_state_machine->_rename_transition(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name);
+ } else {
+ ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_from].node)->_rename_transition("../" + p_name, "../" + p_new_name);
+ }
+ }
+
+ transitions.write[i].to = p_new_name;
+ }
+
+ updating_transitions = false;
+ }
}
void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const {
List<StringName> nodes;
for (const KeyValue<StringName, State> &E : states) {
+ if (E.key == end_node && !prev_state_machine.is_valid()) {
+ continue;
+ }
+
nodes.push_back(E.key);
}
nodes.sort_custom<StringName::AlphCompare>();
@@ -690,9 +834,16 @@ void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const {
}
}
+Ref<AnimationNodeStateMachine> AnimationNodeStateMachine::get_prev_state_machine() const {
+ return prev_state_machine;
+}
+
bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const StringName &p_to) const {
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_from && transitions[i].to == p_to) {
+ if (transitions[i].from == from && transitions[i].to == to) {
return true;
}
}
@@ -700,32 +851,148 @@ bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const S
}
int AnimationNodeStateMachine::find_transition(const StringName &p_from, const StringName &p_to) const {
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_from && transitions[i].to == p_to) {
+ if (transitions[i].from == from && transitions[i].to == to) {
return i;
}
}
return -1;
}
+bool AnimationNodeStateMachine::_can_connect(const StringName &p_name, Vector<Ref<AnimationNodeStateMachine>> p_parents) const {
+ if (p_parents.is_empty()) {
+ Ref<AnimationNodeStateMachine> prev = (Ref<AnimationNodeStateMachine>)this;
+ while (prev.is_valid()) {
+ p_parents.push_back(prev);
+ prev = prev->prev_state_machine;
+ }
+ }
+
+ if (states.has(p_name)) {
+ Ref<AnimationNodeStateMachine> anodesm = states[p_name].node;
+
+ if (anodesm.is_valid() && p_parents.find(anodesm) != -1) {
+ return false;
+ }
+
+ return true;
+ }
+
+ String name = p_name;
+ Vector<String> path = name.split("/");
+
+ if (path.size() < 2) {
+ return false;
+ }
+
+ if (path[0] == "..") {
+ if (prev_state_machine.is_valid()) {
+ return prev_state_machine->_can_connect(name.replace_first("../", ""), p_parents);
+ }
+ } else if (states.has(path[0])) {
+ Ref<AnimationNodeStateMachine> anodesm = states[path[0]].node;
+ if (anodesm.is_valid()) {
+ return anodesm->_can_connect(name.replace_first(path[0] + "/", ""), p_parents);
+ }
+ }
+
+ return false;
+}
+
+StringName AnimationNodeStateMachine::_get_shortest_path(const StringName &p_path) const {
+ // If p_path is something like StateMachine/../StateMachine2/State1,
+ // the result will be StateMachine2/State1. This avoid duplicate
+ // transitions when using add_transition. eg, this two calls is the same:
+ //
+ // add_transition("State1", "StateMachine/../State2", tr)
+ // add_transition("State1", "State2", tr)
+ //
+ // but the second call must be invalid because the transition already exists
+
+ Vector<String> path = String(p_path).split("/");
+ Vector<String> new_path;
+
+ for (int i = 0; i < path.size(); i++) {
+ if (i > 0 && path[i] == ".." && new_path[i - 1] != "..") {
+ new_path.remove_at(i - 1);
+ } else {
+ new_path.push_back(path[i]);
+ }
+ }
+
+ String result;
+ for (int i = 0; i < new_path.size(); i++) {
+ result += new_path[i] + "/";
+ }
+ result.remove_at(result.length() - 1);
+
+ return result;
+}
+
void AnimationNodeStateMachine::add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition) {
- ERR_FAIL_COND(p_from == p_to);
- ERR_FAIL_COND(!states.has(p_from));
- ERR_FAIL_COND(!states.has(p_to));
+ if (updating_transitions) {
+ return;
+ }
+
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+ Vector<String> path_from = String(from).split("/");
+ Vector<String> path_to = String(to).split("/");
+
+ ERR_FAIL_COND(from == end_node || to == start_node);
+ ERR_FAIL_COND(from == to);
+ ERR_FAIL_COND(!_can_connect(from));
+ ERR_FAIL_COND(!_can_connect(to));
ERR_FAIL_COND(p_transition.is_null());
for (int i = 0; i < transitions.size(); i++) {
- ERR_FAIL_COND(transitions[i].from == p_from && transitions[i].to == p_to);
+ ERR_FAIL_COND(transitions[i].from == from && transitions[i].to == to);
+ }
+
+ if (path_from.size() > 1 || path_to.size() > 1) {
+ ERR_FAIL_COND(path_from[0] == path_to[0]);
}
+ updating_transitions = true;
+
+ StringName local_from = String(from).get_slicec('/', 0);
+ StringName local_to = String(to).get_slicec('/', 0);
+ local_from = local_from == ".." ? "Start" : local_from;
+ local_to = local_to == ".." ? "End" : local_to;
+
Transition tr;
- tr.from = p_from;
- tr.to = p_to;
+ tr.from = from;
+ tr.to = to;
+ tr.local_from = local_from;
+ tr.local_to = local_to;
tr.transition = p_transition;
tr.transition->connect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED);
transitions.push_back(tr);
+
+ // do recursive
+ if (path_from.size() > 1) {
+ StringName local_path = String(from).replace_first(path_from[0] + "/", "");
+ if (path_from[0] == "..") {
+ prev_state_machine->add_transition(local_path, String(state_machine_name) + "/" + to, p_transition);
+ } else {
+ ((Ref<AnimationNodeStateMachine>)states[path_from[0]].node)->add_transition(local_path, "../" + to, p_transition);
+ }
+ }
+ if (path_to.size() > 1) {
+ StringName local_path = String(to).replace_first(path_to[0] + "/", "");
+ if (path_to[0] == "..") {
+ prev_state_machine->add_transition(String(state_machine_name) + "/" + from, local_path, p_transition);
+ } else {
+ ((Ref<AnimationNodeStateMachine>)states[path_to[0]].node)->add_transition("../" + from, local_path, p_transition);
+ }
+ }
+
+ updating_transitions = false;
}
Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_transition(int p_transition) const {
@@ -748,44 +1015,52 @@ int AnimationNodeStateMachine::get_transition_count() const {
}
void AnimationNodeStateMachine::remove_transition(const StringName &p_from, const StringName &p_to) {
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+
for (int i = 0; i < transitions.size(); i++) {
- if (transitions[i].from == p_from && transitions[i].to == p_to) {
- transitions.write[i].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
- transitions.remove_at(i);
+ if (transitions[i].from == from && transitions[i].to == to) {
+ remove_transition_by_index(i);
return;
}
}
-
- /*if (playing) {
- path.clear();
- }*/
}
-void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) {
+void AnimationNodeStateMachine::remove_transition_by_index(const int p_transition) {
ERR_FAIL_INDEX(p_transition, transitions.size());
+ Transition tr = transitions[p_transition];
transitions.write[p_transition].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed));
transitions.remove_at(p_transition);
- /*if (playing) {
- path.clear();
- }*/
-}
-void AnimationNodeStateMachine::set_start_node(const StringName &p_node) {
- ERR_FAIL_COND(p_node != StringName() && !states.has(p_node));
- start_node = p_node;
-}
+ Vector<String> path_from = String(tr.from).split("/");
+ Vector<String> path_to = String(tr.to).split("/");
-String AnimationNodeStateMachine::get_start_node() const {
- return start_node;
-}
+ List<Vector<String>> paths;
+ paths.push_back(path_from);
+ paths.push_back(path_to);
-void AnimationNodeStateMachine::set_end_node(const StringName &p_node) {
- ERR_FAIL_COND(p_node != StringName() && !states.has(p_node));
- end_node = p_node;
+ for (List<Vector<String>>::Element *E = paths.front(); E; E = E->next()) {
+ if (E->get()[0].size() > 1) {
+ if (E->get()[0] == "..") {
+ prev_state_machine->_remove_transition(tr.transition);
+ } else if (states.has(E->get()[0])) {
+ Ref<AnimationNodeStateMachine> anodesm = states[E->get()[0]].node;
+
+ if (anodesm.is_valid()) {
+ anodesm->_remove_transition(tr.transition);
+ }
+ }
+ }
+ }
}
-String AnimationNodeStateMachine::get_end_node() const {
- return end_node;
+void AnimationNodeStateMachine::_remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition) {
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].transition == p_transition) {
+ remove_transition_by_index(i);
+ return;
+ }
+ }
}
void AnimationNodeStateMachine::set_graph_offset(const Vector2 &p_offset) {
@@ -796,17 +1071,29 @@ Vector2 AnimationNodeStateMachine::get_graph_offset() const {
return graph_offset;
}
-double AnimationNodeStateMachine::process(double p_time, bool p_seek) {
+double AnimationNodeStateMachine::process(double p_time, bool p_seek, bool p_seek_root) {
Ref<AnimationNodeStateMachinePlayback> playback = get_parameter(this->playback);
ERR_FAIL_COND_V(playback.is_null(), 0.0);
- return playback->process(this, p_time, p_seek);
+ return playback->process(this, p_time, p_seek, p_seek_root);
}
String AnimationNodeStateMachine::get_caption() const {
return "StateMachine";
}
+bool AnimationNodeStateMachine::has_local_transition(const StringName &p_from, const StringName &p_to) const {
+ StringName from = _get_shortest_path(p_from);
+ StringName to = _get_shortest_path(p_to);
+
+ for (int i = 0; i < transitions.size(); i++) {
+ if (transitions[i].local_from == from && transitions[i].local_to == to) {
+ return true;
+ }
+ }
+ return false;
+}
+
Ref<AnimationNode> AnimationNodeStateMachine::get_child_by_name(const StringName &p_name) {
return get_node(p_name);
}
@@ -839,12 +1126,6 @@ bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_
add_transition(trans[i], trans[i + 1], trans[i + 2]);
}
return true;
- } else if (name == "start_node") {
- set_start_node(p_value);
- return true;
- } else if (name == "end_node") {
- set_end_node(p_value);
- return true;
} else if (name == "graph_offset") {
set_graph_offset(p_value);
return true;
@@ -860,7 +1141,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c
String what = name.get_slicec('/', 2);
if (what == "node") {
- if (states.has(node_name)) {
+ if (states.has(node_name) && can_edit_node(node_name)) {
r_ret = states[node_name].node;
return true;
}
@@ -874,22 +1155,21 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c
}
} else if (name == "transitions") {
Array trans;
- trans.resize(transitions.size() * 3);
-
for (int i = 0; i < transitions.size(); i++) {
- trans[i * 3 + 0] = transitions[i].from;
- trans[i * 3 + 1] = transitions[i].to;
- trans[i * 3 + 2] = transitions[i].transition;
+ String from = transitions[i].from;
+ String to = transitions[i].to;
+
+ if (from.get_slicec('/', 0) == ".." || to.get_slicec('/', 0) == "..") {
+ continue;
+ }
+
+ trans.push_back(from);
+ trans.push_back(to);
+ trans.push_back(transitions[i].transition);
}
r_ret = trans;
return true;
- } else if (name == "start_node") {
- r_ret = get_start_node();
- return true;
- } else if (name == "end_node") {
- r_ret = get_end_node();
- return true;
} else if (name == "graph_offset") {
r_ret = get_graph_offset();
return true;
@@ -911,8 +1191,6 @@ void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) c
}
p_list->push_back(PropertyInfo(Variant::ARRAY, "transitions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, "start_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::STRING_NAME, "end_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
@@ -920,10 +1198,24 @@ void AnimationNodeStateMachine::reset_state() {
states.clear();
transitions.clear();
playback = "playback";
- start_node = StringName();
- end_node = StringName();
+ start_node = "Start";
+ end_node = "End";
graph_offset = Vector2();
+ Ref<AnimationNodeStartState> s;
+ s.instantiate();
+ State start;
+ start.node = s;
+ start.position = Vector2(200, 100);
+ states[start_node] = start;
+
+ Ref<AnimationNodeEndState> e;
+ e.instantiate();
+ State end;
+ end.node = e;
+ end.position = Vector2(900, 100);
+ states[end_node] = end;
+
emit_changed();
emit_signal(SNAME("tree_changed"));
}
@@ -963,15 +1255,22 @@ void AnimationNodeStateMachine::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_transition_by_index", "idx"), &AnimationNodeStateMachine::remove_transition_by_index);
ClassDB::bind_method(D_METHOD("remove_transition", "from", "to"), &AnimationNodeStateMachine::remove_transition);
- ClassDB::bind_method(D_METHOD("set_start_node", "name"), &AnimationNodeStateMachine::set_start_node);
- ClassDB::bind_method(D_METHOD("get_start_node"), &AnimationNodeStateMachine::get_start_node);
-
- ClassDB::bind_method(D_METHOD("set_end_node", "name"), &AnimationNodeStateMachine::set_end_node);
- ClassDB::bind_method(D_METHOD("get_end_node"), &AnimationNodeStateMachine::get_end_node);
-
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);
}
AnimationNodeStateMachine::AnimationNodeStateMachine() {
+ Ref<AnimationNodeStartState> s;
+ s.instantiate();
+ State start;
+ start.node = s;
+ start.position = Vector2(200, 100);
+ states[start_node] = start;
+
+ Ref<AnimationNodeEndState> e;
+ e.instantiate();
+ State end;
+ end.node = e;
+ end.position = Vector2(900, 100);
+ states[end_node] = end;
}
diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h
index 3bae0fcffa..9eeac6a183 100644
--- a/scene/animation/animation_node_state_machine.h
+++ b/scene/animation/animation_node_state_machine.h
@@ -93,13 +93,19 @@ class AnimationNodeStateMachinePlayback : public Resource {
StringName prev;
};
- float len_total = 0.0;
+ struct Transition {
+ StringName from;
+ StringName to;
+ StringName next;
+ };
float len_current = 0.0;
float pos_current = 0.0;
- int loops_current = 0;
+ bool end_loop = false;
StringName current;
+ Transition current_transition;
+ bool force_auto_advance = false;
StringName fading_from;
float fading_time = 0.0;
@@ -114,7 +120,9 @@ class AnimationNodeStateMachinePlayback : public Resource {
bool _travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel);
- double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek);
+ double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root);
+
+ bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
protected:
static void _bind_methods();
@@ -144,24 +152,30 @@ private:
Vector2 position;
};
- Map<StringName, State> states;
+ HashMap<StringName, State> states;
struct Transition {
StringName from;
StringName to;
+ StringName local_from;
+ StringName local_to;
Ref<AnimationNodeStateMachineTransition> transition;
};
Vector<Transition> transitions;
StringName playback = "playback";
-
- StringName start_node;
- StringName end_node;
+ StringName state_machine_name;
+ Ref<AnimationNodeStateMachine> prev_state_machine;
+ bool updating_transitions = false;
Vector2 graph_offset;
void _tree_changed();
+ void _remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition);
+ void _rename_transition(const StringName &p_name, const StringName &p_new_name);
+ bool _can_connect(const StringName &p_name, const Vector<Ref<AnimationNodeStateMachine>> p_parents = Vector<Ref<AnimationNodeStateMachine>>()) const;
+ StringName _get_shortest_path(const StringName &p_path) const;
protected:
static void _bind_methods();
@@ -169,10 +183,14 @@ protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+ bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const;
virtual void reset_state() override;
public:
+ StringName start_node = "Start";
+ StringName end_node = "End";
+
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -191,25 +209,24 @@ public:
virtual void get_child_nodes(List<ChildNode> *r_child_nodes) override;
bool has_transition(const StringName &p_from, const StringName &p_to) const;
+ bool has_local_transition(const StringName &p_from, const StringName &p_to) const;
int find_transition(const StringName &p_from, const StringName &p_to) const;
void add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition);
Ref<AnimationNodeStateMachineTransition> get_transition(int p_transition) const;
StringName get_transition_from(int p_transition) const;
StringName get_transition_to(int p_transition) const;
int get_transition_count() const;
- void remove_transition_by_index(int p_transition);
+ void remove_transition_by_index(const int p_transition);
void remove_transition(const StringName &p_from, const StringName &p_to);
- void set_start_node(const StringName &p_node);
- String get_start_node() const;
+ bool can_edit_node(const StringName &p_name) const;
- void set_end_node(const StringName &p_node);
- String get_end_node() const;
+ Ref<AnimationNodeStateMachine> get_prev_state_machine() const;
void set_graph_offset(const Vector2 &p_offset);
Vector2 get_graph_offset() const;
- virtual double process(double p_time, bool p_seek) override;
+ virtual double process(double p_time, bool p_seek, bool p_seek_root) override;
virtual String get_caption() const override;
virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) override;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 402418e5a9..8edfaa5853 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -83,8 +83,31 @@ bool AnimationPlayer::_set(const StringName &p_name, const Variant &p_value) {
set_current_animation(p_value);
} else if (name.begins_with("anims/")) {
+ // Backwards compatibility with 3.x, add them to "default" library.
String which = name.get_slicec('/', 1);
- add_animation(which, p_value);
+
+ Ref<Animation> anim = p_value;
+ Ref<AnimationLibrary> al;
+ if (!has_animation_library(StringName())) {
+ al.instantiate();
+ add_animation_library(StringName(), al);
+ } else {
+ al = get_animation_library(StringName());
+ }
+ al->add_animation(which, anim);
+
+ } else if (name.begins_with("libraries")) {
+ Dictionary d = p_value;
+ while (animation_libraries.size()) {
+ remove_animation_library(animation_libraries[0].name);
+ }
+ List<Variant> keys;
+ d.get_key_list(&keys);
+ for (const Variant &K : keys) {
+ StringName lib_name = K;
+ Ref<AnimationLibrary> lib = d[lib_name];
+ add_animation_library(lib_name, lib);
+ }
} else if (name.begins_with("next/")) {
String which = name.get_slicec('/', 1);
@@ -117,9 +140,13 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_current_animation();
- } else if (name.begins_with("anims/")) {
- String which = name.get_slicec('/', 1);
- r_ret = get_animation(which);
+ } 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;
+ }
+
+ r_ret = d;
} else if (name.begins_with("next/")) {
String which = name.get_slicec('/', 1);
@@ -136,7 +163,7 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const {
for (int i = 0; i < keys.size(); i++) {
array.push_back(keys[i].from);
array.push_back(keys[i].to);
- array.push_back(blend_times[keys[i]]);
+ array.push_back(blend_times.get(keys[i]));
}
r_ret = array;
@@ -173,8 +200,9 @@ void AnimationPlayer::_validate_property(PropertyInfo &property) const {
void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> anim_names;
+ anim_names.push_back(PropertyInfo(Variant::DICTIONARY, "libraries"));
+
for (const KeyValue<StringName, AnimationData> &E : animation_set) {
- anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E.key), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
if (E.value.next != StringName()) {
anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
@@ -256,7 +284,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
for (int i = 0; i < a->get_track_count(); i++) {
p_anim->node_cache.write[i] = nullptr;
- RES resource;
+ Ref<Resource> resource;
Vector<StringName> leftover_path;
Node *child = parent->get_node_and_resource(a->track_get_path(i), resource, leftover_path);
ERR_CONTINUE_MSG(!child, "On Animation: '" + p_anim->name + "', couldn't resolve track: '" + String(a->track_get_path(i)) + "'."); // couldn't find the child node
@@ -560,10 +588,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
//StringName property=a->track_get_path(i).get_property();
- Map<StringName, TrackNodeCache::PropertyAnim>::Element *E = nc->property_anim.find(a->track_get_path(i).get_concatenated_subnames());
+ HashMap<StringName, TrackNodeCache::PropertyAnim>::Iterator E = nc->property_anim.find(a->track_get_path(i).get_concatenated_subnames());
ERR_CONTINUE(!E); //should it continue, or create a new one?
- TrackNodeCache::PropertyAnim *pa = &E->get();
+ TrackNodeCache::PropertyAnim *pa = &E->value;
Animation::UpdateMode update_mode = a->value_track_get_update_mode(i);
@@ -710,10 +738,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
continue;
}
- Map<StringName, TrackNodeCache::BezierAnim>::Element *E = nc->bezier_anim.find(a->track_get_path(i).get_concatenated_subnames());
+ HashMap<StringName, TrackNodeCache::BezierAnim>::Iterator E = nc->bezier_anim.find(a->track_get_path(i).get_concatenated_subnames());
ERR_CONTINUE(!E); //should it continue, or create a new one?
- TrackNodeCache::BezierAnim *ba = &E->get();
+ TrackNodeCache::BezierAnim *ba = &E->value;
real_t bezier = a->bezier_track_interpolate(i, p_time);
if (ba->accum_pass != accum_pass) {
@@ -804,7 +832,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
nc->audio_start = p_time;
}
} else if (nc->audio_playing) {
- bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
+ bool loop = a->get_loop_mode() != Animation::LOOP_NONE;
bool stop = false;
@@ -855,15 +883,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
double at_anim_pos = 0.0;
switch (anim->get_loop_mode()) {
- case Animation::LoopMode::LOOP_NONE: {
+ case Animation::LOOP_NONE: {
at_anim_pos = MIN((double)anim->get_length(), p_time - pos); //seek to end
} break;
- case Animation::LoopMode::LOOP_LINEAR: {
+ case Animation::LOOP_LINEAR: {
at_anim_pos = Math::fposmod(p_time - pos, (double)anim->get_length()); //seek to loop
} break;
- case Animation::LoopMode::LOOP_PINGPONG: {
+ case Animation::LOOP_PINGPONG: {
at_anim_pos = Math::pingpong(p_time - pos, (double)anim->get_length());
} break;
@@ -916,7 +944,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
int pingponged = 0;
switch (cd.from->animation->get_loop_mode()) {
- case Animation::LoopMode::LOOP_NONE: {
+ case Animation::LOOP_NONE: {
if (next_pos < 0) {
next_pos = 0;
} else if (next_pos > len) {
@@ -941,7 +969,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
}
} break;
- case Animation::LoopMode::LOOP_LINEAR: {
+ case Animation::LOOP_LINEAR: {
double looped_next_pos = Math::fposmod(next_pos, (double)len);
if (looped_next_pos == 0 && next_pos != 0) {
// Loop multiples of the length to it, rather than 0
@@ -952,7 +980,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
}
} break;
- case Animation::LoopMode::LOOP_PINGPONG: {
+ case Animation::LOOP_PINGPONG: {
if ((int)Math::floor(abs(next_pos - cd.pos) / len) % 2 == 0) {
if (next_pos < 0 && cd.pos >= 0) {
cd.speed_scale *= -1.0;
@@ -1155,71 +1183,102 @@ void AnimationPlayer::_animation_process(double p_delta) {
}
}
-Error AnimationPlayer::add_animation(const StringName &p_name, const Ref<Animation> &p_animation) {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_V_MSG(String(p_name).contains("/") || String(p_name).contains(":") || String(p_name).contains(",") || String(p_name).contains("["), ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + ".");
-#endif
+void AnimationPlayer::_animation_set_cache_update() {
+ // Relatively fast function to update all animations.
- ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER);
+ animation_set_update_pass++;
+ bool clear_cache_needed = false;
- if (animation_set.has(p_name)) {
- _unref_anim(animation_set[p_name].animation);
- animation_set[p_name].animation = p_animation;
- clear_caches();
- } else {
- AnimationData ad;
- ad.animation = p_animation;
- ad.name = p_name;
- animation_set[p_name] = ad;
- }
+ // 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));
+ if (!animation_set.has(key)) {
+ AnimationData ad;
+ ad.animation = K.value;
+ ad.animation_library = animation_libraries[i].name;
+ ad.name = key;
+ ad.last_update = animation_set_update_pass;
+ animation_set.insert(ad.name, ad);
+ } else {
+ 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) {
+ // Animation changed, update and clear caches.
+ clear_cache_needed = true;
+ ad.animation = K.value;
+ ad.animation_library = animation_libraries[i].name;
+ }
- _ref_anim(p_animation);
- notify_property_list_changed();
- return OK;
-}
+ ad.last_update = animation_set_update_pass;
+ }
+ }
+ }
+ }
-void AnimationPlayer::remove_animation(const StringName &p_name) {
- ERR_FAIL_COND(!animation_set.has(p_name));
+ // Check removed
+ List<StringName> to_erase;
+ for (const KeyValue<StringName, AnimationData> &E : animation_set) {
+ if (E.value.last_update != animation_set_update_pass) {
+ // Was not updated, must be erased
+ to_erase.push_back(E.key);
+ clear_cache_needed = true;
+ }
+ }
- stop();
- _unref_anim(animation_set[p_name].animation);
- animation_set.erase(p_name);
+ while (to_erase.size()) {
+ animation_set.erase(to_erase.front()->get());
+ to_erase.pop_front();
+ }
- clear_caches();
- notify_property_list_changed();
+ if (clear_cache_needed) {
+ // If something was modified or removed, caches need to be cleared
+ clear_caches();
+ }
}
-void AnimationPlayer::_ref_anim(const Ref<Animation> &p_anim) {
- Ref<Animation>(p_anim)->connect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed), varray(), CONNECT_REFERENCE_COUNTED);
+void AnimationPlayer::_animation_added(const StringName &p_name, const StringName &p_library) {
+ _animation_set_cache_update();
}
-void AnimationPlayer::_unref_anim(const Ref<Animation> &p_anim) {
- Ref<Animation>(p_anim)->disconnect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed));
-}
+void AnimationPlayer::_animation_removed(const StringName &p_name, const StringName &p_library) {
+ StringName name = p_library == StringName() ? p_name : StringName(String(p_library) + "/" + String(p_name));
-void AnimationPlayer::rename_animation(const StringName &p_name, const StringName &p_new_name) {
- ERR_FAIL_COND(!animation_set.has(p_name));
- ERR_FAIL_COND(String(p_new_name).contains("/") || String(p_new_name).contains(":"));
- ERR_FAIL_COND(animation_set.has(p_new_name));
+ if (!animation_set.has(name)) {
+ return; // No need to update because not the one from the library being used.
+ }
+ _animation_set_cache_update();
- stop();
- AnimationData ad = animation_set[p_name];
- ad.name = p_new_name;
- animation_set.erase(p_name);
- animation_set[p_new_name] = ad;
+ // Erase blends if needed
+ List<BlendKey> to_erase;
+ for (const KeyValue<BlendKey, float> &E : blend_times) {
+ BlendKey bk = E.key;
+ if (bk.from == name || bk.to == name) {
+ to_erase.push_back(bk);
+ }
+ }
+
+ while (to_erase.size()) {
+ blend_times.erase(to_erase.front()->get());
+ to_erase.pop_front();
+ }
+}
+void AnimationPlayer::_rename_animation(const StringName &p_from_name, const StringName &p_to_name) {
+ // Rename autoplay or blends if needed.
List<BlendKey> to_erase;
- Map<BlendKey, float> to_insert;
+ HashMap<BlendKey, float, BlendKey> to_insert;
for (const KeyValue<BlendKey, float> &E : blend_times) {
BlendKey bk = E.key;
BlendKey new_bk = bk;
bool erase = false;
- if (bk.from == p_name) {
- new_bk.from = p_new_name;
+ if (bk.from == p_from_name) {
+ new_bk.from = p_to_name;
erase = true;
}
- if (bk.to == p_name) {
- new_bk.to = p_new_name;
+ if (bk.to == p_from_name) {
+ new_bk.to = p_to_name;
erase = true;
}
@@ -1235,24 +1294,180 @@ void AnimationPlayer::rename_animation(const StringName &p_name, const StringNam
}
while (to_insert.size()) {
- blend_times[to_insert.front()->key()] = to_insert.front()->get();
- to_insert.erase(to_insert.front());
+ blend_times[to_insert.begin()->key] = to_insert.begin()->value;
+ to_insert.remove(to_insert.begin());
}
- if (autoplay == p_name) {
- autoplay = p_new_name;
+ if (autoplay == p_from_name) {
+ autoplay = p_to_name;
}
+}
+
+void AnimationPlayer::_animation_renamed(const StringName &p_name, const StringName &p_to_name, const StringName &p_library) {
+ StringName from_name = p_library == StringName() ? p_name : StringName(String(p_library) + "/" + String(p_name));
+ StringName to_name = p_library == StringName() ? p_to_name : StringName(String(p_library) + "/" + String(p_to_name));
+
+ if (!animation_set.has(from_name)) {
+ return; // No need to update because not the one from the library being used.
+ }
+ _animation_set_cache_update();
+
+ _rename_animation(from_name, to_name);
+}
+
+Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library) {
+ ERR_FAIL_COND_V(p_animation_library.is_null(), ERR_INVALID_PARAMETER);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V_MSG(String(p_name).contains("/") || String(p_name).contains(":") || String(p_name).contains(",") || String(p_name).contains("["), ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + ".");
+#endif
+
+ 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() + "'.");
+
+ if (animation_libraries[i].name.operator String() >= p_name.operator String()) {
+ break;
+ }
+
+ insert_pos++;
+ }
+
+ AnimationLibraryData ald;
+ ald.name = p_name;
+ ald.library = p_animation_library;
+
+ animation_libraries.insert(insert_pos, ald);
+
+ ald.library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_name));
+ ald.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_name));
+ ald.library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed), varray(p_name));
+
+ _animation_set_cache_update();
+
+ notify_property_list_changed();
+
+ return OK;
+}
+
+void AnimationPlayer::remove_animation_library(const StringName &p_name) {
+ int at_pos = -1;
+
+ for (uint32_t i = 0; i < animation_libraries.size(); i++) {
+ if (animation_libraries[i].name == p_name) {
+ at_pos = i;
+ break;
+ }
+ }
+
+ ERR_FAIL_COND(at_pos == -1);
+
+ animation_libraries[at_pos].library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added));
+ animation_libraries[at_pos].library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added));
+ animation_libraries[at_pos].library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed));
+
+ stop();
+
+ for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[at_pos].library->animations) {
+ _unref_anim(K.value);
+ }
+
+ animation_libraries.remove_at(at_pos);
+ _animation_set_cache_update();
+
+ notify_property_list_changed();
+}
+
+void AnimationPlayer::_ref_anim(const Ref<Animation> &p_anim) {
+ Ref<Animation>(p_anim)->connect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed), varray(), CONNECT_REFERENCE_COUNTED);
+}
+
+void AnimationPlayer::_unref_anim(const Ref<Animation> &p_anim) {
+ Ref<Animation>(p_anim)->disconnect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed));
+}
+
+void AnimationPlayer::rename_animation_library(const StringName &p_name, const StringName &p_new_name) {
+ if (p_name == p_new_name) {
+ return;
+ }
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_MSG(String(p_new_name).contains("/") || String(p_new_name).contains(":") || String(p_new_name).contains(",") || String(p_new_name).contains("["), "Invalid animation library name: " + String(p_new_name) + ".");
+#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) {
+ found = true;
+ animation_libraries[i].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_added));
+ animation_libraries[i].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), varray(p_new_name));
+ animation_libraries[i].library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_new_name));
+ animation_libraries[i].library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed), varray(p_new_name));
+
+ for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].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);
+ }
+ }
+ }
+
+ ERR_FAIL_COND(!found);
+
+ stop();
+
+ animation_libraries.sort(); // Must keep alphabetical order.
+
+ _animation_set_cache_update(); // Update cache.
- clear_caches();
notify_property_list_changed();
}
+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) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+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;
+ }
+ }
+ ERR_FAIL_V(Ref<AnimationLibrary>());
+}
+
+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);
+ }
+ 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);
+ }
+}
+
bool AnimationPlayer::has_animation(const StringName &p_name) const {
return animation_set.has(p_name);
}
Ref<Animation> AnimationPlayer::get_animation(const StringName &p_name) const {
- ERR_FAIL_COND_V(!animation_set.has(p_name), Ref<Animation>());
+ ERR_FAIL_COND_V_MSG(!animation_set.has(p_name), Ref<Animation>(), vformat("Animation not found: %s.", p_name));
const AnimationData &data = animation_set[p_name];
@@ -1274,8 +1489,8 @@ void AnimationPlayer::get_animation_list(List<StringName> *p_animations) const {
}
void AnimationPlayer::set_blend_time(const StringName &p_animation1, const StringName &p_animation2, float p_time) {
- ERR_FAIL_COND(!animation_set.has(p_animation1));
- ERR_FAIL_COND(!animation_set.has(p_animation2));
+ ERR_FAIL_COND_MSG(!animation_set.has(p_animation1), vformat("Animation not found: %s.", p_animation1));
+ ERR_FAIL_COND_MSG(!animation_set.has(p_animation2), vformat("Animation not found: %s.", p_animation2));
ERR_FAIL_COND_MSG(p_time < 0, "Blend time cannot be smaller than 0.");
BlendKey bk;
@@ -1332,7 +1547,7 @@ void AnimationPlayer::play(const StringName &p_name, float p_custom_blend, float
name = playback.assigned;
}
- ERR_FAIL_COND_MSG(!animation_set.has(name), "Animation not found: " + name + ".");
+ ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));
Playback &c = playback;
@@ -1435,7 +1650,7 @@ void AnimationPlayer::set_assigned_animation(const String &p_anim) {
if (is_playing()) {
play(p_anim);
} else {
- ERR_FAIL_COND(!animation_set.has(p_anim));
+ ERR_FAIL_COND_MSG(!animation_set.has(p_anim), vformat("Animation not found: %s.", p_anim));
playback.current.pos = 0;
playback.current.from = &animation_set[p_anim];
playback.assigned = p_anim;
@@ -1478,7 +1693,7 @@ float AnimationPlayer::get_playing_speed() const {
void AnimationPlayer::seek(double p_time, bool p_update) {
if (!playback.current.from) {
if (playback.assigned) {
- ERR_FAIL_COND(!animation_set.has(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);
@@ -1494,7 +1709,7 @@ void AnimationPlayer::seek(double p_time, bool p_update) {
void AnimationPlayer::seek_delta(double p_time, float p_delta) {
if (!playback.current.from) {
if (playback.assigned) {
- ERR_FAIL_COND(!animation_set.has(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);
@@ -1531,12 +1746,12 @@ void AnimationPlayer::_animation_changed() {
}
void AnimationPlayer::_stop_playing_caches() {
- for (Set<TrackNodeCache *>::Element *E = playing_caches.front(); E; E = E->next()) {
- if (E->get()->node && E->get()->audio_playing) {
- E->get()->node->call(SNAME("stop"));
+ for (TrackNodeCache *E : playing_caches) {
+ if (E->node && E->audio_playing) {
+ E->node->call(SNAME("stop"));
}
- if (E->get()->node && E->get()->animation_playing) {
- AnimationPlayer *player = Object::cast_to<AnimationPlayer>(E->get()->node);
+ if (E->node && E->animation_playing) {
+ AnimationPlayer *player = Object::cast_to<AnimationPlayer>(E->node);
if (!player) {
continue;
}
@@ -1585,7 +1800,16 @@ StringName AnimationPlayer::find_animation(const Ref<Animation> &p_animation) co
}
}
- return "";
+ return StringName();
+}
+
+StringName AnimationPlayer::find_animation_library(const Ref<Animation> &p_animation) const {
+ for (const KeyValue<StringName, AnimationData> &E : animation_set) {
+ if (E.value.animation == p_animation) {
+ return E.value.animation_library;
+ }
+ }
+ return StringName();
}
void AnimationPlayer::set_autoplay(const String &p_name) {
@@ -1655,7 +1879,7 @@ void AnimationPlayer::_set_process(bool p_process, bool p_force) {
}
void AnimationPlayer::animation_set_next(const StringName &p_animation, const StringName &p_next) {
- ERR_FAIL_COND(!animation_set.has(p_animation));
+ ERR_FAIL_COND_MSG(!animation_set.has(p_animation), vformat("Animation not found: %s.", p_animation));
animation_set[p_animation].next = p_next;
}
@@ -1764,8 +1988,11 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) {
AnimationPlayer *aux_player = memnew(AnimationPlayer);
EditorNode::get_singleton()->add_child(aux_player);
- aux_player->add_animation(SceneStringNames::get_singleton()->RESET, reset_anim);
- aux_player->set_assigned_animation(SceneStringNames::get_singleton()->RESET);
+ Ref<AnimationLibrary> al;
+ al.instantiate();
+ al->add_animation(SceneStringNames::get_singleton()->RESET, reset_anim);
+ aux_player->add_animation_library("default", al);
+ aux_player->set_assigned_animation("default/" + SceneStringNames::get_singleton()->RESET);
// Forcing the use of the original root because the scene where original player belongs may be not the active one
Node *root = get_node(get_root());
Ref<AnimatedValuesBackup> old_values = aux_player->backup_animated_values(root);
@@ -1789,12 +2016,16 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) {
bool AnimationPlayer::can_apply_reset() const {
return has_animation(SceneStringNames::get_singleton()->RESET) && playback.assigned != SceneStringNames::get_singleton()->RESET;
}
-#endif
+#endif // TOOLS_ENABLED
void AnimationPlayer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_animation", "name", "animation"), &AnimationPlayer::add_animation);
- ClassDB::bind_method(D_METHOD("remove_animation", "name"), &AnimationPlayer::remove_animation);
- ClassDB::bind_method(D_METHOD("rename_animation", "name", "newname"), &AnimationPlayer::rename_animation);
+ ClassDB::bind_method(D_METHOD("add_animation_library", "name", "library"), &AnimationPlayer::add_animation_library);
+ ClassDB::bind_method(D_METHOD("remove_animation_library", "name"), &AnimationPlayer::remove_animation_library);
+ ClassDB::bind_method(D_METHOD("rename_animation_library", "name", "newname"), &AnimationPlayer::rename_animation_library);
+ ClassDB::bind_method(D_METHOD("has_animation_library", "name"), &AnimationPlayer::has_animation_library);
+ ClassDB::bind_method(D_METHOD("get_animation_library", "name"), &AnimationPlayer::get_animation_library);
+ ClassDB::bind_method(D_METHOD("get_animation_library_list"), &AnimationPlayer::_get_animation_library_list);
+
ClassDB::bind_method(D_METHOD("has_animation", "name"), &AnimationPlayer::has_animation);
ClassDB::bind_method(D_METHOD("get_animation", "name"), &AnimationPlayer::get_animation);
ClassDB::bind_method(D_METHOD("get_animation_list"), &AnimationPlayer::_get_animation_list);
@@ -1838,6 +2069,7 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_root"), &AnimationPlayer::get_root);
ClassDB::bind_method(D_METHOD("find_animation", "animation"), &AnimationPlayer::find_animation);
+ ClassDB::bind_method(D_METHOD("find_animation_library", "animation"), &AnimationPlayer::find_animation_library);
ClassDB::bind_method(D_METHOD("clear_caches"), &AnimationPlayer::clear_caches);
@@ -1863,7 +2095,7 @@ void AnimationPlayer::_bind_methods() {
ADD_GROUP("Playback Options", "playback_");
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"), "set_default_blend_time", "get_default_blend_time");
+ 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::INT, "method_call_mode", PROPERTY_HINT_ENUM, "Deferred,Immediate"), "set_method_call_mode", "get_method_call_mode");
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index c4fc69f370..7e4bda14e5 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -36,6 +36,7 @@
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_3d.h"
#include "scene/resources/animation.h"
+#include "scene/resources/animation_library.h"
#ifdef TOOLS_ENABLED
class AnimatedValuesBackup : public RefCounted {
@@ -62,7 +63,6 @@ public:
class AnimationPlayer : public Node {
GDCLASS(AnimationPlayer, Node);
- OBJ_CATEGORY("Animation Nodes");
public:
enum AnimationProcessCallback {
@@ -94,7 +94,7 @@ private:
struct TrackNodeCache {
NodePath path;
uint32_t id = 0;
- RES resource;
+ Ref<Resource> resource;
Node *node = nullptr;
Node2D *node_2d = nullptr;
#ifndef _3D_DISABLED
@@ -132,7 +132,7 @@ private:
Variant capture;
};
- Map<StringName, PropertyAnim> property_anim;
+ HashMap<StringName, PropertyAnim> property_anim;
struct BezierAnim {
Vector<StringName> bezier_property;
@@ -142,7 +142,7 @@ private:
uint64_t accum_pass = 0;
};
- Map<StringName, BezierAnim> bezier_anim;
+ HashMap<StringName, BezierAnim> bezier_anim;
uint32_t last_setup_pass = 0;
TrackNodeCache() {}
@@ -153,6 +153,16 @@ private:
int bone_idx = -1;
int blend_shape_idx = -1;
+ static uint32_t hash(const TrackNodeCacheKey &p_key) {
+ uint32_t h = hash_one_uint64(p_key.id);
+ h = hash_djb2_one_32(p_key.bone_idx, h);
+ return hash_djb2_one_32(p_key.blend_shape_idx, h);
+ }
+
+ inline bool operator==(const TrackNodeCacheKey &p_right) const {
+ return id == p_right.id && bone_idx == p_right.bone_idx && blend_shape_idx == p_right.blend_shape_idx;
+ }
+
inline bool operator<(const TrackNodeCacheKey &p_right) const {
if (id == p_right.id) {
if (blend_shape_idx == p_right.blend_shape_idx) {
@@ -166,7 +176,7 @@ private:
}
};
- Map<TrackNodeCacheKey, TrackNodeCache> node_cache_map;
+ HashMap<TrackNodeCacheKey, TrackNodeCache, TrackNodeCacheKey> node_cache_map;
TrackNodeCache *cache_update[NODE_CACHE_UPDATE_MAX];
int cache_update_size = 0;
@@ -174,7 +184,7 @@ private:
int cache_update_prop_size = 0;
TrackNodeCache::BezierAnim *cache_update_bezier[NODE_CACHE_UPDATE_MAX];
int cache_update_bezier_size = 0;
- Set<TrackNodeCache *> playing_caches;
+ HashSet<TrackNodeCache *> playing_caches;
uint64_t accum_pass = 1;
float speed_scale = 1.0;
@@ -185,16 +195,39 @@ private:
StringName next;
Vector<TrackNodeCache *> node_cache;
Ref<Animation> animation;
+ StringName animation_library;
+ uint64_t last_update = 0;
};
- Map<StringName, AnimationData> animation_set;
+ HashMap<StringName, AnimationData> animation_set;
+
+ struct AnimationLibraryData {
+ StringName name;
+ Ref<AnimationLibrary> library;
+ bool operator<(const AnimationLibraryData &p_data) const { return name.operator String() < p_data.name.operator String(); }
+ };
+
+ LocalVector<AnimationLibraryData> animation_libraries;
+
struct BlendKey {
StringName from;
StringName to;
- bool operator<(const BlendKey &bk) const { return from == bk.from ? String(to) < String(bk.to) : String(from) < String(bk.from); }
+ static uint32_t hash(const BlendKey &p_key) {
+ return hash_one_uint64((uint64_t(p_key.from.hash()) << 32) | uint32_t(p_key.to.hash()));
+ }
+ bool operator==(const BlendKey &bk) const {
+ return from == bk.from && to == bk.to;
+ }
+ bool operator<(const BlendKey &bk) const {
+ if (from == bk.from) {
+ return to < bk.to;
+ } else {
+ return from < bk.from;
+ }
+ }
};
- Map<BlendKey, float> blend_times;
+ HashMap<BlendKey, float, BlendKey> blend_times;
struct PlaybackData {
AnimationData *from = nullptr;
@@ -262,6 +295,15 @@ private:
bool playing = false;
+ uint64_t animation_set_update_pass = 1;
+ void _animation_set_cache_update();
+ void _animation_added(const StringName &p_name, const StringName &p_library);
+ void _animation_removed(const StringName &p_name, const StringName &p_library);
+ void _animation_renamed(const StringName &p_name, const StringName &p_to_name, const StringName &p_library);
+ void _rename_animation(const StringName &p_from_name, const StringName &p_to_name);
+
+ TypedArray<StringName> _get_animation_library_list() const;
+
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -273,13 +315,18 @@ protected:
public:
StringName find_animation(const Ref<Animation> &p_animation) const;
+ StringName find_animation_library(const Ref<Animation> &p_animation) const;
+
+ Error add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library);
+ void remove_animation_library(const StringName &p_name);
+ void rename_animation_library(const StringName &p_name, const StringName &p_new_name);
+ Ref<AnimationLibrary> get_animation_library(const StringName &p_name) const;
+ void get_animation_library_list(List<StringName> *p_animations) const;
+ bool has_animation_library(const StringName &p_name) const;
- Error add_animation(const StringName &p_name, const Ref<Animation> &p_animation);
- void remove_animation(const StringName &p_name);
- void rename_animation(const StringName &p_name, const StringName &p_new_name);
- bool has_animation(const StringName &p_name) const;
Ref<Animation> get_animation(const StringName &p_name) const;
void get_animation_list(List<StringName> *p_animations) const;
+ bool has_animation(const StringName &p_name) const;
void set_blend_time(const StringName &p_animation1, const StringName &p_animation2, float p_time);
float get_blend_time(const StringName &p_animation1, const StringName &p_animation2) const;
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 309c2b5245..d34e8db093 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -88,7 +88,7 @@ void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {
}
}
-void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged) {
+void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_seek_root, real_t p_blend, int p_pingponged) {
ERR_FAIL_COND(!state);
ERR_FAIL_COND(!state->player->has_animation(p_animation));
@@ -115,17 +115,18 @@ void AnimationNode::blend_animation(const StringName &p_animation, double p_time
anim_state.animation = animation;
anim_state.seeked = p_seeked;
anim_state.pingponged = p_pingponged;
+ anim_state.seek_root = p_seek_root;
state->animation_states.push_back(anim_state);
}
-double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections) {
+double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, bool p_seek_root, const Vector<StringName> &p_connections) {
base_path = p_base_path;
parent = p_parent;
connections = p_connections;
state = p_state;
- double t = process(p_time, p_seek);
+ double t = process(p_time, p_seek, p_seek_root);
state = nullptr;
parent = nullptr;
@@ -144,7 +145,7 @@ void AnimationNode::make_invalid(const String &p_reason) {
state->invalid_reasons += String::utf8("• ") + p_reason;
}
-double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
+double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize) {
ERR_FAIL_INDEX_V(p_input, inputs.size(), 0);
ERR_FAIL_COND_V(!state, 0);
@@ -163,7 +164,7 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_
//inputs.write[p_input].last_pass = state->last_pass;
real_t activity = 0.0;
- double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity);
+ double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_optimize, &activity);
Vector<AnimationTree::Activity> *activity_ptr = state->tree->input_activity_map.getptr(base_path);
@@ -174,11 +175,11 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_
return ret;
}
-double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) {
- return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_blend, p_filter, p_optimize);
+double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize) {
+ return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_optimize);
}
-double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) {
+double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) {
ERR_FAIL_COND_V(!p_node.is_valid(), 0);
ERR_FAIL_COND_V(!state, 0);
@@ -198,12 +199,11 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
blendw[i] = 0.0; //all to zero by default
}
- const NodePath *K = nullptr;
- while ((K = filter.next(K))) {
- if (!state->track_map.has(*K)) {
+ for (const KeyValue<NodePath, bool> &E : filter) {
+ if (!state->track_map.has(E.key)) {
continue;
}
- int idx = state->track_map[*K];
+ int idx = state->track_map[E.key];
blendw[idx] = 1.0; //filtered goes to one
}
@@ -287,9 +287,9 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
}
if (!p_seek && p_optimize && !any_valid) {
- return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_connections);
+ return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_seek_root, p_connections);
}
- return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_connections);
+ return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_seek_root, p_connections);
}
int AnimationNode::get_input_count() const {
@@ -333,9 +333,9 @@ void AnimationNode::remove_input(int p_index) {
emit_changed();
}
-double AnimationNode::process(double p_time, bool p_seek) {
+double AnimationNode::process(double p_time, bool p_seek, bool p_seek_root) {
double ret;
- if (GDVIRTUAL_CALL(_process, p_time, p_seek, ret)) {
+ if (GDVIRTUAL_CALL(_process, p_time, p_seek, p_seek_root, ret)) {
return ret;
}
@@ -374,9 +374,8 @@ bool AnimationNode::has_filter() const {
Array AnimationNode::_get_filters() const {
Array paths;
- const NodePath *K = nullptr;
- while ((K = filter.next(K))) {
- paths.push_back(String(*K)); //use strings, so sorting is possible
+ for (const KeyValue<NodePath, bool> &E : filter) {
+ paths.push_back(String(E.key)); //use strings, so sorting is possible
}
paths.sort(); //done so every time the scene is saved, it does not change
@@ -420,9 +419,9 @@ void AnimationNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters);
ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters);
- ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
- ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "seek_root", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "seek_root", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "seek_root", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
ClassDB::bind_method(D_METHOD("set_parameter", "name", "value"), &AnimationNode::set_parameter);
ClassDB::bind_method(D_METHOD("get_parameter", "name"), &AnimationNode::get_parameter);
@@ -434,7 +433,7 @@ 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(_process, "time", "seek");
+ GDVIRTUAL_BIND(_process, "time", "seek", "seek_root");
GDVIRTUAL_BIND(_get_caption);
GDVIRTUAL_BIND(_has_filter);
@@ -488,9 +487,9 @@ void AnimationTree::set_active(bool p_active) {
}
if (!active && is_inside_tree()) {
- for (Set<TrackCache *>::Element *E = playing_caches.front(); E; E = E->next()) {
- if (ObjectDB::get_instance(E->get()->object_id)) {
- E->get()->object->call(SNAME("stop"));
+ for (const TrackCache *E : playing_caches) {
+ if (ObjectDB::get_instance(E->object_id)) {
+ E->object->call(SNAME("stop"));
}
}
@@ -540,6 +539,11 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
List<StringName> sname;
player->get_animation_list(&sname);
+ Ref<Animation> reset_anim;
+ bool has_reset_anim = player->has_animation(SceneStringNames::get_singleton()->RESET);
+ if (has_reset_anim) {
+ reset_anim = player->get_animation(SceneStringNames::get_singleton()->RESET);
+ }
for (const StringName &E : sname) {
Ref<Animation> anim = player->get_animation(E);
for (int i = 0; i < anim->get_track_count(); i++) {
@@ -565,7 +569,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
if (!track) {
- RES resource;
+ Ref<Resource> resource;
Vector<StringName> leftover_path;
Node *child = parent->get_node_and_resource(path, resource, leftover_path);
@@ -593,6 +597,12 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track = track_value;
+ 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_value->init_value = reset_anim->track_get_key_value(rt, 0);
+ }
+ }
} break;
case Animation::TYPE_POSITION_3D:
case Animation::TYPE_ROTATION_3D:
@@ -612,16 +622,17 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_xform->skeleton = nullptr;
track_xform->bone_idx = -1;
+ bool has_rest = false;
if (path.get_subname_count() == 1 && Object::cast_to<Skeleton3D>(node_3d)) {
Skeleton3D *sk = Object::cast_to<Skeleton3D>(node_3d);
track_xform->skeleton = sk;
int bone_idx = sk->find_bone(path.get_subname(0));
if (bone_idx != -1) {
+ has_rest = true;
track_xform->bone_idx = bone_idx;
Transform3D rest = sk->get_bone_rest(bone_idx);
track_xform->init_loc = rest.origin;
- track_xform->ref_rot = rest.basis.get_rotation_quaternion();
- track_xform->init_rot = track_xform->ref_rot.log();
+ track_xform->init_rot = rest.basis.get_rotation_quaternion();
track_xform->init_scale = rest.basis.get_scale();
}
}
@@ -645,6 +656,25 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
}
+ // For non Skeleton3D bone animation.
+ if (has_reset_anim && !has_rest) {
+ int rt = reset_anim->find_track(path, track_type);
+ if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) {
+ switch (track_type) {
+ case Animation::TYPE_POSITION_3D: {
+ track_xform->init_loc = reset_anim->track_get_key_value(rt, 0);
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ track_xform->init_rot = reset_anim->track_get_key_value(rt, 0);
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+ track_xform->init_scale = reset_anim->track_get_key_value(rt, 0);
+ } break;
+ default: {
+ }
+ }
+ }
+ }
#endif // _3D_DISABLED
} break;
case Animation::TYPE_BLEND_SHAPE: {
@@ -675,6 +705,13 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_bshape->object = mesh_3d;
track_bshape->object_id = mesh_3d->get_instance_id();
track = track_bshape;
+
+ 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_bshape->init_value = reset_anim->track_get_key_value(rt, 0);
+ }
+ }
#endif
} break;
case Animation::TYPE_METHOD: {
@@ -704,6 +741,13 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_bezier->object_id = track_bezier->object->get_instance_id();
track = track_bezier;
+
+ 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);
+ }
+ }
} break;
case Animation::TYPE_AUDIO: {
TrackCacheAudio *track_audio = memnew(TrackCacheAudio);
@@ -758,11 +802,10 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
List<NodePath> to_delete;
- const NodePath *K = nullptr;
- while ((K = track_cache.next(K))) {
- TrackCache *tc = track_cache[*K];
+ for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ TrackCache *tc = track_cache[K.key];
if (tc->setup_pass != setup_pass) {
- to_delete.push_back(*K);
+ to_delete.push_back(K.key);
}
}
@@ -775,10 +818,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
state.track_map.clear();
- K = nullptr;
int idx = 0;
- while ((K = track_cache.next(K))) {
- state.track_map[*K] = idx;
+ for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ state.track_map[K.key] = idx;
idx++;
}
@@ -790,9 +832,8 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
void AnimationTree::_clear_caches() {
- const NodePath *K = nullptr;
- while ((K = track_cache.next(K))) {
- memdelete(track_cache[*K]);
+ for (KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ memdelete(K.value);
}
playing_caches.clear();
@@ -819,7 +860,6 @@ void AnimationTree::_process_graph(double p_delta) {
_update_properties(); //if properties need updating, update them
//check all tracks, see if they need modification
-
root_motion_transform = Transform3D();
if (!root.is_valid()) {
@@ -898,11 +938,11 @@ void AnimationTree::_process_graph(double p_delta) {
{
if (started) {
//if started, seek
- root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, 0, true, Vector<StringName>());
+ root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, 0, true, false, Vector<StringName>());
started = false;
}
- root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, p_delta, false, Vector<StringName>());
+ root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, p_delta, false, false, Vector<StringName>());
}
if (!state.valid) {
@@ -922,6 +962,7 @@ void AnimationTree::_process_graph(double p_delta) {
int pingponged = as.pingponged;
#ifndef _3D_DISABLED
bool backward = signbit(delta);
+ bool calc_root = !seeked || as.seek_root;
#endif // _3D_DISABLED
for (int i = 0; i < a->get_track_count(); i++) {
@@ -950,13 +991,13 @@ void AnimationTree::_process_graph(double p_delta) {
case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
- if (t->process_pass != process_pass) {
- t->process_pass = process_pass;
- t->loc = t->init_loc;
- t->rot = t->init_rot;
- t->scale = t->init_scale;
- }
- if (track->root_motion) {
+ if (track->root_motion && calc_root) {
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = Vector3(0, 0, 0);
+ t->rot = Quaternion(0, 0, 0, 1);
+ t->scale = Vector3(0, 0, 0);
+ }
double prev_time = time - delta;
if (!backward) {
if (prev_time < 0) {
@@ -1012,7 +1053,7 @@ void AnimationTree::_process_graph(double p_delta) {
}
a->position_track_interpolate(i, 0, &loc[1]);
t->loc += (loc[1] - loc[0]) * blend;
- prev_time = 0;
+ prev_time = (double)a->get_length();
}
}
@@ -1026,6 +1067,12 @@ void AnimationTree::_process_graph(double p_delta) {
prev_time = !backward ? 0 : (double)a->get_length();
} else {
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = t->init_loc;
+ t->rot = t->init_rot;
+ t->scale = t->init_scale;
+ }
Vector3 loc;
Error err = a->position_track_interpolate(i, time, &loc);
@@ -1040,13 +1087,13 @@ void AnimationTree::_process_graph(double p_delta) {
case Animation::TYPE_ROTATION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
- if (t->process_pass != process_pass) {
- t->process_pass = process_pass;
- t->loc = t->init_loc;
- t->rot = t->init_rot;
- t->scale = t->init_scale;
- }
- if (track->root_motion) {
+ if (track->root_motion && calc_root) {
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = Vector3(0, 0, 0);
+ t->rot = Quaternion(0, 0, 0, 1);
+ t->scale = Vector3(0, 0, 0);
+ }
double prev_time = time - delta;
if (!backward) {
if (prev_time < 0) {
@@ -1091,7 +1138,7 @@ void AnimationTree::_process_graph(double p_delta) {
continue;
}
a->rotation_track_interpolate(i, (double)a->get_length(), &rot[1]);
- t->rot += (rot[1].log() - rot[0].log()) * blend;
+ t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = 0;
}
} else {
@@ -1101,8 +1148,8 @@ void AnimationTree::_process_graph(double p_delta) {
continue;
}
a->rotation_track_interpolate(i, 0, &rot[1]);
- t->rot += (rot[1].log() - rot[0].log()) * blend;
- prev_time = 0;
+ t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
+ prev_time = (double)a->get_length();
}
}
@@ -1112,10 +1159,16 @@ void AnimationTree::_process_graph(double p_delta) {
}
a->rotation_track_interpolate(i, time, &rot[1]);
- t->rot += (rot[1].log() - rot[0].log()) * blend;
+ t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
prev_time = !backward ? 0 : (double)a->get_length();
} else {
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = t->init_loc;
+ t->rot = t->init_rot;
+ t->scale = t->init_scale;
+ }
Quaternion rot;
Error err = a->rotation_track_interpolate(i, time, &rot);
@@ -1123,23 +1176,20 @@ void AnimationTree::_process_graph(double p_delta) {
continue;
}
- if (signbit(rot.dot(t->ref_rot))) {
- rot = -rot;
- }
- t->rot += (rot.log() - t->init_rot) * blend;
+ t->rot = (t->rot * Quaternion().slerp(t->init_rot.inverse() * rot, blend)).normalized();
}
#endif // _3D_DISABLED
} break;
case Animation::TYPE_SCALE_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
- if (t->process_pass != process_pass) {
- t->process_pass = process_pass;
- t->loc = t->init_loc;
- t->rot = t->init_rot;
- t->scale = t->init_scale;
- }
- if (track->root_motion) {
+ if (track->root_motion && calc_root) {
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = Vector3(0, 0, 0);
+ t->rot = Quaternion(0, 0, 0, 1);
+ t->scale = Vector3(0, 0, 0);
+ }
double prev_time = time - delta;
if (!backward) {
if (prev_time < 0) {
@@ -1195,7 +1245,7 @@ void AnimationTree::_process_graph(double p_delta) {
}
a->scale_track_interpolate(i, 0, &scale[1]);
t->scale += (scale[1] - scale[0]) * blend;
- prev_time = 0;
+ prev_time = (double)a->get_length();
}
}
@@ -1209,6 +1259,12 @@ void AnimationTree::_process_graph(double p_delta) {
prev_time = !backward ? 0 : (double)a->get_length();
} else {
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = t->init_loc;
+ t->rot = t->init_rot;
+ t->scale = t->init_scale;
+ }
Vector3 scale;
Error err = a->scale_track_interpolate(i, time, &scale);
@@ -1226,7 +1282,7 @@ void AnimationTree::_process_graph(double p_delta) {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
- t->value = 0;
+ t->value = t->init_value;
}
float value;
@@ -1238,7 +1294,7 @@ void AnimationTree::_process_graph(double p_delta) {
continue;
}
- t->value += value * blend;
+ t->value += (value - t->init_value) * blend;
#endif // _3D_DISABLED
} break;
case Animation::TYPE_VALUE: {
@@ -1246,8 +1302,7 @@ void AnimationTree::_process_graph(double p_delta) {
Animation::UpdateMode update_mode = a->value_track_get_update_mode(i);
- if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { //delta == 0 means seek
-
+ if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) {
Variant value = a->value_track_interpolate(i, time);
if (value == Variant()) {
@@ -1256,10 +1311,14 @@ void AnimationTree::_process_graph(double p_delta) {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
- t->value = value;
- t->value.zero();
+ if (!t->init_value) {
+ t->init_value = value;
+ t->init_value.zero();
+ }
+ t->value = t->init_value;
}
+ Variant::sub(value, t->init_value, value);
Variant::blend(t->value, value, blend, t->value);
} else {
if (blend < CMP_EPSILON) {
@@ -1303,10 +1362,10 @@ void AnimationTree::_process_graph(double p_delta) {
if (t->process_pass != process_pass) {
t->process_pass = process_pass;
- t->value = 0;
+ t->value = t->init_value;
}
- t->value += bezier * blend;
+ t->value += (bezier - t->init_value) * blend;
} break;
case Animation::TYPE_AUDIO: {
if (blend < CMP_EPSILON) {
@@ -1384,7 +1443,7 @@ void AnimationTree::_process_graph(double p_delta) {
t->start = time;
}
} else if (t->playing) {
- bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
+ bool loop = a->get_loop_mode() != Animation::LOOP_NONE;
bool stop = false;
@@ -1453,13 +1512,13 @@ void AnimationTree::_process_graph(double p_delta) {
double at_anim_pos = 0.0;
switch (anim->get_loop_mode()) {
- case Animation::LoopMode::LOOP_NONE: {
+ case Animation::LOOP_NONE: {
at_anim_pos = MAX((double)anim->get_length(), time - pos); //seek to end
} break;
- case Animation::LoopMode::LOOP_LINEAR: {
+ case Animation::LOOP_LINEAR: {
at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); //seek to loop
} break;
- case Animation::LoopMode::LOOP_PINGPONG: {
+ case Animation::LOOP_PINGPONG: {
at_anim_pos = Math::pingpong(time - pos, (double)a->get_length());
} break;
default:
@@ -1505,9 +1564,8 @@ void AnimationTree::_process_graph(double p_delta) {
{
// finally, set the tracks
- const NodePath *K = nullptr;
- while ((K = track_cache.next(K))) {
- TrackCache *track = track_cache[*K];
+ for (const KeyValue<NodePath, TrackCache *> &K : track_cache) {
+ TrackCache *track = K.value;
if (track->process_pass != process_pass) {
continue; //not processed, ignore
}
@@ -1516,12 +1574,11 @@ void AnimationTree::_process_graph(double p_delta) {
case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
- t->rot = t->rot.exp();
if (t->root_motion) {
Transform3D xform;
xform.origin = t->loc;
- xform.basis.set_quaternion_scale(t->rot, t->scale);
+ xform.basis.set_quaternion_scale(t->rot, Vector3(1, 1, 1) + t->scale);
root_motion_transform = xform;
@@ -1641,18 +1698,18 @@ TypedArray<String> AnimationTree::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!root.is_valid()) {
- warnings.push_back(TTR("No root AnimationNode for the graph is set."));
+ warnings.push_back(RTR("No root AnimationNode for the graph is set."));
}
if (!has_node(animation_player)) {
- warnings.push_back(TTR("Path to an AnimationPlayer node containing animations is not set."));
+ warnings.push_back(RTR("Path to an AnimationPlayer node containing animations is not set."));
} else {
AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node(animation_player));
if (!player) {
- warnings.push_back(TTR("Path set for AnimationPlayer does not lead to an AnimationPlayer node."));
+ warnings.push_back(RTR("Path set for AnimationPlayer does not lead to an AnimationPlayer node."));
} else if (!player->has_node(player->get_root())) {
- warnings.push_back(TTR("The AnimationPlayer root node is not a valid node."));
+ warnings.push_back(RTR("The AnimationPlayer root node is not a valid node."));
}
}
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 3ccb6be073..b646efede4 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -37,6 +37,8 @@
#include "scene/resources/animation.h"
class AnimationNodeBlendTree;
+class AnimationNodeStartState;
+class AnimationNodeEndState;
class AnimationPlayer;
class AnimationTree;
@@ -66,6 +68,7 @@ public:
const Vector<real_t> *track_blends = nullptr;
real_t blend = 0.0;
bool seeked = false;
+ bool seek_root = false;
int pingponged = 0;
};
@@ -83,7 +86,7 @@ public:
Vector<real_t> blends;
State *state = nullptr;
- double _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections);
+ double _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, bool p_seek_root, const Vector<StringName> &p_connections);
//all this is temporary
StringName base_path;
@@ -96,12 +99,12 @@ public:
Array _get_filters() const;
void _set_filters(const Array &p_filters);
friend class AnimationNodeBlendTree;
- double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr);
+ double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr);
protected:
- void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged = 0);
- double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
- double blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
+ void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_seek_root, real_t p_blend, int p_pingponged = 0);
+ double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
+ double blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
void make_invalid(const String &p_reason);
@@ -113,7 +116,7 @@ protected:
GDVIRTUAL0RC(Array, _get_parameter_list)
GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName)
GDVIRTUAL1RC(Variant, _get_parameter_default_value, StringName)
- GDVIRTUAL2RC(double, _process, double, bool)
+ GDVIRTUAL3RC(double, _process, double, bool, bool)
GDVIRTUAL0RC(String, _get_caption)
GDVIRTUAL0RC(bool, _has_filter)
@@ -131,7 +134,7 @@ public:
virtual void get_child_nodes(List<ChildNode> *r_child_nodes);
- virtual double process(double p_time, bool p_seek);
+ virtual double process(double p_time, bool p_seek, bool p_seek_root);
virtual String get_caption() const;
int get_input_count() const;
@@ -164,6 +167,14 @@ public:
AnimationRootNode() {}
};
+class AnimationNodeStartState : public AnimationRootNode {
+ GDCLASS(AnimationNodeStartState, AnimationRootNode);
+};
+
+class AnimationNodeEndState : public AnimationRootNode {
+ GDCLASS(AnimationNodeEndState, AnimationRootNode);
+};
+
class AnimationTree : public Node {
GDCLASS(AnimationTree, Node);
@@ -198,8 +209,7 @@ private:
bool rot_used = false;
bool scale_used = false;
Vector3 init_loc = Vector3(0, 0, 0);
- Quaternion ref_rot = Quaternion(0, 0, 0, 1);
- Quaternion init_rot = Quaternion(0, 0, 0, 0);
+ Quaternion init_rot = Quaternion(0, 0, 0, 1);
Vector3 init_scale = Vector3(1, 1, 1);
Vector3 loc;
Quaternion rot;
@@ -212,12 +222,14 @@ private:
struct TrackCacheBlendShape : public TrackCache {
MeshInstance3D *mesh_3d = nullptr;
+ float init_value = 0;
float value = 0;
int shape_index = -1;
TrackCacheBlendShape() { type = Animation::TYPE_BLEND_SHAPE; }
};
struct TrackCacheValue : public TrackCache {
+ Variant init_value;
Variant value;
Vector<StringName> subpath;
TrackCacheValue() { type = Animation::TYPE_VALUE; }
@@ -228,6 +240,7 @@ private:
};
struct TrackCacheBezier : public TrackCache {
+ real_t init_value = 0.0;
real_t value = 0.0;
Vector<StringName> subpath;
TrackCacheBezier() {
@@ -254,7 +267,7 @@ private:
};
HashMap<NodePath, TrackCache *> track_cache;
- Set<TrackCache *> playing_caches;
+ HashSet<TrackCache *> playing_caches;
Ref<AnimationNode> root;
diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp
index 42adc1ea02..47f08219a9 100644
--- a/scene/animation/root_motion_view.cpp
+++ b/scene/animation/root_motion_view.cpp
@@ -114,9 +114,8 @@ void RootMotionView::_notification(int p_what) {
first = false;
transform.orthonormalize(); //don't want scale, too imprecise
- transform.affine_invert();
- accumulated = transform * accumulated;
+ accumulated = accumulated * transform;
accumulated.origin.x = Math::fposmod(accumulated.origin.x, cell_size);
if (zero_y) {
accumulated.origin.y = 0;
@@ -134,9 +133,9 @@ void RootMotionView::_notification(int p_what) {
Vector3 from(i * cell_size, 0, j * cell_size);
Vector3 from_i((i + 1) * cell_size, 0, j * cell_size);
Vector3 from_j(i * cell_size, 0, (j + 1) * cell_size);
- from = accumulated.xform(from);
- from_i = accumulated.xform(from_i);
- from_j = accumulated.xform(from_j);
+ from = accumulated.xform_inv(from);
+ from_i = accumulated.xform_inv(from_i);
+ from_j = accumulated.xform_inv(from_j);
Color c = color, c_i = color, c_j = color;
c.a *= MAX(0, 1.0 - from.length() / radius);
@@ -184,8 +183,8 @@ void RootMotionView::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "animation_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationTree"), "set_animation_path", "get_animation_path");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater"), "set_cell_size", "get_cell_size");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater,suffix:m"), "set_cell_size", "get_cell_size");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,16,0.01,or_greater,suffix:m"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "zero_y"), "set_zero_y", "get_zero_y");
}
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index a2fed718be..5457da472f 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -130,6 +130,7 @@ void Tween::stop() {
started = false;
running = false;
dead = false;
+ total_time = 0;
}
void Tween::pause() {
@@ -151,10 +152,6 @@ bool Tween::is_running() {
return running;
}
-void Tween::set_valid(bool p_valid) {
- valid = p_valid;
-}
-
bool Tween::is_valid() {
return valid;
}
@@ -272,21 +269,24 @@ bool Tween::step(float p_delta) {
ERR_FAIL_COND_V_MSG(tweeners.is_empty(), false, "Tween started, but has no Tweeners.");
current_step = 0;
loops_done = 0;
+ total_time = 0;
start_tweeners();
started = true;
}
float rem_delta = p_delta * speed_scale;
bool step_active = false;
+ total_time += rem_delta;
+
+#ifdef DEBUG_ENABLED
+ float initial_delta = rem_delta;
+ bool potential_infinite = false;
+#endif
while (rem_delta > 0 && running) {
float step_delta = rem_delta;
step_active = false;
-#ifdef DEBUG_ENABLED
- float prev_delta = rem_delta;
-#endif
-
for (Ref<Tweener> &tweener : tweeners.write[current_step]) {
// Modified inside Tweener.step().
float temp_delta = rem_delta;
@@ -311,17 +311,21 @@ bool Tween::step(float p_delta) {
emit_signal(SNAME("loop_finished"), loops_done);
current_step = 0;
start_tweeners();
+#ifdef DEBUG_ENABLED
+ if (loops <= 0 && Math::is_equal_approx(rem_delta, initial_delta)) {
+ if (!potential_infinite) {
+ potential_infinite = true;
+ } else {
+ // Looped twice without using any time, this is 100% certain infinite loop.
+ ERR_FAIL_V_MSG(false, "Infinite loop detected. Check set_loops() description for more info.");
+ }
+ }
+#endif
}
} else {
start_tweeners();
}
}
-
-#ifdef DEBUG_ENABLED
- if (Math::is_equal_approx(rem_delta, prev_delta) && running && loops <= 0) {
- ERR_FAIL_V_MSG(false, "Infinite loop detected. Check set_loops() description for more info.");
- }
-#endif
}
return true;
@@ -331,7 +335,7 @@ bool Tween::can_process(bool p_tree_paused) const {
if (is_bound && pause_mode == TWEEN_PAUSE_BOUND) {
Node *bound_node = get_bound_node();
if (bound_node) {
- return bound_node->can_process();
+ return bound_node->is_inside_tree() && bound_node->can_process();
}
}
@@ -346,6 +350,10 @@ Node *Tween::get_bound_node() const {
}
}
+float Tween::get_total_time() const {
+ return total_time;
+}
+
real_t Tween::run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t p_time, real_t p_initial, real_t p_delta, real_t p_duration) {
if (p_duration == 0) {
// Special case to avoid dividing by 0 in equations.
@@ -448,12 +456,12 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f
Transform2D d = p_delta_val;
Transform2D r;
- APPLY_EQUATION(elements[0][0]);
- APPLY_EQUATION(elements[0][1]);
- APPLY_EQUATION(elements[1][0]);
- APPLY_EQUATION(elements[1][1]);
- APPLY_EQUATION(elements[2][0]);
- APPLY_EQUATION(elements[2][1]);
+ APPLY_EQUATION(columns[0][0]);
+ APPLY_EQUATION(columns[0][1]);
+ APPLY_EQUATION(columns[1][0]);
+ APPLY_EQUATION(columns[1][1]);
+ APPLY_EQUATION(columns[2][0]);
+ APPLY_EQUATION(columns[2][1]);
return r;
}
@@ -488,15 +496,15 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f
Basis d = p_delta_val;
Basis r;
- APPLY_EQUATION(elements[0][0]);
- APPLY_EQUATION(elements[0][1]);
- APPLY_EQUATION(elements[0][2]);
- APPLY_EQUATION(elements[1][0]);
- APPLY_EQUATION(elements[1][1]);
- APPLY_EQUATION(elements[1][2]);
- APPLY_EQUATION(elements[2][0]);
- APPLY_EQUATION(elements[2][1]);
- APPLY_EQUATION(elements[2][2]);
+ APPLY_EQUATION(rows[0][0]);
+ APPLY_EQUATION(rows[0][1]);
+ APPLY_EQUATION(rows[0][2]);
+ APPLY_EQUATION(rows[1][0]);
+ APPLY_EQUATION(rows[1][1]);
+ APPLY_EQUATION(rows[1][2]);
+ APPLY_EQUATION(rows[2][0]);
+ APPLY_EQUATION(rows[2][1]);
+ APPLY_EQUATION(rows[2][2]);
return r;
}
@@ -505,15 +513,15 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f
Transform3D d = p_delta_val;
Transform3D r;
- APPLY_EQUATION(basis.elements[0][0]);
- APPLY_EQUATION(basis.elements[0][1]);
- APPLY_EQUATION(basis.elements[0][2]);
- APPLY_EQUATION(basis.elements[1][0]);
- APPLY_EQUATION(basis.elements[1][1]);
- APPLY_EQUATION(basis.elements[1][2]);
- APPLY_EQUATION(basis.elements[2][0]);
- APPLY_EQUATION(basis.elements[2][1]);
- APPLY_EQUATION(basis.elements[2][2]);
+ APPLY_EQUATION(basis.rows[0][0]);
+ APPLY_EQUATION(basis.rows[0][1]);
+ APPLY_EQUATION(basis.rows[0][2]);
+ APPLY_EQUATION(basis.rows[1][0]);
+ APPLY_EQUATION(basis.rows[1][1]);
+ APPLY_EQUATION(basis.rows[1][2]);
+ APPLY_EQUATION(basis.rows[2][0]);
+ APPLY_EQUATION(basis.rows[2][1]);
+ APPLY_EQUATION(basis.rows[2][2]);
APPLY_EQUATION(origin.x);
APPLY_EQUATION(origin.y);
APPLY_EQUATION(origin.z);
@@ -562,12 +570,12 @@ Variant Tween::calculate_delta_value(Variant p_intial_val, Variant p_final_val)
case Variant::TRANSFORM2D: {
Transform2D i = p_intial_val;
Transform2D f = p_final_val;
- return Transform2D(f.elements[0][0] - i.elements[0][0],
- f.elements[0][1] - i.elements[0][1],
- f.elements[1][0] - i.elements[1][0],
- f.elements[1][1] - i.elements[1][1],
- f.elements[2][0] - i.elements[2][0],
- f.elements[2][1] - i.elements[2][1]);
+ return Transform2D(f.columns[0][0] - i.columns[0][0],
+ f.columns[0][1] - i.columns[0][1],
+ f.columns[1][0] - i.columns[1][0],
+ f.columns[1][1] - i.columns[1][1],
+ f.columns[2][0] - i.columns[2][0],
+ f.columns[2][1] - i.columns[2][1]);
}
case Variant::AABB: {
@@ -579,29 +587,29 @@ Variant Tween::calculate_delta_value(Variant p_intial_val, Variant p_final_val)
case Variant::BASIS: {
Basis i = p_intial_val;
Basis f = p_final_val;
- return Basis(f.elements[0][0] - i.elements[0][0],
- f.elements[0][1] - i.elements[0][1],
- f.elements[0][2] - i.elements[0][2],
- f.elements[1][0] - i.elements[1][0],
- f.elements[1][1] - i.elements[1][1],
- f.elements[1][2] - i.elements[1][2],
- f.elements[2][0] - i.elements[2][0],
- f.elements[2][1] - i.elements[2][1],
- f.elements[2][2] - i.elements[2][2]);
+ return Basis(f.rows[0][0] - i.rows[0][0],
+ f.rows[0][1] - i.rows[0][1],
+ f.rows[0][2] - i.rows[0][2],
+ f.rows[1][0] - i.rows[1][0],
+ f.rows[1][1] - i.rows[1][1],
+ f.rows[1][2] - i.rows[1][2],
+ f.rows[2][0] - i.rows[2][0],
+ f.rows[2][1] - i.rows[2][1],
+ f.rows[2][2] - i.rows[2][2]);
}
case Variant::TRANSFORM3D: {
Transform3D i = p_intial_val;
Transform3D f = p_final_val;
- return Transform3D(f.basis.elements[0][0] - i.basis.elements[0][0],
- f.basis.elements[0][1] - i.basis.elements[0][1],
- f.basis.elements[0][2] - i.basis.elements[0][2],
- f.basis.elements[1][0] - i.basis.elements[1][0],
- f.basis.elements[1][1] - i.basis.elements[1][1],
- f.basis.elements[1][2] - i.basis.elements[1][2],
- f.basis.elements[2][0] - i.basis.elements[2][0],
- f.basis.elements[2][1] - i.basis.elements[2][1],
- f.basis.elements[2][2] - i.basis.elements[2][2],
+ return Transform3D(f.basis.rows[0][0] - i.basis.rows[0][0],
+ f.basis.rows[0][1] - i.basis.rows[0][1],
+ f.basis.rows[0][2] - i.basis.rows[0][2],
+ f.basis.rows[1][0] - i.basis.rows[1][0],
+ f.basis.rows[1][1] - i.basis.rows[1][1],
+ f.basis.rows[1][2] - i.basis.rows[1][2],
+ f.basis.rows[2][0] - i.basis.rows[2][0],
+ f.basis.rows[2][1] - i.basis.rows[2][1],
+ f.basis.rows[2][2] - i.basis.rows[2][2],
f.origin.x - i.origin.x,
f.origin.y - i.origin.y,
f.origin.z - i.origin.z);
@@ -624,6 +632,7 @@ void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("pause"), &Tween::pause);
ClassDB::bind_method(D_METHOD("play"), &Tween::play);
ClassDB::bind_method(D_METHOD("kill"), &Tween::kill);
+ ClassDB::bind_method(D_METHOD("get_total_elapsed_time"), &Tween::get_total_time);
ClassDB::bind_method(D_METHOD("is_running"), &Tween::is_running);
ClassDB::bind_method(D_METHOD("is_valid"), &Tween::is_valid);
@@ -640,7 +649,7 @@ void Tween::_bind_methods() {
ClassDB::bind_method(D_METHOD("parallel"), &Tween::parallel);
ClassDB::bind_method(D_METHOD("chain"), &Tween::chain);
- ClassDB::bind_method(D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &Tween::interpolate_variant);
+ ClassDB::bind_static_method("Tween", D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &Tween::interpolate_variant);
ADD_SIGNAL(MethodInfo("step_finished", PropertyInfo(Variant::INT, "idx")));
ADD_SIGNAL(MethodInfo("loop_finished", PropertyInfo(Variant::INT, "loop_count")));
@@ -671,6 +680,14 @@ void Tween::_bind_methods() {
BIND_ENUM_CONSTANT(EASE_OUT_IN);
}
+Tween::Tween() {
+ ERR_FAIL_MSG("Tween can't be created directly. Use create_tween() method.");
+}
+
+Tween::Tween(bool p_valid) {
+ valid = p_valid;
+}
+
Ref<PropertyTweener> PropertyTweener::from(Variant p_value) {
initial_val = p_value;
do_continue = false;
@@ -741,12 +758,12 @@ bool PropertyTweener::step(float &r_delta) {
}
float time = MIN(elapsed_time - delay, duration);
- target_instance->set_indexed(property, tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type));
-
if (time < duration) {
+ target_instance->set_indexed(property, tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type));
r_delta = 0;
return true;
} else {
+ target_instance->set_indexed(property, final_val);
finished = true;
r_delta = elapsed_time - delay - duration;
emit_signal(SNAME("finished"));
@@ -838,7 +855,7 @@ bool CallbackTweener::step(float &r_delta) {
Callable::CallError ce;
callback.call(nullptr, 0, result, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_call_error_text(this, callback.get_method(), nullptr, 0, ce));
+ ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce));
}
finished = true;
@@ -895,8 +912,13 @@ bool MethodTweener::step(float &r_delta) {
return true;
}
+ Variant current_val;
float time = MIN(elapsed_time - delay, duration);
- Variant current_val = tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type);
+ if (time < duration) {
+ current_val = tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type);
+ } else {
+ current_val = final_val;
+ }
const Variant **argptr = (const Variant **)alloca(sizeof(Variant *));
argptr[0] = &current_val;
@@ -904,7 +926,7 @@ bool MethodTweener::step(float &r_delta) {
Callable::CallError ce;
callback.call(argptr, 1, result, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_call_error_text(this, callback.get_method(), argptr, 1, ce));
+ ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_callable_error_text(callback, argptr, 1, ce));
}
if (time < duration) {
@@ -938,6 +960,7 @@ MethodTweener::MethodTweener(Callable p_callback, Variant p_from, Variant p_to,
callback = p_callback;
initial_val = p_from;
delta_val = tween->calculate_delta_value(p_from, p_to);
+ final_val = p_to;
duration = p_duration;
}
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index 5b0745b2b3..40268405cf 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -103,6 +103,7 @@ private:
ObjectID bound_node;
Vector<List<Ref<Tweener>>> tweeners;
+ float total_time = 0;
int current_step = -1;
int loops = 1;
int loops_done = 0;
@@ -115,6 +116,9 @@ private:
bool valid = false;
bool default_parallel = false;
bool parallel_enabled = false;
+#ifdef DEBUG_ENABLED
+ bool is_infinite = false;
+#endif
typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d);
static interpolater interpolaters[TRANS_MAX][EASE_MAX];
@@ -138,7 +142,6 @@ public:
void kill();
bool is_running();
- void set_valid(bool p_valid);
bool is_valid();
void clear();
@@ -159,15 +162,17 @@ public:
Ref<Tween> parallel();
Ref<Tween> chain();
- real_t run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d);
- Variant interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease);
+ static real_t run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d);
+ static Variant interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease);
Variant calculate_delta_value(Variant p_intial_val, Variant p_final_val);
bool step(float p_delta);
bool can_process(bool p_tree_paused) const;
Node *get_bound_node() const;
+ float get_total_time() const;
- Tween() {}
+ Tween();
+ Tween(bool p_valid);
};
VARIANT_ENUM_CAST(Tween::TweenPauseMode);
@@ -274,6 +279,7 @@ private:
Ref<Tween> tween;
Variant initial_val;
Variant delta_val;
+ Variant final_val;
Callable callback;
};