diff options
Diffstat (limited to 'scene/animation')
-rw-r--r-- | scene/animation/animation_blend_space_1d.cpp | 10 | ||||
-rw-r--r-- | scene/animation/animation_blend_space_1d.h | 2 | ||||
-rw-r--r-- | scene/animation/animation_blend_space_2d.cpp | 22 | ||||
-rw-r--r-- | scene/animation/animation_blend_space_2d.h | 2 | ||||
-rw-r--r-- | scene/animation/animation_blend_tree.cpp | 145 | ||||
-rw-r--r-- | scene/animation/animation_blend_tree.h | 24 | ||||
-rw-r--r-- | scene/animation/animation_node_state_machine.cpp | 47 | ||||
-rw-r--r-- | scene/animation/animation_node_state_machine.h | 8 | ||||
-rw-r--r-- | scene/animation/animation_player.cpp | 210 | ||||
-rw-r--r-- | scene/animation/animation_player.h | 6 | ||||
-rw-r--r-- | scene/animation/animation_tree.cpp | 305 | ||||
-rw-r--r-- | scene/animation/animation_tree.h | 26 | ||||
-rw-r--r-- | scene/animation/root_motion_view.cpp | 8 |
13 files changed, 407 insertions, 408 deletions
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index f30aea3bdd..4b325ee464 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -230,14 +230,14 @@ void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref<Animatio } } -double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_seek_root) { +double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_is_external_seeking) { 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, p_seek_root, 1.0, FILTER_IGNORE, true); + return blend_node(blend_points[0].name, blend_points[0].node, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true); } double blend_pos = get_parameter(blend_position); @@ -307,10 +307,10 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_see for (int i = 0; i < blend_points_used; i++) { if (i == point_lower || i == point_higher) { - double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, weights[i], FILTER_IGNORE, true); + double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, weights[i], FILTER_IGNORE, true); max_time_remaining = MAX(max_time_remaining, remaining); - } else { - blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync); + } else if (sync) { + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); } } diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index 1876ccebc7..30cfe52c8e 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -98,7 +98,7 @@ public: void set_use_sync(bool p_sync); bool is_using_sync() const; - double process(double p_time, bool p_seek, bool p_seek_root) override; + double process(double p_time, bool p_seek, bool p_is_external_seeking) 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 0d8b7ba8f7..4e20429ac9 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, bool p_seek_root) { +double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_is_external_seeking) { _update_triangles(); Vector2 blend_pos = get_parameter(blend_position); @@ -502,7 +502,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_see 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, p_seek_root, blend_weights[j], FILTER_IGNORE, true); + double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, blend_weights[j], FILTER_IGNORE, true); if (first || t < mind) { mind = t; first = false; @@ -512,8 +512,8 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_see } } - if (!found) { - blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync); + if (sync && !found) { + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); } } } else { @@ -538,21 +538,23 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_see na_n->set_backward(na_c->is_backward()); } //see how much animation remains - from = cur_length_internal - blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, false, p_seek_root, 0.0, FILTER_IGNORE, true); + from = cur_length_internal - blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, false, p_is_external_seeking, 0.0, FILTER_IGNORE, true); } - mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_seek_root, 1.0, FILTER_IGNORE, true); + mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true); cur_length_internal = from + mind; cur_closest = new_closest; } else { - mind = blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true); + mind = blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true); } - for (int i = 0; i < blend_points_used; i++) { - if (i != cur_closest) { - blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync); + if (sync) { + for (int i = 0; i < blend_points_used; i++) { + if (i != cur_closest) { + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); + } } } } diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h index 250189f202..41854f73a4 100644 --- a/scene/animation/animation_blend_space_2d.h +++ b/scene/animation/animation_blend_space_2d.h @@ -128,7 +128,7 @@ public: void set_y_label(const String &p_label); String get_y_label() const; - virtual double process(double p_time, bool p_seek, bool p_seek_root) override; + virtual double process(double p_time, bool p_seek, bool p_is_external_seeking) 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 0c91729a6f..6200062f60 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -64,7 +64,7 @@ void AnimationNodeAnimation::_validate_property(PropertyInfo &p_property) const } } -double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_seek_root) { +double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_is_external_seeking) { AnimationPlayer *ap = state->player; ERR_FAIL_COND_V(!ap, 0); @@ -87,40 +87,43 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_seek_r double anim_size = (double)anim->get_length(); double step = 0.0; double prev_time = cur_time; - int pingponged = 0; - bool current_backward = signbit(p_time); + Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE; + bool node_backward = play_mode == PLAY_MODE_BACKWARD; if (p_seek) { step = p_time - cur_time; cur_time = p_time; } else { p_time *= backward ? -1.0 : 1.0; - if (!(cur_time == anim_size && !current_backward) && !(cur_time == 0 && current_backward)) { - cur_time = cur_time + p_time; - step = p_time; - } + cur_time = cur_time + p_time; + step = p_time; } if (anim->get_loop_mode() == Animation::LOOP_PINGPONG) { if (!Math::is_zero_approx(anim_size)) { - if ((int)Math::floor(abs(cur_time - prev_time) / anim_size) % 2 == 0) { - if (prev_time >= 0 && cur_time < 0) { - backward = !backward; - pingponged = -1; - } - if (prev_time <= anim_size && cur_time > anim_size) { - backward = !backward; - pingponged = 1; - } + if (prev_time >= 0 && cur_time < 0) { + backward = !backward; + looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START; + } + if (prev_time <= anim_size && cur_time > anim_size) { + backward = !backward; + looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END; } cur_time = Math::pingpong(cur_time, anim_size); } - } else { - if (anim->get_loop_mode() == Animation::LOOP_LINEAR) { - if (!Math::is_zero_approx(anim_size)) { - cur_time = Math::fposmod(cur_time, anim_size); + } else if (anim->get_loop_mode() == Animation::LOOP_LINEAR) { + if (!Math::is_zero_approx(anim_size)) { + if (prev_time >= 0 && cur_time < 0) { + looped_flag = node_backward ? Animation::LOOPED_FLAG_END : Animation::LOOPED_FLAG_START; + } + if (prev_time <= anim_size && cur_time > anim_size) { + looped_flag = node_backward ? Animation::LOOPED_FLAG_START : Animation::LOOPED_FLAG_END; } - } else if (cur_time < 0) { + cur_time = Math::fposmod(cur_time, anim_size); + } + backward = false; + } else { + if (cur_time < 0) { step += cur_time; cur_time = 0; } else if (cur_time > anim_size) { @@ -128,12 +131,25 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_seek_r cur_time = anim_size; } backward = false; + + // If ended, don't progress animation. So set delta to 0. + if (p_time > 0) { + if (play_mode == PLAY_MODE_FORWARD) { + if (prev_time >= anim_size) { + step = 0; + } + } else { + if (prev_time <= 0) { + step = 0; + } + } + } } if (play_mode == PLAY_MODE_FORWARD) { - blend_animation(animation, cur_time, step, p_seek, p_seek_root, 1.0, pingponged); + blend_animation(animation, cur_time, step, p_seek, p_is_external_seeking, 1.0, looped_flag); } else { - blend_animation(animation, anim_size - cur_time, -step, p_seek, p_seek_root, 1.0, pingponged); + blend_animation(animation, anim_size - cur_time, -step, p_seek, p_is_external_seeking, 1.0, looped_flag); } set_parameter(time, cur_time); @@ -273,7 +289,7 @@ bool AnimationNodeOneShot::has_filter() const { return true; } -double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_seek_root) { +double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_is_external_seeking) { bool cur_active = get_parameter(active); bool cur_prev_active = get_parameter(prev_active); double cur_time = get_parameter(time); @@ -295,9 +311,7 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_seek_roo set_parameter(time_to_restart, cur_time_to_restart); } - if (!cur_active) { - return blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, sync); - } + return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync); } bool os_seek = p_seek; @@ -333,12 +347,11 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_seek_roo double main_rem; if (mix == MIX_MODE_ADD) { - main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, sync); + main_rem = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync); } else { - main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_BLEND, sync); + main_rem = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0 - blend, FILTER_BLEND, sync); // Unlike below, processing this edge is a corner case. } - - double os_rem = blend_input(1, os_seek ? cur_time : p_time, os_seek, p_seek_root, blend, FILTER_PASS, true); + double os_rem = blend_input(1, os_seek ? cur_time : p_time, os_seek, p_is_external_seeking, MAX(CMP_EPSILON, blend), FILTER_PASS, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. if (do_start) { cur_remaining = os_rem; @@ -420,10 +433,10 @@ bool AnimationNodeAdd2::has_filter() const { return true; } -double AnimationNodeAdd2::process(double p_time, bool p_seek, bool p_seek_root) { +double AnimationNodeAdd2::process(double p_time, bool p_seek, bool p_is_external_seeking) { double amount = get_parameter(add_amount); - 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); + double rem0 = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync); + blend_input(1, p_time, p_seek, p_is_external_seeking, amount, FILTER_PASS, sync); return rem0; } @@ -454,11 +467,11 @@ bool AnimationNodeAdd3::has_filter() const { return true; } -double AnimationNodeAdd3::process(double p_time, bool p_seek, bool p_seek_root) { +double AnimationNodeAdd3::process(double p_time, bool p_seek, bool p_is_external_seeking) { double amount = get_parameter(add_amount); - 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); + blend_input(0, p_time, p_seek, p_is_external_seeking, MAX(0, -amount), FILTER_PASS, sync); + double rem0 = blend_input(1, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, sync); + blend_input(2, p_time, p_seek, p_is_external_seeking, MAX(0, amount), FILTER_PASS, sync); return rem0; } @@ -486,11 +499,11 @@ String AnimationNodeBlend2::get_caption() const { return "Blend2"; } -double AnimationNodeBlend2::process(double p_time, bool p_seek, bool p_seek_root) { +double AnimationNodeBlend2::process(double p_time, bool p_seek, bool p_is_external_seeking) { double amount = get_parameter(blend_amount); - 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); + double rem0 = blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0 - amount, FILTER_BLEND, sync); + double rem1 = blend_input(1, p_time, p_seek, p_is_external_seeking, amount, FILTER_PASS, sync); return amount > 0.5 ? rem1 : rem0; //hacky but good enough } @@ -521,11 +534,11 @@ String AnimationNodeBlend3::get_caption() const { return "Blend3"; } -double AnimationNodeBlend3::process(double p_time, bool p_seek, bool p_seek_root) { +double AnimationNodeBlend3::process(double p_time, bool p_seek, bool p_is_external_seeking) { double amount = get_parameter(blend_amount); - 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); + double rem0 = blend_input(0, p_time, p_seek, p_is_external_seeking, MAX(0, -amount), FILTER_IGNORE, sync); + double rem1 = blend_input(1, p_time, p_seek, p_is_external_seeking, 1.0 - ABS(amount), FILTER_IGNORE, sync); + double rem2 = blend_input(2, p_time, p_seek, p_is_external_seeking, MAX(0, amount), FILTER_IGNORE, sync); return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); //hacky but good enough } @@ -553,12 +566,12 @@ String AnimationNodeTimeScale::get_caption() const { return "TimeScale"; } -double AnimationNodeTimeScale::process(double p_time, bool p_seek, bool p_seek_root) { +double AnimationNodeTimeScale::process(double p_time, bool p_seek, bool p_is_external_seeking) { double cur_scale = get_parameter(scale); if (p_seek) { - return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, true); + return blend_input(0, p_time, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true); } else { - return blend_input(0, p_time * cur_scale, false, p_seek_root, 1.0, FILTER_IGNORE, true); + return blend_input(0, p_time * cur_scale, false, p_is_external_seeking, 1.0, FILTER_IGNORE, true); } } @@ -583,16 +596,16 @@ String AnimationNodeTimeSeek::get_caption() const { return "Seek"; } -double AnimationNodeTimeSeek::process(double p_time, bool p_seek, bool p_seek_root) { +double AnimationNodeTimeSeek::process(double p_time, bool p_seek, bool p_is_external_seeking) { double cur_seek_pos = get_parameter(seek_pos); if (p_seek) { - return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, true); + return blend_input(0, p_time, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true); } else if (cur_seek_pos >= 0) { double ret = blend_input(0, cur_seek_pos, true, true, 1.0, FILTER_IGNORE, true); set_parameter(seek_pos, -1.0); //reset return ret; } else { - return blend_input(0, p_time, false, p_seek_root, 1.0, FILTER_IGNORE, true); + return blend_input(0, p_time, false, p_is_external_seeking, 1.0, FILTER_IGNORE, true); } } @@ -700,7 +713,7 @@ bool AnimationNodeTransition::is_from_start() const { return from_start; } -double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_root) { +double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_is_external_seeking) { int cur_current = get_parameter(current); int cur_prev = get_parameter(prev); int cur_prev_current = get_parameter(prev_current); @@ -726,15 +739,17 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_ double rem = 0.0; - for (int i = 0; i < enabled_inputs; i++) { - if (i != cur_current && i != cur_prev) { - blend_input(i, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, sync); + if (sync) { + for (int i = 0; i < enabled_inputs; i++) { + if (i != cur_current && i != cur_prev) { + blend_input(i, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); + } } } if (cur_prev < 0) { // process current animation, check for transition - rem = blend_input(cur_current, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, true); + rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true); if (p_seek) { cur_time = p_time; @@ -753,18 +768,18 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_ blend = xfade_curve->sample(blend); } + // Blend values must be more than CMP_EPSILON to process discrete keys in edge. if (from_start && !p_seek && switched) { //just switched, seek to start of current - - rem = blend_input(cur_current, 0, true, p_seek_root, 1.0 - blend, FILTER_IGNORE, true); + rem = blend_input(cur_current, 0, true, p_is_external_seeking, MAX(CMP_EPSILON, 1.0 - blend), FILTER_IGNORE, true); } else { - rem = blend_input(cur_current, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_IGNORE, true); + rem = blend_input(cur_current, p_time, p_seek, p_is_external_seeking, MAX(CMP_EPSILON, 1.0 - blend), FILTER_IGNORE, true); } if (p_seek) { - blend_input(cur_prev, p_time, true, p_seek_root, blend, FILTER_IGNORE, true); + blend_input(cur_prev, p_time, true, p_is_external_seeking, MAX(CMP_EPSILON, blend), FILTER_IGNORE, true); cur_time = p_time; } else { - blend_input(cur_prev, p_time, false, p_seek_root, blend, FILTER_IGNORE, true); + blend_input(cur_prev, p_time, false, p_is_external_seeking, MAX(CMP_EPSILON, blend), FILTER_IGNORE, true); cur_time += p_time; cur_prev_xfading -= p_time; if (cur_prev_xfading < 0) { @@ -833,8 +848,8 @@ String AnimationNodeOutput::get_caption() const { return "Output"; } -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, FILTER_IGNORE, true); +double AnimationNodeOutput::process(double p_time, bool p_seek, bool p_is_external_seeking) { + return blend_input(0, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true); } AnimationNodeOutput::AnimationNodeOutput() { @@ -1046,9 +1061,9 @@ String AnimationNodeBlendTree::get_caption() const { return "BlendTree"; } -double AnimationNodeBlendTree::process(double p_time, bool p_seek, bool p_seek_root) { +double AnimationNodeBlendTree::process(double p_time, bool p_seek, bool p_is_external_seeking) { 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, p_seek_root, 1.0, FILTER_IGNORE, true); + return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true); } 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 1c31718259..52bf67b8f5 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, bool p_seek_root) override; + virtual double process(double p_time, bool p_seek, bool p_is_external_seeking) override; void set_animation(const StringName &p_name); StringName get_animation() const; @@ -72,7 +72,7 @@ protected: private: PlayMode play_mode = PLAY_MODE_FORWARD; - bool backward = false; + bool backward = false; // Only used by pingpong animation. }; VARIANT_ENUM_CAST(AnimationNodeAnimation::PlayMode) @@ -148,7 +148,7 @@ public: MixMode get_mix_mode() const; virtual bool has_filter() const override; - virtual double process(double p_time, bool p_seek, bool p_seek_root) override; + virtual double process(double p_time, bool p_seek, bool p_is_external_seeking) override; AnimationNodeOneShot(); }; @@ -170,7 +170,7 @@ public: virtual String get_caption() const override; virtual bool has_filter() const override; - virtual double process(double p_time, bool p_seek, bool p_seek_root) override; + virtual double process(double p_time, bool p_seek, bool p_is_external_seeking) override; AnimationNodeAdd2(); }; @@ -190,7 +190,7 @@ public: virtual String get_caption() const override; virtual bool has_filter() const override; - virtual double process(double p_time, bool p_seek, bool p_seek_root) override; + virtual double process(double p_time, bool p_seek, bool p_is_external_seeking) override; AnimationNodeAdd3(); }; @@ -208,7 +208,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, bool p_seek_root) override; + virtual double process(double p_time, bool p_seek, bool p_is_external_seeking) override; virtual bool has_filter() const override; AnimationNodeBlend2(); @@ -228,7 +228,7 @@ public: virtual String get_caption() const override; - double process(double p_time, bool p_seek, bool p_seek_root) override; + double process(double p_time, bool p_seek, bool p_is_external_seeking) override; AnimationNodeBlend3(); }; @@ -246,7 +246,7 @@ public: virtual String get_caption() const override; - double process(double p_time, bool p_seek, bool p_seek_root) override; + double process(double p_time, bool p_seek, bool p_is_external_seeking) override; AnimationNodeTimeScale(); }; @@ -265,7 +265,7 @@ public: virtual String get_caption() const override; - double process(double p_time, bool p_seek, bool p_seek_root) override; + double process(double p_time, bool p_seek, bool p_is_external_seeking) override; AnimationNodeTimeSeek(); }; @@ -331,7 +331,7 @@ public: void set_from_start(bool p_from_start); bool is_from_start() const; - double process(double p_time, bool p_seek, bool p_seek_root) override; + double process(double p_time, bool p_seek, bool p_is_external_seeking) override; AnimationNodeTransition(); }; @@ -341,7 +341,7 @@ class AnimationNodeOutput : public AnimationNode { public: virtual String get_caption() const override; - virtual double process(double p_time, bool p_seek, bool p_seek_root) override; + virtual double process(double p_time, bool p_seek, bool p_is_external_seeking) override; AnimationNodeOutput(); }; @@ -410,7 +410,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, bool p_seek_root) override; + virtual double process(double p_time, bool p_seek, bool p_is_external_seeking) 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 fa33591e8b..d3746c1144 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -89,14 +89,6 @@ String AnimationNodeStateMachineTransition::get_advance_expression() const { return advance_expression; } -void AnimationNodeStateMachineTransition::set_advance_expression_base_node(const NodePath &p_expression_base_node) { - advance_expression_base_node = p_expression_base_node; -} - -NodePath AnimationNodeStateMachineTransition::get_advance_expression_base_node() const { - return advance_expression_base_node; -} - void AnimationNodeStateMachineTransition::set_xfade_time(float p_xfade) { ERR_FAIL_COND(p_xfade < 0); xfade_time = p_xfade; @@ -158,9 +150,6 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_advance_expression", "text"), &AnimationNodeStateMachineTransition::set_advance_expression); ClassDB::bind_method(D_METHOD("get_advance_expression"), &AnimationNodeStateMachineTransition::get_advance_expression); - ClassDB::bind_method(D_METHOD("set_advance_expression_base_node", "path"), &AnimationNodeStateMachineTransition::set_advance_expression_base_node); - ClassDB::bind_method(D_METHOD("get_advance_expression_base_node"), &AnimationNodeStateMachineTransition::get_advance_expression_base_node); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01,suffix:s"), "set_xfade_time", "get_xfade_time"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve"); ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority"); @@ -170,7 +159,6 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ADD_GROUP("Advance", "advance_"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "advance_condition"), "set_advance_condition", "get_advance_condition"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "advance_expression", PROPERTY_HINT_EXPRESSION, ""), "set_advance_expression", "get_advance_expression"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "advance_expression_base_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node"), "set_advance_expression_base_node", "get_advance_expression_base_node"); ADD_GROUP("Disabling", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); @@ -344,11 +332,11 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta return true; } -double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root) { +double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking) { if (p_time == -1) { Ref<AnimationNodeStateMachine> anodesm = p_state_machine->states[current].node; if (anodesm.is_valid()) { - p_state_machine->blend_node(current, p_state_machine->states[current].node, -1, p_seek, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true); + p_state_machine->blend_node(current, p_state_machine->states[current].node, -1, p_seek, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true); } playing = false; return 0; @@ -417,7 +405,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s current = p_state_machine->start_node; } - len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 1.0, AnimationNode::FILTER_IGNORE, true); + len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, 1.0, AnimationNode::FILTER_IGNORE, true); pos_current = 0; } @@ -445,10 +433,10 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s if (current_curve.is_valid()) { fade_blend = current_curve->sample(fade_blend); } - 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, true); + float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_is_external_seeking, MAX(CMP_EPSILON, fade_blend), AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. if (fading_from != StringName()) { - 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, true); + p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_is_external_seeking, MAX(CMP_EPSILON, 1.0 - fade_blend), AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge. } //guess playback position @@ -605,19 +593,17 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s { // if the current node is a state machine, update the "playing" variable to false by passing -1 in p_time Ref<AnimationNodeStateMachine> anodesm = p_state_machine->states[current].node; if (anodesm.is_valid()) { - p_state_machine->blend_node(current, p_state_machine->states[current].node, -1, p_seek, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true); + p_state_machine->blend_node(current, p_state_machine->states[current].node, -1, p_seek, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true); } } current = next; + len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_is_external_seeking, CMP_EPSILON, AnimationNode::FILTER_IGNORE, true); // Process next node's first key in here. if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) { - len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true); pos_current = MIN(pos_current, len_current); - p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true); - + p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_is_external_seeking, 0, AnimationNode::FILTER_IGNORE, true); } else { - len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true); pos_current = 0; } @@ -656,15 +642,10 @@ bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<Anima AnimationTree *tree_base = state_machine->get_animation_tree(); ERR_FAIL_COND_V(tree_base == nullptr, false); - NodePath advance_expression_base_node_path; - Node *expression_base = nullptr; - if (!transition->get_advance_expression_base_node().is_empty()) { - advance_expression_base_node_path = transition->get_advance_expression_base_node(); - expression_base = tree_base->get_tree()->get_root()->get_child(0)->get_node_or_null(advance_expression_base_node_path); - } else { - advance_expression_base_node_path = tree_base->get_advance_expression_base_node(); - expression_base = tree_base->get_node_or_null(advance_expression_base_node_path); - } + NodePath advance_expression_base_node_path = tree_base->get_advance_expression_base_node(); + Node *expression_base = tree_base->get_node_or_null(advance_expression_base_node_path); + + WARN_PRINT_ONCE("Animation transition has a valid expression, but no expression base node was set on its AnimationTree."); if (expression_base) { Ref<Expression> exp = transition->expression; @@ -1150,11 +1131,11 @@ Vector2 AnimationNodeStateMachine::get_graph_offset() const { return graph_offset; } -double AnimationNodeStateMachine::process(double p_time, bool p_seek, bool p_seek_root) { +double AnimationNodeStateMachine::process(double p_time, bool p_seek, bool p_is_external_seeking) { Ref<AnimationNodeStateMachinePlayback> playback_new = get_parameter(playback); ERR_FAIL_COND_V(playback_new.is_null(), 0.0); - return playback_new->process(this, p_time, p_seek, p_seek_root); + return playback_new->process(this, p_time, p_seek, p_is_external_seeking); } String AnimationNodeStateMachine::get_caption() const { diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index ab78b1afe8..0dfe5a3a43 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -54,7 +54,6 @@ private: bool disabled = false; int priority = 1; String advance_expression; - NodePath advance_expression_base_node; friend class AnimationNodeStateMachinePlayback; Ref<Expression> expression; @@ -77,9 +76,6 @@ public: void set_advance_expression(const String &p_expression); String get_advance_expression() const; - void set_advance_expression_base_node(const NodePath &p_expression_base_node); - NodePath get_advance_expression_base_node() const; - void set_xfade_time(float p_xfade); float get_xfade_time() const; @@ -137,7 +133,7 @@ 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, bool p_seek_root); + double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_is_external_seeking); bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const; @@ -243,7 +239,7 @@ public: void set_graph_offset(const Vector2 &p_offset); Vector2 get_graph_offset() const; - virtual double process(double p_time, bool p_seek, bool p_seek_root) override; + virtual double process(double p_time, bool p_seek, bool p_is_external_seeking) 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 fe67c0886f..cc6fadd9b2 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -109,7 +109,7 @@ bool AnimationPlayer::_set(const StringName &p_name, const Variant &p_value) { Ref<AnimationLibrary> lib = d[lib_name]; add_animation_library(lib_name, lib); } - + emit_signal("animation_libraries_updated"); } else if (name.begins_with("next/")) { String which = name.get_slicec('/', 1); animation_set_next(which, p_value); @@ -468,7 +468,7 @@ Variant AnimationPlayer::_post_process_key_value(const Ref<Animation> &p_anim, i return p_value; } -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) { +void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_prev_time, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started, Animation::LoopedFlag p_looped_flag) { _ensure_node_caches(p_anim); ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count()); @@ -664,7 +664,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } } - if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE || (p_delta == 0 && update_mode == Animation::UPDATE_DISCRETE)) { //delta == 0 means seek + if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { Variant value = a->value_track_interpolate(i, p_time); if (value == Variant()) { @@ -681,9 +681,23 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double pa->value_accum = Animation::interpolate_variant(pa->value_accum, value, p_interp); } - } else if (p_is_current && p_delta != 0) { + } else { List<int> indices; - a->value_track_get_key_indices(i, p_time, p_delta, &indices, p_pingponged); + + if (p_seeked) { + int found_key = a->track_find_key(i, p_time); + if (found_key >= 0) { + indices.push_back(found_key); + } + } else { + if (p_started) { + int first_key = a->track_find_key(i, p_prev_time, true); + if (first_key >= 0) { + indices.push_back(first_key); + } + } + a->track_get_key_indices_in_range(i, p_time, p_delta, &indices, p_looped_flag); + } for (int &F : indices) { Variant value = a->track_get_key_value(i, F); @@ -734,16 +748,26 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (!nc->node) { continue; } - if (p_delta == 0) { - continue; - } if (!p_is_current) { break; } List<int> indices; - a->method_track_get_key_indices(i, p_time, p_delta, &indices, p_pingponged); + if (p_seeked) { + int found_key = a->track_find_key(i, p_time); + if (found_key >= 0) { + indices.push_back(found_key); + } + } else { + if (p_started) { + int first_key = a->track_find_key(i, p_prev_time, true); + if (first_key >= 0) { + indices.push_back(first_key); + } + } + a->track_get_key_indices_in_range(i, p_time, p_delta, &indices, p_looped_flag); + } for (int &E : indices) { StringName method = a->method_track_get_name(i, E); @@ -787,9 +811,6 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double if (!nc->node) { continue; } - if (p_delta == 0) { - continue; - } if (p_seeked) { //find whatever should be playing @@ -833,7 +854,13 @@ 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, p_pingponged); + if (p_started) { + int first_key = a->track_find_key(i, p_prev_time, true); + if (first_key >= 0) { + to_play.push_back(first_key); + } + } + a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_looped_flag); if (to_play.size()) { int idx = to_play.back()->get(); @@ -893,7 +920,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double continue; } - if (p_delta == 0 || p_seeked) { + if (p_seeked) { //seek int idx = a->track_find_key(i, p_time); if (idx < 0) { @@ -928,9 +955,9 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double break; } - if (player->is_playing() || p_seeked) { - player->play(anim_name); + if (player->is_playing()) { player->seek(at_anim_pos); + player->play(anim_name); nc->animation_playing = true; playing_caches.insert(nc); } else { @@ -940,7 +967,13 @@ 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, p_pingponged); + if (p_started) { + int first_key = a->track_find_key(i, p_prev_time, true); + if (first_key >= 0) { + to_play.push_back(first_key); + } + } + a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_looped_flag); if (to_play.size()) { int idx = to_play.back()->get(); @@ -952,8 +985,8 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double nc->animation_playing = false; } } else { + player->seek(0.0); player->play(anim_name); - player->seek(0.0, true); nc->animation_playing = true; playing_caches.insert(nc); } @@ -968,9 +1001,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta, float p_blend, bool p_seeked, bool p_started) { double delta = p_delta * speed_scale * cd.speed_scale; double next_pos = cd.pos + delta; + bool backwards = signbit(delta); // Negative zero means playing backwards too. real_t len = cd.from->animation->get_length(); - int pingponged = 0; + Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE; switch (cd.from->animation->get_loop_mode()) { case Animation::LOOP_NONE: { @@ -979,64 +1013,57 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta, } 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) - - 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 - } - } + delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here). } break; 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 - // so state at time=length is previewable in the editor - next_pos = len; - } else { - next_pos = looped_next_pos; + if (next_pos < 0 && cd.pos >= 0) { + looped_flag = Animation::LOOPED_FLAG_START; + } + if (next_pos > len && cd.pos <= len) { + looped_flag = Animation::LOOPED_FLAG_END; } + next_pos = Math::fposmod(next_pos, (double)len); } break; 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; - pingponged = -1; - } - if (next_pos > len && cd.pos <= len) { - cd.speed_scale *= -1.0; - pingponged = 1; - } + if (next_pos < 0 && cd.pos >= 0) { + cd.speed_scale *= -1.0; + looped_flag = Animation::LOOPED_FLAG_START; } - 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; + if (next_pos > len && cd.pos <= len) { + cd.speed_scale *= -1.0; + looped_flag = Animation::LOOPED_FLAG_END; } + next_pos = Math::pingpong(next_pos, (double)len); } break; default: break; } + double prev_pos = cd.pos; // The animation may be changed during process, so it is safer that the state is changed before process. cd.pos = next_pos; - _animation_process_animation(cd.from, cd.pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started, pingponged); + AnimationData *prev_from = cd.from; + _animation_process_animation(cd.from, prev_pos, cd.pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started, looped_flag); + + // End detection. + if (cd.from->animation->get_loop_mode() == Animation::LOOP_NONE) { + if (prev_from != playback.current.from) { + return; // Animation has been changed in the process (may be caused by method track), abort process. + } + if (!backwards && prev_pos <= len && next_pos == len) { + // Playback finished. + end_reached = true; + end_notify = prev_pos < len; // Notify only if not already at the end. + } + if (backwards && prev_pos >= 0 && next_pos == 0) { + // Playback finished. + end_reached = true; + end_notify = prev_pos > 0; // Notify only if not already at the beginning. + } + } } void AnimationPlayer::_animation_process2(double p_delta, bool p_started) { @@ -1044,23 +1071,25 @@ void AnimationPlayer::_animation_process2(double p_delta, bool p_started) { accum_pass++; - _animation_process_data(c.current, p_delta, 1.0f, c.seeked && p_delta != 0, p_started); + bool seeked = c.seeked; // The animation may be changed during process, so it is safer that the state is changed before process. if (p_delta != 0) { c.seeked = false; } + _animation_process_data(c.current, p_delta, 1.0f, seeked, p_started); + List<Blend>::Element *prev = nullptr; for (List<Blend>::Element *E = c.blend.back(); E; E = prev) { Blend &b = E->get(); float blend = b.blend_left / b.blend_time; - _animation_process_data(b.data, p_delta, blend, false, false); - b.blend_left -= Math::absf(speed_scale * p_delta); - prev = E->prev(); if (b.blend_left < 0) { c.blend.erase(E); } + // The effect of animation changes during blending is unknown... + // In that case, we recommends to use method call mode "deferred", not "immediate". + _animation_process_data(b.data, p_delta, blend, false, false); } } @@ -1101,8 +1130,6 @@ void AnimationPlayer::_animation_update_transforms() { } } - cache_update_size = 0; - for (int i = 0; i < cache_update_prop_size; i++) { TrackNodeCache::PropertyAnim *pa = cache_update_prop[i]; @@ -1164,29 +1191,35 @@ void AnimationPlayer::_animation_update_transforms() { } } - cache_update_prop_size = 0; - for (int i = 0; i < cache_update_bezier_size; i++) { TrackNodeCache::BezierAnim *ba = cache_update_bezier[i]; ERR_CONTINUE(ba->accum_pass != accum_pass); ba->object->set_indexed(ba->bezier_property, ba->bezier_accum); } - - cache_update_bezier_size = 0; } void AnimationPlayer::_animation_process(double p_delta) { if (playback.current.from) { end_reached = false; end_notify = false; - _animation_process2(p_delta, playback.started); + bool started = playback.started; // The animation may be changed during process, so it is safer that the state is changed before process. if (playback.started) { playback.started = false; } + cache_update_size = 0; + cache_update_prop_size = 0; + cache_update_bezier_size = 0; + + AnimationData *prev_from = playback.current.from; + _animation_process2(p_delta, started); + if (prev_from != playback.current.from) { + return; // Animation has been changed in the process (may be caused by method track), abort process. + } _animation_update_transforms(); + if (end_reached) { if (queued.size()) { String old = playback.assigned; @@ -1283,6 +1316,7 @@ void AnimationPlayer::_animation_removed(const StringName &p_name, const StringN if (!animation_set.has(name)) { return; // No need to update because not the one from the library being used. } + _animation_set_cache_update(); // Erase blends if needed @@ -1376,8 +1410,9 @@ Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref animation_libraries.insert(insert_pos, ald); ald.library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_name)); - ald.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_name)); + ald.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed).bind(p_name)); ald.library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_name)); + ald.library->connect(SNAME("animation_changed"), callable_mp(this, &AnimationPlayer::_animation_changed)); _animation_set_cache_update(); @@ -1399,29 +1434,18 @@ void AnimationPlayer::remove_animation_library(const StringName &p_name) { 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_removed"), callable_mp(this, &AnimationPlayer::_animation_removed)); animation_libraries[at_pos].library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed)); + animation_libraries[at_pos].library->disconnect(SNAME("animation_changed"), callable_mp(this, &AnimationPlayer::_animation_changed)); 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), 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; @@ -1438,11 +1462,11 @@ void AnimationPlayer::rename_animation_library(const StringName &p_name, const S 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_removed"), callable_mp(this, &AnimationPlayer::_animation_removed)); 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).bind(p_new_name)); - animation_libraries[i].library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added).bind(p_new_name)); + animation_libraries[i].library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_removed).bind(p_new_name)); animation_libraries[i].library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed).bind(p_new_name)); for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) { @@ -1772,9 +1796,8 @@ double AnimationPlayer::get_current_animation_length() const { return playback.current.from->animation->get_length(); } -void AnimationPlayer::_animation_changed() { +void AnimationPlayer::_animation_changed(const StringName &p_name) { clear_caches(); - emit_signal(SNAME("caches_cleared")); if (is_playing()) { playback.seeked = true; //need to restart stuff, like audio } @@ -1813,6 +1836,8 @@ void AnimationPlayer::clear_caches() { cache_update_size = 0; cache_update_prop_size = 0; cache_update_bezier_size = 0; + + emit_signal(SNAME("caches_cleared")); } void AnimationPlayer::set_active(bool p_active) { @@ -2039,14 +2064,14 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) { // Forcing the use of the original root because the scene where original player belongs may be not the active one Ref<AnimatedValuesBackup> old_values = aux_player->backup_animated_values(get_node(get_root())); aux_player->seek(0.0f, true); - aux_player->queue_delete(); + aux_player->queue_free(); if (p_user_initiated) { Ref<AnimatedValuesBackup> new_values = aux_player->backup_animated_values(); old_values->restore(); Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); - ur->create_action(TTR("Anim Apply Reset")); + ur->create_action(TTR("Animation Apply Reset")); ur->add_do_method(new_values.ptr(), "restore"); ur->add_undo_method(old_values.ptr(), "restore"); ur->commit_action(); @@ -2131,7 +2156,7 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationPlayer::advance); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_node"), "set_root", "get_root"); - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ANIMATE_AS_TRIGGER), "set_current_animation", "get_current_animation"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR), "set_current_animation", "get_current_animation"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "assigned_animation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_assigned_animation", "get_assigned_animation"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "autoplay", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_autoplay", "get_autoplay"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_on_save", PROPERTY_HINT_NONE, ""), "set_reset_on_save_enabled", "is_reset_on_save_enabled"); @@ -2151,6 +2176,7 @@ void AnimationPlayer::_bind_methods() { ADD_SIGNAL(MethodInfo("animation_changed", PropertyInfo(Variant::STRING_NAME, "old_name"), PropertyInfo(Variant::STRING_NAME, "new_name"))); ADD_SIGNAL(MethodInfo("animation_started", PropertyInfo(Variant::STRING_NAME, "anim_name"))); ADD_SIGNAL(MethodInfo("animation_list_changed")); + ADD_SIGNAL(MethodInfo("animation_libraries_updated")); ADD_SIGNAL(MethodInfo("caches_cleared")); BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS); diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index 4f32927d25..0b95ee4e9e 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -268,7 +268,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, int p_pingponged = 0); + void _animation_process_animation(AnimationData *p_anim, double p_prev_time, double p_time, double p_delta, float p_interp, bool p_is_current = true, bool p_seeked = false, bool p_started = false, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE); 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); @@ -291,9 +291,7 @@ private: return ret; } - void _animation_changed(); - void _ref_anim(const Ref<Animation> &p_anim); - void _unref_anim(const Ref<Animation> &p_anim); + void _animation_changed(const StringName &p_name); void _set_process(bool p_process, bool p_force = false); diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 4d09fa619c..b06a21dea9 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -50,10 +50,8 @@ void AnimationNode::get_parameter_list(List<PropertyInfo> *r_list) const { Variant AnimationNode::get_parameter_default_value(const StringName &p_parameter) const { Variant ret; - if (GDVIRTUAL_CALL(_get_parameter_default_value, p_parameter, ret)) { - return ret; - } - return Variant(); + GDVIRTUAL_CALL(_get_parameter_default_value, p_parameter, ret); + return ret; } void AnimationNode::set_parameter(const StringName &p_name, const Variant &p_value) { @@ -88,7 +86,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, bool p_seek_root, 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_is_external_seeking, real_t p_blend, Animation::LoopedFlag p_looped_flag) { ERR_FAIL_COND(!state); ERR_FAIL_COND(!state->player->has_animation(p_animation)); @@ -114,19 +112,19 @@ void AnimationNode::blend_animation(const StringName &p_animation, double p_time anim_state.time = p_time; anim_state.animation = animation; anim_state.seeked = p_seeked; - anim_state.pingponged = p_pingponged; - anim_state.seek_root = p_seek_root; + anim_state.looped_flag = p_looped_flag; + anim_state.is_external_seeking = p_is_external_seeking; 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, bool p_seek_root, 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_is_external_seeking, 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, p_seek_root); + double t = process(p_time, p_seek, p_is_external_seeking); state = nullptr; parent = nullptr; @@ -150,7 +148,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, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_sync) { +double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter, bool p_sync) { ERR_FAIL_INDEX_V(p_input, inputs.size(), 0); ERR_FAIL_COND_V(!state, 0); @@ -169,7 +167,7 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, bool //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_seek_root, p_blend, p_filter, p_sync, &activity); + double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_is_external_seeking, p_blend, p_filter, p_sync, &activity); Vector<AnimationTree::Activity> *activity_ptr = state->tree->input_activity_map.getptr(base_path); @@ -180,11 +178,11 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, bool return ret; } -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_sync) { - return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_sync); +double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter, bool p_sync) { + return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_is_external_seeking, p_blend, p_filter, p_sync); } -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_sync, 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_is_external_seeking, real_t p_blend, FilterAction p_filter, bool p_sync, real_t *r_max) { ERR_FAIL_COND_V(!p_node.is_valid(), 0); ERR_FAIL_COND_V(!state, 0); @@ -223,7 +221,7 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri } blendw[i] = blendr[i] * p_blend; - if (blendw[i] > CMP_EPSILON) { + if (!Math::is_zero_approx(blendw[i])) { any_valid = true; } } @@ -238,7 +236,7 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri } blendw[i] = blendr[i] * p_blend; - if (blendw[i] > CMP_EPSILON) { + if (!Math::is_zero_approx(blendw[i])) { any_valid = true; } } @@ -254,7 +252,7 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri blendw[i] = blendr[i]; //not filtered, do not blend } - if (blendw[i] > CMP_EPSILON) { + if (!Math::is_zero_approx(blendw[i])) { any_valid = true; } } @@ -265,7 +263,7 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri for (int i = 0; i < blend_count; i++) { //regular blend blendw[i] = blendr[i] * p_blend; - if (blendw[i] > CMP_EPSILON) { + if (!Math::is_zero_approx(blendw[i])) { any_valid = true; } } @@ -291,15 +289,12 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri new_path = String(parent->base_path) + String(p_subpath) + "/"; } - // If tracks for blending don't exist for one of the animations, Rest or RESET animation is blended as init animation instead. - // Then blend weight is 0 means that the init animation blend weight is 1. - // In that case, processing only the animation with the lacking track will not process the lacking track, and will not properly apply the Reset value. - // This means that all tracks which the animations in the branch that may be blended have must be processed. - // Therefore, the blending process must be executed even if the blend weight is 0. + // This process, which depends on p_sync is needed to process sync correctly in the case of + // that a synced AnimationNodeSync exists under the un-synced AnimationNodeSync. if (!p_seek && !p_sync && !any_valid) { - 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, 0, p_seek, p_is_external_seeking, p_connections); } - return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_seek_root, p_connections); + return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_is_external_seeking, p_connections); } int AnimationNode::get_input_count() const { @@ -312,12 +307,9 @@ String AnimationNode::get_input_name(int p_input) { } String AnimationNode::get_caption() const { - String ret; - if (GDVIRTUAL_CALL(_get_caption, ret)) { - return ret; - } - - return "Node"; + String ret = "Node"; + GDVIRTUAL_CALL(_get_caption, ret); + return ret; } void AnimationNode::add_input(const String &p_name) { @@ -343,13 +335,10 @@ void AnimationNode::remove_input(int p_index) { emit_changed(); } -double AnimationNode::process(double p_time, bool p_seek, bool p_seek_root) { - double ret; - if (GDVIRTUAL_CALL(_process, p_time, p_seek, p_seek_root, ret)) { - return ret; - } - - return 0; +double AnimationNode::process(double p_time, bool p_seek, bool p_is_external_seeking) { + double ret = 0; + GDVIRTUAL_CALL(_process, p_time, p_seek, p_is_external_seeking, ret); + return ret; } void AnimationNode::set_filter_path(const NodePath &p_path, bool p_enable) { @@ -373,12 +362,9 @@ bool AnimationNode::is_path_filtered(const NodePath &p_path) const { } bool AnimationNode::has_filter() const { - bool ret; - if (GDVIRTUAL_CALL(_has_filter, ret)) { - return ret; - } - - return false; + bool ret = false; + GDVIRTUAL_CALL(_has_filter, ret); + return ret; } Array AnimationNode::_get_filters() const { @@ -407,10 +393,8 @@ void AnimationNode::_validate_property(PropertyInfo &p_property) const { Ref<AnimationNode> AnimationNode::get_child_by_name(const StringName &p_name) { Ref<AnimationNode> ret; - if (GDVIRTUAL_CALL(_get_child_by_name, p_name, ret)) { - return ret; - } - return Ref<AnimationNode>(); + GDVIRTUAL_CALL(_get_child_by_name, p_name, ret); + return ret; } void AnimationNode::_bind_methods() { @@ -429,9 +413,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", "seek_root", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "seek_root", "blend", "filter", "sync"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "seek_root", "blend", "filter", "sync"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "is_external_seeking", "blend", "looped_flag"), &AnimationNode::blend_animation, DEFVAL(Animation::LOOPED_FLAG_NONE)); + ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "is_external_seeking", "blend", "filter", "sync"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "is_external_seeking", "blend", "filter", "sync"), &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); @@ -443,7 +427,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", "seek_root"); + GDVIRTUAL_BIND(_process, "time", "seek", "is_external_seeking"); GDVIRTUAL_BIND(_get_caption); GDVIRTUAL_BIND(_has_filter); @@ -602,6 +586,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track_value->object = child; } + track_value->is_discrete = anim->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE; track_value->is_using_angle = anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE; track_value->subpath = leftover_path; @@ -609,6 +594,13 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track = track_value; + // If a value track without a key is cached first, the initial value cannot be determined. + // It is a corner case, but which may cause problems with blending. + ERR_CONTINUE_MSG(anim->track_get_key_count(i) == 0, "AnimationTree: '" + String(E) + "', value track: '" + String(path) + "' must have at least one key to cache for blending."); + track_value->init_value = anim->track_get_key_value(i, 0); + track_value->init_value.zero(); + + // If there is a Reset Animation, it takes precedence by overwriting. if (has_reset_anim) { int rt = reset_anim->find_track(path, track_type); if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { @@ -808,8 +800,18 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { } } else if (track_cache_type == Animation::TYPE_VALUE) { // If it has at least one angle interpolation, it also uses angle interpolation for blending. - TrackCacheValue *track_value = memnew(TrackCacheValue); + TrackCacheValue *track_value = static_cast<TrackCacheValue *>(track); + bool was_discrete = track_value->is_discrete; + bool was_using_angle = track_value->is_using_angle; + track_value->is_discrete |= anim->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE; track_value->is_using_angle |= anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE; + + if (was_discrete != track_value->is_discrete) { + ERR_PRINT_ED("Value track: " + String(path) + " with different update modes are blended. Blending prioritizes Discrete mode, so other update mode tracks will not be blended."); + } + if (was_using_angle != track_value->is_using_angle) { + WARN_PRINT_ED("Value track: " + String(path) + " with different interpolation types for rotation are blended. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value."); + } } track->setup_pass = setup_pass; @@ -857,7 +859,6 @@ void AnimationTree::_clear_caches() { memdelete(K.value); } playing_caches.clear(); - track_cache.clear(); cache_valid = false; } @@ -881,7 +882,9 @@ 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(); + root_motion_position = Vector3(0, 0, 0); + root_motion_rotation = Quaternion(0, 0, 0, 1); + root_motion_scale = Vector3(0, 0, 0); if (!root.is_valid()) { ERR_PRINT("AnimationTree: root AnimationNode is not set, disabling playback."); @@ -968,8 +971,44 @@ void AnimationTree::_process_graph(double p_delta) { if (!state.valid) { return; //state is not valid. do nothing. } - //apply value/transform/bezier blends to track caches and execute method/audio/animation tracks + // Init all value/transform/blend/bezier tracks that track_cache has. + { + for (const KeyValue<NodePath, TrackCache *> &K : track_cache) { + TrackCache *track = K.value; + + switch (track->type) { + case Animation::TYPE_POSITION_3D: { + TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); + if (track->root_motion) { + t->loc = Vector3(0, 0, 0); + t->rot = Quaternion(0, 0, 0, 1); + t->scale = Vector3(1, 1, 1); + } else { + t->loc = t->init_loc; + t->rot = t->init_rot; + t->scale = t->init_scale; + } + } break; + case Animation::TYPE_BLEND_SHAPE: { + TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track); + t->value = t->init_value; + } break; + case Animation::TYPE_VALUE: { + TrackCacheValue *t = static_cast<TrackCacheValue *>(track); + t->value = t->init_value; + } break; + case Animation::TYPE_BEZIER: { + TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track); + t->value = t->init_value; + } break; + default: { + } break; + } + } + } + + // Apply value/transform/blend/bezier blends to track caches and execute method/audio/animation tracks. { bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint(); @@ -979,10 +1018,11 @@ void AnimationTree::_process_graph(double p_delta) { double delta = as.delta; real_t weight = as.blend; bool seeked = as.seeked; - int pingponged = as.pingponged; + Animation::LoopedFlag looped_flag = as.looped_flag; + bool is_external_seeking = as.is_external_seeking; #ifndef _3D_DISABLED - bool backward = signbit(delta); - bool calc_root = !seeked || as.seek_root; + bool backward = signbit(delta); // This flag is required only for the root motion since it calculates the difference between the previous and current frames. + bool calc_root = !seeked || is_external_seeking; #endif // _3D_DISABLED for (int i = 0; i < a->get_track_count(); i++) { @@ -991,37 +1031,31 @@ void AnimationTree::_process_graph(double p_delta) { } NodePath path = a->track_get_path(i); - - ERR_CONTINUE(!track_cache.has(path)); - + if (!track_cache.has(path)) { + continue; // No path, but avoid error spamming. + } TrackCache *track = track_cache[path]; + ERR_CONTINUE(!state.track_map.has(path)); + int blend_idx = state.track_map[path]; + ERR_CONTINUE(blend_idx < 0 || blend_idx >= state.track_count); + real_t blend = (*as.track_blends)[blend_idx] * weight; + if (Math::is_zero_approx(blend)) { + continue; // Nothing to blend. + } + Animation::TrackType ttype = a->track_get_type(i); if (ttype != Animation::TYPE_POSITION_3D && ttype != Animation::TYPE_ROTATION_3D && ttype != Animation::TYPE_SCALE_3D && track->type != ttype) { //broken animation, but avoid error spamming continue; } - track->root_motion = root_motion_track == path; - ERR_CONTINUE(!state.track_map.has(path)); - int blend_idx = state.track_map[path]; - - ERR_CONTINUE(blend_idx < 0 || blend_idx >= state.track_count); - - real_t blend = (*as.track_blends)[blend_idx] * weight; - switch (ttype) { case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); 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) { @@ -1097,12 +1131,6 @@ 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); @@ -1119,12 +1147,6 @@ void AnimationTree::_process_graph(double p_delta) { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); 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) { @@ -1199,12 +1221,6 @@ 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; - } Quaternion rot; Error err = a->rotation_track_interpolate(i, time, &rot); @@ -1221,12 +1237,6 @@ void AnimationTree::_process_graph(double p_delta) { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); 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) { @@ -1302,12 +1312,6 @@ 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); @@ -1324,11 +1328,6 @@ void AnimationTree::_process_graph(double p_delta) { #ifndef _3D_DISABLED TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track); - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->value = t->init_value; - } - float value; Error err = a->blend_shape_track_interpolate(i, time, &value); @@ -1355,15 +1354,6 @@ void AnimationTree::_process_graph(double p_delta) { continue; } - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - if (!t->init_value) { - t->init_value = value; - t->init_value.zero(); - } - t->value = t->init_value; - } - // Special case for angle interpolation. if (t->is_using_angle) { // For blending consistency, it prevents rotation of more than 180 degrees from init_value. @@ -1392,12 +1382,8 @@ void AnimationTree::_process_graph(double p_delta) { } } } else { - if (blend < CMP_EPSILON) { - continue; //nothing to blend - } - if (seeked) { - int idx = a->track_find_key(i, time); + int idx = a->track_find_key(i, time, !is_external_seeking); if (idx < 0) { continue; } @@ -1406,7 +1392,7 @@ void AnimationTree::_process_graph(double p_delta) { t->object->set_indexed(t->subpath, value); } else { List<int> indices; - a->value_track_get_key_indices(i, time, delta, &indices, pingponged); + a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag); for (int &F : indices) { Variant value = a->track_get_key_value(i, F); value = _post_process_key_value(a, i, value, t->object); @@ -1417,13 +1403,10 @@ void AnimationTree::_process_graph(double p_delta) { } break; case Animation::TYPE_METHOD: { - if (blend < CMP_EPSILON) { - continue; //nothing to blend - } TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track); if (seeked) { - int idx = a->track_find_key(i, time); + int idx = a->track_find_key(i, time, !is_external_seeking); if (idx < 0) { continue; } @@ -1434,7 +1417,7 @@ void AnimationTree::_process_graph(double p_delta) { } } else { List<int> indices; - a->method_track_get_key_indices(i, time, delta, &indices, pingponged); + a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag); for (int &F : indices) { StringName method = a->method_track_get_name(i, F); Vector<Variant> params = a->method_track_get_params(i, F); @@ -1450,22 +1433,14 @@ void AnimationTree::_process_graph(double p_delta) { real_t bezier = a->bezier_track_interpolate(i, time); bezier = _post_process_key_value(a, i, bezier, t->object); - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->value = t->init_value; - } - t->value += (bezier - t->init_value) * blend; } break; case Animation::TYPE_AUDIO: { - if (blend < CMP_EPSILON) { - continue; //nothing to blend - } TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); if (seeked) { //find whatever should be playing - int idx = a->track_find_key(i, time); + int idx = a->track_find_key(i, time, !is_external_seeking); if (idx < 0) { continue; } @@ -1505,7 +1480,7 @@ void AnimationTree::_process_graph(double p_delta) { } else { //find stuff to play List<int> to_play; - a->track_get_key_indices_in_range(i, time, delta, &to_play, pingponged); + a->track_get_key_indices_in_range(i, time, delta, &to_play, looped_flag); if (to_play.size()) { int idx = to_play.back()->get(); @@ -1565,16 +1540,9 @@ void AnimationTree::_process_graph(double p_delta) { } real_t db = Math::linear_to_db(MAX(blend, 0.00001)); - if (t->object->has_method(SNAME("set_unit_db"))) { - t->object->call(SNAME("set_unit_db"), db); - } else { - t->object->call(SNAME("set_volume_db"), db); - } + t->object->call(SNAME("set_volume_db"), db); } break; case Animation::TYPE_ANIMATION: { - if (blend < CMP_EPSILON) { - continue; //nothing to blend - } TrackCacheAnimation *t = static_cast<TrackCacheAnimation *>(track); AnimationPlayer *player2 = Object::cast_to<AnimationPlayer>(t->object); @@ -1585,7 +1553,7 @@ void AnimationTree::_process_graph(double p_delta) { if (seeked) { //seek - int idx = a->track_find_key(i, time); + int idx = a->track_find_key(i, time, !is_external_seeking); if (idx < 0) { continue; } @@ -1616,8 +1584,8 @@ void AnimationTree::_process_graph(double p_delta) { } if (player2->is_playing() || seeked) { - player2->play(anim_name); player2->seek(at_anim_pos); + player2->play(anim_name); t->playing = true; playing_caches.insert(t); } else { @@ -1627,7 +1595,7 @@ void AnimationTree::_process_graph(double p_delta) { } else { //find stuff to play List<int> to_play; - a->track_get_key_indices_in_range(i, time, delta, &to_play, pingponged); + a->track_get_key_indices_in_range(i, time, delta, &to_play, looped_flag); if (to_play.size()) { int idx = to_play.back()->get(); @@ -1656,9 +1624,6 @@ void AnimationTree::_process_graph(double p_delta) { // finally, set the tracks for (const KeyValue<NodePath, TrackCache *> &K : track_cache) { TrackCache *track = K.value; - if (track->process_pass != process_pass) { - continue; //not processed, ignore - } switch (track->type) { case Animation::TYPE_POSITION_3D: { @@ -1666,11 +1631,9 @@ void AnimationTree::_process_graph(double p_delta) { TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); if (t->root_motion) { - Transform3D xform; - xform.origin = t->loc; - xform.basis.set_quaternion_scale(t->rot, Vector3(1, 1, 1) + t->scale); - - root_motion_transform = xform; + root_motion_position = t->loc; + root_motion_rotation = t->rot; + root_motion_scale = t->scale - Vector3(1, 1, 1); } else if (t->skeleton && t->bone_idx >= 0) { if (t->loc_used) { @@ -1708,6 +1671,10 @@ void AnimationTree::_process_graph(double p_delta) { case Animation::TYPE_VALUE: { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); + if (t->is_discrete) { + break; // Don't overwrite the value set by UPDATE_DISCRETE. + } + if (t->init_value.get_type() == Variant::BOOL) { t->object->set_indexed(t->subpath, t->value.operator real_t() >= 0.5); } else { @@ -1792,7 +1759,7 @@ void AnimationTree::_setup_animation_player() { AnimationPlayer *new_player = nullptr; if (!animation_player.is_empty()) { - new_player = Object::cast_to<AnimationPlayer>(get_node(animation_player)); + new_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player)); if (new_player && !new_player->is_connected("animation_list_changed", callable_mp(this, &AnimationTree::_animation_player_changed))) { new_player->connect("animation_list_changed", callable_mp(this, &AnimationTree::_animation_player_changed)); } @@ -1879,8 +1846,16 @@ NodePath AnimationTree::get_root_motion_track() const { return root_motion_track; } -Transform3D AnimationTree::get_root_motion_transform() const { - return root_motion_transform; +Vector3 AnimationTree::get_root_motion_position() const { + return root_motion_position; +} + +Quaternion AnimationTree::get_root_motion_rotation() const { + return root_motion_rotation; +} + +Vector3 AnimationTree::get_root_motion_scale() const { + return root_motion_scale; } void AnimationTree::_tree_changed() { @@ -2038,7 +2013,9 @@ void AnimationTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_motion_track", "path"), &AnimationTree::set_root_motion_track); ClassDB::bind_method(D_METHOD("get_root_motion_track"), &AnimationTree::get_root_motion_track); - ClassDB::bind_method(D_METHOD("get_root_motion_transform"), &AnimationTree::get_root_motion_transform); + ClassDB::bind_method(D_METHOD("get_root_motion_position"), &AnimationTree::get_root_motion_position); + ClassDB::bind_method(D_METHOD("get_root_motion_rotation"), &AnimationTree::get_root_motion_rotation); + ClassDB::bind_method(D_METHOD("get_root_motion_scale"), &AnimationTree::get_root_motion_scale); ClassDB::bind_method(D_METHOD("_update_properties"), &AnimationTree::_update_properties); diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 96c1279f82..be0dc1af4e 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -68,8 +68,8 @@ public: const Vector<real_t> *track_blends = nullptr; real_t blend = 0.0; bool seeked = false; - bool seek_root = false; - int pingponged = 0; + bool is_external_seeking = false; + Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE; }; struct State { @@ -86,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, bool p_seek_root, 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_is_external_seeking, const Vector<StringName> &p_connections); //all this is temporary StringName base_path; @@ -99,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, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = 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_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, real_t *r_max = nullptr); protected: - 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_sync = 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_sync = true); + void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_is_external_seeking, real_t p_blend, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE); + double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true); + double blend_input(int p_input, double p_time, bool p_seek, bool p_is_external_seeking, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true); void make_invalid(const String &p_reason); AnimationTree *get_animation_tree() const; @@ -135,7 +135,7 @@ public: virtual void get_child_nodes(List<ChildNode> *r_child_nodes); - virtual double process(double p_time, bool p_seek, bool p_seek_root); + virtual double process(double p_time, bool p_seek, bool p_is_external_seeking); virtual String get_caption() const; int get_input_count() const; @@ -190,7 +190,6 @@ private: struct TrackCache { bool root_motion = false; uint64_t setup_pass = 0; - uint64_t process_pass = 0; Animation::TrackType type = Animation::TrackType::TYPE_ANIMATION; Object *object = nullptr; ObjectID object_id; @@ -233,6 +232,7 @@ private: Variant init_value; Variant value; Vector<StringName> subpath; + bool is_discrete = false; bool is_using_angle = false; TrackCacheValue() { type = Animation::TYPE_VALUE; } }; @@ -294,7 +294,9 @@ private: bool started = true; NodePath root_motion_track; - Transform3D root_motion_transform; + Vector3 root_motion_position = Vector3(0, 0, 0); + Quaternion root_motion_rotation = Quaternion(0, 0, 0, 1); + Vector3 root_motion_scale = Vector3(0, 0, 0); friend class AnimationNode; bool properties_dirty = true; @@ -350,7 +352,9 @@ public: void set_root_motion_track(const NodePath &p_track); NodePath get_root_motion_track() const; - Transform3D get_root_motion_transform() const; + Vector3 get_root_motion_position() const; + Quaternion get_root_motion_rotation() const; + Vector3 get_root_motion_scale() const; real_t get_connection_activity(const StringName &p_path, int p_connection) const; void advance(double p_time); diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp index 47f08219a9..a6ccb4a576 100644 --- a/scene/animation/root_motion_view.cpp +++ b/scene/animation/root_motion_view.cpp @@ -103,7 +103,8 @@ void RootMotionView::_notification(int p_what) { set_physics_process_internal(false); } - transform = tree->get_root_motion_transform(); + transform.origin = tree->get_root_motion_position(); + transform.basis = tree->get_root_motion_rotation(); // Scale is meaningless. } } @@ -113,9 +114,8 @@ void RootMotionView::_notification(int p_what) { first = false; - transform.orthonormalize(); //don't want scale, too imprecise - - accumulated = accumulated * transform; + accumulated.origin += transform.origin; + accumulated.basis *= transform.basis; accumulated.origin.x = Math::fposmod(accumulated.origin.x, cell_size); if (zero_y) { accumulated.origin.y = 0; |