Skip to content

Commit

Permalink
Rework blending method in Variant animation for Int/Array/String
Browse files Browse the repository at this point in the history
  • Loading branch information
TokageItLab committed Nov 16, 2023
1 parent d5217b6 commit 80c9533
Show file tree
Hide file tree
Showing 7 changed files with 530 additions and 370 deletions.
4 changes: 4 additions & 0 deletions core/math/rect2.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ struct _NO_DISCARD_ Rect2 {
return Rect2(Point2(position.x + MIN(size.x, (real_t)0), position.y + MIN(size.y, (real_t)0)), size.abs());
}

_FORCE_INLINE_ Rect2 round() const {
return Rect2(position.round(), size.round());
}

Vector2 get_support(const Vector2 &p_normal) const {
Vector2 half_extents = size * 0.5f;
Vector2 ofs = position + half_extents;
Expand Down
3 changes: 3 additions & 0 deletions core/variant/variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,9 @@ class Variant {
_FORCE_INLINE_ bool is_num() const {
return type == INT || type == FLOAT;
}
_FORCE_INLINE_ bool is_string() const {
return type == STRING || type == STRING_NAME;
}
_FORCE_INLINE_ bool is_array() const {
return type >= ARRAY;
}
Expand Down
84 changes: 39 additions & 45 deletions scene/animation/animation_mixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.");
}
}

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -1710,11 +1696,19 @@ 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->set_indexed(t->subpath, Animation::cast_from_blendwise(t->value, t->init_value.get_type()));

} break;
case Animation::TYPE_BEZIER: {
Expand Down
1 change: 1 addition & 0 deletions scene/animation/animation_mixer.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class AnimationMixer : public Node {
Vector<StringName> subpath;
bool is_continuous = false;
bool is_using_angle = false;
Variant element_size;
TrackCacheValue() { type = Animation::TYPE_VALUE; }
};

Expand Down
7 changes: 1 addition & 6 deletions scene/animation/tween.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,13 +420,8 @@ Variant Tween::interpolate_variant(const Variant &p_initial_val, const Variant &
ERR_FAIL_INDEX_V(p_trans, TransitionType::TRANS_MAX, Variant());
ERR_FAIL_INDEX_V(p_ease, EaseType::EASE_MAX, Variant());

// Special case for bool.
if (p_initial_val.get_type() == Variant::BOOL) {
return run_equation(p_trans, p_ease, p_time, p_initial_val, p_delta_val, p_duration) >= 0.5;
}

Variant ret = Animation::add_variant(p_initial_val, p_delta_val);
ret = Animation::interpolate_variant(p_initial_val, ret, run_equation(p_trans, p_ease, p_time, 0.0, 1.0, p_duration));
ret = Animation::interpolate_variant(p_initial_val, ret, run_equation(p_trans, p_ease, p_time, 0.0, 1.0, p_duration), p_initial_val.is_string());
return ret;
}

Expand Down
Loading

0 comments on commit 80c9533

Please sign in to comment.