Matrix Rotation Around Local Axes

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Matrix Rotation Around Local Axes

Post by Acki »

Hi,
long time since I was here the last time...
great my account is still active !!! :)

You're my last hope !!! :D

I have a problem with Matrix rotations...

I want to rotate around the local axes...
all I found is this approach:

Code: Select all

 
Matrix.Rotatef(rX, 1, 0, 0);
Matrix.Rotatef(rY, 0, 1, 0);
Matrix.Rotatef(rZ, 0, 0, 1);
 
but this rotates the axes locally around the absolute axes !!!
because first Z is rotated the up-vector for Y is not (0, 1, 0) anymore !!!
and after rotating Y the up-vector for X is not (1, 0, 0) anymore !!!

if I'm not mistaken it should look like this:

Code: Select all

 
float cosZ = Math.cos(Math.toRadians(rZ));
float sinZ = Math.sin(Math.toRadians(rZ));
 
Matrix.Rotatef(rX, ?, ?, ?);
Matrix.Rotatef(rY, -sinZ, cosZ, 0);
Matrix.Rotatef(rZ, 0, 0, 1);
 
but how do I calculate the up-vector for X ???
and is my calculation for Y even correct ???

thanks
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
Seven
Posts: 1030
Joined: Mon Nov 14, 2005 2:03 pm

Re: Matrix Rotation Around Local Axes

Post by Seven »

glad to see you back!
sorry I cant be of much help but maybe this will jog something for you.....

I use these for movement.
to move forward i use
newposition = getabsoluteposition() + getIn() * movespeed;

to move up (regardless of my current rotation)
newposition = getabsoluteposition() + getUp() * movespeed;

Code: Select all

 
    vector3df IGE_Object::getIn()
    {
        if (getPrimarySceneNode())
        {
            matrix4 mat = getPrimarySceneNode()->getRelativeTransformation();
            vector3df in(mat[8], mat[9], mat[10]);
            in.normalize();
            return in;
        }
        else return vector3df(0, 0, 0);
    }
 
    vector3df IGE_Object::getLeft()
    {
        if (getPrimarySceneNode())
        {
            matrix4 mat = getPrimarySceneNode()->getRelativeTransformation();
            vector3df left(mat[0], mat[1], mat[2]);
            left.normalize();
            return left;
        }
        else return vector3df(0, 0, 0);
    }
 
    vector3df IGE_Object::getUp()
    {
        if (getPrimarySceneNode())
        {
            matrix4 mat = getPrimarySceneNode()->getRelativeTransformation();
            vector3df up(mat[4], mat[5], mat[6]);
            up.normalize();
            return up;
        }
        else return vector3df(0, 0, 0);
    }
 
if it helps great, if not, well I still enjoyed posting something :)
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Re: Matrix Rotation Around Local Axes

Post by Acki »

thanks for the reply !!! :)

but no, this is the other way around...
you get the transformation and then you get the rotation axes and calculate the vectors (up,left,front)...

but I have a rotation and want it apply to the transformation !!!

I have this problem with OpenGL on Android...
and I made a video showing the problem...
https://www.youtube.com/watch?v=F62RFPDBjHI

but Irrlicht does the same "wrong" rotation on the transformation matrix !!!
I wrote this code to test it...
it does exactly the same in Irrlicht as in the video with Java... ;)

you'll need 2 different textures, but the code is easy to read... :D

Code: Select all

#include <windows.h> // for delta time
#include <irrlicht.h>
 
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
 
