Converting Rotations to display correctly in OpenGL

I am exporting some simple animation of a rotating box using 1 bone. Because of the differences in the coordinate systems between Max and OpenGL, im getting incorrect results.

<node id=“Bone01” sid=“Bone01” name=“Bone01” type=“JOINT”>
<translate sid=“Trans”>2.68195 -3.34879e-008 0.766113 </translate>
<rotate sid=“RotZ”>0 0 1 0</rotate>
<rotate sid=“RotY”>0 1 0 -90</rotate>
<rotate sid=“RotX”>1 0 0 0.000327849</rotate>
<rotate sid=“ScaleAxisR”>1 0 0 0</rotate>
<scale sid=“Scale”>1 1 1 </scale>
<rotate sid=“ScaleAxis”>1 0 0 0</rotate>
</node>

I convert each rotate tag to a quaternion and then multiply z * y * x.
For animations,

<source id=“Bone01-Rotate-X-value”>
<float_array id=“Bone01-Rotate-X-value-array” count=“4”>-0.000027 -0.000027 -0.000027 -0.000027</float_array>
<technique_common>
<accessor source="#Bone01-Rotate-X-value-array" count=“4”>
<param name=“ROTX.ANGLE” type=“float”/>
</accessor>
</technique_common>
</source>
//etc… for Rotate-Y/Z

<channel source="#Bone01-Rotate-X-sampler" target=“Bone01/RotX.ANGLE”/>
<channel source="#Bone01-Rotate-Y-sampler" target=“Bone01/RotY.ANGLE”/>
<channel source="#Bone01-Rotate-Z-sampler" target=“Bone01/RotZ.ANGLE”/>

What is the best way to convert rotations in Max to rotations in OpenGL?

Was this already addressed here?

In a way my previous problem has been solved in the sense that i get proper rotations now instead of weird numbers.

I can see my mesh being animated now, but they do not rotate around the proper axis.

All i need to know is the math to convert a rotation in Max to give equivalent results in OpenGL.

There could be any number of things going wrong. Can you post the code you’re using to pull the transform in from Collada and apply it in OpenGL? In particular, are you using glRotate, glMultMatrix, or glLoadMatrix? Remember that glMultMatrix and glLoadMatrix want a column-major memory layout, whereas C/C++ is row-major, so if you’re using a 2-d array as in float m[4][4], you’re going to have to transpose that before passing it along to glMultMatrix/glLoadMatrix, or use the transpose matrix functions (glMultMatrixTranspose iirc).

Are you taking into account the up_axis change.

A straight up export from max will most likely be exporting geometry and animations and everything using the z axis as the up axis.

If you have set up your openGL cameras to see things Y up then all your rotations would look wierd… your geometry would be sideways too but since you are using a cube you can’t notice that.

When loading a COLLADA document you should pay attention to the <asset> tag. It holds some inportant information such as <up_axis> and <units> which you should pay attention to if you don’t want to experience things looking or acting different then your DCC tool.

I believe switching between the two axis is as simple as a 90 degree rotation around x for all the nodes upon loading.

-Andy


//The Quaternion which will transform Max transformations to OpenGL
	AngleAxis MaxToGL;
	MaxToGL.x = 1.0f;
	MaxToGL.w = -90.0f * (PI/180.0f);//convert to radian
	MaxToGL = MaxToGL.convertToQuat();
	MaxToGL.Normalize();


