I’m having a difficult time writing a skinned mesh exporter for Maya, although for once it’s not Maya’s API that’s causing the problem. This’ll be an API-free post.
I clearly don’t have a good enough grasp of the theory behind skinning.
I’m not sure what information I should be exporting for my joints at the bind pose:
World-space matrix, Inverse world-space, Local-space ( bone space ), etc
I’m somewhat comfortable with the actual deformation of vertices at render-time; I’ve written some small skeletal animation prototypes using existing formats.
I’m not very clear on how to create the initial data.
I have a skeleton in a rest pose.
I have the mesh at that rest pose.
Do I store the rest pose bones local matrices, or do I recursively multiply them with their parents’ matrices, and then store them?
Do I store the model-space (not bone-space) vertices of the rest pose, and call this the bind pose?
If not, how do I determine the bind pose coordinates?
Unforunately my employer has given me a week to implement this; third-party libraries and existing formats are out of the question.
Given the bind-pose vertices of the mesh, the bind-pose matrices are such that they exert no influence on the vertices…or so I’ve gathered.
Does this mean that animation data stores the difference from the bind-pose matrices?
It seems that if only one bone influenced each vertex, the bind-pose vertices would be stored in the space of the bones that influenced them.
What happens when more than one bone influences a vertex? Do you store a vertex position PER WEIGHT per vertex that’s transformed into the weight’s bone’s space?
Another fine spring morning, and a fresh start.
Looking at the file format of an unreleased game that uses skeletal animation, there is actually a vertex position stored per weight.
The general algorithm for show the model in its bind-pose is :
vertex v = 0,0,0
for( number of bones influencing this vertex ) {
v += weight.position * boneMatrix * weight.bias
}
Given the simple situation where only one bone influences a vertex, the bind pose is extracted
as
v = boneMatrix * weight.position;
and the weight.position to store is
weight.position = bind_pose_vertex * inverse(boneMatrix);
This seems correct to me, but I’m doing something wrong when I calculate the weight.position.
Buggy bind-pose picture
The model is stretched in the x and y directions (taller and wider)…but the head, arms, and feet appear correct.
The root of this skeleton is at the hips, and only one bone influences a vertex.
Hi cat, try this:
(1) export the inverse of the bind matrix of each bone in object space, ie. inverse( bind matrix in object space )
(2) export the mesh in bind pose normally
(3) export animation for each bone in parent-space, ie. the transfrom from parent-to-child.
(4) during playback, recursively reconstruct the animation to form a skeleton in object space
(5) multiply this with the stores inverse bind matrix from (1)
(6) now you have a matrix palette that you can use for softskinning, ie, vertex = vertex * matrix[index1] * weight1 + vertex * matrix[index2] * weight2 + …
hope this helps
EDIT:
In maya, the skinCluster has an attribute “geomMatrix” (obj2world) and “bindPreMatrix” (inverse of bind in world space). To get the inverse bind in object space multiply
inversebind_objspace = geomMatrix * bindPreMatrix
Hello,
I had a similar problem like this. It turned out to be an incorrect coordinate export. I used something like during exporting:
vertex.x = MDistance::internalToUI( vertex.x );
The steps I used for exporting are:
- get all the joints orientation with
joint.getOrientation();
val = animCurve.evaluate( time );
The steps for the skinning are:
- build the local animation transformation matrix
LocalOrientationMatrix = Scale * Rotation * Translation
- build the hierarchy matrices, starting from the root joint and store the inverse of that for skinning later on
HierarchyMatrix[i] = OrientationMatrix[i] * HierachyMatrix[i-1]
InverseHierarchyMatrix[i] = inverse( HierarchyMatrix)
- during animation, for each joint multiply the local animation transformation with the local orientation matrix and with parent’s final animation matrix
FinalAnimMatrix[i] = LocalAnimMatrix[i] * LocalOrientationMatrix[i] * FinalAnimMatrix[i-1]
- for skinning matrix, it’s just the inverse hierarchy matrix multiply by the final animation matrix.
SkinMatrix[i] = FinalAnimMatrix[i] * InverseHierarchyMatrix[i]
- final vertex position output would be
for i = 0 to num_joint_influences
VertexPos = VertexPos * SkinMatrix[i]
Hope this helps.
Thanks for the responses. Most of my confusion was resulting from row and column major differences within Maya’s API. Incredibly frustrating, and stupid.
I chose what I thought would be a relatively flexible and debuggable format (md5mesh and md5anim) but I should have started with a more well-known system, I think.
At least I thoroughly understand the different spaces and general transforms involved.