# C3D_

Member

16

• #### Days Won

1

C3D_ had the most liked content!

6 Neutral

• Rank
Member

• Website
• Role
3D Artist
Programmer
• Interests
Art
Design
Programming
1. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

Here is the code for the final function. May it help somebody someday hopefully. (UE4 v 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(); }
2. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

I think i tested all possible rotations so far and the results look always correct. Thanks for your offer to let me post back anytime. I hope i dont have to, haha! Thanks, i could not have done this alone and you will be mentioned when i give this little plugin away!
3. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

@Zakwayda Sir, you did it!! Thank you, thank you, thank you!!!! You are THE man!! And thank you for being so precise. Please dont misunderstand me. I'am more than thankful for your precise instructions. Maybe i did sound rude (i speak german) but i just wanted to inform you that i follow your instructions very carefully. I never felt that you thought that iam not aware. (Although iam not aware of most of this code, lol! ) However, here is the code that gives the correct rotations: Wanted UE4 Euler rotations was X -35,2644 Y 30 Z 35,2644 (Turnig 45 deg clockwise in the UP-axis and then 45 deg clockwise on the world X-axis) // Quaternions 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; // YES!! -35,264 , 29,999 , 35,264 //FQuat qu = qy * qx * qz; // Nope -31.48 , 56.44 , 31.48 FRotator Rotation(qu); FTransform Transform(Rotation, Position, Scale);
4. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

Yes, i check visually. Yes iam aware of floating point precision errors. Here is the new code with the changes applyd, testing all 6 combos again. I think we come closer already: (Tested only 1 combo yet) // Transform FVector Scale(sX, sY, sZ); // Scale FVector Position; // Translation float rad = 0.0174532925199444; // ************************************************************************************ // Conversion XSI Coordinate System to UE4 Coordinate System // Position Swap the Z and Y Coordinates Position.X = pX * 100; // TX Position.Y = pZ * 100; // TZ Position.Z = pY * 100; // TY // Quaternions 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 * qx * qz; // Nope -31.48 , 56.44 , 31.48 FRotator Rotation(qu); FTransform Transform(Rotation, Position, Scale);
5. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

@Zakwayda Cool, just right in time, haha! I was just testing all 6 possible combinations of the quaternion multiplications but every single one failed in the results. void SpawnSMActor(const TCHAR *path,float sX,float sY,float sZ,float rX,float rY,float rZ,float pX,float pY,float pZ) { // Transform FVector Scale(sX, sY, sZ); // Scale FVector Position; // Translation // ************************************************************************************ // Conversion XSI Coordinate System to UE4 Coordinate System // Position Swap the Z and Y Coordinates Position.X = pX * 100; // TX Position.Y = pZ * 100; // TZ Position.Z = pY * 100; // TY // Quaternions FQuat qx(1, 0, 0, -rX); FQuat qz(0, 0, 1, -rY); FQuat qy(0, 1, 0, -rZ); //FQuat qu = qy * qz * qx; // Nope 3.37 , 90 , 5.35 FQuat qu = qy * qx * qz; // Nope 32.52 , 90 , 34.50 //FQuat qu = qx * qy * qz; // Nope 24.58 , 90 , 26.56 //FQuat qu = qx * qz * qy; // Nope 32.3 , 90 , 34.5 //FQuat qu = qz * qx * qy; // Nope 6.67 , 90 , 8.88 //FQuat qu = qz * qy * qx; // Nope 17.56 , 90 , 19.76 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(); } I will check out the new instructions
6. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

@Zakwayda Okay, here is a first try. Not sure if this is what you meant. Is this correct? FQuat qx(pX, 0, 0, -rX); FQuat qz(0, 0, pZ, -rY); FQuat qy(0, pY, 0, -rZ);
7. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

WoW! Thank you so much. I will try to do that! I will post the code when i have it! Thanks!!
8. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

Found this C++ Class References in the XSI docs about Rotation and Quaternions. cpp/classXSI_MATH_CRotation
9. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

@Zakwayda Yupp. Thats the Software iam using. XSI. In my second video on page one of this thread i show exactly how i set the rotation order in the local kinematics property editor. You must have missed it. This is why i was sure that the order is XZY because it changes the XYZ values to those in UE4. Regarding the quaternions: I know that i read some option in the animation editor that mentioned quaternions. I will look into that! And thank you for digging with me! Here are the actual docs: Autodesk Softimage XSI Docs
10. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

Here is some code that i wrote already. It moves the given 3 Euler angles into a matrix. #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 } } Okay. Thank you. Unfortunatly i will have to go with Euler angles. @JohnnyCode mentioned affine transformations and alignment transformations. I will look into that. I guess i have to really learn this stuff to succeed here.
11. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

Thanks for your answer. How would such calculations/transformations look like? Iam 100% new to this topic and i dont have a clue what to do. If someone could just point me into the right direction i could find out myself maybe. Euler Angles -> Matrix -> Matrix Operations? -> Back to Euler
12. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

@JohnnyCode UE4 (Left Handed , Z axis up ) XSI (Right Handed , Y axis up ) Maya (Right Handed , Y axis up )
13. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

Hello, thanks for this link! That gives some interesting insights. I have tested the rotation order in Softimage, only possible in Local Space. But that does not matter since in my test scene the object lies anyway in global space. So local and global is the same. So if i switch the order from XYZ to XZW then i get the "same" values like in UE4. Here is a new video to proof my point: Also here is a link that i have found that says (scroll down to the bottom, last post) Maya to UE4 --> XZY. Maya has the same coordinate system as XSI: XSI (Right Handed , Y axis up ) Maya (Right Handed , Y axis up ) https://answers.unrealengine.com/questions/62077/what-is-the-rotation-order-for-components-or-bones.html Quaternions are for game engines mostly. Every3D software that i know stores animations in Euler angles and so does XSI.
14. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

Hello, thanks for trying to help me. Much appreciated. I indeed tryd to negate the rotation angles. In all possible combinations. But it seems to just make the values positive or negative. I honestly think that it is a Matrix problem of some kind. Here is the function that gets fed with the 3D-app( Softimage XSI) data. 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(); }
15. ## 3D-app Euler-Rotations to UE4 Euler-Rotations

@Zakwayda Hello and thanks for your answer. My 3D software uses XYZ rotation order and Unreal Engine 4 uses XZY rotation order (As far as i could find out so far.) When i import a 3D model into UE4 then i just set the scaling to 100. Everything else gets corrected already on Import.