Skip to content

Commit b54e30f

Browse files
committed
Add slerp_with_rest static func to Animation class
1 parent 2043023 commit b54e30f

File tree

4 files changed

+37
-20
lines changed

4 files changed

+37
-20
lines changed

scene/3d/look_at_modifier_3d.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
#include "look_at_modifier_3d.h"
3232

33+
#include "scene/resources/animation.h"
34+
3335
void LookAtModifier3D::_validate_property(PropertyInfo &p_property) const {
3436
if (Engine::get_singleton()->is_editor_hint() && (p_property.name == "bone_name" || p_property.name == "origin_bone_name")) {
3537
Skeleton3D *skeleton = get_skeleton();
@@ -587,7 +589,7 @@ void LookAtModifier3D::_process_modification(double p_delta) {
587589
// Interpolate through the rest same as AnimationTree blending for preventing to penetrate the bone into the body.
588590
Quaternion rest = skeleton->get_bone_rest(bone).basis.get_rotation_quaternion();
589591
float weight = Tween::run_equation(transition_type, ease_type, 1 - remaining, 0.0, 1.0, 1.0);
590-
destination = rest * Quaternion().slerp(rest.inverse() * from_q, 1 - weight) * Quaternion().slerp(rest.inverse() * destination, weight);
592+
destination = Animation::slerp_with_rest(Animation::slerp_with_rest(rest, from_q, 1 - weight, rest), destination, weight, rest);
591593
} else {
592594
destination = from_q.slerp(destination, Tween::run_equation(transition_type, ease_type, 1 - remaining, 0.0, 1.0, 1.0));
593595
}

scene/animation/animation_mixer.cpp

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,7 +1406,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
14061406
rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx);
14071407
a->try_rotation_track_interpolate(i, end, &rot[1]);
14081408
rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx);
1409-
root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
1409+
root_motion_cache.rot = Animation::slerp_with_rest(root_motion_cache.rot, rot[1], blend, rot[0]);
14101410
prev_time = start;
14111411
}
14121412
} else {
@@ -1418,7 +1418,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
14181418
rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx);
14191419
a->try_rotation_track_interpolate(i, start, &rot[1]);
14201420
rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx);
1421-
root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
1421+
root_motion_cache.rot = Animation::slerp_with_rest(root_motion_cache.rot, rot[1], blend, rot[0]);
14221422
prev_time = end;
14231423
}
14241424
}
@@ -1429,7 +1429,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
14291429
rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx);
14301430
a->try_rotation_track_interpolate(i, time, &rot[1]);
14311431
rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx);
1432-
root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
1432+
root_motion_cache.rot = Animation::slerp_with_rest(root_motion_cache.rot, rot[1], blend, rot[0]);
14331433
prev_time = !backward ? start : end;
14341434
}
14351435
{
@@ -1439,7 +1439,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
14391439
continue;
14401440
}
14411441
rot = post_process_key_value(a, i, rot, t->object_id, t->bone_idx);
1442-
t->rot = (t->rot * Quaternion().slerp(t->init_rot.inverse() * rot, blend)).normalized();
1442+
t->rot = Animation::slerp_with_rest(t->rot, rot, blend, t->init_rot);
14431443
}
14441444
#endif // _3D_DISABLED
14451445
} break;
@@ -1585,21 +1585,8 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
15851585
// Special case for angle interpolation.
15861586
if (t->is_using_angle) {
15871587
// For blending consistency, it prevents rotation of more than 180 degrees from init_value.
1588-
// This is the same as for Quaternion blends.
1589-
float rot_a = t->value;
1590-
float rot_b = value;
1591-
float rot_init = t->init_value;
1592-
rot_a = Math::fposmod(rot_a, (float)Math::TAU);
1593-
rot_b = Math::fposmod(rot_b, (float)Math::TAU);
1594-
rot_init = Math::fposmod(rot_init, (float)Math::TAU);
1595-
if (rot_init < Math::PI) {
1596-
rot_a = rot_a > rot_init + Math::PI ? rot_a - Math::TAU : rot_a;
1597-
rot_b = rot_b > rot_init + Math::PI ? rot_b - Math::TAU : rot_b;
1598-
} else {
1599-
rot_a = rot_a < rot_init - Math::PI ? rot_a + Math::TAU : rot_a;
1600-
rot_b = rot_b < rot_init - Math::PI ? rot_b + Math::TAU : rot_b;
1601-
}
1602-
t->value = Math::fposmod(rot_a + (rot_b - rot_init) * (float)blend, (float)Math::TAU);
1588+
// This is the same with Quaternion blending.
1589+
t->value = Animation::slerp_with_rest((double)t->value, (double)value, blend, (double)t->init_value);
16031590
} else {
16041591
value = Animation::cast_to_blendwise(value);
16051592
if (t->init_value.is_array()) {

scene/resources/animation.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5671,6 +5671,30 @@ bool Animation::_fetch_compressed_by_index(uint32_t p_compressed_track, int p_in
56715671
return false;
56725672
}
56735673

5674+
// Helper functions for Rotation.
5675+
double Animation::slerp_with_rest(double p_from, double p_to, double p_weight, double p_rest) {
5676+
double rot_a = Math::fposmod(p_from, Math::TAU);
5677+
double rot_b = Math::fposmod(p_to, Math::TAU);
5678+
double rot_rest = Math::fposmod(p_rest, Math::TAU);
5679+
if (rot_rest < Math::PI) {
5680+
rot_a = rot_a > rot_rest + Math::PI ? rot_a - Math::TAU : rot_a;
5681+
rot_b = rot_b > rot_rest + Math::PI ? rot_b - Math::TAU : rot_b;
5682+
} else {
5683+
rot_a = rot_a < rot_rest - Math::PI ? rot_a + Math::TAU : rot_a;
5684+
rot_b = rot_b < rot_rest - Math::PI ? rot_b + Math::TAU : rot_b;
5685+
}
5686+
return Math::fposmod(rot_a + (rot_b - rot_rest) * p_weight, Math::TAU);
5687+
}
5688+
5689+
Quaternion Animation::slerp_with_rest(const Quaternion &p_from, const Quaternion &p_to, real_t p_weight, const Quaternion &p_rest) {
5690+
#ifdef MATH_CHECKS
5691+
ERR_FAIL_COND_V_MSG(!p_from.is_normalized(), Quaternion(), "The start quaternion must be normalized.");
5692+
ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
5693+
ERR_FAIL_COND_V_MSG(!p_rest.is_normalized(), Quaternion(), "The rest quaternion must be normalized.");
5694+
#endif
5695+
return (p_from * Quaternion().slerp(p_rest.inverse() * p_to, p_weight)).normalized();
5696+
}
5697+
56745698
// Helper math functions for Variant.
56755699
bool Animation::is_variant_interpolatable(const Variant p_value) {
56765700
Variant::Type type = p_value.get_type();

scene/resources/animation.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,10 @@ class Animation : public Resource {
540540
void optimize(real_t p_allowed_velocity_err = 0.01, real_t p_allowed_angular_err = 0.01, int p_precision = 3);
541541
void compress(uint32_t p_page_size = 8192, uint32_t p_fps = 120, float p_split_tolerance = 4.0); // 4.0 seems to be the split tolerance sweet spot from many tests.
542542

543+
// Helper functions for Rotation.
544+
static double slerp_with_rest(double p_from, double p_to, double p_weight, double p_rest = 0.0); // Deterministic slerp to prevent to cross the inverted rest axis.
545+
static Quaternion slerp_with_rest(const Quaternion &p_from, const Quaternion &p_to, real_t p_weight, const Quaternion &p_rest = Quaternion()); // Deterministic slerp to prevent to cross the inverted rest axis.
546+
543547
// Helper functions for Variant.
544548
static bool is_variant_interpolatable(const Variant p_value);
545549
static bool validate_type_match(const Variant &p_from, Variant &r_to);

0 commit comments

Comments
 (0)