summaryrefslogtreecommitdiff
path: root/scene/animation
diff options
context:
space:
mode:
Diffstat (limited to 'scene/animation')
-rw-r--r--scene/animation/animation_blend_space_2d.cpp7
-rw-r--r--scene/animation/animation_blend_tree.cpp79
-rw-r--r--scene/animation/animation_blend_tree.h26
-rw-r--r--scene/animation/animation_player.cpp135
-rw-r--r--scene/animation/animation_player.h2
-rw-r--r--scene/animation/animation_tree.cpp275
-rw-r--r--scene/animation/animation_tree.h4
7 files changed, 380 insertions, 148 deletions
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index b91a2869e4..e621f06ce9 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -30,6 +30,7 @@
#include "animation_blend_space_2d.h"
+#include "animation_blend_tree.h"
#include "core/math/geometry_2d.h"
void AnimationNodeBlendSpace2D::get_parameter_list(List<PropertyInfo> *r_list) const {
@@ -531,6 +532,12 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
if (new_closest != closest && new_closest != -1) {
float from = 0.0;
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && closest != -1) {
+ //for ping-pong loop
+ Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[closest].node);
+ Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node);
+ if (!na_c.is_null() && !na_n.is_null()) {
+ na_n->set_backward(na_c->is_backward());
+ }
//see how much animation remains
from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, 0.0, FILTER_IGNORE, false);
}
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index d18e1a92d8..d6c5d0b51c 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -30,6 +30,7 @@
#include "animation_blend_tree.h"
+#include "scene/resources/animation.h"
#include "scene/scene_string_names.h"
void AnimationNodeAnimation::set_animation(const StringName &p_name) {
@@ -83,30 +84,55 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek) {
}
Ref<Animation> anim = ap->get_animation(animation);
-
- double step;
+ double anim_size = (double)anim->get_length();
+ double step = 0.0;
+ double prev_time = time;
+ int pingponged = 0;
+ bool current_backward = signbit(p_time);
if (p_seek) {
+ step = p_time - time;
time = p_time;
- step = 0;
} else {
- time = MAX(0, time + p_time);
- step = p_time;
+ p_time *= backward ? -1.0 : 1.0;
+ if (!(time == anim_size && !current_backward) && !(time == 0 && current_backward)) {
+ time = time + p_time;
+ step = p_time;
+ }
}
- double anim_size = anim->get_length();
-
- if (anim->has_loop()) {
+ if (anim->get_loop_mode() == Animation::LoopMode::LOOP_PINGPONG) {
if (anim_size) {
- time = Math::fposmod(time, anim_size);
+ if ((int)Math::floor(abs(time - prev_time) / anim_size) % 2 == 0) {
+ if (prev_time > 0 && time <= 0) {
+ backward = !backward;
+ pingponged = -1;
+ }
+ if (prev_time < anim_size && time >= anim_size) {
+ backward = !backward;
+ pingponged = 1;
+ }
+ }
+ time = Math::pingpong(time, anim_size);
}
-
- } else if (time > anim_size) {
- time = anim_size;
+ } else {
+ if (anim->get_loop_mode() == Animation::LoopMode::LOOP_LINEAR) {
+ if (anim_size) {
+ time = Math::fposmod(time, anim_size);
+ }
+ } else if (time < 0) {
+ time = 0;
+ } else if (time > anim_size) {
+ time = anim_size;
+ }
+ backward = false;
}
- blend_animation(animation, time, step, p_seek, 1.0);
-
+ if (play_mode == PLAY_MODE_FORWARD) {
+ blend_animation(animation, time, step, p_seek, 1.0, pingponged);
+ } else {
+ blend_animation(animation, anim_size - time, -step, p_seek, 1.0, pingponged);
+ }
set_parameter(this->time, time);
return anim_size - time;
@@ -116,11 +142,34 @@ String AnimationNodeAnimation::get_caption() const {
return "Animation";
}
+void AnimationNodeAnimation::set_play_mode(PlayMode p_play_mode) {
+ play_mode = p_play_mode;
+}
+
+AnimationNodeAnimation::PlayMode AnimationNodeAnimation::get_play_mode() const {
+ return play_mode;
+}
+
+void AnimationNodeAnimation::set_backward(bool p_backward) {
+ backward = p_backward;
+}
+
+bool AnimationNodeAnimation::is_backward() const {
+ return backward;
+}
+
void AnimationNodeAnimation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_animation", "name"), &AnimationNodeAnimation::set_animation);
ClassDB::bind_method(D_METHOD("get_animation"), &AnimationNodeAnimation::get_animation);
+ ClassDB::bind_method(D_METHOD("set_play_mode", "mode"), &AnimationNodeAnimation::set_play_mode);
+ ClassDB::bind_method(D_METHOD("get_play_mode"), &AnimationNodeAnimation::get_play_mode);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "play_mode", PROPERTY_HINT_ENUM, "Forward,Backward"), "set_play_mode", "get_play_mode");
+
+ BIND_ENUM_CONSTANT(PLAY_MODE_FORWARD);
+ BIND_ENUM_CONSTANT(PLAY_MODE_BACKWARD);
}
AnimationNodeAnimation::AnimationNodeAnimation() {
@@ -533,7 +582,7 @@ AnimationNodeBlend3::AnimationNodeBlend3() {
/////////////////////////////////
void AnimationNodeTimeScale::get_parameter_list(List<PropertyInfo> *r_list) const {
- r_list->push_back(PropertyInfo(Variant::FLOAT, scale, PROPERTY_HINT_RANGE, "0,32,0.01,or_greater"));
+ r_list->push_back(PropertyInfo(Variant::FLOAT, scale, PROPERTY_HINT_RANGE, "-32,32,0.01,or_lesser,or_greater"));
}
Variant AnimationNodeTimeScale::get_parameter_default_value(const StringName &p_parameter) const {
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index 258443a999..e55dfb58ed 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -42,12 +42,12 @@ class AnimationNodeAnimation : public AnimationRootNode {
uint64_t last_version = 0;
bool skip = false;
-protected:
- void _validate_property(PropertyInfo &property) const override;
-
- static void _bind_methods();
-
public:
+ enum PlayMode {
+ PLAY_MODE_FORWARD,
+ PLAY_MODE_BACKWARD
+ };
+
void get_parameter_list(List<PropertyInfo> *r_list) const override;
static Vector<String> (*get_editable_animation_list)();
@@ -58,9 +58,25 @@ public:
void set_animation(const StringName &p_name);
StringName get_animation() const;
+ void set_play_mode(PlayMode p_play_mode);
+ PlayMode get_play_mode() const;
+
+ void set_backward(bool p_backward);
+ bool is_backward() const;
+
AnimationNodeAnimation();
+
+protected:
+ void _validate_property(PropertyInfo &property) const override;
+ static void _bind_methods();
+
+private:
+ PlayMode play_mode = PLAY_MODE_FORWARD;
+ bool backward = false;
};
+VARIANT_ENUM_CAST(AnimationNodeAnimation::PlayMode)
+
class AnimationNodeOneShot : public AnimationNode {
GDCLASS(AnimationNodeOneShot, AnimationNode);
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index f91422ac1a..93339711bd 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -398,12 +398,13 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
}
}
-void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started) {
+void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started, int p_pingponged) {
_ensure_node_caches(p_anim);
ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count());
Animation *a = p_anim->animation.operator->();
bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint();
+ bool backward = signbit(p_delta);
for (int i = 0; i < a->get_track_count(); i++) {
// If an animation changes this animation (or it animates itself)
@@ -557,8 +558,8 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
continue; //eeh not worth it
}
- float first_key_time = a->track_get_key_time(i, 0);
- float transition = 1.0;
+ double first_key_time = a->track_get_key_time(i, 0);
+ double transition = 1.0;
int first_key = 0;
if (first_key_time == 0.0) {
@@ -566,13 +567,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (key_count == 1) {
continue; //with one key we can't do anything
}
- transition = a->track_get_key_transition(i, 0);
+ transition = (double)a->track_get_key_transition(i, 0);
first_key_time = a->track_get_key_time(i, 1);
first_key = 1;
}
if (p_time < first_key_time) {
- float c = Math::ease(p_time / first_key_time, transition);
+ double c = Math::ease(p_time / first_key_time, transition);
Variant first_value = a->track_get_key_value(i, first_key);
Variant interp_value;
Variant::interpolate(pa->capture, first_value, c, interp_value);
@@ -614,7 +615,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else if (p_is_current && p_delta != 0) {
List<int> indices;
- a->value_track_get_key_indices(i, p_time, p_delta, &indices);
+ a->value_track_get_key_indices(i, p_time, p_delta, &indices, p_pingponged);
for (int &F : indices) {
Variant value = a->track_get_key_value(i, F);
@@ -673,7 +674,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
List<int> indices;
- a->method_track_get_key_indices(i, p_time, p_delta, &indices);
+ a->method_track_get_key_indices(i, p_time, p_delta, &indices, p_pingponged);
for (int &E : indices) {
StringName method = a->method_track_get_name(i, E);
@@ -728,7 +729,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
TrackNodeCache::BezierAnim *ba = &E->get();
- float bezier = a->bezier_track_interpolate(i, p_time);
+ real_t bezier = a->bezier_track_interpolate(i, p_time);
if (ba->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_bezier_size >= NODE_CACHE_UPDATE_MAX);
cache_update_bezier[cache_update_bezier_size++] = ba;
@@ -789,7 +790,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play);
+ a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_pingponged);
if (to_play.size()) {
int idx = to_play.back()->get();
@@ -817,12 +818,14 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
nc->audio_start = p_time;
}
} else if (nc->audio_playing) {
- bool loop = a->has_loop();
+ bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
bool stop = false;
- if (!loop && p_time < nc->audio_start) {
- stop = true;
+ if (!loop) {
+ if ((p_time < nc->audio_start && !backward) || (p_time > nc->audio_start && backward)) {
+ stop = true;
+ }
} else if (nc->audio_len > 0) {
float len = nc->audio_start > p_time ? (a->get_length() - nc->audio_start) + p_time : p_time - nc->audio_start;
@@ -863,12 +866,23 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
Ref<Animation> anim = player->get_animation(anim_name);
- double at_anim_pos;
+ double at_anim_pos = 0.0;
- if (anim->has_loop()) {
- at_anim_pos = Math::fposmod(p_time - pos, (double)anim->get_length()); //seek to loop
- } else {
- at_anim_pos = MIN((double)anim->get_length(), p_time - pos); //seek to end
+ switch (anim->get_loop_mode()) {
+ case Animation::LoopMode::LOOP_NONE: {
+ at_anim_pos = MIN((double)anim->get_length(), p_time - pos); //seek to end
+ } break;
+
+ case Animation::LoopMode::LOOP_LINEAR: {
+ at_anim_pos = Math::fposmod(p_time - pos, (double)anim->get_length()); //seek to loop
+ } break;
+
+ case Animation::LoopMode::LOOP_PINGPONG: {
+ at_anim_pos = Math::pingpong(p_time - pos, (double)anim->get_length());
+ } break;
+
+ default:
+ break;
}
if (player->is_playing() || p_seeked) {
@@ -883,7 +897,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play);
+ a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_pingponged);
if (to_play.size()) {
int idx = to_play.back()->get();
@@ -913,46 +927,73 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
double next_pos = cd.pos + delta;
real_t len = cd.from->animation->get_length();
- bool loop = cd.from->animation->has_loop();
+ int pingponged = 0;
+
+ switch (cd.from->animation->get_loop_mode()) {
+ case Animation::LoopMode::LOOP_NONE: {
+ if (next_pos < 0) {
+ next_pos = 0;
+ } else if (next_pos > len) {
+ next_pos = len;
+ }
- if (!loop) {
- if (next_pos < 0) {
- next_pos = 0;
- } else if (next_pos > len) {
- next_pos = len;
- }
+ bool backwards = signbit(delta); // Negative zero means playing backwards too
+ delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here)
- bool backwards = signbit(delta); // Negative zero means playing backwards too
- delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here)
+ if (&cd == &playback.current) {
+ if (!backwards && cd.pos <= len && next_pos == len) {
+ //playback finished
+ end_reached = true;
+ end_notify = cd.pos < len; // Notify only if not already at the end
+ }
- if (&cd == &playback.current) {
- if (!backwards && cd.pos <= len && next_pos == len) {
- //playback finished
- end_reached = true;
- end_notify = cd.pos < len; // Notify only if not already at the end
+ if (backwards && cd.pos >= 0 && next_pos == 0) {
+ //playback finished
+ end_reached = true;
+ end_notify = cd.pos > 0; // Notify only if not already at the beginning
+ }
}
+ } break;
- if (backwards && cd.pos >= 0 && next_pos == 0) {
- //playback finished
- end_reached = true;
- end_notify = cd.pos > 0; // Notify only if not already at the beginning
+ case Animation::LoopMode::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
+ // so state at time=length is previewable in the editor
+ next_pos = len;
+ } else {
+ next_pos = looped_next_pos;
}
- }
+ } break;
- } else {
- 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
- // so state at time=length is previewable in the editor
- next_pos = len;
- } else {
- next_pos = looped_next_pos;
- }
+ case Animation::LoopMode::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;
+ pingponged = -1;
+ }
+ if (next_pos > len && cd.pos <= len) {
+ cd.speed_scale *= -1.0;
+ pingponged = 1;
+ }
+ }
+ double looped_next_pos = Math::pingpong(next_pos, (double)len);
+ if (looped_next_pos == 0 && next_pos != 0) {
+ // Loop multiples of the length to it, rather than 0
+ // so state at time=length is previewable in the editor
+ next_pos = len;
+ } else {
+ next_pos = looped_next_pos;
+ }
+ } break;
+
+ default:
+ break;
}
cd.pos = next_pos;
- _animation_process_animation(cd.from, cd.pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started);
+ _animation_process_animation(cd.from, cd.pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started, pingponged);
}
void AnimationPlayer::_animation_process2(double p_delta, bool p_started) {
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index d9d88b5510..ea04918988 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -231,7 +231,7 @@ private:
NodePath root;
- void _animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current = true, bool p_seeked = false, bool p_started = false);
+ void _animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current = true, bool p_seeked = false, bool p_started = false, int p_pingponged = 0);
void _ensure_node_caches(AnimationData *p_anim, Node *p_root_override = nullptr);
void _animation_process_data(PlaybackData &cd, double p_delta, float p_blend, bool p_seeked, bool p_started);
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 4b71fe7a01..37e754148c 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -32,6 +32,7 @@
#include "animation_blend_tree.h"
#include "core/config/engine.h"
+#include "scene/resources/animation.h"
#include "scene/scene_string_names.h"
#include "servers/audio/audio_stream.h"
@@ -87,7 +88,7 @@ void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {
}
}
-void AnimationNode::blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend) {
+void AnimationNode::blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend, int p_pingponged) {
ERR_FAIL_COND(!state);
ERR_FAIL_COND(!state->player->has_animation(p_animation));
@@ -113,6 +114,7 @@ void AnimationNode::blend_animation(const StringName &p_animation, real_t p_time
anim_state.time = p_time;
anim_state.animation = animation;
anim_state.seeked = p_seeked;
+ anim_state.pingponged = p_pingponged;
state->animation_states.push_back(anim_state);
}
@@ -418,7 +420,7 @@ 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"), &AnimationNode::blend_animation);
+ 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));
@@ -898,6 +900,10 @@ void AnimationTree::_process_graph(real_t p_delta) {
double delta = as.delta;
real_t weight = as.blend;
bool seeked = as.seeked;
+ int pingponged = as.pingponged;
+#ifndef _3D_DISABLED
+ bool backward = signbit(delta);
+#endif // _3D_DISABLED
for (int i = 0; i < a->get_track_count(); i++) {
NodePath path = a->track_get_path(i);
@@ -939,27 +945,63 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
if (track->root_motion) {
- real_t prev_time = time - delta;
- if (prev_time < 0) {
- if (!a->has_loop()) {
- prev_time = 0;
- } else {
- prev_time = a->get_length() + prev_time;
+ double prev_time = time - delta;
+ if (!backward) {
+ if (prev_time < 0) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = 0;
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
+ }
+ } else {
+ if (prev_time > a->get_length()) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = (double)a->get_length();
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
}
}
Vector3 loc[2];
- if (prev_time > time) {
- Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
- if (err != OK) {
- continue;
+ if (!backward) {
+ if (prev_time > time) {
+ Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->position_track_interpolate(i, (double)a->get_length(), &loc[1]);
+ t->loc += (loc[1] - loc[0]) * blend;
+ prev_time = 0;
+ }
+ } else {
+ if (prev_time < time) {
+ Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->position_track_interpolate(i, 0, &loc[1]);
+ t->loc += (loc[1] - loc[0]) * blend;
+ prev_time = 0;
}
-
- a->position_track_interpolate(i, a->get_length(), &loc[1]);
-
- t->loc += (loc[1] - loc[0]) * blend;
- prev_time = 0;
}
Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
@@ -968,17 +1010,13 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
a->position_track_interpolate(i, time, &loc[1]);
-
t->loc += (loc[1] - loc[0]) * blend;
-
- prev_time = 0;
+ prev_time = !backward ? 0 : (double)a->get_length();
} else {
Vector3 loc;
Error err = a->position_track_interpolate(i, time, &loc);
- //ERR_CONTINUE(err!=OK); //used for testing, should be removed
-
if (err != OK) {
continue;
}
@@ -1000,29 +1038,65 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
if (track->root_motion) {
- real_t prev_time = time - delta;
- if (prev_time < 0) {
- if (!a->has_loop()) {
- prev_time = 0;
- } else {
- prev_time = a->get_length() + prev_time;
+ double prev_time = time - delta;
+ if (!backward) {
+ if (prev_time < 0) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = 0;
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
+ }
+ } else {
+ if (prev_time > a->get_length()) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = (double)a->get_length();
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
}
}
Quaternion rot[2];
- if (prev_time > time) {
- Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
- if (err != OK) {
- continue;
+ if (!backward) {
+ if (prev_time > time) {
+ Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->rotation_track_interpolate(i, (double)a->get_length(), &rot[1]);
+ Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
+ t->rot = (t->rot * q).normalized();
+ prev_time = 0;
+ }
+ } else {
+ if (prev_time < time) {
+ Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->rotation_track_interpolate(i, 0, &rot[1]);
+ Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
+ t->rot = (t->rot * q).normalized();
+ prev_time = 0;
}
-
- a->rotation_track_interpolate(i, a->get_length(), &rot[1]);
-
- Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
- t->rot = (t->rot * q).normalized();
-
- prev_time = 0;
}
Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
@@ -1031,18 +1105,14 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
a->rotation_track_interpolate(i, time, &rot[1]);
-
Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
t->rot = (t->rot * q).normalized();
-
- prev_time = 0;
+ prev_time = !backward ? 0 : (double)a->get_length();
} else {
Quaternion rot;
Error err = a->rotation_track_interpolate(i, time, &rot);
- //ERR_CONTINUE(err!=OK); //used for testing, should be removed
-
if (err != OK) {
continue;
}
@@ -1071,28 +1141,63 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
if (track->root_motion) {
- real_t prev_time = time - delta;
- if (prev_time < 0) {
- if (!a->has_loop()) {
- prev_time = 0;
- } else {
- prev_time = a->get_length() + prev_time;
+ double prev_time = time - delta;
+ if (!backward) {
+ if (prev_time < 0) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = 0;
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
+ }
+ } else {
+ if (prev_time > a->get_length()) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = (double)a->get_length();
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
}
}
Vector3 scale[2];
- if (prev_time > time) {
- Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
- if (err != OK) {
- continue;
+ if (!backward) {
+ if (prev_time > time) {
+ Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->scale_track_interpolate(i, (double)a->get_length(), &scale[1]);
+ t->scale += (scale[1] - scale[0]) * blend;
+ prev_time = 0;
+ }
+ } else {
+ if (prev_time < time) {
+ Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->scale_track_interpolate(i, 0, &scale[1]);
+ t->scale += (scale[1] - scale[0]) * blend;
+ prev_time = 0;
}
-
- a->scale_track_interpolate(i, a->get_length(), &scale[1]);
-
- t->scale += (scale[1] - scale[0]) * blend;
-
- prev_time = 0;
}
Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
@@ -1101,17 +1206,13 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
a->scale_track_interpolate(i, time, &scale[1]);
-
t->scale += (scale[1] - scale[0]) * blend;
-
- prev_time = 0;
+ prev_time = !backward ? 0 : (double)a->get_length();
} else {
Vector3 scale;
Error err = a->scale_track_interpolate(i, time, &scale);
- //ERR_CONTINUE(err!=OK); //used for testing, should be removed
-
if (err != OK) {
continue;
}
@@ -1164,7 +1265,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
} else {
List<int> indices;
- a->value_track_get_key_indices(i, time, delta, &indices);
+ a->value_track_get_key_indices(i, time, delta, &indices, pingponged);
for (int &F : indices) {
Variant value = a->track_get_key_value(i, F);
@@ -1181,7 +1282,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
List<int> indices;
- a->method_track_get_key_indices(i, time, delta, &indices);
+ a->method_track_get_key_indices(i, time, delta, &indices, pingponged);
for (int &F : indices) {
StringName method = a->method_track_get_name(i, F);
@@ -1264,7 +1365,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, time, delta, &to_play);
+ a->track_get_key_indices_in_range(i, time, delta, &to_play, pingponged);
if (to_play.size()) {
int idx = to_play.back()->get();
@@ -1292,12 +1393,20 @@ void AnimationTree::_process_graph(real_t p_delta) {
t->start = time;
}
} else if (t->playing) {
- bool loop = a->has_loop();
+ bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
bool stop = false;
- if (!loop && time < t->start) {
- stop = true;
+ if (!loop) {
+ if (delta > 0) {
+ if (time < t->start) {
+ stop = true;
+ }
+ } else if (delta < 0) {
+ if (time > t->start) {
+ stop = true;
+ }
+ }
} else if (t->len > 0) {
real_t len = t->start > time ? (a->get_length() - t->start) + time : time - t->start;
@@ -1331,7 +1440,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
continue;
}
- if (delta == 0 || seeked) {
+ if (seeked) {
//seek
int idx = a->track_find_key(i, time);
if (idx < 0) {
@@ -1347,12 +1456,20 @@ void AnimationTree::_process_graph(real_t p_delta) {
Ref<Animation> anim = player2->get_animation(anim_name);
- real_t at_anim_pos;
-
- if (anim->has_loop()) {
- at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); //seek to loop
- } else {
- at_anim_pos = MAX(anim->get_length(), time - pos); //seek to end
+ real_t at_anim_pos = 0.0;
+
+ switch (anim->get_loop_mode()) {
+ case Animation::LoopMode::LOOP_NONE: {
+ at_anim_pos = MAX((double)anim->get_length(), time - pos); //seek to end
+ } break;
+ case Animation::LoopMode::LOOP_LINEAR: {
+ at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); //seek to loop
+ } break;
+ case Animation::LoopMode::LOOP_PINGPONG: {
+ at_anim_pos = Math::pingpong(time - pos, (double)a->get_length());
+ } break;
+ default:
+ break;
}
if (player2->is_playing() || seeked) {
@@ -1367,7 +1484,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, time, delta, &to_play);
+ a->track_get_key_indices_in_range(i, time, delta, &to_play, pingponged);
if (to_play.size()) {
int idx = to_play.back()->get();
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 5abea39d20..6fc051fa41 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -66,6 +66,7 @@ public:
const Vector<real_t> *track_blends = nullptr;
real_t blend = 0.0;
bool seeked = false;
+ int pingponged = 0;
};
struct State {
@@ -98,9 +99,10 @@ public:
real_t _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, 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, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend);
+ void blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend, int p_pingponged = 0);
real_t blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
real_t blend_input(int p_input, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
+
void make_invalid(const String &p_reason);
static void _bind_methods();