diff --git a/src/editor/src/components/ModelComponent.cpp b/src/editor/src/components/ModelComponent.cpp index c7f2889..39d61d4 100644 --- a/src/editor/src/components/ModelComponent.cpp +++ b/src/editor/src/components/ModelComponent.cpp @@ -11,13 +11,10 @@ namespace fgl::engine { -#ifdef TITOR_EDITOR void ModelComponent::drawImGui() { drawComponentTransform( m_transform ); - //ImGui::Text( "MODEL COMPONENT WOOOOOO" ); - // TODO: If the model is not set then we should be able to set it to one from the file selection if ( this->m_model == nullptr ) { @@ -30,13 +27,17 @@ namespace fgl::engine ImGui::Text( "%i primitives", model.m_primitives.size() ); } - std::string_view ModelComponent::name() const + std::string_view ModelComponent::humanName() const { if ( !m_model ) return "Empty"; return m_model->getName(); } -#endif + + std::string_view ModelComponent::className() const + { + return "ModelComponent"; + } Model* ModelComponent::operator->() { diff --git a/src/editor/src/gui/EditorGuiContext.cpp b/src/editor/src/gui/EditorGuiContext.cpp index 81029ae..d464bad 100644 --- a/src/editor/src/gui/EditorGuiContext.cpp +++ b/src/editor/src/gui/EditorGuiContext.cpp @@ -98,10 +98,12 @@ namespace fgl::editor { ZoneScoped; auto timer = debug::timing::push( "Draw ImGui" ); - ImGui::ShowDemoWindow(); + // ImGui::ShowDemoWindow(); gui::drawDock(); + gui::drawMenubar( info ); + gui::drawCameraOutputs( info ); gui::drawEntityGUI( info ); gui::drawEntityInfo( info ); diff --git a/src/editor/src/gui/FileBrowser.cpp b/src/editor/src/gui/FileBrowser.cpp index baf1ff6..7818c7c 100644 --- a/src/editor/src/gui/FileBrowser.cpp +++ b/src/editor/src/gui/FileBrowser.cpp @@ -222,7 +222,7 @@ namespace fgl::engine::filesystem ImGui::PopID(); } - FileBrowser::FileBrowser() + FileBrowser::FileBrowser() : m_current_dir( std::make_unique< DirInfo >( TEST_PATH ) ) { m_folder_texture = getTextureStore().load( "./assets/folder.png", vk::Format::eR8G8B8A8Unorm ); m_file_texture = getTextureStore().load( "./assets/file.png", vk::Format::eR8G8B8A8Unorm ); diff --git a/src/editor/src/gui/core.hpp b/src/editor/src/gui/core.hpp index dab0fd3..59fc51f 100644 --- a/src/editor/src/gui/core.hpp +++ b/src/editor/src/gui/core.hpp @@ -20,6 +20,8 @@ namespace fgl::engine::gui void drawDock(); + void drawMenubar( FrameInfo& info ); + void drawImGui( FrameInfo& ); void drawEntityGUI( FrameInfo& ); diff --git a/src/editor/src/gui/drawGameObject.cpp b/src/editor/src/gui/drawGameObject.cpp index 1b3ccdd..792a70a 100644 --- a/src/editor/src/gui/drawGameObject.cpp +++ b/src/editor/src/gui/drawGameObject.cpp @@ -26,14 +26,27 @@ namespace fgl::engine::gui void drawComponentsList( GameObject& game_object ) { + ImGui::PushStyleVar( ImGuiStyleVar_ChildRounding, 5.0f ); + ImGui::BeginChild( + "ComponentsList", + ImVec2( 0, 0 ), + ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY ); + ImGui::SeparatorText( "Components" ); - for ( const GameObjectComponentPtr component : game_object.getComponents() ) + const auto& components { game_object.getComponents() }; + + for ( const GameObjectComponentPtr component : components ) { component->drawNode( SELECTED_COMPONENT ); } - if ( SELECTED_COMPONENT != nullptr ) + ImGui::EndChild(); + ImGui::PopStyleVar(); + + ImGui::BeginChild( "Selected", ImVec2( 0, 0 ), ImGuiChildFlags_Border ); + + if ( SELECTED_COMPONENT != nullptr && std::ranges::find( components, SELECTED_COMPONENT ) != components.end() ) { ImGui::SeparatorText( "Selected Component" ); @@ -41,6 +54,12 @@ namespace fgl::engine::gui SELECTED_COMPONENT->drawImGui(); ImGui::PopID(); } + else + { + ImGui::SeparatorText( "No component selected" ); + } + + ImGui::EndChild(); } } // namespace fgl::engine::gui \ No newline at end of file diff --git a/src/editor/src/gui/helpers.cpp b/src/editor/src/gui/helpers.cpp new file mode 100644 index 0000000..f009f8a --- /dev/null +++ b/src/editor/src/gui/helpers.cpp @@ -0,0 +1,67 @@ +// +// Created by kj16609 on 1/30/25. +// + +#include "helpers.hpp" + +#include "primitives/Rotation.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; + } + return value; + } + + void dragFloat3Rot( const char* const label, Rotation& rot ) + { + enum Axis + { + Pitch = 0, + Yaw = 1, + Roll = 2 + }; + + const glm::vec3 c_dat { rot.euler() }; + glm::vec3 dat { glm::degrees( c_dat ) }; + + ImGui::DragFloat3( label, &dat.x, 1.0f ); + + dat = glm::radians( dat ); + + const glm::vec3 diff { c_dat - dat }; + constexpr float epsilon { std::numeric_limits< float >::epsilon() }; + + const glm::vec< 3, bool > changed_high { glm::greaterThanEqual( diff, glm::vec3( epsilon ) ) }; + const glm::vec< 3, bool > changed_low { glm::lessThanEqual( diff, glm::vec3( -epsilon ) ) }; + const glm::vec< 3, bool > changed { changed_high || changed_low }; + + if ( changed[ Pitch ] ) + { + dat[ Pitch ] = clampEuler( dat[ Pitch ] ); + rot.setX( dat[ Pitch ] ); + } + + if ( changed[ Roll ] ) + { + dat[ Roll ] = clampEuler( dat[ Roll ] ); + rot.setZ( dat[ Roll ] ); + } + + if ( changed[ Yaw ] ) + { + dat[ Yaw ] = clampEuler( dat[ Yaw ] ); + rot.setY( dat[ Yaw ] ); + } + } +} // namespace fgl::engine::gui diff --git a/src/editor/src/gui/helpers.hpp b/src/editor/src/gui/helpers.hpp index 81a6cf6..4a5a499 100644 --- a/src/editor/src/gui/helpers.hpp +++ b/src/editor/src/gui/helpers.hpp @@ -4,8 +4,14 @@ #pragma once +#include "glm/vec3.hpp" #include "safe_include.hpp" +namespace fgl::engine +{ + struct Rotation; +} + namespace fgl::engine::gui { inline void dragFloat3( const char* const label, glm::vec3& vec ) @@ -13,49 +19,6 @@ namespace fgl::engine::gui ImGui::DragFloat3( label, &vec.x ); } - inline void dragFloat3Rot( const char* const label, Rotation& rot ) - { - enum Axis - { - Pitch = 0, - Yaw = 1, - Roll = 2 - }; - - glm::vec3 dat { glm::degrees( rot.euler() ) }; - const glm::vec3 c_dat { dat }; - - constexpr float speed { 1.0f }; - - assert( &dat.x + 1 == &dat.y ); - assert( &dat.y + 1 == &dat.z ); - - ImGui::DragFloat3( label, &dat.x, speed ); - - const glm::vec3 diff { c_dat - dat }; - constexpr float epsilon { std::numeric_limits< float >::epsilon() }; - - const glm::vec< 3, bool > changed_high { glm::greaterThanEqual( diff, glm::vec3( epsilon ) ) }; - const glm::vec< 3, bool > changed_low { glm::lessThanEqual( diff, glm::vec3( -epsilon ) ) }; - const glm::vec< 3, bool > changed { changed_high || changed_low }; - - // Convert back to radians - dat = glm::radians( dat ); - - if ( changed[ Pitch ] ) - { - //TODO: rot.xAngle() = dat[ Pitch ]; - } - - if ( changed[ Roll ] ) - { - //TODO: rot.zAngle() = dat[ Roll ]; - } - - if ( changed[ Yaw ] ) - { - //TODO: rot.yAngle() = dat[ Yaw ]; - } - } + void dragFloat3Rot( const char* label, Rotation& rot ); } // namespace fgl::engine::gui diff --git a/src/editor/src/gui/menubar.cpp b/src/editor/src/gui/menubar.cpp new file mode 100644 index 0000000..2f8d83d --- /dev/null +++ b/src/editor/src/gui/menubar.cpp @@ -0,0 +1,31 @@ +// +// Created by kj16609 on 1/29/25. +// + +#include + +#include "FrameInfo.hpp" + +namespace fgl::engine::gui +{ + + void drawMenubar( FrameInfo& info ) + { + ImGui::BeginMainMenuBar(); + + if ( ImGui::BeginMenu( "File" ) ) + { + if ( ImGui::MenuItem( "Save..." ) ) + { + + + + } + + ImGui::EndMenu(); + } + + ImGui::EndMainMenuBar(); + } + +} // namespace fgl::engine::gui diff --git a/src/engine/EngineContext.cpp b/src/engine/EngineContext.cpp index f2891f1..7de97e1 100644 --- a/src/engine/EngineContext.cpp +++ b/src/engine/EngineContext.cpp @@ -52,7 +52,7 @@ namespace fgl::engine m_vertex_buffer->setDebugName( "Vertex buffer" ); m_index_buffer->setDebugName( "Index buffer" ); - constexpr float offset { 8.0f }; + constexpr float offset { 4.0f }; constexpr std::size_t grid_size { 6 }; constexpr float factor_offset { 1.0f / static_cast< float >( grid_size ) }; @@ -87,8 +87,8 @@ namespace fgl::engine } obj.getTransform().translation = WorldCoordinate( - 10.0f + ( static_cast< float >( x ) * offset ), - 10.0f + ( static_cast< float >( y ) * offset ), + -5.0f + ( static_cast< float >( x ) * offset ), + -5.0f + ( static_cast< float >( y ) * offset ), 0.0f ); m_game_objects_root.addGameObject( std::move( obj ) ); diff --git a/src/engine/assets/image/ImageHandle.cpp b/src/engine/assets/image/ImageHandle.cpp index 6aff1d7..80653b5 100644 --- a/src/engine/assets/image/ImageHandle.cpp +++ b/src/engine/assets/image/ImageHandle.cpp @@ -95,7 +95,6 @@ namespace fgl::engine { vk::DebugUtilsObjectNameInfoEXT info {}; - log::debug( "Setting name of image to {}", str ); m_name = str; info.objectType = vk::ObjectType::eImage; diff --git a/src/engine/gameobjects/GameObject.hpp b/src/engine/gameobjects/GameObject.hpp index 5596bbb..644a88f 100644 --- a/src/engine/gameobjects/GameObject.hpp +++ b/src/engine/gameobjects/GameObject.hpp @@ -72,7 +72,6 @@ namespace fgl::engine Scale& getScale() { return m_transform.scale; } - template < typename T > requires is_component< T > bool hasComponent() const diff --git a/src/engine/gameobjects/components/ModelComponent.hpp b/src/engine/gameobjects/components/ModelComponent.hpp index cc99f4e..859dc6e 100644 --- a/src/engine/gameobjects/components/ModelComponent.hpp +++ b/src/engine/gameobjects/components/ModelComponent.hpp @@ -23,7 +23,8 @@ namespace fgl::engine void drawImGui() override; - std::string_view name() const override; + std::string_view humanName() const override; + std::string_view className() const override; virtual ~ModelComponent() override {} diff --git a/src/engine/gameobjects/components/drawers.cpp b/src/engine/gameobjects/components/drawers.cpp index de62759..79a1587 100644 --- a/src/engine/gameobjects/components/drawers.cpp +++ b/src/engine/gameobjects/components/drawers.cpp @@ -8,54 +8,13 @@ #pragma GCC diagnostic ignored "-Weffc++" #pragma GCC diagnostic ignored "-Wold-style-cast" #include + +#include "editor/src/gui/helpers.hpp" #pragma GCC diagnostic pop namespace fgl::engine { - void inputRotation( const char* label, Rotation& rot, const float speed ) - { - enum Axis - { - Pitch = 0, - Yaw = 1, - Roll = 2 - }; - - glm::vec3 dat { glm::degrees( rot.euler() ) }; - const glm::vec3 c_dat { dat }; - - assert( &dat.x + 1 == &dat.y ); - assert( &dat.y + 1 == &dat.z ); - - ImGui::DragFloat3( label, &dat.x, speed ); - - const glm::vec3 diff { c_dat - dat }; - constexpr float epsilon { std::numeric_limits< float >::epsilon() }; - - const glm::vec< 3, bool > changed_high { glm::greaterThanEqual( diff, glm::vec3( epsilon ) ) }; - const glm::vec< 3, bool > changed_low { glm::lessThanEqual( diff, glm::vec3( -epsilon ) ) }; - const glm::vec< 3, bool > changed { changed_high || changed_low }; - - // Convert back to radians - dat = glm::radians( dat ); - - if ( changed[ Pitch ] ) - { - rot.setX( dat[ Pitch ] ); - } - - if ( changed[ Roll ] ) - { - rot.setZ( dat[ Roll ] ); - } - - if ( changed[ Yaw ] ) - { - rot.setY( dat[ Yaw ] ); - } - } - void drawComponentTransform( ComponentTransform& transform ) { if ( ImGui::CollapsingHeader( "Transform" ) ) @@ -64,7 +23,7 @@ namespace fgl::engine ImGui::DragFloat3( "Position", &transform.translation.x, speed ); - inputRotation( "Rotation", transform.rotation, speed ); + gui::dragFloat3Rot( "Rotation", transform.rotation ); ImGui::DragFloat3( "Scale", &transform.scale.x, speed ); } diff --git a/src/engine/gameobjects/components/interface/ComponentEditorInterface.hpp b/src/engine/gameobjects/components/interface/ComponentEditorInterface.hpp index 97648c1..082c505 100644 --- a/src/engine/gameobjects/components/interface/ComponentEditorInterface.hpp +++ b/src/engine/gameobjects/components/interface/ComponentEditorInterface.hpp @@ -4,6 +4,7 @@ #pragma once +#include #include namespace fgl::engine @@ -12,7 +13,12 @@ namespace fgl::engine struct ComponentEditorInterface { virtual void drawImGui() = 0; - virtual std::string_view name() const = 0; + + [[nodiscard]] virtual std::string_view humanName() const = 0; + [[nodiscard]] virtual std::string_view className() const = 0; + + [[nodiscard]] std::string name() const { return std::format( "{} ({})", humanName(), className() ); } + virtual ~ComponentEditorInterface() = default; }; diff --git a/src/engine/gameobjects/components/interface/GameObjectComponent.cpp b/src/engine/gameobjects/components/interface/GameObjectComponent.cpp index d1eeeb9..e43d94e 100644 --- a/src/engine/gameobjects/components/interface/GameObjectComponent.cpp +++ b/src/engine/gameobjects/components/interface/GameObjectComponent.cpp @@ -17,7 +17,7 @@ namespace fgl::engine void GameObjectComponentBase::drawNode( GameObjectComponentPtr& selected_out ) { ImGui::Indent( INDENT_AMOUNT ); - if ( ImGui::Selectable( this->name().data() ) ) + if ( ImGui::Selectable( this->name().data(), selected_out == this ) ) { selected_out = this; } diff --git a/src/engine/gameobjects/components/interface/GameObjectComponent.hpp b/src/engine/gameobjects/components/interface/GameObjectComponent.hpp index ec8d696..f49dfa0 100644 --- a/src/engine/gameobjects/components/interface/GameObjectComponent.hpp +++ b/src/engine/gameobjects/components/interface/GameObjectComponent.hpp @@ -58,9 +58,7 @@ namespace fgl::engine template < typename T > concept is_component = requires( T t ) { std::is_base_of_v< T, ComponentEngineInterface >; - { - t.ID - } -> std::same_as< const ComponentEngineInterface::ComponentID& >; + { t.ID } -> std::same_as< const ComponentEngineInterface::ComponentID& >; }; } // namespace fgl::engine \ No newline at end of file diff --git a/src/engine/memory/buffers/BufferSuballocation.cpp b/src/engine/memory/buffers/BufferSuballocation.cpp index e3f4741..02c6745 100644 --- a/src/engine/memory/buffers/BufferSuballocation.cpp +++ b/src/engine/memory/buffers/BufferSuballocation.cpp @@ -105,8 +105,6 @@ namespace fgl::engine::memory BufferSuballocation::~BufferSuballocation() { - if ( m_handle.use_count() > 1 ) [[unlikely]] - log::debug( "Destroyed buffer suballocation with {} use counts", m_handle.use_count() ); } SuballocationView BufferSuballocation::view( const vk::DeviceSize offset, const vk::DeviceSize size ) const diff --git a/src/engine/primitives/Rotation.cpp b/src/engine/primitives/Rotation.cpp index 53bbb7d..44b5239 100644 --- a/src/engine/primitives/Rotation.cpp +++ b/src/engine/primitives/Rotation.cpp @@ -8,8 +8,6 @@ #include #include -#include - #include "engine/FGL_DEFINES.hpp" #include "engine/constants.hpp" @@ -20,8 +18,9 @@ namespace fgl::engine glm::quat toQuat( const float roll, const float pitch, const float yaw ) { - /* - const glm::vec3 euler { x / 2.0f, y / 2.0f, z / 2.0f }; + 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 ) }; @@ -30,16 +29,17 @@ namespace fgl::engine 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 q; + 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 ) ) @@ -83,30 +83,30 @@ namespace fgl::engine 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 ); + 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 ) }; + 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 ) { - FGL_UNIMPLEMENTED(); + *this = Rotation( value, yAngle(), zAngle() ); } void Rotation::setY( [[maybe_unused]] const float value ) { - FGL_UNIMPLEMENTED(); + *this = Rotation( xAngle(), value, zAngle() ); } void Rotation::setZ( [[maybe_unused]] const float value ) { - FGL_UNIMPLEMENTED(); + *this = Rotation( xAngle(), yAngle(), value ); } void Rotation::addX( const float value ) diff --git a/src/engine/primitives/TransformComponent.hpp b/src/engine/primitives/TransformComponent.hpp index 76eb3d6..517f64e 100644 --- a/src/engine/primitives/TransformComponent.hpp +++ b/src/engine/primitives/TransformComponent.hpp @@ -43,21 +43,21 @@ namespace fgl::engine Rotation rotation { 0.0f, 0.0f, 0.0f }; //TODO: Figure this out and replace TransformComponent with a template of CType instead - glm::mat4 mat4() const; + [[nodiscard]] glm::mat4 mat4() const; - Matrix< MatrixTransformType< CType >() > mat() const; + [[nodiscard]] Matrix< MatrixTransformType< CType >() > mat() const; - NormalVector forward() const; + [[nodiscard]] NormalVector forward() const; - NormalVector backwards() const { return -forward(); } + [[nodiscard]] NormalVector backwards() const { return -forward(); } - NormalVector right() const; + [[nodiscard]] NormalVector right() const; - NormalVector left() const { return -right(); } + [[nodiscard]] NormalVector left() const { return -right(); } - NormalVector up() const; + [[nodiscard]] NormalVector up() const; - NormalVector down() const { return -up(); } + [[nodiscard]] NormalVector down() const { return -up(); } }; template < CoordinateSpace CType, MatrixType MType > @@ -65,9 +65,9 @@ namespace fgl::engine { glm::mat4 localMatrix = matrix; - [[maybe_unused]] glm::vec4 perspective; - glm::vec3 scale; - glm::vec3 skew; + [[maybe_unused]] glm::vec4 perspective {}; + glm::vec3 scale {}; + glm::vec3 skew {}; // Normalize the matrix if ( glm::epsilonEqual( localMatrix[ 3 ][ 3 ], 0.0f, glm::epsilon< float >() ) ) @@ -97,7 +97,7 @@ namespace fgl::engine localMatrix[ 3 ] = glm::vec4( 0, 0, 0, localMatrix[ 3 ].w ); // Extract scale and skew - glm::vec3 row[ 3 ]; + std::array< glm::vec3, 3 > row {}; for ( int i = 0; i < 3; i++ ) row[ i ] = glm::vec3( localMatrix[ i ] ); // Compute X scale and normalize first row diff --git a/src/engine/primitives/boxes/AxisAlignedBoundingBox.cpp b/src/engine/primitives/boxes/AxisAlignedBoundingBox.cpp index 8de5704..b1edbc1 100644 --- a/src/engine/primitives/boxes/AxisAlignedBoundingBox.cpp +++ b/src/engine/primitives/boxes/AxisAlignedBoundingBox.cpp @@ -117,8 +117,8 @@ namespace fgl::engine } template < CoordinateSpace CType > - AxisAlignedBoundingBox< CType >& AxisAlignedBoundingBox< CType >::combine( const OrientedBoundingBox< CType >& - other ) + AxisAlignedBoundingBox< CType >& AxisAlignedBoundingBox< + CType >::combine( const OrientedBoundingBox< CType >& other ) { const auto other_trf { other.topRightForward() }; const auto other_blb { other.bottomLeftBack() }; diff --git a/src/engine/tree/octtree/OctTreeNode.hpp b/src/engine/tree/octtree/OctTreeNode.hpp index 906abb8..93fa49d 100644 --- a/src/engine/tree/octtree/OctTreeNode.hpp +++ b/src/engine/tree/octtree/OctTreeNode.hpp @@ -90,7 +90,7 @@ namespace fgl::engine //! returns true if this node should contain the given object bool canContain( const GameObject& obj ) const; - bool canContain( const WorldCoordinate& obj ) const; + bool canContain( const WorldCoordinate& coord ) const; GameObject extract( GameObject::GameObjectID id ); diff --git a/src/tests/src/BoundingBoxTest.cpp b/src/tests/src/BoundingBoxTest.cpp deleted file mode 100644 index 2f0bf33..0000000 --- a/src/tests/src/BoundingBoxTest.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// -// Created by kj16609 on 2/14/24. -// - -#include - -#include "engine/assets/model/ModelVertex.hpp" -#include "engine/primitives/boxes/OrientedBoundingBox.hpp" -#include "gtest_printers.hpp" - -using namespace fgl::engine; - -ModelVertex createTestVert( const float x, const float y, const float z ) -{ - ModelVertex vert {}; - vert.m_position = { x, y, z }; - - return vert; -} - -TEST_CASE( "BoundingBox", "[boundingbox]" ) -{ - SECTION( "Combine test" ) - { - std::vector< ModelVertex > model_points {}; - - // Top left - model_points.push_back( createTestVert( 1.0f, 1.0f, 1.0f ) ); - - // Bottom right - model_points.push_back( createTestVert( -1.0f, -1.0f, -1.0f ) ); - - OrientedBoundingBox< CoordinateSpace::Model > model_box( generateBoundingFromVerts( model_points ) ); - - THEN( "The bounding box center must be at 0,0,0" ) - { - REQUIRE( model_box.center().vec() == glm::vec3( 0.0f, 0.0f, 0.0f ) ); - } - - model_points.clear(); - - // Top left - model_points.push_back( createTestVert( 2.0f, 1.0f, 2.0f ) ); - - // Bottom right - model_points.push_back( createTestVert( -2.0f, -1.0f, -2.0f ) ); - - OrientedBoundingBox< CoordinateSpace::Model > model_box2( generateBoundingFromVerts( model_points ) ); - - auto combined_box = model_box.combine( model_box2 ); - - //Check that the points are correct - //The middle point should not change - REQUIRE( combined_box.center() == model_box.center() ); - //The scale should be the max of the two boxes - REQUIRE( combined_box.m_transform.scale == glm::vec3( 2.0f, 1.0f, 2.0f ) ); - - //Check that the points are correct - - const auto out_points { combined_box.points() }; - const auto box2_points { model_box2.points() }; - REQUIRE( out_points.size() == box2_points.size() ); - REQUIRE( out_points.size() == 8 ); - - for ( std::uint32_t i = 0; i < out_points.size(); ++i ) - { - REQUIRE( out_points[ i ] == box2_points[ i ] ); - } - } - -#ifdef FGL_ENABLE_BENCHMARKS - BENCHMARK( "Combine bounding box" ) - { - auto generatePoints = []() - { - std::vector< Coordinate< CoordinateSpace::Model > > model_points {}; - for ( int i = 0; i < 1024; ++i ) - { - model_points - .push_back( Coordinate< CoordinateSpace::Model >( rand() % 256, rand() % 256, rand() % 256 ) ); - } - return model_points; - }; - - BoundingBox< CoordinateSpace::Model > model_box( generateBoundingFromPoints( generatePoints() ) ); - - auto lowest_point { model_box.bottomLeftBack() }; - auto highest_point { model_box.topRightForward() }; - - for ( int i = 0; i < 64; ++i ) - { - const auto points { generatePoints() }; - const auto box { generateBoundingFromPoints( points ) }; - - model_box = model_box.combine( box ); - - //Check each point and set it to the lowest_point if it is lower - for ( const auto& point : points ) - { - lowest_point.x = std::min( lowest_point.x, point.x ); - lowest_point.y = std::min( lowest_point.y, point.y ); - lowest_point.z = std::min( lowest_point.z, point.z ); - highest_point.x = std::max( highest_point.x, point.x ); - highest_point.y = std::max( highest_point.y, point.y ); - highest_point.z = std::max( highest_point.z, point.z ); - } - } - - return model_box; - }; -#endif -} diff --git a/src/tests/src/CameraTesting.cpp.disabled b/src/tests/src/CameraTesting.cpp.disabled deleted file mode 100644 index 95de5da..0000000 --- a/src/tests/src/CameraTesting.cpp.disabled +++ /dev/null @@ -1,313 +0,0 @@ -// -// Created by kj16609 on 2/15/24. -// - -#include - -#include - -#define EXPOSE_CAMERA_INTERNAL -#include "engine/camera/Camera.hpp" -#include "gtest_printers.hpp" - -using namespace fgl::engine; - -TEST_CASE( "Camera", "[camera]" ) -{ - Camera camera; - - SECTION( "Perspective set" ) - { - camera.setPerspectiveProjection( 90.0f, 1.0f, constants::NEAR_PLANE, constants::FAR_PLANE ); - } - - SECTION( "Orthographic set" ) - { - camera.setOrthographicProjection( 1.0f, 1.0f, 1.0f, 1.0f, constants::NEAR_PLANE, constants::FAR_PLANE ); - } - - camera.setPerspectiveProjection( 90.0f, 1.0f, constants::NEAR_PLANE, constants::FAR_PLANE ); - - WHEN( "Camera is it's default orientation" ) - { - camera.setView( WorldCoordinate( constants::WORLD_CENTER ), Rotation( 0.0f ) ); - - CAPTURE( camera.view_matrix ); - - THEN( "Camera up is WORLD_UP" ) - { - const auto camera_up { camera.getUp() }; - REQUIRE( camera_up == constants::WORLD_UP ); - } - - 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 }; - WHEN( "Camera is rotated +90 Yaw" ) - { - rotation_vec.yaw() = glm::radians( 90.0f ); - camera.setView( constants::WORLD_CENTER, rotation_vec ); - const auto camera_forward { camera.getForward() }; - - REQUIRE( camera_forward == constants::WORLD_RIGHT ); - } - - WHEN( "Camera is rotated -90 Yaw" ) - { - rotation_vec.yaw() = glm::radians( -90.0f ); - camera.setView( constants::WORLD_CENTER, rotation_vec ); - const auto camera_forward { camera.getForward() }; - - REQUIRE( camera_forward == constants::WORLD_LEFT ); - } - } - - SECTION( "Camera projection test - Perspective" ) - { - WHEN( "Camera is rotated" ) - { - float x_gen { GENERATE( 0.0f, 90.0f, 180.0f, 270.0f ) }; - float y_gen { GENERATE( 0.0f, 90.0f, 180.0f, 270.0f ) }; - float z_gen { GENERATE( 0.0f, 90.0f, 180.0f, 270.0f ) }; - - const Rotation rotation { x_gen, y_gen, z_gen }; - - camera.setView( constants::WORLD_RIGHT, rotation ); - - THEN( "Camera translation should not change" ) - { - const Coordinate< CoordinateSpace::World > position { camera.getPosition() }; - REQUIRE( position == constants::WORLD_RIGHT ); - } - } - - WHEN( "Camera is translated right by WORLD_RIGHT" ) - { - camera.setView( constants::WORLD_CENTER + constants::WORLD_RIGHT, Rotation( 0.0f ) ); - - THEN( "camera.getPosition() should be WORLD_RIGHT" ) - { - const auto position { camera.getPosition() }; - REQUIRE( position == constants::WORLD_RIGHT ); - } - - THEN( "A point at the origin should be translated to the left" ) - { - const glm::mat4 combined_matrix { camera.getProjectionViewMatrix() }; - - 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 left by WORLD_LEFT" ) - { - camera.setView( constants::WORLD_CENTER + constants::WORLD_LEFT, Rotation( 0.0f ) ); - - THEN( "camera.getPosition() should be WORLD_UP" ) - { - REQUIRE( camera.getPosition() == constants::WORLD_LEFT ); - } - - THEN( "A point at the origin should be translated to the right" ) - { - const auto matrix { camera.getProjectionViewMatrix() }; - const glm::vec3 point { matrix * glm::vec4( constants::WORLD_CENTER, 1.0f ) }; - - CAPTURE( matrix ); - CAPTURE( point ); - - REQUIRE( point.x > 0.0f ); - REQUIRE( point.y == 0.0f ); - } - } - - constexpr int window_width { 1920 }; - constexpr int window_height { 1080 }; - - GIVEN( "A window height of 1920x1080" ) - { - WHEN( "Camera is translated down by WORLD_DOWN " ) - { - camera.setView( - constants::WORLD_CENTER + constants::WORLD_DOWN + constants::WORLD_BACKWARD, Rotation( 0.0f ) ); - - THEN( "camera.getPosition() should be WORLD_DOWN" ) - { - REQUIRE( camera.getPosition() == constants::WORLD_DOWN + constants::WORLD_BACKWARD ); - } - - const auto view_matrix { camera.getViewMatrix() }; - const auto projection_matrix { camera.getProjectionMatrix() }; - const auto matrix { camera.getProjectionViewMatrix() }; - - CAPTURE( view_matrix ); - CAPTURE( projection_matrix ); - CAPTURE( matrix ); - - const glm::vec3 point { glm::projectZO( - constants::WORLD_CENTER, - glm::mat4( 1.0f ), - matrix, - glm::vec4( 0.0f, 0.0f, window_width, window_height ) ) }; - - THEN( "A point should be above the half way point of the screen" ) - { - CAPTURE( point ); - //Because vulkan starts at the top left, the y axis is inverted [0, 1] where 0 is the top. 1 is the bottom. - // This means that a point 'below' the half way mark on the screen will actually be more positive. - REQUIRE( point.y < window_height / 2.0f ); - } - - THEN( "A point should be in the view of the screen" ) - { - REQUIRE( point.x >= 0.0f ); - REQUIRE( point.x <= window_width ); - REQUIRE( point.y >= 0.0f ); - REQUIRE( point.y <= window_height ); - } - - THEN( "A point should be infront of the camera" ) - { - REQUIRE( point.z >= 0.0f ); - AND_THEN( "The point should not be in front of the camera" ) - { - REQUIRE( point.z <= 1.0f ); - } - } - } - - WHEN( "Camera is translated up by WORLD_UP" ) - { - camera.setView( - constants::WORLD_CENTER + constants::WORLD_UP + constants::WORLD_BACKWARD, Rotation( 0.0f ) ); - - THEN( "camera.getPosition() should be WORLD_UP" ) - { - const auto position { camera.getPosition() }; - REQUIRE( position == constants::WORLD_UP + constants::WORLD_BACKWARD ); - } - - const auto view_matrix { camera.getViewMatrix() }; - const auto projection_matrix { camera.getProjectionMatrix() }; - const auto matrix { camera.getProjectionViewMatrix() }; - - CAPTURE( view_matrix ); - CAPTURE( projection_matrix ); - CAPTURE( matrix ); - - const glm::vec3 point { glm::projectZO( - constants::WORLD_CENTER, - glm::mat4( 1.0f ), - matrix, - glm::vec4( 0.0f, 0.0f, window_width, window_height ) ) }; - - CAPTURE( point ); - - THEN( "A point should be below the half way point of the screen" ) - { - //Because vulkan starts at the top left, the y axis is inverted [0, 1] where 0 is the top. 1 is the bottom. - // This means that a point 'below' the half way mark on the screen will actually be more positive. - REQUIRE( point.y > ( window_height / 2 ) ); - } - - THEN( "A point should be in the view of the screen" ) - { - REQUIRE( point.x >= 0.0f ); - REQUIRE( point.x <= window_width ); - REQUIRE( point.y >= 0.0f ); - REQUIRE( point.y <= window_height ); - } - - THEN( "A point should be in front of the camera" ) - { - REQUIRE( point.z > 0.0f ); - AND_THEN( "The point should not be behind the camera" ) - { - REQUIRE( point.z < 1.0f ); - } - } - } - - WHEN( "Camera is translated forward by WORLD_FORWARD" ) - { - camera.setView( constants::WORLD_CENTER + constants::WORLD_FORWARD, Rotation( 0.0f ) ); - - THEN( "camera.getPosition() should be WORLD_FORWARD" ) - { - const auto position { camera.getPosition() }; - REQUIRE( position == constants::WORLD_FORWARD ); - } - - AND_WHEN( "A point is processed through the matrix" ) - { - const auto matrix { camera.getProjectionViewMatrix() }; - const glm::vec3 point { glm::projectZO( - constants::WORLD_CENTER, - glm::mat4( 1.0f ), - matrix, - glm::vec4( 0.0f, 0.0f, window_width, window_height ) ) }; - - THEN( "A point at the origin should be translated back" ) - { - CAPTURE( point ); - - THEN( "Point should be in the view of the screen" ) - { - REQUIRE( point.x >= 0.0f ); - REQUIRE( point.x <= window_width ); - REQUIRE( point.y >= 0.0f ); - REQUIRE( point.y <= window_height ); - } - - THEN( "Point should be behind the camera" ) - { - //Comparing to 1 since the point should be behind the camera (0 to 1 is 'forward' to Z plane. Anything Above 1 is behind) - REQUIRE( point.z > 1.0f ); - } - } - } - } - } - } - - WHEN( "Two points are translated by the camera when directly in front" ) - { - const auto point_near { constants::WORLD_FORWARD }; - const auto point_far { constants::WORLD_FORWARD * 2.0f }; - - const auto projected_near { camera.getProjectionViewMatrix() * glm::vec4( point_near, 1.0f ) }; - const auto projected_far { camera.getProjectionViewMatrix() * glm::vec4( point_far, 1.0f ) }; - - THEN( "The near point should be closer than the far point" ) - { - REQUIRE( projected_near.z < projected_far.z ); - } - } -} \ No newline at end of file diff --git a/src/tests/src/FrustumTesting.cpp.disabled b/src/tests/src/FrustumTesting.cpp.disabled deleted file mode 100644 index a26ce66..0000000 --- a/src/tests/src/FrustumTesting.cpp.disabled +++ /dev/null @@ -1,214 +0,0 @@ -// -// Created by kj16609 on 2/15/24. -// - -#include - -#include - -#define EXPOSE_FRUSTUM_TESTS -#define EXPOSE_CAMERA_TESTS -#include "engine/camera/Camera.hpp" -#include "engine/primitives/Frustum.hpp" -#include "engine/primitives/boxes/OrientedBoundingBox.hpp" -#include "gtest_printers.hpp" - -using namespace fgl::engine; - -constexpr int height { 1080 }; -constexpr int width { 1920 }; -constexpr float ASPECT_RATIO { static_cast< float >( width ) / static_cast< float >( height ) }; - -TEST_CASE( "Frustum", "[frustum][rotation][translation]" ) -{ - Camera camera { Camera::CREATE_TESTING_CAMERA() }; - camera.setPerspectiveProjection( 90.0f, ASPECT_RATIO, constants::NEAR_PLANE, constants::FAR_PLANE ); - - GIVEN( "A default frustum from a default camera" ) - { - const auto base_frustum { camera.getBaseFrustum() }; - const auto frustum { camera.getFrustumBounds() }; - - THEN( "The near plane should have a distance of constants::NEAR_PLANE" ) - { - REQUIRE( base_frustum.near.distance() == constants::NEAR_PLANE ); - } - THEN( "The far plane should have a distance of -constants::FAR_PLANE" ) - { - REQUIRE( base_frustum.far.distance() == -constants::FAR_PLANE ); - } - - GIVEN( "A line going from UP BACK to DOWN BACK" ) - { - const LineSegment< CoordinateSpace::World > line { - constants::WORLD_BACKWARD + ( constants::WORLD_UP * 1.0f ), - constants::WORLD_BACKWARD + ( constants::WORLD_DOWN * 1.0f ) - }; - - THEN( "The signed distance of the end to the start should be positive" ) - { - REQUIRE( signedDistance( line.direction(), line.end, line.start ) > 0.0f ); - } - - THEN( "The signed distance of the flipped line end multiplied by 2 should be negative" ) - { - const WorldCoordinate new_end { line.flip().end * 2.0f }; - REQUIRE( signedDistance( line.direction(), new_end, line.start ) < 0.0f ); - } - - THEN( "The line should intersect the top and bottom planes" ) - { - const auto top_intersection_point { frustum.top.intersection( line ) }; - - AND_THEN( "The line should intersect the top plane BELOW the origin" ) - { - REQUIRE_FALSE( std::isnan( top_intersection_point.z ) ); - REQUIRE( top_intersection_point.z < 0.0f ); - } - - const auto bottom_intersection_point { frustum.bottom.intersection( line ) }; - - AND_THEN( "The line should intersect the bottom plane ABOVE the origin" ) - { - REQUIRE_FALSE( std::isnan( top_intersection_point.z ) ); - REQUIRE( bottom_intersection_point.z > 0.0f ); - } - - const float top_signed_distance { - signedDistance( line.direction(), top_intersection_point, line.start ) - }; - const float bottom_signed_distance { - signedDistance( line.direction(), bottom_intersection_point, line.start ) - }; - - //In this case the top plane is actually the bottom most plane. So it's distance should be higher - REQUIRE( top_signed_distance > bottom_signed_distance ); - } - - std::vector< WorldCoordinate > enter_points {}; - std::vector< WorldCoordinate > exit_points {}; - - WHEN( "The top plane is processed" ) - { - processPlane( frustum.top, line, enter_points, exit_points ); - - THEN( "The enter point vector should be added too" ) - { - REQUIRE( enter_points.size() == 1 ); - - AND_THEN( "The point gotten should be forward (positive distance)" ) - { - REQUIRE( signedDistance( line.direction(), enter_points[ 0 ], line.start ) > 0.0f ); - } - } - } - - WHEN( "The bottom plane is processed" ) - { - processPlane( frustum.bottom, line, enter_points, exit_points ); - - THEN( "The exit point vector should be added too" ) - { - REQUIRE( exit_points.size() == 1 ); - - AND_THEN( "The point gotten should be backwards (negative distance)" ) - { - REQUIRE( signedDistance( line.direction(), exit_points[ 0 ], line.start ) < 0.0f ); - } - } - } - - WHEN( "top and bottom planes are processed" ) - { - processPlane( frustum.top, line, enter_points, exit_points ); - processPlane( frustum.bottom, line, enter_points, exit_points ); - - THEN( "The enter point found should be more positive then the original point" ) - { - const float line_start_distance { signedDistance( line.direction(), line.start, line.start ) }; - const float enter_distance { signedDistance( line.direction(), enter_points[ 0 ], line.start ) }; - - REQUIRE( line_start_distance < enter_distance ); - } - - const auto top_intersection { frustum.top.intersection( line ) }; - THEN( "The last enter point should be the top plane intersection" ) - { - REQUIRE( getLastEnter( enter_points, line ) == top_intersection ); - } - - const auto bottom_intersection { frustum.bottom.intersection( line ) }; - THEN( "The first exit point should be the bottom plane intersection" ) - { - REQUIRE( getFirstExit( exit_points, line ) == bottom_intersection ); - } - - THEN( "The top intersection should happen after the bottom intersection" ) - { - const float top_distance { signedDistance( line.direction(), top_intersection, line.start ) }; - const float bottom_distance { signedDistance( line.direction(), bottom_intersection, line.start ) }; - - REQUIRE( top_distance > bottom_distance ); - } - } - - THEN( "The line should NOT intersect the frustum" ) - { - REQUIRE_FALSE( frustum.intersects( line ) ); - } - } - - GIVEN( "A line going from UP FORWARD to DOWN FORWARD" ) - { - const LineSegment< CoordinateSpace::World > line { - constants::WORLD_FORWARD + ( constants::WORLD_UP * 2.0f ), - constants::WORLD_FORWARD + ( constants::WORLD_DOWN * 2.0f ) - }; - - THEN( "The line should intersect the frustum" ) - { - REQUIRE( frustum.intersects( line ) ); - } - } - } - - GIVEN( "A frustum from a 90deg fov camera" ) - { - const auto frustum { camera.getFrustumBounds() }; - - const auto points { frustum.points() }; - - THEN( "The first 4 points should be beyond 25.0f in the Y axis" ) - { - REQUIRE( points[ 0 ].y > 25.0f ); - REQUIRE( points[ 1 ].y > 25.0f ); - REQUIRE( points[ 2 ].y > 25.0f ); - REQUIRE( points[ 3 ].y > 25.0f ); - } - - THEN( "The last 4 points should be before the 25.0f mark" ) - { - REQUIRE( points[ 4 ].y < 25.0f ); - REQUIRE( points[ 5 ].y < 25.0f ); - REQUIRE( points[ 6 ].y < 25.0f ); - REQUIRE( points[ 7 ].y < 25.0f ); - } - } - - GIVEN( "A camera translated to (WORLD_LEFT,0,0) * 250.0f" ) - { - camera.setView( constants::WORLD_LEFT * 250.0f, constants::DEFAULT_ROTATION ); - - AND_GIVEN( "A point directly infront of the camera" ) - { - WorldCoordinate point { constants::WORLD_LEFT * 250.0f + constants::WORLD_FORWARD }; - - THEN( "The point should be in the frustum" ) - { - const auto frustum { camera.getFrustumBounds() }; - - REQUIRE( frustum.intersects( point ) ); - } - } - } -} \ No newline at end of file diff --git a/src/tests/src/PlaneTests.cpp.disabled b/src/tests/src/PlaneTests.cpp.disabled deleted file mode 100644 index e4159e3..0000000 --- a/src/tests/src/PlaneTests.cpp.disabled +++ /dev/null @@ -1,213 +0,0 @@ -// -// Created by kj16609 on 2/25/24. -// - -#include - -#include - -#define EXPOSE_CAMERA_INTERNAL -#include "engine/primitives/Frustum.hpp" -#include "engine/primitives/TransformComponent.hpp" -#include "engine/primitives/lines/LineSegment.hpp" -#include "gtest_printers.hpp" -#include "operators/vector.hpp" - -using namespace fgl::engine; - -TEST_CASE( "Planes", "[frustum][rotation][translation]" ) -{ - WHEN( "Given a default constructed Plane" ) - { - Plane< CoordinateSpace::World > plane {}; - - THEN( "Distance should be DEFAULT_FLOAT" ) - { - REQUIRE( plane.distance() == constants::DEFAULT_FLOAT ); - } - - THEN( "Direction should be WORLD_FORWARD" ) - { - REQUIRE( plane.getDirection() == NormalVector( constants::WORLD_FORWARD ) ); - } - - THEN( "Position should be Y+ infinity" ) - { - REQUIRE( plane.getPosition() == WorldCoordinate( constants::DEFAULT_VEC3 * constants::WORLD_FORWARD ) ); - } - } - - /* - GIVEN( "A Plane pointing at WORLD_FORWARD with a distance of 0.5" ) - { - const Plane< CoordinateSpace::Model > plane { Coordinate< CoordinateSpace::Model >( constants::WORLD_FORWARD ), 0.5f }; - - TransformComponent component { WorldCoordinate( constants::WORLD_CENTER ), - glm::vec3( 1.0f ), - { 0.0f, 0.0f, 0.0f } }; - - WHEN( "Translated forward 1U" ) - { - component.translation += constants::WORLD_FORWARD; - const auto translated_plane { component.mat() * plane }; - - THEN( "The distance should be 0.5 + 1" ) - { - REQUIRE( translated_plane.distance() == 0.5f + 1.0f ); - } - - THEN( "The direction should be WORLD_BACKWARD" ) - { - REQUIRE( translated_plane.direction() == constants::WORLD_FORWARD ); - } - - THEN( "The position should be 0.5U behind the origin" ) - { - REQUIRE( translated_plane.getPosition() == constants::WORLD_FORWARD * 1.5f ); - } - } - - WHEN( "Translated backwards 1U" ) - { - component.translation -= constants::WORLD_FORWARD; - const auto translated_plane { component.mat() * plane }; - - THEN( "The distance should be 0.5 - 1" ) - { - REQUIRE( translated_plane.distance() == 0.5f - 1.0f ); - } - - THEN( "The direction should be WORLD_BACKWARD" ) - { - REQUIRE( translated_plane.direction() == constants::WORLD_FORWARD ); - } - - THEN( "The position should be 0.5U behind the origin" ) - { - REQUIRE( translated_plane.getPosition() == constants::WORLD_FORWARD * -0.5f ); - } - } - - WHEN( "Rotated +90 Yaw" ) - { - component.translation = constants::WORLD_CENTER; - component.rotation.yaw() += glm::radians( 90.0f ); - - THEN( "Distance should not change" ) - { - REQUIRE( ( component.mat() * plane ).distance() == plane.distance() ); - } - - THEN( "Direction should be WORLD_RIGHT" ) - { - REQUIRE( ( component.mat() * plane ).direction() == constants::WORLD_RIGHT ); - } - } - - WHEN( "Rotated +90 Yaw and translated 1U Right" ) - { - component.translation += constants::WORLD_RIGHT; - component.rotation.yaw() += glm::radians( 90.0f ); - - const auto matrix { component.mat() }; - - const auto translated_plane { matrix * plane }; - - THEN( "new_direction should be WORLD_RIGHT" ) - { - REQUIRE( matrix * plane.direction() == constants::WORLD_RIGHT ); - } - - REQUIRE( static_cast< glm::vec3 >( matrix * plane.getPosition() ) == constants::WORLD_RIGHT * 1.5f ); - - THEN( "The distance should be 0.5 + 1" ) - { - REQUIRE( translated_plane.distance() == 0.5f + 1.0f ); - } - - THEN( "The direction should be WORLD_RIGHT" ) - { - REQUIRE( translated_plane.direction() == constants::WORLD_RIGHT ); - } - - THEN( "The position should be 0.5U behind the origin" ) - { - REQUIRE( translated_plane.getPosition() == constants::WORLD_RIGHT * 1.5f ); - } - } - }*/ -} - -TEST_CASE( "Plane intersections", "[frustum][intersection]" ) -{ - GIVEN( "A line going from -1 to 1 (X,Y and Z)" ) - { - const LineSegment< CoordinateSpace::World > line { glm::vec3( -1 ), glm::vec3( 1 ) }; - - AND_GIVEN( "A plane facing toward line.start" ) - { - const Plane< CoordinateSpace::World > plane { glm::normalize( line.start.vec() ), 0.0f }; - - THEN( "The line should intersect" ) - { - REQUIRE( line.intersects( plane ) ); - - AND_THEN( "The line intersection should be at origin" ) - { - REQUIRE( plane.intersection( line ) == glm::vec3( 0.0f ) ); - } - } - - THEN( "The line should intersect going the opposite way" ) - { - REQUIRE( plane.intersects( line.flip() ) ); - - AND_THEN( "The line intersection should be at origin" ) - { - REQUIRE( plane.intersection( line ) == glm::vec3( 0.0f ) ); - } - } - } - } - - GIVEN( "A line going from WORLD_LEFT - WORLD_FRONT * 20.0 to WORLD_LEFT + WORLD_FRONT * 20.0f" ) - { - const LineSegment< CoordinateSpace::World > line { constants::WORLD_LEFT - ( constants::WORLD_FORWARD * 20.0f ), - constants::WORLD_LEFT - + ( constants::WORLD_FORWARD * 20.0f ) }; - - AND_GIVEN( "A plane facing toward WORLD_FORWARD at distance -2.0f" ) - { - const Plane< CoordinateSpace::World > plane { constants::WORLD_FORWARD, -2.0f }; - - THEN( "The plane should intersect the line" ) - { - REQUIRE( plane.intersects( line ) ); - } - - THEN( "The intersection point should be WORLD_LEFT - (constants::WORLD_FORWARD * 2.0f)" ) - { - REQUIRE( plane.intersection( line ) == constants::WORLD_LEFT - ( constants::WORLD_FORWARD * 2.0f ) ); - } - } - } - - GIVEN( "A line going from WORLD_UP to WORLD_DOWN" ) - { - const LineSegment< fgl::engine::CoordinateSpace::World > line { - constants::WORLD_UP * 5.0f - constants::WORLD_BACKWARD, - constants::WORLD_DOWN * 5.0f - constants::WORLD_BACKWARD - }; - - AND_GIVEN( "A plane facing UP + FORWARD" ) - { - const Plane< fgl::engine::CoordinateSpace::World > plane { constants::WORLD_FORWARD + constants::WORLD_UP, - 0.0f }; - - THEN( "The plane should intersect the line" ) - { - REQUIRE( plane.intersects( line ) ); - } - } - } -} diff --git a/src/tests/src/RotationTests.cpp b/src/tests/src/RotationTests.cpp deleted file mode 100644 index 1f242e7..0000000 --- a/src/tests/src/RotationTests.cpp +++ /dev/null @@ -1,327 +0,0 @@ -// -// Created by kj16609 on 9/28/24. -// - -#include "catch2/catch_approx.hpp" -#include "catch2/catch_test_macros.hpp" -#include "catch2/matchers/catch_matchers.hpp" -#include "engine/primitives/Rotation.hpp" -#include "gtest_printers.hpp" -#include "operators/vector.hpp" - -TEST_CASE( "Rotations", "[transform][rotation]" ) -{ - using namespace fgl::engine; - - GIVEN( "A Default Rotation" ) - { - Rotation rot {}; - - SECTION( "Check quat" ) - { - glm::quat quat { rot.internal_quat() }; - - THEN( "Quat: w should be 1.0" ) - { - REQUIRE( quat.w == Catch::Approx( 1.0 ) ); - } - - auto is_zero { Catch::Approx( 0.0 ) }; - - THEN( "Quat: x should be zero" ) - { - REQUIRE( quat.x == is_zero ); - } - THEN( "Quat: y should be zero" ) - { - REQUIRE( quat.y == is_zero ); - } - THEN( "Quat: z should be zero" ) - { - REQUIRE( quat.z == is_zero ); - } - } - - THEN( "Forward should be WORLD_FORWARD" ) - { - REQUIRE( rot.forward() == NormalVector( constants::WORLD_FORWARD ) ); - } - - THEN( "Right should be WORLD_RIGHT" ) - { - REQUIRE( rot.right() == NormalVector( constants::WORLD_RIGHT ) ); - } - - THEN( "Up should be WORLD_UP" ) - { - REQUIRE( rot.up() == NormalVector( constants::WORLD_UP ) ); - } - - THEN( "Euler X should be 0.0" ) - { - REQUIRE( rot.xAngle() == Catch::Approx( 0.0 ) ); - } - - THEN( "Euler Y should be 0.0" ) - { - REQUIRE( rot.yAngle() == Catch::Approx( 0.0 ) ); - } - - THEN( "Euler Z should be 0.0" ) - { - REQUIRE( rot.zAngle() == Catch::Approx( 0.0 ) ); - } - } - - SECTION( "X Rotation (Roll)" ) - { - for ( float i = 0; i < 360.0f; i += 1.0f ) - { - GIVEN( "A rotation with euler angles (" << i << ", 0.0f, 0.0f)" ) - { - Rotation rot { glm::radians( i ), 0.0f, 0.0f }; - - INFO( "Euler Value: " << i ); - - INFO( "Euler X: " << rot.xAngle() ); - INFO( "Euler Y: " << rot.yAngle() ); - INFO( "Euler Z: " << rot.zAngle() ); - - INFO( - "Quat: (" << rot.internal_quat().w << ", " << rot.internal_quat().x << ", " << rot.internal_quat().y - << ", " << rot.internal_quat().z << ")" ); - - if ( i < 180.0f ) - { - // Test must be disabled due to gimbol lock when converting to euler being weird above 180.0f - THEN( "Euler X should be " << i ) - { - REQUIRE( rot.xAngle() == Catch::Approx( glm::radians( i ) ) ); - REQUIRE( rot.yAngle() == Catch::Approx( 0.0 ) ); - REQUIRE( rot.zAngle() == Catch::Approx( 0.0 ) ); - } - } - - THEN( "forward() should not change throughout the rotation" ) - { - const NormalVector point { rot.forward() }; - - REQUIRE( point == NormalVector( constants::WORLD_FORWARD ) ); - } - - THEN( "A point starting at WORLD_RIGHT should rotate to WORLD_UP when given X+ (Roll)" ) - { - const NormalVector point { rot.right() }; - - // When rotating around the X axis, the right vector should rotate to the up vector - // This means that the x value should be at 0.0 through the entire rotation - REQUIRE( point.x == Catch::Approx( 0.0 ).margin( 0.1 ) ); - - if ( i == 0.0f ) - { - // At 0.0f, the right vector should be the same as the WORLD_RIGHT vector - REQUIRE( point == NormalVector( constants::WORLD_RIGHT ) ); - } - else if ( i == 90.0f ) - { - // At a 90+ rotation the point should be at WORLD_UP - REQUIRE( point == NormalVector( constants::WORLD_UP ) ); - } - else if ( i == 180.0f ) - { - // A full 180 degree rotation should bring the point back to WORLD_LEFT - REQUIRE( point == NormalVector( -constants::WORLD_RIGHT ) ); - } - else if ( i == 270.0f ) - { - // A full 270 degree rotation should bring the point back to WORLD_DOWN - REQUIRE( point == NormalVector( -constants::WORLD_UP ) ); - } - else if ( i == 360.0f ) - { - // A full 360 degree rotation should bring the point back to WORLD_RIGHT - REQUIRE( point == NormalVector( constants::WORLD_RIGHT ) ); - } - } - } - } - } - - SECTION( "Y Rotation (Pitch)" ) - { - for ( float i = 0; i < 360.0f; i += 1.0f ) - { - GIVEN( "A rotation with euler angles (0.0f, " << i << ", 0.0f)" ) - { - //TODO: Figure out how to test for 180.0f deg - Rotation rot { 0.0f, glm::radians( i ), 0.0f }; - - INFO( "Euler Value: " << i ); - - INFO( "Euler X: " << rot.xAngle() ); - INFO( "Euler Y: " << rot.yAngle() ); - INFO( "Euler Z: " << rot.zAngle() ); - - INFO( - "Quat: (" << rot.internal_quat().w << ", " << rot.internal_quat().x << ", " << rot.internal_quat().y - << ", " << rot.internal_quat().z << ")" ); - - if ( i < 90.0f ) - { - // Test must be disabled due to gimbol lock when converting to euler above 90.0f - THEN( "Euler Y should be " << i ) - { - REQUIRE( rot.xAngle() == Catch::Approx( 0.0 ).epsilon( 0.1 ) ); - REQUIRE( rot.yAngle() == Catch::Approx( glm::radians( i ) ).epsilon( 0.01 ) ); - REQUIRE( rot.zAngle() == Catch::Approx( glm::radians( 0.0 ) ).epsilon( 0.01 ) ); - } - } - - THEN( "right() should return WORLD_RIGHT throughout the rotation" ) - { - REQUIRE( rot.right() == NormalVector( constants::WORLD_RIGHT ) ); - } - - THEN( "A point starting at WORLD_FORWARD should rotate to WORLD_UP when given Y+ (pitch)" ) - { - const NormalVector point { rot.forward() }; - - // When rotating around the Y axis, the forward vector should rotate to the left vector - // This means that the x value should be at 0.0 through the entire rotation - REQUIRE( point.y == Catch::Approx( 0.0 ).margin( 0.1 ) ); - - INFO( "Point: " << point.x << ", " << point.y << ", " << point.z ); - - if ( i == 0.0f ) - { - // At 0.0f, the forward vector should be the same as the WORLD_FORWARD vector - REQUIRE( point == NormalVector( constants::WORLD_FORWARD ) ); - } - else if ( i == 90.0f ) - { - // At a 90+ rotation the point should be at WORLD_LEFT - REQUIRE( point == NormalVector( constants::WORLD_UP ) ); - } - else if ( i == 180.0f ) - { - // A full 180 degree rotation should bring the point back to WORLD_BACKWARD - REQUIRE( point == NormalVector( -constants::WORLD_FORWARD ) ); - } - else if ( i == 270.0f ) - { - // A full 270 degree rotation should bring the point back to WORLD_RIGHT - REQUIRE( point == NormalVector( -constants::WORLD_UP ) ); - } - else if ( i == 360.0f ) - { - // A full 360 degree rotation should bring the point back to WORLD_FORWARD - REQUIRE( point == NormalVector( constants::WORLD_FORWARD ) ); - } - } - } - } - } - - SECTION( "Z Rotation (Yaw)" ) - { - for ( float i = 0; i < 360.0f; i += 1.0f ) - { - GIVEN( "A rotation with euler angles (0.0f, 0.0f, " << i << " )" ) - { - Rotation rot { 0.0f, 0.0f, glm::radians( i ) }; - - INFO( "Euler Value: " << i ); - - INFO( "Euler X: " << rot.xAngle() ); - INFO( "Euler Y: " << rot.yAngle() ); - INFO( "Euler Z: " << rot.zAngle() ); - - INFO( - "Quat: (" << rot.internal_quat().w << ", " << rot.internal_quat().x << ", " << rot.internal_quat().y - << ", " << rot.internal_quat().z << ")" ); - - if ( i < 90.0f ) - { - // Test must be disabled due to gimbol lock when converting to euler - THEN( "Euler Z should be " << i ) - { - REQUIRE( rot.xAngle() == Catch::Approx( 0.0 ) ); - REQUIRE( rot.yAngle() == Catch::Approx( 0.0 ) ); - REQUIRE( rot.zAngle() == Catch::Approx( glm::radians( i ) ) ); - } - } - - THEN( "up() should not change throughout the rotation" ) - { - REQUIRE( rot.up() == NormalVector( constants::WORLD_UP ) ); - } - - THEN( "A point starting at WORLD_FORWARD should rotate to WORLD_UP when given Z+ (pitch)" ) - { - const NormalVector point { rot.forward() }; - - // When rotating around the Z axis, the up vector should rotate to the left vector - // This means that the x value should be at 0.0 through the entire rotation - REQUIRE( point.z == Catch::Approx( 0.0 ).margin( 0.1 ) ); - - INFO( "Point: " << point.x << ", " << point.y << ", " << point.z ); - - if ( i == 0.0f ) - { - // At 0.0f, the forward vector should be the same as the WORLD_FORWARD vector - REQUIRE( point == NormalVector( constants::WORLD_FORWARD ) ); - } - else if ( i == 90.0f ) - { - // At a 90+ rotation the point should be at WORLD_UP - REQUIRE( point == NormalVector( constants::WORLD_RIGHT ) ); - } - else if ( i == 180.0f ) - { - // A full 180 degree rotation should bring the point back to WORLD_BACKWARD - REQUIRE( point == NormalVector( constants::WORLD_BACKWARD ) ); - } - else if ( i == 270.0f ) - { - // A full 270 degree rotation should bring the point back to WORLD_DOWN - REQUIRE( point == NormalVector( constants::WORLD_LEFT ) ); - } - else if ( i == 360.0f ) - { - // A full 360 degree rotation should bring the point back to WORLD_FORWARD - REQUIRE( point == NormalVector( constants::WORLD_FORWARD ) ); - } - } - } - } - } - - SECTION( "Y+Z Rotation" ) - { - //TODO: Figure out how to test for 180.0f deg - for ( float i = 0; i < 90.0f; i += 1.0f ) - { - GIVEN( "A rotation with euler angles (0.0f, Y, Z)" ) - { - Rotation rot { 0.0f, glm::radians( i ), glm::radians( i ) }; - - INFO( "Euler Value: " << i ); - - INFO( "Euler X: " << rot.xAngle() ); - INFO( "Euler Y: " << rot.yAngle() ); - INFO( "Euler Z: " << rot.zAngle() ); - - INFO( - "Quat: (" << rot.internal_quat().w << ", " << rot.internal_quat().x << ", " << rot.internal_quat().y - << ", " << rot.internal_quat().z << ")" ); - - THEN( "Euler Y should be " << i ) - { - REQUIRE( rot.xAngle() == Catch::Approx( 0.0 ).epsilon( 0.1 ).margin( 0.1 ) ); - REQUIRE( rot.yAngle() == Catch::Approx( glm::radians( i ) ).epsilon( 0.01 ) ); - REQUIRE( rot.zAngle() == Catch::Approx( glm::radians( i ) ).epsilon( 0.01 ) ); - } - } - } - } -} \ No newline at end of file diff --git a/src/tests/src/TransformTests.cpp b/src/tests/src/TransformTests.cpp deleted file mode 100644 index b9ae099..0000000 --- a/src/tests/src/TransformTests.cpp +++ /dev/null @@ -1,176 +0,0 @@ -// -// Created by kj16609 on 2/16/24. -// - -#include - -#include "engine/primitives/TransformComponent.hpp" -#include "gtest_printers.hpp" -#include "operators/vector.hpp" - -using namespace fgl::engine; - -/* -TEST_CASE( "Matrix", "[transform][rotation][translation][matrix]" ) -{ - TransformComponent component {}; - component.scale = glm::vec3( 8.0f ); - component.translation = WorldCoordinate( constants::WORLD_FORWARD * 100.0f ); - - WHEN( "Rotation is set to 90.0f yaw" ) - { - component.rotation = Rotation( 0.0f ).yaw() += glm::radians( 90.0f ); - - GIVEN( "The matrix from the transform" ) - { - const auto matrix { component.mat() }; - - THEN( "The extracted rotation should match the transform rotation" ) - { - REQUIRE( component.rotation.w == Catch::Approx( matrix.quat().w ) ); - REQUIRE( component.rotation.x == Catch::Approx( matrix.quat().x ) ); - REQUIRE( component.rotation.y == Catch::Approx( matrix.quat().y ) ); - REQUIRE( component.rotation.z == Catch::Approx( matrix.quat().z ) ); - } - } - } -} -*/ - -/* -TEST_CASE( "Quaternion Rotations", "[rotation]" ) -{ - SECTION( "Quaternion build from euler" ) - { - // euler should be in `pitch, roll, yaw` order - const auto q_pitch { glm::angleAxis( 0.0f, constants::WORLD_RIGHT ) }; - const auto q_roll { glm::angleAxis( 0.0f, constants::WORLD_FORWARD ) }; - const auto q_yaw { glm::angleAxis( 0.0f, constants::WORLD_UP ) }; - - REQUIRE( q_pitch == glm::quat( 1.0f, 0.0f, 0.0f, 0.0f ) ); - REQUIRE( q_pitch * q_roll == glm::quat( 1.0f, 0.0f, 0.0f, 0.0f ) ); - REQUIRE( q_pitch * q_roll * q_yaw == glm::quat( 1.0f, 0.0f, 0.0f, 0.0f ) ); - - const Rotation rot { 0.0f, 0.0f, 0.0f }; - - REQUIRE( glm::quat( 1.0f, 0.0f, 0.0f, 0.0f ) == rot ); - } - - WHEN( "Building a quat with a 90.0f deg pitch" ) - { - const glm::quat quat { fgl::engine::buildQuat( glm::radians( 90.0f ), 0.0f, 0.0f ) }; - - THEN( "The output of glm::yaw should be 90.0f deg" ) - { - REQUIRE( glm::pitch( quat ) == Catch::Approx( glm::radians( 90.0f ) ) ); - REQUIRE( glm::roll( quat ) == 0.0f ); - REQUIRE( glm::yaw( quat ) == 0.0f ); - } - } - - WHEN( "Building a quat with a 90.0f deg roll" ) - { - const glm::quat quat { fgl::engine::buildQuat( 0.0f, glm::radians( 90.0f ), 0.0f ) }; - - THEN( "The output of glm::roll should be 90.0f deg" ) - { - REQUIRE( glm::pitch( quat ) == 0.0f ); - REQUIRE( glm::roll( quat ) == Catch::Approx( glm::radians( 90.0f ) ) ); - REQUIRE( glm::yaw( quat ) == 0.0f ); - } - } - - WHEN( "Building a quat with a 90.0f deg yaw" ) - { - const glm::quat quat { fgl::engine::buildQuat( 0.0f, 0.0f, glm::radians( 90.0f ) ) }; - - THEN( "The output of glm::pitch should be 90.0f deg" ) - { - REQUIRE( glm::pitch( quat ) == 0.0f ); - REQUIRE( glm::roll( quat ) == 0.0f ); - REQUIRE( glm::yaw( quat ) == Catch::Approx( glm::radians( 90.0f ) ).epsilon( 0.01f ) ); - } - } -} -*/ - -TEST_CASE( "Transform", "[transform][rotation][translation]" ) -{ - TransformComponent component; - - component.translation = WorldCoordinate( constants::WORLD_CENTER ); - component.scale = glm::vec3( 1.0f ); - component.rotation = Rotation( 0.0f ); - - constexpr auto TEST_POINT { constants::WORLD_Y }; - - SECTION( "Translation" ) - { - WHEN( "Translated Z+" ) - { - component.translation.up() += 1.0f; - - const glm::vec3 translated_point { component.mat4() * glm::vec4( TEST_POINT, 1.0f ) }; - - THEN( "Forward should be WORLD_FORWARD + WORLD_UP" ) - { - REQUIRE( translated_point == constants::WORLD_Y + constants::WORLD_Z ); - } - } - - WHEN( "Translated Y+" ) - { - component.translation.forward() += 1.0f; - - const glm::vec3 translated_point { component.mat4() * glm::vec4( TEST_POINT, 1.0f ) }; - - THEN( "Forward should be WORLD_FORWARD + WORLD_FORWARD" ) - { - REQUIRE( translated_point == constants::WORLD_Y + constants::WORLD_Y ); - } - } - - WHEN( "Translated X+" ) - { - component.translation.right() += 1.0f; - - const glm::vec3 translated_point { component.mat4() * glm::vec4( TEST_POINT, 1.0f ) }; - - THEN( "Forward should be WORLD_FORWARD + WORLD_RIGHT" ) - { - REQUIRE( translated_point == constants::WORLD_Y + constants::WORLD_X ); - } - } - } - - /* - SECTION( "Mix" ) - { - WHEN( "Translated X+1 and Rotated Y+90" ) - { - component.rotation.yAngle() = glm::radians( glm::radians( 90.0f ) ); - component.translation.right() += 1.0f; - - const glm::vec3 translated_point { component.mat4() * glm::vec4( constants::WORLD_Y, 1.0f ) }; - - THEN( "WORLD_FORWARD should be transformed into (WORLD_RIGHT * 2)" ) - { - REQUIRE( translated_point == ( constants::WORLD_X * 2.0f ) ); - } - } - - SECTION( "Translated X+1 Yaw-90" ) - { - component.rotation.yAngle() = glm::radians( -glm::radians( 90.0f ) ); - component.translation.right() += 1.0f; - - const glm::vec3 translated_point { component.mat4() * glm::vec4( TEST_POINT, 1.0f ) }; - - THEN( "WORLD_FORWARD should be transformed into WORLD_CENTER)" ) - { - REQUIRE( translated_point == constants::WORLD_CENTER ); - } - } - } - */ -} \ No newline at end of file diff --git a/src/tests/src/math/quat.cpp b/src/tests/src/math/quat.cpp new file mode 100644 index 0000000..617c119 --- /dev/null +++ b/src/tests/src/math/quat.cpp @@ -0,0 +1,83 @@ +// +// Created by kj16609 on 1/30/25. +// + +#include + +#include "tests/src/gtest_printers.hpp" + +using namespace Catch::literals; + +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 ); + } + + SECTION( "Raw" ) + { + const glm::quat raw_quat { rot.internal_quat() }; + REQUIRE( raw_quat.x == 0.0f ); + REQUIRE( raw_quat.y == 0.0f ); + REQUIRE( raw_quat.z == 0.0f ); + REQUIRE( raw_quat.w == 1.0f ); + } + } + + SECTION( "Predefined" ) + { + SECTION( "-1.126, 1.012, -0.548" ) + { + fgl::engine::Rotation 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 ) ); + } + } + + SECTION( "Singularity" ) + { + SECTION( "North" ) + { + 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 ) ); + } + + SECTION( "South" ) + { + 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 ) ); + } + } + + SECTION( "Beyond Singularity Y" ) + { + fgl::engine::Rotation rot { 0.0f, 1.548f, 0.0f }; + + 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 ) ); + } +}