void showErrors(array<ISceneNode*>* rows, IVideoDriver* driver){
    // use 2 different textures to show the wrong rotations
    ITexture* txtGood = driver->getTexture("gfx/wood.jpg");
    ITexture* txtFalse = driver->getTexture("gfx/wall.jpg");
 
    // rotation around X is always correct
    rows[0][0]->setMaterialTexture(0, txtGood);
    rows[0][1]->setMaterialTexture(0, txtGood);
    rows[0][2]->setMaterialTexture(0, txtGood);
    rows[0][3]->setMaterialTexture(0, txtGood);
 
    // rotation around Y is NOT always correct
    rows[1][0]->setMaterialTexture(0, txtGood);
    rows[1][1]->setMaterialTexture(0, txtFalse);
    rows[1][2]->setMaterialTexture(0, txtGood);
    rows[1][3]->setMaterialTexture(0, txtFalse);
 
    // rotation around Z is only correct if not rotated on the other axes
    rows[2][0]->setMaterialTexture(0, txtGood);
    rows[2][1]->setMaterialTexture(0, txtFalse);
    rows[2][2]->setMaterialTexture(0, txtFalse);
    rows[2][3]->setMaterialTexture(0, txtFalse);
}
 
array<ISceneNode*>* createScene(IrrlichtDevice* device){
    array<ISceneNode*>* rowNodes = new array<ISceneNode*>[3];
    ISceneManager* smgr = device->getSceneManager();
 
    for(int t = 0; t < 4; ++t) {
        // create 4 nodes in 3 rows
        ISceneNode* row1 = smgr->addCubeSceneNode(1);
        ISceneNode* row2 = smgr->addCubeSceneNode(1);
        ISceneNode* row3 = smgr->addCubeSceneNode(1);
 
        // top row rotates around x, so x is doubled
        row1->setScale(vector3df(2, 1, 1));
        row1->setPosition(vector3df(0, 5, t * 5 - 7.5));
        row1->setMaterialFlag(EMF_LIGHTING, false);
 
        // middle row rotates around y, so y is doubled
        row2->setScale(vector3df(1, 2, 1));
        row2->setPosition(vector3df(0, 0, t * 5 - 7.5));
        row2->setMaterialFlag(EMF_LIGHTING, false);
 
        // bottom row rotates around z, so z is doubled
        row3->setScale(vector3df(1, 1, 2));
        row3->setPosition(vector3df(0, -5, t * 5 - 7.5));
        row3->setMaterialFlag(EMF_LIGHTING, false);
 
        // add the nodes to the rows
        rowNodes[0].push_back(row1);
        rowNodes[1].push_back(row2);
        rowNodes[2].push_back(row3);
 
    }
 
    // top row (rotates around x)
    // 1st box is not rotated
    rowNodes[0][0]->setRotation(vector3df(0,0,0));
    // 2nd box is rotated around y
    rowNodes[0][1]->setRotation(vector3df(0,45,0));
    // 3rd box is rotated around z
    rowNodes[0][2]->setRotation(vector3df(0,0,45));
    // 4th box is rotated around y and z
    rowNodes[0][3]->setRotation(vector3df(0,45,45));
 
    // middle row (rotates around y)
    // 1st box is not rotated
    rowNodes[1][0]->setRotation(vector3df(0,0,0));
    // 2nd box is rotated around x
    rowNodes[1][1]->setRotation(vector3df(45,0,0));
    // 3rd box is rotated around z
    rowNodes[1][2]->setRotation(vector3df(0,0,45));
    // 4th box is rotated around x and z
    rowNodes[1][3]->setRotation(vector3df(45,0,45));
 
    // bottom row (rotates around z)
    // 1st box is not rotated
    rowNodes[2][0]->setRotation(vector3df(0,0,0));
    // 2nd box is rotated around x
    rowNodes[2][1]->setRotation(vector3df(45,0,0));
    // 3rd box is rotated around y
    rowNodes[2][2]->setRotation(vector3df(0,45,0));
    // 4th box is rotated around x and y
    rowNodes[2][3]->setRotation(vector3df(45,45,0));
 
    showErrors(rowNodes, device->getVideoDriver());
 
    return rowNodes;
}
 
void updateScene(array<ISceneNode*>* rows){
    // delta from time (Windows)
    static long msLast = GetTickCount();
    float delta = (GetTickCount() - msLast) * 0.05;
    msLast = GetTickCount();
 
    // rotate each row around a specific axis
    for(int t = 0; t < 4; ++t) {
        // top row rotates around X
        vector3df r0 = rows[0][t]->getRotation();
        r0.X += delta;
        rows[0][t]->setRotation(r0);
 
        // middle row rotates around Y
        vector3df r1 = rows[1][t]->getRotation();
        r1.Y += delta;
        rows[1][t]->setRotation(r1);
 
        // bottom row rotates around Z
        vector3df r2 = rows[2][t]->getRotation();
        r2.Z += delta;
        rows[2][t]->setRotation(r2);
    }
}
 
