3DS Max Skeleton Matrices

Hi, I was just looking at a COLLADA file exported from 3DS Max 8 with a skinned character, and I was hoping to get something clarified here. If you assume my <instance_controller> element is defined as follows:

<instance_controller url="#MaleAgent2_Level5_Body_LOD1-mesh-skin">
<skeleton>#Male_Level_3_Pelvis-node</skeleton>
<bind_material>
<technique_common>
<instance_material symbol=“plain” target="#plain">
<bind_vertex_input semantic=“CHANNEL1” input_semantic=“TEXCOORD” input_set=“1”/>
</instance_material>
</technique_common>
</bind_material>
</instance_controller>

and here is the starting bit of the skeleton:

<node id=“Male_Level_3_Pelvis-node” name=“Male_Level_3_Pelvis” sid=“Male_Level_3_Pelvis-node” type=“JOINT”>
<matrix sid=“transform”>0 1 -0.000001 -0.069178 -0.000001 0.000001 1 0 1 0 0.000001 0 0 0 0 1</matrix>
<node id=“Male_Level_3_Pelvis-node_PIVOT” name=“Male_Level_3_Pelvis_PIVOT” type=“NODE”>
<matrix>24.4491 0 0 0 0 43.643 0 0 0 0 22.7951 0 0 0 0 1</matrix>
</node>
<node id=“Male_Level_3_Spine-node” name=“Male_Level_3_Spine” sid=“Male_Level_3_Spine-node” type=“JOINT”>
<matrix sid=“transform”>0.999098 -0.042467 -0.000001 7.38171 0.042467 0.999098 0.000004 -0.379583 0.000001 -0.000004 1 0.00001 0 0 0 1</matrix>
<node id=“Male_Level_3_Spine-node_PIVOT” name=“Male_Level_3_Spine_PIVOT” type=“NODE”>
<matrix>3.39348 0 0 0 0 33.1984 0 0 0 0 24.3037 0 0 0 0 1</matrix>
</node>
<node id=“Male_Level_3_Spine1-node” name=“Male_Level_3_Spine1” sid=“Male_Level_3_Spine1-node” type=“JOINT”>
<matrix sid=“transform”>0.999561 -0.029622 0 3.39377 0.029622 0.999561 0 -0.009774 0 0 1 0 0 0 0 1</matrix>
<node id=“Male_Level_3_Spine1-node_PIVOT” name=“Male_Level_3_Spine1_PIVOT” type=“NODE”>
<matrix>12.28 0 0 0 0 31.4405 0 0 0 0 23.3606 0 0 0 0 1</matrix>
</node>

I’m wondering what the <node> elements with type=“NODE” are supposed to do? These nodes whose id’s are of the form id="*_PIVOT" don’t contain any child nodes, so why do they have matrices? Should their parent nodes of type=“JOINT” be transformed by these matrices? If my question isn’t very clear, let me know so I can try to clarify it. Thanks in advance for any help.

I’m wondering what the <node> elements with type=“NODE” are supposed to do?
These nodes whose id’s are of the form id=“*_PIVOT” don’t contain any child nodes, so why do they have matrices?
Good question. Why are they there at all? They specify a transform, but don’t contain any geometry or anything, so they’ll have no effect on the visual appearance of the model. If I had to guess, I would say it’s a reflection of the way Max stores things internally. In Max (IIRC) each node has a local transform that contributes to the child nodes’ local-to-world transform in the usual way, and they also have an offset transform that can be used to influence only that specific node’s geometry. So in a complex node hierarchy, you can change the offset transform to move a piece of geometry without affecting the rest of the hierarchy below the node. What’s confusing to me is that these pivot nodes have a non-identity transform, but don’t affect the scene in any way. Is that intentional, or a bug in the exporter?

Should their parent nodes of type=“JOINT” be transformed by these matrices?
No.

You might want to ask about this on Feeling Software’s forums. They wrote the Max exporter.

Actually I think most of my explanation above for what these pivot nodes are is wrong. I think the issue is that in Collada pivot points aren’t a first-class concept; the origin is always the pivot point. You can simulate pivots using standard transforms: first translate to the pivot, then rotate/scale, then translate back. In Max though pivot points are a first-class concept, and I think the Max exporter inserts these pivot nodes so that if you import the model back into Max it can figure out what the pivot point is.

That’s my best guess anyway. You still might want to check with the Feeling Software guys to be sure, but I think the bottom line is that the pivot nodes can be safely ignored, which would be the case anyway since they don’t have any effect on the model.

Thanks for the reply. I came to the same conclusion, to just ignore the matrices of type “NODE” and see what happens. If the model doesn’t animate properly, then I was planning to try to multiply the parent “JOINT” node by the matrix of type “NODE”, but you say that I won’t need them anyway, so I guess it should just work when I try it the first way. Will take a while for me to get to that stage, but I will post the outcome here when I have one. Thanks again :slight_smile:

Just to clarify, just because a node has type=“NODE” doesn’t mean you can ignore it. In fact, the default value for the type attribute is “NODE”, so in

<node id="whatever">
    ...
</node>

the “type” is implicitly understood to be “NODE”. This doesn’t mean you can ignore it.

As I understand it, the “type” attribute is largely just an annotation for packages like Max and Maya where joints are drawn differently. If you’re just writing a normal Collada renderer or translator I think you can ignore the type attribute. That attribute isn’t explained very well in the documentation unfortunately.

I wasn’t planning to ignore all nodes of type “NODE” :slight_smile: although my post seems to suggest that.

I was planning to ignore the <node>s of type “NODE” when they appear in the skeleton hierarchy only.

I’ve now implemented this and I’ve got skinned animation working for models that DO NOT have these “NODE” <nodes> in the skeleton. If they do have these nodes, then the animation doesn’t play correctly.

Does anyone know what needs to be done with these matrices? Do they need to be baked into their parent “JOINT” <nodes> as soon as the file is loaded?

OR

Do the parent “JOINT” nodes need to have these matrices multiplied into them after being combined with their child “JOINT” nodes during each frame of animation?

I think I’ve found the problem, the root of the skeleton has a parent <node> of type “JOINT” that is animated. I think these transforms need to be baked into the root node during each frame of animation. I’m trying this right now and I’ll let you know how it went when I’m finished.

EDIT: This works! Just thought I would let people know about this in case someone else has similar problems.

transforms need to be baked

What does it mean ?

Baking transfoms just means multiplying the matrices for a series of transformations (rotation/translation/scale) together to get a single matrix.

OK, thank you

I’ve just come across this problem now, and have the skeleton working for models without the PIVOT nodes.

When you say bake, what are you doing? can you please provide an example?

currently I have something like this


	for (int num = 0; num < (int) bones.size (); num++)
	{
		CBone	*bone = bones[num];
		int		numBonesMatrix = (int) bone->mAnimationMatrices.size ();

		bone->mFinalMatrix = *bone->mAnimationMatrices[frame];

		if (bone->mParent != NULL)
			bone->mFinalMatrix *= bone->mParent->mFinalMatrix;
	}

	for (int num = 0; num < (int) bones.size (); num++)
	{
		CBone		*bone = bones[num];

		matrices[num] = bone->mInvMatrix * bone->mFinalMatrix;
	}


Where in this would I apply this extra matrix?

Thanks

Phil

k fixed now. Thanks for your help