//For every Bone
	for(PInt sfb=0;sfb<BoneNameArray.size();++sfb)
		{			
			domNode* dn;
			//Try to get the Node with the same name
			myDae->getDatabase()->getElement((daeElement**)&dn, 0, e.c_str(), COLLADA_ELEMENT_NODE);			
			
			e = dn->getName();
			cout<<"


Now processing : "<<e<<"
";			
			PInt similj = dn->getTranslate_array().getCount();
//I get my position vector of the bone here, i swap the y and z manually.
			float x = dn->getTranslate_array().get(0)->getValue().get(0);
			float z = -dn->getTranslate_array().get(0)->getValue().get(1);
			float y = dn->getTranslate_array().get(0)->getValue().get(2);
			b.setPosition(x,y,z);


//Here we get the Bone's initial orientation
			Quat Final;

			//for every Rotate array
			for(PInt oiu = 0;oiu<dn->getRotate_array().getCount();++oiu)
			{
				//YZ SWAP
				Quat q1;
				q1.x = dn->getRotate_array().get(oiu)->getValue().get(0);
				q1.y = dn->getRotate_array().get(oiu)->getValue().get(1);
				q1.z = dn->getRotate_array().get(oiu)->getValue().get(2);
				q1.w = dn->getRotate_array().get(oiu)->getValue().get(3);
//Values read in are angle-axis, therefore we must convert it to a quaternion.
				q1.w = q1.w * (PI/180.0f);//change to radians becos values are in degrees
				q1 = q1.convertToQuat();

				q1.Normalize();			
	//Combine all rotation arrays into 1 quaternion
				Final =  Final * q1;
				Final.Normalize();
			}
			//Finally apply MaxToGL to reorient coordinate system.
			Final = Final * MaxToGL ;

In the same way, MaxToGL is applied to all animation keyframes, i can post the code if needed. But this is basically what im doing in order to reorient the animation.

Hi GamerSg,

I can see you swap y and z manually in translation array, then how about the rotation array? The most important thing is what you’ve done in Quat::convertToQuat(). Hope you did the right thing in this function:)

Man this is a toughie to be honest.

I think that’s handled by the final *MaxToGL in the last line.

Some things you might want to check:

(1) “MaxToGL.w = -90.0f * (PI/180.0f);” Shouldn’t that be 90 instead of -90?

(2) Is your convertToQuat function correct?

(3) Are you sure you want

Final =  Final * q1;
...
Final = Final * MaxToGL ;

instead of

Final =  q1 * Final;
...
Final = MaxToGL * Final;

(4) Are you passing the transform to OpenGL correctly?

Without actually being able to sit down and debug your code with a simple test case, it’s hard :(. (1) and (3) are my best guesses as to what’s going wrong.

(1) “MaxToGL.w = -90.0f * (PI/180.0f);” Shouldn’t that be 90 instead of -90?

Hi sthomas,

what’s the full expression of MaxToGL?
like this:
Quaternion MaxToGL(1,0,0,-90.0f * (PI/180.0f))?

GamerSg posted it in his last post:

//The Quaternion which will transform Max transformations to OpenGL
   AngleAxis MaxToGL;
   MaxToGL.x = 1.0f;
   MaxToGL.w = -90.0f * (PI/180.0f);//convert to radian
   MaxToGL = MaxToGL.convertToQuat();
   MaxToGL.Normalize();

With alot of tinkering around and looking through everything, i think i have managed to solve the rotation problem, or so i thought. My current problem is Collada related, how do i find out the orientation of a bone during binding(Bone added to skin modifier)?

To get a basic example working, i created a bone with 0 0 0 rotation. My mesh was a teapot on which i applied the skin modifier and added the bone to the skin modifier. So now, my bone was bound with a rotation of 0 0 0. If i rotate the bone, the mesh will rotate with it, meaning if i set the bone’s rotation to -90 0 0, my mesh will be rotated -90 degrees around the x axis as well. Everything is fine.

Now this is when it screws up, Say i created a bone with -90 0 0 and then bound it to my mesh, my mesh would not be affected by the -90 x axis rotation because that is the rotation with which the bone was bound to the skin. This is expected in Max. But how can i tell from the Collada file?

If i were to export my mesh, both cases give me the same result in the resulting collada file, whereas in Max, the latter case shows the mesh unaffected by the Bone’s default rotation.

The transform matrix at which the mesh was bound to the skin is saved in the COLLADA file, within the <bind_shape_matrix> element of the skin controller. I think that’s the extra information you are currently missing.

Sincerely,