I'm trying to implement skeletal animation from a glTF2 file with a very simple rigged model.
https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/RiggedSimple
It has only two joints and three key frames.
I have a Skeleton with a list of Joints. Each Joint has a position, rotation, scale and a inverse bind matrix, which I load directly from the glTF2 file.
My Animation has a list of Tracks and each Track has a list of KeyFrames. Nothing special so far.
I use the second KeyFrame, no movement right now, so the mesh should be bend.
Now I have a AnimatedSkeleton which is a copy from the Skeleton and I apply the modifications from the KeyFrame to it.
I recalculate all transforms on the AnimatedSkeleton, multiply each Joint world transform with the corresponding inverse bind matrix and fill my JointMatrix array.
In RenderDoc I can see that my JointMatrix array gets filled, unfortunately the mesh is still in his binding pose.
Quick question: It is correct that I replace the values and not add them right?
So I do:
animatedSkeleton.joint.rotation = animation.keyFrame.rotation
and not
animatedSkeleton.joint.rotation += animation.keyFrame.rotation
Here is my HLSL vertex shader skinning code:
matrix GetSkinningMatrix(VSInput vin)
{
matrix skin = Identity;
#if defined(HAS_WEIGHT_SET1) && defined(HAS_JOINT_SET1)
skin +=
mul(SkinJointMatrix[int(vin.joint0.x)], vin.weight0.x) +
mul(SkinJointMatrix[int(vin.joint0.y)], vin.weight0.y) +
mul(SkinJointMatrix[int(vin.joint0.z)], vin.weight0.z) +
mul(SkinJointMatrix[int(vin.joint0.w)], vin.weight0.w);
#endif
return skin;
}
float4 GetPosition(VSInput vin)
{
float4 pos = float4(vin.position, 1.0);
#ifdef USE_SKINNING
pos = mul(pos, GetSkinningMatrix(vin));
#endif
return pos;
}
And my SkeletonJoint methods to recalculate the transform and fill the JointMatrix array.
void CSkeletonJoint::ApplyTransform(const xmath::Matrix& parentWorldTM)
{
xmath::Matrix scaleMat = xmath::Matrix::CreateScale(m_scale);
xmath::Matrix rotMat = xmath::Matrix::CreateFromQuaternion(m_rotation);
xmath::Matrix translateMat = xmath::Matrix::CreateTranslation(m_translation);
m_localTM = xmath::Matrix();
m_localTM *= scaleMat;
m_localTM *= rotMat;
m_localTM *= translateMat;
m_worldTM = parentWorldTM * m_localTM;
for (CSkeletonJoint& childJoint : m_children)
{
childJoint.ApplyTransform(m_worldTM);
}
}
void CSkeletonJoint::Apply(std::vector<xmath::Matrix>& jointMatrices)
{
xmath::Matrix jointMatrix = m_inverseBindMatrix * m_worldTM;
jointMatrices[m_index] = jointMatrix;
for (CSkeletonJoint& childJoint : m_children)
{
childJoint.Apply(jointMatrices);
}
}
I do not recalculate the inverse binding matrix, I use it from the glTF2 file since I need the unmodified version right?
I just don't see where I miscalculated the JointMatrix.