Collada Bone Matrix / Baked Transform Matrix

Started by
16 comments, last by Stragen 10 years, 7 months ago
I've a quick query,

When looking at the visual_scene nodes within a typical DAE file, are the matrices referenced relative to the previous bone? or are they relative to the origin?

eg:
<node id="astroBoy_newSkeleton_deformation_rig" name="deformation_rig" type="NODE">
        <matrix>1 0 0 -0.33707 0 1 0 -0.127721 0 0 1 0 0 0 0 1</matrix>
        <node id="astroBoy_newSkeleton_root" name="root" sid="Bone1" type="JOINT">
          <matrix sid="transform">0.079633 -0.996824 0 0 0.992539 0.079291 -0.092624 0.057089 0.092329 0.007376 0.995701 2.6942 0 0 0 1</matrix>
          <node id="astroBoy_newSkeleton_spine01" name="spine01" sid="Bone2" type="JOINT">
            <matrix sid="transform">-0.915962 -0.140877 0.375723 0 0.152015 -0.988378 0 0 0.371356 0.057115 0.926732 0.460646 0 0 0 1</matrix>
Rig is the origin, root is a transform from the origin, spine01 is a transformation from the root?

or each a transformation from the origin?
Advertisement

iirc every transformation matrix is relative to its parent node.

Also there are exporters that will not bake the matrices and provide you with transform tags like (rotationX, rotationY, scale) in that case you need to build the matrix yourself.

Btw. you mean DAE file dont you?

Jan F. Scheurer - CEO @ Xe-Development

Sign Up for Xe-Engine™Beta

yes, DAE...

Typo... zzz...

I re-read the spec and you're correct, they are relative to each other.

I need to brush up on my matrices, but am i wrong in thinking i should be able to take this baked transformation matrix, multiply by a position vector, and have the destination of the bone?

Actually the bone is the connection between two joints.

After parsing the tree you need to multiply the matrices downwards the tree.

So multiply the matrix of the root with the matrix of the first child then multiply the result of that with the child of the child and so on.

After you have done this you need to multiply the bindshape matrix of the whole skeleton with the inverse bind matrices of the joints.

Following that you go ahead and multiply the inverse bind matrices of each joint with its transform matrix to translate back into worldspace.

The matrices are then ready to use for matrix palette skinning in your vertex shader.

Be sure to check out the "reference" tutorial on collada parsing if you havent already:

http://www.wazim.com/Collada_Tutorial_1.htm

http://www.wazim.com/Collada_Tutorial_2.htm

Jan F. Scheurer - CEO @ Xe-Development

Sign Up for Xe-Engine™Beta

My problem that I have with the Wazim tutorial (its what i'm trying to follow atm) is that I cant follow his code, and I'm getting confused by his explanation.

Though perhaps, what i'm looking for isnt even there... right now i'm trying to generate the skeleton view (so that I can validate the file has loaded in correctly) but i'll focus on what needs to happen here...

If i'm following you correctly (example for 3 joints):

BSM = {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1}

IBM1 = {0,0.995701,0.092623,-0.30639,-1,0,0,0,0,-0.092623,0.995701,-2.82193,0,0,0,1}
IBM2 = {0,-0.957547,0.288277,-0.949417,1,0,0,0,0,0.288277,0.957547,-3.15717,0,0,0,1}
IBM3 = {0,-0.981543,0.191242,-0.581193,1,0,0,0,0,0.191242,0.981543,-3.71747,0,0,0,1}

Root = {1,0,0,-0.33707,0,1,0,-0.127721,0,0,1,0,0,0,0,1}
Trans1 = {0.079633,-0.996824,0,0,0.992539,0.079291,-0.092624,0.057089,0.092329,0.007376,0.995701,2.6942,0,0,0,1}
Trans2 = {-0.915962,-0.140877,0.375723,0,0.152015,-0.988378,0,0,0.371356,0.057115,0.926732,0.460646,0,0,0,1}
Trans3 = {0.9933,0.067026,0.094141,0,-0.058502,0.994172,-0.090553,0,-0.099662,0.084439,0.991432,0.483701,0,0,0,1}

Multiply parent by child... to get individual bones
B1 = Root * Trans1
B2 = B1 * Trans2
B3 = B2 * Trans3

Multiply individual inverse bind pose matrices by skeleton's bind pose matrix...
IBM1 = IBM1 * BSM
IBM2 = IBM2 * BSM
IBM3 = IBM3 * BSM

Multiply individual bones by modified inverse bind pose matrices to get world space?
B1world = B1 * IBM1
B2world = B2 * IBM2
B3world = B3 * IBM3

Would this mean that B1world, B2world, B3world should now be diagonal matrices and representation of these points in space should be able to be done by multiplying a 1x4 vector by this matrix or by using the diagonal values as x,y,z,w?

Be aware that matrix multiplications are order dependant.

I'm using OpenGL and I'm not a math pro, so I'm not sure if this also applies to your code,

but in my code I've ALL multiplications in the reverse order than yours so:

B1 = Trans1 * Root;

IBM1 = BSM * IBM1;

B1world = IBM1 * B1;

Would this mean that B1world, B2world, B3world should now be diagonal matrices and representation of these points in space should be able to be done by multiplying a 1x4 vector by this matrix or by using the diagonal values as x,y,z,w?

The first, so you're using the calculated world matrices to transform your vertices(1x4 vector)

To only create the joint vertices i think you can use the diagonal values.

Jan F. Scheurer - CEO @ Xe-Development

Sign Up for Xe-Engine™Beta

I too am using OpenGL but find myself slipping back into habits of D3D... the old SRT vs TRS argument, appreciate the warning and reminder.

Thanks for running through this, i'll have a sit down and see if the code works, and will come back and update as required.

Same here, set me back a few days when i ported my hlsl shaders to glsl ;)

