From 3a38e50b726ce6040f4fcda85f999278270e8708 Mon Sep 17 00:00:00 2001 From: kj16609 Date: Mon, 26 Feb 2024 02:53:59 -0500 Subject: [PATCH] Gets frustum culling via line intersection mostly working --- src/engine/CMakeLists.txt | 14 - src/engine/Camera.cpp | 2 +- src/engine/EngineContext.cpp | 20 +- src/engine/EngineContext.hpp | 1 + src/engine/KeyboardMovementController.cpp | 2 + src/engine/buffers/Buffer.hpp | 1 + src/engine/buffers/SuballocationView.hpp | 2 + src/engine/debug/drawers.cpp | 115 ++--- src/engine/debug/drawers.hpp | 33 +- src/engine/math/taitBryanMatrix.cpp | 1 + src/engine/primitives/Coordinate.hpp | 6 + src/engine/primitives/Frustum.cpp | 97 ++++- src/engine/primitives/Line.hpp | 7 + src/engine/primitives/Plane.cpp | 30 +- src/engine/primitives/Plane.hpp | 14 +- src/engine/primitives/Vector.hpp | 7 +- src/engine/systems/CullingSystem.cpp | 2 +- src/engine/systems/CullingSystem.hpp | 2 + src/engine/systems/EntityRendererSystem.cpp | 6 + src/engine/texture/TextureHandle.hpp | 3 + src/engine/universe_constants.hpp | 15 + tests/src/FrustumTesting.cpp | 451 +------------------- tests/src/PlaneTests.cpp | 208 +++++++++ 23 files changed, 480 insertions(+), 559 deletions(-) create mode 100644 src/engine/universe_constants.hpp create mode 100644 tests/src/PlaneTests.cpp diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index 7e46c87..bba79bf 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -3,20 +3,6 @@ file(GLOB_RECURSE CPP_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/**.cpp") add_library(FGLEngine STATIC ${CPP_SOURCES}) -target_precompile_headers(FGLEngine PRIVATE - - - - - - - - - - - - -) target_compile_definitions(FGLEngine PUBLIC VULKAN_HPP_FLAGS_MASK_TYPE_AS_PUBLIC) diff --git a/src/engine/Camera.cpp b/src/engine/Camera.cpp index 8b7d5a1..4f4fee7 100644 --- a/src/engine/Camera.cpp +++ b/src/engine/Camera.cpp @@ -9,6 +9,7 @@ #define GLM_ENABLE_EXPERIMENTAL #include +#include #include #include @@ -27,7 +28,6 @@ namespace fgl::engine void Camera::setPerspectiveProjection( float fovy, float aspect, float near, float far ) { - ZoneScoped; projection_matrix = Matrix< MatrixType::CameraToScreen >( glm::perspectiveLH_ZO( fovy, aspect, near, far ) ); base_frustum = createFrustum( aspect, fovy, near, far ); diff --git a/src/engine/EngineContext.cpp b/src/engine/EngineContext.cpp index d97153e..a603090 100644 --- a/src/engine/EngineContext.cpp +++ b/src/engine/EngineContext.cpp @@ -61,8 +61,8 @@ namespace fgl::engine PerFrameSuballocation< HostSingleT< PointLight > > point_lights { global_ubo_buffer, SwapChain::MAX_FRAMES_IN_FLIGHT }; - constexpr std::uint32_t matrix_default_size { 16_MiB }; - constexpr std::uint32_t draw_parameter_default_size { 16_MiB }; + constexpr std::uint32_t matrix_default_size { 64_MiB }; + constexpr std::uint32_t draw_parameter_default_size { 64_MiB }; std::vector< Buffer > matrix_info_buffers {}; @@ -94,6 +94,7 @@ namespace fgl::engine } Camera camera {}; + debug::setDebugDrawingCamera( camera ); auto viewer { GameObject::createGameObject() }; viewer.transform.translation = constants::WORLD_CENTER + glm::vec3( 0.0f, 0.0f, -2.5f ); @@ -246,6 +247,11 @@ namespace fgl::engine inputRVec3( "Rotation", Camera::frustum_alt_transform.rotation ); ImGui::PopID(); } + + if ( camera.update_using_alt || !camera.update_frustums ) + { + debug::world::drawFrustum(); + } } if ( ImGui::CollapsingHeader( "View Frustum" ) ) @@ -409,8 +415,8 @@ namespace fgl::engine TracyVkZone( tracy_ctx, command_buffer, "ImGui Rendering" ); #endif - debug::world::drawPointText( - Coordinate< CoordinateSpace::World >( 0.0f, 0.0f, 0.0f ), camera, { 1.0f, 0.0f, 0.0f } ); + debug::world:: + drawPointText( Coordinate< CoordinateSpace::World >( 0.0f, 0.0f, 0.0f ), { 1.0f, 0.0f, 0.0f } ); ImGui::End(); ImGui::Render(); @@ -465,9 +471,11 @@ namespace fgl::engine model->syncBuffers( command_buffer ); - for ( int x = 0; x < 32; ++x ) + int val { 2 }; + + for ( int x = 0; x < val; ++x ) { - for ( int y = 0; y < 32; ++y ) + for ( int y = 0; y < val; ++y ) { auto sponza = GameObject::createGameObject(); sponza.model = model; diff --git a/src/engine/EngineContext.hpp b/src/engine/EngineContext.hpp index 998d6b2..9872a85 100644 --- a/src/engine/EngineContext.hpp +++ b/src/engine/EngineContext.hpp @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include "Device.hpp" diff --git a/src/engine/KeyboardMovementController.cpp b/src/engine/KeyboardMovementController.cpp index 3c27b43..64cd943 100644 --- a/src/engine/KeyboardMovementController.cpp +++ b/src/engine/KeyboardMovementController.cpp @@ -4,7 +4,9 @@ #include "KeyboardMovementController.hpp" +#include #include +#include #include "engine/primitives/Vector.hpp" diff --git a/src/engine/buffers/Buffer.hpp b/src/engine/buffers/Buffer.hpp index 061f6ae..6c80e08 100644 --- a/src/engine/buffers/Buffer.hpp +++ b/src/engine/buffers/Buffer.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/src/engine/buffers/SuballocationView.hpp b/src/engine/buffers/SuballocationView.hpp index 131abd6..38dd55b 100644 --- a/src/engine/buffers/SuballocationView.hpp +++ b/src/engine/buffers/SuballocationView.hpp @@ -6,6 +6,8 @@ #include +#include + namespace fgl::engine { diff --git a/src/engine/debug/drawers.cpp b/src/engine/debug/drawers.cpp index 6fd6295..c7ec6d8 100644 --- a/src/engine/debug/drawers.cpp +++ b/src/engine/debug/drawers.cpp @@ -8,26 +8,38 @@ #include "engine/model/BoundingBox.hpp" #include "engine/primitives/Line.hpp" #include "engine/primitives/Vector.hpp" +#include "imgui/imgui.h" #include "tracy_colors.hpp" #if ENABLE_IMGUI_DRAWERS namespace fgl::engine::debug { + inline static std::optional< Camera* > debug_camera { std::nullopt }; + + Camera& getDebugDrawingCamera() + { + return *debug_camera.value(); + } + + void setDebugDrawingCamera( Camera& cam ) + { + debug_camera = &cam; + } + const ImVec2 windowSize() { return ImGui::GetMainViewport()->Size; } - Coordinate< CoordinateSpace::Screen > - toScreenSpace( Coordinate< CoordinateSpace::World > world_point, const Camera& camera ) + Coordinate< CoordinateSpace::Screen > toScreenSpace( Coordinate< CoordinateSpace::World > world_point ) { const ImVec2 window_size { windowSize() }; const Coordinate< CoordinateSpace::Screen > screen_point { glm::projectZO( static_cast< glm::vec3 >( world_point ), glm::mat4( 1.0f ), - camera.getProjectionViewMatrix(), + getDebugDrawingCamera().getProjectionViewMatrix(), glm::vec4( 0.0f, 0.0f, window_size.x, window_size.y ) ) }; return screen_point; @@ -58,27 +70,36 @@ namespace fgl::engine::debug namespace world { - void drawBoundingBox( const BoundingBox< CoordinateSpace::World >& box, Camera& camera, const glm::vec3 color ) + + inline void drawLineI( const Line< CoordinateSpace::World > line, const glm::vec3 color ) + { + //Check if the line in intersecting the frustum + if ( getDebugDrawingCamera().getFrustumBounds().intersects( line ) ) + drawLine( line, glm::vec3( 0.0f, 1.0f, 0.0f ) ); + else + drawLine( line, color ); + } + + void drawBoundingBox( const BoundingBox< CoordinateSpace::World >& box, const glm::vec3 color ) { ZoneScopedC( TRACY_DRAWER_FUNC_COLOR ); for ( const auto [ p1, p2 ] : box.lines() ) { - drawLine( p1, p2, camera, color ); + drawLineI( { p1, p2 }, color ); } for ( const auto point : box.points() ) { - drawPointText( point, camera, color ); + drawPointText( point, color ); } } - inline void drawLine( const Line< CoordinateSpace::World > line, const Camera& camera, const glm::vec3 color ) + inline void drawLine( const Line< CoordinateSpace::World > line, const glm::vec3 color ) { - const Coordinate< CoordinateSpace::Screen > start_screen { toScreenSpace( line.start, camera ) }; - const Coordinate< CoordinateSpace::Screen > end_screen { toScreenSpace( line.end, camera ) }; + const Coordinate< CoordinateSpace::Screen > start_screen { toScreenSpace( line.start ) }; + const Coordinate< CoordinateSpace::Screen > end_screen { toScreenSpace( line.end ) }; if ( !inView( start_screen ) && !inView( end_screen ) ) return; - if ( isBehind( start_screen ) || isBehind( end_screen ) ) return; ImGui::GetForegroundDrawList() @@ -88,20 +109,18 @@ namespace fgl::engine::debug void drawLine( const Coordinate< CoordinateSpace::World > start, const Coordinate< CoordinateSpace::World > end, - const Camera& camera, const glm::vec3 color ) { - drawLine( { start, end }, camera, color ); + drawLine( { start, end }, color ); } - void drawPointText( - const Coordinate< CoordinateSpace::World > point, const Camera& camera, const glm::vec3 color ) + void drawPointText( const Coordinate< CoordinateSpace::World > point, const glm::vec3 color ) { - const glm::vec3 screen_point { toScreenSpace( point, camera ) }; + const glm::vec3 screen_point { toScreenSpace( point ) }; if ( !inView( screen_point ) ) return; - drawPoint( point, camera, "", color ); + drawPoint( point, "", color ); const std::string text { "World: (" + std::to_string( point.x ) + ", " + std::to_string( point.y ) + ", " + std::to_string( point.z ) + ")" }; @@ -114,16 +133,15 @@ namespace fgl::engine::debug screen::drawText( glm::vec2( screen_point.x, screen_point.y ), text2, color, glm::vec2( 0.0f, 30.0f ) ); - const Frustum frustum { camera.getFrustumBounds() }; + const Frustum frustum { getDebugDrawingCamera().getFrustumBounds() }; const bool in_view { frustum.pointInside( point ) }; - drawBoolAlpha( point, camera, in_view, glm::vec2( 0.0f, 40.0f ) ); + drawBoolAlpha( point, in_view, glm::vec2( 0.0f, 40.0f ) ); } - void drawPointLabel( - const Coordinate< CoordinateSpace::World > point, const std::string label, const Camera& camera ) + void drawPointLabel( const Coordinate< CoordinateSpace::World > point, const std::string label ) { - const glm::vec3 screen_point { toScreenSpace( point, camera ) }; + const glm::vec3 screen_point { toScreenSpace( point ) }; if ( !inView( screen_point ) ) return; @@ -131,27 +149,20 @@ namespace fgl::engine::debug } void drawPoint( - const Coordinate< CoordinateSpace::World > point, - const Camera& camera, - const std::string label, - const glm::vec3 color ) + const Coordinate< CoordinateSpace::World > point, const std::string label, const glm::vec3 color ) { - const auto screen_point { toScreenSpace( point, camera ) }; + const auto screen_point { toScreenSpace( point ) }; if ( !inView( screen_point ) ) return; ImGui::GetForegroundDrawList() ->AddCircleFilled( glmToImgui( screen_point ), 5.0f, ImColor( color.x, color.y, color.z ) ); - drawPointLabel( point, label, camera ); + drawPointLabel( point, label ); } - void drawBoolAlpha( - const Coordinate< CoordinateSpace::World > point, - const Camera& camera, - const bool value, - const glm::vec2 offset ) + void drawBoolAlpha( const Coordinate< CoordinateSpace::World > point, const bool value, const glm::vec2 offset ) { - const auto screen_point { toScreenSpace( point, camera ) }; + const auto screen_point { toScreenSpace( point ) }; const auto color { value ? glm::vec3( 0.0f, 1.0f, 0.0f ) : glm::vec3( 1.0f, 0.0f, 0.0f ) }; @@ -161,14 +172,14 @@ namespace fgl::engine::debug void drawVector( const Coordinate< CoordinateSpace::World > point, Vector vector, - const Camera& camera, const std::string label, const glm::vec3 color ) { - drawLine( point, point + glm::normalize( vector ), camera, color ); - drawPoint( point + glm::normalize( vector ), camera, label, color ); - drawPointLabel( point, label, camera ); - drawPointText( point + glm::normalize( vector ), camera ); + drawLine( point, point + glm::normalize( vector ), color ); + drawPoint( point + glm::normalize( vector ), label, color ); + drawPoint( point, "", color ); + //drawPointLabel( point, label ); + //drawPointText( point + glm::normalize( vector ) ); //Draw ending lines for the vector (two perpendicular lines) const glm::vec3 perpendicular_vector { glm::normalize( glm::cross( vector, glm::vec3( 0.0f, 1.0f, 0.0f ) ) ) @@ -179,37 +190,33 @@ namespace fgl::engine::debug drawLine( point + glm::normalize( vector ) + perpendicular_vector, point + glm::normalize( vector ) - perpendicular_vector, - camera, color ); drawLine( point + glm::normalize( vector ) + perpendicular_vector2, point + glm::normalize( vector ) - perpendicular_vector2, - camera, color ); } - void drawFrustum( - const Frustum< CoordinateSpace::World >& frustum, const Camera& camera, const WorldCoordinate point ) + void drawFrustum( const Frustum< CoordinateSpace::World >& frustum, const WorldCoordinate point ) { - drawPlane( frustum.near, point, camera, "near" ); - drawPlane( frustum.far, point, camera, "far" ); - drawPlane( frustum.top, point, camera, "top" ); - drawPlane( frustum.bottom, point, camera, "bottom" ); - drawPlane( frustum.right, point, camera, "right" ); - drawPlane( frustum.left, point, camera, "left" ); + drawPlane( frustum.near, point, "near" ); + drawPlane( frustum.far, point, "far" ); + drawPlane( frustum.top, point, "top" ); + drawPlane( frustum.bottom, point, "bottom" ); + drawPlane( frustum.right, point, "right" ); + drawPlane( frustum.left, point, "left" ); } - void drawFrustum( const Camera& camera ) + void drawFrustum() { - const Frustum frustum { camera.getFrustumBounds() }; - drawFrustum( frustum, camera, camera.getFrustumPosition() ); + const Frustum frustum { getDebugDrawingCamera().getFrustumBounds() }; + drawFrustum( frustum, getDebugDrawingCamera().getFrustumPosition() ); } void drawPlane( const Plane< CoordinateSpace::World >& plane, const WorldCoordinate point, - const Camera& camera, const std::string label, const glm::vec3 color ) { @@ -217,8 +224,8 @@ namespace fgl::engine::debug assert( point != constants::DEFAULT_VEC3 ); - drawLine( point, point + normal, camera, color ); - drawPoint( point + normal, camera, label, color ); + drawLine( point, point + normal, color ); + drawPoint( point + normal, label, color ); } } // namespace world diff --git a/src/engine/debug/drawers.hpp b/src/engine/debug/drawers.hpp index a0f0e5c..a4848da 100644 --- a/src/engine/debug/drawers.hpp +++ b/src/engine/debug/drawers.hpp @@ -33,59 +33,46 @@ namespace fgl::engine #if ENABLE_IMGUI_DRAWERS namespace fgl::engine::debug { + Camera& getDebugDrawingCamera(); + void setDebugDrawingCamera( Camera& ); namespace world { void drawBoundingBox( - const BoundingBox< CoordinateSpace::World >& box, - Camera& camera, - const glm::vec3 color = { 1.0f, 1.0f, 1.0f } ); + const BoundingBox< CoordinateSpace::World >& box, const glm::vec3 color = { 1.0f, 1.0f, 1.0f } ); void drawLine( const Coordinate< CoordinateSpace::World > start, const Coordinate< CoordinateSpace::World > end, - const Camera& camera, const glm::vec3 color = { 1.0f, 1.0f, 1.0f } ); - void drawPointLabel( - const Coordinate< CoordinateSpace::World > point, const std::string label, const Camera& camera ); + void drawPointLabel( const Coordinate< CoordinateSpace::World > point, const std::string label ); - void drawLine( - const Line< CoordinateSpace::World > line, - const Camera& camera, - const glm::vec3 color = { 1.0f, 1.0f, 1.0f } ); + void drawLineI( const Line< CoordinateSpace::World > line, const glm::vec3 color = { 1.0f, 1.0f, 1.0f } ); + void drawLine( const Line< CoordinateSpace::World > line, const glm::vec3 color = { 1.0f, 1.0f, 1.0f } ); void drawPointText( - const Coordinate< CoordinateSpace::World > point, - const Camera& camera, - const glm::vec3 color = { 1.0f, 1.0f, 1.0f } ); + const Coordinate< CoordinateSpace::World > point, const glm::vec3 color = { 1.0f, 1.0f, 1.0f } ); void drawBoolAlpha( - const Coordinate< CoordinateSpace::World > point, - const Camera& camera, - const bool value, - const glm::vec2 offset = {} ); + const Coordinate< CoordinateSpace::World > point, const bool value, const glm::vec2 offset = {} ); void drawPoint( const Coordinate< CoordinateSpace::World > point, - const Camera& camera, const std::string label = "", const glm::vec3 color = { 1.0f, 1.0f, 1.0f } ); void drawVector( const Coordinate< CoordinateSpace::World > point, Vector vector, - const Camera& camera, const std::string label = "", const glm::vec3 color = { 1.0f, 1.0f, 1.0f } ); - void drawFrustum( - const Frustum< CoordinateSpace::World >& frustum, const Camera& camera, const WorldCoordinate coordinate ); - void drawFrustum( const Camera& camera ); + void drawFrustum( const Frustum< CoordinateSpace::World >& frustum, const WorldCoordinate coordinate ); + void drawFrustum(); void drawPlane( const Plane< CoordinateSpace::World >& plane, const WorldCoordinate point, - const Camera& camera, const std::string label = "", const glm::vec3 color = { 1.0f, 1.0f, 1.0f } ); diff --git a/src/engine/math/taitBryanMatrix.cpp b/src/engine/math/taitBryanMatrix.cpp index 805b11c..ef30087 100644 --- a/src/engine/math/taitBryanMatrix.cpp +++ b/src/engine/math/taitBryanMatrix.cpp @@ -6,6 +6,7 @@ #include +#include #include #include diff --git a/src/engine/primitives/Coordinate.hpp b/src/engine/primitives/Coordinate.hpp index bb69140..8074ce1 100644 --- a/src/engine/primitives/Coordinate.hpp +++ b/src/engine/primitives/Coordinate.hpp @@ -124,4 +124,10 @@ namespace glm return Coordinate< CType >( static_cast< glm::vec3 >( lhs ) - static_cast< glm::vec3 >( rhs ) ); } + inline float + distance( const Coordinate< CoordinateSpace::World > coord, const Coordinate< CoordinateSpace::World > other ) + { + return glm::distance( static_cast< glm::vec3 >( coord ), static_cast< glm::vec3 >( other ) ); + } + } // namespace glm diff --git a/src/engine/primitives/Frustum.cpp b/src/engine/primitives/Frustum.cpp index d62d485..51f2e0b 100644 --- a/src/engine/primitives/Frustum.cpp +++ b/src/engine/primitives/Frustum.cpp @@ -4,36 +4,104 @@ #include "Frustum.hpp" +#include "engine/debug/drawers.hpp" #include "engine/model/BoundingBox.hpp" namespace fgl::engine { + float signedDistance( const Vector direction, const WorldCoordinate& point, const WorldCoordinate& origin ) + { + const glm::vec3 vector_between { point - origin }; + + float dot { glm::dot( vector_between, static_cast< glm::vec3 >( glm::normalize( direction ) ) ) }; + + return dot; + } + + void processPlane( + const Plane< CoordinateSpace::World > plane, + const Line< CoordinateSpace::World > line, + std::vector< WorldCoordinate >& out_enter_intersections, + std::vector< WorldCoordinate >& out_exit_intersections ) + { + const WorldCoordinate intersection { plane.intersection( line ) }; + + if ( std::isnan( intersection.x ) || std::isnan( intersection.y ) || std::isnan( intersection.z ) ) return; + + //! The line is entering if the line vector is pointing the same direction as the plane's vector + const bool is_line_entering { + glm::dot( glm::normalize( line.direction() ), glm::normalize( plane.direction() ) ) > 0.0f + }; + + if ( is_line_entering ) + { + // debug::world::drawVector( intersection, line.direction() ); + // debug::world::drawVector( intersection, plane.direction(), "", glm::vec3( 0.0f, 1.0f, 0.0f ) ); + out_enter_intersections.emplace_back( intersection ); + } + else + { + // debug::world::drawVector( intersection, line.direction() ); + // debug::world::drawVector( intersection, plane.direction(), "", glm::vec3( 1.0f, 0.0f, 0.0f ) ); + out_exit_intersections.emplace_back( intersection ); + } + } + template <> template <> bool Frustum< CoordinateSpace::World >::intersects( const Line< CoordinateSpace::World > line ) const { - const bool top_intersects { top.intersects( line ) }; - const bool bottom_intersects { bottom.intersects( line ) }; + std::vector< WorldCoordinate > enter_intersections { line.start }; + std::vector< WorldCoordinate > exit_intersections { line.end }; - const bool left_intersects { left.intersects( line ) }; - const bool right_intersects { right.intersects( line ) }; + processPlane( near, line, enter_intersections, exit_intersections ); + processPlane( far, line, enter_intersections, exit_intersections ); + processPlane( left, line, enter_intersections, exit_intersections ); + processPlane( right, line, enter_intersections, exit_intersections ); + processPlane( top, line, enter_intersections, exit_intersections ); + processPlane( bottom, line, enter_intersections, exit_intersections ); - const bool near_intersects { near.intersects( line ) }; - const bool far_intersects { far.intersects( line ) }; + if ( enter_intersections.size() == 0 ) return false; + if ( exit_intersections.size() == 0 ) return false; - //Check if the line passes through the frustum - const bool intersects_left_right { left_intersects && right_intersects }; - const bool intersects_top_bottom { top_intersects && bottom_intersects }; + WorldCoordinate last_enter { enter_intersections.at( 0 ) }; + float last_enter_distance { 0.0f }; - const bool line_within_near_far { !near_intersects && !far_intersects }; + WorldCoordinate first_exit { exit_intersections.at( 0 ) }; + float first_exit_distance { signedDistance( line.direction(), line.end, line.start ) }; + assert( first_exit_distance > 0.0f ); - const bool line_outside_top_bottom { !top_intersects && !bottom_intersects }; - const bool line_outside_left_right { !left_intersects && !right_intersects }; + //Determine the first exit + for ( const auto exit_point : exit_intersections ) + { + const float exit_distance { signedDistance( line.direction(), exit_point, line.start ) }; - const bool line_outside_range { line_outside_top_bottom && line_outside_left_right }; + if ( first_exit_distance > exit_distance ) + { + //The point happens before the previous exit point + first_exit_distance = exit_distance; + first_exit = exit_point; + } + } - return line_within_near_far && !( line_outside_range ) && ( intersects_top_bottom || intersects_left_right ); + for ( const auto enter_point : enter_intersections ) + { + const float enter_distance { signedDistance( line.direction(), enter_point, line.start ) }; + + if ( last_enter_distance < enter_distance ) + { + last_enter_distance = enter_distance; + last_enter = enter_point; + } + } + + const float distance_to_exit { signedDistance( line.direction(), first_exit, line.start ) }; + const float distance_to_enter { signedDistance( line.direction(), last_enter, line.start ) }; + debug::world::drawVector( last_enter, line.direction(), "", glm::vec3( 0.f, 1.f, 0.0f ) ); + debug::world::drawVector( first_exit, line.direction(), "", glm::vec3( 1.f, 0.f, 0.0f ) ); + + return distance_to_exit >= distance_to_enter; } template <> @@ -49,6 +117,7 @@ namespace fgl::engine //Slow check for checking lines for ( const auto line : box.lines() ) { + //intersects( line ); if ( intersects( line ) ) return true; } diff --git a/src/engine/primitives/Line.hpp b/src/engine/primitives/Line.hpp index 9f7a4d3..7ac6945 100644 --- a/src/engine/primitives/Line.hpp +++ b/src/engine/primitives/Line.hpp @@ -5,6 +5,7 @@ #pragma once #include "Coordinate.hpp" +#include "Vector.hpp" namespace fgl::engine { @@ -14,7 +15,13 @@ namespace fgl::engine Coordinate< CType > start; Coordinate< CType > end; + Vector direction() const { return Vector( static_cast< glm::vec3 >( end - start ) ); } + Line( const Coordinate< CType > start, const Coordinate< CType > end ) : start( start ), end( end ) {} + + Line( const glm::vec3 start, glm::vec3 end ) : start( start ), end( end ) {} + + inline Line< CType > flip() const { return { end, start }; } }; } // namespace fgl::engine diff --git a/src/engine/primitives/Plane.cpp b/src/engine/primitives/Plane.cpp index 7a39893..2aff278 100644 --- a/src/engine/primitives/Plane.cpp +++ b/src/engine/primitives/Plane.cpp @@ -4,13 +4,17 @@ #include "Plane.hpp" +#define GLM_ENABLE_EXPERIMENTAL +#include +#include + #include "Line.hpp" namespace fgl::engine { template <> - double Plane< CoordinateSpace::World >::distanceFrom( const WorldCoordinate coord ) const + float Plane< CoordinateSpace::World >::distanceFrom( const WorldCoordinate coord ) const { return glm::dot( m_direction, coord ) - m_distance; } @@ -18,7 +22,29 @@ namespace fgl::engine template <> bool OriginDistancePlane< CoordinateSpace::World >::intersects( const Line< CoordinateSpace::World > line ) const { - return isBehind( line.start ) != isBehind( line.end ); + return isForward( line.start ) != isForward( line.end ); + } + + template <> + Coordinate< CoordinateSpace::World > OriginDistancePlane< + CoordinateSpace::World >::intersection( const Line< CoordinateSpace::World > line ) const + { + const WorldCoordinate line_start { line.start }; + const Vector direction { line.direction() }; + + const float line_dot { glm::dot( this->direction(), line_start ) }; + const float direction_dot { glm::dot( this->direction(), direction ) }; + + // if the dot product of the direction of the plane and the direction of the line is zero, Then there will never be an intersection + if ( direction_dot <= std::numeric_limits< float >::epsilon() + && direction_dot >= -std::numeric_limits< float >::epsilon() ) + return { WorldCoordinate( std::numeric_limits< float >::quiet_NaN() ) }; + + const float t { -( line_dot - this->distance() ) / direction_dot }; + + const WorldCoordinate intersection_point { line.start + ( t * direction ) }; + + return intersection_point; } } // namespace fgl::engine diff --git a/src/engine/primitives/Plane.hpp b/src/engine/primitives/Plane.hpp index 495375d..a6f5758 100644 --- a/src/engine/primitives/Plane.hpp +++ b/src/engine/primitives/Plane.hpp @@ -44,7 +44,7 @@ namespace fgl::engine Coordinate< CType > getPosition() const { return Coordinate< CType >( 0.0f ) + ( m_direction * m_distance ); } //! Returns the distance from a point to the plane. Negative if behind, positive if in front - double distanceFrom( const WorldCoordinate coord ) const; + float distanceFrom( const WorldCoordinate coord ) const; bool isForward( const WorldCoordinate coord ) const { return distanceFrom( coord ) > 0.0; } @@ -61,8 +61,20 @@ namespace fgl::engine } bool intersects( const Line< CType > line ) const; + + Coordinate< CType > intersection( const Line< CType > line ) const; + + Coordinate< CType > mapToPlane( const Coordinate< CType > point ) const; }; + template < CoordinateSpace CType > + Coordinate< CType > OriginDistancePlane< CType >::mapToPlane( const Coordinate< CType > point ) const + { + const float distance { distanceFrom( point ) }; + + return point - ( this->m_direction * distance ); + } + template < CoordinateSpace CType > inline std::ostream& operator<<( std::ostream& os, const OriginDistancePlane< CType > plane ) { diff --git a/src/engine/primitives/Vector.hpp b/src/engine/primitives/Vector.hpp index fe14d2c..5162c14 100644 --- a/src/engine/primitives/Vector.hpp +++ b/src/engine/primitives/Vector.hpp @@ -75,7 +75,7 @@ namespace glm inline glm::vec3 cross( const fgl::engine::Vector vec, const glm::vec3 other ) { - return glm::cross( static_cast< glm::vec3 >( vec ), other ); + return ::glm::cross( static_cast< glm::vec3 >( vec ), other ); } template < CoordinateSpace CType > @@ -84,4 +84,9 @@ namespace glm return Coordinate< CType >( static_cast< glm::vec3 >( lhs ) + static_cast< glm::vec3 >( rhs ) ); } + inline float dot( const fgl::engine::Vector a, const fgl::engine::Vector b ) + { + return ::glm::dot( static_cast< glm::vec3 >( a ), static_cast< glm::vec3 >( b ) ); + } + } // namespace glm diff --git a/src/engine/systems/CullingSystem.cpp b/src/engine/systems/CullingSystem.cpp index cc99400..86d4bd8 100644 --- a/src/engine/systems/CullingSystem.cpp +++ b/src/engine/systems/CullingSystem.cpp @@ -36,7 +36,7 @@ namespace fgl::engine if ( obj.is_visible ) { //Draw the bounding box for debug - debug::world::drawBoundingBox( model_bounding_box, info.camera ); + debug::world::drawBoundingBox( model_bounding_box ); } } } diff --git a/src/engine/systems/CullingSystem.hpp b/src/engine/systems/CullingSystem.hpp index 5df65bc..12ac7c9 100644 --- a/src/engine/systems/CullingSystem.hpp +++ b/src/engine/systems/CullingSystem.hpp @@ -4,6 +4,8 @@ #pragma once +#include + #include "engine/FrameInfo.hpp" namespace fgl::engine diff --git a/src/engine/systems/EntityRendererSystem.cpp b/src/engine/systems/EntityRendererSystem.cpp index f84247d..9fb2346 100644 --- a/src/engine/systems/EntityRendererSystem.cpp +++ b/src/engine/systems/EntityRendererSystem.cpp @@ -90,6 +90,8 @@ namespace fgl::engine std::set< DrawPair > draw_pairs; + std::uint64_t tri_counter { 0 }; + for ( auto& [ key, obj ] : info.game_objects ) { if ( obj.model == nullptr ) continue; @@ -98,6 +100,8 @@ namespace fgl::engine for ( const auto& primitive : obj.model->m_primitives ) { + tri_counter += ( primitive.m_index_buffer.count() / 3 ); + const ModelMatrixInfo matrix_info { .model_matrix = obj.transform.mat4(), .texture_idx = primitive.m_texture->getID() }; //.normal_matrix = obj.transform.normalMatrix() }; @@ -131,6 +135,8 @@ namespace fgl::engine } } + ImGui::Text( "Tris: %lu", tri_counter ); + if ( draw_pairs.empty() ) { std::cout << "Nothing to draw!" << std::endl; diff --git a/src/engine/texture/TextureHandle.hpp b/src/engine/texture/TextureHandle.hpp index 6029e10..10ad25b 100644 --- a/src/engine/texture/TextureHandle.hpp +++ b/src/engine/texture/TextureHandle.hpp @@ -6,6 +6,9 @@ #include +#include +#include + namespace fgl::engine { class ImageView; diff --git a/src/engine/universe_constants.hpp b/src/engine/universe_constants.hpp new file mode 100644 index 0000000..39444e8 --- /dev/null +++ b/src/engine/universe_constants.hpp @@ -0,0 +1,15 @@ +// +// Created by kj16609 on 2/25/24. +// + +#pragma once + +#include "constants.hpp" + +namespace fgl::engine::uconstants +{ + + //! How fast an object falls with zero air resistance per second + constexpr float EARTH_GRAVITY { 9.80665f }; + +} // namespace fgl::engine::uconstants diff --git a/tests/src/FrustumTesting.cpp b/tests/src/FrustumTesting.cpp index a15aafa..de0237f 100644 --- a/tests/src/FrustumTesting.cpp +++ b/tests/src/FrustumTesting.cpp @@ -8,465 +8,32 @@ #define EXPOSE_CAMERA_INTERNAL #include "engine/Camera.hpp" +#include "engine/model/BoundingBox.hpp" #include "engine/primitives/Frustum.hpp" #include "gtest_printers.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.direction() == constants::WORLD_FORWARD ); - } - - THEN( "Position should be Y+ infinity" ) - { - REQUIRE( plane.getPosition() == constants::DEFAULT_VEC3 * constants::WORLD_FORWARD ); - } - } - - GIVEN( "A Plane pointing at WORLD_FORWARD with a distance of 0.5" ) - { - const Plane< CoordinateSpace::Model > plane { 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 ); - } - } - } -} - 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 camera {}; camera.setPerspectiveProjection( 90.0f, ASPECT_RATIO, constants::NEAR_PLANE, constants::FAR_PLANE ); - const auto base_frustum { camera.getBaseFrustum() }; - - GIVEN( "A default camera with a 90 deg fov and a 1920/1080 aspect ratio." ) + GIVEN( "A default frustum from a default camera" ) { - const auto frustum { camera.getFrustumBounds() }; - THEN( "The near plane should be facing WORLD_FORWARD" ) + const auto base_frustum { camera.getBaseFrustum() }; + + THEN( "The near plane should have a distance of constants::NEAR_PLANE" ) { - REQUIRE( frustum.near.direction() == constants::WORLD_FORWARD ); - REQUIRE( frustum.near.distance() == constants::NEAR_PLANE ); + REQUIRE( base_frustum.near.distance() == constants::NEAR_PLANE ); } - - THEN( "The far plane should be facing WORLD_BACKWARD" ) + THEN( "The far plane should have a distance of -constants::FAR_PLANE" ) { - REQUIRE( frustum.far.direction() == constants::WORLD_BACKWARD ); - REQUIRE( frustum.far.distance() == -constants::FAR_PLANE ); - } - } - - WHEN( "Camera is translated backwards" ) - { - camera.setView( constants::WORLD_BACKWARD, Rotation( 0.0f, 0.0f, 0.0f ) ); - //Translate backwards by 1 world unit - const auto translated_backwards { camera.getFrustumBounds() }; - - //Verify that during a translation the direction isn't changed - THEN( "Direction is the same" ) - { - REQUIRE( translated_backwards.near.direction() == base_frustum.near.direction() ); - } - - const auto matrix { camera.frustumTranslationMatrix() }; - const auto plane { camera.getBaseFrustum().near }; - - const Vector new_direction { glm::normalize( matrix * plane.direction() ) }; - const glm::vec3 new_center { matrix * plane.getPosition() }; - const float new_distance { glm::dot( new_center, static_cast< glm::vec3 >( new_direction ) ) }; - - CAPTURE( plane.direction() ); - CAPTURE( new_direction ); - CAPTURE( plane.getPosition() ); - CAPTURE( new_center ); - CAPTURE( new_distance ); - - REQUIRE( new_distance != constants::NEAR_PLANE ); - - THEN( "The near plane distance should be 1U less" ) - { - REQUIRE( translated_backwards.near.distance() == constants::NEAR_PLANE - 1.0f ); - } - - THEN( "The far plane should be translated backwards" ) - { - const glm::vec3 target_far { ( constants::WORLD_FORWARD * constants::FAR_PLANE ) - - constants::WORLD_FORWARD }; - - REQUIRE( translated_backwards.far.getPosition() == target_far ); - REQUIRE( translated_backwards.far.direction() == constants::WORLD_BACKWARD ); - REQUIRE( translated_backwards.far.distance() == -( constants::FAR_PLANE - 1.0f ) ); - // The distance for the far plane should be negative. Due to the fact - // that it is pointing toward the origin, So in order for the center to be positive - // the distance must also be negative - } - } - - WHEN( "Translated Forward" ) - { - 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() }; - - THEN( "Direction is the same" ) - { - //Verify that during a translation the direction isn't changed - REQUIRE( translated_forward.near.direction() == base_frustum.near.direction() ); - } - - THEN( "The near plane should be translated backwards" ) - { - REQUIRE( translated_forward.near.direction() == constants::WORLD_FORWARD ); - REQUIRE( translated_forward.near.distance() == constants::NEAR_PLANE + 1.0f ); - REQUIRE( - translated_forward.near.getPosition() - == ( constants::WORLD_FORWARD * ( constants::NEAR_PLANE + 1.0f ) ) ); - } - - THEN( "The far plane should be translated backwards" ) - { - REQUIRE( translated_forward.far.direction() == constants::WORLD_BACKWARD ); - REQUIRE( translated_forward.far.distance() == -( constants::FAR_PLANE + 1.0f ) ); - REQUIRE( - translated_forward.far.getPosition() - == ( constants::WORLD_FORWARD * ( constants::FAR_PLANE + 1.0f ) ) ); - } - } - - WHEN( "Translated Up" ) - { - 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() }; - - THEN( "Direction is the same" ) - { - //Verify that during a translation the direction isn't changed - REQUIRE( translated_up.near.direction() == base_frustum.near.direction() ); - } - - REQUIRE( translated_up.near.direction() == constants::WORLD_FORWARD ); - REQUIRE( translated_up.near.distance() == constants::NEAR_PLANE ); - - REQUIRE( translated_up.far.direction() == constants::WORLD_BACKWARD ); - REQUIRE( translated_up.far.distance() == -constants::FAR_PLANE ); - } - - const Frustum< fgl::engine::CoordinateSpace::World > frustum { Matrix< MatrixType::ModelToWorld > { 1.0f } - * camera.getBaseFrustum() }; - - WHEN( "A point is above the frustum" ) - { - const WorldCoordinate far_up { constants::WORLD_UP * 1000.0f }; - - THEN( "The point should be outside the frustum" ) - { - REQUIRE_FALSE( frustum.pointInside( far_up ) ); - } - - THEN( "The point should be infront of the bottom plane" ) - { - REQUIRE( frustum.bottom.distanceFrom( far_up ) > 500.0f ); - } - - THEN( "The point should be behind the top plane" ) - { - REQUIRE( frustum.top.distanceFrom( far_up ) < -500.0f ); - } - } - - WHEN( "A point is below the frustum" ) - { - const WorldCoordinate far_down { constants::WORLD_DOWN * 1000.0f }; - - THEN( "The point should be outside the frustum" ) - { - REQUIRE_FALSE( frustum.pointInside( far_down ) ); - } - - THEN( "The point should be infront of the top plane" ) - { - REQUIRE( frustum.top.distanceFrom( far_down ) > 500.0f ); - } - - THEN( "The point should be behind the bottom plane" ) - { - REQUIRE( frustum.bottom.distanceFrom( far_down ) < -500.0f ); - } - } - - //The point FORWARD should be in the frustum - WHEN( "A point infront of the frustum" ) - { - GIVEN( "A distance of 2 units" ) - { - const WorldCoordinate point { constants::WORLD_FORWARD * 2.0f }; - - THEN( "The point should be inside the frustum" ) - { - REQUIRE( frustum.pointInside( point ) ); - } - } - - GIVEN( "A distance of 500 units" ) - { - const WorldCoordinate point { constants::WORLD_FORWARD * 500.0f }; - - THEN( "The point should be behind the far plane" ) - { - CAPTURE( frustum.far.distanceFrom( point ) ); - REQUIRE( frustum.far.distanceFrom( point ) < 0.0f ); - REQUIRE_FALSE( frustum.far.isForward( point ) ); - } - - THEN( "The point should be outside the frustum" ) - { - REQUIRE_FALSE( frustum.pointInside( point ) ); - } - } - } - - //A point FORWARD and 0.2 units to the left should be farther from the right plane - GIVEN( "A point FORWARD and 0.2 units to the left" ) - { - const WorldCoordinate point { ( constants::WORLD_FORWARD * 2.0f ) + constants::WORLD_LEFT * 0.2f }; - - THEN( "The point should be farther from the right plane" ) - { - REQUIRE( frustum.right.distanceFrom( point ) > frustum.left.distanceFrom( point ) ); - } - - THEN( "The point should be in the frustum" ) - { - REQUIRE( frustum.pointInside( point ) ); - } - } - - //A point FORWARD and 0.2 units to the right should be farther from the left plane - GIVEN( "A point FORWARD and 0.2 units to the right" ) - { - const WorldCoordinate point { ( constants::WORLD_FORWARD * 2.0f ) + constants::WORLD_RIGHT * 0.2f }; - - THEN( "The point should be farther from the left plane" ) - { - REQUIRE( frustum.left.distanceFrom( point ) > frustum.right.distanceFrom( point ) ); - } - - THEN( "The point should be in the frustum" ) - { - REQUIRE( frustum.pointInside( point ) ); - } - } - - //A point FORWARD and down 0.2 units should be closer to the bottom plane - GIVEN( "A point FORWARD and down 0.2 units" ) - { - const WorldCoordinate point { ( constants::WORLD_FORWARD * 2.0f ) + ( constants::WORLD_DOWN * 0.2f ) }; - - THEN( "The point should be closer to the bottom plane" ) - { - REQUIRE( frustum.bottom.distanceFrom( point ) < frustum.top.distanceFrom( point ) ); - } - - THEN( "The point should be in the frustum" ) - { - REQUIRE( frustum.pointInside( point ) ); - } - } - - SECTION( "Rotations" ) - { - //Testing rotation of the camera - SECTION( "Pitch" ) - { - Rotation rotation { 0.0f, 0.0f, 0.0f }; - rotation.pitch() -= glm::radians( 90.0f ); - - camera.setView( constants::CENTER, rotation ); - - const auto rotated_frustum = camera.getFrustumBounds(); - - //The point DOWN should be in the frustum - const WorldCoordinate point { constants::WORLD_DOWN * 2.0f }; - - //NEAR should be looking down or approaching - //FAR should be looking up or away - - 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 ); - - REQUIRE( rotated_frustum.top.distanceFrom( point ) > 0.0f ); - REQUIRE( rotated_frustum.bottom.distanceFrom( point ) > 0.0f ); - - REQUIRE( rotated_frustum.right.distanceFrom( point ) > 0.0f ); - REQUIRE( rotated_frustum.left.distanceFrom( point ) > 0.0f ); - - REQUIRE( rotated_frustum.pointInside( point ) ); - } - - SECTION( "Yaw" ) - { - Rotation rotation { 0.0f, 0.0f, 0.0f }; - rotation.yaw() += glm::radians( 90.0f ); - - camera.setView( constants::CENTER, rotation ); - - const auto rotated_frustum = camera.getFrustumBounds(); - - //The point RIGHT should be in the frustum - const WorldCoordinate point { constants::WORLD_RIGHT * 2.0f }; - - //NEAR should be looking right or approaching - //FAR should be looking left or away - - //Precision issues. So I can't write the tests yet - - REQUIRE( rotated_frustum.near.distanceFrom( point ) > 0.0f ); - REQUIRE( rotated_frustum.far.distanceFrom( point ) > 0.0f ); - - REQUIRE( rotated_frustum.top.distanceFrom( point ) > 0.0f ); - REQUIRE( rotated_frustum.bottom.distanceFrom( point ) > 0.0f ); - - REQUIRE( rotated_frustum.right.distanceFrom( point ) > 0.0f ); - REQUIRE( rotated_frustum.left.distanceFrom( point ) > 0.0f ); - - REQUIRE( rotated_frustum.pointInside( point ) ); - - //LEFT should be behind, Thus out outside - REQUIRE_FALSE( rotated_frustum.pointInside( WorldCoordinate( constants::WORLD_LEFT ) ) ); - } - - SECTION( "Roll" ) - { - Rotation rotation { 0.0f, 0.0f, 0.0f }; - rotation.roll() -= glm::radians( 90.0f ); - - camera.setView( constants::CENTER, rotation ); - - const auto rotated_frustum = camera.getFrustumBounds(); - - //The point FORWARD should be in the frustum - const WorldCoordinate point { constants::WORLD_FORWARD * 2.0f }; - - REQUIRE( rotated_frustum.near.distanceFrom( point ) > 0.0f ); - REQUIRE( rotated_frustum.far.distanceFrom( point ) > 0.0f ); - - REQUIRE( rotated_frustum.top.distanceFrom( point ) > 0.0f ); - REQUIRE( rotated_frustum.bottom.distanceFrom( point ) > 0.0f ); - - REQUIRE( rotated_frustum.right.distanceFrom( point ) > 0.0f ); - REQUIRE( rotated_frustum.left.distanceFrom( point ) > 0.0f ); - - REQUIRE( rotated_frustum.pointInside( point ) ); + REQUIRE( base_frustum.far.distance() == -constants::FAR_PLANE ); } } } \ No newline at end of file diff --git a/tests/src/PlaneTests.cpp b/tests/src/PlaneTests.cpp new file mode 100644 index 0000000..3df75d8 --- /dev/null +++ b/tests/src/PlaneTests.cpp @@ -0,0 +1,208 @@ +// +// Created by kj16609 on 2/25/24. +// + +#include + +#include + +#define EXPOSE_CAMERA_INTERNAL +#include "engine/Camera.hpp" +#include "engine/primitives/Frustum.hpp" +#include "gtest_printers.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.direction() == constants::WORLD_FORWARD ); + } + + THEN( "Position should be Y+ infinity" ) + { + REQUIRE( plane.getPosition() == constants::DEFAULT_VEC3 * constants::WORLD_FORWARD ); + } + } + + GIVEN( "A Plane pointing at WORLD_FORWARD with a distance of 0.5" ) + { + const Plane< CoordinateSpace::Model > plane { 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 Line< 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 ), 0.0f }; + + THEN( "The line should intersect" ) + { + REQUIRE( plane.intersects( line ) ); + + 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 Line< 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 Line< 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 ) ); + } + } + } +}