More progress on getting frustums working
This commit is contained in:
@@ -30,6 +30,7 @@ include(dependencies/glfw)
|
||||
include(dependencies/glm)
|
||||
include(dependencies/tracy)
|
||||
include(dependencies/vulkan)
|
||||
include(dependencies/catch2)
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(tests)
|
||||
|
||||
1
cmake_modules/dependencies/catch2.cmake
Normal file
1
cmake_modules/dependencies/catch2.cmake
Normal file
@@ -0,0 +1 @@
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/dependencies/catch2)
|
||||
11
cmake_modules/dependencies/gtest.cmake
Normal file
11
cmake_modules/dependencies/gtest.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
include(FetchContent)
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
|
||||
)
|
||||
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
1
dependencies/catch2
vendored
Submodule
1
dependencies/catch2
vendored
Submodule
Submodule dependencies/catch2 added at b817497528
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "Camera.hpp"
|
||||
|
||||
#include "GameObject.hpp"
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
@@ -27,7 +29,7 @@ namespace fgl::engine
|
||||
ZoneScoped;
|
||||
projection_matrix = Matrix< MatrixType::CameraToScreen >( glm::perspectiveLH( fovy, aspect, near, far ) );
|
||||
|
||||
base_frustum = createFrustum( *this, aspect, fovy, near, far );
|
||||
base_frustum = createFrustum( aspect, fovy, near, far );
|
||||
}
|
||||
|
||||
void Camera::setViewDirection( glm::vec3 position, const Vector direction, glm::vec3 up )
|
||||
@@ -250,14 +252,6 @@ namespace fgl::engine
|
||||
{
|
||||
static auto current_rotation_order { RotationOrder::DEFAULT };
|
||||
|
||||
ImGui::Begin( "CameraRotation" );
|
||||
ImGui::SliderInt(
|
||||
"Rotation Order",
|
||||
reinterpret_cast< int* >( ¤t_rotation_order ),
|
||||
0,
|
||||
static_cast< int >( RotationOrder::END_OF_ENUM - 1 ) );
|
||||
ImGui::End();
|
||||
|
||||
const glm::mat4 rotation_matrix { taitBryanMatrix( rotation, current_rotation_order ) };
|
||||
|
||||
const glm::vec3 forward { rotation_matrix * glm::vec4( constants::WORLD_FORWARD, 0.0f ) };
|
||||
@@ -278,34 +272,52 @@ namespace fgl::engine
|
||||
throw std::runtime_error( "Unimplemented view mode" );
|
||||
}
|
||||
|
||||
frustum = frustumTranslationMatrix() * base_frustum;
|
||||
TransformComponent transform { WorldCoordinate( position ), glm::vec3( 1.0f, 1.0f, 1.0f ), rotation };
|
||||
|
||||
return;
|
||||
if ( update_frustums )
|
||||
{
|
||||
if ( update_using_alt ) [[unlikely]]
|
||||
{
|
||||
frustum = frustum_alt_transform.mat() * base_frustum;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
frustum = transform.mat() * base_frustum;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Frustum< CoordinateSpace::Model >
|
||||
createFrustum( const Camera& camera, const float aspect, const float fov_y, const float near, const float far )
|
||||
createFrustum( const float aspect, const float fov_y, const float near, const float far )
|
||||
{
|
||||
Plane< CoordinateSpace::Model > near_plane { camera.getForward(), near };
|
||||
Plane< CoordinateSpace::Model > far_plane { camera.getBackward(), far };
|
||||
const Plane< CoordinateSpace::Model > near_plane { constants::WORLD_FORWARD, near };
|
||||
const Plane< CoordinateSpace::Model > far_plane { constants::WORLD_BACKWARD, -far };
|
||||
|
||||
const float half_width { near * glm::tan( fov_y / 2.0f ) }; // halfHSide
|
||||
const float half_height { half_width / aspect }; //halfVSide
|
||||
const float half_height { far * glm::tan( fov_y / 2.0f ) };
|
||||
const float half_width { half_height * aspect };
|
||||
|
||||
constexpr glm::vec3 ZERO { 0.0f, 0.0f, 0.0f };
|
||||
const ModelCoordinate far_forward { constants::WORLD_FORWARD * far };
|
||||
const ModelCoordinate right_half { constants::WORLD_RIGHT * half_width };
|
||||
|
||||
const auto far_forward { camera.getForward() * far };
|
||||
const Vector right_forward { far_forward + right_half };
|
||||
const Vector left_forward { far_forward - right_half };
|
||||
|
||||
//top_dir is the direction pointing at the highest point on the far plane
|
||||
const auto far_up { camera.getUp() * half_height };
|
||||
const glm::vec3 top_dir { glm::normalize( far_up + far_forward ) };
|
||||
const Plane< CoordinateSpace::Model > right_plane { glm::cross( right_forward, constants::WORLD_DOWN ), 0.0f };
|
||||
const Plane< CoordinateSpace::Model > left_plane { glm::cross( left_forward, constants::WORLD_UP ), 0.0f };
|
||||
|
||||
Plane< CoordinateSpace::Model > top_plane { glm::cross( top_dir, camera.getUp() ), 0.0f };
|
||||
Plane< CoordinateSpace::Model > bottom_plane { glm::cross( top_dir, camera.getDown() ), 0.0f };
|
||||
const ModelCoordinate top_half { constants::WORLD_UP * half_height };
|
||||
|
||||
const glm::vec3 right_dir { glm::normalize( camera.getRight() * half_width + far_forward ) };
|
||||
Plane< CoordinateSpace::Model > right_plane { glm::cross( right_dir, camera.getRight() ), 0.0f };
|
||||
Plane< CoordinateSpace::Model > left_plane { glm::cross( right_dir, camera.getLeft() ), 0.0f };
|
||||
const Vector top_forward { far_forward + top_half };
|
||||
const Vector bottom_forward { far_forward - top_half };
|
||||
|
||||
const Plane< CoordinateSpace::Model > top_plane { glm::cross( top_forward, constants::WORLD_RIGHT ), 0.0f };
|
||||
|
||||
const Plane< CoordinateSpace::Model > bottom_plane { glm::cross( bottom_forward, constants::WORLD_LEFT ),
|
||||
0.0f };
|
||||
|
||||
std::cout << bottom_plane.direction() << std::endl;
|
||||
|
||||
return { near_plane, far_plane, top_plane, bottom_plane, right_plane, left_plane };
|
||||
}
|
||||
@@ -314,14 +326,17 @@ namespace fgl::engine
|
||||
{
|
||||
glm::mat4 translation { 1.0f };
|
||||
|
||||
translation[ 3 ] = glm::vec4( getPosition(), 1.0f );
|
||||
|
||||
//Apply rotation
|
||||
translation[ 0 ] = glm::vec4( getRight(), 0.0f );
|
||||
translation[ 1 ] = glm::vec4( getUp(), 0.0f );
|
||||
translation[ 2 ] = glm::vec4( getForward(), 0.0f );
|
||||
translation = glm::translate( glm::mat4( 1.0f ), getPosition() );
|
||||
|
||||
return Matrix< MatrixType::ModelToWorld >( translation );
|
||||
}
|
||||
|
||||
WorldCoordinate Camera::getFrustumPosition() const
|
||||
{
|
||||
if ( update_using_alt ) [[unlikely]]
|
||||
return frustum_alt_transform.translation;
|
||||
else
|
||||
return getPosition();
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -4,22 +4,31 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
#include "constants.hpp"
|
||||
#include "engine/primitives/Coordinate.hpp"
|
||||
#include "engine/primitives/Frustum.hpp"
|
||||
#include "engine/primitives/Matrix.hpp"
|
||||
#include "engine/primitives/TransformComponent.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
class Camera;
|
||||
|
||||
Frustum< CoordinateSpace::Model >
|
||||
createFrustum( const Camera& camera, const float aspect, const float fovy, const float near, const float far );
|
||||
createFrustum( const float aspect, const float fovy, const float near, const float far );
|
||||
|
||||
class Camera
|
||||
{
|
||||
#ifdef EXPOSE_CAMERA_INTERNAL
|
||||
|
||||
public:
|
||||
|
||||
#endif
|
||||
|
||||
Matrix< MatrixType::CameraToScreen > projection_matrix { 1.0f };
|
||||
|
||||
Matrix< MatrixType::WorldToCamera > view_matrix { 1.0f };
|
||||
@@ -29,13 +38,25 @@ namespace fgl::engine
|
||||
Frustum< CoordinateSpace::Model > base_frustum {};
|
||||
Frustum< CoordinateSpace::World > frustum {};
|
||||
|
||||
friend Frustum< CoordinateSpace::Model > createFrustum(
|
||||
const Camera& camera, const float aspect, const float fovy, const float near, const float far );
|
||||
|
||||
const Matrix< MatrixType::ModelToWorld > frustumTranslationMatrix() const;
|
||||
|
||||
public:
|
||||
|
||||
inline static TransformComponent frustum_alt_transform { WorldCoordinate( constants::WORLD_CENTER ),
|
||||
glm::vec3( 1.0f ),
|
||||
Vector( 0.0f, 0.0f, 0.0f ) };
|
||||
|
||||
inline static bool update_frustums { true };
|
||||
inline static bool update_using_alt { false };
|
||||
|
||||
Camera()
|
||||
{
|
||||
setPerspectiveProjection( 90.0f, 16.0f / 9.0f, constants::NEAR_PLANE, constants::FAR_PLANE );
|
||||
setViewYXZ( constants::CENTER, Vector( 0.0f, 0.0f, 0.0f ) );
|
||||
}
|
||||
|
||||
WorldCoordinate getFrustumPosition() const;
|
||||
|
||||
const Frustum< CoordinateSpace::Model >& getBaseFrustum() const { return base_frustum; }
|
||||
|
||||
//! Returns the frustum of the camera in world space
|
||||
@@ -64,7 +85,7 @@ namespace fgl::engine
|
||||
const Vector getRight() const
|
||||
{
|
||||
return Vector(
|
||||
glm::normalize( glm::vec3( view_matrix[ 0 ][ 0 ], view_matrix[ 1 ][ 0 ], view_matrix[ 2 ][ 0 ] ) ) );
|
||||
glm::normalize( glm::vec3( -view_matrix[ 0 ][ 0 ], view_matrix[ 1 ][ 0 ], view_matrix[ 2 ][ 0 ] ) ) );
|
||||
}
|
||||
|
||||
const Vector getForward() const
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace fgl::engine
|
||||
|
||||
//camera.setOrthographicProjection( -aspect, aspect, -1, 1, -1, 1 );
|
||||
const float aspect { m_renderer.getAspectRatio() };
|
||||
camera.setPerspectiveProjection( glm::radians( 90.0f ), aspect, 0.1f, 100.f );
|
||||
camera.setPerspectiveProjection( glm::radians( 90.0f ), aspect, constants::NEAR_PLANE, constants::FAR_PLANE );
|
||||
|
||||
const auto old_aspect_ratio { m_renderer.getAspectRatio() };
|
||||
|
||||
@@ -142,12 +142,22 @@ namespace fgl::engine
|
||||
|
||||
if ( old_aspect_ratio != m_renderer.getAspectRatio() )
|
||||
{
|
||||
camera.setPerspectiveProjection( glm::radians( 90.0f ), m_renderer.getAspectRatio(), 0.1f, 100.f );
|
||||
camera.setPerspectiveProjection(
|
||||
glm::radians( 90.0f ), m_renderer.getAspectRatio(), constants::NEAR_PLANE, constants::FAR_PLANE );
|
||||
}
|
||||
|
||||
camera_controller.moveInPlaneXZ( m_window.window(), delta_time, viewer );
|
||||
camera.setViewYXZ( viewer.transform.translation, viewer.transform.rotation );
|
||||
|
||||
{
|
||||
constexpr WorldCoordinate center { 0.0f, 0.0f, 0.0f };
|
||||
//debug::world::drawVector( center, camera.getForward(), camera, { 0.0f, 0.0f, 0.0f } );
|
||||
|
||||
{
|
||||
debug::world::drawFrustum( camera );
|
||||
}
|
||||
}
|
||||
|
||||
if ( auto command_buffer = m_renderer.beginFrame(); command_buffer )
|
||||
{
|
||||
ZoneScopedN( "Render" );
|
||||
@@ -192,6 +202,20 @@ namespace fgl::engine
|
||||
ImGui::Text( "%.3f ms", 1000.0f / ImGui::GetIO().Framerate );
|
||||
ImGui::Text( "Average rolling frametime: %.3f ms", rolling_ms_average.average() );
|
||||
|
||||
auto inputVec3 = []( const std::string label, glm::vec3& vec )
|
||||
{
|
||||
ImGui::PushID( label.c_str() );
|
||||
ImGui::PushItemWidth( 80 );
|
||||
ImGui::Text( label.c_str() );
|
||||
ImGui::DragFloat( "X", &vec.x, 0.1f );
|
||||
ImGui::SameLine();
|
||||
ImGui::DragFloat( "Y", &vec.y, 0.1f );
|
||||
ImGui::SameLine();
|
||||
ImGui::DragFloat( "Z", &vec.z, 0.1f );
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::PopID();
|
||||
};
|
||||
|
||||
if ( ImGui::CollapsingHeader( "Camera" ) )
|
||||
{
|
||||
ImGui::PushItemWidth( 80 );
|
||||
@@ -211,6 +235,19 @@ namespace fgl::engine
|
||||
ImGui::SameLine();
|
||||
ImGui::DragFloat( "Rot Z", &viewer.transform.rotation.z, 0.1f, 0.0f, glm::two_pi< float >() );
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Checkbox( "Update Frustum", &camera.update_frustums );
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Checkbox( "Use Alt Frustum matrix", &camera.update_using_alt );
|
||||
if ( ImGui::CollapsingHeader( "Frustum matrix", &camera.update_using_alt ) )
|
||||
{
|
||||
ImGui::PushID( "FrustumMatrix" );
|
||||
inputVec3( "Translation", Camera::frustum_alt_transform.translation );
|
||||
inputVec3( "Rotation", Camera::frustum_alt_transform.rotation );
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
if ( ImGui::CollapsingHeader( "View Frustum" ) )
|
||||
@@ -228,7 +265,9 @@ namespace fgl::engine
|
||||
ImGui::SameLine( 120.0f );
|
||||
printVec3( plane.direction() );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text( "Distance: %.6f", plane.distance() );
|
||||
ImGui::Text( "Distance: %.3f", plane.distance() );
|
||||
const auto pos { plane.getPosition() };
|
||||
ImGui::Text( "Center: %.2f %2.f %2.f", pos.x, pos.y, pos.z );
|
||||
};
|
||||
|
||||
printPlane( frustum.near, "Near" );
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace fgl::engine
|
||||
{ scale.x * ( c1 * c3 + s1 * s2 * s3 ), scale.x * ( c2 * s3 ), scale.x * ( c1 * s2 * s3 - c3 * s1 ), 0.0f },
|
||||
{ scale.y * ( c3 * s1 * s2 - c1 * s3 ), scale.y * ( c2 * c3 ), scale.y * ( c1 * c3 * s2 + s1 * s3 ), 0.0f },
|
||||
{ scale.z * ( c2 * s1 ), scale.z * ( -s2 ), scale.z * ( c1 * c2 ), 0.0f },
|
||||
{ translation.x, translation.y, translation.z, 1.0f }
|
||||
{ -translation.x, translation.y, -translation.z, 1.0f }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "constants.hpp"
|
||||
#include "engine/primitives/Matrix.hpp"
|
||||
#include "engine/primitives/TransformComponent.hpp"
|
||||
#include "engine/primitives/Vector.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
@@ -17,19 +19,6 @@ namespace fgl::engine
|
||||
|
||||
class Model;
|
||||
|
||||
//TransformComponent is always in world space
|
||||
struct TransformComponent
|
||||
{
|
||||
WorldCoordinate translation { constants::DEFAULT_VEC3 };
|
||||
glm::vec3 scale { 1.0f, 1.0f, 1.0f };
|
||||
Vector rotation { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
//TODO: Figure this out and replace TransformComponent with a template of CType instead
|
||||
glm::mat4 mat4() const;
|
||||
|
||||
glm::mat3 normalMatrix() const;
|
||||
};
|
||||
|
||||
class GameObject
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -27,4 +27,8 @@ namespace fgl::engine::constants
|
||||
|
||||
constexpr float DEFAULT_FLOAT { std::numeric_limits< float >::max() };
|
||||
|
||||
constexpr float NEAR_PLANE { 0.1f };
|
||||
constexpr float FAR_PLANE { 5.0f };
|
||||
constexpr glm::vec3 CENTER { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
} // namespace fgl::engine::constants
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace fgl::engine::debug
|
||||
const ImVec2 window_size { windowSize() };
|
||||
|
||||
world_point.z = -world_point.z;
|
||||
world_point.x = -world_point.x;
|
||||
|
||||
return Coordinate< CoordinateSpace::Screen >( glm::project(
|
||||
static_cast< glm::vec3 >( world_point ),
|
||||
@@ -101,7 +102,7 @@ namespace fgl::engine::debug
|
||||
|
||||
if ( !inView( screen_point ) ) return;
|
||||
|
||||
drawPoint( point, camera, color );
|
||||
drawPoint( point, camera, "", color );
|
||||
|
||||
const std::string text { "World: (" + std::to_string( point.x ) + ", " + std::to_string( point.y ) + ", "
|
||||
+ std::to_string( point.z ) + ")" };
|
||||
@@ -120,7 +121,22 @@ namespace fgl::engine::debug
|
||||
drawBoolAlpha( point, camera, in_view, glm::vec2( 0.0f, 40.0f ) );
|
||||
}
|
||||
|
||||
void drawPoint( const Coordinate< CoordinateSpace::World > point, const Camera& camera, const glm::vec3 color )
|
||||
void drawPointLabel(
|
||||
const Coordinate< CoordinateSpace::World > point, const std::string label, const Camera& camera )
|
||||
{
|
||||
ZoneScoped;
|
||||
const glm::vec3 screen_point { toScreenSpace( point, camera ) };
|
||||
|
||||
if ( !inView( screen_point ) ) return;
|
||||
|
||||
screen::drawText( glm::vec2( screen_point.x, screen_point.y ), label );
|
||||
}
|
||||
|
||||
void drawPoint(
|
||||
const Coordinate< CoordinateSpace::World > point,
|
||||
const Camera& camera,
|
||||
const std::string label,
|
||||
const glm::vec3 color )
|
||||
{
|
||||
ZoneScoped;
|
||||
const auto screen_point { toScreenSpace( point, camera ) };
|
||||
@@ -128,6 +144,8 @@ namespace fgl::engine::debug
|
||||
|
||||
ImGui::GetForegroundDrawList()
|
||||
->AddCircleFilled( glmToImgui( screen_point ), 5.0f, ImColor( color.x, color.y, color.z ) );
|
||||
|
||||
drawPointLabel( point, label, camera );
|
||||
}
|
||||
|
||||
void drawBoolAlpha(
|
||||
@@ -146,12 +164,15 @@ namespace fgl::engine::debug
|
||||
|
||||
void drawVector(
|
||||
const Coordinate< CoordinateSpace::World > point,
|
||||
const Vector vector,
|
||||
Vector vector,
|
||||
const Camera& camera,
|
||||
const std::string label,
|
||||
const glm::vec3 color )
|
||||
{
|
||||
drawLine( point, point + vector.norm(), camera, color );
|
||||
drawPoint( point + vector.norm(), camera, 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 );
|
||||
|
||||
//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 ) ) )
|
||||
@@ -160,26 +181,49 @@ namespace fgl::engine::debug
|
||||
/ 4.0f };
|
||||
|
||||
drawLine(
|
||||
point + vector.norm() + perpendicular_vector,
|
||||
point + vector.norm() - perpendicular_vector,
|
||||
point + glm::normalize( vector ) + perpendicular_vector,
|
||||
point + glm::normalize( vector ) - perpendicular_vector,
|
||||
camera,
|
||||
color );
|
||||
|
||||
drawLine(
|
||||
point + vector.norm() + perpendicular_vector2,
|
||||
point + vector.norm() - perpendicular_vector2,
|
||||
point + glm::normalize( vector ) + perpendicular_vector2,
|
||||
point + glm::normalize( vector ) - perpendicular_vector2,
|
||||
camera,
|
||||
color );
|
||||
}
|
||||
|
||||
void drawFrustum( const Frustum< CoordinateSpace::World >& frustum, const Camera& camera )
|
||||
void drawFrustum(
|
||||
const Frustum< CoordinateSpace::World >& frustum, const Camera& camera, const WorldCoordinate point )
|
||||
{
|
||||
drawPlane( frustum.near, camera );
|
||||
drawPlane( frustum.far, camera );
|
||||
drawPlane( frustum.top, camera );
|
||||
drawPlane( frustum.bottom, camera );
|
||||
drawPlane( frustum.right, camera );
|
||||
drawPlane( frustum.left, camera );
|
||||
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" );
|
||||
}
|
||||
|
||||
void drawFrustum( const Camera& camera )
|
||||
{
|
||||
const Frustum frustum { camera.getFrustumBounds() };
|
||||
drawFrustum( frustum, camera, camera.getFrustumPosition() );
|
||||
}
|
||||
|
||||
void drawPlane(
|
||||
const Plane< CoordinateSpace::World >& plane,
|
||||
const WorldCoordinate point,
|
||||
const Camera& camera,
|
||||
const std::string label,
|
||||
const glm::vec3 color )
|
||||
{
|
||||
ZoneScoped;
|
||||
const auto normal { plane.direction() };
|
||||
|
||||
assert( point != constants::DEFAULT_VEC3 );
|
||||
|
||||
drawLine( point, point + normal, camera, color );
|
||||
drawPoint( point + normal, camera, label, color );
|
||||
}
|
||||
|
||||
void drawCameraInfo( const Camera& camera )
|
||||
@@ -191,9 +235,7 @@ namespace fgl::engine::debug
|
||||
*/
|
||||
|
||||
{
|
||||
const Frustum frustum { camera.getFrustumBounds() };
|
||||
|
||||
drawFrustum( frustum, camera );
|
||||
drawFrustum( camera );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -211,17 +253,6 @@ namespace fgl::engine::debug
|
||||
*/
|
||||
}
|
||||
|
||||
void drawPlane( const Plane< CoordinateSpace::World >& plane, const Camera& camera, const glm::vec3 color )
|
||||
{
|
||||
assert( plane.direction() != constants::DEFAULT_VEC3 );
|
||||
assert( plane.getPosition() != constants::DEFAULT_VEC3 );
|
||||
|
||||
const auto pos { plane.getPosition() };
|
||||
const auto dir { plane.direction() };
|
||||
|
||||
drawVector( pos, dir, camera, color );
|
||||
}
|
||||
|
||||
} // namespace world
|
||||
|
||||
namespace screen
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "engine/primitives/Coordinate.hpp"
|
||||
#include "engine/primitives/Frustum.hpp"
|
||||
#include "engine/primitives/Line.hpp"
|
||||
#include "engine/primitives/Plane.hpp"
|
||||
|
||||
@@ -41,6 +42,9 @@ namespace fgl::engine::debug
|
||||
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 drawLine(
|
||||
const Line< CoordinateSpace::World > line,
|
||||
const Camera& camera,
|
||||
@@ -59,19 +63,25 @@ namespace fgl::engine::debug
|
||||
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,
|
||||
const glm::vec3 vector,
|
||||
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 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 } );
|
||||
|
||||
} // namespace world
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "engine/constants.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
@@ -36,11 +38,11 @@ namespace fgl::engine
|
||||
|
||||
Coordinate() : glm::vec3( constants::DEFAULT_VEC3 ) {}
|
||||
|
||||
explicit Coordinate( const glm::vec3 position ) : glm::vec3( position ) {}
|
||||
constexpr explicit Coordinate( const glm::vec3 position ) : glm::vec3( position ) {}
|
||||
|
||||
explicit Coordinate( const float x, const float y, const float z ) : glm::vec3( x, y, z ) {}
|
||||
constexpr explicit Coordinate( const float x, const float y, const float z ) : glm::vec3( x, y, z ) {}
|
||||
|
||||
explicit Coordinate( const float value ) : glm::vec3( value ) {}
|
||||
constexpr explicit Coordinate( const float value ) : glm::vec3( value ) {}
|
||||
|
||||
operator glm::vec4() const { return glm::vec4( x, y, z, 1.0f ); }
|
||||
|
||||
@@ -70,6 +72,13 @@ namespace fgl::engine
|
||||
|
||||
static_assert( sizeof( glm::vec3 ) == sizeof( ModelCoordinate ) );
|
||||
|
||||
template < CoordinateSpace CType >
|
||||
inline ::std::ostream& operator<<( ::std::ostream& os, const Coordinate< CType > coord )
|
||||
{
|
||||
os << "(" << coord.x << ", " << coord.y << ", " << coord.z << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
|
||||
namespace glm
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
class Camera;
|
||||
|
||||
template < CoordinateSpace CType = CoordinateSpace::World >
|
||||
struct Frustum
|
||||
{
|
||||
@@ -55,7 +57,7 @@ namespace fgl::engine
|
||||
assert( near_plane.direction() != far_plane.direction() );
|
||||
}
|
||||
|
||||
bool pointInside( const WorldCoordinate& coord ) const
|
||||
bool pointInside( const WorldCoordinate coord ) const
|
||||
{
|
||||
static_assert(
|
||||
CType == CoordinateSpace::World, "pointInside can only be called on World coordinate Frustums" );
|
||||
@@ -63,8 +65,27 @@ namespace fgl::engine
|
||||
return near.isForward( coord ) && far.isForward( coord ) && top.isForward( coord )
|
||||
&& bottom.isForward( coord ) && right.isForward( coord ) && left.isForward( coord );
|
||||
}
|
||||
|
||||
bool operator==( const Frustum< CType >& other ) const
|
||||
{
|
||||
return near == other.near && far == other.far && top == other.top && bottom == other.bottom
|
||||
&& right == other.right && left == other.left;
|
||||
}
|
||||
};
|
||||
|
||||
template < CoordinateSpace CType >
|
||||
inline std::ostream& operator<<( std::ostream& os, const Frustum< CType >& frustum )
|
||||
{
|
||||
os << "Frustum: " << std::endl;
|
||||
os << "\tNear: " << frustum.near << std::endl;
|
||||
os << "\tFar: " << frustum.far << std::endl;
|
||||
os << "\tTop: " << frustum.top << std::endl;
|
||||
os << "\tBottom: " << frustum.bottom << std::endl;
|
||||
os << "\tRight: " << frustum.right << std::endl;
|
||||
os << "\tLeft: " << frustum.left << std::endl;
|
||||
return os;
|
||||
}
|
||||
|
||||
template < CoordinateSpace CType, MatrixType MType >
|
||||
Frustum< EvolvedType< MType >() > operator*( const Matrix< MType >& matrix, const Frustum< CType >& frustum )
|
||||
{
|
||||
|
||||
16
src/engine/primitives/Plane.cpp
Normal file
16
src/engine/primitives/Plane.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Created by kj16609 on 2/16/24.
|
||||
//
|
||||
|
||||
#include "Plane.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
template <>
|
||||
double Plane< CoordinateSpace::World >::distanceFrom( const WorldCoordinate coord ) const
|
||||
{
|
||||
return glm::dot( m_direction, coord ) - m_distance;
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -9,6 +9,9 @@
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include "Coordinate.hpp"
|
||||
#include "Matrix.hpp"
|
||||
#include "Vector.hpp"
|
||||
@@ -18,7 +21,7 @@ namespace fgl::engine
|
||||
{
|
||||
|
||||
template < CoordinateSpace CType = CoordinateSpace::World >
|
||||
class Plane
|
||||
class OriginDistancePlane
|
||||
{
|
||||
float m_distance { constants::DEFAULT_FLOAT };
|
||||
Vector m_direction { constants::DEFAULT_VEC3 };
|
||||
@@ -27,60 +30,87 @@ namespace fgl::engine
|
||||
|
||||
bool valid() const { return m_distance != constants::DEFAULT_FLOAT && m_direction != constants::DEFAULT_VEC3; }
|
||||
|
||||
Plane( const glm::vec3 vector, const float distance ) : m_distance( distance ), m_direction( vector ) {}
|
||||
|
||||
Plane( const glm::vec3 normal, const glm::vec3 point ) :
|
||||
Plane( glm::normalize( normal ), glm::dot( glm::normalize( normal ), point ) )
|
||||
OriginDistancePlane( const glm::vec3 vector, const float distance ) :
|
||||
m_distance( distance ),
|
||||
m_direction( glm::normalize( vector ) )
|
||||
{}
|
||||
|
||||
Plane( const Vector vector, const float distance ) : m_distance( distance ), m_direction( vector ) {}
|
||||
OriginDistancePlane( const Vector vector, const float distance ) :
|
||||
m_distance( distance ),
|
||||
m_direction( glm::normalize( vector ) )
|
||||
{}
|
||||
|
||||
Plane() = default;
|
||||
OriginDistancePlane() = default;
|
||||
|
||||
//! Returns the closest point on the plane to the 0,0,0 origin
|
||||
Coordinate< CType > getPosition() const
|
||||
{
|
||||
assert( valid() );
|
||||
|
||||
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;
|
||||
|
||||
bool isForward( const WorldCoordinate coord ) const { return distanceFrom( coord ) > 0.0; }
|
||||
|
||||
bool isBehind( const WorldCoordinate coord ) const { return !isForward( coord ); }
|
||||
|
||||
//! Returns the distance from a point to the plane. Negative if behind, positive if in front
|
||||
double distanceFrom( const WorldCoordinate coord ) const
|
||||
//! Returns a normalized Vector
|
||||
Vector direction() const
|
||||
{
|
||||
if constexpr ( CType == CoordinateSpace::World )
|
||||
{
|
||||
assert( valid() );
|
||||
return -( glm::dot( coord, m_direction ) - m_distance );
|
||||
}
|
||||
else
|
||||
throw std::runtime_error( "Plane must be in Plane<CoordinateType::World> to use distanceFrom" );
|
||||
assert( m_direction != constants::DEFAULT_VEC3 );
|
||||
return m_direction;
|
||||
}
|
||||
|
||||
Vector direction() const { return m_direction; }
|
||||
float distance() const
|
||||
{
|
||||
assert( m_distance != constants::DEFAULT_FLOAT );
|
||||
return m_distance;
|
||||
}
|
||||
|
||||
float distance() const { return m_distance; }
|
||||
bool operator==( const OriginDistancePlane& other ) const
|
||||
{
|
||||
return m_distance == other.m_distance && m_direction == other.m_direction;
|
||||
}
|
||||
};
|
||||
|
||||
template < CoordinateSpace CType, MatrixType MType >
|
||||
Plane< EvolvedType< MType >() > operator*( const Matrix< MType >& matrix, const Plane< CType >& plane )
|
||||
template < CoordinateSpace CType >
|
||||
inline std::ostream& operator<<( std::ostream& os, const OriginDistancePlane< CType > plane )
|
||||
{
|
||||
os << "Plane: " << plane.direction() << " " << plane.distance();
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<( std::ostream& os, const Vector vector )
|
||||
{
|
||||
os << "(" << vector.x << ", " << vector.y << ", " << vector.z << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
template < CoordinateSpace CType, MatrixType MType >
|
||||
OriginDistancePlane< EvolvedType< MType >() >
|
||||
operator*( const Matrix< MType >& matrix, const OriginDistancePlane< CType >& plane )
|
||||
{
|
||||
assert( plane.valid() );
|
||||
constexpr auto NewCT { EvolvedType< MType >() };
|
||||
constexpr auto OldCT { CType };
|
||||
|
||||
const Vector old_direction { plane.direction() };
|
||||
const Vector new_direction { glm::normalize( matrix * old_direction ) };
|
||||
|
||||
const Coordinate< OldCT > old_center { plane.getPosition() };
|
||||
|
||||
//Translate old_center using matrix
|
||||
const Coordinate< NewCT > new_center { matrix * old_center };
|
||||
|
||||
//Calculate distance between new_center and 0,0,0
|
||||
const float new_distance { glm::dot( plane.direction(), new_center ) };
|
||||
//Project new_center onto inverse of new_direction
|
||||
const auto new_distance { static_cast< float >( glm::dot( new_direction, new_center ) ) };
|
||||
|
||||
return { glm::normalize( new_center ), new_distance };
|
||||
return { new_direction, new_distance };
|
||||
}
|
||||
|
||||
template < CoordinateSpace CType >
|
||||
using Plane = OriginDistancePlane< CType >;
|
||||
|
||||
} // namespace fgl::engine
|
||||
|
||||
28
src/engine/primitives/TransformComponent.hpp
Normal file
28
src/engine/primitives/TransformComponent.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Created by kj16609 on 2/15/24.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Vector.hpp"
|
||||
#include "engine/primitives/Matrix.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
//TransformComponent is always in world space
|
||||
struct TransformComponent
|
||||
{
|
||||
WorldCoordinate translation { constants::DEFAULT_VEC3 };
|
||||
glm::vec3 scale { 1.0f, 1.0f, 1.0f };
|
||||
Vector rotation { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
//TODO: Figure this out and replace TransformComponent with a template of CType instead
|
||||
glm::mat4 mat4() const;
|
||||
|
||||
inline Matrix< MatrixType::ModelToWorld > mat() const { return Matrix< MatrixType::ModelToWorld >( mat4() ); }
|
||||
|
||||
glm::mat3 normalMatrix() const;
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -20,16 +20,14 @@ namespace fgl::engine
|
||||
float& pitch { x };
|
||||
float& yaw { z };
|
||||
|
||||
explicit Vector( const float value ) : glm::vec3( value ) {}
|
||||
constexpr explicit Vector( const float value ) : glm::vec3( value ) {}
|
||||
|
||||
explicit Vector( const glm::vec3 vec ) : glm::vec3( vec ) {}
|
||||
constexpr explicit Vector( const glm::vec3 vec ) : glm::vec3( vec ) {}
|
||||
|
||||
explicit Vector( const float x, const float y, const float z ) : glm::vec3( x, y, z ) {}
|
||||
constexpr explicit Vector( const float x, const float y, const float z ) : glm::vec3( x, y, z ) {}
|
||||
|
||||
operator glm::vec4() const { return glm::vec4( static_cast< glm::vec3 >( *this ), 0.0f ); }
|
||||
|
||||
Vector norm() const { return Vector( glm::normalize( static_cast< glm::vec3 >( *this ) ) ); }
|
||||
|
||||
Vector operator*( const float scalar ) const { return Vector( static_cast< glm::vec3 >( *this ) * scalar ); }
|
||||
|
||||
glm::vec3 right() const;
|
||||
@@ -51,14 +49,19 @@ namespace fgl::engine
|
||||
return Vector( -static_cast< glm::vec3 >( vec ) );
|
||||
}
|
||||
|
||||
inline Vector operator*( const glm::mat4 matrix, const Vector vector )
|
||||
{
|
||||
return Vector( matrix * glm::vec4( static_cast< glm::vec3 >( vector ), 0.0f ) );
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
|
||||
namespace glm
|
||||
{
|
||||
|
||||
inline glm::vec3 normalize( fgl::engine::Vector vector )
|
||||
inline Vector normalize( fgl::engine::Vector vector )
|
||||
{
|
||||
return glm::normalize( static_cast< glm::vec3 >( vector ) );
|
||||
return Vector( glm::normalize( static_cast< glm::vec3 >( vector ) ) );
|
||||
}
|
||||
|
||||
inline glm::vec3 cross( const fgl::engine::Vector vec, const glm::vec3 other )
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
|
||||
|
||||
add_custom_target(FGLTests)
|
||||
|
||||
enable_testing()
|
||||
|
||||
file(GLOB_RECURSE FGL_TEST_SOURCES "*.cpp")
|
||||
|
||||
foreach (test_source ${FGL_TEST_SOURCES})
|
||||
get_filename_component(test_name ${test_source} NAME_WE)
|
||||
add_executable(${test_name} ${test_source})
|
||||
target_link_libraries(${test_name} FGLEngine)
|
||||
add_test(NAME ${test_name} COMMAND ${test_name})
|
||||
add_dependencies(FGLTests ${test_name})
|
||||
endforeach (test_source)
|
||||
add_executable(FGLTests ${FGL_TEST_SOURCES})
|
||||
target_link_libraries(FGLTests FGLEngine Catch2::Catch2WithMain)
|
||||
|
||||
include(CTest)
|
||||
include(Catch)
|
||||
catch_discover_tests(FGLTests)
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
using namespace fgl::engine;
|
||||
|
||||
int main()
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
TEST_CASE( "BoundingBox", "[boundingbox]" )
|
||||
{
|
||||
{
|
||||
std::vector< Coordinate< CoordinateSpace::Model > > model_points {};
|
||||
@@ -31,20 +33,20 @@ int main()
|
||||
|
||||
//Check that the points are correct
|
||||
//The middle point should not change
|
||||
assert( combined_box.middle == model_box.middle );
|
||||
REQUIRE( combined_box.middle == model_box.middle );
|
||||
//The scale should be the max of the two boxes
|
||||
assert( combined_box.scale == glm::vec3( 2.0f, 1.0f, 2.0f ) );
|
||||
REQUIRE( combined_box.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() };
|
||||
assert( out_points.size() == box2_points.size() );
|
||||
assert( out_points.size() == 8 );
|
||||
REQUIRE( out_points.size() == box2_points.size() );
|
||||
REQUIRE( out_points.size() == 8 );
|
||||
|
||||
for ( std::uint32_t i = 0; i < out_points.size(); ++i )
|
||||
{
|
||||
assert( out_points[ i ] == box2_points[ i ] );
|
||||
REQUIRE( out_points[ i ] == box2_points[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,13 +88,11 @@ int main()
|
||||
}
|
||||
|
||||
//Check that the lowest point is the one from the combined box too
|
||||
assert( model_box.bottomLeftBack().x == lowest_point.x );
|
||||
assert( model_box.bottomLeftBack().y == lowest_point.y );
|
||||
assert( model_box.bottomLeftBack().z == lowest_point.z );
|
||||
assert( model_box.topRightForward().x == highest_point.x );
|
||||
assert( model_box.topRightForward().y == highest_point.y );
|
||||
assert( model_box.topRightForward().z == highest_point.z );
|
||||
REQUIRE( model_box.bottomLeftBack().x == lowest_point.x );
|
||||
REQUIRE( model_box.bottomLeftBack().y == lowest_point.y );
|
||||
REQUIRE( model_box.bottomLeftBack().z == lowest_point.z );
|
||||
REQUIRE( model_box.topRightForward().x == highest_point.x );
|
||||
REQUIRE( model_box.topRightForward().y == highest_point.y );
|
||||
REQUIRE( model_box.topRightForward().z == highest_point.z );
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
35
tests/src/CameraTesting.cpp
Normal file
35
tests/src/CameraTesting.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Created by kj16609 on 2/15/24.
|
||||
//
|
||||
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#define EXPOSE_CAMERA_INTERNAL
|
||||
#include "engine/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 );
|
||||
}
|
||||
|
||||
const auto camera_up { camera.getUp() };
|
||||
REQUIRE( camera_up == constants::WORLD_UP );
|
||||
|
||||
const auto camera_forward { camera.getForward() };
|
||||
REQUIRE( camera_forward == constants::WORLD_FORWARD );
|
||||
|
||||
const auto camera_right { camera.getRight() };
|
||||
REQUIRE( camera_right == constants::WORLD_RIGHT );
|
||||
}
|
||||
256
tests/src/FrustumTesting.cpp
Normal file
256
tests/src/FrustumTesting.cpp
Normal file
@@ -0,0 +1,256 @@
|
||||
//
|
||||
// Created by kj16609 on 2/15/24.
|
||||
//
|
||||
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define EXPOSE_CAMERA_INTERNAL
|
||||
#include "engine/Camera.hpp"
|
||||
#include "engine/primitives/Frustum.hpp"
|
||||
#include "gtest_printers.hpp"
|
||||
|
||||
using namespace fgl::engine;
|
||||
|
||||
constexpr float ASPECT_RATIO { 16.0f / 9.0f };
|
||||
|
||||
TEST_CASE( "Frustum creation", "[frustum]" )
|
||||
{
|
||||
Camera camera;
|
||||
camera.setPerspectiveProjection( 90.0f, ASPECT_RATIO, constants::NEAR_PLANE, constants::FAR_PLANE );
|
||||
|
||||
const auto base_frustum = camera.getBaseFrustum();
|
||||
|
||||
REQUIRE( camera.getRight() == constants::WORLD_RIGHT );
|
||||
|
||||
REQUIRE( base_frustum.near.direction() == constants::WORLD_FORWARD );
|
||||
REQUIRE( base_frustum.near.distance() == constants::NEAR_PLANE );
|
||||
|
||||
REQUIRE( base_frustum.far.direction() == constants::WORLD_BACKWARD );
|
||||
REQUIRE( base_frustum.far.distance() == -constants::FAR_PLANE );
|
||||
}
|
||||
|
||||
TEST_CASE( "Frustum translations", "[frustum][translation]" )
|
||||
{
|
||||
Camera camera;
|
||||
camera.setPerspectiveProjection( 90.0f, ASPECT_RATIO, constants::NEAR_PLANE, constants::FAR_PLANE );
|
||||
|
||||
const auto base_frustum = camera.getBaseFrustum();
|
||||
glm::mat4 mat { 1.0f };
|
||||
|
||||
REQUIRE( camera.getRight() == constants::WORLD_RIGHT );
|
||||
|
||||
{
|
||||
//Translate backwards by 1 world unit
|
||||
mat = glm::translate( mat, glm::vec3( constants::WORLD_BACKWARD ) );
|
||||
const auto translated_backwards { Matrix< MatrixType::ModelToWorld >( mat ) * base_frustum };
|
||||
|
||||
//Verify that during a translation the direction isn't changed
|
||||
REQUIRE( translated_backwards.near.direction() == base_frustum.near.direction() );
|
||||
|
||||
REQUIRE( translated_backwards.near.direction() == constants::WORLD_FORWARD );
|
||||
REQUIRE( translated_backwards.near.distance() == constants::NEAR_PLANE - 1.0f );
|
||||
|
||||
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 poining toward the origin, So in order for the center to be positive
|
||||
// the distance must also be negative
|
||||
}
|
||||
|
||||
{
|
||||
//Translate forward by 1 world unit
|
||||
mat = glm::translate( glm::mat4( 1.0f ), glm::vec3( constants::WORLD_FORWARD ) );
|
||||
const auto translated_forward { Matrix< MatrixType::ModelToWorld >( mat ) * base_frustum };
|
||||
|
||||
//Verify that during a translation the direction isn't changed
|
||||
REQUIRE( translated_forward.near.direction() == base_frustum.near.direction() );
|
||||
|
||||
REQUIRE( translated_forward.near.direction() == constants::WORLD_FORWARD );
|
||||
REQUIRE( translated_forward.near.distance() == constants::NEAR_PLANE + 1.0f );
|
||||
|
||||
REQUIRE( translated_forward.far.direction() == constants::WORLD_BACKWARD );
|
||||
REQUIRE( translated_forward.far.distance() == -( constants::FAR_PLANE + 1.0f ) );
|
||||
}
|
||||
|
||||
{
|
||||
mat = glm::rotate( glm::mat4( 1.0f ), -glm::radians( 90.0f ), constants::WORLD_UP );
|
||||
|
||||
const auto rotated_right { Matrix< MatrixType::ModelToWorld >( mat ) * base_frustum };
|
||||
|
||||
REQUIRE( rotated_right.near.direction().x == constants::WORLD_RIGHT.x );
|
||||
REQUIRE( rotated_right.near.direction().y < 0.000001f ); // Precision issues. However it's VERY close to 0
|
||||
|
||||
REQUIRE( rotated_right.near.distance() == constants::NEAR_PLANE );
|
||||
|
||||
REQUIRE( rotated_right.far.direction().x == constants::WORLD_LEFT.x );
|
||||
REQUIRE( rotated_right.far.direction().y < 0.000001f ); // Precision issues. However it's VERY close to 0
|
||||
|
||||
REQUIRE( rotated_right.far.distance() == -constants::FAR_PLANE );
|
||||
}
|
||||
|
||||
const Frustum< fgl::engine::CoordinateSpace::World > frustum { Matrix< MatrixType::ModelToWorld > { 1.0f }
|
||||
* camera.getBaseFrustum() };
|
||||
|
||||
{
|
||||
//A point extremely far up should be in front of the bottom plane but behind the top plane
|
||||
const WorldCoordinate far_up { constants::WORLD_UP * 1000.0f };
|
||||
|
||||
REQUIRE( frustum.top.distanceFrom( far_up ) < -500.0f );
|
||||
REQUIRE( frustum.bottom.distanceFrom( far_up ) > 500.0f );
|
||||
|
||||
REQUIRE_FALSE( frustum.pointInside( far_up ) );
|
||||
}
|
||||
|
||||
{
|
||||
//A point extremely far below should be in front of the top plane but behind the bottom plane
|
||||
const WorldCoordinate far_down { constants::WORLD_DOWN * 1000.0f };
|
||||
|
||||
REQUIRE( frustum.top.distanceFrom( far_down ) > 500.0f );
|
||||
REQUIRE( frustum.bottom.distanceFrom( far_down ) < -500.0f );
|
||||
|
||||
REQUIRE_FALSE( frustum.pointInside( far_down ) );
|
||||
}
|
||||
|
||||
//The point FORWARD should be in the frustum
|
||||
{
|
||||
const WorldCoordinate point { constants::WORLD_FORWARD * 2.0f };
|
||||
|
||||
REQUIRE( frustum.near.distanceFrom( point ) > 0.0f );
|
||||
REQUIRE( frustum.far.distanceFrom( point ) > 0.0f );
|
||||
|
||||
REQUIRE( frustum.top.distanceFrom( point ) > 0.0f );
|
||||
REQUIRE( frustum.bottom.distanceFrom( point ) > 0.0f );
|
||||
|
||||
REQUIRE( frustum.right.distanceFrom( point ) > 0.0f );
|
||||
REQUIRE( frustum.left.distanceFrom( point ) > 0.0f );
|
||||
|
||||
REQUIRE( frustum.pointInside( point ) );
|
||||
}
|
||||
|
||||
//A point FORWARD and 0.2 units to the left should be farther from the right plane
|
||||
{
|
||||
const WorldCoordinate point { ( constants::WORLD_FORWARD * 2.0f ) + constants::WORLD_LEFT * 0.2f };
|
||||
|
||||
REQUIRE( frustum.near.distanceFrom( point ) > 0.0f );
|
||||
REQUIRE( frustum.far.distanceFrom( point ) > 0.0f );
|
||||
|
||||
REQUIRE( frustum.top.distanceFrom( point ) > 0.0f );
|
||||
REQUIRE( frustum.bottom.distanceFrom( point ) > 0.0f );
|
||||
|
||||
REQUIRE( frustum.right.distanceFrom( point ) > 0.0f );
|
||||
REQUIRE( frustum.left.distanceFrom( point ) > 0.0f );
|
||||
|
||||
// right_dist > left_dist
|
||||
REQUIRE( frustum.right.distanceFrom( point ) > frustum.left.distanceFrom( point ) );
|
||||
|
||||
REQUIRE( frustum.pointInside( point ) );
|
||||
}
|
||||
|
||||
//A point FORWARD and 0.2 units to the right should be farther from the left plane
|
||||
{
|
||||
const WorldCoordinate point { ( constants::WORLD_FORWARD * 2.0f ) + constants::WORLD_RIGHT * 0.2f };
|
||||
|
||||
// left_dist > right_dist
|
||||
REQUIRE( frustum.left.distanceFrom( point ) > frustum.right.distanceFrom( point ) );
|
||||
}
|
||||
|
||||
//A point FORWARD and down 0.2 units should be closer to the bottom plane
|
||||
{
|
||||
const WorldCoordinate point { ( constants::WORLD_FORWARD * 2.0f ) + ( constants::WORLD_DOWN * 0.02f ) };
|
||||
|
||||
REQUIRE( frustum.top.distanceFrom( point ) > frustum.bottom.distanceFrom( point ) );
|
||||
}
|
||||
|
||||
//Camera should be non-rotated at 0,0,0 so it should be identical
|
||||
REQUIRE( Matrix< MatrixType::ModelToWorld > { 1.0f } * camera.getBaseFrustum() == camera.getFrustumBounds() );
|
||||
|
||||
//Testing rotation of the camera
|
||||
{
|
||||
std::cout << "Testing rotation (Pitch)" << std::endl;
|
||||
Vector rotation { 0.0f, 0.0f, 0.0f };
|
||||
rotation.pitch -= glm::radians( 90.0f );
|
||||
|
||||
camera.setViewYXZ( 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
|
||||
|
||||
//TODO: Rewrite these equals to allow for some mild precission errors
|
||||
//REQUIRE( rotated_frustum.near.direction(), constants::WORLD_DOWN );
|
||||
//REQUIRE( rotated_frustum.far.direction(), constants::WORLD_UP );
|
||||
|
||||
REQUIRE( rotated_frustum.near.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 ) );
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "Testing rotation (Yaw)" << std::endl;
|
||||
Vector rotation { 0.0f, 0.0f, 0.0f };
|
||||
rotation.yaw -= glm::radians( 90.0f );
|
||||
|
||||
camera.setViewYXZ( 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 ) ) );
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "Testing rotation (Roll)" << std::endl;
|
||||
Vector rotation { 0.0f, 0.0f, 0.0f };
|
||||
rotation.roll -= glm::radians( 90.0f );
|
||||
|
||||
camera.setViewYXZ( 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 ) );
|
||||
}
|
||||
}
|
||||
82
tests/src/RotationTests.cpp
Normal file
82
tests/src/RotationTests.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
//
|
||||
// Created by kj16609 on 2/16/24.
|
||||
//
|
||||
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include "engine/primitives/TransformComponent.hpp"
|
||||
#include "gtest_printers.hpp"
|
||||
|
||||
using namespace fgl::engine;
|
||||
|
||||
TEST_CASE( "Transform rotations", "[transform][rotation]" )
|
||||
{
|
||||
TransformComponent component;
|
||||
|
||||
component.translation = constants::WORLD_CENTER;
|
||||
component.scale = glm::vec3( 1.0f );
|
||||
component.rotation = Vector( 0.0f );
|
||||
|
||||
REQUIRE( component.mat4() == glm::mat4( 1.0f ) );
|
||||
|
||||
//This should pitch any point by 90 degrees (Roughly equal to WORLD_UP)
|
||||
constexpr auto TEST_POINT { constants::WORLD_FORWARD };
|
||||
|
||||
SECTION( "Pitch+ (UP)" )
|
||||
{
|
||||
//Rotate by pitch
|
||||
component.rotation.pitch = glm::radians( 90.0f );
|
||||
|
||||
const glm::vec3 rotated_point { component.mat4() * glm::vec4( TEST_POINT, 1.0f ) };
|
||||
|
||||
//Must be dot here since the precision isn't good enough to be exact.
|
||||
// If the dot product is close to 1, then the vectors are close to being equal
|
||||
REQUIRE( glm::dot( rotated_point, constants::WORLD_UP ) > 0.99f );
|
||||
}
|
||||
|
||||
SECTION( "Pitch- (DOWN)" )
|
||||
{
|
||||
component.rotation.pitch = -glm::radians( 90.0f );
|
||||
|
||||
const glm::vec3 rotated_point { component.mat4() * glm::vec4( TEST_POINT, 1.0f ) };
|
||||
|
||||
REQUIRE( glm::dot( rotated_point, constants::WORLD_DOWN ) > 0.99f );
|
||||
}
|
||||
|
||||
SECTION( "Yaw+ (RIGHT)" )
|
||||
{
|
||||
component.rotation.yaw = glm::radians( 90.0f );
|
||||
|
||||
const glm::vec3 rotated_point { component.mat4() * glm::vec4( TEST_POINT, 1.0f ) };
|
||||
|
||||
REQUIRE( glm::dot( rotated_point, constants::WORLD_RIGHT ) > 0.99f );
|
||||
}
|
||||
|
||||
SECTION( "Yaw- (LEFT)" )
|
||||
{
|
||||
component.rotation.yaw = -glm::radians( 90.0f );
|
||||
|
||||
const glm::vec3 rotated_point { component.mat4() * glm::vec4( TEST_POINT, 1.0f ) };
|
||||
|
||||
REQUIRE( glm::dot( rotated_point, constants::WORLD_LEFT ) > 0.99f );
|
||||
}
|
||||
|
||||
//Roll must be tested some other way. The testing point should be to the right, And instead should become WORLD_UP or WORLD_DOWN (WORLD_UP for -roll, WORLD_DOWN for +roll)
|
||||
SECTION( "Roll+ (FORWARD)" )
|
||||
{
|
||||
component.rotation.roll = glm::radians( 90.0f );
|
||||
|
||||
const glm::vec3 rotated_point { component.mat4() * glm::vec4( constants::WORLD_RIGHT, 1.0f ) };
|
||||
|
||||
REQUIRE( glm::dot( rotated_point, constants::WORLD_UP ) > 0.99f );
|
||||
}
|
||||
|
||||
SECTION( "Roll- (BACKWARD)" )
|
||||
{
|
||||
component.rotation.roll = -glm::radians( 90.0f );
|
||||
|
||||
const glm::vec3 rotated_point { component.mat4() * glm::vec4( constants::WORLD_RIGHT, 1.0f ) };
|
||||
|
||||
REQUIRE( glm::dot( rotated_point, constants::WORLD_DOWN ) > 0.99f );
|
||||
}
|
||||
}
|
||||
22
tests/src/gtest_printers.hpp
Normal file
22
tests/src/gtest_printers.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Created by kj16609 on 2/15/24.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace glm
|
||||
{
|
||||
inline void PrintTo( const glm::vec3& vec, ::std::ostream* os )
|
||||
{
|
||||
*os << "(" << vec.x << "," << vec.y << "," << vec.z << ")";
|
||||
}
|
||||
|
||||
} // namespace glm
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
inline void PrintTo( const Vector& vec, ::std::ostream* os )
|
||||
{
|
||||
glm::PrintTo( static_cast< glm::vec3 >( vec ), os );
|
||||
}
|
||||
} // namespace fgl::engine
|
||||
Reference in New Issue
Block a user