int main(){
    //! create Irrlicht
    IrrlichtDevice* device = createDevice(EDT_OPENGL, dimension2du(800, 600));
    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr = device->getSceneManager();
 
    //! create the camera
    ICameraSceneNode* cam = smgr->addCameraSceneNode();
    cam->setPosition(vector3df(15,0,0));
    cam->setTarget(vector3df(0,0,0));
 
    //! create the scene
    array<ISceneNode*>* the_cubes = createScene(device);
 
    //! main loop
    while(device->run()){
        updateScene(the_cubes);
        driver->beginScene(true, true, video::SColor(0,60,110,160));
        smgr->drawAll();
        driver->endScene();
    }
 
    //! close program
    device->drop();
    return 0;
}
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
CuteAlien
Admin
Posts: 9644
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Matrix Rotation Around Local Axes

Post by CuteAlien »

As soon as you apply 2 rotations one after another you will always get a rotation order - you can test that in real-life. If you want to handle this by rotating around some common axis in between you have to do some rotation around an axis (quaternions help with that - they have a function in irrlicht to rotate around an axis... and it's kinda what they do anyway). But you have to figure out which axis you mean by that then.

If you only rotate a single axis at a time then my rotateAxesXYZToEuler function here might help: http://irrlicht.sourceforge.net/forum/v ... =4&t=52136
Depending on useLocalAxes parameter it rotates around current or about global axes.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Re: Matrix Rotation Around Local Axes

Post by Acki »

ahh, a familiar name !!! :D
how do you do?
CuteAlien wrote:As soon as you apply 2 rotations one after another you will always get a rotation order - you can test that in real-life.
exactly !!!
but I guess nobody gets what the (in my opinion) problem/error is, even in Irrlicht !!!

all I see everywhere is this rotation sequence:

Code: Select all

rotatef(rx, 1,0,0);
rotatef(ry, 0,1,0);
rotatef(rz, 0,0,1);
sometimes the order is reversed (from the test I guess this is what Irrlicht uses):

Code: Select all

rotatef(rz, 0,0,1);
rotatef(ry, 0,1,0);
rotatef(rx, 1,0,0);
btw. I know the multiplication is from right to left, so the bottom rotation is executed first... ;)

but the problem is not the order of rotations, but the UP-VECTORS, the tripple after the rotation (1,0,0) (0,1,0) and (0,0,1) !!!
this are the WORLD-UP-VECTORS !!!

the first rotation can be done around one of this vectors, because object and world axes do align at start...
but the following rotations can't (should not) use them after that !!!

it's hard to explain in text...
so I made a video...
https://www.youtube.com/watch?v=uvMLs7N2DlY

hope this makes more clear what I mean... :P
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
CuteAlien
Admin
Posts: 9644
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Matrix Rotation Around Local Axes

Post by CuteAlien »

Yeah, check the code I linked, it does allow you to do local rotations. But only 1 axis at a time. You can't rotate at 2 axis at the same time (like you want to have with a controller) - that needs you to figure out which axis you really want then. But the code works well for an editor where you just want switch between rotating around global/local axes (that's what I use it for).

It's btw not a problem in Irrlicht, it's just how Euler angles work. They just are not very nice to use for adding rotations as soon as more than one axis is involved, for that it's easier to go over matrices.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Re: Matrix Rotation Around Local Axes

Post by Acki »

yeah, I fully aggree...
but we're talking past each other...

let me phrase my whole question differently... :lol:

I have a vector:

Code: Select all

vector3df(1, 1, 1)
now I rotate that vector 45° around Z, so I ignore X for now (0, 1, 1)...
so X and Y will change to represent the new Y axis...
I calculate X and Y for the new Y vector like this:

Code: Select all

