Emmanuel BENOîT
afe7c43351
... with a few leftover bugs and a few missing features. Still, it works nicely.
201 lines
5.2 KiB
C++
201 lines
5.2 KiB
C++
#include "externals.hh"
|
|
#include "camera.hh"
|
|
#include "utilities.hh"
|
|
|
|
#include <glm/gtc/matrix_access.hpp>
|
|
|
|
|
|
/*= T_Camera =================================================================*/
|
|
|
|
T_Camera::T_Camera( ) noexcept
|
|
{
|
|
cvtFov2Np( );
|
|
cvtAnglesToVectors( );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
void T_Camera::camera(
|
|
glm::vec3 const& lookAt ,
|
|
glm::vec3 const& angles ,
|
|
const float distance ) noexcept
|
|
{
|
|
lookAt_ = lookAt;
|
|
angles_ = angles;
|
|
distance_ = distance;
|
|
cvtAnglesToVectors( );
|
|
}
|
|
|
|
void T_Camera::camera(
|
|
glm::vec3 const& lookAt ,
|
|
glm::vec3 const& position ,
|
|
glm::vec3 const& up ) noexcept
|
|
{
|
|
pos_ = position;
|
|
up_ = up;
|
|
lookAt_ = lookAt;
|
|
cvtVectorsToAngles( );
|
|
}
|
|
|
|
void T_Camera::cvtAnglesToVectors( ) noexcept
|
|
{
|
|
anglesToMatrix( &angles_.x , &rotMat_[ 0 ].x );
|
|
dir_ = glm::vec3( 0 , 0 , -distance_ ) * rotMat_;
|
|
up_ = glm::vec3( 0 , 1 , 0 ) * rotMat_;
|
|
pos_ = lookAt_ - dir_;
|
|
}
|
|
|
|
void T_Camera::cvtVectorsToAngles( ) noexcept
|
|
{
|
|
dir_ = lookAt_ - pos_;
|
|
const float distance{ length( dir_ ) };
|
|
if ( distance == 0 || length( up_ ) == 0 ) {
|
|
return;
|
|
}
|
|
distance_ = distance;
|
|
|
|
const glm::vec3 nDir{ - dir_ / distance_ };
|
|
const glm::vec3 nUp{ normalize( up_ ) };
|
|
if ( abs( dot( nUp , nDir ) ) == 1 ) {
|
|
return;
|
|
}
|
|
|
|
const glm::vec3 side{ normalize( cross( nUp , nDir ) ) };
|
|
const glm::vec3 up{ normalize( cross( nDir , side ) ) };
|
|
column( rotMat_ , 0 ) = side;
|
|
column( rotMat_ , 1 ) = up;
|
|
column( rotMat_ , 2 ) = nDir;
|
|
|
|
glm::vec3 nAngles;
|
|
nAngles.x = atan2f( nDir.y , nDir.z );
|
|
|
|
const float c2{ sqrtf( side.x * side.x + side.y * side.y ) };
|
|
nAngles.y = atan2f( -side.z , c2 );
|
|
|
|
const float s1{ sinf( nAngles.x ) } , c1{ cosf( nAngles.x ) };
|
|
nAngles.z = atan2f( s1 * nDir.x - c1 * up.x ,
|
|
c1 * up.y - s1 * nDir.y );
|
|
|
|
angles_ = nAngles * 180.f / float( M_PI );
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*/
|
|
|
|
void T_Camera::handleDragAndDrop(
|
|
ImVec2 const& move ,
|
|
T_KeyboardModifiers modifiers ,
|
|
T_MouseButtons buttons ) noexcept
|
|
{
|
|
if ( move.x == 0 || move.y == 0 ) {
|
|
return;
|
|
}
|
|
|
|
const float fdx( move.x * .1f * ( ( modifiers & E_KeyboardModifier::CTRL ) ? 1.f : .1f ) );
|
|
const float fdy( move.y * .1f * ( ( modifiers & E_KeyboardModifier::CTRL ) ? 1.f : .1f ) );
|
|
|
|
if ( ( buttons & E_MouseButton::LEFT ) && ( modifiers & E_KeyboardModifier::SHIFT ) ) {
|
|
// Left mouse button, shift - move camera
|
|
const auto side( glm::normalize( glm::cross( up_ , dir_ ) ) );
|
|
lookAt_ += .1f * ( side * fdx + up_ * fdy );
|
|
} else if ( buttons & E_MouseButton::LEFT ) {
|
|
// Left mouse button, no shift - change yaw/pitch
|
|
updateAngle( angles_.y , fdx );
|
|
updateAngle( angles_.x , fdy );
|
|
} else if ( buttons & E_MouseButton::RIGHT ) {
|
|
// Right mouse button - change roll
|
|
updateAngle( angles_.z , fdx );
|
|
}
|
|
cvtAnglesToVectors( );
|
|
}
|
|
|
|
void T_Camera::handleWheel(
|
|
const float wheel ,
|
|
T_KeyboardModifiers modifiers ,
|
|
T_MouseButtons /* buttons */ ) noexcept
|
|
{
|
|
const float delta( wheel * ( ( modifiers & E_KeyboardModifier::CTRL ) ? 1.f : .1f) );
|
|
if ( modifiers & E_KeyboardModifier::SHIFT ) {
|
|
fov_ = std::max( 1.f , std::min( 179.f , fov_ + delta ) );
|
|
cvtFov2Np( );
|
|
} else {
|
|
distance_ = std::max( .01f , distance_ - delta );
|
|
cvtAnglesToVectors( );
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
T_Camera::T_Changes T_Camera::makeUI( ) noexcept
|
|
{
|
|
using namespace ImGui;
|
|
|
|
// What gets changed
|
|
static const T_Changes changeFlags[] = {
|
|
E_Changes::MATRIX ,
|
|
{} ,
|
|
E_Changes::MATRIX ,
|
|
E_Changes::MATRIX ,
|
|
{} ,
|
|
E_Changes::MATRIX ,
|
|
E_Changes::MATRIX ,
|
|
{} ,
|
|
E_Changes::FOV ,
|
|
E_Changes::FOV ,
|
|
};
|
|
|
|
// Which update gets called.
|
|
enum E_ChangeCall_ {
|
|
CC_NONE ,
|
|
CC_FROM_VECTORS ,
|
|
CC_FROM_ANGLES ,
|
|
CC_FROM_FOV ,
|
|
CC_FROM_NP ,
|
|
};
|
|
static const E_ChangeCall_ changeCalls[] = {
|
|
CC_FROM_ANGLES ,
|
|
CC_NONE ,
|
|
CC_FROM_ANGLES ,
|
|
CC_FROM_ANGLES ,
|
|
CC_NONE ,
|
|
CC_FROM_VECTORS ,
|
|
CC_FROM_VECTORS ,
|
|
CC_NONE ,
|
|
CC_FROM_FOV ,
|
|
CC_FROM_NP ,
|
|
};
|
|
|
|
static_assert( sizeof( changeFlags ) / sizeof( T_Changes ) == sizeof( changeCalls ) / sizeof( E_ChangeCall_ ) ,
|
|
"invalid configuration" );
|
|
|
|
// Changes (also draws the fields)
|
|
const bool changed[] = {
|
|
DragFloat3( "Target" , &lookAt_.x ) ,
|
|
( Separator( ) , false ) ,
|
|
DragFloat( "Distance" , &distance_ , .1f , .1f , 1e8 , "%.1f" ) ,
|
|
DragFloat3( "Angles" , &angles_.x , .01f , -180 , 180 ) ,
|
|
( Separator( ) , false ) ,
|
|
DragFloat3( "Position" , &pos_.x ) ,
|
|
DragFloat3( "Up vector" , &up_.x ) ,
|
|
( Separator( ) , false ) ,
|
|
DragFloat( "FoV" , &fov_ , .01f , .01f , 179.9f ) ,
|
|
DragFloat( "Near plane" , &np_ , .00001f , .001f , 1e6f ) ,
|
|
};
|
|
|
|
static_assert( sizeof( changeFlags ) / sizeof( T_Changes ) == sizeof( changed ) / sizeof( bool ) ,
|
|
"invalid configuration" );
|
|
|
|
T_Changes changes;
|
|
for ( unsigned i = 0 ; i < sizeof( changed ) / sizeof( bool ) ; i ++ ) {
|
|
if ( changed[ i ] ) {
|
|
switch ( changeCalls[ i ] ) {
|
|
case CC_NONE: break;
|
|
case CC_FROM_VECTORS: cvtVectorsToAngles( ); break;
|
|
case CC_FROM_ANGLES: cvtAnglesToVectors( ); break;
|
|
case CC_FROM_FOV: cvtFov2Np( ); break;
|
|
case CC_FROM_NP: cvtNp2Fov( ); break;
|
|
}
|
|
changes = changes | changeFlags[ i ];
|
|
}
|
|
}
|
|
return changes;
|
|
}
|