Search code examples
c++quaternionsjmonkeyengine

rotation matrix to quaternion (and back) what is wrong?


I copied a code for conversion of 3D roation matrix to quaternions and back. The same code is used in jMonkey (I just rewrote it into my C++ class). However, it does not work properly (at least not as I would expect.)

e.g. I made this test:

matrix (a,b,c):  
a : 0.707107 0.000000 0.707107 
b : 0.000000 -1.000000 0.000000 
c : -0.707107 0.000000 0.707107 

>>> ortonormality:  
a.a b.b c.c  1.000000 1.000000 1.000000  
a.b a.c b.c  0.000000 0.000000 0.000000 

>>> matrix -> quat 
quat: 0.000000 0.594604 0.000000 0.594604  norm(quat) 0.707107  

>>> quat -> matrix  
matrix (a,b,c):
a: 0.000000 0.000000 1.000000 
b: 0.000000 1.000000 0.000000 
c: -1.000000 0.000000 0.000000 

I think the problem is in matrix -> quat because I have used quat -> matrix procedure before, and it was working fine. Also it is strange that quaternion made from orthonormal matrix is not unitary.

the matrix -> quat procedure

inline void fromMatrix( TYPE m00, TYPE m01, TYPE m02,    TYPE m10, TYPE m11, TYPE m12,        TYPE m20, TYPE m21, TYPE m22) {
    // Use the Graphics Gems code, from 
    // ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z
    TYPE t = m00 + m11 + m22;
    // we protect the division by s by ensuring that s>=1
    if (t >= 0) { // by w
        TYPE s = sqrt(t + 1);
        w = 0.5 * s;
        s = 0.5 / s;                 
        x = (m21 - m12) * s;
        y = (m02 - m20) * s;
        z = (m10 - m01) * s;
    } else if ((m00 > m11) && (m00 > m22)) { // by x
        TYPE s = sqrt(1 + m00 - m11 - m22); 
        x = s * 0.5; 
        s = 0.5 / s;
        y = (m10 + m01) * s;
        z = (m02 + m20) * s;
        w = (m21 - m12) * s;
    } else if (m11 > m22) { // by y
        TYPE s = sqrt(1 + m11 - m00 - m22); 
        y = s * 0.5; 
        s = 0.5 / s;
        x = (m10 + m01) * s;
        z = (m21 + m12) * s;
        w = (m02 - m20) * s;
    } else { // by z
        TYPE s = sqrt(1 + m22 - m00 - m11); 
        z = s * 0.5; 
        s = 0.5 / s;
        x = (m02 + m20) * s;
        y = (m21 + m12) * s;
        w = (m10 - m01) * s;
    }
}

the quat -> matrix procedure

inline void toMatrix( MAT& result) const {
        TYPE r2 = w*w + x*x + y*y + z*z;
        //TYPE s  = (r2 > 0) ? 2d / r2 : 0;
        TYPE s  = 2 / r2;
        // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs
        // will be used 2-4 times each.
        TYPE xs = x * s;  TYPE ys = y * s;  TYPE zs = z * s;
        TYPE xx = x * xs; TYPE xy = x * ys; TYPE xz = x * zs;
        TYPE xw = w * xs; TYPE yy = y * ys; TYPE yz = y * zs;
        TYPE yw = w * ys; TYPE zz = z * zs; TYPE zw = w * zs;
        // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here
        result.xx = 1 - (yy + zz);
        result.xy =     (xy - zw);
        result.xz =     (xz + yw);
        result.yx =     (xy + zw);
        result.yy = 1 - (xx + zz);
        result.yz =     (yz - xw);
        result.zx =     (xz - yw);
        result.zy =     (yz + xw);
        result.zz = 1 - (xx + yy);
};

sorry for TYPE, VEC, MAT, QUAT it is part of class tepmpltes... should be replaced by double, Vec3d, Mat3d, Quat3d or float, Vec3f, Mat3f, Quat3f.

EDIT:

I also checked if I get the same behaviour with jMonkey directly (in case I made a bug in Java to C++ conversion ). And I do - using this code:

    Matrix3f Min  = new Matrix3f( 0.707107f, 0.000000f, 0.707107f, 0.000000f, -1.000000f, 0.000000f, -0.707107f,  0.000000f,  0.707107f  );
    Matrix3f Mout = new Matrix3f( );
    Quaternion q = new Quaternion();
    q.fromRotationMatrix(Min);
    System.out.println( q.getX()+" "+q.getY()+" "+q.getZ()+" "+q.getW() );
    q.toRotationMatrix(Mout);
    System.out.println( Mout.get(0,0) +" "+Mout.get(0,1)+" "+Mout.get(0,2) );
    System.out.println( Mout.get(1,0) +" "+Mout.get(1,1)+" "+Mout.get(1,2) );
    System.out.println( Mout.get(2,0) +" "+Mout.get(2,1)+" "+Mout.get(2,2) );

Solution

  • Your matrix:

    matrix (a,b,c):  
    a : 0.707107 0.000000 0.707107 
    b : 0.000000 -1.000000 0.000000 
    c : -0.707107 0.000000 0.707107
    

    is orthogonal but it is not a rotation matrix. A rotation matrix has determinant 1; your matrix has determinant -1 and is thus an improper rotation.

    I think your code is likely correct and the issue is in your data. Try it with a real rotation matrix.