float newX = -sin(45); // right handed
float newY = cos(45);
or in other words the new vector now is:

Code: Select all

vector3df(-sin(45), cos(45), 1)
now I want to rotate that vector around it's Y axis, witch now is pointing at:

Code: Select all

vector3df(-sin(45), cos(45), 0)
and here I'm stuck !!!
how do I calculate the changes to vector3df(-sin(45), cos(45), 1) if I rotate around vector3df(-sin(45), cos(45), 0) !?!?!
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
CuteAlien
Admin
Posts: 9644
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Matrix Rotation Around Local Axes

Post by CuteAlien »

Uhm yes - that's rotation in the local coordinate system. I don't want to calculate this with sin/cos - easier with matrices. As I do in the link I gave you.

edit:
Matrices are easy because - you just set the 3 axes (the inner 3x3 matrix is basically 3 axes) in the direction you want stuff to point at.
And if you want it local instead of global - you still calculate it global (just set rotations) - then get the current matrix and multiply it to that one. In a way - you multiply matrices means: You add their operations. Which is something pretty hard to do with Euler angles or sin/cos. So with matrices you can always calculate as if you have a clean non-rotated object in it's original state. And then append your result to existing rotations.

My function is a bit more complicated because I don't use current matrix - but current rotations. Reason is that I want to ignore scale/translation which are also in the current matrix - so I build the matrix new instead from the rotation. So it's the current matrix without scaling/moving.

edit2:
Btw - you could certainly calculate it all with sin/cos. By calculating how much of the new X and Y you to have to regard in the next rotation from your previous rotation. And then you write down those equations - you'll end up exactly with the same operation a matrix multiplication is doing.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Re: Matrix Rotation Around Local Axes

Post by Acki »

I made another video to explain the problem I have with this...
https://www.youtube.com/watch?v=RrD-korYraE

and I apologize for the terms error and wrong, it's just one way how it's done, but not how I want it to do... :D

everything I do, I do with a matrix (4x4)...
maybe I don't get how to use it to get the result I want... :oops:

to use trigonometry is fine with me...
I just don't get the formula(s) for the last rotation around X !!!

some examples (code snipets) may help... ;)
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
CuteAlien
Admin
Posts: 9644
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Matrix Rotation Around Local Axes

Post by CuteAlien »

I watched this one as well. But still looks rotation in current local coordinate system to me. Which is what the rotateAxesXYZToEuler I linked above does. Sorry, maybe I'm missing something.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Re: Matrix Rotation Around Local Axes

Post by Acki »

yeah, I had a look at it...
but I can't use it, because OpenGL Matrix doesn't have the methods you're using there... :shock:
like rotateVect() and setRotationAxisRadians()...

I guessed they may are called differently...
so I tried (guessed) what methods it could be for the OGL matrix and came up with this...
but it still has the wrong rotations, just like with the normal approach, it seems it's screwed up even more now...

Code: Select all

float[] transMat = new float[16];
float[] rotMatX = new float[16];
float[] rotMatY = new float[16];
float[] rotMatZ = new float[16];
float[] posMat = new float[16];
float[] sclMat = new float[16];
 
Matrix.setIdentityM(transMat, 0);
Matrix.setIdentityM(posMat, 0);
Matrix.setIdentityM(rotMatX, 0);
Matrix.setIdentityM(rotMatY, 0);
Matrix.setIdentityM(rotMatZ, 0);
Matrix.setIdentityM(sclMat, 0);
 
Matrix.translateM(posMat, 0, node.Position.x, node.Position.y, node.Position.z);
Matrix.setRotateM(rotMatX, 0, node.Rotation.x, 1,0,1);
Matrix.setRotateM(rotMatY, 0, node.Rotation.y, 0,1,0);
Matrix.setRotateM(rotMatZ, 0, node.Rotation.z, 0,0,1);
Matrix.scaleM(sclMat, 0, node.Scale.x, node.Scale.y, node.Scale.z);
 
