From 7444258374c39fbde8a629f3cd44db6ef1377964 Mon Sep 17 00:00:00 2001 From: kj16609 Date: Mon, 19 Feb 2024 16:55:25 -0500 Subject: [PATCH] Fixup some tests and match up rotation for camera and objects --- src/engine/CMakeLists.txt | 2 +- src/engine/Camera.cpp | 213 +------------------ src/engine/Camera.hpp | 25 +-- src/engine/EngineContext.cpp | 2 +- src/engine/GameObject.cpp | 59 ++--- src/engine/KeyboardMovementController.cpp | 16 +- src/engine/constants.hpp | 5 + src/engine/math/taitBryanMatrix.cpp | 198 +++++++++++++++++ src/engine/math/taitBryanMatrix.hpp | 26 +++ src/engine/primitives/Rotation.cpp | 2 +- src/engine/primitives/Rotation.hpp | 14 +- src/engine/primitives/TransformComponent.hpp | 2 - tests/CMakeLists.txt | 1 + tests/src/CameraTesting.cpp | 79 ++++--- tests/src/FrustumTesting.cpp | 23 +- tests/src/TransformTests.cpp | 38 ++-- tests/src/VectorTests.cpp | 23 +- tests/src/gtest_printers.hpp | 20 ++ tests/src/taitBryanMatrixTests.cpp | 35 +++ 19 files changed, 430 insertions(+), 353 deletions(-) create mode 100644 src/engine/math/taitBryanMatrix.cpp create mode 100644 src/engine/math/taitBryanMatrix.hpp create mode 100644 tests/src/taitBryanMatrixTests.cpp diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index febbf31..ed52345 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -33,4 +33,4 @@ else () endif () #GLM settings -target_compile_definitions(FGLEngine PUBLIC GLM_FORCE_RADIANS GLM_FORCE_DEPTH_ZERO_TO_ONE) +target_compile_definitions(FGLEngine PUBLIC GLM_FORCE_RADIANS GLM_FORCE_DEPTH_ZERO_TO_ONE GLM_FORCE_LEFT_HANDED) diff --git a/src/engine/Camera.cpp b/src/engine/Camera.cpp index 10118ce..404f173 100644 --- a/src/engine/Camera.cpp +++ b/src/engine/Camera.cpp @@ -5,6 +5,7 @@ #include "Camera.hpp" #include "GameObject.hpp" +#include "engine/math/taitBryanMatrix.hpp" #define GLM_ENABLE_EXPERIMENTAL #include @@ -45,201 +46,7 @@ namespace fgl::engine setViewDirection( position, Vector( glm::normalize( target - position ) ), up ); } - enum RotationOrder - { - XZY, - XYZ, - YXZ, - YZX, - ZYX, - ZXY, - END_OF_ENUM, - DEFAULT = XYZ - }; - - template < int N > - inline std::tuple< float, float > extract( const Rotation rotation, const RotationOrder order ) - { - switch ( order ) - { - case XZY: - switch ( N ) - { - case 1: - return { glm::cos( rotation.x ), glm::sin( rotation.x ) }; - case 2: - return { glm::cos( rotation.z ), glm::sin( rotation.z ) }; - case 3: - return { glm::cos( rotation.y ), glm::sin( rotation.y ) }; - } - break; - case XYZ: // DEFAULT - switch ( N ) - { - case 1: - return { glm::cos( rotation.x ), glm::sin( rotation.x ) }; - case 2: - return { glm::cos( rotation.y ), glm::sin( rotation.y ) }; - case 3: - return { glm::cos( -rotation.z ), glm::sin( -rotation.z ) }; - } - break; - case YXZ: - switch ( N ) - { - case 1: - return { glm::cos( rotation.y ), glm::sin( rotation.y ) }; - case 2: - return { glm::cos( rotation.x ), glm::sin( rotation.x ) }; - case 3: - return { glm::cos( rotation.z ), glm::sin( rotation.z ) }; - } - break; - case YZX: - switch ( N ) - { - case 1: - return { glm::cos( rotation.y ), glm::sin( rotation.y ) }; - case 2: - return { glm::cos( rotation.z ), glm::sin( rotation.z ) }; - case 3: - return { glm::cos( rotation.x ), glm::sin( rotation.x ) }; - } - break; - case ZYX: - switch ( N ) - { - case 1: - return { glm::cos( rotation.z ), glm::sin( rotation.z ) }; - case 2: - return { glm::cos( rotation.y ), glm::sin( rotation.y ) }; - case 3: - return { glm::cos( rotation.x ), glm::sin( rotation.x ) }; - } - break; - case ZXY: - switch ( N ) - { - case 1: - return { glm::cos( rotation.z ), glm::sin( rotation.z ) }; - case 2: - return { glm::cos( rotation.x ), glm::sin( rotation.x ) }; - case 3: - return { glm::cos( rotation.y ), glm::sin( rotation.y ) }; - } - break; - case END_OF_ENUM: - throw std::runtime_error( "Unimplemented rotation order" ); - } - std::unreachable(); - } - - glm::mat4 taitBryanMatrix( const Rotation rotation, const RotationOrder order = DEFAULT ) - { - glm::mat4 mat { 1.0f }; - - const auto [ c1, s1 ] = extract< 1 >( rotation, order ); - const auto [ c2, s2 ] = extract< 2 >( rotation, order ); - const auto [ c3, s3 ] = extract< 3 >( rotation, order ); - - switch ( order ) - { - case RotationOrder::XZY: - { - const glm::vec3 row_0 { ( c2 * c3 ), -( s2 ), ( c2 * s3 ) }; - - const glm::vec3 row_1 { ( s1 * s3 ) + ( c1 * c3 * s2 ), - ( c1 * c2 ), - ( c1 * s2 * s3 ) - ( c3 * s1 ) }; - - const glm::vec3 row_2 { ( c3 * s1 * s2 ) - ( c1 * s3 ), - ( c2 * s1 ), - ( c1 * c3 ) + ( s1 * s2 * s3 ) }; - - mat[ 0 ] = glm::vec4( row_0, 0.0f ); - mat[ 1 ] = glm::vec4( row_1, 0.0f ); - mat[ 2 ] = glm::vec4( row_2, 0.0f ); - return mat; - } - case RotationOrder::XYZ: - { - const glm::vec3 row_0 { ( c2 * c3 ), -( c2 * s3 ), s2 }; - const glm::vec3 row_1 { ( c1 * s3 ) + ( c3 * s1 * s2 ), - ( c1 * c3 ) - ( s1 * s2 * s3 ), - -( c2 * s1 ) }; - const glm::vec3 row_2 { ( s1 * s3 ) - ( c1 * c3 * s2 ), - ( c3 * s1 ) + ( c1 * s2 * s3 ), - ( c1 * c2 ) }; - - mat[ 0 ] = glm::vec4( row_0, 0.0f ); - mat[ 1 ] = glm::vec4( row_1, 0.0f ); - mat[ 2 ] = glm::vec4( row_2, 0.0f ); - return mat; - } - case RotationOrder::YXZ: - { - const glm::vec3 row_0 { ( c1 * c3 ) + ( s1 * s2 * s3 ), ( c3 * s1 * s2 ) - ( c1 * s3 ), c2 * s1 }; - const glm::vec3 row_1 { c2 * s3, c2 * c3, -s2 }; - const glm::vec3 row_2 { ( c1 * s2 * s3 ) - ( c3 * s1 ), ( c1 * c3 * s2 ) + ( s1 * s3 ), c1 * c2 }; - - mat[ 0 ] = glm::vec4( row_0, 0.0f ); - mat[ 1 ] = glm::vec4( row_1, 0.0f ); - mat[ 2 ] = glm::vec4( row_2, 0.0f ); - return mat; - } - case RotationOrder::YZX: - { - const glm::vec3 row_0 { ( c1 * c2 ), - ( s1 * s3 ) - ( c1 * c3 * s2 ), - ( c3 * s1 ) + ( c1 * s2 * s3 ) }; - const glm::vec3 row_1 { s2, c2 * c3, -( c2 * s3 ) }; - const glm::vec3 row_2 { -( c2 * s1 ), - ( c1 * s3 ) + ( c3 * s1 * s2 ), - ( c1 * c3 ) - ( s1 * s2 * s3 ) }; - - mat[ 0 ] = glm::vec4( row_0, 0.0f ); - mat[ 1 ] = glm::vec4( row_1, 0.0f ); - mat[ 2 ] = glm::vec4( row_2, 0.0f ); - return mat; - } - case RotationOrder::ZYX: // Roll, Pitch, Yaw - { - const glm::vec3 row_0 { ( c1 * c2 ), - ( c1 * s2 * s3 ) - ( c3 * s1 ), - ( s1 * s3 ) + ( c1 * c3 * s2 ) }; - const glm::vec3 row_1 { ( c2 * s1 ), - ( c1 * c3 ) + ( s1 * s2 * s3 ), - ( c3 * s1 * s2 ) - ( c1 * s3 ) }; - const glm::vec3 row_2 { -s2, c2 * s3, c2 * c3 }; - - mat[ 0 ] = glm::vec4( row_0, 0.0f ); - mat[ 1 ] = glm::vec4( row_1, 0.0f ); - mat[ 2 ] = glm::vec4( row_2, 0.0f ); - return mat; - } - case RotationOrder::ZXY: // Roll, Yaw, Pitch - { - const glm::vec3 row_0 { ( c1 * c3 ) - ( s1 * s2 * s3 ), - -( c2 * s1 ), - ( c1 * s3 ) + ( c3 * s1 * s2 ) }; - - const glm::vec3 row_1 { ( c3 * s1 ) + ( c1 * s2 * s3 ), - ( c1 * c2 ), - ( s1 * s3 ) - ( c1 * c3 * s2 ) }; - - const glm::vec3 row_2 { -( c2 * s3 ), ( s2 ), ( c2 * c3 ) }; - - mat[ 0 ] = glm::vec4( row_0, 0.0f ); - mat[ 1 ] = glm::vec4( row_1, 0.0f ); - mat[ 2 ] = glm::vec4( row_2, 0.0f ); - return mat; - } - default: - throw std::runtime_error( "Unimplemented rotation order" ); - } - } - - void Camera::setViewYXZ( glm::vec3 position, const Rotation rotation, const ViewMode mode ) + void Camera::setView( glm::vec3 pos, const Rotation rotation, const ViewMode mode ) { ZoneScoped; @@ -251,16 +58,14 @@ namespace fgl::engine { case ViewMode::TaitBryan: { - static auto current_rotation_order { RotationOrder::DEFAULT }; - - const glm::mat4 rotation_matrix { taitBryanMatrix( rotation, current_rotation_order ) }; + const glm::mat4 rotation_matrix { taitBryanMatrix( rotation ) }; const glm::vec3 forward { rotation_matrix * glm::vec4( constants::WORLD_FORWARD, 0.0f ) }; const glm::vec3 camera_up { rotation_matrix * glm::vec4( constants::WORLD_UP, 0.0f ) }; - view_matrix = - Matrix< MatrixType::WorldToCamera >( glm::lookAtLH( position, position + forward, camera_up ) ); + view_matrix = Matrix< MatrixType::WorldToCamera >( glm::lookAtLH( pos, pos + forward, camera_up ) ); + inverse_view_matrix = glm::inverse( view_matrix ); break; } @@ -273,7 +78,7 @@ namespace fgl::engine throw std::runtime_error( "Unimplemented view mode" ); } - TransformComponent transform { WorldCoordinate( position ), glm::vec3( 1.0f, 1.0f, 1.0f ), rotation }; + TransformComponent transform { WorldCoordinate( pos ), glm::vec3( 1.0f, 1.0f, 1.0f ), rotation }; if ( update_frustums ) { @@ -323,11 +128,7 @@ namespace fgl::engine const Matrix< MatrixType::ModelToWorld > Camera::frustumTranslationMatrix() const { - glm::mat4 translation { 1.0f }; - - translation = glm::translate( glm::mat4( 1.0f ), getPosition() ); - - return Matrix< MatrixType::ModelToWorld >( translation ); + return Matrix< MatrixType::ModelToWorld >( getInverseViewMatrix() ); } WorldCoordinate Camera::getFrustumPosition() const diff --git a/src/engine/Camera.hpp b/src/engine/Camera.hpp index ee14d46..74aa0b0 100644 --- a/src/engine/Camera.hpp +++ b/src/engine/Camera.hpp @@ -32,6 +32,7 @@ namespace fgl::engine Matrix< MatrixType::CameraToScreen > projection_matrix { 1.0f }; Matrix< MatrixType::WorldToCamera > view_matrix { 1.0f }; + glm::mat4 inverse_view_matrix { 1.0f }; //! Frustum of the camera in model space relative to the camera //! @note Must be transformed by the inverse view matrix to get the frustum in world space @@ -52,7 +53,7 @@ namespace fgl::engine Camera() { setPerspectiveProjection( 90.0f, 16.0f / 9.0f, constants::NEAR_PLANE, constants::FAR_PLANE ); - setViewYXZ( constants::CENTER, Rotation( 0.0f, 0.0f, 0.0f ) ); + setView( constants::CENTER, Rotation( 0.0f, 0.0f, 0.0f ) ); } WorldCoordinate getFrustumPosition() const; @@ -71,6 +72,8 @@ namespace fgl::engine return projection_matrix * view_matrix; } + const glm::mat4 getInverseViewMatrix() const { return glm::inverse( view_matrix ); } + void setOrthographicProjection( float left, float right, float top, float bottom, float near, float far ); void setPerspectiveProjection( float fovy, float aspect, float near, float far ); @@ -80,23 +83,11 @@ namespace fgl::engine return WorldCoordinate( glm::inverse( view_matrix )[ 3 ] ); } - const Vector getUp() const - { - return Vector( - glm::normalize( glm::vec3( view_matrix[ 0 ][ 1 ], view_matrix[ 1 ][ 1 ], view_matrix[ 2 ][ 1 ] ) ) ); - } + const Vector getUp() const { return Vector( glm::normalize( glm::vec3( inverse_view_matrix[ 1 ] ) ) ); } - const Vector getRight() const - { - return Vector( - glm::normalize( -glm::vec3( view_matrix[ 0 ][ 0 ], view_matrix[ 1 ][ 0 ], view_matrix[ 2 ][ 0 ] ) ) ); - } + const Vector getRight() const { return -Vector( glm::normalize( glm::vec3( inverse_view_matrix[ 0 ] ) ) ); } - const Vector getForward() const - { - return Vector( - glm::normalize( glm::vec3( view_matrix[ 0 ][ 2 ], view_matrix[ 1 ][ 2 ], view_matrix[ 2 ][ 2 ] ) ) ); - } + const Vector getForward() const { return Vector( glm::normalize( glm::vec3( inverse_view_matrix[ 2 ] ) ) ); } const Vector getLeft() const { return -getRight(); } @@ -113,7 +104,7 @@ namespace fgl::engine TaitBryan }; - void setViewYXZ( glm::vec3 pos, const Rotation rotation, const ViewMode mode = TaitBryan ); + void setView( glm::vec3 pos, const Rotation rotation, const ViewMode mode = TaitBryan ); }; } // namespace fgl::engine diff --git a/src/engine/EngineContext.cpp b/src/engine/EngineContext.cpp index 06b56eb..7797ac3 100644 --- a/src/engine/EngineContext.cpp +++ b/src/engine/EngineContext.cpp @@ -147,7 +147,7 @@ namespace fgl::engine } camera_controller.moveInPlaneXZ( m_window.window(), delta_time, viewer ); - camera.setViewYXZ( viewer.transform.translation, viewer.transform.rotation ); + camera.setView( viewer.transform.translation, viewer.transform.rotation ); { constexpr WorldCoordinate center { 0.0f, 0.0f, 0.0f }; diff --git a/src/engine/GameObject.cpp b/src/engine/GameObject.cpp index 407be1d..7bc8c7b 100644 --- a/src/engine/GameObject.cpp +++ b/src/engine/GameObject.cpp @@ -4,56 +4,29 @@ #include "GameObject.hpp" +#include "engine/math/taitBryanMatrix.hpp" + namespace fgl::engine { glm::mat4 TransformComponent::mat4() const { - const float c3 { glm::cos( -rotation.yaw ) }; // yaw - const float s3 { glm::sin( -rotation.yaw ) }; // yaw - - const float c2 { glm::cos( rotation.pitch ) }; // pitch - const float s2 { glm::sin( rotation.pitch ) }; // pitch - - const float c1 { glm::cos( rotation.roll ) }; // roll - const float s1 { glm::sin( rotation.roll ) }; // roll + const glm::mat4 rotation_mat { taitBryanMatrix( rotation ) }; //TODO: This uses the Tait-Bryan angles stuff, It should use the respective function. // Or maybe something else can be done here - return glm::mat4 { - { scale.x * ( c1 * c3 + s1 * s2 * s3 ), scale.x * ( c2 * s3 ), scale.x * ( c1 * s2 * s3 - c3 * s1 ), 0.0f }, - { scale.y * ( c3 * s1 * s2 - c1 * s3 ), scale.y * ( c2 * c3 ), scale.y * ( c1 * c3 * s2 + s1 * s3 ), 0.0f }, - { scale.z * ( c2 * s1 ), scale.z * ( -s2 ), scale.z * ( c1 * c2 ), 0.0f }, - { translation.x, translation.y, translation.z, 1.0f } - }; - } - - glm::mat3 TransformComponent::normalMatrix() const - { - const float c3 { glm::cos( rotation.z ) }; - const float s3 { glm::sin( rotation.z ) }; - const float c2 { glm::cos( rotation.x ) }; - const float s2 { glm::sin( rotation.x ) }; - const float c1 { glm::cos( rotation.y ) }; - const float s1 { glm::sin( rotation.y ) }; - const glm::vec3 invScale { 1.0f / scale }; - - return glm::mat3 { - { - invScale.x * ( c1 * c3 + s1 * s2 * s3 ), - invScale.x * ( c2 * s3 ), - invScale.x * ( c1 * s2 * s3 - c3 * s1 ), - }, - { - invScale.y * ( c3 * s1 * s2 - c1 * s3 ), - invScale.y * ( c2 * c3 ), - invScale.y * ( c1 * c3 * s2 + s1 * s3 ), - }, - { - invScale.z * ( c2 * s1 ), - invScale.z * ( -s2 ), - invScale.z * ( c1 * c2 ), - }, - }; + return glm::mat4 { { scale.x * rotation_mat[ 0 ][ 0 ], + scale.x * rotation_mat[ 0 ][ 1 ], + scale.x * rotation_mat[ 0 ][ 2 ], + 0.0f }, + { scale.y * rotation_mat[ 1 ][ 0 ], + scale.y * rotation_mat[ 1 ][ 1 ], + scale.y * rotation_mat[ 1 ][ 2 ], + 0.0f }, + { scale.z * rotation_mat[ 2 ][ 0 ], + scale.z * rotation_mat[ 2 ][ 1 ], + scale.z * rotation_mat[ 2 ][ 2 ], + 0.0f }, + { translation.x, translation.y, translation.z, 1.0f } }; } } // namespace fgl::engine \ No newline at end of file diff --git a/src/engine/KeyboardMovementController.cpp b/src/engine/KeyboardMovementController.cpp index c5b2aec..3f6c079 100644 --- a/src/engine/KeyboardMovementController.cpp +++ b/src/engine/KeyboardMovementController.cpp @@ -31,10 +31,10 @@ namespace fgl::engine Rotation rotate { 0.0f }; - if ( glfwGetKey( window, key_mappings.look_right ) == GLFW_PRESS ) rotate.yaw += 1.f; - if ( glfwGetKey( window, key_mappings.look_left ) == GLFW_PRESS ) rotate.yaw -= 1.f; - if ( glfwGetKey( window, key_mappings.look_up ) == GLFW_PRESS ) rotate.pitch += 1.f; - if ( glfwGetKey( window, key_mappings.look_down ) == GLFW_PRESS ) rotate.pitch -= 1.f; + if ( glfwGetKey( window, key_mappings.look_right ) == GLFW_PRESS ) rotate.yaw() += 1.f; + if ( glfwGetKey( window, key_mappings.look_left ) == GLFW_PRESS ) rotate.yaw() -= 1.f; + if ( glfwGetKey( window, key_mappings.look_up ) == GLFW_PRESS ) rotate.pitch() += 1.f; + if ( glfwGetKey( window, key_mappings.look_down ) == GLFW_PRESS ) rotate.pitch() -= 1.f; static bool cursor_enabled { true }; static bool cursor_restored { false }; @@ -77,9 +77,9 @@ namespace fgl::engine const auto xpos { pos.x }; const auto ypos { pos.y }; - target.transform.rotation.yaw += + target.transform.rotation.yaw() += static_cast< float >( ( xpos * 0.006 ) * static_cast< double >( look_speed ) ); - target.transform.rotation.pitch -= + target.transform.rotation.pitch() -= static_cast< float >( ( ypos * 0.006 ) * static_cast< double >( look_speed ) ); setCursorPos( window, { 0, 0 } ); @@ -90,8 +90,8 @@ namespace fgl::engine > std::numeric_limits< float >::epsilon() ) target.transform.rotation += look_speed * dt * glm::normalize( rotate ); - target.transform.rotation.pitch = glm::clamp( target.transform.rotation.pitch, -1.5f, 1.5f ); - target.transform.rotation.yaw = glm::mod( target.transform.rotation.yaw, glm::two_pi< float >() ); + target.transform.rotation.pitch() = glm::clamp( target.transform.rotation.pitch(), -1.5f, 1.5f ); + target.transform.rotation.yaw() = glm::mod( target.transform.rotation.yaw(), glm::two_pi< float >() ); } const glm::vec3 forward_dir { target.transform.rotation.forward() }; diff --git a/src/engine/constants.hpp b/src/engine/constants.hpp index d34cfc7..bd55c55 100644 --- a/src/engine/constants.hpp +++ b/src/engine/constants.hpp @@ -4,6 +4,7 @@ #pragma once +#include #include namespace fgl::engine::constants @@ -33,4 +34,8 @@ namespace fgl::engine::constants constexpr auto EPSILON { std::numeric_limits< float >::epsilon() * 2 }; + constexpr auto FRUSTUM_ORIGIN { constants::WORLD_CENTER }; + + constexpr glm::mat4 MAT4_IDENTITY { 1.0f }; + } // namespace fgl::engine::constants diff --git a/src/engine/math/taitBryanMatrix.cpp b/src/engine/math/taitBryanMatrix.cpp new file mode 100644 index 0000000..5a91831 --- /dev/null +++ b/src/engine/math/taitBryanMatrix.cpp @@ -0,0 +1,198 @@ +// +// Created by kj16609 on 2/19/24. +// + +#include "taitBryanMatrix.hpp" + +#include + +#include + +#include "engine/primitives/Rotation.hpp" + +namespace fgl::engine +{ + + template < int N > + inline std::tuple< float, float > extract( const Rotation rotation, const RotationOrder order ) + { + switch ( order ) + { + case XZY: + switch ( N ) + { + case 1: + return { glm::cos( rotation.x ), glm::sin( rotation.x ) }; + case 2: + return { glm::cos( rotation.z ), glm::sin( rotation.z ) }; + case 3: + return { glm::cos( rotation.y ), glm::sin( rotation.y ) }; + } + break; + case XYZ: // DEFAULT + switch ( N ) + { + case 1: + return { glm::cos( rotation.x ), glm::sin( rotation.x ) }; + case 2: + return { glm::cos( rotation.y ), glm::sin( rotation.y ) }; + case 3: + return { glm::cos( -rotation.z ), glm::sin( -rotation.z ) }; + } + break; + case YXZ: + switch ( N ) + { + case 1: + return { glm::cos( rotation.y ), glm::sin( rotation.y ) }; + case 2: + return { glm::cos( rotation.x ), glm::sin( rotation.x ) }; + case 3: + return { glm::cos( rotation.z ), glm::sin( rotation.z ) }; + } + break; + case YZX: + switch ( N ) + { + case 1: + return { glm::cos( rotation.y ), glm::sin( rotation.y ) }; + case 2: + return { glm::cos( rotation.z ), glm::sin( rotation.z ) }; + case 3: + return { glm::cos( rotation.x ), glm::sin( rotation.x ) }; + } + break; + case ZYX: + switch ( N ) + { + case 1: + return { glm::cos( rotation.z ), glm::sin( rotation.z ) }; + case 2: + return { glm::cos( rotation.y ), glm::sin( rotation.y ) }; + case 3: + return { glm::cos( rotation.x ), glm::sin( rotation.x ) }; + } + break; + case ZXY: + switch ( N ) + { + case 1: + return { glm::cos( rotation.z ), glm::sin( rotation.z ) }; + case 2: + return { glm::cos( rotation.x ), glm::sin( rotation.x ) }; + case 3: + return { glm::cos( rotation.y ), glm::sin( rotation.y ) }; + } + break; + case END_OF_ENUM: + throw std::runtime_error( "Unimplemented rotation order" ); + } + std::unreachable(); + } + + glm::mat4 taitBryanMatrix( const Rotation rotation, const RotationOrder order ) + { + glm::mat4 mat { 1.0f }; + + const auto [ c1, s1 ] = extract< 1 >( rotation, order ); + const auto [ c2, s2 ] = extract< 2 >( rotation, order ); + const auto [ c3, s3 ] = extract< 3 >( rotation, order ); + + switch ( order ) + { + case RotationOrder::XZY: + { + const glm::vec3 row_0 { ( c2 * c3 ), -( s2 ), ( c2 * s3 ) }; + + const glm::vec3 row_1 { ( s1 * s3 ) + ( c1 * c3 * s2 ), + ( c1 * c2 ), + ( c1 * s2 * s3 ) - ( c3 * s1 ) }; + + const glm::vec3 row_2 { ( c3 * s1 * s2 ) - ( c1 * s3 ), + ( c2 * s1 ), + ( c1 * c3 ) + ( s1 * s2 * s3 ) }; + + mat[ 0 ] = glm::vec4( row_0, 0.0f ); + mat[ 1 ] = glm::vec4( row_1, 0.0f ); + mat[ 2 ] = glm::vec4( row_2, 0.0f ); + return mat; + } + case RotationOrder::XYZ: + { + const glm::vec3 row_0 { ( c2 * c3 ), -( c2 * s3 ), s2 }; + const glm::vec3 row_1 { ( c1 * s3 ) + ( c3 * s1 * s2 ), + ( c1 * c3 ) - ( s1 * s2 * s3 ), + -( c2 * s1 ) }; + const glm::vec3 row_2 { ( s1 * s3 ) - ( c1 * c3 * s2 ), + ( c3 * s1 ) + ( c1 * s2 * s3 ), + ( c1 * c2 ) }; + + mat[ 0 ] = glm::vec4( row_0, 0.0f ); + mat[ 1 ] = glm::vec4( row_1, 0.0f ); + mat[ 2 ] = glm::vec4( row_2, 0.0f ); + return mat; + } + case RotationOrder::YXZ: + { + const glm::vec3 row_0 { ( c1 * c3 ) + ( s1 * s2 * s3 ), ( c3 * s1 * s2 ) - ( c1 * s3 ), c2 * s1 }; + const glm::vec3 row_1 { c2 * s3, c2 * c3, -s2 }; + const glm::vec3 row_2 { ( c1 * s2 * s3 ) - ( c3 * s1 ), ( c1 * c3 * s2 ) + ( s1 * s3 ), c1 * c2 }; + + mat[ 0 ] = glm::vec4( row_0, 0.0f ); + mat[ 1 ] = glm::vec4( row_1, 0.0f ); + mat[ 2 ] = glm::vec4( row_2, 0.0f ); + return mat; + } + case RotationOrder::YZX: + { + const glm::vec3 row_0 { ( c1 * c2 ), + ( s1 * s3 ) - ( c1 * c3 * s2 ), + ( c3 * s1 ) + ( c1 * s2 * s3 ) }; + const glm::vec3 row_1 { s2, c2 * c3, -( c2 * s3 ) }; + const glm::vec3 row_2 { -( c2 * s1 ), + ( c1 * s3 ) + ( c3 * s1 * s2 ), + ( c1 * c3 ) - ( s1 * s2 * s3 ) }; + + mat[ 0 ] = glm::vec4( row_0, 0.0f ); + mat[ 1 ] = glm::vec4( row_1, 0.0f ); + mat[ 2 ] = glm::vec4( row_2, 0.0f ); + return mat; + } + case RotationOrder::ZYX: // Roll, Pitch, Yaw + { + const glm::vec3 row_0 { ( c1 * c2 ), + ( c1 * s2 * s3 ) - ( c3 * s1 ), + ( s1 * s3 ) + ( c1 * c3 * s2 ) }; + const glm::vec3 row_1 { ( c2 * s1 ), + ( c1 * c3 ) + ( s1 * s2 * s3 ), + ( c3 * s1 * s2 ) - ( c1 * s3 ) }; + const glm::vec3 row_2 { -s2, c2 * s3, c2 * c3 }; + + mat[ 0 ] = glm::vec4( row_0, 0.0f ); + mat[ 1 ] = glm::vec4( row_1, 0.0f ); + mat[ 2 ] = glm::vec4( row_2, 0.0f ); + return mat; + } + case RotationOrder::ZXY: // Roll, Yaw, Pitch + { + const glm::vec3 row_0 { ( c1 * c3 ) - ( s1 * s2 * s3 ), + -( c2 * s1 ), + ( c1 * s3 ) + ( c3 * s1 * s2 ) }; + + const glm::vec3 row_1 { ( c3 * s1 ) + ( c1 * s2 * s3 ), + ( c1 * c2 ), + ( s1 * s3 ) - ( c1 * c3 * s2 ) }; + + const glm::vec3 row_2 { -( c2 * s3 ), ( s2 ), ( c2 * c3 ) }; + + mat[ 0 ] = glm::vec4( row_0, 0.0f ); + mat[ 1 ] = glm::vec4( row_1, 0.0f ); + mat[ 2 ] = glm::vec4( row_2, 0.0f ); + return mat; + } + default: + throw std::runtime_error( "Unimplemented rotation order" ); + } + } + +} // namespace fgl::engine diff --git a/src/engine/math/taitBryanMatrix.hpp b/src/engine/math/taitBryanMatrix.hpp new file mode 100644 index 0000000..ff29774 --- /dev/null +++ b/src/engine/math/taitBryanMatrix.hpp @@ -0,0 +1,26 @@ +// +// Created by kj16609 on 2/19/24. +// + +#pragma once + +#include + +namespace fgl::engine +{ + class Rotation; + + enum RotationOrder + { + XZY, + XYZ, + YXZ, + YZX, + ZYX, + ZXY, + END_OF_ENUM, + DEFAULT = ZXY + }; + + glm::mat4 taitBryanMatrix( const Rotation rotation, const RotationOrder order = DEFAULT ); +} // namespace fgl::engine diff --git a/src/engine/primitives/Rotation.cpp b/src/engine/primitives/Rotation.cpp index 61e6ae7..9559cea 100644 --- a/src/engine/primitives/Rotation.cpp +++ b/src/engine/primitives/Rotation.cpp @@ -32,7 +32,7 @@ namespace fgl::engine glm::vec3 Rotation::forward() const { //TODO: Figure out how to do this with Z axis bullshit - return glm::vec3 { glm::sin( yaw ), glm::cos( yaw ), 0.0f }; + return glm::vec3 { glm::sin( yaw() ), glm::cos( yaw() ), 0.0f }; } glm::vec3 Rotation::backwards() const diff --git a/src/engine/primitives/Rotation.hpp b/src/engine/primitives/Rotation.hpp index 833c112..2e60364 100644 --- a/src/engine/primitives/Rotation.hpp +++ b/src/engine/primitives/Rotation.hpp @@ -10,9 +10,17 @@ namespace fgl::engine { struct Rotation : public glm::vec3 { - float& pitch { x }; - float& roll { y }; - float& yaw { z }; + float& pitch() { return x; } + + float pitch() const { return x; } + + float& roll() { return y; } + + float roll() const { return z; } + + float& yaw() { return z; } + + float yaw() const { return z; } Rotation(); diff --git a/src/engine/primitives/TransformComponent.hpp b/src/engine/primitives/TransformComponent.hpp index cd72a1d..44ca463 100644 --- a/src/engine/primitives/TransformComponent.hpp +++ b/src/engine/primitives/TransformComponent.hpp @@ -23,8 +23,6 @@ namespace fgl::engine glm::mat4 mat4() const; inline Matrix< MatrixType::ModelToWorld > mat() const { return Matrix< MatrixType::ModelToWorld >( mat4() ); } - - glm::mat3 normalMatrix() const; }; } // namespace fgl::engine diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a745465..dfb2a3d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,6 +6,7 @@ file(GLOB_RECURSE FGL_TEST_SOURCES "*.cpp") add_executable(FGLTests ${FGL_TEST_SOURCES}) target_link_libraries(FGLTests FGLEngine Catch2::Catch2WithMain) +target_compile_definitions(FGLTests PUBLIC GLM_FORCE_RADIANS GLM_FORCE_DEPTH_ZERO_TO_ONE GLM_FORCE_LEFT_HANDED) include(CTest) include(Catch) diff --git a/tests/src/CameraTesting.cpp b/tests/src/CameraTesting.cpp index c782cd0..e91a47c 100644 --- a/tests/src/CameraTesting.cpp +++ b/tests/src/CameraTesting.cpp @@ -4,6 +4,8 @@ #include +#include + #define EXPOSE_CAMERA_INTERNAL #include "engine/Camera.hpp" #include "gtest_printers.hpp" @@ -26,41 +28,48 @@ TEST_CASE( "Camera", "[camera]" ) camera.setPerspectiveProjection( 90.0f, 1.0f, constants::NEAR_PLANE, constants::FAR_PLANE ); - SECTION( "Default orientation" ) + WHEN( "Camera is it's default orientation" ) { - const auto camera_up { camera.getUp() }; - REQUIRE( camera_up == constants::WORLD_UP ); + camera.setView( constants::WORLD_CENTER, Rotation( 0.0f ) ); - const auto camera_forward { camera.getForward() }; - REQUIRE( camera_forward == constants::WORLD_FORWARD ); + THEN( "Camera up is WORLD_UP" ) + { + const auto camera_up { camera.getUp() }; + REQUIRE( camera_up == constants::WORLD_UP ); + } - const auto camera_right { camera.getRight() }; - REQUIRE( camera_right == constants::WORLD_RIGHT ); + THEN( "Camera right is WORLD_RIGHT" ) + { + const auto camera_right { camera.getRight() }; + REQUIRE( camera_right == constants::WORLD_RIGHT ); + } + + THEN( "Camera forward is WORLD_FORWARD" ) + { + const auto camera_forward { camera.getForward() }; + REQUIRE( camera_forward == constants::WORLD_FORWARD ); + } } SECTION( "Rotations" ) { Rotation rotation_vec { 0.0f }; - SECTION( "Yaw+ (Right)" ) + WHEN( "Camera is rotated +90 Yaw" ) { - rotation_vec.yaw = glm::radians( 90.0f ); - camera.setViewYXZ( constants::WORLD_CENTER, rotation_vec ); + rotation_vec.yaw() = glm::radians( 90.0f ); + camera.setView( constants::WORLD_CENTER, rotation_vec ); const auto camera_forward { camera.getForward() }; - REQUIRE( camera_forward.x == constants::WORLD_LEFT.x ); - REQUIRE( camera_forward.y <= std::numeric_limits< float >::epsilon() ); - REQUIRE( camera_forward.z <= std::numeric_limits< float >::epsilon() ); + REQUIRE( camera_forward == constants::WORLD_RIGHT ); } - SECTION( "Yaw- (Left)" ) + WHEN( "Camera is rotated -90 Yaw" ) { - rotation_vec.yaw = glm::radians( -90.0f ); - camera.setViewYXZ( constants::WORLD_CENTER, rotation_vec ); + rotation_vec.yaw() = glm::radians( -90.0f ); + camera.setView( constants::WORLD_CENTER, rotation_vec ); const auto camera_forward { camera.getForward() }; - REQUIRE( camera_forward.x == constants::WORLD_RIGHT.x ); - REQUIRE( camera_forward.y <= std::numeric_limits< float >::epsilon() ); - REQUIRE( camera_forward.z <= std::numeric_limits< float >::epsilon() ); + REQUIRE( camera_forward == constants::WORLD_LEFT ); } } @@ -74,7 +83,7 @@ TEST_CASE( "Camera", "[camera]" ) const Rotation rotation { x_gen, y_gen, z_gen }; - camera.setViewYXZ( constants::WORLD_RIGHT, rotation ); + camera.setView( constants::WORLD_RIGHT, rotation ); THEN( "Camera translation should not change" ) { @@ -85,7 +94,7 @@ TEST_CASE( "Camera", "[camera]" ) WHEN( "Camera is translated right by WORLD_RIGHT" ) { - camera.setViewYXZ( constants::WORLD_CENTER + constants::WORLD_RIGHT, Rotation( 0.0f ) ); + camera.setView( constants::WORLD_CENTER + constants::WORLD_RIGHT, Rotation( 0.0f ) ); THEN( "camera.getPosition() should be WORLD_RIGHT" ) { @@ -95,17 +104,29 @@ TEST_CASE( "Camera", "[camera]" ) THEN( "A point at the origin should be translated to the left" ) { - const auto matrix { camera.getProjectionViewMatrix() }; - const auto point { matrix * glm::vec4( constants::WORLD_CENTER, 1.0f ) }; + const glm::mat4 combined_matrix { camera.getProjectionViewMatrix() }; - REQUIRE( point.x < 0.0f ); - REQUIRE( point.y == 0.0f ); + const glm::vec3 point { constants::WORLD_CENTER }; + + const glm::vec3 translated_point { combined_matrix * glm::vec4( point, 1.0f ) }; + + const auto projection_matrix { static_cast< glm::mat4 >( camera.getProjectionMatrix() ) }; + const auto view_matrix { static_cast< glm::mat4 >( camera.getViewMatrix() ) }; + + CAPTURE( point ); + CAPTURE( projection_matrix ); + CAPTURE( view_matrix ); + CAPTURE( combined_matrix ); + CAPTURE( translated_point ); + + REQUIRE( translated_point.x < 0.0f ); + REQUIRE( translated_point.y == 0.0f ); } } WHEN( "Camera is translated up by WORLD_UP" ) { - camera.setViewYXZ( constants::WORLD_CENTER + constants::WORLD_UP, Rotation( 0.0f ) ); + camera.setView( constants::WORLD_CENTER + constants::WORLD_UP, Rotation( 0.0f ) ); THEN( "camera.getPosition() should be WORLD_UP" ) { @@ -116,7 +137,9 @@ TEST_CASE( "Camera", "[camera]" ) THEN( "A point at the origin should be translated down" ) { const auto matrix { camera.getProjectionViewMatrix() }; - const auto point { matrix * glm::vec4( constants::WORLD_CENTER, 1.0f ) }; + const glm::vec3 point { matrix * glm::vec4( constants::WORLD_CENTER, 1.0f ) }; + + CAPTURE( point ); REQUIRE( point.x == 0.0f ); REQUIRE( point.y < 0.0f ); @@ -125,7 +148,7 @@ TEST_CASE( "Camera", "[camera]" ) WHEN( "Camera is translated forward by WORLD_FORWARD" ) { - camera.setViewYXZ( constants::WORLD_CENTER + constants::WORLD_FORWARD, Rotation( 0.0f ) ); + camera.setView( constants::WORLD_CENTER + constants::WORLD_FORWARD, Rotation( 0.0f ) ); THEN( "camera.getPosition() should be WORLD_FORWARD" ) { diff --git a/tests/src/FrustumTesting.cpp b/tests/src/FrustumTesting.cpp index 9d66250..a6e8140 100644 --- a/tests/src/FrustumTesting.cpp +++ b/tests/src/FrustumTesting.cpp @@ -32,7 +32,7 @@ TEST_CASE( "Frustum", "[frustum][rotation][translation]" ) { WHEN( "Translated backwards" ) { - camera.setViewYXZ( constants::WORLD_CENTER - constants::WORLD_FORWARD, Rotation( 0.0f, 0.0f, 0.0f ) ); + camera.setView( constants::WORLD_CENTER - constants::WORLD_FORWARD, Rotation( 0.0f, 0.0f, 0.0f ) ); //Translate backwards by 1 world unit const auto translated_backwards { camera.getFrustumBounds() }; @@ -68,7 +68,7 @@ TEST_CASE( "Frustum", "[frustum][rotation][translation]" ) WHEN( "Translated Forward" ) { - camera.setViewYXZ( constants::WORLD_CENTER + constants::WORLD_FORWARD, Rotation( 0.0f, 0.0f, 0.0f ) ); + camera.setView( constants::WORLD_CENTER + constants::WORLD_FORWARD, Rotation( 0.0f, 0.0f, 0.0f ) ); //Translate forward by 1 world unit const auto translated_forward { camera.getFrustumBounds() }; @@ -99,7 +99,7 @@ TEST_CASE( "Frustum", "[frustum][rotation][translation]" ) WHEN( "Translated Up" ) { - camera.setViewYXZ( constants::WORLD_CENTER + constants::WORLD_UP, Rotation( 0.0f, 0.0f, 0.0f ) ); + camera.setView( constants::WORLD_CENTER + constants::WORLD_UP, Rotation( 0.0f, 0.0f, 0.0f ) ); //Translate up by 1 world unit const auto translated_up { camera.getFrustumBounds() }; @@ -199,9 +199,9 @@ TEST_CASE( "Frustum", "[frustum][rotation][translation]" ) SECTION( "Pitch" ) { Rotation rotation { 0.0f, 0.0f, 0.0f }; - rotation.pitch -= glm::radians( 90.0f ); + rotation.pitch() -= glm::radians( 90.0f ); - camera.setViewYXZ( constants::CENTER, rotation ); + camera.setView( constants::CENTER, rotation ); const auto rotated_frustum = camera.getFrustumBounds(); @@ -211,9 +211,8 @@ TEST_CASE( "Frustum", "[frustum][rotation][translation]" ) //NEAR should be looking down or approaching //FAR should be looking up or away - //TODO: Rewrite these equals to allow for some mild precission errors - //REQUIRE( rotated_frustum.near.direction(), constants::WORLD_DOWN ); - //REQUIRE( rotated_frustum.far.direction(), constants::WORLD_UP ); + REQUIRE( rotated_frustum.near.direction() == constants::WORLD_DOWN ); + REQUIRE( rotated_frustum.far.direction() == constants::WORLD_UP ); REQUIRE( rotated_frustum.near.distanceFrom( point ) > 0.0f ); REQUIRE( rotated_frustum.far.distanceFrom( point ) > 0.0f ); @@ -230,9 +229,9 @@ TEST_CASE( "Frustum", "[frustum][rotation][translation]" ) SECTION( "Yaw" ) { Rotation rotation { 0.0f, 0.0f, 0.0f }; - rotation.yaw += glm::radians( 90.0f ); + rotation.yaw() += glm::radians( 90.0f ); - camera.setViewYXZ( constants::CENTER, rotation ); + camera.setView( constants::CENTER, rotation ); const auto rotated_frustum = camera.getFrustumBounds(); @@ -262,9 +261,9 @@ TEST_CASE( "Frustum", "[frustum][rotation][translation]" ) SECTION( "Roll" ) { Rotation rotation { 0.0f, 0.0f, 0.0f }; - rotation.roll -= glm::radians( 90.0f ); + rotation.roll() -= glm::radians( 90.0f ); - camera.setViewYXZ( constants::CENTER, rotation ); + camera.setView( constants::CENTER, rotation ); const auto rotated_frustum = camera.getFrustumBounds(); diff --git a/tests/src/TransformTests.cpp b/tests/src/TransformTests.cpp index d02549a..398fff3 100644 --- a/tests/src/TransformTests.cpp +++ b/tests/src/TransformTests.cpp @@ -4,6 +4,7 @@ #include +#include "engine/math/taitBryanMatrix.hpp" #include "engine/primitives/TransformComponent.hpp" #include "gtest_printers.hpp" @@ -17,17 +18,28 @@ TEST_CASE( "Transform", "[transform][rotation][translation]" ) component.scale = glm::vec3( 1.0f ); component.rotation = Rotation( 0.0f ); - REQUIRE( component.mat4() == glm::mat4( 1.0f ) ); - constexpr auto TEST_POINT { constants::WORLD_FORWARD }; - SECTION( "Rotation" ) + GIVEN( "A default rotation" ) { + THEN( "The rotation is (0,0,0)" ) + { + REQUIRE( component.rotation.pitch() == 0.0f ); + REQUIRE( component.rotation.yaw() == 0.0f ); + REQUIRE( component.rotation.roll() == 0.0f ); + } + + THEN( "The rotation matrix is the identity matrix" ) + { + REQUIRE( constants::MAT4_IDENTITY == taitBryanMatrix( component.rotation ) ); + REQUIRE( constants::MAT4_IDENTITY == component.mat4() ); + } + // Tests behaviour that a point from WORLD_FORWARD should end up WORLD_UP when pitched 90 degrees WHEN( "Rotated +90 Pitch" ) { //Rotate by pitch - component.rotation.pitch = glm::radians( 90.0f ); + component.rotation.pitch() = glm::radians( 90.0f ); const glm::vec3 rotated_point { component.mat4() * glm::vec4( TEST_POINT, 1.0f ) }; @@ -40,7 +52,7 @@ TEST_CASE( "Transform", "[transform][rotation][translation]" ) // Tests behaviour that a point from WORLD_FORWARD should end up WORLD_DOWN when pitched -90 degrees WHEN( "Rotated -90 Pitch" ) { - component.rotation.pitch = -glm::radians( 90.0f ); + component.rotation.pitch() = -glm::radians( 90.0f ); const glm::vec3 rotated_point { component.mat4() * glm::vec4( TEST_POINT, 1.0f ) }; @@ -53,7 +65,7 @@ TEST_CASE( "Transform", "[transform][rotation][translation]" ) // Tests behaviour that a point from WORLD_FORWARD should end up WORLD_RIGHT when yawed 90 degrees WHEN( "Rotated +90 Yaw" ) { - component.rotation.yaw = glm::radians( 90.0f ); + component.rotation.yaw() = glm::radians( 90.0f ); const glm::vec3 rotated_point { component.mat4() * glm::vec4( TEST_POINT, 1.0f ) }; @@ -66,7 +78,7 @@ TEST_CASE( "Transform", "[transform][rotation][translation]" ) // Tests behaviour that a point from WORLD_FORWARD should end up WORLD_LEFT when yawed -90 degrees WHEN( "Rotated -90 Yaw" ) { - component.rotation.yaw = -glm::radians( 90.0f ); + component.rotation.yaw() = -glm::radians( 90.0f ); const glm::vec3 rotated_point { component.mat4() * glm::vec4( TEST_POINT, 1.0f ) }; @@ -80,7 +92,7 @@ TEST_CASE( "Transform", "[transform][rotation][translation]" ) //This behaviour assumes that WORLD_RIGHT is 90 deg YAW+ from WORLD_FORWARD WHEN( "Rotated +90 Roll" ) { - component.rotation.roll = glm::radians( 90.0f ); + component.rotation.roll() = glm::radians( 90.0f ); const glm::vec3 rotated_point { component.mat4() * glm::vec4( constants::WORLD_RIGHT, 1.0f ) }; @@ -93,7 +105,7 @@ TEST_CASE( "Transform", "[transform][rotation][translation]" ) //Tests behaviour that a point from WORLD_RIGHT should end up WORLD_UP when rolled -90 degrees WHEN( "Rotated -90 Roll" ) { - component.rotation.roll = -glm::radians( 90.0f ); + component.rotation.roll() = -glm::radians( 90.0f ); const glm::vec3 rotated_point { component.mat4() * glm::vec4( constants::WORLD_RIGHT, 1.0f ) }; @@ -106,7 +118,7 @@ TEST_CASE( "Transform", "[transform][rotation][translation]" ) SECTION( "Translation" ) { - WHEN( "Translated X+" ) + WHEN( "Translated Z+" ) { component.translation.up() += 1.0f; @@ -130,7 +142,7 @@ TEST_CASE( "Transform", "[transform][rotation][translation]" ) } } - WHEN( "Translated Z+" ) + WHEN( "Translated X+" ) { component.translation.right() += 1.0f; @@ -147,7 +159,7 @@ TEST_CASE( "Transform", "[transform][rotation][translation]" ) { WHEN( "Translated X+1 and Rotated Y+90" ) { - component.rotation.yaw = glm::radians( 90.0f ); + component.rotation.yaw() = glm::radians( 90.0f ); component.translation.right() += 1.0f; const glm::vec3 translated_point { component.mat4() * glm::vec4( constants::WORLD_FORWARD, 1.0f ) }; @@ -160,7 +172,7 @@ TEST_CASE( "Transform", "[transform][rotation][translation]" ) SECTION( "Translated X+1 Yaw-90" ) { - component.rotation.yaw = glm::radians( -90.0f ); + component.rotation.yaw() = glm::radians( -90.0f ); component.translation.right() += 1.0f; const glm::vec3 translated_point { component.mat4() * glm::vec4( TEST_POINT, 1.0f ) }; diff --git a/tests/src/VectorTests.cpp b/tests/src/VectorTests.cpp index dae036f..afba260 100644 --- a/tests/src/VectorTests.cpp +++ b/tests/src/VectorTests.cpp @@ -10,19 +10,6 @@ using namespace fgl::engine; -namespace Catch -{ - template <> - struct StringMaker< Rotation > - { - static std::string convert( const Rotation& value ) - { - return StringMaker< glm::vec3 >::convert( static_cast< glm::vec3 >( value ) ); - } - }; - -} // namespace Catch - TEST_CASE( "Vector", "[vector][transforms]" ) { WHEN( "Vector is default constructed" ) @@ -48,7 +35,7 @@ TEST_CASE( "Vector", "[vector][transforms]" ) //Rotate by 90 degrees on yaw TransformComponent transform; - transform.rotation.yaw += glm::radians( 90.0f ); + transform.rotation.yaw() += glm::radians( 90.0f ); const Vector value { transform.mat() * rotation_vec }; @@ -64,7 +51,7 @@ TEST_CASE( "Vector", "[vector][transforms]" ) //Rotate by 90 degrees on yaw TransformComponent transform; - transform.rotation.yaw += glm::radians( -90.0f ); + transform.rotation.yaw() += glm::radians( -90.0f ); const Vector value { transform.mat() * rotation_vec }; @@ -82,15 +69,15 @@ TEST_CASE( "Rotation", "[vector][transforms]" ) Rotation rot {}; THEN( "Yaw should be 0.0f" ) { - REQUIRE( rot.yaw == 0.0f ); + REQUIRE( rot.yaw() == 0.0f ); } THEN( "Pitch should be 0.0f" ) { - REQUIRE( rot.pitch == 0.0f ); + REQUIRE( rot.pitch() == 0.0f ); } THEN( "Roll should be 0.0f" ) { - REQUIRE( rot.roll == 0.0f ); + REQUIRE( rot.roll() == 0.0f ); } THEN( "Forward should be WORLD_FORWARD" ) diff --git a/tests/src/gtest_printers.hpp b/tests/src/gtest_printers.hpp index af9a6e1..e3ecbda 100644 --- a/tests/src/gtest_printers.hpp +++ b/tests/src/gtest_printers.hpp @@ -9,6 +9,7 @@ #include #include +#include "engine/primitives/Rotation.hpp" #include "engine/primitives/Vector.hpp" namespace Catch @@ -28,6 +29,25 @@ namespace Catch } }; + template <> + struct StringMaker< fgl::engine::Rotation > + { + static std::string convert( const fgl::engine::Rotation& rot ) + { + return StringMaker< glm::vec3 >::convert( static_cast< glm::vec3 >( rot ) ); + } + }; + + template <> + struct StringMaker< glm::mat4 > + { + static std::string convert( const glm::mat4& mat ) + { + return "\n\t" + glm::to_string( mat[ 0 ] ) + "\n\t" + glm::to_string( mat[ 1 ] ) + "\n\t" + + glm::to_string( mat[ 2 ] ) + "\n\t" + glm::to_string( mat[ 3 ] ); + } + }; + } // namespace Catch #ifndef NDEBUG diff --git a/tests/src/taitBryanMatrixTests.cpp b/tests/src/taitBryanMatrixTests.cpp new file mode 100644 index 0000000..be5f3e9 --- /dev/null +++ b/tests/src/taitBryanMatrixTests.cpp @@ -0,0 +1,35 @@ +// +// Created by kj16609 on 2/19/24. +// + +#include + +#include "engine/Camera.hpp" +#include "engine/math/taitBryanMatrix.hpp" +#include "gtest_printers.hpp" + +using namespace fgl::engine; + +TEST_CASE( "Tait-Bryan rotations", "[matrix][rotation][camera]" ) +{ + WHEN( "Rotation is default constructed" ) + { + THEN( "The rotation is (0,0,0)" ) + { + Rotation rotation; + REQUIRE( rotation.pitch() == 0.0f ); + REQUIRE( rotation.yaw() == 0.0f ); + REQUIRE( rotation.roll() == 0.0f ); + } + } + + WHEN( "Given a default rotation (0,0,0)" ) + { + Rotation rot; + + THEN( "The rotation matrix is the identity matrix" ) + { + REQUIRE( constants::MAT4_IDENTITY == taitBryanMatrix( rot ) ); + } + } +}