ROOT logo
// @(#)root/gl:$Id: TGLPerspectiveCamera.cxx 27369 2009-02-06 17:35:54Z matevz $
// Author:  Richard Maunder  25/05/2005

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TGLPerspectiveCamera.h"
#include "TGLUtil.h"
#include "TGLIncludes.h"

#include "TMath.h"
#include "TError.h"

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGLPerspectiveCamera                                                 //
//                                                                      //
// Perspective projection camera - with characteristic foreshortening.  //
//                                                                      //
// TODO: Currently constrains YOZ plane to be floor - this is never     //
// 'tipped'. While useful we really need to extend so can:              //
// i) Pick any one of the three natural planes of the world to be floor.//
// ii) Can use a free arcball style camera with no contraint - integrate//
// TArcBall.                                                            //
//////////////////////////////////////////////////////////////////////////

ClassImp(TGLPerspectiveCamera)

Double_t TGLPerspectiveCamera::fgFOVMin = 0.01;
Double_t TGLPerspectiveCamera::fgFOVDefault = 30;
Double_t TGLPerspectiveCamera::fgFOVMax = 120.0;
UInt_t   TGLPerspectiveCamera::fgFOVDeltaSens = 500;

//______________________________________________________________________________
TGLPerspectiveCamera::TGLPerspectiveCamera(const TGLVector3 & hAxes, const TGLVector3 & vAxes) :
   TGLCamera(hAxes, vAxes),
   fFOV(fgFOVDefault)
{
   // Construct perspective camera
   Setup(TGLBoundingBox(TGLVertex3(-100,-100,-100), TGLVertex3(100,100,100)));
   fCamTrans.MoveLF(1, fDollyDefault);
}

//______________________________________________________________________________
TGLPerspectiveCamera::~TGLPerspectiveCamera()
{
   // Destroy perspective camera
}

//______________________________________________________________________________
void TGLPerspectiveCamera::Setup(const TGLBoundingBox & box, Bool_t reset)
{
   // Setup camera limits suitible to view the world volume defined by 'box'
   // and call Reset() to initialise camera.

   if (fExternalCenter == kFALSE)
   {
      TGLVertex3 center = box.Center();
      SetCenterVec(center.X(), center.Y(), center.Z());
   }

   // At default FOV, the dolly should be set so as to encapsulate the scene.
   TGLVector3 extents = box.Extents();
   Int_t sortInd[3];
   TMath::Sort(3, extents.CArr(), sortInd);
   Double_t size = TMath::Hypot(extents[sortInd[0]], extents[sortInd[1]]);
   Double_t fov  = TMath::Min(fgFOVDefault, fgFOVDefault*fViewport.Aspect());

   fDollyDefault  = size / (2.0*TMath::Tan(fov*TMath::Pi()/360));
   fDollyDistance = 0.002 * fDollyDefault;

   if (reset)
   {
      Reset();
   }
}

//______________________________________________________________________________
void TGLPerspectiveCamera::Reset()
{
   // Reset the camera to defaults - reframe the world volume established in Setup()
   // in default state. Note: limits defined in Setup() are not adjusted.

   fFOV = fgFOVDefault;

   fCamTrans.SetIdentity();
   fCamTrans.MoveLF(1, fDollyDefault);

   IncTimeStamp();
}

//______________________________________________________________________________
Bool_t TGLPerspectiveCamera::Zoom(Int_t delta, Bool_t mod1, Bool_t mod2)
{
   // Zoom the camera - 'adjust lens focal length, retaining camera position'.
   // Arguments are:
   //
   // 'delta' - mouse viewport delta (pixels) - +ive zoom in, -ive zoom out
   // 'mod1' / 'mod2' - sensitivity modifiers - see TGLCamera::AdjustAndClampVal()
   //
   // Returns kTRUE is redraw required (camera change), kFALSE otherwise.

   // TODO: Bring all mouse handling into camera classes - would simplify interface and
   // remove these non-generic cases.
   if (AdjustAndClampVal(fFOV, fgFOVMin, fgFOVMax, delta, fgFOVDeltaSens, mod1, mod2)) {
      IncTimeStamp();
      return kTRUE;
   } else {
      return kFALSE;
   }
}

