diff options
Diffstat (limited to 'scene/animation')
-rw-r--r-- | scene/animation/animation_blend_space_2d.cpp | 7 | ||||
-rw-r--r-- | scene/animation/animation_blend_tree.cpp | 79 | ||||
-rw-r--r-- | scene/animation/animation_blend_tree.h | 26 | ||||
-rw-r--r-- | scene/animation/animation_player.cpp | 135 | ||||
-rw-r--r-- | scene/animation/animation_player.h | 2 | ||||
-rw-r--r-- | scene/animation/animation_tree.cpp | 275 | ||||
-rw-r--r-- | scene/animation/animation_tree.h | 4 |
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(); |