diff --git a/engine/core/math/Quaternion.cpp b/engine/core/math/Quaternion.cpp index c9dad926..ec9a5c07 100644 --- a/engine/core/math/Quaternion.cpp +++ b/engine/core/math/Quaternion.cpp @@ -27,7 +27,11 @@ Quaternion::Quaternion( float* const r ) } Quaternion::Quaternion(const float xAngle, const float yAngle, const float zAngle){ - this->fromEulerAngles(xAngle, yAngle, zAngle); + this->fromEulerAngles(xAngle, yAngle, zAngle, RotationOrder::ZYX); +} + +Quaternion::Quaternion(const float xAngle, const float yAngle, const float zAngle, RotationOrder order){ + this->fromEulerAngles(xAngle, yAngle, zAngle, order); } Quaternion::Quaternion(const Vector3* akAxis){ @@ -163,14 +167,33 @@ void Quaternion::swap(Quaternion& other){ std::swap(z, other.z); } -void Quaternion::fromEulerAngles(const float xAngle, const float yAngle, const float zAngle){ +void Quaternion::fromEulerAngles(const float xAngle, const float yAngle, const float zAngle, RotationOrder order){ Quaternion qx, qy, qz; qx.fromAngleAxis(xAngle, Vector3(1,0,0)); qy.fromAngleAxis(yAngle, Vector3(0,1,0)); qz.fromAngleAxis(zAngle, Vector3(0,0,1)); - *this = (qz * (qy * qx)); //order ZYX + switch (order) { + case RotationOrder::XYZ: + *this = qx * (qy * qz); + break; + case RotationOrder::XZY: + *this = qx * (qz * qy); + break; + case RotationOrder::YXZ: + *this = qy * (qx * qz); + break; + case RotationOrder::YZX: + *this = qy * (qz * qx); + break; + case RotationOrder::ZXY: + *this = qz * (qx * qy); + break; + case RotationOrder::ZYX: + *this = qz * (qy * qx); + break; + } } void Quaternion::fromAxes (const Vector3* akAxis){ @@ -312,11 +335,127 @@ void Quaternion::fromAngleAxis (const float angle, const Vector3& rkAxis){ z = fSin*rkAxis.z; } -Vector3 Quaternion::getEulerAngles() const{ - Quaternion q = *this; - q.normalize(); +Vector3 Quaternion::getEulerAngles(RotationOrder order) const{ + Vector3 eulerAngles; + + switch (order) { + case RotationOrder::XYZ: { + // Roll (X-axis rotation) + float sinr_cosp = 2 * (w * x + y * z); + float cosr_cosp = 1 - 2 * (x * x + y * y); + eulerAngles.x = atan2(sinr_cosp, cosr_cosp); + + // Pitch (Y-axis rotation) + float sinp = 2 * (w * y - z * x); + if (fabs(sinp) >= 1) // Handle gimbal lock + eulerAngles.y = copysign(M_PI / 2, sinp); + else + eulerAngles.y = asin(sinp); + + // Yaw (Z-axis rotation) + float siny_cosp = 2 * (w * z + x * y); + float cosy_cosp = 1 - 2 * (y * y + z * z); + eulerAngles.z = atan2(siny_cosp, cosy_cosp); + break; + } + case RotationOrder::XZY: { + // Roll (X-axis rotation) + float sinr_cosp = 2 * (w * x + y * z); + float cosr_cosp = 1 - 2 * (x * x + z * z); + eulerAngles.x = atan2(sinr_cosp, cosr_cosp); + + // Yaw (Z-axis rotation) + float siny = 2 * (w * z - x * y); + if (fabs(siny) >= 1) // Handle gimbal lock + eulerAngles.z = copysign(M_PI / 2, siny); + else + eulerAngles.z = asin(siny); + + // Pitch (Y-axis rotation) + float sinp_cosp = 2 * (w * y + z * x); + float cosp_cosp = 1 - 2 * (y * y + z * z); + eulerAngles.y = atan2(sinp_cosp, cosp_cosp); + break; + } + case RotationOrder::YXZ: { + // Yaw (Y-axis rotation) + float siny_cosp = 2 * (w * y + x * z); + float cosy_cosp = 1 - 2 * (y * y + z * z); + eulerAngles.y = atan2(siny_cosp, cosy_cosp); + + // Roll (X-axis rotation) + float sinr = 2 * (w * x - y * z); + if (fabs(sinr) >= 1) // Handle gimbal lock + eulerAngles.x = copysign(M_PI / 2, sinr); + else + eulerAngles.x = asin(sinr); + + // Pitch (Z-axis rotation) + float sinp_cosp = 2 * (w * z + x * y); + float cosp_cosp = 1 - 2 * (z * z + x * x); + eulerAngles.z = atan2(sinp_cosp, cosp_cosp); + break; + } + case RotationOrder::YZX: { + // Pitch (Z-axis rotation) + float sinp = 2 * (w * z - x * y); + if (fabs(sinp) >= 1) // Handle gimbal lock + eulerAngles.z = copysign(M_PI / 2, sinp); + else + eulerAngles.z = asin(sinp); + + // Yaw (Y-axis rotation) + float siny_cosp = 2 * (w * y + z * x); + float cosy_cosp = 1 - 2 * (y * y + z * z); + eulerAngles.y = atan2(siny_cosp, cosy_cosp); + + // Roll (X-axis rotation) + float sinr_cosp = 2 * (w * x + y * z); + float cosr_cosp = 1 - 2 * (x * x + z * z); + eulerAngles.x = atan2(sinr_cosp, cosr_cosp); + break; + } + case RotationOrder::ZXY: { + // Roll (X-axis rotation) + float sinr = 2 * (w * x - y * z); + if (fabs(sinr) >= 1) // Handle gimbal lock + eulerAngles.x = copysign(M_PI / 2, sinr); + else + eulerAngles.x = asin(sinr); + + // Pitch (Y-axis rotation) + float sinp_cosp = 2 * (w * y + z * x); + float cosp_cosp = 1 - 2 * (y * y + x * x); + eulerAngles.y = atan2(sinp_cosp, cosp_cosp); + + // Yaw (Z-axis rotation) + float siny_cosp = 2 * (w * z + x * y); + float cosy_cosp = 1 - 2 * (z * z + y * y); + eulerAngles.z = atan2(siny_cosp, cosy_cosp); + break; + } + case RotationOrder::ZYX: { + // Yaw (Z-axis rotation) + float siny_cosp = 2 * (w * z + x * y); + float cosy_cosp = 1 - 2 * (y * y + z * z); + eulerAngles.z = atan2(siny_cosp, cosy_cosp); + + // Pitch (Y-axis rotation) + float sinp = 2 * (w * y - z * x); + if (fabs(sinp) >= 1) // Handle gimbal lock + eulerAngles.y = copysign(M_PI / 2, sinp); + else + eulerAngles.y = asin(sinp); + + // Roll (X-axis rotation) + float sinr_cosp = 2 * (w * x + y * z); + float cosr_cosp = 1 - 2 * (x * x + y * y); + eulerAngles.x = atan2(sinr_cosp, cosr_cosp); + break; + } + } - return Vector3(q.getPitch(), q.getYaw(), q.getRoll()); + return Vector3(Angle::radToDefault(eulerAngles.x), Angle::radToDefault(eulerAngles.y), Angle::radToDefault(eulerAngles.z)); } Vector3 Quaternion::xAxis(void) const{ diff --git a/engine/core/math/Quaternion.h b/engine/core/math/Quaternion.h index 72a85eeb..337de004 100644 --- a/engine/core/math/Quaternion.h +++ b/engine/core/math/Quaternion.h @@ -9,6 +9,15 @@ Supernova::Quaternion operator * (float fScalar, const Supernova::Quaternion& rk namespace Supernova { + enum class RotationOrder{ + XYZ, + XZY, + YXZ, + YZX, + ZXY, + ZYX + }; + class Quaternion { public: @@ -20,6 +29,7 @@ namespace Supernova { Quaternion( const float fW, const float fX, const float fY, const float fZ ); explicit Quaternion( float* const r ); Quaternion(const float xAngle, const float yAngle, const float zAngle); + Quaternion(const float xAngle, const float yAngle, const float zAngle, RotationOrder order); Quaternion(const Vector3* akAxis); Quaternion(const Vector3& xaxis, const Vector3& yaxis, const Vector3& zaxis); Quaternion(const float angle, const Vector3& rkAxis); @@ -51,7 +61,7 @@ namespace Supernova { void swap(Quaternion& other); - void fromEulerAngles(const float xAngle, const float yAngle, const float zAngle); + void fromEulerAngles(const float xAngle, const float yAngle, const float zAngle, RotationOrder order); void fromAxes (const Vector3* akAxis); void fromAxes (const Vector3& xaxis, const Vector3& yaxis, const Vector3& zaxis); Quaternion& fromRotationMatrix (const Matrix3& kRot); @@ -60,8 +70,7 @@ namespace Supernova { void fromAngle (const float angle); void fromAngleAxis (const float angle, const Vector3& rkAxis); - Vector3 getEulerAngles() const; - + Vector3 getEulerAngles(RotationOrder order) const; Vector3 xAxis(void) const; Vector3 yAxis(void) const; Vector3 zAxis(void) const; diff --git a/engine/core/script/LuaBridgeAddon.h b/engine/core/script/LuaBridgeAddon.h index 527f9c66..18e7298c 100644 --- a/engine/core/script/LuaBridgeAddon.h +++ b/engine/core/script/LuaBridgeAddon.h @@ -52,6 +52,8 @@ namespace luabridge template<> struct Stack : EnumWrapper{}; template<> struct Stack : EnumWrapper{}; + template<> struct Stack : EnumWrapper{}; + template<> struct Stack : EnumWrapper{}; template<> struct Stack : EnumWrapper{}; template<> struct Stack : EnumWrapper{}; diff --git a/engine/core/script/binding/MathClassesLua.cpp b/engine/core/script/binding/MathClassesLua.cpp index 61d94b17..aedfff12 100644 --- a/engine/core/script/binding/MathClassesLua.cpp +++ b/engine/core/script/binding/MathClassesLua.cpp @@ -35,12 +35,15 @@ namespace luabridge void LuaBinding::registerMathClasses(lua_State *L){ #ifndef DISABLE_LUA_BINDINGS - static const Vector2 ZERO; - static const Vector2 UNIT_X; - static const Vector2 UNIT_Y; - static const Vector2 NEGATIVE_UNIT_X; - static const Vector2 NEGATIVE_UNIT_Y; - static const Vector2 UNIT_SCALE; + luabridge::getGlobalNamespace(L) + .beginNamespace("RotationOrder") + .addVariable("XYZ", RotationOrder::XYZ) + .addVariable("XZY", RotationOrder::XZY) + .addVariable("YXZ", RotationOrder::YXZ) + .addVariable("YZX", RotationOrder::YZX) + .addVariable("ZXY", RotationOrder::ZXY) + .addVariable("ZYX", RotationOrder::ZYX) + .endNamespace(); luabridge::getGlobalNamespace(L) .beginClass("Vector2") @@ -289,6 +292,7 @@ void LuaBinding::registerMathClasses(lua_State *L){ void(), void(const float, const float, const float, const float), void(const float, const float, const float), + void(const float, const float, const float, RotationOrder), void(const Vector3*), void(const Vector3&, const Vector3&, const Vector3&), void(const float, const Vector3&),