//______________________________________________________________________________
Bool_t TGLPerspectiveCamera::Truck(Int_t xDelta, Int_t yDelta, Bool_t mod1, Bool_t mod2)
{
   // Truck the camera - 'move camera parallel to film plane'.
   // Returns kTRUE is redraw required (camera change), kFALSE otherwise.

   Double_t lenMidClip = 0.5 * (fFarClip + fNearClip) * TMath::Tan(0.5*fFOV*TMath::DegToRad());

   Double_t xstep = xDelta * lenMidClip / fViewport.Height();
   Double_t ystep = yDelta * lenMidClip / fViewport.Height();

   xstep = AdjustDelta(xstep, 1.0, mod1, mod2);
   ystep = AdjustDelta(ystep, 1.0, mod1, mod2);

   return Truck(-xstep, -ystep);
}

//______________________________________________________________________________
void TGLPerspectiveCamera::Apply(const TGLBoundingBox & sceneBox,
                                 const TGLRect        * pickRect) const
{
   // Apply the camera to the current GL context, setting the viewport, projection
   // and modelview matricies. After this verticies etc can be directly entered
   // in the world frame. This also updates the cached frustum values, enabling
   // all the projection, overlap tests etc defined in TGLCamera to be used.
   //
   // Arguments are:
   // 'box' - view volume box - used to adjust near/far clipping
   // 'pickRect' - optional picking rect. If non-null, restrict drawing to this
   // viewport rect.

   // TODO: If we retained the box from Setup first argument could be dropped?

   // MT This whole thing is convoluted. We can calculate camera postion
   // and look-at direction without calling unproject and seeking clipping
   // plane intersection.
   // Besides, this would give as a proper control over camera transforamtion
   // matrix.
   //
   // Much better since Oct 2007, the clipping planes stuff still
   // needs to be cleaned.

   glViewport(fViewport.X(), fViewport.Y(), fViewport.Width(), fViewport.Height());

   if(fViewport.Width() == 0 || fViewport.Height() == 0)
   {
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      return;
   }

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();

   // To find decent near/far clip plane distances we construct the
   // frustum thus:
   // i) first setup perspective with arbitary near/far planes
   gluPerspective(fFOV, fViewport.Aspect(), 1.0, 1000.0);
   //   printf("FIRST STEP FOV %f, aspect %f, nearClip %f, farClip %f \n", fFOV, fViewport.Aspect(), 1., 1000.);

   // ii) setup modelview
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   TGLMatrix  mx     = fCamBase*fCamTrans;
   TGLVector3 pos    = mx.GetTranslation();
   TGLVector3 fwd    = mx.GetBaseVec(1);
   TGLVector3 center = pos - fwd;
   TGLVector3 up     = fCamBase.GetBaseVec(3);

   gluLookAt(pos[0],    pos[1],    pos[2],
             center[0], center[1], center[2],
             up[0],     up[1],     up[2]);

   // iii) update the cached frustum planes so we can get eye point/direction
   Bool_t modifiedCache = kFALSE;
   if (fCacheDirty) {
      UpdateCache();
      modifiedCache = kTRUE;
   }

   // iv) Create a clip plane, using the eye direction as normal, passing through eye point
   TGLPlane clipPlane(EyeDirection(), EyePoint());
   fCacheDirty = modifiedCache;

   // v) find the near/far distance which just encapsulate the passed bounding box vertexes
   //    not ideal - should really find the nearest/further points on box surface
   //    which intersect frustum - however this much more complicated
   Double_t currentDist;
   for (UInt_t i=0; i<8; i++) {
      currentDist = clipPlane.DistanceTo(sceneBox[i]);
      if (i==0)
      {
         fNearClip = currentDist;
         fFarClip  = fNearClip;
      }
      if (currentDist < fNearClip)
         fNearClip = currentDist;
      if (currentDist > fFarClip)
         fFarClip = currentDist;
   }
   // Add 1% each way to avoid any rounding conflicts with drawn objects
   fNearClip *= 0.49; // 0.99; TODO Look at - avoid removing clipping + manip objs
   fFarClip  *= 2.01; // 1.01;
   if (fFarClip < 2.0)
      fFarClip = 2.0;
   if (fNearClip < fFarClip/1000.0)
      fNearClip = fFarClip/1000.0;

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();

   // vi) Load up any picking rect and reset the perspective using the
   // correct near/far clips distances
   if (pickRect)
   {
      TGLRect rect(*pickRect);
      WindowToViewport(rect);
      gluPickMatrix(rect.X(), rect.Y(), rect.Width(), rect.Height(),
                    (Int_t*) fViewport.CArr());
      gluPerspective(fFOV, fViewport.Aspect(), fNearClip, fFarClip);
   }
   else
   {
      gluPerspective(fFOV, fViewport.Aspect(), fNearClip, fFarClip);
      glGetDoublev(GL_PROJECTION_MATRIX, fLastNoPickProjM.Arr());
   }

   glMatrixMode(GL_MODELVIEW);

   if (fCacheDirty) UpdateCache();
}

