summaryrefslogtreecommitdiff
path: root/scene/animation/animation_player.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/animation/animation_player.cpp')
-rw-r--r--scene/animation/animation_player.cpp179
1 files changed, 118 insertions, 61 deletions
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 26caf826a7..128c6cae8b 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 */
@@ -174,9 +174,9 @@ void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> anim_names;
for (const KeyValue<StringName, AnimationData> &E : animation_set) {
- anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E.key), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+ anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E.key), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
if (E.value.next != StringName()) {
- anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
}
@@ -186,7 +186,7 @@ void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(E);
}
- p_list->push_back(PropertyInfo(Variant::ARRAY, "blend_times", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "blend_times", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
void AnimationPlayer::advance(float p_time) {
@@ -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
@@ -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
@@ -1813,7 +1870,7 @@ void AnimationPlayer::_bind_methods() {
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, "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_NOEDITOR), "set_autoplay", "get_autoplay");
+ 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");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_length", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_current_animation_length");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "current_animation_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_current_animation_position");