diff options
Diffstat (limited to 'scene/animation')
| -rw-r--r-- | scene/animation/animation_blend_space_1d.cpp | 8 | ||||
| -rw-r--r-- | scene/animation/animation_blend_space_1d.h | 4 | ||||
| -rw-r--r-- | scene/animation/animation_blend_space_2d.cpp | 21 | ||||
| -rw-r--r-- | scene/animation/animation_blend_space_2d.h | 4 | ||||
| -rw-r--r-- | scene/animation/animation_blend_tree.cpp | 91 | ||||
| -rw-r--r-- | scene/animation/animation_blend_tree.h | 30 | ||||
| -rw-r--r-- | scene/animation/animation_node_state_machine.cpp | 20 | ||||
| -rw-r--r-- | scene/animation/animation_node_state_machine.h | 4 | ||||
| -rw-r--r-- | scene/animation/animation_player.cpp | 175 | ||||
| -rw-r--r-- | scene/animation/animation_player.h | 8 | ||||
| -rw-r--r-- | scene/animation/animation_tree.cpp | 318 | ||||
| -rw-r--r-- | scene/animation/animation_tree.h | 22 | ||||
| -rw-r--r-- | scene/animation/easing_equations.h | 4 | ||||
| -rw-r--r-- | scene/animation/root_motion_view.cpp | 4 | ||||
| -rw-r--r-- | scene/animation/root_motion_view.h | 4 | ||||
| -rw-r--r-- | scene/animation/tween.cpp | 42 | ||||
| -rw-r--r-- | scene/animation/tween.h | 9 |
17 files changed, 516 insertions, 252 deletions
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index 9a71e7bf55..849316c568 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -292,10 +292,10 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek) { // actually blend the animations now - float max_time_remaining = 0.0; + double max_time_remaining = 0.0; for (int i = 0; i < blend_points_used; i++) { - float remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false); + double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false); max_time_remaining = MAX(max_time_remaining, remaining); } diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index 6730c09fd4..7038cece06 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index b91a2869e4..a3aa3f6cc8 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -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 { @@ -133,7 +134,7 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) { } } if (erase) { - triangles.remove(i); + triangles.remove_at(i); i--; } @@ -223,7 +224,7 @@ int AnimationNodeBlendSpace2D::get_triangle_point(int p_triangle, int p_point) { void AnimationNodeBlendSpace2D::remove_triangle(int p_triangle) { ERR_FAIL_INDEX(p_triangle, triangles.size()); - triangles.remove(p_triangle); + triangles.remove_at(p_triangle); } int AnimationNodeBlendSpace2D::get_triangle_count() const { @@ -437,7 +438,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) { Vector2 blend_pos = get_parameter(blend_position); int closest = get_parameter(this->closest); double length_internal = get_parameter(this->length_internal); - float mind = 0.0; //time of min distance point + double mind = 0.0; //time of min distance point if (blend_mode == BLEND_MODE_INTERPOLATED) { if (triangles.size() == 0) { @@ -501,7 +502,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) { for (int j = 0; j < 3; j++) { if (i == triangle_points[j]) { //blend with the given weight - float t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false); + double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false); if (first || t < mind) { mind = t; first = false; @@ -529,8 +530,14 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) { } if (new_closest != closest && new_closest != -1) { - float from = 0.0; + double 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_space_2d.h b/scene/animation/animation_blend_space_2d.h index a919fff1d2..1356656bf8 100644 --- a/scene/animation/animation_blend_space_2d.h +++ b/scene/animation/animation_blend_space_2d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index d18e1a92d8..3d0ac291b8 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -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) { @@ -56,7 +57,7 @@ void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const { } anims += String(names[i]); } - if (anims != String()) { + if (!anims.is_empty()) { property.hint = PROPERTY_HINT_ENUM; property.hint_string = anims; } @@ -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() { @@ -322,6 +371,8 @@ void AnimationNodeOneShot::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeOneShot::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeOneShot::is_using_sync); + ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_mode", PROPERTY_HINT_ENUM, "Blend,Add"), "set_mix_mode", "get_mix_mode"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadein_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadein_time", "get_fadein_time"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadeout_time", "get_fadeout_time"); @@ -533,7 +584,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 { @@ -699,7 +750,7 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) { return 0; } - float rem = 0.0; + double rem = 0.0; if (prev < 0) { // process current animation, check for transition @@ -806,7 +857,7 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNod ERR_FAIL_COND(nodes.has(p_name)); ERR_FAIL_COND(p_node.is_null()); ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output); - ERR_FAIL_COND(String(p_name).find("/") != -1); + ERR_FAIL_COND(String(p_name).contains("/")); Node n; n.node = p_node; diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index 258443a999..2acacd7396 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -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_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index b5d7a0555e..a68f7e037d 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -50,9 +50,9 @@ bool AnimationNodeStateMachineTransition::has_auto_advance() const { void AnimationNodeStateMachineTransition::set_advance_condition(const StringName &p_condition) { String cs = p_condition; - ERR_FAIL_COND(cs.find("/") != -1 || cs.find(":") != -1); + ERR_FAIL_COND(cs.contains("/") || cs.contains(":")); advance_condition = p_condition; - if (cs != String()) { + if (!cs.is_empty()) { advance_condition_name = "conditions/" + cs; } else { advance_condition_name = StringName(); @@ -464,7 +464,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } if (path.size()) { //if it came from path, remove path - path.remove(0); + path.remove_at(0); } current = next; if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) { @@ -536,7 +536,7 @@ Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position) { ERR_FAIL_COND(states.has(p_name)); ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(String(p_name).find("/") != -1); + ERR_FAIL_COND(String(p_name).contains("/")); State state; state.node = p_node; @@ -553,7 +553,7 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<AnimationNode> p_node) { ERR_FAIL_COND(states.has(p_name) == false); ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(String(p_name).find("/") != -1); + ERR_FAIL_COND(String(p_name).contains("/")); { Ref<AnimationNode> node = states[p_name].node; @@ -624,7 +624,7 @@ void AnimationNodeStateMachine::remove_node(const StringName &p_name) { for (int i = 0; i < transitions.size(); i++) { if (transitions[i].from == p_name || transitions[i].to == p_name) { transitions.write[i].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed)); - transitions.remove(i); + transitions.remove_at(i); i--; } } @@ -751,7 +751,7 @@ void AnimationNodeStateMachine::remove_transition(const StringName &p_from, cons for (int i = 0; i < transitions.size(); i++) { if (transitions[i].from == p_from && transitions[i].to == p_to) { transitions.write[i].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed)); - transitions.remove(i); + transitions.remove_at(i); return; } } @@ -764,7 +764,7 @@ void AnimationNodeStateMachine::remove_transition(const StringName &p_from, cons void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) { ERR_FAIL_INDEX(p_transition, transitions.size()); transitions.write[p_transition].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed)); - transitions.remove(p_transition); + transitions.remove_at(p_transition); /*if (playing) { path.clear(); }*/ diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index 6f0e3107fd..b980556875 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index f91422ac1a..4a3fd72080 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -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); @@ -624,7 +625,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double pa->object->set_indexed(pa->subpath, value, &valid); //you are not speshul #ifdef DEBUG_ENABLED if (!valid) { - ERR_PRINT("Failed setting track value '" + String(pa->owner->path) + "'. Check if property exists or the type of key is valid. Animation '" + a->get_name() + "' at node '" + get_path() + "'."); + ERR_PRINT("Failed setting track value '" + String(pa->owner->path) + "'. Check if the property exists or the type of key is valid. Animation '" + a->get_name() + "' at node '" + get_path() + "'."); } #endif @@ -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,14 +729,14 @@ 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; ba->bezier_accum = bezier; ba->accum_pass = accum_pass; } else { - ba->bezier_accum = Math::lerp(ba->bezier_accum, bezier, p_interp); + ba->bezier_accum = Math::lerp(ba->bezier_accum, (float)bezier, p_interp); } } break; @@ -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) { @@ -1029,8 +1070,24 @@ void AnimationPlayer::_animation_update_transforms() { bool valid; pa->object->set_indexed(pa->subpath, pa->value_accum, &valid); //you are not speshul #ifdef DEBUG_ENABLED + if (!valid) { - ERR_PRINT("Failed setting key at time " + rtos(playback.current.pos) + " in Animation '" + get_current_animation() + "' at Node '" + get_path() + "', Track '" + String(pa->owner->path) + "'. Check if property exists or the type of key is right for the property"); + // Get subpath as string for printing the error + // Cannot use `String::join(Vector<String>)` because this is a vector of StringName + String key_debug; + if (pa->subpath.size() > 0) { + key_debug = pa->subpath[0]; + for (int subpath_index = 1; subpath_index < pa->subpath.size(); ++subpath_index) { + key_debug += "."; + key_debug += pa->subpath[subpath_index]; + } + } + ERR_PRINT("Failed setting key '" + key_debug + + "' at time " + rtos(playback.current.pos) + + " in Animation '" + get_current_animation() + + "' at Node '" + get_path() + + "', Track '" + String(pa->owner->path) + + "'. Check if the property exists or the type of key is right for the property."); } #endif @@ -1114,7 +1171,7 @@ void AnimationPlayer::_animation_process(double p_delta) { Error AnimationPlayer::add_animation(const StringName &p_name, const Ref<Animation> &p_animation) { #ifdef DEBUG_ENABLED - ERR_FAIL_COND_V_MSG(String(p_name).find("/") != -1 || String(p_name).find(":") != -1 || String(p_name).find(",") != -1 || String(p_name).find("[") != -1, ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + "."); + ERR_FAIL_COND_V_MSG(String(p_name).contains("/") || String(p_name).contains(":") || String(p_name).contains(",") || String(p_name).contains("["), ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + "."); #endif ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER); @@ -1156,7 +1213,7 @@ void AnimationPlayer::_unref_anim(const Ref<Animation> &p_anim) { void AnimationPlayer::rename_animation(const StringName &p_name, const StringName &p_new_name) { ERR_FAIL_COND(!animation_set.has(p_name)); - ERR_FAIL_COND(String(p_new_name).find("/") != -1 || String(p_new_name).find(":") != -1); + ERR_FAIL_COND(String(p_new_name).contains("/") || String(p_new_name).contains(":")); ERR_FAIL_COND(animation_set.has(p_new_name)); stop(); @@ -1375,7 +1432,7 @@ bool AnimationPlayer::is_playing() const { } void AnimationPlayer::set_current_animation(const String &p_anim) { - if (p_anim == "[stop]" || p_anim == "") { + if (p_anim == "[stop]" || p_anim.is_empty()) { stop(); } else if (!is_playing() || playback.assigned != p_anim) { play(p_anim); @@ -1713,7 +1770,7 @@ Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values(Node *p_root_o Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) { ERR_FAIL_COND_V(!can_apply_reset(), Ref<AnimatedValuesBackup>()); - Ref<Animation> reset_anim = animation_set["RESET"].animation; + Ref<Animation> reset_anim = animation_set[SceneStringNames::get_singleton()->RESET].animation; ERR_FAIL_COND_V(reset_anim.is_null(), Ref<AnimatedValuesBackup>()); Node *root_node = get_node_or_null(root); @@ -1721,8 +1778,8 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) { AnimationPlayer *aux_player = memnew(AnimationPlayer); EditorNode::get_singleton()->add_child(aux_player); - aux_player->add_animation("RESET", reset_anim); - aux_player->set_assigned_animation("RESET"); + aux_player->add_animation(SceneStringNames::get_singleton()->RESET, reset_anim); + aux_player->set_assigned_animation(SceneStringNames::get_singleton()->RESET); // Forcing the use of the original root because the scene where original player belongs may be not the active one Node *root = get_node(get_root()); Ref<AnimatedValuesBackup> old_values = aux_player->backup_animated_values(root); @@ -1744,7 +1801,7 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) { } bool AnimationPlayer::can_apply_reset() const { - return has_animation("RESET") && playback.assigned != StringName("RESET"); + return has_animation(SceneStringNames::get_singleton()->RESET) && playback.assigned != SceneStringNames::get_singleton()->RESET; } #endif diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index d9d88b5510..c4fc69f370 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -198,7 +198,7 @@ private: struct PlaybackData { AnimationData *from = nullptr; - float pos = 0.0; + double pos = 0.0; float speed_scale = 1.0; }; @@ -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..f2f69fc439 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -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, double p_time, double 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,17 +114,18 @@ 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); } -real_t AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, real_t p_time, bool p_seek, const Vector<StringName> &p_connections) { +double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections) { base_path = p_base_path; parent = p_parent; connections = p_connections; state = p_state; - real_t t = process(p_time, p_seek); + double t = process(p_time, p_seek); state = nullptr; parent = nullptr; @@ -136,13 +138,13 @@ real_t AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode void AnimationNode::make_invalid(const String &p_reason) { ERR_FAIL_COND(!state); state->valid = false; - if (state->invalid_reasons != String()) { + if (!state->invalid_reasons.is_empty()) { state->invalid_reasons += "\n"; } state->invalid_reasons += String::utf8("• ") + p_reason; } -real_t AnimationNode::blend_input(int p_input, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) { +double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) { ERR_FAIL_INDEX_V(p_input, inputs.size(), 0); ERR_FAIL_COND_V(!state, 0); @@ -161,7 +163,7 @@ real_t AnimationNode::blend_input(int p_input, real_t p_time, bool p_seek, real_ //inputs.write[p_input].last_pass = state->last_pass; real_t activity = 0.0; - real_t ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity); + double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity); Vector<AnimationTree::Activity> *activity_ptr = state->tree->input_activity_map.getptr(base_path); @@ -172,11 +174,11 @@ real_t AnimationNode::blend_input(int p_input, real_t p_time, bool p_seek, real_ return ret; } -real_t AnimationNode::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, bool p_optimize) { +double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) { return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_blend, p_filter, p_optimize); } -real_t AnimationNode::_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, bool p_optimize, real_t *r_max) { +double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) { ERR_FAIL_COND_V(!p_node.is_valid(), 0); ERR_FAIL_COND_V(!state, 0); @@ -312,7 +314,7 @@ void AnimationNode::add_input(const String &p_name) { //root nodes can't add inputs ERR_FAIL_COND(Object::cast_to<AnimationRootNode>(this) != nullptr); Input input; - ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1); + ERR_FAIL_COND(p_name.contains(".") || p_name.contains("/")); input.name = p_name; inputs.push_back(input); emit_changed(); @@ -320,14 +322,14 @@ void AnimationNode::add_input(const String &p_name) { void AnimationNode::set_input_name(int p_input, const String &p_name) { ERR_FAIL_INDEX(p_input, inputs.size()); - ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1); + ERR_FAIL_COND(p_name.contains(".") || p_name.contains("/")); inputs.write[p_input].name = p_name; emit_changed(); } void AnimationNode::remove_input(int p_index) { ERR_FAIL_INDEX(p_index, inputs.size()); - inputs.remove(p_index); + inputs.remove_at(p_index); emit_changed(); } @@ -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)); @@ -794,7 +796,7 @@ void AnimationTree::_clear_caches() { cache_valid = false; } -void AnimationTree::_process_graph(real_t p_delta) { +void AnimationTree::_process_graph(double p_delta) { _update_properties(); //if properties need updating, update them //check all tracks, see if they need modification @@ -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; } @@ -1138,8 +1239,7 @@ void AnimationTree::_process_graph(real_t p_delta) { continue; } - t->value = Math::lerp(t->value, value, blend); - + t->value = Math::lerp(t->value, value, (float)blend); #endif // _3D_DISABLED } break; case Animation::TYPE_VALUE: { @@ -1164,7 +1264,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 +1281,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); @@ -1235,10 +1335,10 @@ void AnimationTree::_process_graph(real_t p_delta) { t->playing = false; playing_caches.erase(t); } else { - real_t start_ofs = a->audio_track_get_key_start_offset(i, idx); + double start_ofs = a->audio_track_get_key_start_offset(i, idx); start_ofs += time - a->track_get_key_time(i, idx); - real_t end_ofs = a->audio_track_get_key_end_offset(i, idx); - real_t len = stream->get_length(); + double end_ofs = a->audio_track_get_key_end_offset(i, idx); + double len = stream->get_length(); if (start_ofs > len - end_ofs) { t->object->call("stop"); @@ -1264,7 +1364,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(); @@ -1274,9 +1374,9 @@ void AnimationTree::_process_graph(real_t p_delta) { t->playing = false; playing_caches.erase(t); } else { - real_t start_ofs = a->audio_track_get_key_start_offset(i, idx); - real_t end_ofs = a->audio_track_get_key_end_offset(i, idx); - real_t len = stream->get_length(); + double start_ofs = a->audio_track_get_key_start_offset(i, idx); + double end_ofs = a->audio_track_get_key_end_offset(i, idx); + double len = stream->get_length(); t->object->call("set_stream", stream); t->object->call("play", start_ofs); @@ -1292,14 +1392,22 @@ 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; + double len = t->start > time ? (a->get_length() - t->start) + time : time - t->start; if (len > t->len) { stop = true; @@ -1331,7 +1439,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 +1455,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 + double 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 +1483,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..705ee91c76 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -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 { @@ -82,7 +83,7 @@ public: Vector<real_t> blends; State *state = nullptr; - real_t _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, real_t p_time, bool p_seek, const Vector<StringName> &p_connections); + double _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections); //all this is temporary StringName base_path; @@ -95,12 +96,13 @@ public: Array _get_filters() const; void _set_filters(const Array &p_filters); friend class AnimationNodeBlendTree; - 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); + double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr); protected: - void blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend); - 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 blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged = 0); + double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); + double blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); + void make_invalid(const String &p_reason); static void _bind_methods(); @@ -232,8 +234,8 @@ private: struct TrackCacheAudio : public TrackCache { bool playing = false; - real_t start = 0.0; - real_t len = 0.0; + double start = 0.0; + double len = 0.0; TrackCacheAudio() { type = Animation::TYPE_AUDIO; @@ -263,7 +265,7 @@ private: void _clear_caches(); bool _update_caches(AnimationPlayer *player); - void _process_graph(real_t p_delta); + void _process_graph(double p_delta); uint64_t setup_pass = 1; uint64_t process_pass = 1; diff --git a/scene/animation/easing_equations.h b/scene/animation/easing_equations.h index c38d083b7f..6d246c7a93 100644 --- a/scene/animation/easing_equations.h +++ b/scene/animation/easing_equations.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp index 770996820d..0d44687588 100644 --- a/scene/animation/root_motion_view.cpp +++ b/scene/animation/root_motion_view.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/root_motion_view.h b/scene/animation/root_motion_view.h index d64c8bc675..e8b141c1fd 100644 --- a/scene/animation/root_motion_view.h +++ b/scene/animation/root_motion_view.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index da933ae02d..a2fed718be 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -249,8 +249,6 @@ bool Tween::custom_step(float p_delta) { } bool Tween::step(float p_delta) { - ERR_FAIL_COND_V_MSG(tweeners.is_empty(), false, "Tween started, but has no Tweeners."); - if (dead) { return false; } @@ -260,10 +258,8 @@ bool Tween::step(float p_delta) { } if (is_bound) { - Object *bound_instance = ObjectDB::get_instance(bound_node); - if (bound_instance) { - Node *bound_node = Object::cast_to<Node>(bound_instance); - // This can't by anything else than Node, so we can omit checking if casting succeeded. + Node *bound_node = get_bound_node(); + if (bound_node) { if (!bound_node->is_inside_tree()) { return true; } @@ -273,6 +269,7 @@ bool Tween::step(float p_delta) { } if (!started) { + ERR_FAIL_COND_V_MSG(tweeners.is_empty(), false, "Tween started, but has no Tweeners."); current_step = 0; loops_done = 0; start_tweeners(); @@ -286,6 +283,10 @@ bool Tween::step(float p_delta) { float step_delta = rem_delta; step_active = false; +#ifdef DEBUG_ENABLED + float prev_delta = rem_delta; +#endif + for (Ref<Tweener> &tweener : tweeners.write[current_step]) { // Modified inside Tweener.step(). float temp_delta = rem_delta; @@ -315,21 +316,34 @@ bool Tween::step(float p_delta) { start_tweeners(); } } + +#ifdef DEBUG_ENABLED + if (Math::is_equal_approx(rem_delta, prev_delta) && running && loops <= 0) { + ERR_FAIL_V_MSG(false, "Infinite loop detected. Check set_loops() description for more info."); + } +#endif } return true; } -bool Tween::should_pause() { +bool Tween::can_process(bool p_tree_paused) const { if (is_bound && pause_mode == TWEEN_PAUSE_BOUND) { - Object *bound_instance = ObjectDB::get_instance(bound_node); - if (bound_instance) { - Node *bound_node = Object::cast_to<Node>(bound_instance); - return !bound_node->can_process(); + Node *bound_node = get_bound_node(); + if (bound_node) { + return bound_node->can_process(); } } - return pause_mode != TWEEN_PAUSE_PROCESS; + return !p_tree_paused || pause_mode == TWEEN_PAUSE_PROCESS; +} + +Node *Tween::get_bound_node() const { + if (is_bound) { + return Object::cast_to<Node>(ObjectDB::get_instance(bound_node)); + } else { + return nullptr; + } } real_t Tween::run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t p_time, real_t p_initial, real_t p_delta, real_t p_duration) { diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 6a48d332b8..5b0745b2b3 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -97,7 +97,7 @@ public: private: TweenProcessMode process_mode = TweenProcessMode::TWEEN_PROCESS_IDLE; - TweenPauseMode pause_mode = TweenPauseMode::TWEEN_PAUSE_STOP; + TweenPauseMode pause_mode = TweenPauseMode::TWEEN_PAUSE_BOUND; TransitionType default_transition = TransitionType::TRANS_LINEAR; EaseType default_ease = EaseType::EASE_IN_OUT; ObjectID bound_node; @@ -164,7 +164,8 @@ public: Variant calculate_delta_value(Variant p_intial_val, Variant p_final_val); bool step(float p_delta); - bool should_pause(); + bool can_process(bool p_tree_paused) const; + Node *get_bound_node() const; Tween() {} }; |