【问题标题】:How to convert Euler Rotations from one Coordinate System to another? Right Handed Y-up to Left handed Z-up如何将欧拉旋转从一个坐标系转换为另一个坐标系?右手 Y 向上到左手 Z 向上
【发布时间】:2019-11-23 07:31:40
【问题描述】:

我为 UE4 编写了一个插件,用于将静态网格体 Actor 匹配并生成到 UE4 的关卡中。
该插件从每个脚本从 Softimage XSI 导出的文本文件(Scale、Rotation、Transformation)中读取坐标。一切都已经奏效了。但不是轮换。

我知道它与坐标系有关。 但是我究竟如何从一种转换到另一种呢?

我认为到目前为止我发现了什么(不是 100% 确定)
XSI 是右手 Y-up,旋转顺序 XYZ
UE4 是左手 Z-up,旋转顺序 XZY

在这两个应用程序中,我都有以度为单位的欧拉角。
所以在我的 3D 软件 (Softimage XSI) 中,我有 XYZ 度数,我将其存储到磁盘上的文本文件中。
逐行,其中每一行都是一个对象。
在 UE4 中,插件读取此行并生成一个 SM Actor 到关卡。

+++++新信息+++++

您好,感谢您到目前为止的回答!

我制作了一个视频来展示细节,展示情况。

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


+++++ Actor 生成函数 +++++

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();
}


如果我将 3D 应用程序中的对象在 UP 轴上顺时针旋转 45 度
,在 X 轴上旋转 45 度,我会得到:

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

如果我在 UE4 中执行相同的旋转,我会得到:

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

所以这些将是 UE4 中的正确旋转角度!

但是使用上面列出的代码,我得到:

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

那是错误的!因为它只是像看起来那样翻转了一些位置。角度与我的 3D 应用程序中的角度基本相同。

Picture



这是我第二次尝试在不使用 UE4 API 的情况下解决转换问题。
我知道它不完整,我仍然不完全理解我必须采取的步骤。

但希望它是一个开始。
正如上面提到的@DavidC.Rankin,我需要定义 (1) 原始坐标系。
不确定这是否是必要的,但我将来自维基百科的关于欧拉的信息放在 C++ 代码中的矩阵中。
这是所有 6 个 Tait-Bryan 角矩阵:
它是否正确?如果是这样,我将如何定义
(2) 目标坐标系?
(3) 旋转顺序?

#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
    }
}

【问题讨论】:

  • 请展示并解释更多您尝试过的内容。它可能有助于提供一个读取数据的代码基础来转换它们(不是,即只是通过不变地传递它,即为实际代码制作一个占位符)并以所需的形式输出/转发它们。如果你有一些东西可以完成这项工作并且只是无法正确轮换,那么就为它制作一个minimal reproducible example
  • 必须定义(1)原坐标系; (2)目标坐标系; (3) 轮换顺序。然后你可以形成你的转换矩阵。请参阅Euler angles - Wikipedia 作为一个示例。大约有 4 种不同的方法来选择坐标系统和旋转顺序。 (维基百科也不错)
  • @DavidC.Rankin 你好大卫,谢谢。这是一个真正有帮助的答案。我需要知道实现目标的步骤。现在我也许可以自己弄清楚了。
  • 我发布的答案好运吗?

标签: c++ unreal-engine4


【解决方案1】:

好的,Zakwayda 先生在 gamedev.net 上解决了问题(谢谢!)-> Link
Softimage XSI 2015 到 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();
}

【讨论】:

    【解决方案2】:

    我最近在 PlayCanvas(Unity 具有相同的坐标系)和 Unreal Coordinate System 之间创建了几个转换器函数。太忙了,无法在这里编写 C# 实现,但希望这个 javascript 能引导一些可怜的灵魂朝着正确的方向前进……

    /** 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};
    }
    

    【讨论】:

    • 我使用您的代码并从 unreal 到 playcanvas 进行测试,它确实有效!谢谢!
    【解决方案3】:

    您可以在 UE4 中使用 FTransform 转换您的旋转。 这为您提供了一个转换矩阵,我相信可能会解决您的问题。

    https://api.unrealengine.com/INT/API/Runtime/Core/Math/FTransform/index.html

    由 Scale、Rotation(作为四元数)和 翻译。

    变换可用于从一个空格转换为另一个空格,例如 例如通过将位置和方向从局部空间转换为 世界空间。

    从外观上看,在我看来,您只需交换 Z 和 Y 坐标值,以及旋转的 Pitch 和 Yaw 值。

    例如:

    Pitch = Yaw;
    Yaw = Pitch;
    

    这是一个如何在 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);
    

    所以我们简单地交换了YZ坐标。

    现在我们可以对旋转做同样的事情 注意命名有点不同,但 UE4 很快就解释了,这是一张图片:

    // 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());
    

    现在从您的问题来看,我不确定轴的方向,但如果网格沿相反方向移动,您可以简单地取轴的负值:

    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);
    

    在这里,我们将InPosition.X-InPosition.X 取负值一起移动到另一个方向。 在您的 2 个不同坐标系之间转换应该很简单。

    【讨论】:

    • 感谢您的回答。我查看了 FTransform,但在网上找不到任何代码示例。它还使用四元数,老实说,我有点害怕它们,因为我不理解它们。接下来是我希望尽可能远离 UE4 API(更新)。我也不确定俯仰偏航和滚动术语。我是 3D 艺术家,习惯于 XYZ。它还说 FTransform 用于将本地转换为世界空间,反之亦然。您确定这也可以将右手 Y-up XYZ 订单转换为左手 Z-up XZY 订单吗?抱歉,我对这些东西很陌生。
    • 是的,它可以在2个坐标空间之间转换,现在不管是相对的还是世界的或其他的。对于您的情况,我将发布一个代码示例。我不明白为什么使用这个 api 会出现问题。未来不太可能改变。
    • 俯仰横滚和偏航也等于 XYZ 坐标的度数。并且 UE4 提供了一个 api 来使用 FRotators 和 Quaternions。
    • 你要做的很简单,只要指定X=X,Y=Z和Z=Y就可以了,你可以使用蓝图或者C++。在 c++ 中,它是你想要的 FRotator。
    • 好的。您对更新是正确的。我认为这不会改变。我所拥有的是我的 3d 软件中的欧拉角。我也需要 UE4 的欧拉角。我的整个代码都是用 C++ 编写的,而不是蓝图中的。如果对你没问题,让我们谈谈 XYZ。它对我来说不那么混乱。谢谢你!
    猜你喜欢
    • 2020-11-11
    • 2019-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-20
    • 2013-03-25
    相关资源
    最近更新 更多