Rotating a rotated node correctly

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Rotating a rotated node correctly

Post by robmar »

Imagine you want to use a model in your game, but that model was produced such that its facing upwards, rotated 90 degrees on its X-axis, what do you do? Just rotate it back -90 degrees right?

Yes, but then if you´ve got mixed models, some rotated, some not, and general code to control the orientation (rotation of models), then you have to add the required rotation to the object, taking into account its existing orientation.

With rotation of a node´s Z-axis this works (consider that the node´s Y-axis is rotated to the world X-axis, and the X-axis becomes vertical, which must be then adjusted to sping the node around over the floor) but with rotation of the X-axis, which puts the Z-axis vertical, it doesn´t work, and the node´s Y-axis, still rotates the model around the floor!

This problem seems to relate to the way the xyz rotation angles are processed by Irrlicht, causing what is called gimble lock to occur.

To solve this problem ... anyone got a tidy solution to this????

In this code below, which doesn´t yet solve this problem (help!) we include the facility to rotate the rotated node around a centre point in space.

This code below is a first rought attempt to provide a solution to object rotation under these circumstances:-

Code: Select all

 
// If new node rotation set, the node must be rotated, however, this needs to take into account the object´s existing rotation
// 1. get up-vector of current node rotation
// 2. multiply each axis of it by requried rot in degrees
 
 core::vector3df up(0.f,1.f,0.f);   Set to standard up-vector
 up.rotateYZBy( pListObjects->rot3D.X );  // Rotate all axis to requried direction in degrees
 up.rotateXZBy( pListObjects->rot3D.Y );
 up.rotateXYBy( pListObjects->rot3D.Z );
 // We now have the object´s actual up vector based on its rotation
 
// We will consider only the "Y" rotation only of node (spin over floor), regardless of any current rotation of node:-
// By multiplying the object´s up vector by the required node rotation (Y) input by the user (to be applied onto current rotation), we move the required rotation to be applied, to the correct (rotated) axis of the node.
// Note: The below tests (!up.Z) work even with very small values near to zero, but its probably not the best way to do it! 
if ( !up.Z ) // If not rotated on x-axis (i.e. Z-up), so Y or X are vertical rotation axis
 {
 if ( up.X )
 _3dfPasteObjectRotationAdj.X = m_3dfPasteObjectRotation.Y;  Set spin rotation to node´s Y-axis, which is its X-axis
 else
 _3dfPasteObjectRotationAdj.Y = m_3dfPasteObjectRotation.Y;
 }
 else // If rotated on x-axis, rotation stays on Y! No idea why yet!! ///todo3d????
 {
 _3dfPasteObjectRotationAdj.Z = m_3dfPasteObjectRotation.Y;
 }
 // The "3dfPasteObjectRotationAdj" rotation should now be ready to add to the nodes current rotation
 
// ???? Do we need to process rotation directions here or above?
 
 // Test code to rotate a new rotation to match new object rotation
 //_3dfRotV = pListObjects->rot3D;
 //fLenRotV =_3dfRotV.getLength();
 //_3dfRotV.normalize();
 //adjObjRot = (_3dfPasteObjectRotationAdj.rotationToDirection( _3dfRotV )).normalize() * fLenRotV; 
 
// pListObjects->rot3D = adjObjRot + _3dfPasteObjectRotationAdj; // Add rotation to object
 pListObjects->rot3D += _3dfPasteObjectRotationAdj; // Add actual "Y" rotation to object
 pListObjects->rot3D.X = fmod( pListObjects->rot3D.X, 360.0f ); // Keep to bounds
 pListObjects->rot3D.Y = fmod( pListObjects->rot3D.Y, 360.0f );
 pListObjects->rot3D.Z = fmod( pListObjects->rot3D.Z, 360.0f );
 
 // Rotate animation-rotation to match new object rotation
 _3dfRotV = pListObjects->m_a3dfRotVector;
 fLenRotV =_3dfRotV.getLength();
 _3dfRotV.normalize();
 pListObjects->m_a3dfRotVector = (_3dfPasteObjectRotationAdj.rotationToDirection( _3dfRotV )).normalize() * fLenRotV; 
