Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TGLPerspectiveCamera.cxx
Go to the documentation of this file.
1 // @(#)root/gl:$Id$
2 // Author: Richard Maunder 25/05/2005
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include "TGLPerspectiveCamera.h"
13 #include "TGLUtil.h"
14 #include "TGLIncludes.h"
15 
16 #include "TMath.h"
17 #include "TError.h"
18 
19 /** \class TGLPerspectiveCamera
20 \ingroup opengl
21 Perspective projection camera - with characteristic foreshortening.
22 */
23 
24 ClassImp(TGLPerspectiveCamera);
25 
26 Double_t TGLPerspectiveCamera::fgFOVMin = 0.01;
27 Double_t TGLPerspectiveCamera::fgFOVDefault = 30;
28 Double_t TGLPerspectiveCamera::fgFOVMax = 120.0;
29 UInt_t TGLPerspectiveCamera::fgFOVDeltaSens = 500;
30 
31 ////////////////////////////////////////////////////////////////////////////////
32 /// Construct default XOZ perspective camera
33 
34 TGLPerspectiveCamera::TGLPerspectiveCamera() :
35  TGLCamera(TGLVector3(-1.0, 0.0, 0.0), TGLVector3(0.0, 1.0, 0.0)),
36  fFOV(fgFOVDefault)
37 {
38  Setup(TGLBoundingBox(TGLVertex3(-100,-100,-100), TGLVertex3(100,100,100)));
39  fCamTrans.MoveLF(1, fDollyDefault);
40 }
41 
42 ////////////////////////////////////////////////////////////////////////////////
43 /// Construct perspective camera
44 
45 TGLPerspectiveCamera::TGLPerspectiveCamera(const TGLVector3 & hAxes, const TGLVector3 & vAxes) :
46  TGLCamera(hAxes, vAxes),
47  fFOV(fgFOVDefault)
48 {
49  Setup(TGLBoundingBox(TGLVertex3(-100,-100,-100), TGLVertex3(100,100,100)));
50  fCamTrans.MoveLF(1, fDollyDefault);
51 }
52 
53 ////////////////////////////////////////////////////////////////////////////////
54 /// Destroy perspective camera
55 
56 TGLPerspectiveCamera::~TGLPerspectiveCamera()
57 {
58 }
59 
60 ////////////////////////////////////////////////////////////////////////////////
61 /// Setup camera limits suitable to view the world volume defined by 'box'
62 /// and call Reset() to initialise camera.
63 
64 void TGLPerspectiveCamera::Setup(const TGLBoundingBox & box, Bool_t reset)
65 {
66  if (fExternalCenter == kFALSE)
67  {
68  if (fFixDefCenter)
69  {
70  SetCenterVec(fFDCenter.X(), fFDCenter.Y(), fFDCenter.Z());
71  }
72  else
73  {
74  TGLVertex3 center = box.Center();
75  SetCenterVec(center.X(), center.Y(), center.Z());
76  }
77  }
78 
79  // At default FOV, the dolly should be set so as to encapsulate the scene.
80  TGLVector3 extents = box.Extents();
81  Int_t sortInd[3];
82  TMath::Sort(3, extents.CArr(), sortInd);
83  Double_t size = TMath::Hypot(extents[sortInd[0]], extents[sortInd[1]]);
84  Double_t fov = TMath::Min(fgFOVDefault, fgFOVDefault*fViewport.Aspect());
85 
86  fDollyDefault = size / (2.0*TMath::Tan(fov*TMath::Pi()/360));
87  fDollyDistance = 0.002 * fDollyDefault;
88 
89  if (reset)
90  {
91  Reset();
92  }
93 }
94 
95 ////////////////////////////////////////////////////////////////////////////////
96 /// Reset the camera to defaults - reframe the world volume established in Setup()
97 /// in default state. Note: limits defined in Setup() are not adjusted.
98 
99 void TGLPerspectiveCamera::Reset()
100 {
101  fFOV = fgFOVDefault;
102 
103  fCamTrans.SetIdentity();
104  fCamTrans.MoveLF(1, fDollyDefault);
105 
106  IncTimeStamp();
107 }
108 
109 ////////////////////////////////////////////////////////////////////////////////
110 /// Zoom the camera - 'adjust lens focal length, retaining camera position'.
111 /// Arguments are:
112 ///
113 /// - 'delta' - mouse viewport delta (pixels) - +ive zoom in, -ive zoom out
114 /// - 'mod1' / 'mod2' - sensitivity modifiers - see TGLCamera::AdjustAndClampVal()
115 ///
116 /// Returns kTRUE is redraw required (camera change), kFALSE otherwise.
117 
118 Bool_t TGLPerspectiveCamera::Zoom(Int_t delta, Bool_t mod1, Bool_t mod2)
119 {
120  // TODO: Bring all mouse handling into camera classes - would simplify interface and
121  // remove these non-generic cases.
122  if (AdjustAndClampVal(fFOV, fgFOVMin, fgFOVMax, delta, fgFOVDeltaSens, mod1, mod2)) {
123  IncTimeStamp();
124  return kTRUE;
125  } else {
126  return kFALSE;
127  }
128 }
129 
130 ////////////////////////////////////////////////////////////////////////////////
131 /// Truck the camera - 'move camera parallel to film plane'.
132 /// Returns kTRUE is redraw required (camera change), kFALSE otherwise.
133 
134 Bool_t TGLPerspectiveCamera::Truck(Int_t xDelta, Int_t yDelta, Bool_t mod1, Bool_t mod2)
135 {
136  Double_t lenMidClip = 0.5 * (fFarClip + fNearClip) * TMath::Tan(0.5*fFOV*TMath::DegToRad());
137 
138  Double_t xstep = xDelta * lenMidClip / fViewport.Height();
139  Double_t ystep = yDelta * lenMidClip / fViewport.Height();
140 
141  xstep = AdjustDelta(xstep, 1.0, mod1, mod2);
142  ystep = AdjustDelta(ystep, 1.0, mod1, mod2);
143 
144  return Truck(-xstep, -ystep);
145 }
146 
147 ////////////////////////////////////////////////////////////////////////////////
148 /// Apply the camera to the current GL context, setting the viewport, projection
149 /// and modelview matrices. After this vertices etc can be directly entered
150 /// in the world frame. This also updates the cached frustum values, enabling
151 /// all the projection, overlap tests etc defined in TGLCamera to be used.
152 ///
153 /// Arguments are:
154 /// - 'box' - view volume box - used to adjust near/far clipping
155 /// - 'pickRect' - optional picking rect. If non-null, restrict drawing to this
156 /// viewport rect.
157 
158 void TGLPerspectiveCamera::Apply(const TGLBoundingBox & sceneBox,
159  const TGLRect * pickRect) const
160 {
161  // TODO: If we retained the box from Setup first argument could be dropped?
162 
163  // MT This whole thing is convoluted. We can calculate camera position
164  // and look-at direction without calling unproject and seeking clipping
165  // plane intersection.
166  // Besides, this would give as a proper control over camera transformation
167  // matrix.
168  //
169  // Much better since Oct 2007, the clipping planes stuff still
170  // needs to be cleaned.
171 
172  glViewport(fViewport.X(), fViewport.Y(), fViewport.Width(), fViewport.Height());
173 
174  if(fViewport.Width() == 0 || fViewport.Height() == 0)
175  {
176  glMatrixMode(GL_PROJECTION);
177  glLoadIdentity();
178  glMatrixMode(GL_MODELVIEW);
179  glLoadIdentity();
180  return;
181  }
182 
183  glMatrixMode(GL_PROJECTION);
184  glLoadIdentity();
185 
186  // To find decent near/far clip plane distances we construct the
187  // frustum thus:
188  // i) first setup perspective with arbitrary near/far planes
189  gluPerspective(fFOV, fViewport.Aspect(), 1.0, 1000.0);
190  // printf("FIRST STEP FOV %f, aspect %f, nearClip %f, farClip %f \n", fFOV, fViewport.Aspect(), 1., 1000.);
191 
192  // ii) setup modelview
193  glMatrixMode(GL_MODELVIEW);
194  glLoadIdentity();
195  TGLMatrix mx = fCamBase*fCamTrans;
196  TGLVector3 pos = mx.GetTranslation();
197  TGLVector3 fwd = mx.GetBaseVec(1);
198  TGLVector3 center = pos - fwd;
199  TGLVector3 up = mx.GetBaseVec(3);
200 
201  gluLookAt(pos[0], pos[1], pos[2],
202  center[0], center[1], center[2],
203  up[0], up[1], up[2]);
204 
205  // iii) update the cached frustum planes so we can get eye point/direction
206  Bool_t modifiedCache = kFALSE;
207  if (fCacheDirty) {
208  UpdateCache();
209  modifiedCache = kTRUE;
210  }
211 
212  // iv) Create a clip plane, using the eye direction as normal, passing through eye point
213  TGLPlane clipPlane(EyeDirection(), EyePoint());
214  fCacheDirty = modifiedCache;
215 
216  // v) find the near/far distance which just encapsulate the passed bounding box vertexes
217  // not ideal - should really find the nearest/further points on box surface
218  // which intersect frustum - however this much more complicated
219  Double_t currentDist;
220  for (UInt_t i=0; i<8; i++) {
221  currentDist = clipPlane.DistanceTo(sceneBox[i]);
222  if (i==0)
223  {
224  fNearClip = currentDist;
225  fFarClip = fNearClip;
226  }
227  if (currentDist < fNearClip)
228  fNearClip = currentDist;
229  if (currentDist > fFarClip)
230  fFarClip = currentDist;
231  }
232  // Add 1% each way to avoid any rounding conflicts with drawn objects
233  fNearClip *= 0.49; // 0.99; TODO Look at - avoid removing clipping + manip objs
234  fFarClip *= 2.01; // 1.01;
235  if (fFarClip < 2.0)
236  fFarClip = 2.0;
237  if (fNearClip < fFarClip/1000.0)
238  fNearClip = fFarClip/1000.0;
239 
240  glMatrixMode(GL_PROJECTION);
241  glLoadIdentity();
242 
243  // vi) Load up any picking rect and reset the perspective using the
244  // correct near/far clips distances
245  if (pickRect)
246  {
247  TGLRect rect(*pickRect);
248  WindowToViewport(rect);
249  gluPickMatrix(rect.X(), rect.Y(), rect.Width(), rect.Height(),
250  (Int_t*) fViewport.CArr());
251  gluPerspective(fFOV, fViewport.Aspect(), fNearClip, fFarClip);
252  }
253  else
254  {
255  gluPerspective(fFOV, fViewport.Aspect(), fNearClip, fFarClip);
256  glGetDoublev(GL_PROJECTION_MATRIX, fLastNoPickProjM.Arr());
257  }
258 
259  glMatrixMode(GL_MODELVIEW);
260 
261  if (fCacheDirty) UpdateCache();
262 }
263 
264 ////////////////////////////////////////////////////////////////////////////////
265 /// Configure the camera state.
266 /// - fov - set directly field-of-view in degrees (default = 30);
267 /// - dolly - additional move along the camera forward direction;
268 /// - center - new camera center (can be 0 for no change);
269 /// - hRotate - additional "up/down" rotation in radians;
270 /// - vRotate - additional "left/right" rotation in radians.
271 
272 void TGLPerspectiveCamera::Configure(Double_t fov, Double_t dolly, Double_t center[3],
273  Double_t hRotate, Double_t vRotate)
274 {
275  fFOV = fov;
276 
277  // Don't generally constrain external configuration
278  // However exceeding the vRotate limits or silly FOV values will
279  // cause very weird behaviour or projections so fix these
280 
281  if (fFOV > 170.0) {
282  fFOV = 170.0;
283  } else if (fFOV < 0.1) {
284  fFOV = 0.1;
285  }
286 
287  if (center)
288  SetCenterVec(center[0], center[1], center[2]);
289 
290  fCamTrans.MoveLF(1, dolly);
291  RotateRad(hRotate, vRotate);
292 
293  IncTimeStamp();
294 }