summaryrefslogtreecommitdiff
path: root/scene/animation/animation_tree.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/animation/animation_tree.cpp')
-rw-r--r--scene/animation/animation_tree.cpp318
1 files changed, 217 insertions, 101 deletions
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();