#include "externals.hh" #include "camera.hh" #include "utilities.hh" #include /*= 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_; distance_ = glm::length( dir_ ); if ( distance_ == 0 ) { angles_ = glm::vec3( 0 ); return; } const glm::vec3 nDir{ - dir_ / distance_ }; const glm::vec3 side{ normalize( cross( normalize( up_ ) , 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::handleDND( ImVec2 const& move , const bool hasCtrl , const bool hasShift , const bool lmb ) noexcept { if ( move.x == 0 || move.y == 0 ) { return; } const float fdx( move.x * .1f * ( hasCtrl ? 1.f : .1f ) ); const float fdy( move.y * .1f * ( hasCtrl ? 1.f : .1f ) ); if ( lmb && hasShift ) { // Left mouse button, shift - move camera const auto side( glm::normalize( glm::cross( up_ , dir_ ) ) ); lookAt_ += .1f * ( side * fdx + up_ * fdy ); } else if ( lmb ) { // Left mouse button, no shift - change yaw/pitch updateAngle( angles_.y , fdx ); updateAngle( angles_.x , fdy ); } else { // Right mouse button - change roll updateAngle( angles_.z , fdx ); } cvtAnglesToVectors( ); } void T_Camera::handleWheel( const float wheel , const bool hasCtrl , const bool hasShift ) noexcept { const float delta( wheel * ( hasCtrl ? 1.f : .1f) ); if ( hasShift ) { 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; }