Hey all,
I'm trying to combine multiple primitive transforms into a single matrix by creating a matrix for each one and multiplying a cumulative product matrix by each in sequence. So something like:
matrix4 finalTransform;
finalTransform *= someRotationTransform;
finalTransform *= someTranslationTransform;
...
The problem is that I can't figure out how to create a rotation transform matrix that preserves uniform scale (1, 1, 1). I tried both matrix4.setRotationRadians and quaternion.getMatrix and both mess up the scale of the resulting matrix. Bottom line is that I need to create a matrix that contains purely rotation and nothing else. I'm sure there's a simple solution to this but I've spent hours trying different things and searching the web with no luck. Thanks in advance!
How can I add a rotation to a matrix without changing scale?
-
- Posts: 4
- Joined: Sat Mar 15, 2008 8:40 am
-
- Posts: 4
- Joined: Sat Mar 15, 2008 8:40 am
Example
Here's a code example illustrating this problem (bug?):
The output:
cubeNode: rotation(0.000000, 44.999996, 0.000000) scale(0.707107, 1.000000, 0.707107)
Code: Select all
// create a cube and rotate it 45 degrees
ISceneNode* cubeNode = smgr->addCubeSceneNode();
cubeNode->setRotation(core::vector3df(0, 45, 0));
// get absolute transformation
cubeNode->updateAbsolutePosition();
matrix4 trans = cubeNode->getAbsoluteTransformation();
// print rotation and scale
vector3df rot = trans.getRotationDegrees();
vector3df scale = trans.getScale();
printf("cubeNode: rotation(%f, %f, %f) scale(%f, %f, %f)\n",
rot.X, rot.Y, rot.Z, scale.X, scale.Y, scale.Z);
cubeNode: rotation(0.000000, 44.999996, 0.000000) scale(0.707107, 1.000000, 0.707107)
Well would this not suffice in setting the scale:
And from what you posted it looks as if it is using the cos() and sin(), or one or the other, for the values. Because .707107 == sqr(2)/2.
Code: Select all
cubeNode->setScale(vector3df(1.0f, 1.0f, 1.0f));
TheQuestion = 2B || !2B
-
- Posts: 4
- Joined: Sat Mar 15, 2008 8:40 am
Manually setting scale won't quite work
Halifax, thanks for the quick reply! Yeah you'd think you could just save the original scale from before the rotation and then multiply by (original/modified) after the rotation. The trick is getting a matrix representing a scale transform of (original/modified), which I can't figure out how to do without messing up the rotation. Some revised code to illustrate:
Output:
cubeNode: rotation(0.000000, 44.999996, 0.000000) scale(0.707107, 1.000000, 0.707107)
cubeNode: rotation(0.000000, 89.980217, -0.000000) scale(1.000000, 1.000000, 1.000000)
So if you try to create a rotation matrix, the scale of that matrix is messed up, and if you try to create a scale matrix, the rotation gets messed up. This is because the scale and rotation are stored in overlapping cells inside the transform matrix. This page quickly explains how the transform matrix is structured: http://www.ruthless.zathras.de/facts/ap ... matrix.php
I think that to solve this problem, you'd need to come up with formulas where the input is the desired rotation and scale and the output is the nine cells of the transform matrix where rotation and scale are stored. I'm sure this must have been solved many times over by many people but I'm not sure what to search for :[
Code: Select all
// create a cube and rotate it 45 degrees
ISceneNode* cubeNode = smgr->addCubeSceneNode();
cubeNode->setRotation(core::vector3df(0, 45, 0));
cubeNode->updateAbsolutePosition();
// print cube rotation and scale before adjusting scale
matrix4 trans = cubeNode->getAbsoluteTransformation();
vector3df rot = trans.getRotationDegrees();
vector3df scale = trans.getScale();
printf("cubeNode: rotation(%f, %f, %f) scale(%f, %f, %f)\n",
rot.X, rot.Y, rot.Z, scale.X, scale.Y, scale.Z);
// try to adjust scale back to (1, 1, 1)
cubeNode->setScale(vector3df(1.0f / scale.X, 1.0f / scale.Y, 1.0f / scale.Z));
cubeNode->updateAbsolutePosition();
// print cube rotation and scale after adjusting scale
trans = cubeNode->getAbsoluteTransformation();
rot = trans.getRotationDegrees();
scale = trans.getScale();
printf("cubeNode: rotation(%f, %f, %f) scale(%f, %f, %f)\n",
rot.X, rot.Y, rot.Z, scale.X, scale.Y, scale.Z);
cubeNode: rotation(0.000000, 44.999996, 0.000000) scale(0.707107, 1.000000, 0.707107)
cubeNode: rotation(0.000000, 89.980217, -0.000000) scale(1.000000, 1.000000, 1.000000)
So if you try to create a rotation matrix, the scale of that matrix is messed up, and if you try to create a scale matrix, the rotation gets messed up. This is because the scale and rotation are stored in overlapping cells inside the transform matrix. This page quickly explains how the transform matrix is structured: http://www.ruthless.zathras.de/facts/ap ... matrix.php
I think that to solve this problem, you'd need to come up with formulas where the input is the desired rotation and scale and the output is the nine cells of the transform matrix where rotation and scale are stored. I'm sure this must have been solved many times over by many people but I'm not sure what to search for :[
-
- Posts: 4
- Joined: Sat Mar 15, 2008 8:40 am
Nevermind - realized my misunderstanding: although you can create a scaling matrix by setting three cells, you can't extract scale by simply reading the values of those cells. Scale permeates other cells in ways that I don't understand yet :] Anyway I found a simple workaround for my specific implementation problem so all is well. Thanks again!
Alright, and yeah it is using the sin() for the scale in your examples above. Can you post your solution? (I am sure other people will run into it down the road.)snuggybear wrote:Nevermind - realized my misunderstanding: although you can create a scaling matrix by setting three cells, you can't extract scale by simply reading the values of those cells. Scale permeates other cells in ways that I don't understand yet :] Anyway I found a simple workaround for my specific implementation problem so all is well. Thanks again!
TheQuestion = 2B || !2B