How can I add a rotation to a matrix without changing scale?

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
snuggybear
Posts: 4
Joined: Sat Mar 15, 2008 8:40 am

How can I add a rotation to a matrix without changing scale?

Post by snuggybear »

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!
snuggybear
Posts: 4
Joined: Sat Mar 15, 2008 8:40 am

Example

Post by snuggybear »

Here's a code example illustrating this problem (bug?):

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);
The output:

cubeNode: rotation(0.000000, 44.999996, 0.000000) scale(0.707107, 1.000000, 0.707107)
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

Post by Halifax »

Well would this not suffice in setting the scale:

Code: Select all

cubeNode->setScale(vector3df(1.0f, 1.0f, 1.0f));
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.
TheQuestion = 2B || !2B
snuggybear
Posts: 4
Joined: Sat Mar 15, 2008 8:40 am

Manually setting scale won't quite work

Post by snuggybear »

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:

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); 
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 :[
snuggybear
Posts: 4
Joined: Sat Mar 15, 2008 8:40 am

Post by snuggybear »

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!
Halifax
Posts: 1424
Joined: Sun Apr 29, 2007 10:40 pm
Location: $9D95

Post by Halifax »

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!
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.)
TheQuestion = 2B || !2B
Post Reply