Franc Pujolar
Franc Pujolar

Reputation: 59

How do I calculate a COLLADA file's parent and children joint transforms?

Trying to implement rigging:

    <visual_scene id="Scene" name="Scene">
      <node id="Armature" name="Armature" type="NODE">
        <matrix sid="transform">1 0 0 0 0 0 1 0 0 -1 0 0 0 0 0 1</matrix>
        <node id="Armature_Bone" name="Bone" sid="Bone" type="JOINT">
          <matrix sid="transform">0.3299372 0.944003 -1.78814e-7 0 -4.76837e-7 0 -1 0 -0.944003 0.3299374 3.8743e-7 0 0 0 0 1</matrix>
          <node id="Armature_Bone_001" name="Bone.001" sid="Bone_001" type="JOINT">
            <matrix sid="transform">0.886344 -0.4630275 3.31894e-7 2.98023e-8 0.4630274 0.886344 -1.86307e-7 1.239941 -2.07907e-7 3.18808e-7 1 -2.84217e-14 0 0 0 1</matrix>
            <node id="Armature_Bone_002" name="Bone.002" sid="Bone_002" type="JOINT">
              <matrix sid="transform">0.9669114 0.2551119 -1.83038e-7 -1.19209e-7 -0.2551119 0.9669115 1.29195e-7 1.219687 2.09941e-7 -7.82246e-8 1 0 0 0 0 1</matrix>
              <node id="Armature_Bone_003" name="Bone.003" sid="Bone_003" type="JOINT">
                <matrix sid="transform">0.8538353 0.5205433 1.0139e-7 -1.19209e-7 -0.5205433 0.8538353 2.4693e-7 1.815649 4.19671e-8 -2.63615e-7 1 5.68434e-14 0 0 0 1</matrix>

Now if I set each bone's matrix to glm::mat4(1), I get this:

Screenshot

But if I try to multiply by a joints parent transform, like in the Thin Matrix rigging tutorial, I get very weird results:

void SkelManager::setTposeTransforms(std::vector<Joint>& _reference)
{
    for (int child = 0; child < _reference.size(); child++)
    {
        if (_reference[child].parent == -1)
        {
            //_reference[child].tPose = glm::mat4(1);
            _reference[child].tPose = _reference[child].transform;
        }
        for (int parent = 0; parent < _reference.size(); parent++)
        if (_reference[child].parent == parent)
        {
            //_reference[child].tPose = glm::mat4(1);
            _reference[child].tPose = _reference[parent].tPose * _reference[child].transform;
        }
    }
}

Result of the above code

Please help, I've been stuck on this for a couple weeks and I've had no success, and no matter how hard I search the web I can't find anything that works, any ideas on what I could be doing wrong?

Upvotes: 3

Views: 877

Answers (1)

recp
recp

Reputation: 393

I use glm::make_mat4(tmpFloatArray), and then I transpose it, not sure if this is the correct way):

See COLLADA spec's about matrix:

Matrices in COLLADA are column matrices in the mathematical sense. These matrices are written in row- major order to aid the human reader. See the example.

so yes, you need to transpose it.

It is not so hard to load COLLADA's skeleton animations. Follow these steps:

Importer side:

  1. Load all joint node hierarchy, multiply joint transform with parent until root node as you do for normal/other nodes (scene graph). It is better to do multiplication when transforms are changed for each frame...
  2. Load Controller->Skin element with joint IDs, weights... also bind_shape_matrix and INV_BIND_MATRIX
  3. Load Animation object[s] to animate joints
  4. Load instance_controller, it stores material and <skeleton> element which indicates where is the root node for joint hierarchy. It is important because you need to start resolve SID from that element not entire document or top nodes in scene...

Render side:

  1. Prepare all joint transforms for each frame if needed. Multiply joint transforms with their parents

  2. Create this matrix for each joints:

    FinalJointTrans4x4 = JointTransform * InvBindPose * BindShapeMatrix

    JointTransform is the transform that multiplied with parents...

    InvBindPose (or InvBindMatrix) is the transform you read from skin->joints->INV_BIND_MATRIX for each joints

    BindShapeMatrix is the transform that you read from skin->bind_shape_matrix

  3. Send these FinalJointTrans4x4 matrices and weights to shader (a uniform buffer would be good to store matrices)

  4. Use these information in the shader, render it.

Maybe (from http://github.com/recp/gk):

...
mat4 skinMat;
    
skinMat = uJoints[JOINTS.x] * WEIGHTS.x
        + uJoints[JOINTS.y] * WEIGHTS.y
        + uJoints[JOINTS.z] * WEIGHTS.z
        + uJoints[JOINTS.w] * WEIGHTS.w;
    
pos4  = skinMat * pos4;
norm4 = skinMat * norm4;

...

#ifdef JOINT_COUNT
  gl_Position = VP * pos4;
#else
  gl_Position = MVP * pos4;
#endif
...

There may other details that I may forgot to mention (I may edit the answer later) but this must help a lot.

PS: There is a library called AssetKit (http://github.com/recp/assetkit) you can use it to load COLLADA files if you like.

Upvotes: 2

Related Questions