diff options
Diffstat (limited to 'scene/animation/animation_mixer.cpp')
-rw-r--r-- | scene/animation/animation_mixer.cpp | 111 |
1 files changed, 68 insertions, 43 deletions
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index 7080a5338b..e600de6b8b 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -35,13 +35,13 @@ #include "core/config/project_settings.h" #include "scene/animation/animation_player.h" #include "scene/resources/animation.h" -#include "scene/scene_string_names.h" #include "servers/audio/audio_stream.h" #ifndef _3D_DISABLED #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/node_3d.h" #include "scene/3d/skeleton_3d.h" +#include "scene/3d/skeleton_modifier_3d.h" #endif // _3D_DISABLED #ifdef TOOLS_ENABLED @@ -300,7 +300,7 @@ Error AnimationMixer::add_animation_library(const StringName &p_name, const Ref< ald.library->connect(SNAME("animation_added"), callable_mp(this, &AnimationMixer::_animation_added).bind(p_name)); ald.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationMixer::_animation_removed).bind(p_name)); ald.library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationMixer::_animation_renamed).bind(p_name)); - ald.library->connect(SNAME("animation_changed"), callable_mp(this, &AnimationMixer::_animation_changed)); + ald.library->connect(SceneStringName(animation_changed), callable_mp(this, &AnimationMixer::_animation_changed)); _animation_set_cache_update(); @@ -324,7 +324,7 @@ void AnimationMixer::remove_animation_library(const StringName &p_name) { animation_libraries[at_pos].library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationMixer::_animation_added)); animation_libraries[at_pos].library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationMixer::_animation_removed)); animation_libraries[at_pos].library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationMixer::_animation_renamed)); - animation_libraries[at_pos].library->disconnect(SNAME("animation_changed"), callable_mp(this, &AnimationMixer::_animation_changed)); + animation_libraries[at_pos].library->disconnect(SceneStringName(animation_changed), callable_mp(this, &AnimationMixer::_animation_changed)); animation_libraries.remove_at(at_pos); _animation_set_cache_update(); @@ -484,10 +484,6 @@ void AnimationMixer::set_callback_mode_process(AnimationCallbackModeProcess p_mo if (was_active) { set_active(true); } - -#ifdef TOOLS_ENABLED - emit_signal(SNAME("mixer_updated")); -#endif // TOOLS_ENABLED } AnimationMixer::AnimationCallbackModeProcess AnimationMixer::get_callback_mode_process() const { @@ -496,9 +492,7 @@ AnimationMixer::AnimationCallbackModeProcess AnimationMixer::get_callback_mode_p void AnimationMixer::set_callback_mode_method(AnimationCallbackModeMethod p_mode) { callback_mode_method = p_mode; -#ifdef TOOLS_ENABLED emit_signal(SNAME("mixer_updated")); -#endif // TOOLS_ENABLED } AnimationMixer::AnimationCallbackModeMethod AnimationMixer::get_callback_mode_method() const { @@ -507,9 +501,8 @@ AnimationMixer::AnimationCallbackModeMethod AnimationMixer::get_callback_mode_me void AnimationMixer::set_callback_mode_discrete(AnimationCallbackModeDiscrete p_mode) { callback_mode_discrete = p_mode; -#ifdef TOOLS_ENABLED + _clear_caches(); emit_signal(SNAME("mixer_updated")); -#endif // TOOLS_ENABLED } AnimationMixer::AnimationCallbackModeDiscrete AnimationMixer::get_callback_mode_discrete() const { @@ -635,9 +628,9 @@ bool AnimationMixer::_update_caches() { #endif Ref<Animation> reset_anim; - bool has_reset_anim = has_animation(SceneStringNames::get_singleton()->RESET); + bool has_reset_anim = has_animation(SceneStringName(RESET)); if (has_reset_anim) { - reset_anim = get_animation(SceneStringNames::get_singleton()->RESET); + reset_anim = get_animation(SceneStringName(RESET)); } for (const StringName &E : sname) { Ref<Animation> anim = get_animation(E); @@ -696,14 +689,24 @@ bool AnimationMixer::_update_caches() { track_value->init_value = anim->track_get_key_value(i, 0); track_value->init_value.zero(); + track_value->is_init = false; + // Can't interpolate them, need to convert. track_value->is_variant_interpolatable = Animation::is_variant_interpolatable(track_value->init_value); // If there is a Reset Animation, it takes precedence by overwriting. if (has_reset_anim) { int rt = reset_anim->find_track(path, track_src_type); - if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { - track_value->init_value = track_src_type == Animation::TYPE_VALUE ? reset_anim->track_get_key_value(rt, 0) : (reset_anim->track_get_key_value(rt, 0).operator Array())[0]; + if (rt >= 0) { + if (track_src_type == Animation::TYPE_VALUE) { + if (reset_anim->track_get_key_count(rt) > 0) { + track_value->init_value = reset_anim->track_get_key_value(rt, 0); + } + } else { + if (reset_anim->track_get_key_count(rt) > 0) { + track_value->init_value = (reset_anim->track_get_key_value(rt, 0).operator Array())[0]; + } + } } } @@ -877,7 +880,7 @@ bool AnimationMixer::_update_caches() { if (track_value->init_value.is_string() && anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE) { WARN_PRINT_ONCE_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' blends String types. This is an experimental algorithm."); } - track_value->is_using_angle |= anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE; + track_value->is_using_angle = track_value->is_using_angle || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE; } if (check_angle_interpolation && (was_using_angle != track_value->is_using_angle)) { WARN_PRINT_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' has different interpolation types for rotation between some animations which may be blended together. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value."); @@ -930,6 +933,7 @@ void AnimationMixer::_process_animation(double p_delta, bool p_update_only) { _blend_process(p_delta, p_update_only); _blend_apply(); _blend_post_process(); + emit_signal(SNAME("mixer_applied")); }; clear_animation_instances(); } @@ -1002,6 +1006,7 @@ void AnimationMixer::_blend_init() { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); t->value = Animation::cast_to_blendwise(t->init_value); t->element_size = t->init_value.is_string() ? (real_t)(t->init_value.operator String()).length() : 0; + t->use_continuous = false; t->use_discrete = false; } break; case Animation::TYPE_AUDIO: { @@ -1421,6 +1426,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { bool is_discrete = is_value && a->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE; bool force_continuous = callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS; if (t->is_variant_interpolatable && (!is_discrete || force_continuous)) { + t->use_continuous = true; Variant value = is_value ? a->value_track_interpolate(i, time, is_discrete && force_continuous ? backward : false) : Variant(a->bezier_track_interpolate(i, time)); value = post_process_key_value(a, i, value, t->object_id); if (value == Variant()) { @@ -1456,12 +1462,12 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { t->value = Animation::blend_variant(t->value, value, blend); } } else { - t->use_discrete = true; if (seeked) { int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, true); if (idx < 0) { continue; } + t->use_discrete = true; Variant value = a->track_get_key_value(i, idx); value = post_process_key_value(a, i, value, t->object_id); Object *t_obj = ObjectDB::get_instance(t->object_id); @@ -1472,6 +1478,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { List<int> indices; a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag); for (int &F : indices) { + t->use_discrete = true; Variant value = a->track_get_key_value(i, F); value = post_process_key_value(a, i, value, t->object_id); Object *t_obj = ObjectDB::get_instance(t->object_id); @@ -1596,7 +1603,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } } break; case Animation::TYPE_ANIMATION: { - if (p_update_only || Math::is_zero_approx(blend)) { + if (Math::is_zero_approx(blend)) { continue; } TrackCacheAnimation *t = static_cast<TrackCacheAnimation *>(track); @@ -1610,7 +1617,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } if (seeked) { // Seek. - int idx = a->track_find_key(i, time, is_external_seeking ? Animation::FIND_MODE_NEAREST : Animation::FIND_MODE_EXACT, true); + int idx = a->track_find_key(i, time, Animation::FIND_MODE_NEAREST, true); if (idx < 0) { continue; } @@ -1623,10 +1630,13 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { double at_anim_pos = 0.0; switch (anim->get_loop_mode()) { case Animation::LOOP_NONE: { - at_anim_pos = MAX((double)anim->get_length(), time - pos); //seek to end + if (!is_external_seeking && ((!backward && time >= pos + (double)anim->get_length()) || (backward && time <= pos))) { + continue; // Do nothing if current time is outside of length when started. + } + at_anim_pos = MIN((double)anim->get_length(), time - pos); // Seek to end. } break; case Animation::LOOP_LINEAR: { - at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); //seek to loop + at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); // Seek to loop. } break; case Animation::LOOP_PINGPONG: { at_anim_pos = Math::pingpong(time - pos, (double)a->get_length()); @@ -1634,14 +1644,14 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { default: break; } - if (player2->is_playing() || seeked) { - player2->seek(at_anim_pos); + if (player2->is_playing() || !is_external_seeking) { + player2->seek(at_anim_pos, false, p_update_only); player2->play(anim_name); t->playing = true; playing_caches.insert(t); } else { player2->set_assigned_animation(anim_name); - player2->seek(at_anim_pos, true); + player2->seek(at_anim_pos, true, p_update_only); } } else { // Find stuff to play. @@ -1673,7 +1683,8 @@ void AnimationMixer::_blend_apply() { // Finally, set the tracks. for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) { TrackCache *track = K.value; - if (!deterministic && Math::is_zero_approx(track->total_weight)) { + bool is_zero_amount = Math::is_zero_approx(track->total_weight); + if (!deterministic && is_zero_amount) { continue; } switch (track->type) { @@ -1733,10 +1744,24 @@ void AnimationMixer::_blend_apply() { case Animation::TYPE_VALUE: { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); - if (!t->is_variant_interpolatable || (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT && t->use_discrete)) { + if (t->use_discrete && !t->use_continuous) { + t->is_init = true; // If only disctere value is applied, no more RESET. + } + + if ((t->is_init && (is_zero_amount || !t->use_continuous)) || + (callback_mode_discrete != ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS && + !is_zero_amount && + callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT && + t->use_discrete)) { break; // Don't overwrite the value set by UPDATE_DISCRETE. } + if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS) { + t->is_init = false; // Always update in Force Continuous. + } else { + t->is_init = !t->use_continuous; // If there is no Continuous in non-Force Continuous type, it means RESET. + } + // Trim unused elements if init array/string is not blended. if (t->value.is_array()) { int actual_blended_size = (int)Math::round(Math::abs(t->element_size.operator real_t())); @@ -1916,7 +1941,7 @@ bool AnimationMixer::is_reset_on_save_enabled() const { } bool AnimationMixer::can_apply_reset() const { - return has_animation(SceneStringNames::get_singleton()->RESET); + return has_animation(SceneStringName(RESET)); } void AnimationMixer::_build_backup_track_cache() { @@ -1975,6 +2000,7 @@ void AnimationMixer::_build_backup_track_cache() { if (t_obj) { t->value = Animation::cast_to_blendwise(t_obj->get_indexed(t->subpath)); } + t->use_continuous = true; t->use_discrete = false; if (t->init_value.is_array()) { t->element_size = MAX(t->element_size.operator int(), (t->value.operator Array()).size()); @@ -2002,7 +2028,7 @@ Ref<AnimatedValuesBackup> AnimationMixer::make_backup() { Ref<AnimatedValuesBackup> backup; backup.instantiate(); - Ref<Animation> reset_anim = animation_set[SceneStringNames::get_singleton()->RESET].animation; + Ref<Animation> reset_anim = animation_set[SceneStringName(RESET)].animation; ERR_FAIL_COND_V(reset_anim.is_null(), Ref<AnimatedValuesBackup>()); _blend_init(); @@ -2011,7 +2037,7 @@ Ref<AnimatedValuesBackup> AnimationMixer::make_backup() { pi.delta = 0; pi.seeked = true; pi.weight = 1.0; - make_animation_instance(SceneStringNames::get_singleton()->RESET, pi); + make_animation_instance(SceneStringName(RESET), pi); _build_backup_track_cache(); backup->set_data(track_cache); @@ -2023,7 +2049,7 @@ Ref<AnimatedValuesBackup> AnimationMixer::make_backup() { void AnimationMixer::reset() { ERR_FAIL_COND(!can_apply_reset()); - Ref<Animation> reset_anim = animation_set[SceneStringNames::get_singleton()->RESET].animation; + Ref<Animation> reset_anim = animation_set[SceneStringName(RESET)].animation; ERR_FAIL_COND(reset_anim.is_null()); Node *root_node_object = get_node_or_null(root_node); @@ -2033,11 +2059,11 @@ void AnimationMixer::reset() { root_node_object->add_child(aux_player); Ref<AnimationLibrary> al; al.instantiate(); - al->add_animation(SceneStringNames::get_singleton()->RESET, reset_anim); + al->add_animation(SceneStringName(RESET), reset_anim); aux_player->set_reset_on_save_enabled(false); aux_player->set_root_node(aux_player->get_path_to(root_node_object)); aux_player->add_animation_library("", al); - aux_player->set_assigned_animation(SceneStringNames::get_singleton()->RESET); + aux_player->set_assigned_animation(SceneStringName(RESET)); aux_player->seek(0.0f, true); aux_player->queue_free(); } @@ -2057,7 +2083,7 @@ Ref<AnimatedValuesBackup> AnimationMixer::apply_reset(bool p_user_initiated) { } ERR_FAIL_COND_V(!can_apply_reset(), Ref<AnimatedValuesBackup>()); - Ref<Animation> reset_anim = animation_set[SceneStringNames::get_singleton()->RESET].animation; + Ref<Animation> reset_anim = animation_set[SceneStringName(RESET)].animation; ERR_FAIL_COND_V(reset_anim.is_null(), Ref<AnimatedValuesBackup>()); Ref<AnimatedValuesBackup> backup_current = make_backup(); @@ -2082,7 +2108,7 @@ Ref<AnimatedValuesBackup> AnimationMixer::apply_reset(bool p_user_initiated) { void AnimationMixer::capture(const StringName &p_name, double p_duration, Tween::TransitionType p_trans_type, Tween::EaseType p_ease_type) { ERR_FAIL_COND(!active); ERR_FAIL_COND(!has_animation(p_name)); - ERR_FAIL_COND(Math::is_zero_approx(p_duration)); + ERR_FAIL_COND(p_duration <= 0); Ref<Animation> reference_animation = get_animation(p_name); if (!cache_valid) { @@ -2228,19 +2254,16 @@ void AnimationMixer::_bind_methods() { ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationMixer::advance); GDVIRTUAL_BIND(_post_process_key_value, "animation", "track", "value", "object_id", "object_sub_idx"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deterministic"), "set_deterministic", "is_deterministic"); + /* ---- Capture feature ---- */ + ClassDB::bind_method(D_METHOD("capture", "name", "duration", "trans_type", "ease_type"), &AnimationMixer::capture, DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN)); /* ---- Reset on save ---- */ ClassDB::bind_method(D_METHOD("set_reset_on_save_enabled", "enabled"), &AnimationMixer::set_reset_on_save_enabled); ClassDB::bind_method(D_METHOD("is_reset_on_save_enabled"), &AnimationMixer::is_reset_on_save_enabled); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reset_on_save", PROPERTY_HINT_NONE, ""), "set_reset_on_save_enabled", "is_reset_on_save_enabled"); - - /* ---- Capture feature ---- */ - ClassDB::bind_method(D_METHOD("capture", "name", "duration", "trans_type", "ease_type"), &AnimationMixer::capture, DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN)); - - ADD_SIGNAL(MethodInfo("mixer_updated")); // For updating dummy player. + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deterministic"), "set_deterministic", "is_deterministic"); + 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::NODE_PATH, "root_node"), "set_root_node", "get_root_node"); ADD_GROUP("Root Motion", "root_motion_"); @@ -2270,13 +2293,15 @@ void AnimationMixer::_bind_methods() { ADD_SIGNAL(MethodInfo(SNAME("animation_finished"), PropertyInfo(Variant::STRING_NAME, "anim_name"))); ADD_SIGNAL(MethodInfo(SNAME("animation_started"), PropertyInfo(Variant::STRING_NAME, "anim_name"))); ADD_SIGNAL(MethodInfo(SNAME("caches_cleared"))); + ADD_SIGNAL(MethodInfo(SNAME("mixer_applied"))); + ADD_SIGNAL(MethodInfo(SNAME("mixer_updated"))); // For updating dummy player. ClassDB::bind_method(D_METHOD("_reset"), &AnimationMixer::reset); ClassDB::bind_method(D_METHOD("_restore", "backup"), &AnimationMixer::restore); } AnimationMixer::AnimationMixer() { - root_node = SceneStringNames::get_singleton()->path_pp; + root_node = SceneStringName(path_pp); } AnimationMixer::~AnimationMixer() { |