summaryrefslogtreecommitdiffstats
path: root/scene/animation/animation_mixer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/animation/animation_mixer.cpp')
-rw-r--r--scene/animation/animation_mixer.cpp222
1 files changed, 148 insertions, 74 deletions
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index fb17dae832..bba3dc6d7d 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -445,7 +445,7 @@ bool AnimationMixer::is_active() const {
void AnimationMixer::set_root_node(const NodePath &p_path) {
root_node = p_path;
- clear_caches();
+ _clear_caches();
}
NodePath AnimationMixer::get_root_node() const {
@@ -454,7 +454,7 @@ NodePath AnimationMixer::get_root_node() const {
void AnimationMixer::set_deterministic(bool p_deterministic) {
deterministic = p_deterministic;
- clear_caches();
+ _clear_caches();
}
bool AnimationMixer::is_deterministic() const {
@@ -563,7 +563,7 @@ void AnimationMixer::_clear_audio_streams() {
void AnimationMixer::_clear_playing_caches() {
for (const TrackCache *E : playing_caches) {
if (ObjectDB::get_instance(E->object_id)) {
- E->object->call(SNAME("stop"));
+ E->object->call(SNAME("stop"), true);
}
}
playing_caches.clear();
@@ -667,6 +667,7 @@ bool AnimationMixer::_update_caches() {
track_value->init_value = reset_anim->track_get_key_value(rt, 0);
}
}
+
} break;
case Animation::TYPE_POSITION_3D:
case Animation::TYPE_ROTATION_3D:
@@ -812,6 +813,7 @@ bool AnimationMixer::_update_caches() {
track_bezier->init_value = (reset_anim->track_get_key_value(rt, 0).operator Array())[0];
}
}
+
} break;
case Animation::TYPE_AUDIO: {
TrackCacheAudio *track_audio = memnew(TrackCacheAudio);
@@ -868,43 +870,26 @@ bool AnimationMixer::_update_caches() {
track_value->is_continuous |= anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE;
track_value->is_using_angle |= anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE;
- // TODO: Currently, misc type cannot be blended. In the future,
- // it should have a separate blend weight, just as bool is converted to 0 and 1.
+ // TODO: Currently, misc type cannot be blended.
+ // In the future, it should have a separate blend weight, just as bool is converted to 0 and 1.
// Then, it should provide the correct precedence value.
+ bool skip_update_mode_warning = false;
if (track_value->is_continuous) {
- switch (track_value->init_value.get_type()) {
- case Variant::NIL:
- case Variant::STRING_NAME:
- case Variant::NODE_PATH:
- case Variant::RID:
- case Variant::OBJECT:
- case Variant::CALLABLE:
- case Variant::SIGNAL:
- case Variant::DICTIONARY:
- case Variant::ARRAY:
- case Variant::PACKED_BYTE_ARRAY:
- case Variant::PACKED_INT32_ARRAY:
- case Variant::PACKED_INT64_ARRAY:
- case Variant::PACKED_FLOAT32_ARRAY:
- case Variant::PACKED_FLOAT64_ARRAY:
- case Variant::PACKED_STRING_ARRAY:
- case Variant::PACKED_VECTOR2_ARRAY:
- case Variant::PACKED_VECTOR3_ARRAY:
- case Variant::PACKED_COLOR_ARRAY: {
- WARN_PRINT_ONCE_ED("AnimationMixer: '" + String(E) + "', Value Track: '" + String(path) + "' uses a non-numeric type as key value with UpdateMode.UPDATE_CONTINUOUS. This will not be blended correctly, so it is forced to UpdateMode.UPDATE_DISCRETE.");
- track_value->is_continuous = false;
- break;
- }
- default: {
- }
+ if (!Animation::is_variant_interpolatable(track_value->init_value)) {
+ WARN_PRINT_ONCE_ED("AnimationMixer: '" + String(E) + "', Value Track: '" + String(path) + "' uses a non-numeric type as key value with UpdateMode.UPDATE_CONTINUOUS. This will not be blended correctly, so it is forced to UpdateMode.UPDATE_DISCRETE.");
+ track_value->is_continuous = false;
+ skip_update_mode_warning = true;
+ }
+ if (track_value->init_value.is_string()) {
+ WARN_PRINT_ONCE_ED("AnimationMixer: '" + String(E) + "', Value Track: '" + String(path) + "' blends String types. This is an experimental algorithm.");
}
}
- if (was_continuous != track_value->is_continuous) {
- WARN_PRINT_ONCE_ED("Value Track: " + String(path) + " has different update modes between some animations may be blended. Blending prioritizes UpdateMode.UPDATE_CONTINUOUS, so the process treat UpdateMode.UPDATE_DISCRETE as UpdateMode.UPDATE_CONTINUOUS with InterpolationType.INTERPOLATION_NEAREST.");
+ if (!skip_update_mode_warning && was_continuous != track_value->is_continuous) {
+ WARN_PRINT_ONCE_ED("AnimationMixer: '" + String(E) + "', Value Track: '" + String(path) + "' has different update modes between some animations which may be blended together. Blending prioritizes UpdateMode.UPDATE_CONTINUOUS, so the process treats UpdateMode.UPDATE_DISCRETE as UpdateMode.UPDATE_CONTINUOUS with InterpolationType.INTERPOLATION_NEAREST.");
}
if (was_using_angle != track_value->is_using_angle) {
- WARN_PRINT_ONCE_ED("Value Track: " + String(path) + " has different interpolation types for rotation between some animations may be blended. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value.");
+ WARN_PRINT_ONCE_ED("AnimationMixer: '" + 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.");
}
}
@@ -950,9 +935,7 @@ bool AnimationMixer::_update_caches() {
void AnimationMixer::_process_animation(double p_delta, bool p_update_only) {
_blend_init();
if (_blend_pre_process(p_delta, track_count, track_map)) {
- if (!deterministic) {
- _blend_calc_total_weight();
- }
+ _blend_calc_total_weight();
_blend_process(p_delta, p_update_only);
_blend_apply();
_blend_post_process();
@@ -1024,7 +1007,8 @@ void AnimationMixer::_blend_init() {
} break;
case Animation::TYPE_VALUE: {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
- t->value = t->init_value;
+ 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;
} break;
case Animation::TYPE_BEZIER: {
TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);
@@ -1111,7 +1095,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
ERR_CONTINUE(blend_idx < 0 || blend_idx >= track_count);
real_t blend = blend_idx < track_weights.size() ? track_weights[blend_idx] * weight : weight;
if (!deterministic) {
- // If undeterministic, do normalization.
+ // If non-deterministic, do normalization.
// It would be better to make this if statement outside the for loop, but come here since too much code...
if (Math::is_zero_approx(track->total_weight)) {
continue;
@@ -1434,13 +1418,15 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
}
t->value = Math::fposmod(rot_a + (rot_b - rot_init) * (float)blend, (float)Math_TAU);
} else {
- if (t->init_value.get_type() == Variant::BOOL) {
- value = Animation::subtract_variant(value.operator real_t(), t->init_value.operator real_t());
- t->value = Animation::blend_variant(t->value.operator real_t(), value.operator real_t(), blend);
- } else {
- value = Animation::subtract_variant(value, t->init_value);
- t->value = Animation::blend_variant(t->value, value, blend);
+ value = Animation::cast_to_blendwise(value);
+ if (t->init_value.is_array()) {
+ t->element_size = MAX(t->element_size.operator int(), (value.operator Array()).size());
+ } else if (t->init_value.is_string()) {
+ real_t length = Animation::subtract_variant((real_t)(value.operator Array()).size(), (real_t)(t->init_value.operator String()).length());
+ t->element_size = Animation::blend_variant(t->element_size, length, blend);
}
+ value = Animation::subtract_variant(value, Animation::cast_to_blendwise(t->init_value));
+ t->value = Animation::blend_variant(t->value, value, blend);
}
} else {
if (seeked) {
@@ -1710,10 +1696,23 @@ void AnimationMixer::_blend_apply() {
break; // Don't overwrite the value set by UPDATE_DISCRETE.
}
- if (t->init_value.get_type() == Variant::BOOL) {
- t->object->set_indexed(t->subpath, t->value.operator real_t() >= 0.5);
- } else {
- t->object->set_indexed(t->subpath, t->value);
+ // 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()));
+ if (actual_blended_size < (t->value.operator Array()).size()) {
+ real_t abs_weight = Math::abs(track->total_weight);
+ if (abs_weight >= 1.0) {
+ (t->value.operator Array()).resize(actual_blended_size);
+ } else if (t->init_value.is_string()) {
+ (t->value.operator Array()).resize(Animation::interpolate_variant((t->init_value.operator String()).length(), actual_blended_size, abs_weight));
+ }
+ }
+ }
+
+ // t->object isn't safe here, get instance from id (GH-85365).
+ Object *obj = ObjectDB::get_instance(t->object_id);
+ if (obj) {
+ obj->set_indexed(t->subpath, Animation::cast_from_blendwise(t->value, t->init_value.get_type()));
}
} break;
@@ -1789,7 +1788,7 @@ void AnimationMixer::_blend_apply() {
void AnimationMixer::_call_object(Object *p_object, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred) {
// Separate function to use alloca() more efficiently
- const Variant **argptrs = (const Variant **)alloca(sizeof(const Variant **) * p_params.size());
+ const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * p_params.size());
const Variant *args = p_params.ptr();
uint32_t argcount = p_params.size();
for (uint32_t i = 0; i < argcount; i++) {
@@ -1874,7 +1873,6 @@ bool AnimationMixer::is_reset_on_save_enabled() const {
return reset_on_save;
}
-#ifdef TOOLS_ENABLED
bool AnimationMixer::can_apply_reset() const {
return has_animation(SceneStringNames::get_singleton()->RESET);
}
@@ -1935,7 +1933,6 @@ void AnimationMixer::_build_backup_track_cache() {
if (asp) {
t->object->call(SNAME("set_stream"), Ref<AudioStream>());
}
- track = memnew(TrackCache); // Make disable this track cache.
} break;
default: {
} // The rest don't matter.
@@ -1965,29 +1962,6 @@ Ref<AnimatedValuesBackup> AnimationMixer::make_backup() {
return backup;
}
-Ref<AnimatedValuesBackup> AnimationMixer::apply_reset(bool p_user_initiated) {
- if (!p_user_initiated && dummy) {
- return Ref<AnimatedValuesBackup>();
- }
- ERR_FAIL_COND_V(!can_apply_reset(), Ref<AnimatedValuesBackup>());
-
- Ref<Animation> reset_anim = animation_set[SceneStringNames::get_singleton()->RESET].animation;
- ERR_FAIL_COND_V(reset_anim.is_null(), Ref<AnimatedValuesBackup>());
-
- Ref<AnimatedValuesBackup> backup_current = make_backup();
- if (p_user_initiated) {
- EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
- ur->create_action(TTR("Animation Apply Reset"));
- ur->add_do_method(this, "_reset");
- ur->add_undo_method(this, "_restore", backup_current);
- ur->commit_action();
- } else {
- reset();
- }
-
- return backup_current;
-}
-
void AnimationMixer::reset() {
ERR_FAIL_COND(!can_apply_reset());
@@ -2016,6 +1990,30 @@ void AnimationMixer::restore(const Ref<AnimatedValuesBackup> &p_backup) {
track_cache = HashMap<NodePath, AnimationMixer::TrackCache *>();
cache_valid = false;
}
+
+#ifdef TOOLS_ENABLED
+Ref<AnimatedValuesBackup> AnimationMixer::apply_reset(bool p_user_initiated) {
+ if (!p_user_initiated && dummy) {
+ return Ref<AnimatedValuesBackup>();
+ }
+ ERR_FAIL_COND_V(!can_apply_reset(), Ref<AnimatedValuesBackup>());
+
+ Ref<Animation> reset_anim = animation_set[SceneStringNames::get_singleton()->RESET].animation;
+ ERR_FAIL_COND_V(reset_anim.is_null(), Ref<AnimatedValuesBackup>());
+
+ Ref<AnimatedValuesBackup> backup_current = make_backup();
+ if (p_user_initiated) {
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
+ ur->create_action(TTR("Animation Apply Reset"));
+ ur->add_do_method(this, "_reset");
+ ur->add_undo_method(this, "_restore", backup_current);
+ ur->commit_action();
+ } else {
+ reset();
+ }
+
+ return backup_current;
+}
#endif // TOOLS_ENABLED
/* -------------------------------------------- */
@@ -2134,6 +2132,9 @@ 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")));
+
+ ClassDB::bind_method(D_METHOD("_reset"), &AnimationMixer::reset);
+ ClassDB::bind_method(D_METHOD("_restore", "backup"), &AnimationMixer::restore);
}
AnimationMixer::AnimationMixer() {
@@ -2142,3 +2143,76 @@ AnimationMixer::AnimationMixer() {
AnimationMixer::~AnimationMixer() {
}
+
+void AnimatedValuesBackup::set_data(const HashMap<NodePath, AnimationMixer::TrackCache *> p_data) {
+ clear_data();
+
+ for (const KeyValue<NodePath, AnimationMixer::TrackCache *> &E : p_data) {
+ AnimationMixer::TrackCache *track = get_cache_copy(E.value);
+ if (!track) {
+ continue; // Some types of tracks do not get a copy and must be ignored.
+ }
+
+ data.insert(E.key, track);
+ }
+}
+
+HashMap<NodePath, AnimationMixer::TrackCache *> AnimatedValuesBackup::get_data() const {
+ HashMap<NodePath, AnimationMixer::TrackCache *> ret;
+ for (const KeyValue<NodePath, AnimationMixer::TrackCache *> &E : data) {
+ AnimationMixer::TrackCache *track = get_cache_copy(E.value);
+ ERR_CONTINUE(!track); // Backup shouldn't contain tracks that cannot be copied, this is a mistake.
+
+ ret.insert(E.key, track);
+ }
+ return ret;
+}
+
+void AnimatedValuesBackup::clear_data() {
+ for (KeyValue<NodePath, AnimationMixer::TrackCache *> &K : data) {
+ memdelete(K.value);
+ }
+ data.clear();
+}
+
+AnimationMixer::TrackCache *AnimatedValuesBackup::get_cache_copy(AnimationMixer::TrackCache *p_cache) const {
+ switch (p_cache->type) {
+ case Animation::TYPE_VALUE: {
+ AnimationMixer::TrackCacheValue *src = static_cast<AnimationMixer::TrackCacheValue *>(p_cache);
+ AnimationMixer::TrackCacheValue *tc = memnew(AnimationMixer::TrackCacheValue(*src));
+ return tc;
+ }
+
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D: {
+ AnimationMixer::TrackCacheTransform *src = static_cast<AnimationMixer::TrackCacheTransform *>(p_cache);
+ AnimationMixer::TrackCacheTransform *tc = memnew(AnimationMixer::TrackCacheTransform(*src));
+ return tc;
+ }
+
+ case Animation::TYPE_BLEND_SHAPE: {
+ AnimationMixer::TrackCacheBlendShape *src = static_cast<AnimationMixer::TrackCacheBlendShape *>(p_cache);
+ AnimationMixer::TrackCacheBlendShape *tc = memnew(AnimationMixer::TrackCacheBlendShape(*src));
+ return tc;
+ }
+
+ case Animation::TYPE_BEZIER: {
+ AnimationMixer::TrackCacheBezier *src = static_cast<AnimationMixer::TrackCacheBezier *>(p_cache);
+ AnimationMixer::TrackCacheBezier *tc = memnew(AnimationMixer::TrackCacheBezier(*src));
+ return tc;
+ }
+
+ case Animation::TYPE_AUDIO: {
+ AnimationMixer::TrackCacheAudio *src = static_cast<AnimationMixer::TrackCacheAudio *>(p_cache);
+ AnimationMixer::TrackCacheAudio *tc = memnew(AnimationMixer::TrackCacheAudio(*src));
+ return tc;
+ }
+
+ case Animation::TYPE_METHOD:
+ case Animation::TYPE_ANIMATION: {
+ // Nothing to do here.
+ } break;
+ }
+ return nullptr;
+}