Search code examples
c++unreal-engine4

How to convert Euler Rotations from one Coordinate System to another? Right Handed Y-up to Left handed Z-up


I wrote a plugin for UE4 for matching and spawning Static Mesh Actors into the level of UE4.
The plugin reads coordinates from a text file (Scale,Rotation,Transformation) which are exported from Softimage XSI per script. Everything works already. BUT not the Rotations.

I know it has something to do with the coordinate system. But how exactly do i convert from one to the other?

What i think that i found out so far (not 100% sure)
XSI is Right handed Y-up , Rotation Order XYZ
UE4 is Left handed Z-up, Rotation Order XZY

In both applications i have Euler angles in degrees.
So in my 3D software (Softimage XSI) i have XYZ degrees which i store to a text file on disk.
Line by line where each line is an object.
In UE4 the plugin reads this lines and spawns an SM Actor to the level.

+++++ New Information +++++

Hello, thanks for the answers so far!

I made a video to show the details, and to show the circumstances.

https://www.youtube.com/watch?v=sWX84FxZTw0


+++++ The Actor spawn function +++++

void SpawnSMActor(const TCHAR *path,float sX,float sY,float sZ,float rX,float rY,float rZ,float pX,float pY,float pZ) 
{
    // Load Static Mesh from given Reference Path from UE4 Explorer
    UStaticMesh* StaMesh = LoadObject<UStaticMesh>(nullptr, path);

    // Transform
    FVector objectScale(sX, sY, sZ);        // Scale

    // ********************************************************************************
    // Conversion XSI Coordinate System to UE4 Coordinate System

    FVector NewPosition;
    FRotator NewRotation;

    // We just simply swap the Z and Y Coordinates

    NewPosition.X = pX * 100; //    TX
    NewPosition.Y = pZ * 100; //    TZ
    NewPosition.Z = pY * 100; //    TY

    // We just simply swap the Pitch(Y) and Yaw(Z) angles

    NewRotation.Roll =  rX;     //  RX
    NewRotation.Pitch = rZ;     //  RZ
    NewRotation.Yaw =   -rY;    //  RY

    FRotator NewobjectRotation(NewRotation.Quaternion());

    FTransform objectTransform(NewobjectRotation, NewPosition, objectScale);

    // ********************************************************************************

    // Creating the Actor and Positioning it in the World based on the Static Mesh
    UWorld* currentWorld = GEditor->GetEditorWorldContext().World();
    ULevel* currentLevel = currentWorld->GetCurrentLevel();
    UClass* StaticMeshClass = AStaticMeshActor::StaticClass();
    AActor* NewActorCreated = GEditor->AddActor(currentLevel, StaticMeshClass, objectTransform, true, RF_Public | RF_Standalone | RF_Transactional);
    AStaticMeshActor* smActor = Cast<AStaticMeshActor>(NewActorCreated);

    smActor->GetStaticMeshComponent()->SetStaticMesh(StaMesh);
    smActor->SetActorScale3D(objectScale);

    // ID Name & Visible Name
    //smActor->Rename(TEXT("MyStaticMeshInTheWorld"));
    //smActor->SetActorLabel("MyStaticMeshInTheWorld");

    GEditor->EditorUpdateComponents();
    smActor->GetStaticMeshComponent()->RegisterComponentWithWorld(currentWorld);
    currentWorld->UpdateWorldComponents(true, false);
    smActor->RerunConstructionScripts();
    GLevelEditorModeTools().MapChangeNotify();
}


If i rotate an object in my 3D app by 45 degree clockwise on the UP axis
and 45 degree on the X-axis i get:

X -54,7356 °
Y -30 °
Z 35,2644 °

If i do the same Rotations in UE4 i get:

X -35,2644 °
Y 30 °
Z 35,2644 °

So those would be the correct rotation angles in UE4!

But with the code listed above i get:

X -54,7355 °
Y 35,2643 °
Z 30 °

And thats wrong! Because it just flips some positions as it seems. The angles are essentialy the same as from my 3D app.

Picture



Here is my second attempt to solve the conversion without the UE4 API.
I know its not complete and i still dont fully understand the steps i will have to take.

