From 2bfc037ae780b0a18ba52b5b1a2f281c8bbc66a9 Mon Sep 17 00:00:00 2001 From: penggrin12 Date: Mon, 11 May 2026 15:31:54 +0300 Subject: [PATCH 1/2] Separate Quaternion from Vector4 & Implement many Vector4 methods --- clib.json | 1 + include/CMakeLists.txt | 1 + include/Quaternion.hpp | 296 +++++++++++++++++++++++++++++++++++++++++ include/Vector4.hpp | 251 +++++++++++++++++++++++++++++----- 4 files changed, 513 insertions(+), 36 deletions(-) create mode 100644 include/Quaternion.hpp diff --git a/clib.json b/clib.json index 800d7059..185153c0 100644 --- a/clib.json +++ b/clib.json @@ -40,6 +40,7 @@ "include/ModelAnimation.hpp", "include/Mouse.hpp", "include/Music.hpp", + "include/Quaternion.hpp", "include/physac.hpp", "include/Ray.hpp", "include/RayCollision.hpp", diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 0a812930..84bad5ef 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -23,6 +23,7 @@ set(RAYLIB_CPP_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/ModelAnimation.hpp ${CMAKE_CURRENT_SOURCE_DIR}/Mouse.hpp ${CMAKE_CURRENT_SOURCE_DIR}/Music.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/Quaternion.hpp ${CMAKE_CURRENT_SOURCE_DIR}/Ray.hpp ${CMAKE_CURRENT_SOURCE_DIR}/RayCollision.hpp ${CMAKE_CURRENT_SOURCE_DIR}/RaylibException.hpp diff --git a/include/Quaternion.hpp b/include/Quaternion.hpp new file mode 100644 index 00000000..d4ebd204 --- /dev/null +++ b/include/Quaternion.hpp @@ -0,0 +1,296 @@ +#ifndef RAYLIB_CPP_INCLUDE_QUATERNION_HPP_ +#define RAYLIB_CPP_INCLUDE_QUATERNION_HPP_ + +#ifndef RAYLIB_CPP_NO_MATH +#include +#include +#endif + +#include + +#include "./raylib-cpp-utils.hpp" +#include "./raylib.hpp" +#include "./raymath.hpp" + +namespace raylib { +class Vector4 : public ::Vector4 {}; + +/** + * Quaternion type + */ +class Quaternion : public ::Quaternion { +public: + constexpr Quaternion(const ::Quaternion& quat) : ::Quaternion{quat.x, quat.y, quat.z, quat.w} {} + explicit constexpr Quaternion(const raylib::Vector4 vec4) : ::Quaternion{vec4.x, vec4.y, vec4.z, vec4.w} {} + + explicit constexpr Quaternion(const float x = 0, const float y = 0, const float z = 0, const float w = 1) : ::Quaternion{x, y, z, w} {} + + GETTERSETTER(float, X, x) + GETTERSETTER(float, Y, y) + GETTERSETTER(float, Z, z) + GETTERSETTER(float, W, w) + + Quaternion& operator=(const ::Quaternion& quaternion) { + set(quaternion); + return *this; + } + + /* + * An exact value by value equality comparison. + * Due to floating point inaccuracies consider using Equals instead. + */ + constexpr bool operator==(const ::Quaternion& other) const { + return x == other.x && y == other.y && z == other.z && w == other.w; + } + + /* + * An exact value by value inequality comparison. + * Due to floating point inaccuracies consider using Equals instead. + */ + constexpr bool operator!=(const ::Quaternion& other) const { return !(*this == other); } + + RLCPP_NODISCARD std::string ToString() const { return ::TextFormat("Quaternion(%f, %f, %f, %f)", x, y, z, w); } + + operator std::string() const { return ToString(); } + +#ifndef RAYLIB_CPP_NO_MATH + /** + * Add two quaternions + */ + RLCPP_NODISCARD Quaternion Add(const ::Quaternion& quaternion) const { return ::QuaternionAdd(*this, quaternion); } + + /** + * Add two quaternions + */ + Quaternion operator+(const ::Quaternion& quaternion) const { return ::QuaternionAdd(*this, quaternion); } + + Quaternion& operator+=(const ::Quaternion& quaternion) { + set(::QuaternionAdd(*this, quaternion)); + + return *this; + } + + /** + * Add quaternion and float value + */ + RLCPP_NODISCARD Quaternion Add(const float value) const { + return ::QuaternionAddValue(*this, value); + } + + /** + * Add quaternion and float value + */ + Quaternion operator+(const float value) const { + return ::QuaternionAddValue(*this, value); + } + + /** + * Add quaternion and float value + */ + Quaternion& operator+=(const float value) { + set(::QuaternionAddValue(*this, value)); + + return *this; + } + + /** + * Add quaternion and float value + */ + friend Quaternion operator+(const float lhs, const Quaternion& rhs) { return rhs + lhs; } + + /** + * Subtract two quaternions. + */ + RLCPP_NODISCARD Quaternion Subtract(const ::Quaternion& quaternion) const { return ::QuaternionSubtract(*this, quaternion); } + + /** + * Subtract two quaternions. + */ + Quaternion operator-(const ::Quaternion& quaternion) const { return ::QuaternionSubtract(*this, quaternion); } + + Quaternion& operator-=(const ::Quaternion& quaternion) { + set(::QuaternionSubtract(*this, quaternion)); + + return *this; + } + + /** + * Subtract quaternion by float value + */ + RLCPP_NODISCARD Quaternion Subtract(const float value) const { + return ::QuaternionSubtractValue(*this, value); + } + + /** + * Get identity quaternion + */ + static RLCPP_NODISCARD Quaternion Identity() { + return ::QuaternionIdentity(); + } + + RLCPP_NODISCARD float Length() const { return ::QuaternionLength(*this); } + + RLCPP_NODISCARD Quaternion Normalize() const { return ::QuaternionNormalize(*this); } + + RLCPP_NODISCARD Quaternion Invert() const { return ::QuaternionInvert(*this); } + + /** + * Multiply quaternion by quaternion + */ + RLCPP_NODISCARD Quaternion Multiply(const ::Quaternion& other) const { return ::QuaternionMultiply(*this, other); } + + /** + * Multiply quaternion by quaternion + */ + Quaternion operator*(const ::Quaternion& other) const { return ::QuaternionMultiply(*this, other); } + + /** + * Multiply quaternion by quaternion + */ + Quaternion& operator*=(const ::Quaternion& other) { + set(::QuaternionMultiply(*this, other)); + + return *this; + } + + /** + * Scale quaternion components by value (multiply) + */ + RLCPP_NODISCARD Quaternion Scale(const float scale) const { return ::QuaternionScale(*this, scale); } + + /** + * Divide quaternion by quaternion + */ + RLCPP_NODISCARD Quaternion Divide(const ::Quaternion& quaternion) const { return ::QuaternionDivide(*this, quaternion); } + + /** + * Divide quaternion by quaternion + */ + Quaternion operator/(const ::Quaternion& quaternion) const { return ::QuaternionDivide(*this, quaternion); } + + /** + * Divide quaternion by quaternion + */ + Quaternion& operator/=(const ::Quaternion& quaternion) { + set(::QuaternionDivide(*this, quaternion)); + + return *this; + } + + /** + * Divide quaternion components by value + */ + RLCPP_NODISCARD constexpr Quaternion Divide(const float div) const { return ::Quaternion{x / div, y / div, z / div, w / div}; } + + RLCPP_NODISCARD Quaternion Lerp(const ::Quaternion& v2, const float amount) const + { + return ::QuaternionLerp(*this, v2, amount); + } + + /** + * Calculate normalized linear interpolation between two quaternions + */ + RLCPP_NODISCARD Quaternion Nlerp(const ::Quaternion& v2, const float amount) const { + return ::QuaternionNlerp(*this, v2, amount); + } + + /** + * Calculates spherical linear interpolation between two quaternions + */ + RLCPP_NODISCARD Quaternion Slerp(const ::Quaternion& v2, const float amount) const { + return ::QuaternionSlerp(*this, v2, amount); + } + + /** + * Calculate quaternion cubic spline interpolation using Cubic Hermite Spline + */ + RLCPP_NODISCARD Quaternion CubicHermiteSpline( + const ::Quaternion& outTangent1, + const ::Quaternion& q2, + const ::Quaternion& inTangent2, + const float t + ) const { + return ::QuaternionCubicHermiteSpline(*this, outTangent1, q2, inTangent2, t); + } + + /** + * Calculate quaternion based on the rotation from one vector to another + */ + static RLCPP_NODISCARD Quaternion FromVector3ToVector3(const ::Vector3& from, const ::Vector3& to) { + return ::QuaternionFromVector3ToVector3(from, to); + } + + /** + * Get a quaternion for a given rotation matrix + */ + static RLCPP_NODISCARD Quaternion FromMatrix(const ::Matrix& mat) { + return ::QuaternionFromMatrix(mat); + } + + /** + * Get a matrix for this quaternion + */ + RLCPP_NODISCARD ::Matrix ToMatrix() const { + return ::QuaternionToMatrix(*this); + } + + /** + * Get rotation quaternion for an angle and axis + * NOTE: Angle must be provided in radians + */ + static RLCPP_NODISCARD Quaternion FromAxisAngle(const ::Vector3& axis, const float angle) { + return ::QuaternionFromAxisAngle(axis, angle); + } + + /** + * Get the rotation angle and axis for this quaternion + */ + void ToAxisAngle(::Vector3* outAxis, float* outAngle) const { + ::QuaternionToAxisAngle(*this, outAxis, outAngle); + } + + /** + * Get the quaternion equivalent to Euler angles + * NOTE: Rotation order is ZYX + */ + static RLCPP_NODISCARD Quaternion FromEuler(const float pitch, const float yaw, const float roll) { + return ::QuaternionFromEuler(pitch, yaw, roll); + } + + /** + * Get the Euler angles equivalent to quaternion + * NOTE: Angles are returned in radians + */ + RLCPP_NODISCARD ::Vector3 ToEuler() const { + return ::QuaternionToEuler(*this); + } + + /** + * Transform quaternion given transformation matrix + */ + RLCPP_NODISCARD Quaternion Transform(const ::Matrix& mat) const { + return ::QuaternionTransform(*this, mat); + } + + /* + * Check whether two given quaternions are almost equal + */ + RLCPP_NODISCARD bool Equals(const ::Quaternion& other) const { + return static_cast(::QuaternionEquals(*this, other)); + } +#endif +protected: + void set(const ::Quaternion& quat) { + x = quat.x; + y = quat.y; + z = quat.z; + w = quat.w; + } +}; + +} // namespace raylib + +using RQuaternion = raylib::Quaternion; + +#endif // RAYLIB_CPP_INCLUDE_QUATERNION_HPP_ + diff --git a/include/Vector4.hpp b/include/Vector4.hpp index 71593717..e42c6bbb 100644 --- a/include/Vector4.hpp +++ b/include/Vector4.hpp @@ -13,17 +13,20 @@ #include "./raymath.hpp" namespace raylib { +class Quaternion : public ::Quaternion {}; + /** * Vector4 type */ class Vector4 : public ::Vector4 { public: constexpr Vector4(const ::Vector4& vec) : ::Vector4{vec.x, vec.y, vec.z, vec.w} {} + explicit constexpr Vector4(const raylib::Quaternion quat) : ::Vector4{quat.x, quat.y, quat.z, quat.w} {} - constexpr Vector4(float x = 0, float y = 0, float z = 0, float w = 0) : ::Vector4{x, y, z, w} {} - constexpr Vector4(::Rectangle rectangle) : ::Vector4{rectangle.x, rectangle.y, rectangle.width, rectangle.height} {} + explicit constexpr Vector4(const float x = 0, const float y = 0, const float z = 0, const float w = 0) : ::Vector4{x, y, z, w} {} + explicit constexpr Vector4(const ::Rectangle rectangle) : ::Vector4{rectangle.x, rectangle.y, rectangle.width, rectangle.height} {} - Vector4(::Color color) { set(ColorNormalize(color)); } + explicit Vector4(const ::Color color) { set(ColorNormalize(color)); } GETTERSETTER(float, X, x) GETTERSETTER(float, Y, y) @@ -35,75 +38,255 @@ class Vector4 : public ::Vector4 { return *this; } + /* + * An exact value by value equality comparison. + * Due to floating point inaccuracies consider using Equals instead. + */ constexpr bool operator==(const ::Vector4& other) const { return x == other.x && y == other.y && z == other.z && w == other.w; } + /* + * An exact value by value inequality comparison. + * Due to floating point inaccuracies consider using Equals instead. + */ constexpr bool operator!=(const ::Vector4& other) const { return !(*this == other); } RLCPP_NODISCARD constexpr ::Rectangle ToRectangle() const { return {x, y, z, w}; } constexpr operator ::Rectangle() const { return {x, y, z, w}; } - RLCPP_NODISCARD std::string ToString() const { return TextFormat("Vector4(%f, %f, %f, %f)", x, y, z, w); } + RLCPP_NODISCARD std::string ToString() const { return ::TextFormat("Vector4(%f, %f, %f, %f)", x, y, z, w); } operator std::string() const { return ToString(); } #ifndef RAYLIB_CPP_NO_MATH - RLCPP_NODISCARD Vector4 Multiply(const ::Vector4& vector4) const { return QuaternionMultiply(*this, vector4); } + static Vector4 Zero() { return ::Vector4Zero(); } + + static Vector4 One() { return ::Vector4One(); } + + /** + * Add two vectors + */ + RLCPP_NODISCARD Vector4 Add(const ::Vector4& vector4) const { return ::Vector4Add(*this, vector4); } + + /** + * Add two vectors + */ + Vector4 operator+(const ::Vector4& vector4) const { return ::Vector4Add(*this, vector4); } + + Vector4& operator+=(const ::Vector4& vector4) { + set(::Vector4Add(*this, vector4)); + + return *this; + } + + /** + * Add vector and float value + */ + RLCPP_NODISCARD Vector4 Add(const float value) const { + return ::Vector4AddValue(*this, value); + } + + /** + * Add vector and float value + */ + Vector4 operator+(const float value) const { + return ::Vector4AddValue(*this, value); + } + + /** + * Add vector and float value + */ + Vector4& operator+=(const float value) { + set(::Vector4AddValue(*this, value)); + + return *this; + } - Vector4 operator*(const ::Vector4& vector4) const { return QuaternionMultiply(*this, vector4); } + /** + * Add vector and float value + */ + friend Vector4 operator+(const float lhs, const Vector4& rhs) { return rhs + lhs; } - RLCPP_NODISCARD Vector4 Lerp(const ::Vector4& vector4, float amount) const { return QuaternionLerp(*this, vector4, amount); } + /** + * Subtract two vectors. + */ + RLCPP_NODISCARD Vector4 Subtract(const ::Vector4& vector4) const { return ::Vector4Subtract(*this, vector4); } + + /** + * Subtract two vectors. + */ + Vector4 operator-(const ::Vector4& vector4) const { return ::Vector4Subtract(*this, vector4); } - RLCPP_NODISCARD Vector4 Nlerp(const ::Vector4& vector4, float amount) const { return QuaternionNlerp(*this, vector4, amount); } + Vector4& operator-=(const ::Vector4& vector4) { + set(::Vector4Subtract(*this, vector4)); - RLCPP_NODISCARD Vector4 Slerp(const ::Vector4& vector4, float amount) const { return QuaternionSlerp(*this, vector4, amount); } + return *this; + } - RLCPP_NODISCARD Matrix ToMatrix() const { return QuaternionToMatrix(*this); } + /** + * Subtract vector by float value + */ + RLCPP_NODISCARD Vector4 Subtract(const float value) const { + return ::Vector4SubtractValue(*this, value); + } - RLCPP_NODISCARD float Length() const { return QuaternionLength(*this); } + /** + * Subtract vector by float value + */ + Vector4 operator-(const float value) const { + return ::Vector4SubtractValue(*this, value); + } + + /** + * Subtract vector by float value + */ + Vector4& operator-=(const float value) { + set(::Vector4SubtractValue(*this, value)); + + return *this; + } + + /** + * Subtract vector by float value + */ + friend Vector4 operator-(const float lhs, const Vector4& rhs) { return rhs - lhs; } + + /** + * Negate provided vector + */ + RLCPP_NODISCARD Vector4 Negate() const { return ::Vector4Negate(*this); } - RLCPP_NODISCARD Vector4 Normalize() const { return QuaternionNormalize(*this); } + /** + * Negate provided vector + */ + Vector4 operator-() const { return ::Vector4Negate(*this); } - RLCPP_NODISCARD Vector4 Invert() const { return QuaternionInvert(*this); } + /** + * Multiply vector by vector + */ + RLCPP_NODISCARD Vector4 Multiply(const ::Vector4& other) const { return ::Vector4Multiply(*this, other); } - void ToAxisAngle(::Vector3* outAxis, float* outAngle) const { QuaternionToAxisAngle(*this, outAxis, outAngle); } + /** + * Multiply vector by vector + */ + Vector4 operator*(const ::Vector4& other) const { return ::Vector4Multiply(*this, other); } /** - * Get the rotation angle and axis for a given quaternion + * Multiply vector by vector */ - RLCPP_NODISCARD std::pair ToAxisAngle() const { - Vector3 outAxis; - float outAngle; - QuaternionToAxisAngle(*this, &outAxis, &outAngle); + Vector4& operator*=(const ::Vector4& other) { + set(::Vector4Multiply(*this, other)); - return { outAxis, outAngle }; + return *this; } - RLCPP_NODISCARD Vector4 Transform(const ::Matrix& matrix) const { return ::QuaternionTransform(*this, matrix); } + /** + * Scale vector components by value (multiply) + */ + RLCPP_NODISCARD Vector4 Scale(const float scale) const { return ::Vector4Scale(*this, scale); } - static Vector4 Identity() { return ::QuaternionIdentity(); } + /** + * Scale vector components by value (multiply) + */ + Vector4 operator*(const float scale) const { return ::Vector4Scale(*this, scale); } - static Vector4 FromVector3ToVector3(const ::Vector3& from, const ::Vector3& to) { - return ::QuaternionFromVector3ToVector3(from, to); + /** + * Scale vector components by value (multiply) + */ + Vector4& operator*=(const float scale) { + set(::Vector4Scale(*this, scale)); + + return *this; } - static Vector4 FromMatrix(const ::Matrix& matrix) { return ::QuaternionFromMatrix(matrix); } + /** + * Scale vector components by value (multiply) + */ + friend Vector4 operator*(const float lhs, const Vector4& rhs) { return rhs * lhs; } + + /** + * Divide vector by vector + */ + RLCPP_NODISCARD Vector4 Divide(const ::Vector4& vector4) const { return ::Vector4Divide(*this, vector4); } + + /** + * Divide vector by vector + */ + Vector4 operator/(const ::Vector4& vector4) const { return ::Vector4Divide(*this, vector4); } + + /** + * Divide vector by vector + */ + Vector4& operator/=(const ::Vector4& vector4) { + set(::Vector4Divide(*this, vector4)); - static Vector4 FromAxisAngle(const ::Vector3& axis, const float angle) { - return ::QuaternionFromAxisAngle(axis, angle); + return *this; } - static Vector4 FromEuler(const float pitch, const float yaw, const float roll) { - return ::QuaternionFromEuler(pitch, yaw, roll); + /** + * Divide vector components by value + */ + RLCPP_NODISCARD constexpr Vector4 Divide(const float div) const { return ::Vector4{x / div, y / div, z / div, w / div}; } + + /** + * Divide vector components by value + */ + Vector4 operator/(const float div) const { return Divide(div); } + + /** + * Divide vector components by value + */ + constexpr Vector4& operator/=(const float div) { + x /= div; + y /= div; + z /= div; + w /= div; + + return *this; } - static Vector4 FromEuler(const ::Vector3& vector3) { - return ::QuaternionFromEuler(vector3.x, vector3.y, vector3.z); + /** + * Divide vector components by value + */ + constexpr friend Vector4 operator/(const float lhs, const Vector4& rhs) { + return Vector4{ + lhs / rhs.x, + lhs / rhs.y, + lhs / rhs.z, + lhs / rhs.w + }; } - RLCPP_NODISCARD Vector3 ToEuler() const { return ::QuaternionToEuler(*this); } + RLCPP_NODISCARD float Length() const { return ::Vector4Length(*this); } + + RLCPP_NODISCARD float LengthSqr() const { return ::Vector4LengthSqr(*this); } + + RLCPP_NODISCARD float DotProduct(const ::Vector4& v2) const { return ::Vector4DotProduct(*this, v2); } + + RLCPP_NODISCARD float Distance(const ::Vector4& v2) const { return ::Vector4Distance(*this, v2); } + + RLCPP_NODISCARD float DistanceSqr(const ::Vector4& v2) const { return ::Vector4DistanceSqr(*this, v2); } + + RLCPP_NODISCARD Vector4 Normalize() const { return ::Vector4Normalize(*this); } + + RLCPP_NODISCARD Vector4 Min(const ::Vector4& v2) const { return ::Vector4Min(*this, v2); } + + RLCPP_NODISCARD Vector4 Max(const ::Vector4& v2) const { return ::Vector4Max(*this, v2); } + + RLCPP_NODISCARD Vector4 Lerp(const ::Vector4& v2, const float amount) const { return ::Vector4Lerp(*this, v2, amount); } + + RLCPP_NODISCARD Vector4 MoveTowards(const ::Vector4& target, const float maxDistance) const { return ::Vector4MoveTowards(*this, target, maxDistance); } + + RLCPP_NODISCARD Vector4 Invert() const { return ::Vector4Invert(*this); } + + /* + * Check whether two given vectors are almost equal + */ + RLCPP_NODISCARD bool Equals(const ::Vector4& other) const { + return static_cast(::Vector4Equals(*this, other)); + } #endif RLCPP_NODISCARD Color ColorFromNormalized() const { return ::ColorFromNormalized(*this); } @@ -118,12 +301,8 @@ class Vector4 : public ::Vector4 { } }; -// Alias the Vector4 as Quaternion. -using Quaternion = Vector4; - } // namespace raylib using RVector4 = raylib::Vector4; -using RQuaternion = raylib::Quaternion; #endif // RAYLIB_CPP_INCLUDE_VECTOR4_HPP_ From 859cef392bd232eb65f08578d2671144f06df7ee Mon Sep 17 00:00:00 2001 From: penggrin12 Date: Mon, 11 May 2026 15:44:56 +0300 Subject: [PATCH 2/2] (Try to) fix a GCC/Clang build error for Vector4 --- include/Vector4.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Vector4.hpp b/include/Vector4.hpp index e42c6bbb..37ec1cf3 100644 --- a/include/Vector4.hpp +++ b/include/Vector4.hpp @@ -238,7 +238,7 @@ class Vector4 : public ::Vector4 { /** * Divide vector components by value */ - constexpr Vector4& operator/=(const float div) { + Vector4& operator/=(const float div) { x /= div; y /= div; z /= div;