TGLCamera.cxx

Go to the documentation of this file.
00001 // @(#)root/gl:$Id: TGLCamera.cxx 37397 2010-12-08 13:54:23Z matevz $
00002 // Author:  Richard Maunder  25/05/2005
00003 
00004 /*************************************************************************
00005  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
00006  * All rights reserved.                                                  *
00007  *                                                                       *
00008  * For the licensing terms see $ROOTSYS/LICENSE.                         *
00009  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
00010  *************************************************************************/
00011 
00012 #include "TGLCamera.h"
00013 #include "TGLIncludes.h"
00014 #include "TGLBoundingBox.h"
00015 #include "TError.h"
00016 #include "TMath.h"
00017 
00018 //////////////////////////////////////////////////////////////////////////
00019 //                                                                      //
00020 // TGLCamera                                                            //
00021 //                                                                      //
00022 // Abstract base camera class - concrete classes for orthographic and   //
00023 // persepctive cameras derive from it. This class maintains values for  //
00024 // the current:                                                         //
00025 // i)   Viewport                                                        //
00026 // ii)  Projection, modelview and clip matricies - extracted from GL    //
00027 // iii) The 6 frustum planes                                            //
00028 // iv)  Expanded frustum interest box                                   //
00029 //                                                                      //
00030 // It provides methods for various projection, overlap and intersection //
00031 // tests for viewport and world locations, against the true frustum and //
00032 // expanded interest box, and for extracting eye position and direction.//
00033 //                                                                      //
00034 // It also defines the pure virtual manipulation interface methods the  //
00035 // concrete ortho and prespective classes must implement.               //
00036 //////////////////////////////////////////////////////////////////////////
00037 
00038 ClassImp(TGLCamera)
00039 
00040 const Double_t TGLCamera::fgInterestBoxExpansion = 1.3;
00041 UInt_t         TGLCamera::fgDollyDeltaSens       = 500;
00042 
00043 //______________________________________________________________________________
00044 TGLCamera::TGLCamera() :
00045    fExternalCenter(kFALSE),
00046    fFixDefCenter(kFALSE),
00047    fCenter(&fDefCenter),
00048    fNearClip(0), fFarClip(0),
00049    fDollyDefault(1.0), fDollyDistance(1.0),
00050    fVAxisMinAngle(0.01f),
00051    fCacheDirty(kTRUE),
00052    fTimeStamp (1),
00053    fProjM(), fModVM(), fClipM(),
00054    fViewport(0,0,100,100),
00055    fLargestSeen(0.0)
00056 {
00057    // Default base camera constructor
00058    for (UInt_t i = 0; i < kPlanesPerFrustum; i++ ) {
00059       fFrustumPlanes[i].Set(1.0, 0.0, 0.0, 0.0);
00060    }
00061    TGLVertex3 origin;
00062    fCamBase.Set(origin, TGLVector3(1, 0, 0), TGLVector3(0, 0, 1));
00063 }
00064 
00065 //______________________________________________________________________________
00066 TGLCamera::TGLCamera(const TGLVector3 & hAxis, const TGLVector3 & vAxis) :
00067    fExternalCenter(kFALSE),
00068    fFixDefCenter(kFALSE),
00069    fCenter(&fDefCenter),
00070    fNearClip(0), fFarClip(0),
00071    fDollyDefault(1.0), fDollyDistance(1.0),
00072    fVAxisMinAngle(0.01f),
00073    fCacheDirty(kTRUE),
00074    fTimeStamp (1),
00075    fProjM(), fModVM(), fClipM(),
00076    fViewport(0,0,100,100),
00077    fLargestSeen(0.0)
00078 {
00079    // Default base camera constructor
00080    for (UInt_t i = 0; i < kPlanesPerFrustum; i++ ) {
00081       fFrustumPlanes[i].Set(1.0, 0.0, 0.0, 0.0);
00082    }
00083    TGLVertex3 origin;
00084    fCamBase.Set(origin, vAxis, hAxis);
00085 }
00086 
00087 //______________________________________________________________________________
00088 TGLCamera::~TGLCamera()
00089 {
00090    // Base camera destructor.
00091 }
00092 
00093 //______________________________________________________________________________
00094 void TGLCamera::SetViewport(const TGLRect & viewport)
00095 {
00096    // Set viewport extents from passed 'viewport' rect.
00097 
00098    fViewport = viewport;
00099    IncTimeStamp();
00100 }
00101 
00102 //______________________________________________________________________________
00103 void TGLCamera::UpdateCache() const
00104 {
00105    // Update internally cached frustum values
00106    assert(fCacheDirty);
00107 
00108    glGetDoublev(GL_PROJECTION_MATRIX, fProjM.Arr());
00109    glGetDoublev(GL_MODELVIEW_MATRIX, fModVM.Arr());
00110 
00111    // Multiply projection by modelview to get the clip matrix
00112    // TODO: Move this into TGLMatrix or shift all over to ROOT ones
00113    fClipM  = fProjM;
00114    fClipM *= fModVM;
00115 
00116    // RIGHT clipping plane
00117    fFrustumPlanes[kRight].Set(fClipM[ 3] - fClipM[ 0],
00118                               fClipM[ 7] - fClipM[ 4],
00119                               fClipM[11] - fClipM[ 8],
00120                               fClipM[15] - fClipM[12]);
00121 
00122    // LEFT clipping plane
00123    fFrustumPlanes[kLeft].Set(fClipM[ 3] + fClipM[ 0],
00124                              fClipM[ 7] + fClipM[ 4],
00125                              fClipM[11] + fClipM[ 8],
00126                              fClipM[15] + fClipM[12]);
00127 
00128    // BOTTOM clipping plane
00129    fFrustumPlanes[kBottom].Set(fClipM[ 3] + fClipM[ 1],
00130                                fClipM[ 7] + fClipM[ 5],
00131                                fClipM[11] + fClipM[ 9],
00132                                fClipM[15] + fClipM[13]);
00133 
00134 
00135    // TOP clipping plane
00136    fFrustumPlanes[kTop].Set(fClipM[ 3] - fClipM[ 1],
00137                             fClipM[ 7] - fClipM[ 5],
00138                             fClipM[11] - fClipM[ 9],
00139                             fClipM[15] - fClipM[13]);
00140 
00141    // FAR clipping plane
00142    fFrustumPlanes[kFar].Set(fClipM[ 3] - fClipM[ 2],
00143                             fClipM[ 7] - fClipM[ 6],
00144                             fClipM[11] - fClipM[10],
00145                             fClipM[15] - fClipM[14]);
00146 
00147    // NEAR clipping plane
00148    fFrustumPlanes[kNear].Set(fClipM[ 3] + fClipM[ 2],
00149                              fClipM[ 7] + fClipM[ 6],
00150                              fClipM[11] + fClipM[10],
00151                              fClipM[15] + fClipM[14]);
00152 
00153    fCacheDirty = kFALSE;
00154 }
00155 
00156 //______________________________________________________________________________
00157 TGLBoundingBox TGLCamera::Frustum(Bool_t asBox) const
00158 {
00159    // Return the the current camera frustum. If asBox == kFALSE return
00160    // a true frustum (truncated square based pyramid). If asBox == kTRUE
00161    // return a true box, using the far clipping plane intersection projected
00162    // back to the near plane.
00163    //
00164    // Camera must have valid frustum cache - call Apply() after last modifcation, before using
00165    //
00166    // Note: TGLBoundingBox is not really valid when filled with truncated pyramid
00167    // - this is used as a visual debug aid only so ok.
00168 
00169    // TODO: BoundingBox object is not always valid
00170    // Need a generic bounding volume object
00171    if (fCacheDirty) {
00172       Error("TGLCamera::FrustumBox()", "cache dirty - must call Apply()");
00173    }
00174 
00175 
00176    TGLVertex3 vertex[8];
00177 
00178    //    7-------6
00179    //   /|      /|
00180    //  3-------2 |
00181    //  | 4-----|-5
00182    //  |/      |/
00183    //  0-------1
00184 
00185    // Get four vertices of frustum on the far clipping plane
00186    // We assume they always intersect
00187    vertex[4] = Intersection(fFrustumPlanes[kFar], fFrustumPlanes[kBottom], fFrustumPlanes[kLeft]).second;
00188    vertex[5] = Intersection(fFrustumPlanes[kFar], fFrustumPlanes[kBottom], fFrustumPlanes[kRight]).second;
00189    vertex[6] = Intersection(fFrustumPlanes[kFar], fFrustumPlanes[kTop],    fFrustumPlanes[kRight]).second;
00190    vertex[7] = Intersection(fFrustumPlanes[kFar], fFrustumPlanes[kTop],    fFrustumPlanes[kLeft]).second;
00191 
00192    if (asBox) {
00193       // Now find the matching four verticies for above, projected onto near clip plane
00194       // As near and far clip planes are parallel this forms a orientated box encompassing the frustum
00195       vertex[0] = fFrustumPlanes[kNear].NearestOn(vertex[4]);
00196       vertex[1] = fFrustumPlanes[kNear].NearestOn(vertex[5]);
00197       vertex[2] = fFrustumPlanes[kNear].NearestOn(vertex[6]);
00198       vertex[3] = fFrustumPlanes[kNear].NearestOn(vertex[7]);
00199    } else {
00200       // Returing true frustum - find verticies at near clipping plane
00201       // We assume they always intersect
00202       vertex[0] = Intersection(fFrustumPlanes[kNear], fFrustumPlanes[kBottom], fFrustumPlanes[kLeft]).second;
00203       vertex[1] = Intersection(fFrustumPlanes[kNear], fFrustumPlanes[kBottom], fFrustumPlanes[kRight]).second;
00204       vertex[2] = Intersection(fFrustumPlanes[kNear], fFrustumPlanes[kTop],    fFrustumPlanes[kRight]).second;
00205       vertex[3] = Intersection(fFrustumPlanes[kNear], fFrustumPlanes[kTop],    fFrustumPlanes[kLeft]).second;
00206    }
00207 
00208    return TGLBoundingBox(vertex);
00209 }
00210 
00211 //______________________________________________________________________________
00212 TGLVertex3 TGLCamera::EyePoint() const
00213 {
00214    // Return the camera eye point (vertex) in world space
00215    // Camera must have valid frustum cache - call Apply() after last modifcation, before using
00216    if (fCacheDirty) {
00217       Error("TGLPerspectiveCamera::FrustumBox()", "cache dirty - must call Apply()");
00218    }
00219 
00220    // Use intersection of right/left/top frustum planes - can be done in
00221    // other ways from camera values but this is easiest.
00222    // Note for an ortho camera this will result in an infinite z distance
00223    // which is theorectically correct although of limited use
00224    return Intersection(fFrustumPlanes[kRight], fFrustumPlanes[kLeft], fFrustumPlanes[kTop]).second;
00225 }
00226 
00227 //______________________________________________________________________________
00228 TGLVector3 TGLCamera::EyeDirection() const
00229 {
00230    // Extract the camera eye direction (vector), running from EyePoint()
00231    // Camera must have valid frustum cache - call Apply() after last modifcation, before using
00232    if (fCacheDirty) {
00233       Error("TGLCamera::FrustumBox()", "cache dirty - must call Apply()");
00234    }
00235    // Direction is just normal of near clipping plane
00236    return fFrustumPlanes[kNear].Norm();
00237 }
00238 
00239 //______________________________________________________________________________
00240 TGLVertex3 TGLCamera::FrustumCenter() const
00241 {
00242    // Find the center of the camera frustum from intersection of planes
00243    // This method will work even with parallel left/right & top/bottom and
00244    // infinite eye point of ortho cameras
00245    // Camera must have valid frustum cache - call Apply() after last modifcation, before using
00246    if (fCacheDirty) {
00247       Error("TGLCamera::FrustumCenter()", "cache dirty - must call Apply()");
00248    }
00249    std::pair<Bool_t, TGLVertex3> nearBottomLeft = Intersection(fFrustumPlanes[kNear],
00250                                                                fFrustumPlanes[kBottom],
00251                                                                fFrustumPlanes[kLeft]);
00252    std::pair<Bool_t, TGLVertex3> farTopRight    = Intersection(fFrustumPlanes[kFar],
00253                                                                fFrustumPlanes[kTop],
00254                                                                fFrustumPlanes[kRight]);
00255    // Planes should intersect
00256    if (!nearBottomLeft.first || !farTopRight.first) {
00257       Error("TGLCamera::FrustumCenter()", "frustum planes invalid");
00258       return TGLVertex3(0.0, 0.0, 0.0);
00259    }
00260    return nearBottomLeft.second + (farTopRight.second - nearBottomLeft.second)/2.0;
00261 }
00262 
00263 //______________________________________________________________________________
00264 EOverlap TGLCamera::FrustumOverlap(const TGLBoundingBox & box) const
00265 {
00266    // Calcaulte overlap (kInside, kOutside, kPartial) of box with camera
00267    // frustum
00268    // Camera must have valid frustum cache - call Apply() after last modifcation, before using
00269    if (fCacheDirty) {
00270       Error("TGLCamera::FrustumOverlap()", "cache dirty - must call Apply()");
00271    }
00272 
00273    // Test shape against each plane in frustum - returning overlap result
00274    // This method can result in kFALSE positives, where shape lies outside
00275    // frustum, but not outside a single plane of it. In this case the shape
00276    // will be regarded incorrectly as intersecting (kPartial)
00277    // TODO: Improve this - have a reliable test (seperating axes).
00278 
00279    Int_t planesInside = 0; // Assume outside to start
00280    for (Int_t planeIndex = 0; planeIndex < kPlanesPerFrustum; ++planeIndex) {
00281       EOverlap planeOverlap = box.Overlap(fFrustumPlanes[planeIndex]);
00282 
00283       // Special case - any object which comes through the near clipping
00284       // plane is completely removed - disabled at present
00285       // TODO: In future may want to fade object (opacity) as they approach
00286       // near clip - how will this be returned? template pair?
00287       /*if (planeIndex == kNear && planeOverlap == kPartial) {
00288          return kOutside;
00289       }*/
00290       // Once we find a single plane which shape is outside, we are outside the frustum
00291       if ( planeOverlap == kOutside ) {
00292          return kOutside;
00293       } else if ( planeOverlap == kInside ) {
00294          planesInside++;
00295       }
00296    }
00297    // Completely inside frustum
00298    if ( planesInside == kPlanesPerFrustum ) {
00299       return kInside;
00300    } else {
00301       return kPartial;
00302    }
00303 }
00304 
00305 //______________________________________________________________________________
00306 EOverlap TGLCamera::ViewportOverlap(const TGLBoundingBox & box) const
00307 {
00308    // Calculate overlap (kInside, kOutside, kPartial) of box projection onto viewport
00309    // (as rect) against the viewport rect.
00310    // Camera must have valid frustum cache - call Apply() after last modifcation, before using.
00311 
00312    return ViewportRect(box).Overlap(fViewport);
00313 }
00314 
00315 //______________________________________________________________________________
00316 TGLRect TGLCamera::ViewportRect(const TGLBoundingBox & box,
00317                                 const TGLBoundingBox::EFace face) const
00318 {
00319    // Calculate viewport rectangle which just contains projection of single 'face'
00320    // of world frame bounding box 'box' onto the viewport. Note use other version
00321    // of ViewportRect() if you want whole 'box' contained
00322    return ViewportRect(box, &face);
00323 }
00324 
00325 //______________________________________________________________________________
00326 TGLRect TGLCamera::ViewportRect(const TGLBoundingBox & box,
00327                                 const TGLBoundingBox::EFace * face) const
00328 {
00329    // Calculate viewport rectangle which just contains projection of
00330    // world frame bounding box 'box' onto the viewport. If face is
00331    // null the rect contains the whole bounding box (8 vertices/6
00332    // faces). If face is non-null it indicates a box face, and the
00333    // rect contains the single face (4 vertices). Note use other
00334    // version of ViewportRect() if you wish to just pass a static
00335    // EFace enum member (e.g. kFaceLowX)
00336    //
00337    // Note:
00338    //    i)   Rectangle is NOT clipped by viewport limits - so can result
00339    //         in rect with corners outside viewport - negative etc
00340    //    ii)  TGLRect provides int (pixel based) values - not subpxiel accurate
00341    //    iii) Camera must have valid frustum cache - call Apply() after last
00342    //         modifcation, before calling
00343 
00344    if (fCacheDirty) {
00345       Error("TGLCamera::ViewportSize()", "cache dirty - must call Apply()");
00346    }
00347 
00348    // TODO: Maybe TGLRect should be converted to Double_t so subpixel accurate
00349    // Would give better LOD calculations at small sizes
00350 
00351    // May often result in a rect bigger then the viewport
00352    // as gluProject does not clip.
00353    Double_t winX, winY, winZ;
00354    TGLRect  screenRect;
00355 
00356    // TGLBoundingBox::Vertices() & TGLBoundingBox::FaceVertices() return
00357    // const & vectors so this *should* all be effficient...
00358    UInt_t vertexCount;
00359    if (face) {
00360       vertexCount = box.FaceVertices(*face).size();
00361    } else {
00362       vertexCount = box.NumVertices();
00363    }
00364 
00365    for (UInt_t i = 0; i < vertexCount; i++)
00366    {
00367       const TGLVertex3 & vertex = face ? box.Vertex(box.FaceVertices(*face).at(i)) :
00368                                          box.Vertex(i);
00369 
00370       gluProject(vertex.X(), vertex.Y(), vertex.Z(),
00371                  fModVM.CArr(), fProjM.CArr(), fViewport.CArr(),
00372                  &winX, &winY, &winZ);
00373 
00374       if (i == 0) {
00375          screenRect.SetCorner(static_cast<Int_t>(winX),static_cast<Int_t>(winY));
00376       } else {
00377          screenRect.Expand(static_cast<Int_t>(winX), static_cast<Int_t>(winY));
00378       }
00379    }
00380 
00381    return screenRect;
00382 }
00383 
00384 //______________________________________________________________________________
00385 TGLVertex3 TGLCamera::WorldToViewport(const TGLVertex3 & worldVertex,
00386                                       TGLMatrix* modviewMat) const
00387 {
00388    // Convert a 3D world vertex to '3D' viewport (screen) one. The X()/Y()
00389    // components of the viewport vertex are the horizontal/vertical pixel
00390    // positions. The Z() component is the viewport depth value - for a
00391    // default depth range this is 0.0 (at near clip plane) to 1.0 (at far
00392    // clip plane). See OpenGL gluProject & glDepth documentation
00393    //
00394    // Camera must have valid frustum cache - call Apply() after last modifcation, before using
00395 
00396    if (fCacheDirty) {
00397       Error("TGLCamera::WorldToViewport()", "cache dirty - must call Apply()");
00398    }
00399    TGLVertex3 viewportVertex;
00400    gluProject(worldVertex[0], worldVertex[1], worldVertex[2],
00401               modviewMat ? modviewMat->CArr() : fModVM.CArr(),
00402               fProjM.CArr(), fViewport.CArr(),
00403               &viewportVertex[0], &viewportVertex[1], &viewportVertex[2]);
00404    return viewportVertex;
00405 }
00406 
00407 //______________________________________________________________________________
00408 TGLVector3 TGLCamera::WorldDeltaToViewport(const TGLVertex3 & worldRef,
00409                                            const TGLVector3 & worldDelta) const
00410 {
00411    // Convert a 3D vector worldDelta (shift) about vertex worldRef to a viewport
00412    // (screen) '3D' vector. The X()/Y() components of the vector are the horizontal /
00413    // vertical pixel deltas. The Z() component is the viewport depth delta - for a
00414    // default depth range between 0.0 (at near clip plane) to 1.0 (at far clip plane)
00415    // See OpenGL gluProject & glDepth documentation
00416    //
00417    // Camera must have valid frustum cache - call Apply()
00418    if (fCacheDirty) {
00419       Error("TGLCamera::WorldToViewport()", "cache dirty - must call Apply()");
00420    }
00421    TGLVertex3 other = worldRef + worldDelta;
00422    TGLVertex3 v1 = WorldToViewport(worldRef);
00423    TGLVertex3 v2 = WorldToViewport(other);
00424    return v2 - v1;
00425 }
00426 
00427 //______________________________________________________________________________
00428 TGLVertex3 TGLCamera::ViewportToWorld(const TGLVertex3 & viewportVertex,
00429                                       TGLMatrix* modviewMat) const
00430 {
00431    // Convert a '3D' viewport vertex to 3D world one. The X()/Y() components
00432    // of viewportVertex are the horizontal/vertical pixel position.
00433 
00434    // The Z() component is the viewport depth value - for a default
00435    // depth range this is 0.0 (at near clip plane) to 1.0 (at far clip
00436    // plane). Without Z() the viewport position corresponds to a line
00437    // in 3D world space - see:
00438    // TGLLine3 TGLCamera::ViewportToWorld(Double_t viewportX, Double_t viewportY) const
00439    //
00440    // See also OpenGL gluUnProject & glDepth documentation.
00441    //
00442    // Camera must have valid frustum cache - call Apply() after last
00443    // modifcation, before using.
00444 
00445    if (fCacheDirty) {
00446       Error("TGLCamera::ViewportToWorld()", "cache dirty - must call Apply()");
00447    }
00448    TGLVertex3 worldVertex;
00449    gluUnProject(viewportVertex[0], viewportVertex[1], viewportVertex[2],
00450                 modviewMat ? modviewMat->CArr() : fModVM.CArr(),
00451                 fProjM.CArr(), fViewport.CArr(),
00452                 &worldVertex[0], &worldVertex[1], &worldVertex[2]);
00453    return worldVertex;
00454 }
00455 
00456 //______________________________________________________________________________
00457 TGLLine3 TGLCamera::ViewportToWorld(Double_t viewportX, Double_t viewportY) const
00458 {
00459    // Convert a 2D viewport position to 3D world line - the projection of the
00460    // viewport point into 3D space. Line runs from near to far camera clip planes
00461    // (the minimum and maximum visible depth). See also
00462    //    TGLVertex3 TGLCamera::ViewportToWorld(const TGLVertex3 & viewportVertex) const
00463    // for 3D viewport -> 3D world vertex conversions.
00464    // See also OpenGL gluUnProject & glDepth documentation
00465    //
00466    // Camera must have valid frustum cache - call Apply() after last modifcation, before using
00467    if (fCacheDirty) {
00468       Error("TGLCamera::Viewport2DToWorldLine()", "cache dirty - must call Apply()");
00469    }
00470    // Find world verticies at near and far clip planes, and return line through them
00471    TGLVertex3 nearClipWorld = ViewportToWorld(TGLVertex3(viewportX, viewportY, 0.0));
00472    TGLVertex3 farClipWorld = ViewportToWorld(TGLVertex3(viewportX, viewportY, 1.0));
00473    return TGLLine3(nearClipWorld, farClipWorld - nearClipWorld);
00474 }
00475 
00476 //______________________________________________________________________________
00477 TGLLine3 TGLCamera::ViewportToWorld(const TPoint & viewport) const
00478 {
00479    // Convert a 2D viewport position to 3D world line - the projection of the
00480    // viewport point into 3D space. Line runs from near to far camera clip planes
00481    // (the minimum and maximum visible depth). See also
00482    //    TGLVertex3 TGLCamera::ViewportToWorld(const TGLVertex3 & viewportVertex) const
00483    // for 3D viewport -> 3D world vertex conversions.
00484    // See also OpenGL gluUnProject & glDepth documentation
00485    //
00486    // Camera must have valid frustum cache - call Apply() after last modifcation, before using
00487    return ViewportToWorld(viewport.GetX(), viewport.GetY());
00488 }
00489 
00490 //______________________________________________________________________________
00491 std::pair<Bool_t, TGLVertex3> TGLCamera::ViewportPlaneIntersection(Double_t viewportX, Double_t viewportY,
00492                                                                    const TGLPlane & worldPlane) const
00493 {
00494    // Find the intersection of projection of supplied viewport point (a 3D world
00495    // line - see ViewportToWorld) with supplied world plane. Returns std::pair
00496    // of Bool_t and TGLVertex3. If line intersects std::pair.first (Bool_t) is
00497    // kTRUE, and std::pair.second (TGLVertex) contains the intersection vertex.
00498    // If line does not intersect (line and plane parallel) std::pair.first
00499    // (Bool_t) if kFALSE, and std::pair.second (TGLVertex) is invalid.
00500    //
00501    // NOTE: The projection lines is extended for the plane intersection test
00502    // hence the intersection vertex can lie outside the near/far clip regions
00503    // (not visible)
00504    //
00505    // Camera must have valid frustum cache - call Apply() after last modifcation, before using
00506    TGLLine3 worldLine = ViewportToWorld(viewportX, viewportY);
00507 
00508    // Find intersection of line with plane
00509    return Intersection(worldPlane, worldLine, kTRUE /* extended */ );
00510 }
00511 
00512 //______________________________________________________________________________
00513 std::pair<Bool_t, TGLVertex3> TGLCamera::ViewportPlaneIntersection(const TPoint & viewport,
00514                                                                    const TGLPlane & worldPlane) const
00515 {
00516    // Find the intersection of projection of supplied viewport TPoint (a 3D world
00517    // line - see ViewportToWorld) with supplied world plane. Returns std::pair
00518    // of bool and vertex. If line intersects
00519    //
00520    // Camera must have valid frustum cache - call Apply() after last modifcation, before using
00521    return ViewportPlaneIntersection(viewport.GetX(), viewport.GetY(), worldPlane);
00522 }
00523 
00524 //______________________________________________________________________________
00525 TGLVector3 TGLCamera::ViewportDeltaToWorld(const TGLVertex3 & worldRef, Double_t viewportXDelta,
00526                                            Double_t viewportYDelta, TGLMatrix* modviewMat) const
00527 {
00528    // Apply a 2D viewport delta (shift) to the projection of worldRef onto viewport,
00529    // returning the resultant world vector which equates to it. Useful for making
00530    // 3D world objects track mouse moves.
00531    //
00532    // Camera must have valid frustum cache - call Apply()
00533    if (fCacheDirty) {
00534       Error("TGLCamera::ViewportDeltaToWorld()", "cache dirty - must call Apply()");
00535    }
00536    TGLVertex3 winVertex = WorldToViewport(worldRef, modviewMat);
00537    winVertex.Shift(viewportXDelta, viewportYDelta, 0.0);
00538    return (ViewportToWorld(winVertex, modviewMat) - worldRef);
00539 }
00540 
00541 //______________________________________________________________________________
00542 Bool_t TGLCamera::OfInterest(const TGLBoundingBox & box, Bool_t ignoreSize) const
00543 {
00544    // Calculate if the an object defined by world frame bounding box
00545    // is 'of interest' to the camera. This is defined as box:
00546    //
00547    // i) intersecting completely or partially (kInside/kPartial) with
00548    // cameras interest box (fInterestBox)
00549    // ii) having significant length OR volume ratio compared to this
00550    // interest box
00551    //
00552    // If a box is 'of interest' returns kTRUE, kFALSE otherwise.  See
00553    // TGLCamera::UpdateInterest() for more details of camera interest
00554    // box.
00555    //
00556    // Note: Length/volume ratios NOT dependent on the projected size
00557    // of box at current camera configuration as we do not want
00558    // continual changes.  This is used when (re) populating the scene
00559    // with objects from external client.
00560    //
00561    // TODO: Might be more logical to move this test out to client -
00562    // and have accessor for fInterestBox instead?
00563 
00564    Bool_t interest = kFALSE;
00565 
00566    // *********** IMPORTANT - Bootstrapping the camera with empty scene
00567    //
00568    // Initially the camera can't be Setup() (limits etc) until the
00569    // scene is populated and it has a valid bounding box to pass to
00570    // the camera.  However the scene can't be populated without
00571    // knowing if objects sent are 'of interest' - which needs a camera
00572    // interest box, made from a properly setup camera frustum - catch
00573    // 22.
00574    //
00575    // To overcome this we track the largest box diagonal seen so far and
00576    // regard anything over 0.001 of this as 'of interest'. This enables
00577    // us to get a roughly populated scene with largest objects, setup
00578    // the camera, and do first draw.  We then do a
00579    // TGLCamera::UpdateInterest() - which always return kTRUE, and
00580    // thus fires an internal rebuild to fill scene properly and
00581    // finally setup camera properly.
00582 
00583    if (fInterestBox.IsEmpty()) {
00584       if (box.Diagonal() >= fLargestSeen * 0.001) {
00585          if (box.Diagonal() > fLargestSeen) {
00586             fLargestSeen = box.Diagonal();
00587          }
00588          interest = kTRUE;
00589       }
00590    } else {
00591       // Objects are of interest if the have length ratio c.f. the
00592       // current interest box, and they at least partially overlap it.
00593       // Some objects have zero volume BBs - e.g. single points - skip
00594       // the test for these as there is no way to threshold on 0.
00595       if (box.IsEmpty()) {
00596          interest = kTRUE;
00597       } else {
00598          if (ignoreSize || box.Diagonal() / fInterestBox.Diagonal() > 0.0001)
00599             interest = fInterestBox.Overlap(box) != kOutside;
00600       }
00601    }
00602 
00603    return interest;
00604 }
00605 
00606 //______________________________________________________________________________
00607 Bool_t TGLCamera::UpdateInterest(Bool_t force)
00608 {
00609    // Update the internal interest box (fInterestBox) of the camera.
00610    // The interest box is an orientated bounding box, calculated as
00611    // an expanded container round the frustum. It is used to test if
00612    // if object bounding boxes are of interest (should be accepted
00613    // into viewer scene) for a camera - see TGLCamera::OfInterest()
00614    //
00615    // The interest box is updated if the frustum is no longer contained
00616    // in the existing one, or a new one calculated on the current frustum
00617    // differs significantly in volume (camera has been zoomed/dollyed
00618    // sizable amount).
00619    //
00620    // If the interest box is updated we return kTRUE - kFALSE otherwise.
00621    //
00622    Bool_t exposedUpdate = kFALSE;
00623 
00624    // Construct a new interest box using the current frustum box as a basis
00625    TGLBoundingBox frustumBox = Frustum(kTRUE);
00626    TGLBoundingBox newInterestBox(frustumBox);
00627 
00628    // The Z(2) axis of frustum (near->far plane) can be quite shallow c.f. X(0)/Y(1)
00629    // For interest box we want to expand to ensure it is at least size
00630    // of smaller X/Y to avoid excessive interest box recalculations
00631    TGLVector3 frustumExtents = frustumBox.Extents();
00632    Double_t minBoxLength = frustumExtents.Mag() * fgInterestBoxExpansion;
00633    newInterestBox.Scale(minBoxLength/frustumExtents[0], minBoxLength/frustumExtents[1], minBoxLength/frustumExtents[2]);
00634 
00635    // Calculate volume ratio of new to old
00636    Double_t volRatio = 0.0;
00637 
00638    // If the interest box is empty the interest is ALWAYS updated
00639    // See TGLCamera::OfInterest() comment on bootstrapping
00640    if (!fInterestBox.IsEmpty()) {
00641       volRatio = newInterestBox.Volume() / fInterestBox.Volume();
00642    }
00643 
00644    // Update the existing interest box with new one if:
00645    // i) Volume ratio old/new interest has changed significantly
00646    // ii) The current frustum is not inside existing interest
00647    // iii) Force case (debugging)
00648    if (volRatio > 8.0 || volRatio < 0.125 || fInterestBox.IsEmpty() ||
00649        fInterestBox.Overlap(frustumBox) != kInside || force) {
00650       fPreviousInterestBox = fInterestBox;
00651       fInterestBox = newInterestBox;
00652 
00653       // Frustum should be fully contained now
00654       if (fInterestBox.Overlap(frustumBox) != kInside) {
00655          Error("TGLCamera::UpdateInterest", "update interest box does not contain frustum");
00656       }
00657 
00658       exposedUpdate = kTRUE;
00659 
00660       // Keep the real frustum (true and box versions) as debuging aid
00661       fInterestFrustum = Frustum(kFALSE);
00662       fInterestFrustumAsBox = frustumBox;
00663 
00664       if (gDebug>2 || force) {
00665          Info("TGLCamera::UpdateInterest", "changed - volume ratio %f", volRatio );
00666       }
00667    }
00668 
00669    return exposedUpdate;
00670 }
00671 
00672 //______________________________________________________________________________
00673 void TGLCamera::ResetInterest()
00674 {
00675    // Clear out the existing interest box
00676    fInterestBox.SetEmpty();
00677 
00678    // We also reset the bootstrapping variable - see
00679    // TGLCamera::OfInterest comments.
00680    fLargestSeen = 0.0;
00681 }
00682 
00683 //______________________________________________________________________________
00684 Bool_t TGLCamera::AdjustAndClampVal(Double_t & val, Double_t min, Double_t max,
00685                                     Int_t screenShift, Int_t screenShiftRange,
00686                                     Bool_t mod1, Bool_t mod2) const
00687 {
00688    // Adjust a passed REFERENCE value 'val', based on screenShift delta.
00689    // Two modifier flags ('mod1' / 'mod2' ) for sensitivity:
00690    //
00691    // mod1 = kFALSE, mod2 = kFALSE : normal sensitivity (screenShift/screenShiftRange)
00692    // mod1 = kTRUE,  mod2 = kFALSE : 0.1x sensitivity
00693    // mod1 = kTRUE,  mod2 = kTRUE  : 0.01x sensitivity
00694    // mod1 = kFALSE, mod2 = kTRUE  : 10.0x sensitivity
00695    //
00696    // 'val' is modified and clamped to 'min' / 'max' range.
00697    // Return bool kTRUE if val actually changed.
00698    //
00699    // Used as common interaction function for adjusting zoom/dolly etc
00700    if (screenShift == 0) {
00701       return kFALSE;
00702    }
00703 
00704    // Calculate a sensitivity based on passed modifiers
00705    Double_t sens = val * static_cast<Double_t>(screenShift);
00706 
00707    if (mod1) {
00708       sens *= 0.1;
00709       if (mod2) {
00710          sens *= 0.1;
00711       }
00712    } else {
00713       if (mod2) {
00714          sens *= 10.0;
00715       }
00716    }
00717 
00718    Double_t oldVal = val;
00719    Double_t shift  = sens / static_cast<Double_t>(screenShiftRange);
00720    val -= shift;
00721 
00722    if (val < min) {
00723       val = min;
00724    }
00725    else if (val > max) {
00726       val = max;
00727    }
00728 
00729    return val != oldVal;
00730 }
00731 
00732 //______________________________________________________________________________
00733 Double_t TGLCamera::AdjustDelta(Double_t screenShift, Double_t deltaFactor,
00734                                 Bool_t mod1, Bool_t mod2) const
00735 {
00736    // Adjust a passed screen value and apply modifiers.
00737    // See AdjustAndClampVal() for details.
00738 
00739    if (screenShift == 0)
00740       return 0;
00741 
00742    // Calculate a sensitivity based on passed modifiers
00743    Double_t sens = 1.0;
00744 
00745    if (mod1) {
00746       sens *= 0.1;
00747       if (mod2) {
00748          sens *= 0.1;
00749       }
00750    } else {
00751       if (mod2) {
00752          sens *= 10.0;
00753       }
00754    }
00755 
00756    return sens * deltaFactor * screenShift;
00757 }
00758 
00759 //______________________________________________________________________________
00760 void TGLCamera::DrawDebugAids() const
00761 {
00762    // Draw out some debugging aids for the camera:
00763    //
00764    // i) The frustum used to create the current interest box (RED)
00765    // ii) The same frustum as a squared off box (ORANGE)
00766    // iii) The axis aligned version of the frustum used as interest box basis (YELLOW)
00767    // iv) The current interest box (BLUE)
00768 
00769    // Interest box frustum base (RED)
00770    glColor3d(1.0,0.0,0.0);
00771    fInterestFrustum.Draw();
00772 
00773    // Interest box frustum as box (ORANGE)
00774    glColor3d(1.0,0.65,0.15);
00775    fInterestFrustumAsBox.Draw();
00776 
00777    // Current Interest box (BLUE)
00778    glColor3d(0.0,0.0,1.0);
00779    fInterestBox.Draw();
00780 
00781    // Previous interest (GREY)
00782    glColor3d(.8,.7,.6);
00783    fPreviousInterestBox.Draw();
00784 
00785    // Also draw line from current eye point out in eye direction - should not
00786    // appear if calculated correctly
00787    TGLVertex3 start = EyePoint();
00788    TGLVertex3 end = start + EyeDirection();
00789    glColor3d(1.0,1.0,1.0);
00790    glBegin(GL_LINES);
00791    glVertex3dv(start.CArr());
00792    glVertex3dv(end.CArr());
00793    glEnd();
00794 }
00795 
00796 //______________________________________________________________________________
00797 void TGLCamera::SetExternalCenter(Bool_t enable)
00798 {
00799    // Set camera center diffrent than scene center, if enable is kTRUE.
00800 
00801    if (fExternalCenter == enable)
00802       return;
00803 
00804    fExternalCenter = enable;
00805    if (fExternalCenter)
00806       fCenter = &fExtCenter;
00807    else
00808       fCenter = &fDefCenter;
00809 
00810    TGLMatrix bt = fCamBase * fCamTrans;
00811    fCamBase.SetBaseVec(4, *fCenter);
00812    TGLMatrix binv = fCamBase; binv.Invert();
00813    fCamTrans = binv * bt;
00814 
00815    IncTimeStamp();
00816 }
00817 
00818 //______________________________________________________________________________
00819 void TGLCamera::SetCenterVec(Double_t x, Double_t y, Double_t z)
00820 {
00821    // Set camera center vector.
00822 
00823    if (fExternalCenter)
00824       fExtCenter.Set(x, y, z);
00825    else
00826       fDefCenter.Set(x, y, z);
00827 
00828    TGLMatrix bt = fCamBase * fCamTrans;
00829    fCamBase.SetBaseVec(4, *fCenter);
00830    TGLMatrix binv = fCamBase; binv.Invert();
00831    fCamTrans = binv * bt;
00832 
00833    IncTimeStamp();
00834 }
00835 
00836 //______________________________________________________________________________
00837 void TGLCamera::SetCenterVecWarp(Double_t x, Double_t y, Double_t z)
00838 {
00839    // Set camera center vector and do not keep the same combined
00840    // camera transformation matrix.
00841    // It appears as if the camera warped to the new center.
00842 
00843    if (fExternalCenter)
00844       fExtCenter.Set(x, y, z);
00845    else
00846       fDefCenter.Set(x, y, z);
00847 
00848    fCamBase.SetBaseVec(4, *fCenter);
00849 
00850    IncTimeStamp();
00851 }
00852 
00853 //______________________________________________________________________________
00854 Double_t TGLCamera::GetTheta() const
00855 {
00856    // Get angle between camera up axis.
00857 
00858    TGLVector3 fwd  = fCamTrans.GetBaseVec(1);
00859    TGLVector3 zdir = fCamBase.GetBaseVec(3);
00860    fCamBase.RotateIP(fwd);
00861    return TMath::ACos(fwd*zdir);
00862 }
00863 
00864 //______________________________________________________________________________
00865 Bool_t TGLCamera::Truck(Double_t xDelta, Double_t yDelta)
00866 {
00867    // Truck the camera - 'move camera parallel to film plane'.
00868    // Returns kTRUE is redraw required (camera change), kFALSE otherwise.
00869 
00870    if (xDelta != 0 || yDelta != 0)
00871    {
00872       fCamTrans.MoveLF(2, xDelta);
00873       fCamTrans.MoveLF(3, yDelta);
00874 
00875       IncTimeStamp();
00876       return kTRUE;
00877    }
00878    else
00879    {
00880       return kFALSE;
00881    }
00882 }
00883 
00884 //______________________________________________________________________________
00885 Bool_t TGLCamera::Rotate(Int_t xDelta, Int_t yDelta, Bool_t mod1, Bool_t mod2)
00886 {
00887    // Rotate the camera round view volume center established in Setup().
00888    // Arguments are:
00889    // xDelta - horizontal delta (pixels)
00890    // YDelta - vertical delta (pixels)
00891 
00892    Double_t vRotate = AdjustDelta(xDelta, TMath::TwoPi() / fViewport.Width(), mod1, mod2);
00893    Double_t hRotate = AdjustDelta(yDelta, TMath::Pi()   / fViewport.Height(), mod1, mod2);
00894 
00895    return RotateRad(hRotate, vRotate);
00896 }
00897 
00898 //______________________________________________________________________________
00899 Bool_t TGLCamera::RotateRad(Double_t hRotate, Double_t vRotate)
00900 {
00901    // Rotate camera around center.
00902 
00903    using namespace TMath;
00904    if (hRotate != 0.0)
00905    {
00906       TGLVector3 fwd  = fCamTrans.GetBaseVec(1);
00907       TGLVector3 lft  = fCamTrans.GetBaseVec(2);
00908       TGLVector3 up   = fCamTrans.GetBaseVec(3);
00909       TGLVector3 pos  = fCamTrans.GetTranslation();
00910 
00911       TGLVector3 deltaT = pos - (pos*lft)*lft;
00912       Double_t   deltaF = deltaT * fwd;
00913       Double_t   deltaU = deltaT * up;
00914 
00915       // up vector lock
00916       TGLVector3 zdir = fCamBase.GetBaseVec(3);
00917       fCamBase.RotateIP(fwd);
00918       Double_t theta = ACos(fwd*zdir);
00919       if(theta+hRotate < fVAxisMinAngle)
00920          hRotate = fVAxisMinAngle - theta;
00921       else if(theta+hRotate > Pi() - fVAxisMinAngle)
00922          hRotate = Pi() - fVAxisMinAngle - theta;
00923 
00924       fCamTrans.MoveLF(1, -deltaF);
00925       fCamTrans.MoveLF(3, -deltaU);
00926       fCamTrans.RotateLF(3, 1, hRotate);
00927       fCamTrans.MoveLF(3,  deltaU);
00928       fCamTrans.MoveLF(1,  deltaF);
00929    }
00930    if (vRotate != 0.0)
00931    {
00932       fCamTrans.RotatePF(1, 2, -vRotate);
00933    }
00934 
00935    IncTimeStamp();
00936    return kTRUE;
00937 }
00938 
00939 //______________________________________________________________________________
00940 Bool_t TGLCamera::Dolly(Int_t delta, Bool_t mod1, Bool_t mod2)
00941 {
00942    // Dolly the camera - 'move camera along eye line, retaining lens focal length'.
00943    // Arguments are:
00944    //
00945    // 'delta' - mouse viewport delta (pixels) - +ive dolly in, -ive dolly out
00946    // 'mod1' / 'mod2' - sensitivity modifiers - see TGLCamera::AdjustAndClampVal()
00947    //
00948    // Returns kTRUE is redraw required (camera change), kFALSE otherwise.
00949 
00950    Double_t step = AdjustDelta(delta, fDollyDistance, mod1, mod2);
00951    if (step == 0)
00952       return kFALSE;
00953 
00954    fCamTrans.MoveLF(1, -step);
00955 
00956    IncTimeStamp();
00957    return kTRUE;
00958 }

Generated on Tue Jul 5 14:18:06 2011 for ROOT_528-00b_version by  doxygen 1.5.1