//______________________________________________________________________________
void TGLPerspectiveCamera::Configure(Double_t fov, Double_t dolly, Double_t center[3],
                                     Double_t hRotate, Double_t vRotate)
{
   // Configure the camera state
   fFOV = fov;

   // Don't generally constrain external configuration
   // However exceeding the vRotate limits or silly FOV values will
   // cause very weird behaviour or projections so fix these

   if (fFOV > 170.0) {
      fFOV = 170.0;
   } else if (fFOV < 0.1) {
      fFOV = 0.1;
   }

   SetCenterVec(center[0], center[1], center[2]);
   fCamTrans.MoveLF(1, dolly);
   RotateRad(hRotate, vRotate);

   IncTimeStamp();
}
 TGLPerspectiveCamera.cxx:1
 TGLPerspectiveCamera.cxx:2
 TGLPerspectiveCamera.cxx:3
 TGLPerspectiveCamera.cxx:4
 TGLPerspectiveCamera.cxx:5
 TGLPerspectiveCamera.cxx:6
 TGLPerspectiveCamera.cxx:7
 TGLPerspectiveCamera.cxx:8
 TGLPerspectiveCamera.cxx:9
 TGLPerspectiveCamera.cxx:10
 TGLPerspectiveCamera.cxx:11
 TGLPerspectiveCamera.cxx:12
 TGLPerspectiveCamera.cxx:13
 TGLPerspectiveCamera.cxx:14
 TGLPerspectiveCamera.cxx:15
 TGLPerspectiveCamera.cxx:16
 TGLPerspectiveCamera.cxx:17
 TGLPerspectiveCamera.cxx:18
 TGLPerspectiveCamera.cxx:19
 TGLPerspectiveCamera.cxx:20
 TGLPerspectiveCamera.cxx:21
 TGLPerspectiveCamera.cxx:22
 TGLPerspectiveCamera.cxx:23
 TGLPerspectiveCamera.cxx:24
 TGLPerspectiveCamera.cxx:25
 TGLPerspectiveCamera.cxx:26
 TGLPerspectiveCamera.cxx:27
 TGLPerspectiveCamera.cxx:28
 TGLPerspectiveCamera.cxx:29
 TGLPerspectiveCamera.cxx:30
 TGLPerspectiveCamera.cxx:31
 TGLPerspectiveCamera.cxx:32
 TGLPerspectiveCamera.cxx:33
 TGLPerspectiveCamera.cxx:34
 TGLPerspectiveCamera.cxx:35
 TGLPerspectiveCamera.cxx:36
 TGLPerspectiveCamera.cxx:37
 TGLPerspectiveCamera.cxx:38
 TGLPerspectiveCamera.cxx:39
 TGLPerspectiveCamera.cxx:40
 TGLPerspectiveCamera.cxx:41
 TGLPerspectiveCamera.cxx:42
 TGLPerspectiveCamera.cxx:43
 TGLPerspectiveCamera.cxx:44
 TGLPerspectiveCamera.cxx:45
 TGLPerspectiveCamera.cxx:46
 TGLPerspectiveCamera.cxx:47
 TGLPerspectiveCamera.cxx:48
 TGLPerspectiveCamera.cxx:49
 TGLPerspectiveCamera.cxx:50
 TGLPerspectiveCamera.cxx:51
 TGLPerspectiveCamera.cxx:52
 TGLPerspectiveCamera.cxx:53
 TGLPerspectiveCamera.cxx:54
 TGLPerspectiveCamera.cxx:55
 TGLPerspectiveCamera.cxx:56
 TGLPerspectiveCamera.cxx:57
 TGLPerspectiveCamera.cxx:58
 TGLPerspectiveCamera.cxx:59
 TGLPerspectiveCamera.cxx:60
 TGLPerspectiveCamera.cxx:61
 TGLPerspectiveCamera.cxx:62
 TGLPerspectiveCamera.cxx:63
 TGLPerspectiveCamera.cxx:64
 TGLPerspectiveCamera.cxx:65
 TGLPerspectiveCamera.cxx:66
 TGLPerspectiveCamera.cxx:67
 TGLPerspectiveCamera.cxx:68
 TGLPerspectiveCamera.cxx:69
 TGLPerspectiveCamera.cxx:70
 TGLPerspectiveCamera.cxx:71
 TGLPerspectiveCamera.cxx:72
 TGLPerspectiveCamera.cxx:73
 TGLPerspectiveCamera.cxx:74
 TGLPerspectiveCamera.cxx:75
 TGLPerspectiveCamera.cxx:76
 TGLPerspectiveCamera.cxx:77
 TGLPerspectiveCamera.cxx:78
 TGLPerspectiveCamera.cxx:79
 TGLPerspectiveCamera.cxx:80
 TGLPerspectiveCamera.cxx:81
 TGLPerspectiveCamera.cxx:82
 TGLPerspectiveCamera.cxx:83
 TGLPerspectiveCamera.cxx:84
 TGLPerspectiveCamera.cxx:85
 TGLPerspectiveCamera.cxx:86
 TGLPerspectiveCamera.cxx:87
 TGLPerspectiveCamera.cxx:88
 TGLPerspectiveCamera.cxx:89
 TGLPerspectiveCamera.cxx:90
 TGLPerspectiveCamera.cxx:91
 TGLPerspectiveCamera.cxx:92
 TGLPerspectiveCamera.cxx:93
 TGLPerspectiveCamera.cxx:94
 TGLPerspectiveCamera.cxx:95
 TGLPerspectiveCamera.cxx:96
 TGLPerspectiveCamera.cxx:97
 TGLPerspectiveCamera.cxx:98
 TGLPerspectiveCamera.cxx:99
 TGLPerspectiveCamera.cxx:100
 TGLPerspectiveCamera.cxx:101
 TGLPerspectiveCamera.cxx:102
 TGLPerspectiveCamera.cxx:103
 TGLPerspectiveCamera.cxx:104
 TGLPerspectiveCamera.cxx:105
 TGLPerspectiveCamera.cxx:106
 TGLPerspectiveCamera.cxx:107
 TGLPerspectiveCamera.cxx:108
 TGLPerspectiveCamera.cxx:109
 TGLPerspectiveCamera.cxx:110
 TGLPerspectiveCamera.cxx:111
 TGLPerspectiveCamera.cxx:112
 TGLPerspectiveCamera.cxx:113
 TGLPerspectiveCamera.cxx:114
 TGLPerspectiveCamera.cxx:115
 TGLPerspectiveCamera.cxx:116
 TGLPerspectiveCamera.cxx:117
 TGLPerspectiveCamera.cxx:118
 TGLPerspectiveCamera.cxx:119
 TGLPerspectiveCamera.cxx:120
 TGLPerspectiveCamera.cxx:121
 TGLPerspectiveCamera.cxx:122
 TGLPerspectiveCamera.cxx:123
 TGLPerspectiveCamera.cxx:124
 TGLPerspectiveCamera.cxx:125
 TGLPerspectiveCamera.cxx:126
 TGLPerspectiveCamera.cxx:127
 TGLPerspectiveCamera.cxx:128
 TGLPerspectiveCamera.cxx:129
 TGLPerspectiveCamera.cxx:130
 TGLPerspectiveCamera.cxx:131
 TGLPerspectiveCamera.cxx:132
 TGLPerspectiveCamera.cxx:133
 TGLPerspectiveCamera.cxx:134
 TGLPerspectiveCamera.cxx:135
 TGLPerspectiveCamera.cxx:136
 TGLPerspectiveCamera.cxx:137
 TGLPerspectiveCamera.cxx:138
 TGLPerspectiveCamera.cxx:139
 TGLPerspectiveCamera.cxx:140
 TGLPerspectiveCamera.cxx:141
 TGLPerspectiveCamera.cxx:142
 TGLPerspectiveCamera.cxx:143
 TGLPerspectiveCamera.cxx:144
 TGLPerspectiveCamera.cxx:145
 TGLPerspectiveCamera.cxx:146
 TGLPerspectiveCamera.cxx:147
 TGLPerspectiveCamera.cxx:148
 TGLPerspectiveCamera.cxx:149
 TGLPerspectiveCamera.cxx:150
 TGLPerspectiveCamera.cxx:151
 TGLPerspectiveCamera.cxx:152
 TGLPerspectiveCamera.cxx:153
 TGLPerspectiveCamera.cxx:154
 TGLPerspectiveCamera.cxx:155
 TGLPerspectiveCamera.cxx:156
 TGLPerspectiveCamera.cxx:157
 TGLPerspectiveCamera.cxx:158
 TGLPerspectiveCamera.cxx:159
 TGLPerspectiveCamera.cxx:160
 TGLPerspectiveCamera.cxx:161
 TGLPerspectiveCamera.cxx:162
 TGLPerspectiveCamera.cxx:163
 TGLPerspectiveCamera.cxx:164
 TGLPerspectiveCamera.cxx:165
 TGLPerspectiveCamera.cxx:166
 TGLPerspectiveCamera.cxx:167
 TGLPerspectiveCamera.cxx:168
 TGLPerspectiveCamera.cxx:169
 TGLPerspectiveCamera.cxx:170
 TGLPerspectiveCamera.cxx:171
 TGLPerspectiveCamera.cxx:172
 TGLPerspectiveCamera.cxx:173
 TGLPerspectiveCamera.cxx:174
 TGLPerspectiveCamera.cxx:175
 TGLPerspectiveCamera.cxx:176
 TGLPerspectiveCamera.cxx:177
 TGLPerspectiveCamera.cxx:178
 TGLPerspectiveCamera.cxx:179
 TGLPerspectiveCamera.cxx:180
 TGLPerspectiveCamera.cxx:181
 TGLPerspectiveCamera.cxx:182
 TGLPerspectiveCamera.cxx:183
 TGLPerspectiveCamera.cxx:184
 TGLPerspectiveCamera.cxx:185
 TGLPerspectiveCamera.cxx:186
 TGLPerspectiveCamera.cxx:187
 TGLPerspectiveCamera.cxx:188
 TGLPerspectiveCamera.cxx:189
 TGLPerspectiveCamera.cxx:190
 TGLPerspectiveCamera.cxx:191
 TGLPerspectiveCamera.cxx:192
 TGLPerspectiveCamera.cxx:193
 TGLPerspectiveCamera.cxx:194
 TGLPerspectiveCamera.cxx:195
 TGLPerspectiveCamera.cxx:196
 TGLPerspectiveCamera.cxx:197
 TGLPerspectiveCamera.cxx:198
 TGLPerspectiveCamera.cxx:199
 TGLPerspectiveCamera.cxx:200
 TGLPerspectiveCamera.cxx:201
 TGLPerspectiveCamera.cxx:202
 TGLPerspectiveCamera.cxx:203
 TGLPerspectiveCamera.cxx:204
 TGLPerspectiveCamera.cxx:205
 TGLPerspectiveCamera.cxx:206
 TGLPerspectiveCamera.cxx:207
 TGLPerspectiveCamera.cxx:208
 TGLPerspectiveCamera.cxx:209
 TGLPerspectiveCamera.cxx:210
 TGLPerspectiveCamera.cxx:211
 TGLPerspectiveCamera.cxx:212
 TGLPerspectiveCamera.cxx:213
 TGLPerspectiveCamera.cxx:214
 TGLPerspectiveCamera.cxx:215
 TGLPerspectiveCamera.cxx:216
 TGLPerspectiveCamera.cxx:217
 TGLPerspectiveCamera.cxx:218
 TGLPerspectiveCamera.cxx:219
 TGLPerspectiveCamera.cxx:220
 TGLPerspectiveCamera.cxx:221
 TGLPerspectiveCamera.cxx:222
 TGLPerspectiveCamera.cxx:223
 TGLPerspectiveCamera.cxx:224
 TGLPerspectiveCamera.cxx:225
 TGLPerspectiveCamera.cxx:226
 TGLPerspectiveCamera.cxx:227
 TGLPerspectiveCamera.cxx:228
 TGLPerspectiveCamera.cxx:229
 TGLPerspectiveCamera.cxx:230
 TGLPerspectiveCamera.cxx:231
 TGLPerspectiveCamera.cxx:232
 TGLPerspectiveCamera.cxx:233
 TGLPerspectiveCamera.cxx:234
 TGLPerspectiveCamera.cxx:235
 TGLPerspectiveCamera.cxx:236
 TGLPerspectiveCamera.cxx:237
 TGLPerspectiveCamera.cxx:238
 TGLPerspectiveCamera.cxx:239
 TGLPerspectiveCamera.cxx:240
 TGLPerspectiveCamera.cxx:241
 TGLPerspectiveCamera.cxx:242
 TGLPerspectiveCamera.cxx:243
 TGLPerspectiveCamera.cxx:244
 TGLPerspectiveCamera.cxx:245
 TGLPerspectiveCamera.cxx:246
 TGLPerspectiveCamera.cxx:247
 TGLPerspectiveCamera.cxx:248
 TGLPerspectiveCamera.cxx:249
 TGLPerspectiveCamera.cxx:250
 TGLPerspectiveCamera.cxx:251
 TGLPerspectiveCamera.cxx:252
 TGLPerspectiveCamera.cxx:253
 TGLPerspectiveCamera.cxx:254
 TGLPerspectiveCamera.cxx:255
 TGLPerspectiveCamera.cxx:256
 TGLPerspectiveCamera.cxx:257
 TGLPerspectiveCamera.cxx:258
 TGLPerspectiveCamera.cxx:259
 TGLPerspectiveCamera.cxx:260
 TGLPerspectiveCamera.cxx:261
 TGLPerspectiveCamera.cxx:262
 TGLPerspectiveCamera.cxx:263
 TGLPerspectiveCamera.cxx:264
 TGLPerspectiveCamera.cxx:265
 TGLPerspectiveCamera.cxx:266
 TGLPerspectiveCamera.cxx:267
 TGLPerspectiveCamera.cxx:268
 TGLPerspectiveCamera.cxx:269
 TGLPerspectiveCamera.cxx:270
 TGLPerspectiveCamera.cxx:271
 TGLPerspectiveCamera.cxx:272
 TGLPerspectiveCamera.cxx:273
 TGLPerspectiveCamera.cxx:274