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.