diff options
author | Sean Harmer <sean.harmer@kdab.com> | 2019-03-11 16:46:17 +0000 |
---|---|---|
committer | Sean Harmer <sean.harmer@kdab.com> | 2019-03-11 17:07:32 +0000 |
commit | 31aa763b0d1f4ec2439c6dc33b4a63b88c16369e (patch) | |
tree | 3e2203296a1937366fac5561b3ad9f9d49e22067 | |
parent | 4af8fcdc05d0198a569e7c39fa934614bbe31803 (diff) |
Make slerping safer
Now handles case where the two bounding keyframe quaternions are
equal by returning the first as the interpolated value.
Change-Id: I43d0dfb1e20afafd690817d30aeac2d510847422
Reviewed-by: Mike Krus <mike.krus@kdab.com>
-rw-r--r-- | src/animation/backend/animationutils.cpp | 19 |
1 files changed, 13 insertions, 6 deletions
diff --git a/src/animation/backend/animationutils.cpp b/src/animation/backend/animationutils.cpp index 0496ff681..c876fcb87 100644 --- a/src/animation/backend/animationutils.cpp +++ b/src/animation/backend/animationutils.cpp @@ -277,13 +277,12 @@ ClipResults evaluateClipAtLocalTime(AnimationClip *clip, float localTime) channelResults[i++] = channelComponent.fcurve.evaluateAtTime(localTime, lowerKeyframeBound); } } else { - // There's only one keyframe. We cant compute omega. Interoplate per component + // There's only one keyframe. We cant compute omega. Interpolate per component const int lowerKeyframeBound = channel.channelComponents[0].fcurve.lowerKeyframeBound(localTime); if (lowerKeyframeBound + 1 >= channel.channelComponents[0].fcurve.keyframeCount()) { for (const auto &channelComponent : qAsConst(channel.channelComponents)) channelResults[i++] = channelComponent.fcurve.evaluateAtTime(localTime, lowerKeyframeBound); } else { - auto quaternionFromChannel = [channel](const int keyframe) { const float w = channel.channelComponents[0].fcurve.keyframe(keyframe).value; const float x = channel.channelComponents[1].fcurve.keyframe(keyframe).value; @@ -296,10 +295,18 @@ ClipResults evaluateClipAtLocalTime(AnimationClip *clip, float localTime) const auto lowerQuat = quaternionFromChannel(lowerKeyframeBound); const auto higherQuat = quaternionFromChannel(lowerKeyframeBound + 1); - - const float omega = std::acos(QQuaternion::dotProduct(lowerQuat, higherQuat)); - for (const auto &channelComponent : qAsConst(channel.channelComponents)) - channelResults[i++] = channelComponent.fcurve.evaluateAtTimeAsSlerp(localTime, lowerKeyframeBound, omega); + const float omega = std::acos(qBound(-1.0f, QQuaternion::dotProduct(lowerQuat, higherQuat), 1.0f)); + + if (qFuzzyIsNull(omega)) { + // If the two keyframe quaternions are equal, just return the first one as the interpolated value. + channelResults[0] = lowerQuat.scalar(); + channelResults[1] = lowerQuat.x(); + channelResults[2] = lowerQuat.y(); + channelResults[3] = lowerQuat.z(); + } else { + for (const auto &channelComponent : qAsConst(channel.channelComponents)) + channelResults[i++] = channelComponent.fcurve.evaluateAtTimeAsSlerp(localTime, lowerKeyframeBound, omega); + } } } } else { |