Matrix.setRotateEulerM(transMat, 0, 1, 0, 0);
Matrix.setRotateEulerM(transMat, 0, 0, 1, 0);
Matrix.setRotateEulerM(transMat, 0, 0, 0, 1);
        
Matrix.multiplyMM(transMat, 0, sclMat, 0, transMat, 0);
Matrix.multiplyMM(transMat, 0, rotMatZ, 0, transMat, 0);
Matrix.multiplyMM(transMat, 0, rotMatY, 0, transMat, 0);
Matrix.multiplyMM(transMat, 0, rotMatX, 0, transMat, 0);
Matrix.multiplyMM(transMat, 0, posMat, 0, transMat, 0);
 
// pass the matrix to OpenGL
_gl.glMultMatrixf(transMat, 0);
it's over my head... :?
I think the trigonometry approach makes more sense to me...
if only I knew how to calculate it... :oops:
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Re: Matrix Rotation Around Local Axes

Post by Acki »

ok, I had a look at Irrlicht's matrix4 for this methods...
and I guess I'm getting closer !!!
this methods use trigonometry, and it seems exactly what I want !!!

this are the three methods I need:

Code: Select all

    inline CMatrix4<T>& CMatrix4<T>::setRotationAxisRadians( const T& angle, const vector3d<T>& axis ){
        const f64 c = cos(angle);
        const f64 s = sin(angle);
        const f64 t = 1.0 - c;
 
        const f64 tx  = t * axis.X;
        const f64 ty  = t * axis.Y;     
        const f64 tz  = t * axis.Z;
 
        const f64 sx  = s * axis.X;
        const f64 sy  = s * axis.Y;
        const f64 sz  = s * axis.Z;
        
        M[0] = (T)(tx * axis.X + c);
        M[1] = (T)(tx * axis.Y + sz);
        M[2] = (T)(tx * axis.Z - sy);
 
        M[4] = (T)(ty * axis.X - sz);
        M[5] = (T)(ty * axis.Y + c);
        M[6] = (T)(ty * axis.Z + sx);
 
        M[8]  = (T)(tz * axis.X + sy);
        M[9]  = (T)(tz * axis.Y - sx);
        M[10] = (T)(tz * axis.Z + c);
 
        return *this;
    }
 
 
    inline void CMatrix4<T>::rotateVect( vector3df& vect ) const{
        vector3df tmp = vect;
        vect.X = tmp.X*M[0] + tmp.Y*M[4] + tmp.Z*M[8];
        vect.Y = tmp.X*M[1] + tmp.Y*M[5] + tmp.Z*M[9];
        vect.Z = tmp.X*M[2] + tmp.Y*M[6] + tmp.Z*M[10];
    }
 
 
    inline CMatrix4<T>& CMatrix4<T>::setRotationRadians( const vector3d<T>& rotation ){
        const f64 cr = cos( rotation.X );
        const f64 sr = sin( rotation.X );
        const f64 cp = cos( rotation.Y );
        const f64 sp = sin( rotation.Y );
        const f64 cy = cos( rotation.Z );
        const f64 sy = sin( rotation.Z );
 
        M[0] = (T)( cp*cy );
        M[1] = (T)( cp*sy );
        M[2] = (T)( -sp );
 
        const f64 srsp = sr*sp;
        const f64 crsp = cr*sp;
 
        M[4] = (T)( srsp*cy-cr*sy );
        M[5] = (T)( srsp*sy+cr*cy );
        M[6] = (T)( sr*cp );
 
        M[8] = (T)( crsp*cy+sr*sy );
        M[9] = (T)( crsp*sy-sr*cy );
        M[10] = (T)( cr*cp );
 
        return *this;
    }
but now I have another problem !!!
how do I port this methods to OpenGL ???

the Irrlicht matrix is row major with translations in the 4th row, Left-Handed...
but the OpenGL matrix is column major, Right-Handed (not sure where translations are stored, but probably in the 4th column)...

maybe someone can change the methods, so they work with OGL matrices, please !?!?! :D
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
CuteAlien
Admin
Posts: 9644
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Matrix Rotation Around Local Axes

Post by CuteAlien »