// Check if object to be rotated around centre point
 if ( m_3dfPasteSpaceRotation.Y != 0.0f ) // Then rotate the coordinates in 3D space
 rotateAroundCentre( pListObjects->m_3dfObjCentre, m_3dfRotCentre, m_3dfPasteSpaceRotation );
 
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Re: Rotating a rotated node correctly

Post by christianclavet »

HI, Robmar!

Yes, there is a solution (I would hope someday, somebody release the code for that or even put it into Irrlicht for it to be used directly)
You must rotate based on Quarternions (Rotating based on Euler angles will create the "gimbal lock" problem. My math skills are too poor at the moment to tackle that adequately.

A convertion function I would love to have is:
- Input the base angle as Euler angle,
- Then enter the offset also in Euler angle,
- Use theses infos and convert/rotate the node from quaternions operation.
- Current final angle can be get in Quaternions and/or Euler notation

Here some info about this:
http://en.wikipedia.org/wiki/Gimbal_lock#Solutions
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: Rotating a rotated node correctly

Post by robmar »

Hi Christian, I agree with you completely! Thanks for the link, I´ll take a look.

I have tried what you suggested, setting one quarernion to the base orientation, then multiplying by the rotation to add, but it didnt produce the expected result.

I am sure that the Irrlicht guys know how to solve this problem, but I guess they are busy with other stuff. We sort it out one day for sure!
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Re: Rotating a rotated node correctly

Post by christianclavet »

That code from SMSO look really near a solution:
To rotate a node about its local y-axis by, say, 15deg. I do this

Code: Select all

core::matrix4 matrix = node->getAbsoluteTransformation();
core::vector3df yaxis(0.0f, 1.0f, 0.0f); // yaxis in world space
matrix.rotateVect(yaxis); // yaxis in local space
yaxis.normalize();
 
core::quaternion q;
q.fromAngleAxis(15.0f*core::DEGTORAD, yaxis);
 
core::vector3df rot = q.getMatrix().getRotationDegrees();
node->setRotation(rot);
http://irrlicht.sourceforge.net/forum/v ... n+rotation

So for rotating an object say that initial rotation is 10,30,0 and you want it to rotate at 10,40,0. You will have to find the orientation vector from theses 2 angles, then get the angle distance to do the proper move. Again, I'm really not an expert on this and can't on my own provide a "code" or "math" solution for this.

From the example given in code, he only defined the orientation vector to be pointing UP (on the Y vector axe line, and then rotating on this) If I understand correctly the code...

One other thing to note, all rotation input in the quaternion MUST be in radian. Irrlicht as a convertion function as in that line:

Code: Select all

q.fromAngleAxis(15.0f*core::DEGTORAD, yaxis);
smso
Posts: 246
Joined: Fri Jun 04, 2010 3:28 pm
Location: Hong Kong

Re: Rotating a rotated node correctly

Post by smso »

Something like this:

Code: Select all

void rotateNodeFromTo(scene::ISceneNode* node, const core::vector3df& from, const core::vector3df& to)
{
    if (core::iszero(from.getLengthSQ()))
        return;
    if (core::iszero(to.getLengthSQ()))
        return;
    
    core::vector3df dir1 = from;
    core::vector3df dir2 = to;    
    
    dir1.normalize();
    dir2.normalize();
    
    core::quaternion q;
    q.rotationFromTo(dir1, dir2);
    
    
    core::matrix4 m1 = q.getMatrix();
    core::matrix4 m2 = node->getAbsoluteTransformation();
    
    node->setRotation((m1*m2).getRotationDegrees());
}
 
//rotateNodeFromTo(node, core::vector3df(1.0f, 0.0f, 0.0f), core::vector3df(1.0f, 0.0f, 0.1f));
Regards,
smso
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: Rotating a rotated node correctly

Post by robmar »

That looks good, but if the node doesn´t yet exist, if node positions are being calculated prior to creation based on exisiting oritentation and rotation data, this call has to be replaced:

core::matrix4 m2 = node->getAbsoluteTransformation();

with

core::matrix4 m2 = ???
smso
Posts: 246
Joined: Fri Jun 04, 2010 3:28 pm
Location: Hong Kong

Re: Rotating a rotated node correctly

Post by smso »

robmar wrote:That looks good, but if the node doesn´t yet exist, if node positions are being calculated prior to creation based on exisiting oritentation and rotation data, this call has to be replaced:

core::matrix4 m2 = node->getAbsoluteTransformation();

with

core::matrix4 m2 = ???
In short, you have to find the relevant 3D transformation matrices, say, m3, m4, m5, and multiply them:

Code: Select all

// N.B. this means m5 acts on the node first, followed by m4 and then m3!
core::matrix4 m2 = m3 * m4 * m5;
 
But how?
The following should keep you going
(1)
google:
+matrix +rotate +scale +site:.edu

(2)
http://www.cs.trinity.edu/~jhowland/cs3353/intro/intro/

(3)
look at the source code of irrlicht's:
matrix4.h
and see how M[0], M[1], ..., M[15] are filled up

(4)
beware of row-major order Vs column-major order in matrices
http://en.wikipedia.org/wiki/Row-major_order

Regards,
smso
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: Rotating a rotated node correctly

Post by robmar »

What does getabsolutetransformation take into account? I guess I can step through the code to see.
smso
Posts: 246
Joined: Fri Jun 04, 2010 3:28 pm
Location: Hong Kong

Re: Rotating a rotated node correctly

Post by smso »

robmar wrote:What does getabsolutetransformation take into account? I guess I can step through the code to see.
This takes the transformation matrices of all the parents of the node into account and represents a property in world space.

Explanation
==========
For a hierarchy of nodes, like,

node A -> node B -> node C

where:
node A is the parent of node B,
node B is the parent of node C
and node A itself does not have any parent.

And if the relative transformation matrices of the nodes are mat_A, mat_B and mat_C respectively,
then the absolute transformation matrix of node C is:

mat_A * mat_B * mat_C


Regards,
smso
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Re: Rotating a rotated node correctly

Post by christianclavet »

Nice!
smso wrote:Something like this:

Code: Select all

void rotateNodeFromTo(scene::ISceneNode* node, const core::vector3df& from, const core::vector3df& to)
{
    
    core::quaternion q;
    q.rotationFromTo(dir1, dir2);
   
}
 
//rotateNodeFromTo(node, core::vector3df(1.0f, 0.0f, 0.0f), core::vector3df(1.0f, 0.0f, 0.1f));
Regards,
smso
I really would like to use this to make my node rotate in an editor (could be IRB or FK editor or anything else) by inputting "offsets" coming from the mouse (mouse movements). So the user could rotate his node without suffering gimbal lock.

What are the format for those 2 angles? How does it work?

Can we use this?: Take the "original" angle of the node, then rotate it at that angle in degree? (15.0,22,0)?

Code: Select all

rotateNodeFromTo(node, core::vector3df(0.0f, 0.0f, 0.0f), core::vector3df(15.5f, 22.0f, 0.0f));
. So the "dir1" would be the origin of the node and "dir2" would be the offset rotation.

Does it need to be translated in Radian first? The trick to multiply the matrices are really cool! I'm checking the API doc for the quaternion and strangely I think I understand more now! I think I will have to try myself to see If I could do it. Thanks again for your examples and explanations!
smso
Posts: 246
Joined: Fri Jun 04, 2010 3:28 pm
Location: Hong Kong

Re: Rotating a rotated node correctly

Post by smso »

In the function

Code: Select all

//rotateNodeFromTo(node, core::vector3df(1.0f, 0.0f, 0.0f), core::vector3df(1.0f, 0.0f, 0.1f));

Code: Select all

core::vector3df(1.0f, 0.0f, 0.0f)
is the vector in the x-direction, NOT the 1 degree x-rotation.

Code: Select all

core::vector3df(1.0f, 0.0f, 0.1f)
is the vector nearly in the x-direction, NOT the 1 degree x-rotation plus 0.1 degree z-rotation.


If you do this for a node without any parent:

Code: Select all

node->setRotation(core::vector3df(0.0f, 0.0f, 0.0f));
what is its orientation or direction in world space?

core::vector3df(0.0f, 0.0f, 0.0f) ? NO!
The answer is that you have to load the node in irrlicht to see the orientation and it is the value ASSIGNED by you.
The following may help to visualize

Code: Select all

// simple usage:
// createLocalAxes(smgr, node);
 
core::array<scene::IAnimatedMeshSceneNode*> createLocalAxes
(
        scene::ISceneManager* smgr,
        scene::ISceneNode* parent,
        f32 scale = 1.0f
)
{
        core::array<scene::IAnimatedMeshSceneNode*> axes;
        scene::IAnimatedMeshSceneNode* xAxis = 0;
        scene::IAnimatedMeshSceneNode* yAxis = 0;
        scene::IAnimatedMeshSceneNode* zAxis = 0;
        
        f32 parentScale = parent->getScale().Y;
        
        f32 h = scale;
        f32 ch = h * 0.6f;
        f32 cd = h * 0.05f;
        f32 d = h * 0.3f;
        
        xAxis = smgr->addAnimatedMeshSceneNode
        (
                smgr->addArrowMesh
                (
                        "xAxis",
                        video::SColor(255, 255,0,0),
                        video::SColor(255, 255,0,0),
                        4, 8,
                        h, ch, cd, d
                )
        );
        xAxis->setMaterialFlag(video::EMF_LIGHTING, false);
        xAxis->setMaterialFlag(video::EMF_BACK_FACE_CULLING, false);
        //xAxis->setMaterialFlag(video::EMF_ZBUFFER, false);
        xAxis->setParent(parent);
        xAxis->setScale(core::vector3df(scale));
        
        xAxis->setRotation(core::vector3df(0.0f, 0.0f, 90.0f));
        
        
        yAxis = smgr->addAnimatedMeshSceneNode
        (
                smgr->addArrowMesh
                (
                        "yAxis",
                        video::SColor(255, 0,255,0),
                        video::SColor(255, 0,255,0),
                        4, 8,
                        h, ch, cd, d
                )
        );
        yAxis->setMaterialFlag(video::EMF_LIGHTING, false);
        yAxis->setMaterialFlag(video::EMF_BACK_FACE_CULLING, false);
        yAxis->setParent(parent);
        yAxis->setScale(core::vector3df(scale));
        
        zAxis = smgr->addAnimatedMeshSceneNode
        (
                smgr->addArrowMesh
                (
                        "zAxis",
                        video::SColor(255, 0,0,255),
                        video::SColor(255, 0,0,255),
                        4, 8,
                        h, ch, cd, d
                )
        );
        zAxis->setMaterialFlag(video::EMF_LIGHTING, false);
        zAxis->setMaterialFlag(video::EMF_BACK_FACE_CULLING, false);
        //zAxis->setMaterialFlag(video::EMF_ZBUFFER, false);
        zAxis->setParent(parent);
        zAxis->setScale(core::vector3df(scale));
        
        zAxis->setRotation(core::vector3df(90.0f, 0.0f, 0.0f));
 
        axes.push_back(xAxis);
        axes.push_back(yAxis);
        axes.push_back(zAxis);
        
        return axes;
}

Regards,
smso
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: Rotating a rotated node correctly

Post by robmar »

SMSO wrote "This takes the transformation matrices of all the parents of the node into account and represents a property in world space."

Thanks SMSO, much appreciated, I have it clear now.
Post Reply