I created a simple object that is simply two bones and a cuboid. It looks like an elbow joint of an arm sticking straight up in the air. I have the following matrices:
Root (Contains everything) = rootMatrix
Cube (Contains the entire model) = modelMatrix
Armature (Contains the two bones) = armatureMatrix
Armature_RootBase (Root "upper arm" bone)
SkinMatrix_RootBase (The skin matrix for the root bone)
Armature_TipTop (is the "forearm" bone)
SkinMatrix_TipTop (The skin matrix for the second bone)
When loading the model in, I transform all the vertices by the bind matrix to have the bind pose as the default. I get the bind matrix with:
Matrix4f bindMatrix = new Matrix4f();
Matrix4f.mul(bindMatrix, rootMatrix, bindMatrix);
Matrix4f.mul(bindMatrix, modelMatrix, bindMatrix);
That works great. The bind pose is exactly as it should be, how it's shown in Blender.
Now when I try to animate is where the problems come in. I'm using GLSL to apply the bone matrices to the vertices. When I send identity matrices for all the bones it works fine. I've done tests and I know the GLSL is working and sending the matrices and applying the bones to the correct vertices. The main problem is I don't know how to properly get the matrices.
I have a bind pose model and I need to take a rotation, translation and scale and create the proper matrix to transform the model.
I'll explain my current non-working process, because it might just need a little tweak to make it work.
I have structures to mimic the structure of the armature. At the moment I'm exporting full animations from Blender (I'll work out interpolation later), so I have a key frame for every frame. I can apply an animation to a model and I'm able to lookup the animation rotation, translation and scale for any bone.
Basically I go through and calculate the new bone matrices, then send them to my shader to be applied to the bones. So the update process goes like this:
Starting with the root bone
UpdateFinalTransform(Bone)
calculateAnimationTransform()
foreach child bone
UpdateFinalTransform(child)
Where calculateAnimationTransform() looks like this:
transformMatrix.setIdentity();
combinedMatrix.setIdentity(); //clear the transform and animation matrices
Matrix4f bindMatrix = new Matrix4f(getBindMatrix()); //get the bind matrix for this model
Matrix4f.mul(transformMatrix, (Matrix4f)bindMatrix.invert(), transformMatrix); //multiply by the inverted bind matrix
Matrix4f skinMatrix = getSkinWeightMatrix(); //get the skin matrix for this bone
Matrix4f animationTransform = new Matrix4f();
//rotate, scale, translate
animationTransform.applyQuaternion(new Quaternion(rotation));
tmpMatrix.setIdentity();
tmpMatrix.m00 = scale.x;
tmpMatrix.m11 = scale.y;
tmpMatrix.m22 = scale.z;
Matrix4f.mul(animationTransform, tmpMatrix, animationTransform);
tmpMatrix.setIdentity();
tmpMatrix.m30 = position.x;
tmpMatrix.m31 = position.y;
tmpMatrix.m32 = position.z;
Matrix4f.mul(animationTransform, tmpMatrix, animationTransform);
setCombinedMatrix(animationTransform); //set the animation transform for this bone, so that children bones can access it
combinedMatrix = getTransformMatrix(); //get the combined transform of this animation transform * parent animation transform
Matrix4f finalMatrix = Matrix4f.mul(skinMatrix, combinedMatrix, null); //skin the combined matrix
Matrix4f.mul(finalMatrix, transformMatrix, transformMatrix); //apply the inverted bind pose
Matrix4f.mul((Matrix4f)bindMatrix.invert(), transformMatrix, transformMatrix); //undo the inverted bind pose
each bone has that getTransformMatrix() method that looks like this:
if (parent == null) {
return Matrix4f.mul(combinedMatrix, parentArmature.getArmatureRootMatrix(), null);
} else {
return Matrix4f.mul(combinedMatrix, parent.getCombinedMatrix(), null);
}
combinedMatrix is set to its default armature matrix if an animation matrix is not defined.
So my questions.
1. Am I creating the animationTransform properly?
2. Is this the correct order of transforms?
3. What am I missing?
4. How do I do it properly?