Don't worry too much, I think OpenGL matrix layout is the same in memory as Irrlicht and DirectX (it just has a stupid documentation which make it look like it's different).
And left-handed right handed also doesn't matter here (maybe you have to use -angle instead of angle to get the result you expect). Irrlicht directly passes it's matrices to OpenGL as they are (aside from texture-matrix, but that doesn't matter here).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Re: Matrix Rotation Around Local Axes

Post by Acki »

yeah, right...

the matrix array goes from 0 to 15 for both (DX and OGL)...
how they are interpreted internally is irrelevant, 0-3 is either a row or a column, but it's always the same (0-3) in the array !!! :D

LH or RH doesn't matter at the moment...
if the result rotates the wrong direction I simply have to negate the angle...

I ported your code and the needed methods from Irrlicht to Java...
but I still can't get it to work !!!
it gives strange non-linear rotations on some axes... :O

it works perfectly in Irrlicht !!!

this are the ported Irrlicht methods:

Code: Select all

    void setRotationAxisDegrees(float[] M, float angle, Vector3d axis ){
        double angle_rad = Math.toRadians(angle);
        double c = Math.cos(angle_rad);
        double s = Math.sin(angle_rad);
        double t = 1.0 - c;
 
        double tx  = t * axis.x;
        double ty  = t * axis.y;
        double tz  = t * axis.z;
 
        double sx  = s * axis.x;
        double sy  = s * axis.y;
        double sz  = s * axis.z;
 
        M[0] = (float) (tx * axis.x + c);
        M[1] = (float) (tx * axis.y + sz);
        M[2] = (float) (tx * axis.z - sy);
 
        M[4] = (float) (ty * axis.x - sz);
        M[5] = (float) (ty * axis.y + c);
        M[6] = (float) (ty * axis.z + sx);
 
        M[8] = (float) (tz * axis.x + sy);
        M[9] = (float) (tz * axis.y - sx);
        M[10] = (float) (tz * axis.z + c);
    }
 
    void rotateVect(float[] M, Vector3d vect){
        Vector3d tmp = new Vector3d(vect);
        vect.x = tmp.x * M[0] + tmp.y * M[4] + tmp.z * M[8];
        vect.y = tmp.x * M[1] + tmp.y * M[5] + tmp.z * M[9];
        vect.z = tmp.x * M[2] + tmp.y * M[6] + tmp.z * M[10];
    }
 
    void setRotationDegrees(float[] M, Vector3d rotation){
        double radX = Math.toRadians(rotation.x);
        double radY = Math.toRadians(rotation.y);
        double radZ = Math.toRadians(rotation.z);
        double cr = Math.cos(radX);
        double sr = Math.sin(radX);
        double cp = Math.cos(radY);
        double sp = Math.sin(radY);
        double cy = Math.cos(radZ);
        double sy = Math.sin(radZ);
 
        double srsp = sr * sp;
        double crsp = cr * sp;
 
        M[0] = (float)(cp * cy);
        M[1] = (float)(cp * sy);
        M[2] = (float)(-sp);
 
        M[4] = (float)(srsp * cy - cr * sy);
        M[5] = (float)(srsp * sy + cr * cy);
        M[6] = (float)(sr * cp);
 
        M[8] = (float)(crsp * cy + sr * sy);
        M[9] = (float)(crsp * sy - sr * cy);
        M[10] = (float)(cr * cp);
    }
 
    Vector3d getScale(float[] M){
        // Deal with the 0 rotation case first
        if( (M[1] == 0) && (M[2] == 0) &&
            (M[4] == 0) && (M[6] == 0) &&
            (M[8] == 0) && (M[9] == 0))
        return new Vector3d(M[0], M[5], M[10]);
 
        // We have to do the full calculation.
        return new Vector3d((float)Math.sqrt(M[0] * M[0] + M[1] * M[1] + M[2] * M[2]),
                            (float)Math.sqrt(M[4] * M[4] + M[5] * M[5] + M[6] * M[6]),
                            (float)Math.sqrt(M[8] * M[8] + M[9] * M[9] + M[10] * M[10]));
    }
 
    float clamp(float value, float low, float high) {
        return Math.min(Math.max(value,low), high);
    }
 
    Vector3d getRotationDegrees(float[] M){
        Vector3d scale = getScale(M);
        // we need to check for negative scale on to axes, which would bring up wrong results
        if (scale.y < 0 && scale.z < 0) {
            scale.y = -scale.y;
            scale.z = -scale.z;
        }
        else if (scale.x < 0 && scale.z < 0) {
            scale.x = -scale.x;
            scale.z = -scale.z;
        }
        else if (scale.x<0 && scale.y<0) {
            scale.x = -scale.x;
            scale.y = -scale.y;
        }
        Vector3d invScale = new Vector3d((1f / scale.x), (1f / scale.y), (1f / scale.z));
 
        double Y = -Math.asin(clamp(M[2] * invScale.x, -1.f, 1.f));
        double C = Math.cos(Y);
        Y = Math.toDegrees(Y);
 
        double rotx, roty, X, Z;
 
        if (C != 0){
            double invC = 1f / C;
            rotx = M[10] * invC * invScale.z;
            roty = M[6] * invC * invScale.y;
            X = Math.toDegrees(Math.atan2( roty, rotx ));
            rotx = M[0] * invC * invScale.x;
            roty = M[1] * invC * invScale.x;
            Z = Math.toDegrees(Math.atan2( roty, rotx ));
        } else {
            X = 0.0;
            rotx = M[5] * invScale.y;
            roty = -M[4] * invScale.y;
            Z = Math.toDegrees(Math.atan2( roty, rotx ));
        }
 
        // fix values that get below zero
        if (X < 0.0) X += 360.0;
        if (Y < 0.0) Y += 360.0;
        if (Z < 0.0) Z += 360.0;
 
        return new Vector3d((float)X, (float)Y, (float)Z);
    }
and this is your code from the other thread:

Code: Select all

    public void spinAxes(float x, float y, float z) {
        float[] transformation = new float[16];
        float[] matRotX = new float[16];
        float[] matRotY = new float[16];
        float[] matRotZ = new float[16];
        Matrix.setIdentityM(transformation, 0);
        Matrix.setIdentityM(matRotX, 0);
        Matrix.setIdentityM(matRotY, 0);
        Matrix.setIdentityM(matRotZ, 0);
 
        setRotationDegrees(transformation, Rotation);
 
        Vector3d axisX = new Vector3d(1,0,0);
        Vector3d axisY = new Vector3d(0,1,0);
        Vector3d axisZ = new Vector3d(0,0,1);
 
        rotateVect(transformation, axisX);
        rotateVect(transformation, axisY);
        rotateVect(transformation, axisZ);
 
        setRotationAxisDegrees(matRotX, x, axisX);
        setRotationAxisDegrees(matRotY, y, axisY);
        setRotationAxisDegrees(matRotZ, z, axisZ);
 
        float[] newTransform = new float[16];
        Matrix.setIdentityM(newTransform, 0);
       
        // not sure about the order here, I tried different orders...
        Matrix.multiplyMM(newTransform, 0, newTransform, 0, transformation, 0);
        Matrix.multiplyMM(newTransform, 0, newTransform, 0, matRotZ, 0);
        Matrix.multiplyMM(newTransform, 0, newTransform, 0, matRotY, 0);
        Matrix.multiplyMM(newTransform, 0, newTransform, 0, matRotX, 0);
 
        Rotation = getRotationDegrees(newTransform);
    }
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
Acki
Posts: 3496
Joined: Tue Jun 29, 2004 12:04 am
Location: Nobody's Place (Venlo NL)
Contact:

Re: Matrix Rotation Around Local Axes

Post by Acki »

of course I also had a look at your Quaternion approach...

but honestly, I don't get how to use it to rotate the axes !!! :oops:

can you give me an example on how to use your Quaternion method to rotate the 3 axes, please !? ;)
while(!asleep) sheep++;
IrrExtensions:Image
http://abusoft.g0dsoft.com
try Stendhal a MORPG written in Java
Post Reply