But its hopefully a start.
As @DavidC.Rankin menntioned above i need to define the (1) the original coordinate system.
Not sure if this is what is necessary but i put the info from Wikipedia about Euler to matrix in C++ code.
This are all 6 of the Tait–Bryan angles matrices:
Is this correct? And if so how will i define
(2) the target coordinate system ?
(3) the order of rotations ?

#include "pch.h"
#include <iostream>
#include <string>
#include "linalg.h"
using namespace linalg::aliases;
using namespace std;

float x,y,z;

//Pre

void MatrixXZY(float3 angles, float3x3& matrix);
void MatrixXYZ(float3 angles, float3x3& matrix);
void MatrixYXZ(float3 angles, float3x3& matrix);
void MatrixYZX(float3 angles, float3x3& matrix);
void MatrixZYX(float3 angles, float3x3& matrix);
void MatrixZXY(float3 angles, float3x3& matrix);

void PrintMatrix(string name, float3 angles, float3x3& matrix);
void MatrixDecomposeYXZ(float3x3& matrix, float3& angles);




int main()
{
    float3 AnglesIn = { 0, 0, 0 };
    float3 AnglesOut;
    float3x3 Matrix;  // Matrix [Spalte][Zeile]

    cout << "-----------------------------" << endl;
    cout << "Input" << endl;
    cout << AnglesIn[0] << " " << AnglesIn[1] << " " << AnglesIn[2] << " " << endl;
    cout << "-----------------------------" << endl << endl;

    MatrixXZY(AnglesIn, Matrix);
    PrintMatrix("XZY", AnglesIn, Matrix);
    MatrixXYZ(AnglesIn, Matrix);
    PrintMatrix("XYZ", AnglesIn, Matrix);

    MatrixYXZ(AnglesIn, Matrix);
    PrintMatrix("YXZ", AnglesIn, Matrix);
    MatrixDecomposeYXZ(Matrix, AnglesOut);
    cout << "-----------------------------" << endl;
    cout << AnglesOut.x << " " << AnglesOut.y << " " << AnglesOut.z << " " << endl;
    cout << "-----------------------------" << endl << endl;


    MatrixYZX(AnglesIn, Matrix);
    PrintMatrix("YZX", AnglesIn, Matrix);

    MatrixZYX(AnglesIn, Matrix);
    PrintMatrix("ZYX", AnglesIn, Matrix);
    MatrixZXY(AnglesIn, Matrix);
    PrintMatrix("ZXY", AnglesIn, Matrix);

}



void MatrixXZY(float3 angles, float3x3& matrix)
{
    float cosX = cosf(angles.x);     // X
    float sinX = sinf(angles.x);

    float cosY = cosf(angles.y);     // Y
    float sinY = sinf(angles.y);

    float cosZ = cosf(angles.z);     // Z
    float sinZ = sinf(angles.z);


    matrix[0][0] = cosZ * cosY;                         // Spalte 1
    matrix[0][1] = sinX * sinY + cosX * cosY * sinZ;
    matrix[0][2] = cosY * sinX * sinZ - cosX * sinY;

    matrix[1][0] = -sinZ;                               // Spalte 2
    matrix[1][1] = cosX * cosZ;
    matrix[1][2] = cosZ * sinX;

    matrix[2][0] = cosZ * sinY;                         // Spalte 3
    matrix[2][1] = cosX * sinZ * sinY - cosY * sinX;
    matrix[2][2] = cosX * cosY + sinX * sinZ * sinY;

}
void MatrixXYZ(float3 angles, float3x3& matrix)
{
    float cosX = cosf(angles.x);     // X
    float sinX = sinf(angles.x);

    float cosY = cosf(angles.y);     // Y
    float sinY = sinf(angles.y);

    float cosZ = cosf(angles.z);     // Z
    float sinZ = sinf(angles.z);


    matrix[0][0] = cosY * cosZ;                         // Spalte 1
    matrix[0][1] = cosX * sinZ + cosZ * sinX * sinY;
    matrix[0][2] = sinX * sinZ - cosX * cosZ * sinY;

    matrix[1][0] = -cosY * sinZ;                        // Spalte 2
    matrix[1][1] = cosX * cosZ - sinX * sinY * sinZ;
    matrix[1][2] = cosZ * sinX + cosX * sinY * sinZ;

    matrix[2][0] = sinY;                                // Spalte 3
    matrix[2][1] = -cosY * sinX;
    matrix[2][2] = cosX * cosY;

}

