Reputation: 79
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.
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
}
}
Upvotes: 4
Views: 8530
Reputation: 63
I recently created a couple of converter functions between PlayCanvas(Unity has the same coordinate system) to Unreal Coordinate System. Too busy to write a C# implementation here, but hope this javascript one leads some poor souls in the right direction...
/** Convert spatial coordinates from PlayCanvas to Unreal **/
function Pos3D_PlayCanvasToUnreal(x, y, z) {
return {x:-z*100, y:x*100, z:y*100};
}
/** Convert spatial quaternion from PlayCanvas to Unreal **/
function Quat_PlayCanvasToUnreal(q) {
return {x:-q.z, y:q.x, z:q.y, w:-q.w};
}
Upvotes: 1
Reputation: 79
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();
}
Upvotes: 1
Reputation: 3225
You can transform your rotation with FTransform in UE4. This provides you a transformation matrix and i believe might solve your problem.
https://api.unrealengine.com/INT/API/Runtime/Core/Math/FTransform/index.html
Transform composed of Scale, Rotation (as a quaternion), and Translation.
Transforms can be used to convert from one space to another, for example by transforming positions and directions from local space to world space.
And from the looks of it, to me it sounds like you just have to swap the Z and Y coordinate values, and the Pitch and Yaw values for the rotation.
Eg:
Pitch = Yaw;
Yaw = Pitch;
Here's a code example how to do this in UE4 C++
// We just simply swap the Z and Y Coordinates
FVector NewPosition;
NewPosition.X = InPosition.X;
NewPosition.Y = InPosition.Z;
NewPosition.Z = InPosition.Y;
SetActorLocation(NewPosition);
So we simply swapped the Y
and Z
coordinates.
Now we can do the same with rotation note the naming is a little different but UE4 explains it pretty quick here's an image:
// We just simply swap the Pitch(Y) and Yaw(Z) angles
FRotator NewRotation;
NewRotation.Roll = InRotation.X;
NewRotation.Pitch = InRotation.Z;
NewRotation.Yaw = InRotation.Y;
SetActorRotation(NewRotation.Quaternion());
Now from your question i am not sure which direction the axes are but you can simply take the negative of the axes if the mesh is moving in the opposite direction for example:
FVector NewPosition;
NewPosition.X = -InPosition.X; // Note the - in front of InPosition.X; You can also multiply with -1
NewPosition.Y = InPosition.Z;
NewPosition.Z = InPosition.Y;
SetActorLocation(NewPosition);
Here we move InPosition.X
by taking it's negative with -InPosition.X
in the other direction.
It should be that simple to convert between your 2 diffent coordinate systems.
Upvotes: 0