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