From 1d348e10711b682a70be94c31c1079d7bbfdac74 Mon Sep 17 00:00:00 2001 From: kj16609 Date: Thu, 27 Feb 2025 12:06:22 -0500 Subject: [PATCH] Fixes the gimbal lock and rotation issues --- src/editor/src/gui/drawGameObject.cpp | 15 +- src/editor/src/gui/helpers.cpp | 38 +++-- src/editor/src/gui/helpers.hpp | 7 +- src/engine/FGL_DEFINES.hpp | 4 +- src/engine/KeyboardMovementController.cpp | 28 ++-- .../assets/model/builders/SceneBuilder.cpp | 2 +- src/engine/camera/Camera.cpp | 6 +- src/engine/camera/Camera.hpp | 8 +- src/engine/constants.hpp | 2 + src/engine/gameobjects/GameObject.hpp | 2 +- src/engine/gameobjects/components/drawers.cpp | 9 +- .../interface/GameObjectComponent.hpp | 6 +- src/engine/math/raycast/raycast.hpp | 2 +- src/engine/primitives/Rotation.cpp | 150 ------------------ src/engine/primitives/TransformComponent.cpp | 29 +++- src/engine/primitives/TransformComponent.hpp | 45 +++++- .../boxes/AxisAlignedBoundingBox.cpp | 24 +-- .../primitives/rotation/EulerRotation.cpp | 16 ++ .../primitives/rotation/EulerRotation.hpp | 31 ++++ .../primitives/rotation/QuatRotation.cpp | 105 ++++++++++++ .../QuatRotation.hpp} | 43 ++--- .../primitives/rotation/UniversalRotation.cpp | 57 +++++++ .../primitives/rotation/UniversalRotation.hpp | 87 ++++++++++ .../primitives/vectors/NormalVector.hpp | 2 +- src/tests/src/gtest_printers.hpp | 29 ++-- src/tests/src/math/quat.cpp | 103 +++++++----- src/tests/src/math/transform.cpp | 24 +++ 27 files changed, 568 insertions(+), 306 deletions(-) delete mode 100644 src/engine/primitives/Rotation.cpp create mode 100644 src/engine/primitives/rotation/EulerRotation.cpp create mode 100644 src/engine/primitives/rotation/EulerRotation.hpp create mode 100644 src/engine/primitives/rotation/QuatRotation.cpp rename src/engine/primitives/{Rotation.hpp => rotation/QuatRotation.hpp} (52%) create mode 100644 src/engine/primitives/rotation/UniversalRotation.cpp create mode 100644 src/engine/primitives/rotation/UniversalRotation.hpp create mode 100644 src/tests/src/math/transform.cpp diff --git a/src/editor/src/gui/drawGameObject.cpp b/src/editor/src/gui/drawGameObject.cpp index 792a70a..9ad01c4 100644 --- a/src/editor/src/gui/drawGameObject.cpp +++ b/src/editor/src/gui/drawGameObject.cpp @@ -14,12 +14,21 @@ namespace fgl::engine::gui ImGui::InputText( "Name", &name_input_temp ); if ( game_object.getName() != name_input_temp ) game_object.setName( name_input_temp ); + auto& transform { game_object.getTransform() }; + // Transform - Position - dragFloat3( "Position", game_object.getTransform().translation.vec() ); + dragFloat3( "Position", transform.translation.vec() ); - dragFloat3Rot( "Rotation", game_object.getRotation() ); + if ( transform.rotation.isEuler() ) + { + dragFloat3Rot( "Rotation", transform.rotation.euler() ); + } + else + { + ImGui::Text( "Rotation was in quaternion" ); + } - dragFloat3( "Scale", game_object.getScale() ); + dragFloat3( "Scale", transform.scale ); } static GameObjectComponentPtr SELECTED_COMPONENT { nullptr }; diff --git a/src/editor/src/gui/helpers.cpp b/src/editor/src/gui/helpers.cpp index f009f8a..765c181 100644 --- a/src/editor/src/gui/helpers.cpp +++ b/src/editor/src/gui/helpers.cpp @@ -4,26 +4,24 @@ #include "helpers.hpp" -#include "primitives/Rotation.hpp" +#include "../../../engine/primitives/rotation/QuatRotation.hpp" +#include "primitives/rotation/EulerRotation.hpp" namespace fgl::engine::gui { float clampEuler( const float value ) { - if ( value >= 180.0f ) - { - // Wrap around. - return value - 360.0f; - } - else if ( value <= -180.0f ) - { - return value + 360.0f; - } + constexpr float max { 180.0f }; + constexpr float wrap { 360.0f }; + + if ( value >= max ) return clampEuler( value - wrap ); + if ( value <= -max ) return clampEuler( value + wrap ); + return value; } - void dragFloat3Rot( const char* const label, Rotation& rot ) + void dragFloat3Rot( const char* const label, EulerRotation& rot ) { enum Axis { @@ -32,7 +30,7 @@ namespace fgl::engine::gui Roll = 2 }; - const glm::vec3 c_dat { rot.euler() }; + const glm::vec3 c_dat { rot.vec() }; glm::vec3 dat { glm::degrees( c_dat ) }; ImGui::DragFloat3( label, &dat.x, 1.0f ); @@ -49,19 +47,19 @@ namespace fgl::engine::gui if ( changed[ Pitch ] ) { dat[ Pitch ] = clampEuler( dat[ Pitch ] ); - rot.setX( dat[ Pitch ] ); - } - - if ( changed[ Roll ] ) - { - dat[ Roll ] = clampEuler( dat[ Roll ] ); - rot.setZ( dat[ Roll ] ); + rot.x = dat[ Pitch ]; } if ( changed[ Yaw ] ) { dat[ Yaw ] = clampEuler( dat[ Yaw ] ); - rot.setY( dat[ Yaw ] ); + rot.y = dat[ Yaw ]; + } + + if ( changed[ Roll ] ) + { + dat[ Roll ] = clampEuler( dat[ Roll ] ); + rot.z = dat[ Roll ]; } } } // namespace fgl::engine::gui diff --git a/src/editor/src/gui/helpers.hpp b/src/editor/src/gui/helpers.hpp index 0890ab9..d9954ef 100644 --- a/src/editor/src/gui/helpers.hpp +++ b/src/editor/src/gui/helpers.hpp @@ -12,8 +12,9 @@ namespace fgl::engine { - struct Rotation; -} + struct EulerRotation; + struct QuatRotation; +} // namespace fgl::engine namespace fgl::engine::gui { @@ -22,6 +23,6 @@ namespace fgl::engine::gui ImGui::DragFloat3( label, &vec.x ); } - void dragFloat3Rot( const char* label, Rotation& rot ); + void dragFloat3Rot( const char* label, EulerRotation& rot ); } // namespace fgl::engine::gui diff --git a/src/engine/FGL_DEFINES.hpp b/src/engine/FGL_DEFINES.hpp index 12b6254..3eade49 100644 --- a/src/engine/FGL_DEFINES.hpp +++ b/src/engine/FGL_DEFINES.hpp @@ -16,9 +16,9 @@ #define FGL_DEFAULT_DEFAULT_CTOR( ClassName ) ClassName() = default; #define FGL_DEFAULT_COPY_ASSIGN( ClassName ) ClassName& operator=( const ClassName& ) = default; -#define FGL_DEFAULT_COPY_CTOR( ClassName ) ClassName( const ClassName& ) = default; +#define FGL_DEFAULT_COPY_CTOR( ClassName ) [[nodiscard]] ClassName( const ClassName& ) = default; #define FGL_DEFAULT_MOVE_ASSIGN( ClassName ) ClassName& operator=( ClassName&& ) = default; -#define FGL_DEFAULT_MOVE_CTOR( ClassName ) ClassName( ClassName&& ) = default; +#define FGL_DEFAULT_MOVE_CTOR( ClassName ) [[nodiscard]] ClassName( ClassName&& ) = default; #define FGL_DEFAULT_COPY( ClassName ) FGL_DEFAULT_COPY_CTOR( ClassName ) FGL_DEFAULT_COPY_ASSIGN( ClassName ) #define FGL_DEFAULT_MOVE( ClassName ) FGL_DEFAULT_MOVE_CTOR( ClassName ) FGL_DEFAULT_MOVE_ASSIGN( ClassName ) #define FGL_DEFAULT_ALL_RO5( ClassName ) \ diff --git a/src/engine/KeyboardMovementController.cpp b/src/engine/KeyboardMovementController.cpp index c54300d..30449fe 100644 --- a/src/engine/KeyboardMovementController.cpp +++ b/src/engine/KeyboardMovementController.cpp @@ -35,15 +35,6 @@ namespace fgl::engine constexpr float pitch_modifier { 1.0f }; constexpr float yaw_modifier { 1.0f }; - float pitch_change { 0.0f }; - float yaw_change { 0.0f }; - - if ( glfwGetKey( window, key_mappings.look_right ) == GLFW_PRESS ) yaw_change += rotation_rate * yaw_modifier; - if ( glfwGetKey( window, key_mappings.look_left ) == GLFW_PRESS ) yaw_change -= rotation_rate * yaw_modifier; - if ( glfwGetKey( window, key_mappings.look_up ) == GLFW_PRESS ) pitch_change += rotation_rate * pitch_modifier; - if ( glfwGetKey( window, key_mappings.look_down ) == GLFW_PRESS ) - pitch_change -= rotation_rate * pitch_modifier; - static bool cursor_enabled { true }; static bool cursor_restored { true }; static glm::vec2 stored_cursor_pos {}; @@ -78,6 +69,21 @@ namespace fgl::engine if ( cursor_enabled ) { + float pitch_change { 0.0f }; + float yaw_change { 0.0f }; + + if ( glfwGetKey( window, key_mappings.look_right ) == GLFW_PRESS ) + yaw_change += rotation_rate * yaw_modifier; + + if ( glfwGetKey( window, key_mappings.look_left ) == GLFW_PRESS ) + yaw_change -= rotation_rate * yaw_modifier; + + if ( glfwGetKey( window, key_mappings.look_up ) == GLFW_PRESS ) + pitch_change += rotation_rate * pitch_modifier; + + if ( glfwGetKey( window, key_mappings.look_down ) == GLFW_PRESS ) + pitch_change -= rotation_rate * pitch_modifier; + if ( pitch_change > std::numeric_limits< float >::epsilon() || pitch_change < -std::numeric_limits< float >::epsilon() ) target.getTransform().rotation.addX( dt * pitch_change ); @@ -92,13 +98,11 @@ namespace fgl::engine const float xpos { pos.x }; const float ypos { pos.y }; - Rotation target_rotation { target.getTransform().rotation }; + UniversalRotation& target_rotation { target.getTransform().rotation }; target_rotation.addZ( ( xpos * 0.006f ) * look_speed ); target_rotation.addX( ( ypos * 0.006f ) * look_speed ); - target.getTransform().rotation = target_rotation; - setCursorPos( window, { 0, 0 } ); } diff --git a/src/engine/assets/model/builders/SceneBuilder.cpp b/src/engine/assets/model/builders/SceneBuilder.cpp index 340a98f..4d79415 100644 --- a/src/engine/assets/model/builders/SceneBuilder.cpp +++ b/src/engine/assets/model/builders/SceneBuilder.cpp @@ -462,7 +462,7 @@ namespace fgl::engine static_cast< float >( y ), static_cast< float >( z ) }; - transform_component.rotation = rotation; + transform_component.rotation = UniversalRotation( rotation ); } else if ( !node.rotation.empty() ) { diff --git a/src/engine/camera/Camera.cpp b/src/engine/camera/Camera.cpp index a8c61da..df6efc1 100644 --- a/src/engine/camera/Camera.cpp +++ b/src/engine/camera/Camera.cpp @@ -272,7 +272,7 @@ namespace fgl::engine { const auto& [ pos, scale, rotation ] = m_transform; - const auto rotation_matrix { rotation.mat() }; + const auto rotation_matrix { rotation.forcedQuat().mat() }; const glm::vec3 forward { rotation_matrix * glm::vec4( constants::WORLD_FORWARD, 0.0f ) }; @@ -287,7 +287,7 @@ namespace fgl::engine updateFrustum(); } - FGL_FLATTEN_HOT void Camera::setView( const WorldCoordinate pos, const Rotation& rotation, const ViewMode mode ) + FGL_FLATTEN_HOT void Camera::setView( const WorldCoordinate pos, const QuatRotation& rotation, const ViewMode mode ) { switch ( mode ) { @@ -346,7 +346,7 @@ namespace fgl::engine { FGL_ASSERT( renderer, "Camera renderer is null" ); this->setPerspectiveProjection( m_fov_y, aspectRatio(), constants::NEAR_PLANE, constants::FAR_PLANE ); - this->setView( WorldCoordinate( constants::CENTER ), Rotation( 0.0f, 0.0f, 0.0f ) ); + this->setView( WorldCoordinate( constants::CENTER ), QuatRotation( 0.0f, 0.0f, 0.0f ) ); for ( std::uint8_t i = 0; i < constants::MAX_FRAMES_IN_FLIGHT; ++i ) { diff --git a/src/engine/camera/Camera.hpp b/src/engine/camera/Camera.hpp index 53413e9..76d3461 100644 --- a/src/engine/camera/Camera.hpp +++ b/src/engine/camera/Camera.hpp @@ -12,13 +12,13 @@ #include #pragma GCC diagnostic pop +#include "../primitives/rotation/QuatRotation.hpp" #include "CompositeSwapchain.hpp" #include "debug/Track.hpp" #include "engine/descriptors/DescriptorSet.hpp" #include "engine/memory/buffers/HostSingleT.hpp" #include "engine/memory/buffers/UniqueFrameSuballocation.hpp" #include "engine/primitives/Frustum.hpp" -#include "engine/primitives/Rotation.hpp" #include "engine/primitives/TransformComponent.hpp" #include "engine/rendering/types.hpp" @@ -122,9 +122,7 @@ namespace fgl::engine void setExtent( vk::Extent2D extent ); - [[nodiscard]] const Rotation& getRotation() const { return m_transform.rotation; } - - Rotation& getRotation() { return m_transform.rotation; } + [[nodiscard]] QuatRotation getRotation() const { return m_transform.rotation.forcedQuat(); } [[nodiscard]] const WorldTransform& getTransform() const { return m_transform; } @@ -154,7 +152,7 @@ namespace fgl::engine TaitBryan }; - void setView( WorldCoordinate pos, const Rotation& rotation, ViewMode mode = TaitBryan ); + void setView( WorldCoordinate pos, const QuatRotation& rotation, ViewMode mode = TaitBryan ); void setOrthographicProjection( float left, float right, float top, float bottom, float near, float far ); void setPerspectiveProjection( float fovy, float aspect, float near, float far ); diff --git a/src/engine/constants.hpp b/src/engine/constants.hpp index c470127..5fb561f 100644 --- a/src/engine/constants.hpp +++ b/src/engine/constants.hpp @@ -51,4 +51,6 @@ namespace fgl::engine::constants constexpr std::uint8_t MAX_FRAMES_IN_FLIGHT { 2 }; + constexpr glm::vec3 DEFAULT_SCALE { 1.0f }; + } // namespace fgl::engine::constants diff --git a/src/engine/gameobjects/GameObject.hpp b/src/engine/gameobjects/GameObject.hpp index e6ea061..25eb0aa 100644 --- a/src/engine/gameobjects/GameObject.hpp +++ b/src/engine/gameobjects/GameObject.hpp @@ -135,7 +135,7 @@ namespace fgl::engine // const Rotation& getRotation() const { return m_transform.rotation; } - Rotation& getRotation() { return m_transform.rotation; } + QuatRotation getRotation() const { return m_transform.rotation.forcedQuat(); } //Misc static GameObject createGameObject(); diff --git a/src/engine/gameobjects/components/drawers.cpp b/src/engine/gameobjects/components/drawers.cpp index 79a1587..0fdb8cb 100644 --- a/src/engine/gameobjects/components/drawers.cpp +++ b/src/engine/gameobjects/components/drawers.cpp @@ -23,7 +23,14 @@ namespace fgl::engine ImGui::DragFloat3( "Position", &transform.translation.x, speed ); - gui::dragFloat3Rot( "Rotation", transform.rotation ); + if ( transform.rotation.isEuler() ) + { + gui::dragFloat3Rot( "Rotation", transform.rotation.euler() ); + } + else + { + ImGui::Text( "Rotation could not be edited: Was quaternion" ); + } ImGui::DragFloat3( "Scale", &transform.scale.x, speed ); } diff --git a/src/engine/gameobjects/components/interface/GameObjectComponent.hpp b/src/engine/gameobjects/components/interface/GameObjectComponent.hpp index f49dfa0..3ad435a 100644 --- a/src/engine/gameobjects/components/interface/GameObjectComponent.hpp +++ b/src/engine/gameobjects/components/interface/GameObjectComponent.hpp @@ -18,7 +18,7 @@ namespace fgl::engine { enum Mode { - //! Object is non moving, When this is set only the transform on the component should be used. + //! Object is non-moving, When this is set only the transform on the component should be used. Static, //! Object moves in relation to it's parent Local, @@ -28,7 +28,7 @@ namespace fgl::engine ComponentTransform() = default; - ComponentTransform( const WorldCoordinate position_i, const Scale scale_i, const Rotation rotation_i ) : + ComponentTransform( const WorldCoordinate position_i, const Scale scale_i, const QuatRotation rotation_i ) : TransformComponent( position_i, scale_i, rotation_i ) {} }; @@ -50,7 +50,7 @@ namespace fgl::engine struct GameObjectComponent : public GameObjectComponentBase { constexpr static ComponentID ID { T_ID }; - ComponentTransform m_transform { WorldCoordinate( 0.0f ), Scale( 1.0 ), Rotation() }; + ComponentTransform m_transform { WorldCoordinate( 0.0f ), Scale( 1.0 ), QuatRotation() }; virtual ComponentID id() const override final { return ID; } }; diff --git a/src/engine/math/raycast/raycast.hpp b/src/engine/math/raycast/raycast.hpp index 1729859..812dafb 100644 --- a/src/engine/math/raycast/raycast.hpp +++ b/src/engine/math/raycast/raycast.hpp @@ -3,7 +3,7 @@ // #pragma once -#include "engine/primitives/Rotation.hpp" +#include "../../primitives/rotation/QuatRotation.hpp" #include "engine/primitives/boxes/OrientedBoundingBox.hpp" #include "engine/primitives/points/Coordinate.hpp" diff --git a/src/engine/primitives/Rotation.cpp b/src/engine/primitives/Rotation.cpp deleted file mode 100644 index 3a89b9e..0000000 --- a/src/engine/primitives/Rotation.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// -// Created by kj16609 on 2/17/24. -// - -#include "Rotation.hpp" - -#define GLM_ENABLE_EXPERIMENTAL -#include -#include - -#include - -#include "engine/constants.hpp" - -namespace fgl::engine -{ - Rotation::Rotation() : Rotation( 0.0f ) - {} - - glm::quat toQuat( const float roll, const float pitch, const float yaw ) - { - static_assert( 0.0f / 2.0f == 0.0f ); - - const glm::vec3 euler { roll / 2.0f, -pitch / 2.0f, yaw / 2.0f }; - const glm::vec3 sin { glm::sin( euler ) }; - const glm::vec3 cos { glm::cos( euler ) }; - - glm::quat q {}; - q.w = cos.x * cos.y * cos.z + sin.x * sin.y * sin.z; - q.x = sin.x * cos.y * cos.z - cos.x * sin.y * sin.z; - q.y = cos.x * sin.y * cos.z + sin.x * cos.y * sin.z; - q.z = cos.x * cos.y * sin.z - sin.x * sin.y * cos.z; - - /* - const glm::quat q_x { glm::angleAxis( roll, constants::WORLD_X ) }; // Roll - const glm::quat q_y { glm::angleAxis( -pitch, constants::WORLD_Y ) }; // Pitch - // In order to get it so that PITCH+ is UP we must invert the pitch - const glm::quat q_z { glm::angleAxis( yaw, constants::WORLD_Z ) }; // Yaw - - const glm::quat q { q_z * q_y * q_x }; - */ - - return glm::normalize( q ); - } - - Rotation::Rotation( const float x_i, const float y_i, const float z_i ) : glm::quat( toQuat( x_i, y_i, z_i ) ) - {} - - Rotation::Rotation( const float value ) : Rotation( value, value, value ) - {} - - NormalVector Rotation::forward() const - { - return mat() * NormalVector( constants::WORLD_FORWARD ); - } - - NormalVector Rotation::right() const - { - return mat() * NormalVector( constants::WORLD_RIGHT ); - } - - NormalVector Rotation::up() const - { - return mat() * NormalVector( constants::WORLD_UP ); - } - - glm::vec3 Rotation::euler() const - { - return { xAngle(), yAngle(), zAngle() }; - } - - float Rotation::xAngle() const - { - //Extract X angle from quaternion - const float sinr_cosp { 2.0f * ( w * x + y * z ) }; - const float cosr_cosp { 1.0f - 2.0f * ( x * x + y * y ) }; - return std::atan2( sinr_cosp, cosr_cosp ); - } - - float Rotation::yAngle() const - { - // Extract Y angle from quaternion - const float sinp { std::sqrt( 1.0f + 2.0f * ( w * y - x * z ) ) }; - const float cosp { std::sqrt( 1.0f - 2.0f * ( w * y - x * z ) ) }; - - // We must invert the pitch in order to 'fix' it after being flipped in the constructor and add functions - return -( ( 2.0f * std::atan2( sinp, cosp ) ) - ( std::numbers::pi_v< float > / 2.0f ) ); - } - - float Rotation::zAngle() const - { - // Extract Z angle from quaternion - const float siny_cosp { 2.0f * ( w * z + x * y ) }; - const float cosy_cosp { 1.0f - ( 2.0f * ( y * y + z * z ) ) }; - return std::atan2( siny_cosp, cosy_cosp ); - } - - void Rotation::setX( [[maybe_unused]] const float value ) - { - *this = Rotation( value, yAngle(), zAngle() ); - } - - void Rotation::setY( [[maybe_unused]] const float value ) - { - *this = Rotation( xAngle(), value, zAngle() ); - } - - void Rotation::setZ( [[maybe_unused]] const float value ) - { - *this = Rotation( xAngle(), yAngle(), value ); - } - - void Rotation::addX( const float value ) - { - const glm::quat q { glm::angleAxis( value, constants::WORLD_X ) }; - *this = *this * q; - } - - void Rotation::addY( const float value ) - { - // Because the camera is flipped. We must also flip the pitch rotation - const glm::quat q { glm::angleAxis( -value, constants::WORLD_Y ) }; - *this = *this * q; - } - - void Rotation::addZ( const float value ) - { - const glm::quat q { glm::angleAxis( value, constants::WORLD_Z ) }; - *this = *this * q; - } - - void Rotation::addXWorld( const float value ) - { - const glm::quat q { glm::angleAxis( value, constants::WORLD_X ) }; - *this = q * *this; - } - - void Rotation::addYWorld( const float value ) - { - const glm::quat q { glm::angleAxis( value, constants::WORLD_Y ) }; - *this = q * *this; - } - - void Rotation::addZWorld( const float value ) - { - const glm::quat q { glm::angleAxis( value, constants::WORLD_Z ) }; - *this = q * *this; - } - -} // namespace fgl::engine diff --git a/src/engine/primitives/TransformComponent.cpp b/src/engine/primitives/TransformComponent.cpp index 8094cbc..873d57b 100644 --- a/src/engine/primitives/TransformComponent.cpp +++ b/src/engine/primitives/TransformComponent.cpp @@ -10,7 +10,7 @@ namespace fgl::engine template < CoordinateSpace CType > glm::mat4 TransformComponent< CType >::mat4() const { - const glm::mat3 rotation_mat { rotation.mat() }; + const glm::mat3 rotation_mat { rotation.forcedQuat().mat() }; // We must flip the z axis in order to match vulkan. Where 0,0 is the top left of the screen and Z+ is down @@ -47,4 +47,31 @@ namespace fgl::engine template struct TransformComponent< CoordinateSpace::World >; template struct TransformComponent< CoordinateSpace::Model >; + namespace v2 + { + TransformComponent::TransformComponent() : + m_position( 0.0f, 0.0f, 0.0f ), + m_scale( 1.0, 1.0, 1.0 ), + m_rotation() + {} + + Matrix TransformComponent::mat() const + { + glm::mat3 rotation_mat { m_rotation.mat() }; + rotation_mat[ 0 ] = rotation_mat[ 0 ] * m_scale.x; + rotation_mat[ 1 ] = rotation_mat[ 1 ] * m_scale.y; + rotation_mat[ 2 ] = rotation_mat[ 2 ] * m_scale.z; + + const glm::mat4 mat { { rotation_mat[ 0 ], 0.0f }, + { rotation_mat[ 1 ], 0.0f }, + { rotation_mat[ 2 ], 0.0f }, + { m_position.vec(), 1.0f } }; + + const Matrix matrix { mat }; + + return matrix; + } + + } // namespace v2 + } // namespace fgl::engine diff --git a/src/engine/primitives/TransformComponent.hpp b/src/engine/primitives/TransformComponent.hpp index 517f64e..130b4ee 100644 --- a/src/engine/primitives/TransformComponent.hpp +++ b/src/engine/primitives/TransformComponent.hpp @@ -4,14 +4,53 @@ #pragma once -#include "Rotation.hpp" #include "Scale.hpp" #include "engine/FGL_DEFINES.hpp" #include "engine/debug/logging/logging.hpp" #include "engine/primitives/points/Coordinate.hpp" +#include "rotation/QuatRotation.hpp" +#include "rotation/UniversalRotation.hpp" namespace fgl::engine { + namespace v2 + { + struct Coordinate : public glm::vec3 + { + public: + + glm::vec3 vec() const { return static_cast< glm::vec3 >( *this ); } + + Coordinate() = delete; + + Coordinate( const float x, const float y, const float z ) : glm::vec3( x, y, z ) {} + }; + + struct Matrix : public glm::mat4 + { + public: + + Matrix() = delete; + + explicit Matrix( const float value ) : glm::mat4( value ) {} + + explicit Matrix( const glm::mat4& matrix ) : glm::mat4( matrix ) {} + }; + + class TransformComponent + { + Coordinate m_position; + Scale m_scale; + QuatRotation m_rotation; + + public: + + TransformComponent(); + + Matrix mat() const; + }; + } // namespace v2 + template < MatrixType > class Matrix; @@ -40,7 +79,7 @@ namespace fgl::engine { Coordinate< CType > translation { constants::WORLD_CENTER }; Scale scale { 1.0f, 1.0f, 1.0f }; - Rotation rotation { 0.0f, 0.0f, 0.0f }; + UniversalRotation rotation { constants::DEFAULT_ROTATION }; //TODO: Figure this out and replace TransformComponent with a template of CType instead [[nodiscard]] glm::mat4 mat4() const; @@ -128,7 +167,7 @@ namespace fgl::engine // At this point, the rotation matrix is orthogonal (no skew), so it's safe to calculate the quaternion. const glm::quat rotation { glm::quat_cast( glm::mat3( row[ 0 ], row[ 1 ], row[ 2 ] ) ) }; - return { Coordinate< CType >( translation ), Scale( scale ), Rotation( rotation ) }; + return { Coordinate< CType >( translation ), Scale( scale ), QuatRotation( rotation ) }; } // A game object will be going from world to camera space diff --git a/src/engine/primitives/boxes/AxisAlignedBoundingBox.cpp b/src/engine/primitives/boxes/AxisAlignedBoundingBox.cpp index b1edbc1..64914b4 100644 --- a/src/engine/primitives/boxes/AxisAlignedBoundingBox.cpp +++ b/src/engine/primitives/boxes/AxisAlignedBoundingBox.cpp @@ -156,25 +156,15 @@ namespace fgl::engine FGL_NOTNANVEC3( this->m_top_right_forward ); FGL_NOTNANVEC3( this->m_bottom_left_back ); - if ( oobb.m_transform.rotation - == Rotation() ) // If default rotation then we can simply just take it as the box is + for ( const auto& point : oobb.points() ) { - m_top_right_forward = oobb.topRightForward(); - m_bottom_left_back = oobb.bottomLeftBack(); - } - else - { - //Rotation has been done. Need to use the points to figure it out - for ( const auto& point : oobb.points() ) - { - m_top_right_forward.x = std::max( m_top_right_forward.x, point.x ); - m_top_right_forward.y = std::max( m_top_right_forward.y, point.y ); - m_top_right_forward.z = std::max( m_top_right_forward.z, point.z ); + m_top_right_forward.x = std::max( m_top_right_forward.x, point.x ); + m_top_right_forward.y = std::max( m_top_right_forward.y, point.y ); + m_top_right_forward.z = std::max( m_top_right_forward.z, point.z ); - m_bottom_left_back.x = std::min( m_bottom_left_back.x, point.x ); - m_bottom_left_back.y = std::min( m_bottom_left_back.y, point.y ); - m_bottom_left_back.z = std::min( m_bottom_left_back.z, point.z ); - } + m_bottom_left_back.x = std::min( m_bottom_left_back.x, point.x ); + m_bottom_left_back.y = std::min( m_bottom_left_back.y, point.y ); + m_bottom_left_back.z = std::min( m_bottom_left_back.z, point.z ); } } diff --git a/src/engine/primitives/rotation/EulerRotation.cpp b/src/engine/primitives/rotation/EulerRotation.cpp new file mode 100644 index 0000000..33d1c1a --- /dev/null +++ b/src/engine/primitives/rotation/EulerRotation.cpp @@ -0,0 +1,16 @@ +// +// Created by kj16609 on 2/27/25. +// +#include "EulerRotation.hpp" + +namespace fgl::engine +{ + + QuatRotation EulerRotation::toRotation() const + { + const QuatRotation rotation { x, y, z }; + + return rotation; + } + +} // namespace fgl::engine diff --git a/src/engine/primitives/rotation/EulerRotation.hpp b/src/engine/primitives/rotation/EulerRotation.hpp new file mode 100644 index 0000000..f6c4378 --- /dev/null +++ b/src/engine/primitives/rotation/EulerRotation.hpp @@ -0,0 +1,31 @@ +// +// Created by kj16609 on 2/27/25. +// +#pragma once + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Weffc++" +#include "glm/vec3.hpp" +#pragma GCC diagnostic pop + +#include "QuatRotation.hpp" + +namespace fgl::engine +{ + + struct EulerRotation : private glm::vec3 + { + public: + + using glm::vec3::x; + using glm::vec3::y; + using glm::vec3::z; + + glm::vec3& vec() { return *this; } + + QuatRotation toRotation() const; + + EulerRotation( const glm::vec3& rotation ) : glm::vec3( rotation ) {} + }; + +} // namespace fgl::engine \ No newline at end of file diff --git a/src/engine/primitives/rotation/QuatRotation.cpp b/src/engine/primitives/rotation/QuatRotation.cpp new file mode 100644 index 0000000..3fe30da --- /dev/null +++ b/src/engine/primitives/rotation/QuatRotation.cpp @@ -0,0 +1,105 @@ +// +// Created by kj16609 on 2/17/24. +// + +#include "QuatRotation.hpp" + +#define GLM_ENABLE_EXPERIMENTAL +#include +#include + +#include "constants.hpp" + +namespace fgl::engine +{ + + + QuatRotation::QuatRotation() : QuatRotation( 0.0f ) + {} + + glm::quat toQuat( const float roll, const float pitch, const float yaw ) + { + static_assert( 0.0f / 2.0f == 0.0f ); + + const glm::vec3 euler { roll / 2.0f, -pitch / 2.0f, yaw / 2.0f }; + const glm::vec3 sin { glm::sin( euler ) }; + const glm::vec3 cos { glm::cos( euler ) }; + + //TODO: There might be a better way to do this from euler angles. Since this is probably slow? + glm::quat q {}; + q.w = cos.x * cos.y * cos.z + sin.x * sin.y * sin.z; + q.x = sin.x * cos.y * cos.z - cos.x * sin.y * sin.z; + q.y = cos.x * sin.y * cos.z + sin.x * cos.y * sin.z; + q.z = cos.x * cos.y * sin.z - sin.x * sin.y * cos.z; + + /* + const glm::quat q_x { glm::angleAxis( roll, constants::WORLD_X ) }; // Roll + const glm::quat q_y { glm::angleAxis( -pitch, constants::WORLD_Y ) }; // Pitch + // In order to get it so that PITCH+ is UP we must invert the pitch + const glm::quat q_z { glm::angleAxis( yaw, constants::WORLD_Z ) }; // Yaw + + const glm::quat q { q_z * q_y * q_x }; + */ + + return glm::normalize( q ); + } + + QuatRotation::QuatRotation( const float x_i, const float y_i, const float z_i ) : glm::quat( toQuat( x_i, y_i, z_i ) ) + {} + + QuatRotation::QuatRotation( const float value ) : QuatRotation( value, value, value ) + {} + + NormalVector QuatRotation::forward() const + { + return mat() * NormalVector( constants::WORLD_FORWARD ); + } + + NormalVector QuatRotation::right() const + { + return mat() * NormalVector( constants::WORLD_RIGHT ); + } + + NormalVector QuatRotation::up() const + { + return mat() * NormalVector( constants::WORLD_UP ); + } + + void QuatRotation::addX( const float value ) + { + const glm::quat q { glm::angleAxis( value, constants::WORLD_X ) }; + *this = *this * q; + } + + void QuatRotation::addY( const float value ) + { + // Because the camera is flipped. We must also flip the pitch rotation + const glm::quat q { glm::angleAxis( -value, constants::WORLD_Y ) }; + *this = *this * q; + } + + void QuatRotation::addZ( const float value ) + { + const glm::quat q { glm::angleAxis( value, constants::WORLD_Z ) }; + *this = *this * q; + } + + void QuatRotation::addXWorld( const float value ) + { + const glm::quat q { glm::angleAxis( value, constants::WORLD_X ) }; + *this = q * *this; + } + + void QuatRotation::addYWorld( const float value ) + { + const glm::quat q { glm::angleAxis( value, constants::WORLD_Y ) }; + *this = q * *this; + } + + void QuatRotation::addZWorld( const float value ) + { + const glm::quat q { glm::angleAxis( value, constants::WORLD_Z ) }; + *this = q * *this; + } + +} // namespace fgl::engine diff --git a/src/engine/primitives/Rotation.hpp b/src/engine/primitives/rotation/QuatRotation.hpp similarity index 52% rename from src/engine/primitives/Rotation.hpp rename to src/engine/primitives/rotation/QuatRotation.hpp index 4395966..13f09cc 100644 --- a/src/engine/primitives/Rotation.hpp +++ b/src/engine/primitives/rotation/QuatRotation.hpp @@ -7,45 +7,33 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #pragma GCC diagnostic ignored "-Wduplicated-branches" -#include -#include +#include +#include #pragma GCC diagnostic pop #define GLM_ENABLE_EXPERIMENTAL -#include +#include +#include #include "engine/primitives/matricies/RotationMatrix.hpp" #include "engine/primitives/vectors/NormalVector.hpp" namespace fgl::engine { - struct Rotation; + struct QuatRotation; } namespace fgl::engine { - - /* - - TODO: Fix the singularity issue. Upon reaching +90y if x & z are both 0.0f then a singularity is entered and gimal locking occurs. - - The fix for this seems to be detecting the singularity and doing a different formula for quat -> euler converstions. - - There is also an issue with the editor in that it is impossible to reach beyond +90y because of how the math comes out. - The X and Z rapidly swap from +/- - - This swapping effect also hapens on quaternions.online, So it might be that both our implementations have some sort of fix - */ - - struct Rotation : private glm::quat + struct QuatRotation : private glm::quat { - Rotation(); + QuatRotation(); - Rotation( float x, float y, float z ); + QuatRotation( float x, float y, float z ); - Rotation( float value ); + QuatRotation( float value ); - Rotation( const glm::quat& quat ) : glm::quat( quat ) {} + QuatRotation( const glm::quat& quat ) : glm::quat( quat ) {} RotationMatrix mat() const { return { glm::toMat3( *this ) }; } @@ -53,11 +41,6 @@ namespace fgl::engine NormalVector right() const; NormalVector up() const; - glm::vec3 euler() const; - float xAngle() const; - float yAngle() const; - float zAngle() const; - void setX( float ); void setY( float ); void setZ( float ); @@ -75,15 +58,15 @@ namespace fgl::engine // internal inline glm::quat internal_quat() const { return *this; } - bool operator==( const Rotation& rot ) const + bool operator==( const QuatRotation& rot ) const { return static_cast< glm::quat >( *this ) == static_cast< glm::quat >( rot ); } - friend bool operator==( const Rotation&, const glm::quat& ); + friend bool operator==( const QuatRotation&, const glm::quat& ); }; - inline bool operator==( const Rotation& rot, const glm::quat& quat ) + inline bool operator==( const QuatRotation& rot, const glm::quat& quat ) { return static_cast< glm::quat >( rot ) == quat; } diff --git a/src/engine/primitives/rotation/UniversalRotation.cpp b/src/engine/primitives/rotation/UniversalRotation.cpp new file mode 100644 index 0000000..0ff7bd3 --- /dev/null +++ b/src/engine/primitives/rotation/UniversalRotation.cpp @@ -0,0 +1,57 @@ +// +// Created by kj16609 on 2/27/25. +// +#include "UniversalRotation.hpp" + +namespace fgl::engine +{ + + void UniversalRotation::addX( const float value ) + { + if ( isQuat() ) [[likely]] + q_rotation.addX( value ); + else [[unlikely]] + e_rotation.x += value; + } + + void UniversalRotation::addY( const float value ) + { + if ( isQuat() ) [[likely]] + q_rotation.addY( value ); + else [[unlikely]] + e_rotation.y += value; + } + + void UniversalRotation::addZ( const float value ) + { + if ( isQuat() ) [[likely]] + q_rotation.addZ( value ); + else [[unlikely]] + e_rotation.z += value; + } + + void UniversalRotation::addXWorld( const float value ) + { + if ( isQuat() ) [[likely]] + q_rotation.addXWorld( value ); + else [[unlikely]] + e_rotation.x += value; + } + + void UniversalRotation::addYWorld( const float value ) + { + if ( isQuat() ) [[likely]] + q_rotation.addYWorld( value ); + else [[unlikely]] + e_rotation.y += value; + } + + void UniversalRotation::addZWorld( const float value ) + { + if ( isQuat() ) [[likely]] + q_rotation.addZWorld( value ); + else [[unlikely]] + e_rotation.z += value; + } + +} // namespace fgl::engine \ No newline at end of file diff --git a/src/engine/primitives/rotation/UniversalRotation.hpp b/src/engine/primitives/rotation/UniversalRotation.hpp new file mode 100644 index 0000000..937c931 --- /dev/null +++ b/src/engine/primitives/rotation/UniversalRotation.hpp @@ -0,0 +1,87 @@ +// +// Created by kj16609 on 2/27/25. +// +#pragma once +#include "EulerRotation.hpp" +#include "FGL_DEFINES.hpp" +#include "QuatRotation.hpp" + +namespace fgl::engine +{ + class UniversalRotation + { + union + { + QuatRotation q_rotation; + EulerRotation e_rotation { constants::DEFAULT_ROTATION }; + }; + + //! If true then the rotation is in a + bool m_euler { true }; + + public: + + [[nodiscard]] UniversalRotation() : e_rotation( constants::DEFAULT_ROTATION ), m_euler( true ) {} + + [[nodiscard]] UniversalRotation( const EulerRotation& euler ) : e_rotation( euler ) {} + + [[nodiscard]] UniversalRotation( const QuatRotation& q_rotation ) : q_rotation( q_rotation ) {} + + FGL_DEFAULT_MOVE( UniversalRotation ); + FGL_DEFAULT_COPY( UniversalRotation ); + + NormalVector forward() const { return forcedQuat().forward(); } + + NormalVector right() const { return forcedQuat().right(); } + + NormalVector up() const { return forcedQuat().up(); } + + inline bool isEuler() const { return m_euler; } + + inline bool isQuat() const { return !isEuler(); } + + //! Returns a quaternion rotation no matter the original rotation + FGL_HOT [[nodiscard]] QuatRotation forcedQuat() const + { + if ( m_euler ) [[unlikely]] + return e_rotation.toRotation(); + else [[likely]] + return q_rotation; + } + + // Marked cold because it's only usd in the editor + FGL_COLD EulerRotation& euler() + { + FGL_ASSERT( isEuler(), "Rotation is not euler" ); + return e_rotation; + } + + // Marked cold because it's only usd in the editor + FGL_COLD const EulerRotation& euler() const + { + FGL_ASSERT( isEuler(), "Rotation is not euler" ); + return e_rotation; + } + + FGL_HOT QuatRotation& quat() + { + FGL_ASSERT( isQuat(), "Rotation is not quat" ); + return q_rotation; + } + + FGL_HOT const QuatRotation& quat() const + { + FGL_ASSERT( isQuat(), "Rotation is not quat" ); + return q_rotation; + } + + // Universal modification + void addX( float value ); + void addY( float value ); + void addZ( float value ); + + void addXWorld( float value ); + void addYWorld( float value ); + void addZWorld( float value ); + }; +} // namespace fgl::engine \ No newline at end of file diff --git a/src/engine/primitives/vectors/NormalVector.hpp b/src/engine/primitives/vectors/NormalVector.hpp index 191322e..0452c53 100644 --- a/src/engine/primitives/vectors/NormalVector.hpp +++ b/src/engine/primitives/vectors/NormalVector.hpp @@ -10,8 +10,8 @@ #include #pragma GCC diagnostic pop +#include "../rotation/QuatRotation.hpp" #include "engine/primitives/CoordinateSpace.hpp" -#include "engine/primitives/Rotation.hpp" namespace fgl::engine { diff --git a/src/tests/src/gtest_printers.hpp b/src/tests/src/gtest_printers.hpp index 1658518..a1e8342 100644 --- a/src/tests/src/gtest_printers.hpp +++ b/src/tests/src/gtest_printers.hpp @@ -9,7 +9,7 @@ #include #include -#include "engine/primitives/Rotation.hpp" +#include "../../engine/primitives/rotation/QuatRotation.hpp" #include "engine/primitives/Scale.hpp" #include "engine/primitives/matricies/Matrix.hpp" #include "engine/primitives/points/Coordinate.hpp" @@ -32,15 +32,6 @@ namespace Catch } }; - template <> - struct StringMaker< fgl::engine::Rotation > - { - static std::string convert( const fgl::engine::Rotation& rot ) - { - return StringMaker< glm::vec3 >::convert( { rot.xAngle(), rot.yAngle(), rot.zAngle() } ); - } - }; - template <> struct StringMaker< glm::mat4 > { @@ -60,6 +51,24 @@ namespace Catch } }; + template <> + struct StringMaker< fgl::engine::QuatRotation > + { + static std::string convert( const fgl::engine::QuatRotation& rot ) + { + return StringMaker< glm::quat >::convert( rot.internal_quat() ); + } + }; + + template <> + struct StringMaker< fgl::engine::EulerRotation > + { + static std::string convert( const fgl::engine::QuatRotation& rot ) + { + return StringMaker< glm::vec3 >::convert( glm::vec3( rot.x, rot.y, rot.z ) ); + } + }; + template < fgl::engine::MatrixType MType > struct StringMaker< fgl::engine::Matrix< MType > > { diff --git a/src/tests/src/math/quat.cpp b/src/tests/src/math/quat.cpp index 617c119..1ea4f9f 100644 --- a/src/tests/src/math/quat.cpp +++ b/src/tests/src/math/quat.cpp @@ -12,22 +12,7 @@ TEST_CASE( "Quaternions", "[math][rotation]" ) { SECTION( "Default" ) { - fgl::engine::Rotation rot {}; - - SECTION( "X Euler" ) - { - REQUIRE( rot.xAngle() == 0.0f ); - } - - SECTION( "Y Euler" ) - { - REQUIRE( rot.yAngle() == 0.0f ); - } - - SECTION( "Z Euler" ) - { - REQUIRE( rot.zAngle() == 0.0f ); - } + fgl::engine::QuatRotation rot {}; SECTION( "Raw" ) { @@ -43,41 +28,81 @@ TEST_CASE( "Quaternions", "[math][rotation]" ) { SECTION( "-1.126, 1.012, -0.548" ) { - fgl::engine::Rotation rot { -1.256f, 1.012f, -0.548 }; + fgl::engine::QuatRotation rot { -1.256f, 1.012f, -0.548 }; - REQUIRE_THAT( rot.internal_quat().w, Catch::Matchers::WithinRel( 0.604f, 0.1f ) ); - REQUIRE_THAT( rot.internal_quat().x, Catch::Matchers::WithinRel( -0.601f, 0.1f ) ); - REQUIRE_THAT( rot.internal_quat().y, Catch::Matchers::WithinRel( -0.239f, 0.1f ) ); - REQUIRE_THAT( rot.internal_quat().z, Catch::Matchers::WithinRel( -0.466f, 0.1f ) ); + REQUIRE_THAT( rot.internal_quat().w, Catch::Matchers::WithinAbs( 0.604f, 0.1f ) ); + REQUIRE_THAT( rot.internal_quat().x, Catch::Matchers::WithinAbs( -0.601f, 0.1f ) ); + REQUIRE_THAT( rot.internal_quat().y, Catch::Matchers::WithinAbs( -0.239f, 0.1f ) ); + REQUIRE_THAT( rot.internal_quat().z, Catch::Matchers::WithinAbs( -0.466f, 0.1f ) ); } } - SECTION( "Singularity" ) + SECTION( "Test xRange" ) { - SECTION( "North" ) + for ( std::size_t i = 0; i < 360; ++i ) { - fgl::engine::Rotation rot { 0.0f, 90.0f, 0.0f }; - REQUIRE_THAT( rot.euler().x, Catch::Matchers::WithinRel( 0.0f, 0.1f ) ); - REQUIRE_THAT( rot.euler().y, Catch::Matchers::WithinRel( 90.0f, 0.1f ) ); - REQUIRE_THAT( rot.euler().z, Catch::Matchers::WithinRel( 0.0f, 0.1f ) ); + DYNAMIC_SECTION( "X Angle" << i ) + { + constexpr float offset { std::numbers::pi_v< float > / 360.0f }; + fgl::engine::QuatRotation rot1 { static_cast< float >( i ) * offset, 0.0f, 0.0f }; + + fgl::engine::QuatRotation rot2 { 0.0f, 0.0f, 0.0f }; + for ( std::size_t x = 0; x < i; ++x ) rot2.addX( offset ); + + // compare both quaternions + using namespace Catch::Matchers; + + REQUIRE_THAT( rot1.internal_quat().x, WithinAbs( rot2.internal_quat().x, 0.01f ) ); + REQUIRE_THAT( rot1.internal_quat().y, WithinAbs( rot2.internal_quat().y, 0.01f ) ); + REQUIRE_THAT( rot1.internal_quat().z, WithinAbs( rot2.internal_quat().z, 0.01f ) ); + REQUIRE_THAT( rot1.internal_quat().w, WithinAbs( rot2.internal_quat().w, 0.01f ) ); + } } - SECTION( "South" ) + SECTION( "Test yRange" ) { - fgl::engine::Rotation rot { 0.0f, -90.0f, 0.0f }; + for ( std::size_t i = 0; i < 360; ++i ) + { + DYNAMIC_SECTION( "Y Angle: " << i ) + { + constexpr float offset { std::numbers::pi_v< float > / 360.0f }; + fgl::engine::QuatRotation rot1 { 0.0f, static_cast< float >( i ) * offset, 0.0f }; - REQUIRE_THAT( rot.euler().x, Catch::Matchers::WithinRel( 0.0f, 0.1f ) ); - REQUIRE_THAT( rot.euler().y, Catch::Matchers::WithinRel( 90.0f, 0.1f ) ); - REQUIRE_THAT( rot.euler().z, Catch::Matchers::WithinRel( 0.0f, 0.1f ) ); + fgl::engine::QuatRotation rot2 { 0.0f, 0.0f, 0.0f }; + for ( std::size_t x = 0; x < i; ++x ) rot2.addY( offset ); + + // compare both quaternions + using namespace Catch::Matchers; + + REQUIRE_THAT( rot1.internal_quat().x, WithinAbs( rot2.internal_quat().x, 0.01f ) ); + REQUIRE_THAT( rot1.internal_quat().y, WithinAbs( rot2.internal_quat().y, 0.01f ) ); + REQUIRE_THAT( rot1.internal_quat().z, WithinAbs( rot2.internal_quat().z, 0.01f ) ); + REQUIRE_THAT( rot1.internal_quat().w, WithinAbs( rot2.internal_quat().w, 0.01f ) ); + } + } } - } - SECTION( "Beyond Singularity Y" ) - { - fgl::engine::Rotation rot { 0.0f, 1.548f, 0.0f }; + SECTION( "Test zRange" ) + { + for ( std::size_t i = 0; i < 360; ++i ) + { + DYNAMIC_SECTION( "Z Angle" << i ) + { + constexpr float offset { std::numbers::pi_v< float > / 360.0f }; + fgl::engine::QuatRotation rot1 { 0.0f, 0.0f, static_cast< float >( i ) * offset }; - REQUIRE_THAT( rot.euler().x, Catch::Matchers::WithinRel( -3.142f, 0.1f ) ); - REQUIRE_THAT( rot.euler().y, Catch::Matchers::WithinRel( 1.548f, 0.1f ) ); - REQUIRE_THAT( rot.euler().z, Catch::Matchers::WithinRel( -3.142f, 0.1f ) ); + fgl::engine::QuatRotation rot2 { 0.0f, 0.0f, 0.0f }; + for ( std::size_t x = 0; x < i; ++x ) rot2.addZ( offset ); + + // compare both quaternions + using namespace Catch::Matchers; + + REQUIRE_THAT( rot1.internal_quat().x, WithinAbs( rot2.internal_quat().x, 0.01f ) ); + REQUIRE_THAT( rot1.internal_quat().y, WithinAbs( rot2.internal_quat().y, 0.01f ) ); + REQUIRE_THAT( rot1.internal_quat().z, WithinAbs( rot2.internal_quat().z, 0.01f ) ); + REQUIRE_THAT( rot1.internal_quat().w, WithinAbs( rot2.internal_quat().w, 0.01f ) ); + } + } + } } } diff --git a/src/tests/src/math/transform.cpp b/src/tests/src/math/transform.cpp new file mode 100644 index 0000000..cb7143a --- /dev/null +++ b/src/tests/src/math/transform.cpp @@ -0,0 +1,24 @@ +// +// Created by kj16609 on 2/18/25. +// + +#include + +#include "primitives/TransformComponent.hpp" + +TEST_CASE( "Transform", "[transform]" ) +{ + using namespace fgl::engine::v2; + + SECTION( "Default" ) + { + TransformComponent transform {}; + + + + + + + + } +}