1
\$\begingroup\$

I have a Collada file with a rigged mesh. I've read the node tags in the library_visual_scenes tag and extracted the matrix for each node and stored everything in a hierarchical bone structure. My Matrix container is "row major", so I'd store the first float of a matrix tag in the 1st row, 1st column, the second in the 1st row, 2nd column, etc. From what I gather this is the Bind Pose Matrix. After that I went through the tag and extracted the float array in the source tag of the skin tag of the controller for the mesh. I stored each matrix from this float array in their corresponding Bone as the Inverse Bind Matrix. I also extracted the bind-shape-matrix and stored it.

Now I'd like to draw the skeleton with OpenGL to see if everything is working correctly before I go about skinning. I iterate once over my bones and multiply a bone's Bind Pose Matrix by it's parents and store that. After that I iterate again over the bones and multiply the result of the previous matrix multiplication by the Inverse Bind Matrix and then by the Bind Shape Matrix. The results look something like this:

[0.2, 9.2, 5.8, 1.2 ]
[4.6, -3.3, -0.2, -0.1 ]
[-1.8, 0.2, -4.2, -3.9 ]
[0, 0, 0, 1 ]

I've had to go to various sources to get the little understanding of Collada I have and books about 3d transform matricies can get pretty intense. I've hit a brick wall and if you could please read through this and see if there is something I'm doing wrong, and how I'd go about getting an X,Y,Z to draw a point for each of these joints once I've calculated the final transform, I'd really appreciate it.

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

I see you are trying to parse a COLLADA file by hand. I was once as young and naive as you. Welcome to our shared hell. My COLLADA loader is 2837 lines long (one .cpp) and it doesn't even support half the spec.

Before we continue, I simply must point you to this excellent resource loader: ASSIMP. It has a silly name, but it will load any COLLADA file perfectly.

My Matrix container is "row major"

Are you sure about that? OpenGL uses column-major matrices, in this format:

[ X1 Y1 Z1 WX ]
[ X2 Y2 Z2 WY ]
[ X3 Y3 Z3 WZ ]
[ TX TY TZ CZ ]

From what I gather this is the Bind Pose Matrix.

The nodes will either be a node for a mesh or a joint, as you probably realize by now. You are correct, the animation source list (the combination of translate, rotate and scale tags inside the node tag) is the bind pose. But it is not required! If no animation source list is provided, you must use the inverse bind matrix and inverse it to get the bind matrix. For rendering models with skinned animation, however, you only need the inverse bind matrix.

The inverse bind matrix for a joint is defined in the skin.

For each node that is not a joint (every mesh), you must look up the skin using either the id or the name as specified in the node tag. A node with a skin has an instance_controller tag with a url.

Then you have to add the joints to the skeleton for this model. Keep in mind that a skin might not have inverse bind matrix definitions for every joint, to keep things easy.

Here's what that looks like in my code. It also explains why COLLADA is so hard to parse.

std::string* src_name = (*it)->skin_ptr->vertex_weights->input_joint_names->source_ptr->data->GetStringData();
float* src_matrix = (*it)->skin_ptr->vertex_weights->input_inv_bind_matrix->source_ptr->data->GetFloatData();

for (unsigned int j = 0; j < (*it)->skin_ptr->vertex_weights->input_inv_bind_matrix->source_ptr->data0>count; j++)
{
    // collada stores its matrices as row-major, my matrix class uses column-major

    tb::Mat4x4 inv_bind_matrix;

    inv_bind_matrix[tb::Mat4x4::X1] = src_matrix[ 0];
    inv_bind_matrix[tb::Mat4x4::Y1] = src_matrix[ 4];
    inv_bind_matrix[tb::Mat4x4::Z1] = src_matrix[ 8];
    inv_bind_matrix[tb::Mat4x4::WX] = src_matrix[12];

    inv_bind_matrix[tb::Mat4x4::X2] = src_matrix[ 1];
    inv_bind_matrix[tb::Mat4x4::Y2] = src_matrix[ 5];
    inv_bind_matrix[tb::Mat4x4::Z2] = src_matrix[ 9];
    inv_bind_matrix[tb::Mat4x4::WY] = src_matrix[13];

    inv_bind_matrix[tb::Mat4x4::X3] = src_matrix[ 2];
    inv_bind_matrix[tb::Mat4x4::Y3] = src_matrix[ 6];
    inv_bind_matrix[tb::Mat4x4::Z3] = src_matrix[10];
    inv_bind_matrix[tb::Mat4x4::WZ] = src_matrix[14];

    inv_bind_matrix[tb::Mat4x4::TX] = src_matrix[ 3];
    inv_bind_matrix[tb::Mat4x4::TY] = src_matrix[ 7];
    inv_bind_matrix[tb::Mat4x4::TZ] = src_matrix[10];
    inv_bind_matrix[tb::Mat4x4::CZ] = src_matrix[15];

    (*it)->skin_ptr->joint_names.push_back(*src_name);
    (*it)->skin_ptr->inverse_bind_matrices.push_back(inv_bind_matrix);

    src_name++;
    src_matrix += 16;
}

So, here's what you have to do if you want to draw the skeleton:

  • Parse the root skeleton (all joints)
  • Locate the mesh you want to draw.
  • Locate the skin for the mesh.
  • Parse the inverse bind matrices in the skin and put them in the skeleton for the joint.
  • To get a joint's bind matrix, multiply its parents inverse bind matrix (recursive) with its inverse bind matrix and inverse it.
  • To get a joint position, get the translation part of the matrix.
  • Draw lines between every joint used by the model.

And then you're not even there yet. Because every node might also have a local transformation. And that complicates matters to an insane degree.

So unless you have no other choice, use ASSIMP! It will save you headaches and nightmares.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks so much for your response. I've gone through and recursively calculated each bones bind matrix as you've instructed and took the translation part of the matrix but it seems I've done something wrong or missed a step because the result doesn't look correct. Or perhaps the Blender exporter is messing something up. Here's a image of my results : link On the left is the model inside of Blender. On the right is the result inside OpenGL. Here is a link to the XML file I am using: link Thanks again. \$\endgroup\$
    – KyleT
    Jul 5, 2012 at 22:48
  • 1
    \$\begingroup\$ And I've looked into libraries like ASSIMP. I know it would alleviate a ton of headaches and frustrations but I'd rather understand what it is I'm doing. \$\endgroup\$
    – KyleT
    Jul 5, 2012 at 22:51
  • 1
    \$\begingroup\$ I figured out my problem. I was multiplying the parent joint and child joints matrices in the wrong order. I'm an idiot. Thanks for the help! \$\endgroup\$
    – KyleT
    Jul 8, 2012 at 4:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .