raylib: [raymath] bug in QuaternionToMatrix/QuaternionFromMatrix?

Please, before submitting a new issue verify and check:

  • I tested it on latest raylib version from master branch
  • I checked there is no similar issue already reported
  • My code has no errors or misuse of raylib (I do not think so, at least)

Issue description

Source:

Vector3 euler_src= {(float)0, PI / 4, (float)0};
auto quat1 = QuaternionFromEuler(euler_src.x, euler_src.y, euler_src.z);
auto euler1 = QuaternionToEuler(quat1);
auto mat1 = QuaternionToMatrix(quat1);

cout << "orig angles: (" << euler_src.x << "," << euler_src.y << "," << euler_src.z << ")" << endl;
cout << "euler1: (" << euler1.x << "," << euler1.y << "," << euler1.z << ")" << endl;
cout << "quat1: (" << quat1.x << "," << quat1.y << "," << quat1.z << "," << quat1.w << ")" << endl;

auto quat2 = QuaternionFromMatrix(mat1);
auto euler2 = QuaternionToEuler(quat2);
auto mat2 = MatrixRotateXYZ(euler_src);

cout << "euler2: (" << euler2.x << "," << euler2.y << "," << euler2.z << ")" << endl;
cout << "quat2: (" << quat2.x << "," << quat2.y << "," << quat2.z << "," << quat2.w << ")" << endl;      

This prints:

orig angles: (0,0.785398,0)
euler1: (0,45,0)
quat1: (0,0.382683,0,0.92388)
euler2: (0,-45,0)
quat2: (0,-0.336557,0,1.0505)

These are indeed different angles, which is not what I think would be expected, because the first quaternion is made by converting from euler, and the second quaternion is made by converting back from the matrix.

Environment

Linux x64, but I don’t think this is relevant because this is in the standalone math library.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 15 (15 by maintainers)

Most upvoted comments

this works:

Matrix MatrixRotateZYX(Vector3 ang) 
{
	Matrix result = MatrixIdentity();
float x = ang.x, y = ang.y, z = ang.z;
float a = cosf( x ), b = sinf( x );
float c = cosf( y ), d = sinf( y );
float e = cosf( z ), f = sinf( z );

float ce = c * e, cf = c * f, de = d * e, df = d * f;

result.m0  = ce - df * b;
result.m4  = - a * f;
result.m8  = de + cf * b;

result.m1  = cf + de * b;
result.m5  = a * e;
result.m9  = df - ce * b;

result.m2  = - a * d;
result.m6  = b;
result.m10 = a * c;			
}

but there is still the issue with quat to mat…

okay I can see (i think) equivalent but different values happening with euler to quat with different code, the problem seems to be with matrix to quat, which seems to be way out… I’ve tried a few other similar methods but seem to not be getting correct results… I’ll have a think about it and try again another time, in the mean time if anyone else wants to chip in by all means!

I’ll have a play over the weekend, its probably a variable transposed somewhere I’m betting…

On Thursday, 30 July 2020, Adrie notifications@github.com wrote:

Even despite that, the euler orientations don’t match:

euler1: (0,45,0) euler2: (0,-45,0) these are definitely different angles.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.< https://ci5.googleusercontent.com/proxy/y5Bg62YfNrfqW2ub7PWmH6H2kUR-hcbiXRaCIzHJ6t8CdghhrDDCY55NLfzriGwU-yjBR7jGYCFKsgSSJQ-hY_pnVjDcN6aq1a1QUp7TR-uIgH8VtkWzGnlqwd0_dI7ZrhSLrboDvy6Z10lG09UAfNhIOMtOxzQ38R65ugQBD9UWsxa5nM9MmXS0EjcSULYKpth97J4yxLJqGK_Dbc5CzHFdDWdLd09I4O2itarrTA=s0-d-e1-ft#https://github.com/notifications/beacon/AAFO4SMUY2JC2R6AZQMCIBDR6CTEZA5CNFSM4PLL7PA2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOE6Y7KLY.gif>

sorry haven’t had time to look at this till now…

my QtoM function seems to line up with taking a quat from a matrix and again if you take a quat to euler and then create a matrix rotated in the order XYZ …

#include "raylib.h"
#include "raymath.h"

// gcc main.c -lm -lX11 libraylib.a -ldl -lpthread


 Matrix QtoM(Quaternion q) 
{
    Matrix m = MatrixIdentity();
    float a2=2*(q.x*q.x), b2=2*(q.y*q.y), c2=2*(q.z*q.z), d2=2*(q.w*q.w);
    
    float ab=2*(q.x*q.y), ac=2*(q.x*q.z), bc=2*(q.y*q.z);
    float ad=2*(q.x*q.w), bd=2*(q.y*q.w), cd=2*(q.z*q.w);

    m.m0  = 1 - b2 - c2;
    m.m1  = ab - cd;
    m.m2  = ac + bd;
    
    m.m4  = ab + cd;
    m.m5  = 1 - a2 - c2;
    m.m6  = bc - ad;
    
    m.m8  = ac - bd;
    m.m9  = bc + ad;
    m.m10 = 1 - a2 - b2;

    return m;
}

void main() 
{
    Matrix m1,m2,m3,m4;
    Vector3 v1,v2;
    v1.x=60;v1.y=20;v1.z=40;
    Quaternion q1,q2;

    InitWindow(800, 400, "raylib [core] example - basic window");

    Camera3D camera = { 0 };
    camera.position = (Vector3){ 0.0f, 10.0f, 10.0f };  // Camera position
    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
    camera.fovy = 45.0f;                                // Camera field-of-view Y
    camera.type = CAMERA_PERSPECTIVE;                   // Camera mode type

    Mesh msh = GenMeshCylinder(.1, 1, 32);
    Model mod = LoadModelFromMesh(msh);

    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
    //--------------------------------------------------------------------------------------

    // Main game loop
    while (!WindowShouldClose())    // Detect window close button or ESC key
    {
        // Update
        v1.x += 0.01;
        v1.y += 0.03;
        v1.z += 0.05;
        
        if (v1.x>PI*2) v1.x-=PI*2;
        if (v1.y>PI*2) v1.y-=PI*2;
        if (v1.z>PI*2) v1.z-=PI*2;
        
        q1 = QuaternionFromEuler(v1.x, v1.y, v1.z);
        
        m1 = MatrixRotateX(v1.x);
        m1 = MatrixMultiply(MatrixRotateY(v1.y), m1);
        m1 = MatrixMultiply(MatrixRotateZ(v1.z), m1);
        
        //m1 = MatrixRotateZYX(v1);     
        //m1 = MatrixRotateXYZ(v1);
        
        m2 = QuaternionToMatrix(q1);
        
        q1 = QuaternionFromMatrix(m1);
        //q1 = QuaternionNormalize(q1);
        m3 = QtoM(q1);
        
        v2 = QuaternionToEuler(q1);
        m4 = MatrixRotateX(v2.x*DEG2RAD);
        m4 = MatrixMultiply(MatrixRotateY(v2.y*DEG2RAD), m4);
        m4 = MatrixMultiply(MatrixRotateZ(v2.z*DEG2RAD), m4);

        // Draw
        //----------------------------------------------------------------------------------
        BeginDrawing();

            ClearBackground(RAYWHITE);

            BeginMode3D(camera);

                mod.transform = m1;
                DrawModel(mod, (Vector3){-1,0,0},1.0,RED);
                mod.transform = m2;
                DrawModel(mod, (Vector3){1,0,0},1.0,RED);
                mod.transform = m3;
                DrawModel(mod, (Vector3){0,0,0},1.0,RED);
                mod.transform = m4;
                DrawModel(mod, (Vector3){0,0,-1},1.0,RED);

                DrawGrid(10, 1.0f);
 
            EndMode3D();
            
            DrawText(TextFormat("%2.3f",v1.x),20,20,20,BLACK);
            DrawText(TextFormat("%2.3f",v1.y),20,40,20,BLACK);
            DrawText(TextFormat("%2.3f",v1.z),20,60,20,BLACK);

            DrawText(TextFormat("%2.3f",v2.x),200,20,20,BLACK);
            DrawText(TextFormat("%2.3f",v2.y),200,40,20,BLACK);
            DrawText(TextFormat("%2.3f",v2.z),200,60,20,BLACK);
        EndDrawing();
        //----------------------------------------------------------------------------------
    }

    // De-Initialization
    //--------------------------------------------------------------------------------------
    CloseWindow();        // Close window and OpenGL context
    //--------------------------------------------------------------------------------------
    
}

I see issues with multiple quaternion conversions (not just matrix) I’ve not managed to get a quat to matrix working as I suspect that different functions are using different rotation orders (in effect) this code should highlight the issues I see

#include "raylib.h"
#include "raymath.h"

// gcc main.c -lm -lX11 libraylib.a -ldl -lpthread

Matrix QtoM(Quaternion q) 
{
    Matrix m = MatrixIdentity();
    float a2=2*(q.x*q.x), b2=2*(q.y*q.y), c2=2*(q.z*q.z), d2=2*(q.w*q.w);
    
    float ab=2*(q.x*q.y), ac=2*(q.x*q.z), bc=2*(q.y*q.z);
    float ad=2*(q.x*q.w), bd=2*(q.y*q.w), cd=2*(q.z*q.w);

    m.m0  = 1 - b2 - c2;
    m.m4  = ab - cd;
    m.m8  = ac + bd;
    
    m.m1  = ab + cd;
    m.m5  = 1 - a2 - c2;
    m.m9  = bc - ad;
    
    m.m2  = ac - bd;
    m.m6  = bc + ad;
    m.m10 = 1 - a2 - b2;

    return m;
}
 

void main() 
{
    Matrix m1,m2,m3,m4;
    Vector3 v1,v2;
    v1.x=60;v1.y=20;v1.z=40;
    Quaternion q1,q2;

    InitWindow(800, 400, "raylib [core] example - basic window");

    Camera3D camera = { 0 };
    camera.position = (Vector3){ 0.0f, 10.0f, 10.0f };  // Camera position
    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
    camera.fovy = 45.0f;                                // Camera field-of-view Y
    camera.type = CAMERA_PERSPECTIVE;                   // Camera mode type

    Mesh msh = GenMeshCylinder(.1, 1, 32);
    Model mod = LoadModelFromMesh(msh);

    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
    //--------------------------------------------------------------------------------------

    // Main game loop
    while (!WindowShouldClose())    // Detect window close button or ESC key
    {
        // Update
        v1.x += 0.01;
        v1.y += 0.03;
        v1.z += 0.05;
        
        if (v1.x>PI*2) v1.x-=PI*2;
        if (v1.y>PI*2) v1.y-=PI*2;
        if (v1.z>PI*2) v1.z-=PI*2;
        
        q1 = QuaternionFromEuler(v1.x, v1.y, v1.z);
        
        m1 = MatrixRotateX(v1.x);
        m1 = MatrixMultiply(MatrixRotateY(v1.y), m1);
        m1 = MatrixMultiply(MatrixRotateZ(v1.z), m1);
        
        //m1 = MatrixRotateZYX(v1);     
        //m1 = MatrixRotateXYZ(v1);
        
        m2 = QuaternionToMatrix(q1);
        
        q1 = QuaternionFromMatrix(m1);
        //q1 = QuaternionNormalize(q1);
        m3 = QtoM(q1);
        
        v2 = QuaternionToEuler(q1);
        m4 = MatrixRotateX(v2.x*DEG2RAD);
        m4 = MatrixMultiply(MatrixRotateY(v2.y*DEG2RAD), m4);
        m4 = MatrixMultiply(MatrixRotateZ(v2.z*DEG2RAD), m4);

        // Draw
        //----------------------------------------------------------------------------------
        BeginDrawing();

            ClearBackground(RAYWHITE);

            BeginMode3D(camera);

                mod.transform = m1;
                DrawModel(mod, (Vector3){-1,0,0},1.0,RED);
                mod.transform = m2;
                DrawModel(mod, (Vector3){1,0,0},1.0,RED);
                mod.transform = m3;
                DrawModel(mod, (Vector3){0,0,0},1.0,RED);
                mod.transform = m4;
                DrawModel(mod, (Vector3){0,0,-1},1.0,RED);

                DrawGrid(10, 1.0f);
 
            EndMode3D();
            
            DrawText(TextFormat("%2.3f",v1.x),20,20,20,BLACK);
            DrawText(TextFormat("%2.3f",v1.y),20,40,20,BLACK);
            DrawText(TextFormat("%2.3f",v1.z),20,60,20,BLACK);

            DrawText(TextFormat("%2.3f",v2.x),200,20,20,BLACK);
            DrawText(TextFormat("%2.3f",v2.y),200,40,20,BLACK);
            DrawText(TextFormat("%2.3f",v2.z),200,60,20,BLACK);
        EndDrawing();
        //----------------------------------------------------------------------------------
    }

    // De-Initialization
    //--------------------------------------------------------------------------------------
    CloseWindow();        // Close window and OpenGL context
    //--------------------------------------------------------------------------------------
    
}