diff --git a/framework/CMakeLists.txt b/framework/CMakeLists.txt index d00f06f..cebfb03 100644 --- a/framework/CMakeLists.txt +++ b/framework/CMakeLists.txt @@ -39,14 +39,15 @@ add_executable (xna WIN32 "content/manager.cpp" "content/reader.cpp" "csharp/binary.cpp" - "content/lzx/decoder.cpp" - "content/lzx/decoderstream.cpp" - "content/typereadermanager.cpp" - "csharp/object.cpp" - "csharp/type.cpp" - "platform/init-dx.cpp" - "game/servicecontainer.cpp" - "common/color.cpp") +"content/lzx/decoder.cpp" +"content/lzx/decoderstream.cpp" +"content/typereadermanager.cpp" +"csharp/object.cpp" +"csharp/type.cpp" +"platform/init-dx.cpp" +"game/servicecontainer.cpp" +"common/color.cpp" +"common/math.cpp" "common/quaternion.cpp") if (CMAKE_VERSION VERSION_GREATER 3.12) set_property(TARGET xna PROPERTY CXX_STANDARD 20) diff --git a/framework/common/color.cpp b/framework/common/color.cpp index 0081cae..b2c533a 100644 --- a/framework/common/color.cpp +++ b/framework/common/color.cpp @@ -1,5 +1,71 @@ #include "common/color.hpp" namespace xna { + Color::Color(float r, float g, float b, float a) : + _packedValue(PackHelper(r, g, b, a)) { + } + Color::Color(Vector3 const& vector) : + _packedValue(PackHelper(vector.X, vector.Y, vector.Z, 1.0F)) { + } + + Color::Color(Vector4 const& vector) : + _packedValue(PackHelper(vector.X, vector.Y, vector.Z, vector.W)) { + } + + void Color::PackFromVector4(Vector4 const& vector) { + _packedValue = PackHelper(vector.X, vector.Y, vector.Z, vector.W); + } + + Color Color::FromNonPremultiplied(Vector4 const& vector) { + Color color; + color._packedValue = PackHelper(vector.X * vector.W, vector.Y * vector.W, vector.Z * vector.W, vector.W); + return color; + } + + Color Color::FromNonPremultiplied(Int r, Int g, Int b, Int a) { + r = ClampToByte32(r * a / ByteMaxValue); + g = ClampToByte32(g * a / ByteMaxValue); + b = ClampToByte32(b * a / ByteMaxValue); + a = ClampToByte32(a); + g <<= 8; + b <<= 16; + a <<= 24; + + Color color; + color._packedValue = static_cast(r | g | b | a); + return color; + } + + Color Color::Lerp(Color const& value1, Color const& value2, float amount) { + const Int r1 = value1.R(); + const Int g1 = value1.G(); + const Int b1 = value1.B(); + const Int a1 = value1.A(); + const Int r2 = value2.R(); + const Int g2 = value2.G(); + const Int b2 = value2.B(); + const Int a2 = value2.A(); + + const auto bitmask = static_cast(PackUtils::PackUNorm(65536.0f, amount)); + + const Int r = r1 + ((r2 - r1) * bitmask >> 16); + const Int g = g1 + ((g2 - g1) * bitmask >> 16); + const Int b = b1 + ((b2 - b1) * bitmask >> 16); + const Int a = a1 + ((a2 - a1) * bitmask >> 16); + + Color color; + color._packedValue = static_cast(r | g << 8 | b << 16 | a << 24); + return color; + } + + Uint Color::PackHelper(float vectorX, float vectorY, float vectorZ, float vectorW) { + const auto byteMax = static_cast(ByteMaxValue); + const auto x = PackUtils::PackUNorm(byteMax, vectorX); + const auto y = PackUtils::PackUNorm(byteMax, vectorY) << 8; + const auto z = PackUtils::PackUNorm(byteMax, vectorZ); + const auto w = PackUtils::PackUNorm(byteMax, vectorW) << 24; + + return x | y | z | w; + } } \ No newline at end of file diff --git a/framework/common/math.cpp b/framework/common/math.cpp new file mode 100644 index 0000000..2dac1f8 --- /dev/null +++ b/framework/common/math.cpp @@ -0,0 +1,20 @@ +#include "common/math.hpp" + +namespace xna { + float MathHelper::CatmullRom(float value1, float value2, float value3, float value4, float amount) { + const auto num1 = amount * amount; + const auto num2 = amount * num1; + return (0.5F * (2.0F * value2 + (-value1 + value3) * amount + (2.0F * value1 - 5.0F * value2 + 4.0F * value3 - value4) * num1 + (-value1 + 3.0F * value2 - 3.0F * value3 + value4) * num2)); + } + + float MathHelper::Hermite(float value1, float tangent1, float value2, float tangent2, float amount) { + const auto num1 = amount; + const auto num2 = num1 * num1; + const auto num3 = num1 * num2; + const auto num4 = (2.0F * num3 - 3.0F * num2 + 1.0F); + const auto num5 = (-2.0F * num3 + 3.0F * num2); + const auto num6 = num3 - 2.0f * num2 + num1; + const auto num7 = num3 - num2; + return value1 * num4 + value2 * num5 + tangent1 * num6 + tangent2 * num7; + } +} \ No newline at end of file diff --git a/framework/common/quaternion.cpp b/framework/common/quaternion.cpp new file mode 100644 index 0000000..725d000 --- /dev/null +++ b/framework/common/quaternion.cpp @@ -0,0 +1,133 @@ +#include "common/quaternion.hpp" +#include "common/matrix.hpp" + +namespace xna { + Quaternion Quaternion::CreateFromAxisAngle(Vector3 const& axis, float angle) { + const auto num1 = angle * 0.5f; + const auto num2 = std::sin(num1); + const auto num3 = std::cos(num1); + Quaternion fromAxisAngle; + fromAxisAngle.X = axis.X * num2; + fromAxisAngle.Y = axis.Y * num2; + fromAxisAngle.Z = axis.Z * num2; + fromAxisAngle.W = num3; + return fromAxisAngle; + } + + Quaternion Quaternion::CreateFromYawPitchRoll(float yaw, float pitch, float roll) { + const auto num1 = roll * 0.5f; + const auto num2 = std::sin(num1); + const auto num3 = std::cos(num1); + const auto num4 = pitch * 0.5f; + const auto num5 = std::sin(num4); + const auto num6 = std::cos(num4); + const auto num7 = yaw * 0.5f; + const auto num8 = std::sin(num7); + const auto num9 = std::cos(num7); + Quaternion fromYawPitchRoll; + fromYawPitchRoll.X = (num9 * num5 * num3 + num8 * num6 * num2); + fromYawPitchRoll.Y = (num8 * num6 * num3 - num9 * num5 * num2); + fromYawPitchRoll.Z = (num9 * num6 * num2 - num8 * num5 * num3); + fromYawPitchRoll.W = (num9 * num6 * num3 + num8 * num5 * num2); + return fromYawPitchRoll; + } + + Quaternion Quaternion::CreateFromRotationMatrix(Matrix const& matrix) { + const auto num1 = matrix.M11 + matrix.M22 + matrix.M33; + + Quaternion fromRotationMatrix; + if (num1 > 0.0) + { + const auto num2 = std::sqrt(num1 + 1.0F); + fromRotationMatrix.W = num2 * 0.5f; + const auto num3 = 0.5f / num2; + fromRotationMatrix.X = (matrix.M23 - matrix.M32) * num3; + fromRotationMatrix.Y = (matrix.M31 - matrix.M13) * num3; + fromRotationMatrix.Z = (matrix.M12 - matrix.M21) * num3; + } + else if (matrix.M11 >= matrix.M22 && matrix.M11 >= matrix.M33) + { + const auto num4 = std::sqrt(1.0F + matrix.M11 - matrix.M22 - matrix.M33); + const auto num5 = 0.5f / num4; + fromRotationMatrix.X = 0.5f * num4; + fromRotationMatrix.Y = (matrix.M12 + matrix.M21) * num5; + fromRotationMatrix.Z = (matrix.M13 + matrix.M31) * num5; + fromRotationMatrix.W = (matrix.M23 - matrix.M32) * num5; + } + else if (matrix.M22 > matrix.M33) + { + const auto num6 = std::sqrt(1.0F + matrix.M22 - matrix.M11 - matrix.M33); + const auto num7 = 0.5f / num6; + fromRotationMatrix.X = (matrix.M21 + matrix.M12) * num7; + fromRotationMatrix.Y = 0.5f * num6; + fromRotationMatrix.Z = (matrix.M32 + matrix.M23) * num7; + fromRotationMatrix.W = (matrix.M31 - matrix.M13) * num7; + } + else + { + const auto num8 = std::sqrt(1.0F + matrix.M33 - matrix.M11 - matrix.M22); + const auto num9 = 0.5f / num8; + fromRotationMatrix.X = (matrix.M31 + matrix.M13) * num9; + fromRotationMatrix.Y = (matrix.M32 + matrix.M23) * num9; + fromRotationMatrix.Z = 0.5f * num8; + fromRotationMatrix.W = (matrix.M12 - matrix.M21) * num9; + } + return fromRotationMatrix; + } + + Quaternion Quaternion::Slerp(Quaternion const& quaternion1, Quaternion const& quaternion2, float amount) { + const auto num1 = amount; + auto d = quaternion1.X * quaternion2.X + quaternion1.Y * quaternion2.Y + quaternion1.Z * quaternion2.Z + quaternion1.W * quaternion2.W; + bool flag = false; + + if (d < 0.0) { + flag = true; + d = -d; + } + + float num2 = 0; + float num3 = 0; + + if (d > 0.99999898672103882) { + num2 = 1.0f - num1; + num3 = flag ? -num1 : num1; + } + else { + const auto a = std::acos(d); + const auto num4 = 1.0F / std::sin(a); + num2 = std::sin((1.0F - num1) * a) * num4; + num3 = flag ? -std::sin(num1 * a) * num4 : std::sin(num1 * a) * num4; + } + Quaternion quaternion; + quaternion.X = num2 * quaternion1.X + num3 * quaternion2.X; + quaternion.Y = num2 * quaternion1.Y + num3 * quaternion2.Y; + quaternion.Z = num2 * quaternion1.Z + num3 * quaternion2.Z; + quaternion.W = num2 * quaternion1.W + num3 * quaternion2.W; + return quaternion; + } + + Quaternion Quaternion::Lerp(Quaternion const& quaternion1, Quaternion const& quaternion2, float amount) { + const auto num1 = amount; + const auto num2 = 1.0f - num1; + Quaternion quaternion; + + if (quaternion1.X * quaternion2.X + quaternion1.Y * quaternion2.Y + quaternion1.Z * quaternion2.Z + quaternion1.W * quaternion2.W >= 0.0) { + quaternion.X = num2 * quaternion1.X + num1 * quaternion2.X; + quaternion.Y = num2 * quaternion1.Y + num1 * quaternion2.Y; + quaternion.Z = num2 * quaternion1.Z + num1 * quaternion2.Z; + quaternion.W = num2 * quaternion1.W + num1 * quaternion2.W; + } + else { + quaternion.X = num2 * quaternion1.X - num1 * quaternion2.X; + quaternion.Y = num2 * quaternion1.Y - num1 * quaternion2.Y; + quaternion.Z = num2 * quaternion1.Z - num1 * quaternion2.Z; + quaternion.W = num2 * quaternion1.W - num1 * quaternion2.W; + } + const auto num3 = 1.0f / std::sqrt(quaternion.X * quaternion.X + quaternion.Y * quaternion.Y + quaternion.Z * quaternion.Z + quaternion.W * quaternion.W); + quaternion.X *= num3; + quaternion.Y *= num3; + quaternion.Z *= num3; + quaternion.W *= num3; + return quaternion; + } +} \ No newline at end of file diff --git a/inc/common/color.hpp b/inc/common/color.hpp index 40f52d1..4f22f5f 100644 --- a/inc/common/color.hpp +++ b/inc/common/color.hpp @@ -27,41 +27,15 @@ namespace xna { _packedValue = static_cast(r | g | b | a); } - Color(float r, float g, float b, float a = 1.0F) : - _packedValue(PackHelper(r, g, b, a)) { - } + Color(float r, float g, float b, float a = 1.0F); + Color(Vector3 const& vector); + Color(Vector4 const& vector); - Color(Vector3 const& vector) : - _packedValue(PackHelper(vector.X, vector.Y, vector.Z, 1.0F)) { - } + virtual void PackFromVector4(Vector4 const& vector) override; - Color(Vector4 const& vector) : - _packedValue(PackHelper(vector.X, vector.Y, vector.Z, vector.W)) { - } + static Color FromNonPremultiplied(Vector4 const& vector); - virtual void PackFromVector4(Vector4 const& vector) override { - _packedValue = PackHelper(vector.X, vector.Y, vector.Z, vector.W); - } - - static Color FromNonPremultiplied(Vector4 const& vector) { - Color color; - color._packedValue = PackHelper(vector.X * vector.W, vector.Y * vector.W, vector.Z * vector.W, vector.W); - return color; - } - - static Color FromNonPremultiplied(Int r, Int g, Int b, Int a) { - r = ClampToByte32(r * a / ByteMaxValue); - g = ClampToByte32(g * a / ByteMaxValue); - b = ClampToByte32(b * a / ByteMaxValue); - a = ClampToByte32(a); - g <<= 8; - b <<= 16; - a <<= 24; - - Color color; - color._packedValue = static_cast(r | g | b | a); - return color; - } + static Color FromNonPremultiplied(Int r, Int g, Int b, Int a); constexpr Vector3 ToVector3() const { Vector3 vector3; @@ -122,27 +96,7 @@ namespace xna { _packedValue = value; } - static Color Lerp(Color const& value1, Color const& value2, float amount) { - const Int r1 = value1.R(); - const Int g1 = value1.G(); - const Int b1 = value1.B(); - const Int a1 = value1.A(); - const Int r2 = value2.R(); - const Int g2 = value2.G(); - const Int b2 = value2.B(); - const Int a2 = value2.A(); - - const auto bitmask = static_cast(PackUtils::PackUNorm(65536.0f, amount)); - - const Int r = r1 + ((r2 - r1) * bitmask >> 16); - const Int g = g1 + ((g2 - g1) * bitmask >> 16); - const Int b = b1 + ((b2 - b1) * bitmask >> 16); - const Int a = a1 + ((a2 - a1) * bitmask >> 16); - - Color color; - color._packedValue = static_cast(r | g << 8 | b << 16 | a << 24); - return color; - } + static Color Lerp(Color const& value1, Color const& value2, float amount); static constexpr Color Multiply(Color const& value, float scale) { const Uint r = value.R(); @@ -203,15 +157,7 @@ namespace xna { return value > ByteMaxValue ? ByteMaxValue : value; } - static Uint PackHelper(float vectorX, float vectorY, float vectorZ, float vectorW) { - const auto byteMax = static_cast(ByteMaxValue); - const auto x = PackUtils::PackUNorm(byteMax, vectorX); - const auto y = PackUtils::PackUNorm(byteMax, vectorY) << 8; - const auto z = PackUtils::PackUNorm(byteMax, vectorZ); - const auto w = PackUtils::PackUNorm(byteMax, vectorW) << 24; - - return x | y | z | w; - } + static Uint PackHelper(float vectorX, float vectorY, float vectorZ, float vectorW); }; struct Colors { diff --git a/inc/common/math.hpp b/inc/common/math.hpp index 452d28d..9d71509 100644 --- a/inc/common/math.hpp +++ b/inc/common/math.hpp @@ -6,9 +6,9 @@ namespace xna { struct MathHelper { - static constexpr double E = 2.7182818284590451; - static constexpr double PI = 3.1415926535897931; - static constexpr double TAU = 6.2831853071795862; + static constexpr double E = 2.7182818284590452354; + static constexpr double PI = 3.14159265358979323846; + static constexpr double TAU = PI * 2; static constexpr double EPSILON = std::numeric_limits::epsilon(); static constexpr float ToRadians(float degrees) { return degrees * (static_cast(PI) / 180.0f); } @@ -36,22 +36,8 @@ namespace xna { return Lerp(value1, value2, (num * num * (3.0F - 2.0F * num))); } - static float CatmullRom(float value1, float value2, float value3, float value4, float amount) { - const auto num1 = amount * amount; - const auto num2 = amount * num1; - return (0.5F * (2.0F * value2 + (-value1 + value3) * amount + (2.0F * value1 - 5.0F * value2 + 4.0F * value3 - value4) * num1 + (-value1 + 3.0F * value2 - 3.0F * value3 + value4) * num2)); - } - - static float Hermite(float value1, float tangent1, float value2, float tangent2, float amount) { - const auto num1 = amount; - const auto num2 = num1 * num1; - const auto num3 = num1 * num2; - const auto num4 = (2.0F * num3 - 3.0F * num2 + 1.0F); - const auto num5 = (-2.0F * num3 + 3.0F * num2); - const auto num6 = num3 - 2.0f * num2 + num1; - const auto num7 = num3 - num2; - return value1 * num4 + value2 * num5 + tangent1 * num6 + tangent2 * num7; - } + static float CatmullRom(float value1, float value2, float value3, float value4, float amount); + static float Hermite(float value1, float tangent1, float value2, float tangent2, float amount); }; } diff --git a/inc/common/packedvalue.hpp b/inc/common/packedvalue.hpp index bb43a40..53de09b 100644 --- a/inc/common/packedvalue.hpp +++ b/inc/common/packedvalue.hpp @@ -1,9 +1,9 @@ #ifndef CXNA_COMMON_PACKEDVECTOR_HPP #define CXNA_COMMON_PACKEDVECTOR_HPP +#include "../default.hpp" #include "vectors.hpp" #include -#include "../default.hpp" namespace xna { class IPackedVector { diff --git a/inc/common/quaternion.hpp b/inc/common/quaternion.hpp index d7adeba..5cb2e91 100644 --- a/inc/common/quaternion.hpp +++ b/inc/common/quaternion.hpp @@ -15,9 +15,200 @@ namespace xna { constexpr Quaternion(float X, float Y, float Z, float W) : X(X), Y(Y), Z(Z), W(W) { } + constexpr Quaternion(Vector3 vectorPart, float scalarPart) + : X(vectorPart.X), Y(vectorPart.Y), Z(vectorPart.Z), W(scalarPart) { } + constexpr bool operator==(const Quaternion& other) const { return X == other.X && Y == other.Y && Z == other.Z && W == other.W; } + + static constexpr Quaternion Identity() { + return { 0.0f, 0.0f, 0.0f, 1.0f }; + } + + float constexpr LengthSquared() const { + return X * X + Y * Y + Z * Z + W * W; + } + + inline float Length() const { return std::sqrt(LengthSquared()); } + + void Normalize() { + const auto num = 1.0F / Length(); + X *= num; + Y *= num; + Z *= num; + W *= num; + } + + static Quaternion Normalize(Quaternion const& quaternion) { + auto q = quaternion; + q.Normalize(); + return q; + } + + constexpr void Conjugate() { + X = -X; + Y = -Y; + Z = -Z; + } + + static constexpr Quaternion Conjugate(Quaternion const& value) { + Quaternion quaternion; + quaternion.X = -value.X; + quaternion.Y = -value.Y; + quaternion.Z = -value.Z; + quaternion.W = value.W; + return quaternion; + } + + static constexpr Quaternion Inverse(Quaternion const& quaternion) { + const auto num = 1.0f / quaternion.LengthSquared(); + Quaternion quaternion1; + quaternion1.X = -quaternion.X * num; + quaternion1.Y = -quaternion.Y * num; + quaternion1.Z = -quaternion.Z * num; + quaternion1.W = quaternion.W * num; + return quaternion1; + } + + static Quaternion CreateFromAxisAngle(Vector3 const& axis, float angle); + static Quaternion CreateFromYawPitchRoll(float yaw, float pitch, float roll); + static Quaternion CreateFromRotationMatrix(Matrix const& matrix); + + static constexpr float Dot(Quaternion const& quaternion1, Quaternion const& quaternion2) { + return quaternion1.X * quaternion2.X + quaternion1.Y * quaternion2.Y + quaternion1.Z * quaternion2.Z + quaternion1.W * quaternion2.W; + } + + static Quaternion Slerp(Quaternion const& quaternion1, Quaternion const& quaternion2, float amount); + static Quaternion Lerp(Quaternion const& quaternion1, Quaternion const& quaternion2, float amount); + + static constexpr Quaternion Concatenate(Quaternion const& value1, Quaternion const& value2) { + const auto x1 = value2.X; + const auto y1 = value2.Y; + const auto z1 = value2.Z; + const auto w1 = value2.W; + const auto x2 = value1.X; + const auto y2 = value1.Y; + const auto z2 = value1.Z; + const auto w2 = value1.W; + const auto num1 = y1 * z2 - z1 * y2; + const auto num2 = z1 * x2 - x1 * z2; + const auto num3 = x1 * y2 - y1 * x2; + const auto num4 = x1 * x2 + y1 * y2 + z1 * z2; + Quaternion quaternion; + quaternion.X = (x1 * w2 + x2 * w1) + num1; + quaternion.Y = (y1 * w2 + y2 * w1) + num2; + quaternion.Z = (z1 * w2 + z2 * w1) + num3; + quaternion.W = w1 * w2 - num4; + return quaternion; + } + + static constexpr Quaternion Negate(Quaternion const& quaternion) { + Quaternion quaternion1; + quaternion1.X = -quaternion.X; + quaternion1.Y = -quaternion.Y; + quaternion1.Z = -quaternion.Z; + quaternion1.W = -quaternion.W; + return quaternion1; + } + + static constexpr Quaternion Add(Quaternion const& quaternion1, Quaternion const& quaternion2) { + Quaternion quaternion; + quaternion.X = quaternion1.X + quaternion2.X; + quaternion.Y = quaternion1.Y + quaternion2.Y; + quaternion.Z = quaternion1.Z + quaternion2.Z; + quaternion.W = quaternion1.W + quaternion2.W; + return quaternion; + } + + static constexpr Quaternion Subtract(Quaternion const& quaternion1, Quaternion const& quaternion2) { + Quaternion quaternion; + quaternion.X = quaternion1.X - quaternion2.X; + quaternion.Y = quaternion1.Y - quaternion2.Y; + quaternion.Z = quaternion1.Z - quaternion2.Z; + quaternion.W = quaternion1.W - quaternion2.W; + return quaternion; + } + + static constexpr Quaternion Multiply(Quaternion const& quaternion1, Quaternion const& quaternion2) { + const auto x1 = quaternion1.X; + const auto y1 = quaternion1.Y; + const auto z1 = quaternion1.Z; + const auto w1 = quaternion1.W; + const auto x2 = quaternion2.X; + const auto y2 = quaternion2.Y; + const auto z2 = quaternion2.Z; + const auto w2 = quaternion2.W; + const auto num1 = y1 * z2 - z1 * y2; + const auto num2 = z1 * x2 - x1 * z2; + const auto num3 = x1 * y2 - y1 * x2; + const auto num4 = x1 * x2 + y1 * y2 + z1 * z2; + Quaternion quaternion; + quaternion.X = (x1 * w2 + x2 * w1) + num1; + quaternion.Y = (y1 * w2 + y2 * w1) + num2; + quaternion.Z = (z1 * w2 + z2 * w1) + num3; + quaternion.W = w1 * w2 - num4; + return quaternion; + } + + static constexpr Quaternion Multiply(Quaternion const& quaternion1, float scaleFactor) { + Quaternion quaternion; + quaternion.X = quaternion1.X * scaleFactor; + quaternion.Y = quaternion1.Y * scaleFactor; + quaternion.Z = quaternion1.Z * scaleFactor; + quaternion.W = quaternion1.W * scaleFactor; + return quaternion; + } + + static constexpr Quaternion Divide(Quaternion const& quaternion1, Quaternion const& quaternion2) { + const auto x = quaternion1.X; + const auto y = quaternion1.Y; + const auto z = quaternion1.Z; + const auto w = quaternion1.W; + const auto num1 = 1.0f / (quaternion2.X * quaternion2.X + quaternion2.Y * quaternion2.Y + quaternion2.Z * quaternion2.Z + quaternion2.W * quaternion2.W); + const auto num2 = -quaternion2.X * num1; + const auto num3 = -quaternion2.Y * num1; + const auto num4 = -quaternion2.Z * num1; + const auto num5 = quaternion2.W * num1; + const auto num6 = y * num4 - z * num3; + const auto num7 = z * num2 - x * num4; + const auto num8 = x * num3 - y * num2; + const auto num9 = x * num2 + y * num3 + z * num4; + Quaternion quaternion; + quaternion.X = (x * num5 + num2 * w) + num6; + quaternion.Y = (y * num5 + num3 * w) + num7; + quaternion.Z = (z * num5 + num4 * w) + num8; + quaternion.W = w * num5 - num9; + return quaternion; + } + + constexpr Quaternion operator-(Quaternion const& quaternion) const { + return Quaternion::Negate(quaternion); + } + + constexpr friend Quaternion operator+(Quaternion const& q1, Quaternion const& q2) { + return Quaternion::Add(q1, q2); + } + + constexpr friend Quaternion operator-(Quaternion const& q1, Quaternion const& q2) { + return Quaternion::Add(q1, q2); + } + + constexpr friend Quaternion operator*(Quaternion const& q1, Quaternion const& q2) { + return Quaternion::Multiply(q1, q2); + } + + constexpr friend Quaternion operator/(Quaternion const& q1, Quaternion const& q2) { + return Quaternion::Divide(q1, q2); + } + + constexpr friend Quaternion operator*(Quaternion const& q1, float scaleValue) { + return Quaternion::Multiply(q1, scaleValue); + } + + constexpr friend Quaternion operator*(float scaleValue, Quaternion const& q1) { + return Quaternion::Multiply(q1, scaleValue); + } }; constexpr Vector2 Vector2::Transform(Vector2 const& value, Quaternion const& rotation) {