Files
EgtGraphics/SceneCamera.cpp
T
SaraP 95bceba089 EgtGraphics 2.7b3 :
- in vista prospettica con zoom standard introdotto valore massimo per fov.
2025-02-19 16:16:59 +01:00

622 lines
20 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2013-2014
//----------------------------------------------------------------------------
// File : SceneCamera.cpp Data : 10.02.14 Versione : 1.5b1
// Contenuto : Implementazione della gestione camera della classe scena.
//
//
//
// Modifiche : 10.02.14 DS Creazione modulo.
//
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "Scene.h"
#include "/EgtDev/Include/EgtILogger.h"
#include "/EgtDev/Include/EGnStringUtils.h"
#include "/EgtDev/Include/EGkFrame3d.h"
using namespace std ;
//--------------------------- Constants --------------------------------------
static const double MIN_DIST_CAMERA = 10 ;
static const double STD_DIST_CAMERA = 1000 ;
//----------------------------------------------------------------------------
bool
Scene::SetCameraType( bool bOrthoOrPersp)
{
m_bOrthographic = bOrthoOrPersp ;
// ricalcolo i parametri di vista
m_bDistOk = false ;
CalcDistCamera() ;
CalcClippingPlanesFromExtView() ;
return true ;
}
//----------------------------------------------------------------------------
bool
Scene::SetZoomType( int nZoomMode)
{
if ( nZoomMode != ZT_STD && nZoomMode != ZT_DOLLY)
return false ;
m_nPerspZoomType = nZoomMode ;
return true ;
}
//----------------------------------------------------------------------------
double
Scene::CalcMinCameraDistance()
{
double dMinDist = MIN_DIST_CAMERA ;
// nel caso di vista prospettica la distanza della camera deve garantire di non finire dentro gli oggetti
if ( ! m_bOrthographic) {
// calcolo il box di tutti gli oggetti visibili nel frame della camera
Frame3d frCamera ;
CalcCameraFrame( frCamera) ;
BBox3d b3Tot ;
if ( m_pGeomDB != nullptr)
m_pGeomDB->GetGlobalBBox( GDB_ID_ROOT, b3Tot, BBF_ONLY_VISIBLE) ;
b3Tot.ToLoc( frCamera) ;
// la distanza minima della camera è quella che garantisce di trovarsi ad una distanza pari almeno a PERSPECTIVE_NEAR_PLANE
// dalla faccia del box davanti alla camera
Point3d ptMax = b3Tot.GetMax() ;
ptMax.ToGlob( frCamera) ;
dMinDist = ( ptMax - m_ptCenter) * m_vtDirCamera + PERSPECTIVE_NEAR_PLANE + 10 * EPS_SMALL ;
}
return dMinDist ;
}
//----------------------------------------------------------------------------
bool
Scene::CalcDistCamera()
{
// verifico se il calcolo è necessario
if ( m_bDistOk)
return true ;
if ( m_dDistCamera < EPS_SMALL)
m_dDistCamera = STD_DIST_CAMERA ;
else {
double dMinDist = CalcMinCameraDistance() ;
if ( m_dDistCamera < dMinDist)
m_dDistCamera = dMinDist ;
}
m_bDistOk = true ;
return true ;
}
//----------------------------------------------------------------------------
bool
Scene::SetCenter( const Point3d& ptCenter)
{
// assegno il punto
m_ptCenter = ptCenter ;
// invalido ExtView
m_bExtViewOk = false ;
// calcolo nuovi dati di Z clipping
CalcClippingPlanesFromExtView() ;
return true ;
}
//----------------------------------------------------------------------------
bool
Scene::SetCamera( double dAngVertDeg, double dAngOrizzDeg, double dDist)
{
// assegno la direzione ed eventuale distanza
m_vtDirCamera = FromSpherical( 1, dAngVertDeg, dAngOrizzDeg) ;
if ( dDist > EPS_SMALL)
m_dDistCamera = dDist ;
if ( ! VerifyCamera())
return false ;
// se vista da sopra, impongo vtUp da angolo orizzontale
if ( abs( dAngVertDeg) < EPS_ANG_SMALL) {
m_vtUp = - FromPolar( 1, dAngOrizzDeg) ;
m_bUpOk = true ;
}
// lo stesso anche per vista da sotto
else if ( abs( dAngVertDeg - ANG_STRAIGHT) < EPS_ANG_SMALL) {
m_vtUp = FromPolar( 1, dAngOrizzDeg) ;
m_bUpOk = true ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
Scene::SetCamera( int nDir, double dDist)
{
switch ( nDir) {
case CT_TOP :
m_vtDirCamera.Set( 0, 0, 1) ;
break ;
case CT_FRONT :
m_vtDirCamera.Set( 0, -1, 0) ;
break ;
case CT_RIGHT :
m_vtDirCamera.Set( 1, 0, 0) ;
break ;
case CT_BACK :
m_vtDirCamera.Set( 0, 1, 0) ;
break ;
case CT_LEFT :
m_vtDirCamera.Set( -1, 0, 0) ;
break ;
case CT_BOTTOM :
m_vtDirCamera.Set( 0, 0, -1) ;
break ;
case CT_ISO_SW :
m_vtDirCamera.Set( -SQRT1_3, -SQRT1_3, SQRT1_3) ;
break ;
case CT_ISO_SE :
m_vtDirCamera.Set( SQRT1_3, -SQRT1_3, SQRT1_3) ;
break ;
case CT_ISO_NE :
m_vtDirCamera.Set( SQRT1_3, SQRT1_3, SQRT1_3) ;
break ;
case CT_ISO_NW :
m_vtDirCamera.Set( -SQRT1_3, SQRT1_3, SQRT1_3) ;
break ;
case CT_CPLANE :
m_vtDirCamera = GetGridFrame().VersZ() ;
break ;
default :
return false ;
break ;
}
if ( nDir != CT_CPLANE)
m_vtDirCamera.Rotate( Z_AX, m_nOrizzOffsCamera * ANG_RIGHT) ;
// assegno eventuale distanza
if ( dDist > EPS_SMALL)
m_dDistCamera = dDist ;
return VerifyCamera( nDir != CT_CPLANE) ;
}
//----------------------------------------------------------------------------
bool
Scene::VerifyCamera( bool bUseOrizzOffsCamera)
{
// verifico il versore
if ( ! m_vtDirCamera.Normalize())
m_vtDirCamera.Set( 0, 0, 1) ;
// ricalcolo direzione Up
m_bUpOk = false ;
if ( ! CalcDirUp( bUseOrizzOffsCamera))
return false ;
// verifico la distanza
m_bDistOk = false ;
CalcDistCamera() ;
// ricalcolo ExtView
m_bExtViewOk = false ;
if ( ! CalcExtView())
return false ;
// calcolo nuovi dati di Z clipping
return CalcClippingPlanesFromExtView() ;
}
//----------------------------------------------------------------------------
void
Scene::GetCamera( double* pdAngVertDeg, double* pdAngOrizzDeg, double* pdDist) const
{
double dAngVertDeg ;
double dAngOrizzDeg ;
m_vtDirCamera.ToSpherical( nullptr, &dAngVertDeg, &dAngOrizzDeg) ;
if ( abs( dAngVertDeg) < EPS_ANG_SMALL) {
if ( m_bUpOk)
(-m_vtUp).ToSpherical( nullptr, nullptr, &dAngOrizzDeg) ;
else
dAngOrizzDeg = -90 ;
}
else if ( abs( dAngVertDeg - ANG_STRAIGHT) < EPS_ANG_SMALL) {
if ( m_bUpOk)
(m_vtUp).ToSpherical( nullptr, nullptr, &dAngOrizzDeg) ;
else
dAngOrizzDeg = 90 ;
}
if ( pdAngVertDeg != nullptr)
*pdAngVertDeg = dAngVertDeg ;
if ( pdAngOrizzDeg != nullptr)
*pdAngOrizzDeg = dAngOrizzDeg ;
if ( pdDist != nullptr)
*pdDist = m_dDistCamera ;
}
//----------------------------------------------------------------------------
int
Scene::GetCameraDir( void) const
{
Vector3d vtDir = m_vtDirCamera ;
vtDir.Rotate( Z_AX, - m_nOrizzOffsCamera * ANG_RIGHT) ;
if ( vtDir.IsZplus())
return CT_TOP ;
else if ( vtDir.IsYminus())
return CT_FRONT ;
else if ( vtDir.IsXplus())
return CT_RIGHT ;
else if ( vtDir.IsYplus())
return CT_BACK ;
else if ( vtDir.IsXminus())
return CT_LEFT ;
else if ( vtDir.IsZminus())
return CT_BOTTOM ;
else if ( AreSameVectorApprox( vtDir, Vector3d( - SQRT1_3, - SQRT1_3, SQRT1_3)))
return CT_ISO_SW ;
else if ( AreSameVectorApprox( vtDir, Vector3d( SQRT1_3, - SQRT1_3, SQRT1_3)))
return CT_ISO_SE ;
else if ( AreSameVectorApprox( vtDir, Vector3d( SQRT1_3, SQRT1_3, SQRT1_3)))
return CT_ISO_NE ;
else if ( AreSameVectorApprox( vtDir, Vector3d( - SQRT1_3, SQRT1_3, SQRT1_3)))
return CT_ISO_NW ;
else
return CT_NONE ;
}
//----------------------------------------------------------------------------
Point3d
Scene::GetProjectedCenter( void) const
{
Point3d ptVCen ;
if ( ! Project( m_ptCenter, ptVCen))
ptVCen.Set( 0, 0, 0) ;
return ptVCen ;
}
//----------------------------------------------------------------------------
bool
Scene::CalcDirUp( bool bUseOrizzOffsCamera)
{
// verifico se il calcolo è necessario
if ( m_bUpOk)
return true ;
// direzione perpendicolare giacente nel piano XY
// ( m_vtDirCamera è opposta alla direzione in cui si guarda)
Vector3d vtPerpXY = m_vtDirCamera ^ Z_AX ;
if ( ! vtPerpXY.Normalize()) {
vtPerpXY = -X_AX ;
if ( bUseOrizzOffsCamera)
vtPerpXY.Rotate( Z_AX, m_nOrizzOffsCamera * ANG_RIGHT) ;
}
// direzione Up
m_vtUp = vtPerpXY ^ m_vtDirCamera ;
m_bUpOk = m_vtUp.Normalize() ;
// invalido ExtView
m_bExtViewOk = false ;
return m_bUpOk ;
}
//----------------------------------------------------------------------------
bool
Scene::CalcCameraFrame( Frame3d& frView)
{
// verifico direzione camera Up
if ( ! CalcDirUp())
return false ;
// eseguo il calcolo
Vector3d vtZ = m_vtDirCamera ;
Vector3d vtY = m_vtUp ;
Vector3d vtX = vtY ^ vtZ ;
if ( ! frView.Set( m_ptCenter + m_dDistCamera * vtZ, vtX, vtY, vtZ))
return false ;
return true ;
}
//----------------------------------------------------------------------------
bool
Scene::PanCamera( const Point3d& ptWinOld, const Point3d& ptWinNew)
{
// porto i due punti in coordinate mondo
double dZCenter = GetProjectedCenter().z ;
Point3d ptViewOld( ptWinOld.x, ptWinOld.y, dZCenter) ;
Point3d ptWorldOld ;
if ( ! UnProject( ptViewOld, ptWorldOld))
return false ;
Point3d ptViewNew( ptWinNew.x, ptWinNew.y, dZCenter) ;
Point3d ptWorldNew ;
if ( ! UnProject( ptViewNew, ptWorldNew))
return false ;
// calcolo lo spostamento tra i due punti
Vector3d vtMove = ptWorldNew - ptWorldOld ;
// applico questo spostamento al centro di vista
SetCenter( GetCenter() - vtMove) ;
// se dolly aggiusto la distanza per non entrare negli oggetti
if ( ! m_bOrthographic && m_nPerspZoomType == ZT_DOLLY)
CalcDistCamera() ;
else
m_bDistOk = true ;
return true ;
}
//----------------------------------------------------------------------------
bool
Scene::RotateCamera( const Point3d& ptWinOld, const Point3d& ptWinNew)
{
// angoli correnti della camera
double dAngVertDeg ;
double dAngOrizzDeg ;
GetCamera( &dAngVertDeg, &dAngOrizzDeg) ;
// incrementi
double dDeltaAngVertDeg = ( ptWinNew.y - ptWinOld.y) / m_nViewportH * 360 ;
double dDeltaAngOrizzDeg = ( ptWinNew.x - ptWinOld.x) / m_nViewportW * 360 ;
// calcolo nuovi angoli e limitazione dell'angolo verticale
const double MIN_ANG_VERT = 0.0 ;
const double MAX_ANG_VERT = 180.0 ;
dAngVertDeg -= dDeltaAngVertDeg ;
if ( dAngVertDeg < MIN_ANG_VERT)
dAngVertDeg = MIN_ANG_VERT ;
else if ( dAngVertDeg > MAX_ANG_VERT)
dAngVertDeg = MAX_ANG_VERT ;
dAngOrizzDeg -= dDeltaAngOrizzDeg ;
// imposto nuova direzione camera
return SetCamera( dAngVertDeg, dAngOrizzDeg, 0) ;
}
//----------------------------------------------------------------------------
bool
Scene::ZoomAll( void)
{
// aggiorno l'ingombro
UpdateExtension() ;
// traslo il centro di vista nel centro del box
Point3d ptExtCent ;
if ( ! m_b3ExtWorld.GetCenter( ptExtCent))
return false ;
m_ptCenter = ptExtCent ;
m_bExtViewOk = false ;
// calcolo nuovi dati di ingombro
CalcDimViewFromExtView() ;
if ( ! m_bOrthographic) {
if ( m_nPerspZoomType == ZT_DOLLY) {
// adatto la distanza della camera in modo che il fov resti al valore standard
m_dDistCamera = m_dHalfHeight / tan( PERSPECTIVE_STD_FOV * 0.5 * DEGTORAD) + ( m_b3ExtView.GetMax().z - m_b3ExtView.GetMin().z) * 0.5 ;
}
else {
// aggiusto la distanza della camera
m_b3ExtView.GetDiameter( m_dDistCamera) ;
// il valore di m_dHalfHeight calcolato dal box non garantisce che l'oggetto sia interamente contenuto nel frustum
// perchè è il valore in corrispondenza della faccia del box davanti alla camera. Per il calcolo del fov m_dHalfHeight
// deve essere calcolato in corrispondenza di m_ptCenter
// distanza della camera dalla faccia davanti del box
double dDist = m_dDistCamera - ( m_b3ExtView.GetMax().z - m_b3ExtView.GetMin().z) * 0.5 ;
// aggiorno le dimensioni
double dAspect = m_dHalfWidth / m_dHalfHeight ;
m_dHalfHeight = m_dHalfHeight / dDist * m_dDistCamera ;
m_dHalfWidth = m_dHalfHeight * dAspect ;
}
}
// la distanza della camera è calcolata correttamente
m_bDistOk = true ;
CalcClippingPlanesFromExtView() ;
return true ;
}
//----------------------------------------------------------------------------
bool
Scene::ZoomObject( int nId)
{
// Verifico puntatore a DB geometrico
if ( m_pGeomDB == nullptr)
return false ;
// determino l'ingombro dell'oggetto/i in globale
BBox3d b3Obj ;
if ( nId == GDB_ID_SEL) {
int nMyId = m_pGeomDB->GetFirstSelectedObj() ;
while ( nMyId != GDB_ID_NULL) {
BBox3d b3Curr ;
if ( m_pGeomDB->GetGlobalBBox( nMyId, b3Curr, BBF_ONLY_VISIBLE))
b3Obj.Add( b3Curr) ;
nMyId = m_pGeomDB->GetNextSelectedObj() ;
}
}
else
m_pGeomDB->GetGlobalBBox( nId, b3Obj, BBF_ONLY_VISIBLE) ;
// recupero centro
Point3d ptCent ;
if ( ! b3Obj.GetCenter( ptCent))
return false ;
// determino le dimensioni di vista nel riferimento di vista
Frame3d frView ;
if ( ! CalcCameraFrame( frView))
return false ;
b3Obj.ToLoc( frView) ;
const double COEFF = 1.05 ;
double dHalfWidth = COEFF * ( b3Obj.GetMax().x - b3Obj.GetMin().x) / 2 ;
double dHalfHeight = COEFF * ( b3Obj.GetMax().y - b3Obj.GetMin().y) / 2 ;
// imposto il centro e le dimensioni di vista
m_ptCenter = ptCent ;
if ( ! AdjustDimView( dHalfWidth, dHalfHeight))
return false ;
if ( ! m_bOrthographic) {
// cfr conti analoghi in ZoomAll
if ( m_nPerspZoomType == ZT_DOLLY) {
m_dDistCamera = m_dHalfHeight / tan( PERSPECTIVE_STD_FOV * 0.5 * DEGTORAD) + ( b3Obj.GetMax().z - b3Obj.GetMin().z) * 0.5 ;
}
else {
// verifico di non finire dentro l'oggetto
double dDiam ; b3Obj.GetDiameter( dDiam) ;
if ( m_dDistCamera < dDiam * 0.5)
m_dDistCamera = dDiam * 0.5 ;
// distanza della camera dalla faccia davanti del box
double dDist = m_dDistCamera - ( b3Obj.GetMax().z - b3Obj.GetMin().z) * 0.5 ;
// ricalcolo le dimensioni
double dAspect = m_dHalfWidth / m_dHalfHeight ;
m_dHalfHeight = m_dHalfHeight / dDist * m_dDistCamera ;
m_dHalfWidth = m_dHalfHeight * dAspect ;
}
}
// la distanza della camera è calcolata per non finire dentro gli oggetti
m_bDistOk = true ;
return true ;
}
//----------------------------------------------------------------------------
bool
Scene::ZoomRadius( double dRadius)
{
// adatto le dimensioni a quelle della vista
if ( ! AdjustDimView( dRadius, dRadius))
return false ;
// per il dolly va modificata la distanza dalla camera
if ( ! m_bOrthographic && m_nPerspZoomType == ZT_DOLLY) {
m_dDistCamera = m_dHalfHeight / tan( PERSPECTIVE_STD_FOV * 0.5 * DEGTORAD) ;
// verifico la distanza della camera
CalcDistCamera() ;
}
else
m_bDistOk = true ;
return true ;
}
//----------------------------------------------------------------------------
bool
Scene::ZoomChange( double dCoeff)
{
const double MIN_COEFF = 0.5 ;
const double MAX_COEFF = 2 ;
// cambio le dimensioni di zoom
if ( ! m_bOrthographic && m_nPerspZoomType == ZT_DOLLY) {
// per il dolly va modificata la distanza della camera
m_dDistCamera *= dCoeff ;
}
else {
// controllo i limiti
dCoeff = clamp( dCoeff, MIN_COEFF, MAX_COEFF) ;
m_dHalfWidth *= dCoeff ;
m_dHalfHeight *= dCoeff ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
Scene::ZoomOnPoint( const Point3d& ptWin, double dCoeff)
{
// porto il punto in coordinate mondo
Point3d ptView( ptWin.x, ptWin.y, GetProjectedCenter().z) ;
Point3d ptWorld ;
if ( ! UnProject( ptView, ptWorld))
return false ;
// modifico opportunamente il centro di vista
Point3d ptOldCen = m_ptCenter ;
Point3d ptNewCen = ptWorld + ( m_ptCenter - ptWorld) * dCoeff ;
SetCenter( ptNewCen) ;
m_bDistOk = true ;
if ( ! m_bOrthographic) {
if ( m_nPerspZoomType == ZT_DOLLY) {
// nel caso di dolly verifico se il nuovo zoom fa entrare negli oggetti
double dMinDist = CalcMinCameraDistance() ;
if ( m_dDistCamera * dCoeff < dMinDist) {
// adatto il coefficiente di zoom e ricalcolo il centro
dCoeff = dMinDist / m_dDistCamera ;
Point3d ptNewCenter = ptWorld + ( ptOldCen - ptWorld) * dCoeff ;
SetCenter( ptNewCenter) ;
}
}
else {
// nel caso standard verifico se il nuovo fov supera il valore massimo
if ( m_dHalfHeight * dCoeff / m_dDistCamera > PERSPECTIVE_TAN_MAX_FOV) {
// adatto il coefficiente di zoom e ricalcolo il centro
dCoeff = PERSPECTIVE_TAN_MAX_FOV * m_dDistCamera / m_dHalfHeight ;
Point3d ptNewCenter = ptWorld + ( ptOldCen - ptWorld) * dCoeff ;
SetCenter( ptNewCenter) ;
}
}
}
return ZoomChange( dCoeff) ;
}
//----------------------------------------------------------------------------
bool
Scene::ZoomWin( const Point3d& ptWin1, const Point3d& ptWin2)
{
// porto i punti in coordinate mondo
Point3d ptWorld1 ;
if ( ! UnProject( Point3d( ptWin1.x, ptWin1.y, GetProjectedCenter().z), ptWorld1))
return false ;
Point3d ptWorld2 ;
if ( ! UnProject( Point3d( ptWin2.x, ptWin2.y, GetProjectedCenter().z), ptWorld2))
return false ;
// recupero il riferimento della camera (vista)
Frame3d frView ;
if ( ! CalcCameraFrame( frView))
return false ;
// porto i punti in coordinate camera (vista)
Point3d ptView1 = GetToLoc( ptWorld1, frView) ;
Point3d ptView2 = GetToLoc( ptWorld2, frView) ;
// imposto il nuovo centro di vista
Point3d ptNewCen = 0.5 * ( ptWorld1 + ptWorld2) ;
SetCenter( ptNewCen) ;
// imposto il nuovo zoom
double dHalfWidth = 0.5 * abs( ptView1.x - ptView2.x) ;
double dHalfHeight = 0.5 * abs( ptView1.y - ptView2.y) ;
// adatto le dimensioni a quelle della vista
if ( ! AdjustDimView( dHalfWidth, dHalfHeight))
return false ;
// per il dolly va modificata la distanza della camera
if ( ! m_bOrthographic && m_nPerspZoomType == ZT_DOLLY) {
m_dDistCamera = m_dHalfHeight / tan( PERSPECTIVE_STD_FOV * 0.5 * DEGTORAD) ;
CalcDistCamera() ;
}
else
m_bDistOk = true ;
return true ;
}
//----------------------------------------------------------------------------
void
Scene::GetOrthoCamParam( double* pdWidth, double* pdHeight, double* pdNear, double* pdFar) const
{
if ( pdWidth != nullptr)
*pdWidth = m_dHalfWidth ;
if ( pdHeight != nullptr)
*pdHeight = m_dHalfHeight ;
if ( pdNear != nullptr)
*pdNear = m_dZNear ;
if ( pdFar != nullptr)
*pdFar = m_dZFar ;
}