Be sure to vote the posts up when they were helpful so others can find them better when using the search.

Jan F. Scheurer - CEO @ Xe-Development

Sign Up for Xe-Engine™Beta

Ok, back again after tidying up some of the trial and error:

The code i'm using below is as follows.

RenderBones(BONE* bone, glm::vec4 originV, glm::mat4 prevB)
	glm::mat4 BSM = glm::mat4(1.0f,0.0f,0.0f,0.0f,0.0f,1.0f,0.0f,0.0f,0.0f,0.0f,1.0f,0.0f,0.0f,0.0f,0.0f,1.0f); //manually entered at this time.

	glm::mat4 TRANS = bone->boneMatrix;
	
	glm::mat4 IBM;
	if(namedJoints.count(bone->boneSID) != 0)
		IBM = namedJoints[bone->boneSID];
	
	glm::mat4 B = TRANS * prevB; //TRANSFORM TIMES PREVIOUS.
	
	IBM = BSM * IBM;

	glm::mat4 BWORLD = IBM * B; //IBM * B

	glm::vec4 unitVec = glm::vec4(1.0f,1.0f,1.0f,0.0f);
	glm::vec4 destV = (BWORLD * unitVec);
	
	glBegin(GL_LINES);
	glVertex3f(originV.x,originV.y,originV.z);
	glVertex3f(destV.x,destV.y,destV.z);
	glEnd();
	
	for(int i = 0; i<bone->children.size(); i++){
		RenderBones(&bone->children[i],destV,B);
	}
I suspect that i've not got something right in the way that i'm either using the data or that i'm misinterpreting how to use baked matrices.

This code appears to be traversing the skeleton right, except its resulting in a birds nest output...

Eg. the attached... |edit| upload didnt work, simply imagine a bunch of pickup sticks dropped on the ground so that there were at least the ends touching each other

I'm thinking these baked matrices need some work before i can use them in this way, i've confirmed that the values are being imported correctly, and that they're passing through... i feel i've missed something major.

|edit2|
Looking at the files Wazim has up on his site, i'm not sure that the baked transformations are exactly what i expect them to be...

the Maya export has:
<node id="astroBoy_newSkeleton_root" name="root" sid="bone0" type="JOINT">
<translate sid="translate">0 6.84326 -0.145007</translate>
<rotate sid="jointOrientZ">0 0 1 90</rotate>
<rotate sid="jointOrientY">0 1 0 84.6854</rotate>
<rotate sid="jointOrientX">1 0 0 90</rotate>
<rotate sid="rotateZ">0 0 1 0</rotate>
<rotate sid="rotateY">0 1 0 -4.56752</rotate>
<rotate sid="rotateX">1 0 0 0</rotate>

while the Max export has:
<node id="astroBoy_newSkeleton_root" name="root" sid="Bone1" type="JOINT">
<matrix sid="transform">0.079633 -0.996824 0 0 0.992539 0.079291 -0.092624 0.057089 0.092329 0.007376 0.995701 2.6942 0 0 0 1</matrix>

I'm not sure how this "baked" matrix has been reached...

The matrix transforms look good to me.

I'm pretty sure the w component of the unitVector needs to be 1 in your case (otherwise it should be the vertex weight[how much the joint matrix is affecting the vertex]).

Also i think you're multiplying in DX order again, destV should be unitVec * BWORLD.

Jan F. Scheurer - CEO @ Xe-Development

Sign Up for Xe-Engine™Beta

This topic is closed to new replies.

Advertisement