void MatrixYXZ(float3 angles, float3x3& matrix)
{
    float cosX = cosf(angles.x);     // X
    float sinX = sinf(angles.x);

    float cosY = cosf(angles.y);     // Y
    float sinY = sinf(angles.y);

    float cosZ = cosf(angles.z);     // Z
    float sinZ = sinf(angles.z);

    matrix[0][0] = cosY * cosZ + sinY * sinX * sinZ;    // Spalte 1
    matrix[0][1] = cosX * sinZ;
    matrix[0][2] = cosY * sinX * sinZ - cosZ * sinY;

    matrix[1][0] = cosZ * sinY * sinX - cosY * sinZ;    // Spalte 2
    matrix[1][1] = cosX * cosZ;
    matrix[1][2] = cosY * cosZ * sinX + sinY * sinZ;

    matrix[2][0] = cosX * sinY;                         // Spalte 3
    matrix[2][1] = -sinX;
    matrix[2][2] = cosY * cosX;

}
void MatrixYZX(float3 angles, float3x3& matrix)
{
    float cosX = cosf(angles.x);     // X
    float sinX = sinf(angles.x);

    float cosY = cosf(angles.y);     // Y
    float sinY = sinf(angles.y);

    float cosZ = cosf(angles.z);     // Z
    float sinZ = sinf(angles.z);


    matrix[0][0] = cosY * cosZ;                         // Spalte 1
    matrix[0][1] = sinZ;
    matrix[0][2] = -cosZ * sinY;

    matrix[1][0] = sinY * sinX - cosY * cosX * sinZ;    // Spalte 2
    matrix[1][1] = cosZ * cosX;
    matrix[1][2] = cosY * sinX + cosX * sinY * sinZ;

    matrix[2][0] = cosX * sinY + cosY * sinZ * sinX;    // Spalte 3
    matrix[2][1] = -cosZ * sinX;
    matrix[2][2] = cosY * cosX - sinY * sinZ * sinX;

}

void MatrixZYX(float3 angles, float3x3& matrix)
{
    float cosX = cosf(angles.x);     // X
    float sinX = sinf(angles.x);

    float cosY = cosf(angles.y);     // Y
    float sinY = sinf(angles.y);

    float cosZ = cosf(angles.z);     // Z
    float sinZ = sinf(angles.z);


    matrix[0][0] = cosZ * cosY;                         // Spalte 1
    matrix[0][1] = cosY * sinZ;
    matrix[0][2] = -sinY;

    matrix[1][0] = cosZ * sinY * sinX - cosX * sinZ;    // Spalte 2
    matrix[1][1] = cosZ * cosX + sinZ * sinY * sinX;
    matrix[1][2] = cosY * sinX;

    matrix[2][0] = sinZ * sinX + cosZ * cosX * sinY;    // Spalte 3
    matrix[2][1] = cosX * sinZ * sinY - cosZ * sinX;
    matrix[2][2] = cosY * cosX;

}
void MatrixZXY(float3 angles, float3x3& matrix)
{
    float cosX = cosf(angles.x);     // X
    float sinX = sinf(angles.x);

    float cosY = cosf(angles.y);     // Y
    float sinY = sinf(angles.y);

    float cosZ = cosf(angles.z);     // Z
    float sinZ = sinf(angles.z);


    matrix[0][0] = cosZ * cosY - sinZ * sinX * sinY;        // Spalte 1
    matrix[0][1] = cosY * sinZ + cosZ * sinX * sinY;
    matrix[0][2] = -cosX * sinY;

    matrix[1][0] = -cosX * sinZ;                            // Spalte 2
    matrix[1][1] = cosZ * cosX;
    matrix[1][2] = sinX;

    matrix[2][0] = cosZ * sinY + cosY * sinZ * sinX;        // Spalte 3
    matrix[2][1] = sinZ * sinY - cosZ * cosY * sinX;
    matrix[2][2] = cosX * cosY;

}


