I use the XNA Animation Component library to perform blending between animations. It uses spherical linear interpolation between matrices.
In most cases this works without problems. However, I have one model where all of the animations are blended wrong. Weird rotation and warping of the model occurs.
I was told by a math expert that the problem may lie in the method that does the matrix interpolation. He said: "The right way to do it would be to decompose A=UP (polar decomposition), obtain quaternion from U and lerp/slerp it, while interpolating P component-wise. I'm not sure why the original author refrained from implementing the polar decomposition as its implementation is straightforward and easy."
I have included the code below.
I need some help in making the recommended changes. I am an expert in C# but know almost nothing about matrix interpolation.
private static Quaternion qStart, qEnd, qResult;
private static Vector3 curTrans, nextTrans, lerpedTrans;
private static Vector3 curScale, nextScale, lerpedScale;
private static Matrix startRotation, endRotation;
private static Matrix returnMatrix;
public static void SlerpMatrix(
ref Matrix start,
ref Matrix end,
float slerpAmount,
out Matrix result)
{
if (start == end)
{
result = start;
return;
}
// Get rotation components and interpolate (not completely accurate but I don't want
// to get into polar decomposition and this seems smooth enough)
Quaternion.CreateFromRotationMatrix(ref start, out qStart);
Quaternion.CreateFromRotationMatrix(ref end, out qEnd);
Quaternion.Lerp(ref qStart, ref qEnd, slerpAmount, out qResult);
// Get final translation components
curTrans.X = start.M41;
curTrans.Y = start.M42;
curTrans.Z = start.M43;
nextTrans.X = end.M41;
nextTrans.Y = end.M42;
nextTrans.Z = end.M43;
Vector3.Lerp(ref curTrans, ref nextTrans, slerpAmount, out lerpedTrans);
// Get final scale component
Matrix.CreateFromQuaternion(ref qStart, out startRotation);
Matrix.CreateFromQuaternion(ref qEnd, out endRotation);
curScale.X = start.M11 - startRotation.M11;
curScale.Y = start.M22 - startRotation.M22;
curScale.Z = start.M33 - startRotation.M33;
nextScale.X = end.M11 - endRotation.M11;
nextScale.Y = end.M22 - endRotation.M22;
nextScale.Z = end.M33 - endRotation.M33;
Vector3.Lerp(ref curScale, ref nextScale, slerpAmount, out lerpedScale);
// Create the rotation matrix from the slerped quaternions
Matrix.CreateFromQuaternion(ref qResult, out result);
// Set the translation
result.M41 = lerpedTrans.X;
result.M42 = lerpedTrans.Y;
result.M43 = lerpedTrans.Z;
// Add the lerped scale component
result.M11 += lerpedScale.X;
result.M22 += lerpedScale.Y;
result.M33 += lerpedScale.Z;
}