void PrintMatrix(string name, float3 angles, float3x3& matrix)
{
    cout << "-----------------------------" << endl;
    cout << name << "-Matrix" << endl;
    cout << "-----------------------------" << endl;

    cout << matrix[0][0] << " " << matrix[1][0] << " " << matrix[2][0] << " " << endl;
    cout << matrix[0][1] << " " << matrix[1][1] << " " << matrix[2][1] << " " << endl;
    cout << matrix[0][2] << " " << matrix[1][2] << " " << matrix[2][2] << " " << endl;
    cout << "-----------------------------" << endl << endl << endl;
}



void MatrixDecomposeYXZ(float3x3& matrix, float3& angles)
{
    angles.x = asinf(-matrix[2][1]);                        //              X
    if (cosf(angles.x) > 0.0001)                            // Not at poles X
    {
        angles.y = atan2f(matrix[2][0], matrix[2][2]);      //              Y
        angles.z = atan2f(matrix[0][1], matrix[1][1]);      //              Z
    }
    else
    {
        angles.y = 0.0f;                                    //              Y
        angles.z = atan2f(-matrix[1][0], matrix[0][0]);     //              Z
    }
}

Solution

  • Okay, the problem got solved on gamedev.net by Mr. Zakwayda (Thanks!) -> Link
    Softimage XSI 2015 to Unreal Engine 4.22.3

    void SpawnSMActor(const TCHAR *path,float sX,float sY,float sZ,float rX,float rY,float rZ,float pX,float pY,float pZ) 
    {
        float rad = 0.0174532925199444; // FQuat needs Radians. So degree * Pi/180 | Pi/180 = 0.0174532...
    
        // Transform
        FVector Scale(sX, sY, sZ);      // Scale
        FVector Position;               // Translation
    
        // ************************************************************************************
        // Conversion XSI Coordinate System to UE4 Coordinate System
    
        // Position - Swap Z and Y axis and correct Position Scaling
    
        Position.X = pX * 100; 
        Position.Y = pZ * 100; 
        Position.Z = pY * 100; 
    
        // Quaternions - Convert Rotations from XSI to UE4
    
        FQuat qx(FVector(1, 0, 0), -rX * rad);
        FQuat qz(FVector(0, 0, 1), -rY * rad);
        FQuat qy(FVector(0, 1, 0), -rZ * rad);
    
        FQuat qu = qy * qz * qx; // Change Rotation Order if necessary
    
        FRotator Rotation(qu);
        FTransform Transform(Rotation, Position, Scale);
    
        // ************************************************************************************
        // Load Static Mesh from given Reference Path from UE4 Explorer
    
        UStaticMesh* StaMesh = LoadObject<UStaticMesh>(nullptr, path);
    
        // Creating the Actor and Positioning it in the World based on the Static Mesh
        UWorld* currentWorld = GEditor->GetEditorWorldContext().World();
        ULevel* currentLevel = currentWorld->GetCurrentLevel();
        UClass* StaticMeshClass = AStaticMeshActor::StaticClass();
        AActor* NewActorCreated = GEditor->AddActor(currentLevel, StaticMeshClass, Transform, true, RF_Public | RF_Standalone | RF_Transactional);
        AStaticMeshActor* smActor = Cast<AStaticMeshActor>(NewActorCreated);
    
        smActor->GetStaticMeshComponent()->SetStaticMesh(StaMesh);
        smActor->SetActorScale3D(Scale);
    
        // ID Name & Visible Name
        //smActor->Rename(TEXT("MyStaticMeshInTheWorld"));
        //smActor->SetActorLabel("MyStaticMeshInTheWorld");
    
        GEditor->EditorUpdateComponents();
        smActor->GetStaticMeshComponent()->RegisterComponentWithWorld(currentWorld);
        currentWorld->UpdateWorldComponents(true, false);
        smActor->RerunConstructionScripts();
        GLevelEditorModeTools().MapChangeNotify();
    }