Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TGLCamera.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 "TGLCamera.h"
13#include "TGLIncludes.h"
14#include "TGLBoundingBox.h"
15#include "TError.h"
16#include "TMath.h"
17
18/** \class TGLCamera
19\ingroup opengl
20Abstract base camera class - concrete classes for orthographic and
21perspective cameras derive from it. This class maintains values for
22the current:
23 1. Viewport
24 2. Projection, modelview and clip matrices - extracted from GL
25 3. The 6 frustum planes
26 4. Expanded frustum interest box
27
28It provides methods for various projection, overlap and intersection
29tests for viewport and world locations, against the true frustum and
30expanded interest box, and for extracting eye position and direction.
31
32It also defines the pure virtual manipulation interface methods the
33concrete ortho and perspective classes must implement.
34*/
35
37
40
41////////////////////////////////////////////////////////////////////////////////
42/// Default base camera constructor
43
45 fExternalCenter(kFALSE),
46 fFixDefCenter(kFALSE),
47 fWasArcBalled(kFALSE),
48 fCenter(&fDefCenter),
49 fNearClip(0), fFarClip(0),
50 fDollyDefault(1.0), fDollyDistance(1.0),
51 fVAxisMinAngle(0.01f),
52 fCacheDirty(kTRUE),
53 fTimeStamp (1),
54 fProjM(), fModVM(), fClipM(),
55 fViewport(0,0,100,100),
56 fLargestSeen(0.0)
57{
58 for (UInt_t i = 0; i < kPlanesPerFrustum; i++ ) {
59 fFrustumPlanes[i].Set(1.0, 0.0, 0.0, 0.0);
60 }
61 TGLVertex3 origin;
62 fCamBase.Set(origin, TGLVector3(1, 0, 0), TGLVector3(0, 0, 1));
63}
64
65////////////////////////////////////////////////////////////////////////////////
66/// Default base camera constructor
67
68TGLCamera::TGLCamera(const TGLVector3 & hAxis, const TGLVector3 & vAxis) :
69 fExternalCenter(kFALSE),
70 fFixDefCenter(kFALSE),
71 fWasArcBalled(kFALSE),
72 fCenter(&fDefCenter),
73 fNearClip(0), fFarClip(0),
74 fDollyDefault(1.0), fDollyDistance(1.0),
75 fVAxisMinAngle(0.01f),
76 fCacheDirty(kTRUE),
77 fTimeStamp (1),
78 fProjM(), fModVM(), fClipM(),
79 fViewport(0,0,100,100),
80 fLargestSeen(0.0)
81{
82 for (UInt_t i = 0; i < kPlanesPerFrustum; i++ ) {
83 fFrustumPlanes[i].Set(1.0, 0.0, 0.0, 0.0);
84 }
85 TGLVertex3 origin;
86 fCamBase.Set(origin, vAxis, hAxis);
87}
88
89////////////////////////////////////////////////////////////////////////////////
90/// Base camera destructor.
91
93{
94}
95
96////////////////////////////////////////////////////////////////////////////////
97/// Set viewport extents from passed 'viewport' rect.
98
99void TGLCamera::SetViewport(const TGLRect & viewport)
100{
101 fViewport = viewport;
102 IncTimeStamp();
103}
104
105////////////////////////////////////////////////////////////////////////////////
106/// Update internally cached frustum values
107
109{
110 assert(fCacheDirty);
111
112 glGetDoublev(GL_PROJECTION_MATRIX, fProjM.Arr());
113 glGetDoublev(GL_MODELVIEW_MATRIX, fModVM.Arr());
114
115 // Multiply projection by modelview to get the clip matrix
116 // TODO: Move this into TGLMatrix or shift all over to ROOT ones
117 fClipM = fProjM;
118 fClipM *= fModVM;
119
120 // RIGHT clipping plane
122 fClipM[ 7] - fClipM[ 4],
123 fClipM[11] - fClipM[ 8],
124 fClipM[15] - fClipM[12]);
125
126 // LEFT clipping plane
128 fClipM[ 7] + fClipM[ 4],
129 fClipM[11] + fClipM[ 8],
130 fClipM[15] + fClipM[12]);
131
132 // BOTTOM clipping plane
134 fClipM[ 7] + fClipM[ 5],
135 fClipM[11] + fClipM[ 9],
136 fClipM[15] + fClipM[13]);
138
139 // TOP clipping plane
141 fClipM[ 7] - fClipM[ 5],
142 fClipM[11] - fClipM[ 9],
143 fClipM[15] - fClipM[13]);
144
145 // FAR clipping plane
147 fClipM[ 7] - fClipM[ 6],
148 fClipM[11] - fClipM[10],
149 fClipM[15] - fClipM[14]);
150
151 // NEAR clipping plane
153 fClipM[ 7] + fClipM[ 6],
154 fClipM[11] + fClipM[10],
155 fClipM[15] + fClipM[14]);
156
158}
159
160////////////////////////////////////////////////////////////////////////////////
161/// Return the current camera frustum. If asBox == kFALSE return
162/// a true frustum (truncated square based pyramid). If asBox == kTRUE
163/// return a true box, using the far clipping plane intersection projected
164/// back to the near plane.
165///
166/// Camera must have valid frustum cache - call Apply() after last modification, before using
167///
168/// Note: TGLBoundingBox is not really valid when filled with truncated pyramid
169/// - this is used as a visual debug aid only so ok.
170
172{
173 // TODO: BoundingBox object is not always valid
174 // Need a generic bounding volume object
175 if (fCacheDirty) {
176 Error("TGLCamera::FrustumBox()", "cache dirty - must call Apply()");
177 }
178
179
180 TGLVertex3 vertex[8];
181
182 // 7-------6
183 // /| /|
184 // 3-------2 |
185 // | 4-----|-5
186 // |/ |/
187 // 0-------1
188
189 // Get four vertices of frustum on the far clipping plane
190 // We assume they always intersect
195
196 if (asBox) {
197 // Now find the matching four vertices for above, projected onto near clip plane
198 // As near and far clip planes are parallel this forms a orientated box encompassing the frustum
199 vertex[0] = fFrustumPlanes[kNear].NearestOn(vertex[4]);
200 vertex[1] = fFrustumPlanes[kNear].NearestOn(vertex[5]);
201 vertex[2] = fFrustumPlanes[kNear].NearestOn(vertex[6]);
202 vertex[3] = fFrustumPlanes[kNear].NearestOn(vertex[7]);
203 } else {
204 // Returning true frustum - find vertices at near clipping plane
205 // We assume they always intersect
210 }
211
212 return TGLBoundingBox(vertex);
213}
214
215////////////////////////////////////////////////////////////////////////////////
216/// Return the camera eye point (vertex) in world space
217/// Camera must have valid frustum cache - call Apply() after last modification, before using
218
220{
221 if (fCacheDirty) {
222 Error("TGLPerspectiveCamera::FrustumBox()", "cache dirty - must call Apply()");
223 }
224
225 // Use intersection of right/left/top frustum planes - can be done in
226 // other ways from camera values but this is easiest.
227 // Note for an ortho camera this will result in an infinite z distance
228 // which is theoretically correct although of limited use
230}
231
232////////////////////////////////////////////////////////////////////////////////
233/// Extract the camera eye direction (vector), running from EyePoint()
234/// Camera must have valid frustum cache - call Apply() after last modification, before using
235
237{
238 if (fCacheDirty) {
239 Error("TGLCamera::FrustumBox()", "cache dirty - must call Apply()");
240 }
241 // Direction is just normal of near clipping plane
242 return fFrustumPlanes[kNear].Norm();
243}
244
245////////////////////////////////////////////////////////////////////////////////
246/// Find the center of the camera frustum from intersection of planes
247/// This method will work even with parallel left/right & top/bottom and
248/// infinite eye point of ortho cameras
249/// Camera must have valid frustum cache - call Apply() after last modification, before using
250
252{
253 if (fCacheDirty) {
254 Error("TGLCamera::FrustumCenter()", "cache dirty - must call Apply()");
255 }
256 std::pair<Bool_t, TGLVertex3> nearBottomLeft = Intersection(fFrustumPlanes[kNear],
259 std::pair<Bool_t, TGLVertex3> farTopRight = Intersection(fFrustumPlanes[kFar],
262 // Planes should intersect
263 if (!nearBottomLeft.first || !farTopRight.first) {
264 Error("TGLCamera::FrustumCenter()", "frustum planes invalid");
265 return TGLVertex3(0.0, 0.0, 0.0);
266 }
267 return nearBottomLeft.second + (farTopRight.second - nearBottomLeft.second)/2.0;
268}
269
270////////////////////////////////////////////////////////////////////////////////
271/// Calculate overlap (kInside, kOutside, kPartial) of box with camera
272/// frustum
273/// Camera must have valid frustum cache - call Apply() after last modification, before using
274
276{
277 if (fCacheDirty) {
278 Error("TGLCamera::FrustumOverlap()", "cache dirty - must call Apply()");
279 }
280
281 // Test shape against each plane in frustum - returning overlap result
282 // This method can result in kFALSE positives, where shape lies outside
283 // frustum, but not outside a single plane of it. In this case the shape
284 // will be regarded incorrectly as intersecting (kPartial)
285 // TODO: Improve this - have a reliable test (separating axes).
286
287 Int_t planesInside = 0; // Assume outside to start
288 for (Int_t planeIndex = 0; planeIndex < kPlanesPerFrustum; ++planeIndex)
289 {
290 Rgl::EOverlap planeOverlap = box.Overlap(fFrustumPlanes[planeIndex]);
291
292 // Special case - any object which comes through the near clipping
293 // plane is completely removed - disabled at present
294 // TODO: In future may want to fade object (opacity) as they approach
295 // near clip - how will this be returned? template pair?
296 /*if (planeIndex == kNear && planeOverlap == kPartial) {
297 return kOutside;
298 }*/
299 // Once we find a single plane which shape is outside, we are outside the frustum
300 if (planeOverlap == Rgl::kOutside) {
301 return Rgl::kOutside;
302 } else if (planeOverlap == Rgl::kInside) {
303 planesInside++;
304 }
305 }
306 // Completely inside frustum
307 if (planesInside == kPlanesPerFrustum) {
308 return Rgl::kInside;
309 } else {
310 return Rgl::kPartial;
311 }
312}
313
314////////////////////////////////////////////////////////////////////////////////
315/// Calculate overlap (kInside, kOutside, kPartial) of box projection onto viewport
316/// (as rect) against the viewport rect.
317/// Camera must have valid frustum cache - call Apply() after last modification, before using.
318
320{
322}
323
324////////////////////////////////////////////////////////////////////////////////
325/// Calculate viewport rectangle which just contains projection of single 'face'
326/// of world frame bounding box 'box' onto the viewport. Note use other version
327/// of ViewportRect() if you want whole 'box' contained
328
330 const TGLBoundingBox::EFace face) const
331{
332 return ViewportRect(box, &face);
333}
334
335////////////////////////////////////////////////////////////////////////////////
336/// Calculate viewport rectangle which just contains projection of
337/// world frame bounding box 'box' onto the viewport. If face is
338/// null the rect contains the whole bounding box (8 vertices/6
339/// faces). If face is non-null it indicates a box face, and the
340/// rect contains the single face (4 vertices). Note use other
341/// version of ViewportRect() if you wish to just pass a static
342/// EFace enum member (e.g. kFaceLowX)
343///
344/// Note:
345/// 1. Rectangle is NOT clipped by viewport limits - so can result
346/// in rect with corners outside viewport - negative etc
347/// 2. TGLRect provides int (pixel based) values - not subpixel accurate
348/// 3. Camera must have valid frustum cache - call Apply() after last
349/// modification, before calling
350
352 const TGLBoundingBox::EFace * face) const
353{
354 if (fCacheDirty) {
355 Error("TGLCamera::ViewportSize()", "cache dirty - must call Apply()");
356 }
357
358 // TODO: Maybe TGLRect should be converted to Double_t so subpixel accurate
359 // Would give better LOD calculations at small sizes
360
361 // May often result in a rect bigger then the viewport
362 // as gluProject does not clip.
363 Double_t winX, winY, winZ;
364 TGLRect screenRect;
365
366 // TGLBoundingBox::Vertices() & TGLBoundingBox::FaceVertices() return
367 // const & vectors so this *should* all be efficient...
368 UInt_t vertexCount;
369 if (face) {
370 vertexCount = box.FaceVertices(*face).size();
371 } else {
372 vertexCount = box.NumVertices();
373 }
374
375 for (UInt_t i = 0; i < vertexCount; i++)
376 {
377 const TGLVertex3 & vertex = face ? box.Vertex(box.FaceVertices(*face).at(i)) :
378 box.Vertex(i);
379
380 gluProject(vertex.X(), vertex.Y(), vertex.Z(),
382 &winX, &winY, &winZ);
383
384 if (i == 0) {
385 screenRect.SetCorner(static_cast<Int_t>(winX),static_cast<Int_t>(winY));
386 } else {
387 screenRect.Expand(static_cast<Int_t>(winX), static_cast<Int_t>(winY));
388 }
389 }
390
391 return screenRect;
392}
393
394////////////////////////////////////////////////////////////////////////////////
395/// Convert a 3D world vertex to '3D' viewport (screen) one. The X()/Y()
396/// components of the viewport vertex are the horizontal/vertical pixel
397/// positions. The Z() component is the viewport depth value - for a
398/// default depth range this is 0.0 (at near clip plane) to 1.0 (at far
399/// clip plane). See OpenGL gluProject & glDepth documentation
400///
401/// Camera must have valid frustum cache - call Apply() after last modification, before using
402
404 TGLMatrix* modviewMat) const
405{
406 if (fCacheDirty) {
407 Error("TGLCamera::WorldToViewport()", "cache dirty - must call Apply()");
408 }
409 TGLVertex3 viewportVertex;
410 gluProject(worldVertex[0], worldVertex[1], worldVertex[2],
411 modviewMat ? modviewMat->CArr() : fModVM.CArr(),
413 &viewportVertex[0], &viewportVertex[1], &viewportVertex[2]);
414 return viewportVertex;
415}
416
417////////////////////////////////////////////////////////////////////////////////
418/// Convert a 3D vector worldDelta (shift) about vertex worldRef to a viewport
419/// (screen) '3D' vector. The X()/Y() components of the vector are the horizontal /
420/// vertical pixel deltas. The Z() component is the viewport depth delta - for a
421/// default depth range between 0.0 (at near clip plane) to 1.0 (at far clip plane)
422/// See OpenGL gluProject & glDepth documentation
423///
424/// Camera must have valid frustum cache - call Apply()
425
427 const TGLVector3 & worldDelta) const
428{
429 if (fCacheDirty) {
430 Error("TGLCamera::WorldToViewport()", "cache dirty - must call Apply()");
431 }
432 TGLVertex3 other = worldRef + worldDelta;
433 TGLVertex3 v1 = WorldToViewport(worldRef);
435 return v2 - v1;
436}
437
438////////////////////////////////////////////////////////////////////////////////
439/// Convert a '3D' viewport vertex to 3D world one. The X()/Y() components
440/// of viewportVertex are the horizontal/vertical pixel position.
441
443 TGLMatrix* modviewMat) const
444{
445 // The Z() component is the viewport depth value - for a default
446 // depth range this is 0.0 (at near clip plane) to 1.0 (at far clip
447 // plane). Without Z() the viewport position corresponds to a line
448 // in 3D world space - see:
449 // TGLLine3 TGLCamera::ViewportToWorld(Double_t viewportX, Double_t viewportY) const
450 //
451 // See also OpenGL gluUnProject & glDepth documentation.
452 //
453 // Camera must have valid frustum cache - call Apply() after last
454 // modification, before using.
455
456 if (fCacheDirty) {
457 Error("TGLCamera::ViewportToWorld()", "cache dirty - must call Apply()");
458 }
459 TGLVertex3 worldVertex;
460 gluUnProject(viewportVertex[0], viewportVertex[1], viewportVertex[2],
461 modviewMat ? modviewMat->CArr() : fModVM.CArr(),
463 &worldVertex[0], &worldVertex[1], &worldVertex[2]);
464 return worldVertex;
465}
466
467////////////////////////////////////////////////////////////////////////////////
468/// Convert a 2D viewport position to 3D world line - the projection of the
469/// viewport point into 3D space. Line runs from near to far camera clip planes
470/// (the minimum and maximum visible depth). See also
471/// TGLVertex3 TGLCamera::ViewportToWorld(const TGLVertex3 & viewportVertex) const
472/// for 3D viewport -> 3D world vertex conversions.
473/// See also OpenGL gluUnProject & glDepth documentation
474///
475/// Camera must have valid frustum cache - call Apply() after last modification, before using
476
478{
479 if (fCacheDirty) {
480 Error("TGLCamera::Viewport2DToWorldLine()", "cache dirty - must call Apply()");
481 }
482 // Find world vertices at near and far clip planes, and return line through them
483 TGLVertex3 nearClipWorld = ViewportToWorld(TGLVertex3(viewportX, viewportY, 0.0));
484 TGLVertex3 farClipWorld = ViewportToWorld(TGLVertex3(viewportX, viewportY, 1.0));
485 return TGLLine3(nearClipWorld, farClipWorld - nearClipWorld);
486}
487
488////////////////////////////////////////////////////////////////////////////////
489/// Convert a 2D viewport position to 3D world line - the projection of the
490/// viewport point into 3D space. Line runs from near to far camera clip planes
491/// (the minimum and maximum visible depth). See also
492/// TGLVertex3 TGLCamera::ViewportToWorld(const TGLVertex3 & viewportVertex) const
493/// for 3D viewport -> 3D world vertex conversions.
494/// See also OpenGL gluUnProject & glDepth documentation
495///
496/// Camera must have valid frustum cache - call Apply() after last modification, before using
497
499{
500 return ViewportToWorld(viewport.GetX(), viewport.GetY());
501}
502
503////////////////////////////////////////////////////////////////////////////////
504/// Find the intersection of projection of supplied viewport point (a 3D world
505/// line - see ViewportToWorld) with supplied world plane. Returns std::pair
506/// of Bool_t and TGLVertex3. If line intersects std::pair.first (Bool_t) is
507/// kTRUE, and std::pair.second (TGLVertex) contains the intersection vertex.
508/// If line does not intersect (line and plane parallel) std::pair.first
509/// (Bool_t) if kFALSE, and std::pair.second (TGLVertex) is invalid.
510///
511/// NOTE: The projection lines is extended for the plane intersection test
512/// hence the intersection vertex can lie outside the near/far clip regions
513/// (not visible)
514///
515/// Camera must have valid frustum cache - call Apply() after last modification, before using
516
517std::pair<Bool_t, TGLVertex3> TGLCamera::ViewportPlaneIntersection(Double_t viewportX, Double_t viewportY,
518 const TGLPlane & worldPlane) const
519{
520 TGLLine3 worldLine = ViewportToWorld(viewportX, viewportY);
521
522 // Find intersection of line with plane
523 return Intersection(worldPlane, worldLine, kTRUE /* extended */ );
524}
525
526////////////////////////////////////////////////////////////////////////////////
527/// Find the intersection of projection of supplied viewport TPoint (a 3D world
528/// line - see ViewportToWorld) with supplied world plane. Returns std::pair
529/// of bool and vertex. If line intersects
530///
531/// Camera must have valid frustum cache - call Apply() after last modification, before using
532
533std::pair<Bool_t, TGLVertex3> TGLCamera::ViewportPlaneIntersection(const TPoint & viewport,
534 const TGLPlane & worldPlane) const
535{
536 return ViewportPlaneIntersection(viewport.GetX(), viewport.GetY(), worldPlane);
537}
538
539////////////////////////////////////////////////////////////////////////////////
540/// Apply a 2D viewport delta (shift) to the projection of worldRef onto viewport,
541/// returning the resultant world vector which equates to it. Useful for making
542/// 3D world objects track mouse moves.
543///
544/// Camera must have valid frustum cache - call Apply()
545
547 Double_t viewportYDelta, TGLMatrix* modviewMat) const
548{
549 if (fCacheDirty) {
550 Error("TGLCamera::ViewportDeltaToWorld()", "cache dirty - must call Apply()");
551 }
552 TGLVertex3 winVertex = WorldToViewport(worldRef, modviewMat);
553 winVertex.Shift(viewportXDelta, viewportYDelta, 0.0);
554 return (ViewportToWorld(winVertex, modviewMat) - worldRef);
555}
556
557////////////////////////////////////////////////////////////////////////////////
558/// Calculate if the an object defined by world frame bounding box
559/// is 'of interest' to the camera. This is defined as box:
560///
561/// 1. intersecting completely or partially (kInside/kPartial) with
562/// cameras interest box (fInterestBox)
563/// 2. having significant length OR volume ratio compared to this
564/// interest box
565///
566/// If a box is 'of interest' returns kTRUE, kFALSE otherwise. See
567/// TGLCamera::UpdateInterest() for more details of camera interest
568/// box.
569///
570/// Note: Length/volume ratios NOT dependent on the projected size
571/// of box at current camera configuration as we do not want
572/// continual changes. This is used when (re) populating the scene
573/// with objects from external client.
574///
575/// TODO: Might be more logical to move this test out to client -
576/// and have accessor for fInterestBox instead?
577
579{
580 Bool_t interest = kFALSE;
581
582 // *********** IMPORTANT - Bootstrapping the camera with empty scene
583 //
584 // Initially the camera can't be Setup() (limits etc) until the
585 // scene is populated and it has a valid bounding box to pass to
586 // the camera. However the scene can't be populated without
587 // knowing if objects sent are 'of interest' - which needs a camera
588 // interest box, made from a properly setup camera frustum - catch
589 // 22.
590 //
591 // To overcome this we track the largest box diagonal seen so far and
592 // regard anything over 0.001 of this as 'of interest'. This enables
593 // us to get a roughly populated scene with largest objects, setup
594 // the camera, and do first draw. We then do a
595 // TGLCamera::UpdateInterest() - which always return kTRUE, and
596 // thus fires an internal rebuild to fill scene properly and
597 // finally setup camera properly.
598
599 if (fInterestBox.IsEmpty())
600 {
601 if (box.Diagonal() >= fLargestSeen * 0.001)
602 {
603 if (box.Diagonal() > fLargestSeen) {
604 fLargestSeen = box.Diagonal();
605 }
606 interest = kTRUE;
607 }
608 }
609 else
610 {
611 // Objects are of interest if the have length ratio c.f. the
612 // current interest box, and they at least partially overlap it.
613 // Some objects have zero volume BBs - e.g. single points - skip
614 // the test for these as there is no way to threshold on 0.
615 if (box.IsEmpty())
616 {
617 interest = kTRUE;
618 }
619 else
620 {
621 if (ignoreSize || box.Diagonal() / fInterestBox.Diagonal() > 0.0001)
622 interest = fInterestBox.Overlap(box) != Rgl::kOutside;
623 }
624 }
625
626 return interest;
627}
628
629////////////////////////////////////////////////////////////////////////////////
630/// Update the internal interest box (fInterestBox) of the camera.
631/// The interest box is an orientated bounding box, calculated as
632/// an expanded container round the frustum. It is used to test if
633/// if object bounding boxes are of interest (should be accepted
634/// into viewer scene) for a camera - see TGLCamera::OfInterest()
635///
636/// The interest box is updated if the frustum is no longer contained
637/// in the existing one, or a new one calculated on the current frustum
638/// differs significantly in volume (camera has been zoomed/dollyed
639/// sizable amount).
640///
641/// If the interest box is updated we return kTRUE - kFALSE otherwise.
642
644{
645 Bool_t exposedUpdate = kFALSE;
646
647 // Construct a new interest box using the current frustum box as a basis
648 TGLBoundingBox frustumBox = Frustum(kTRUE);
649 TGLBoundingBox newInterestBox(frustumBox);
650
651 // The Z(2) axis of frustum (near->far plane) can be quite shallow c.f. X(0)/Y(1)
652 // For interest box we want to expand to ensure it is at least size
653 // of smaller X/Y to avoid excessive interest box recalculations
654 TGLVector3 frustumExtents = frustumBox.Extents();
655 Double_t minBoxLength = frustumExtents.Mag() * fgInterestBoxExpansion;
656 newInterestBox.Scale(minBoxLength/frustumExtents[0], minBoxLength/frustumExtents[1], minBoxLength/frustumExtents[2]);
657
658 // Calculate volume ratio of new to old
659 Double_t volRatio = 0.0;
660
661 // If the interest box is empty the interest is ALWAYS updated
662 // See TGLCamera::OfInterest() comment on bootstrapping
663 if (!fInterestBox.IsEmpty()) {
664 volRatio = newInterestBox.Volume() / fInterestBox.Volume();
665 }
666
667 // Update the existing interest box with new one if:
668 // 1. Volume ratio old/new interest has changed significantly
669 // 2. The current frustum is not inside existing interest
670 // 3. Force case (debugging)
671 if (volRatio > 8.0 || volRatio < 0.125 || fInterestBox.IsEmpty() ||
672 fInterestBox.Overlap(frustumBox) != Rgl::kInside || force)
673 {
675 fInterestBox = newInterestBox;
676
677 // Frustum should be fully contained now
678 if (fInterestBox.Overlap(frustumBox) != Rgl::kInside) {
679 Error("TGLCamera::UpdateInterest", "update interest box does not contain frustum");
680 }
681
682 exposedUpdate = kTRUE;
683
684 // Keep the real frustum (true and box versions) as debugging aid
686 fInterestFrustumAsBox = frustumBox;
687
688 if (gDebug>2 || force) {
689 Info("TGLCamera::UpdateInterest", "changed - volume ratio %f", volRatio );
690 }
691 }
692
693 return exposedUpdate;
694}
695
696////////////////////////////////////////////////////////////////////////////////
697/// Clear out the existing interest box
698
700{
702
703 // We also reset the bootstrapping variable - see
704 // TGLCamera::OfInterest comments.
705 fLargestSeen = 0.0;
706}
707
708////////////////////////////////////////////////////////////////////////////////
709/// Adjust a passed REFERENCE value 'val', based on screenShift delta.
710/// Two modifier flags ('mod1' / 'mod2' ) for sensitivity:
711///
712/// - mod1 = kFALSE, mod2 = kFALSE : normal sensitivity (screenShift/screenShiftRange)
713/// - mod1 = kTRUE, mod2 = kFALSE : 0.1x sensitivity
714/// - mod1 = kTRUE, mod2 = kTRUE : 0.01x sensitivity
715/// - mod1 = kFALSE, mod2 = kTRUE : 10.0x sensitivity
716///
717/// 'val' is modified and clamped to 'min' / 'max' range.
718/// Return bool kTRUE if val actually changed.
719///
720/// Used as common interaction function for adjusting zoom/dolly etc
721
723 Int_t screenShift, Int_t screenShiftRange,
724 Bool_t mod1, Bool_t mod2) const
725{
726 if (screenShift == 0) {
727 return kFALSE;
728 }
729
730 // Calculate a sensitivity based on passed modifiers
731 Double_t sens = val * static_cast<Double_t>(screenShift);
732
733 if (mod1) {
734 sens *= 0.1;
735 if (mod2) {
736 sens *= 0.1;
737 }
738 } else {
739 if (mod2) {
740 sens *= 10.0;
741 }
742 }
743
744 Double_t oldVal = val;
745 Double_t shift = sens / static_cast<Double_t>(screenShiftRange);
746 val -= shift;
747
748 if (val < min) {
749 val = min;
750 }
751 else if (val > max) {
752 val = max;
753 }
754
755 return val != oldVal;
756}
757
758////////////////////////////////////////////////////////////////////////////////
759/// Adjust a passed screen value and apply modifiers.
760/// See AdjustAndClampVal() for details.
761
763 Bool_t mod1, Bool_t mod2) const
764{
765 if (screenShift == 0)
766 return 0;
767
768 // Calculate a sensitivity based on passed modifiers
769 Double_t sens = 1.0;
770
771 if (mod1) {
772 sens *= 0.1;
773 if (mod2) {
774 sens *= 0.1;
775 }
776 } else {
777 if (mod2) {
778 sens *= 10.0;
779 }
780 }
781
782 return sens * deltaFactor * screenShift;
783}
784
785////////////////////////////////////////////////////////////////////////////////
786/// Draw out some debugging aids for the camera:
787///
788/// 1. The frustum used to create the current interest box (RED)
789/// 2. The same frustum as a squared off box (ORANGE)
790/// 3. The axis aligned version of the frustum used as interest box basis (YELLOW)
791/// 4. The current interest box (BLUE)
792
794{
795 // Interest box frustum base (RED)
796 glColor3d(1.0,0.0,0.0);
798
799 // Interest box frustum as box (ORANGE)
800 glColor3d(1.0,0.65,0.15);
802
803 // Current Interest box (BLUE)
804 glColor3d(0.0,0.0,1.0);
806
807 // Previous interest (GREY)
808 glColor3d(.8,.7,.6);
810
811 // Also draw line from current eye point out in eye direction - should not
812 // appear if calculated correctly
813 TGLVertex3 start = EyePoint();
814 TGLVertex3 end = start + EyeDirection();
815 glColor3d(1.0,1.0,1.0);
816 glBegin(GL_LINES);
817 glVertex3dv(start.CArr());
818 glVertex3dv(end.CArr());
819 glEnd();
820}
821
822////////////////////////////////////////////////////////////////////////////////
823/// Set camera center diffrent than scene center, if enable is kTRUE.
824
826{
827 if (fExternalCenter == enable)
828 return;
829
830 fExternalCenter = enable;
831 if (fExternalCenter)
833 else
835
838 TGLMatrix binv = fCamBase; binv.Invert();
839 fCamTrans = binv * bt;
840
841 IncTimeStamp();
842}
843
844////////////////////////////////////////////////////////////////////////////////
845/// Set camera center vector.
846
848{
849 if (fExternalCenter)
850 fExtCenter.Set(x, y, z);
851 else
852 fDefCenter.Set(x, y, z);
853
856 TGLMatrix binv = fCamBase; binv.Invert();
857 fCamTrans = binv * bt;
858
859 IncTimeStamp();
860}
861
862////////////////////////////////////////////////////////////////////////////////
863/// Set camera center vector and do not keep the same combined
864/// camera transformation matrix.
865/// It appears as if the camera warped to the new center.
866
868{
869 if (fExternalCenter)
870 fExtCenter.Set(x, y, z);
871 else
872 fDefCenter.Set(x, y, z);
873
875
876 IncTimeStamp();
877}
878
879////////////////////////////////////////////////////////////////////////////////
880/// Get angle between camera up axis.
881
883{
886 fCamBase.RotateIP(fwd);
887 return TMath::ACos(fwd*zdir);
888}
889
890////////////////////////////////////////////////////////////////////////////////
891/// Truck the camera - 'move camera parallel to film plane'.
892/// Returns kTRUE is redraw required (camera change), kFALSE otherwise.
893
895{
896 if (xDelta != 0 || yDelta != 0)
897 {
898 fCamTrans.MoveLF(2, xDelta);
899 fCamTrans.MoveLF(3, yDelta);
900
901 IncTimeStamp();
902 return kTRUE;
903 }
904 else
905 {
906 return kFALSE;
907 }
908}
909
910////////////////////////////////////////////////////////////////////////////////
911/// Rotate the camera round view volume center established in Setup().
912/// Arguments are:
913/// - xDelta - horizontal delta (pixels)
914/// - YDelta - vertical delta (pixels)
915
916Bool_t TGLCamera::Rotate(Int_t xDelta, Int_t yDelta, Bool_t mod1, Bool_t mod2)
917{
918 Double_t vRotate = AdjustDelta(xDelta, TMath::TwoPi() / fViewport.Width(), mod1, mod2);
919 Double_t hRotate = AdjustDelta(yDelta, TMath::Pi() / fViewport.Height(), mod1, mod2);
920
921 return RotateRad(hRotate, vRotate);
922}
923
924////////////////////////////////////////////////////////////////////////////////
925/// Rotate camera around center.
926
928{
929 using namespace TMath;
930
931 if (fWasArcBalled)
932 {
933 Double_t *M = fCamTrans.Arr();
934 Double_t d = M[2];
935 if (d > 1) d = 1;
936 else if (d < -1) d = -1; // Fix numerical errors
937
938 Double_t theta = ASin(d);
939 Double_t phi = Abs(Cos(theta)) > 8.7e-6 ? ATan2(M[1], M[0]) : ATan2(-M[4], M[5]);
940
941 M[0] = M[5] = M[10] = 1;
942 M[1] = M[2] = M[4] = M[6] = M[8] = M[9] = 0;
943 fCamTrans.RotateLF(1, 2, phi);
944 fCamTrans.RotateLF(1, 3, theta);
945 }
946
947 if (hRotate != 0.0 || fWasArcBalled)
948 {
953
954 Double_t deltaF = pos * fwd;
955 Double_t deltaU = pos * up;
956
957 // up vector lock
959
960 fCamBase.RotateIP(fwd);
961 Double_t theta = ACos(fwd*zdir);
962 if (theta + hRotate < fVAxisMinAngle)
963 hRotate = fVAxisMinAngle - theta;
964 else if (theta + hRotate > Pi() - fVAxisMinAngle)
965 hRotate = Pi() - fVAxisMinAngle - theta;
966
967 fCamTrans.MoveLF(1, -deltaF);
968 fCamTrans.MoveLF(3, -deltaU);
969 fCamTrans.RotateLF(3, 1, hRotate);
970 fCamTrans.MoveLF(3, deltaU);
971 fCamTrans.MoveLF(1, deltaF);
972
974 }
975 if (vRotate != 0.0)
976 {
977 fCamTrans.RotatePF(1, 2, -vRotate);
978 }
979
980 IncTimeStamp();
981 return kTRUE;
982}
983
984////////////////////////////////////////////////////////////////////////////////
985/// Rotate the camera round view volume center established in Setup().
986/// Arguments are:
987/// - xDelta - horizontal delta (pixels)
988/// - YDelta - vertical delta (pixels)
989
991{
992 Double_t vRotate = AdjustDelta(xDelta, TMath::TwoPi() / fViewport.Width(), mod1, mod2);
993 Double_t hRotate = AdjustDelta(yDelta, TMath::Pi() / fViewport.Height(), mod1, mod2);
994
995 return RotateArcBallRad(hRotate, vRotate);
996}
997
998////////////////////////////////////////////////////////////////////////////////
999/// Rotate camera around center.
1000
1002{
1003 using namespace TMath;
1004
1009
1010 Double_t deltaF = pos * fwd;
1011 Double_t deltaL = pos * lft;
1012 Double_t deltaU = pos * up;
1013
1014 fCamTrans.MoveLF(1, -deltaF);
1015 fCamTrans.MoveLF(2, -deltaL);
1016 fCamTrans.MoveLF(3, -deltaU);
1017
1018 if (hRotate != 0.0)
1019 {
1020 fCamTrans.RotateLF(3, 1, hRotate);
1021 }
1022 if (vRotate != 0.0)
1023 {
1024 fCamTrans.RotateLF(1, 2, -vRotate);
1025 }
1026
1027 fCamTrans.MoveLF(3, deltaU);
1028 fCamTrans.MoveLF(2, deltaL);
1029 fCamTrans.MoveLF(1, deltaF);
1030
1032
1033 IncTimeStamp();
1034 return kTRUE;
1035}
1036
1037////////////////////////////////////////////////////////////////////////////////
1038/// Dolly the camera - 'move camera along eye line, retaining lens focal length'.
1039/// Arguments are:
1040///
1041/// - 'delta' - mouse viewport delta (pixels) - +ive dolly in, -ive dolly out
1042/// - 'mod1' / 'mod2' - sensitivity modifiers - see TGLCamera::AdjustAndClampVal()
1043///
1044/// Returns kTRUE is redraw required (camera change), kFALSE otherwise.
1045
1047{
1048 Double_t step = AdjustDelta(delta, fDollyDistance, mod1, mod2);
1049 if (step == 0)
1050 return kFALSE;
1051
1052 fCamTrans.MoveLF(1, -step);
1053
1054 IncTimeStamp();
1055 return kTRUE;
1056}
#define GL_LINES
Definition GL_glu.h:284
#define d(i)
Definition RSha256.hxx:102
unsigned int UInt_t
Definition RtypesCore.h:46
constexpr Bool_t kFALSE
Definition RtypesCore.h:101
double Double_t
Definition RtypesCore.h:59
constexpr Bool_t kTRUE
Definition RtypesCore.h:100
#define ClassImp(name)
Definition Rtypes.h:377
std::pair< Bool_t, TGLLine3 > Intersection(const TGLPlane &p1, const TGLPlane &p2)
Find 3D line interestion of this plane with 'other'.
Definition TGLUtil.cxx:516
Int_t gDebug
Definition TROOT.cxx:595
Concrete class describing an orientated (free) or axis aligned box of 8 vertices.
Double_t Volume() const
TGLVector3 Extents() const
Double_t Diagonal() const
void SetEmpty()
Set bounding box empty - all vertices at (0,0,0)
Bool_t IsEmpty() const
void Scale(Double_t factor)
Isotropically scale bounding box along it's LOCAL axes, preserving center.
Rgl::EOverlap Overlap(const TGLPlane &plane) const
Find overlap (Inside, Outside, Partial) of plane c.f. bounding box.
void Draw(Bool_t solid=kFALSE) const
Draw the bounding box as either wireframe (default) of solid using current GL color.
Abstract base camera class - concrete classes for orthographic and perspective cameras derive from it...
Definition TGLCamera.h:44
Rgl::EOverlap FrustumOverlap(const TGLBoundingBox &box) const
Calculate overlap (kInside, kOutside, kPartial) of box with camera frustum Camera must have valid fru...
static UInt_t fgDollyDeltaSens
Definition TGLCamera.h:112
TGLVector3 EyeDirection() const
Extract the camera eye direction (vector), running from EyePoint() Camera must have valid frustum cac...
virtual Bool_t RotateRad(Double_t hRotate, Double_t vRotate)
Rotate camera around center.
Bool_t fCacheDirty
Definition TGLCamera.h:95
TGLVertex3 FrustumCenter() const
Find the center of the camera frustum from intersection of planes This method will work even with par...
TGLCamera()
Default base camera constructor.
Definition TGLCamera.cxx:44
TGLVector3 fDefCenter
Definition TGLCamera.h:82
void ResetInterest()
Clear out the existing interest box.
virtual Bool_t Dolly(Int_t delta, Bool_t mod1, Bool_t mod2)
Dolly the camera - 'move camera along eye line, retaining lens focal length'.
TGLBoundingBox Frustum(Bool_t asBox=kTRUE) const
expansion c.f. aligned current frustum box
TGLMatrix fCamBase
Definition TGLCamera.h:76
virtual Bool_t RotateArcBallRad(Double_t hRotate, Double_t vRotate)
Rotate camera around center.
TGLVector3 * fCenter
Definition TGLCamera.h:84
void IncTimeStamp()
Definition TGLCamera.h:124
TGLMatrix fCamTrans
Definition TGLCamera.h:77
TGLMatrix fProjM
no-pick projection matrix (cached)
Definition TGLCamera.h:98
Bool_t AdjustAndClampVal(Double_t &val, Double_t min, Double_t max, Int_t screenShift, Int_t screenShiftRange, Bool_t mod1, Bool_t mod2) const
Adjust a passed REFERENCE value 'val', based on screenShift delta.
TGLVector3 ViewportDeltaToWorld(const TGLVertex3 &worldRef, Double_t viewportXDelta, Double_t viewportYDelta, TGLMatrix *modviewMat=nullptr) const
Apply a 2D viewport delta (shift) to the projection of worldRef onto viewport, returning the resultan...
virtual Bool_t Truck(Double_t xDelta, Double_t yDelta)
Truck the camera - 'move camera parallel to film plane'.
Rgl::EOverlap ViewportOverlap(const TGLBoundingBox &box) const
Calculate overlap (kInside, kOutside, kPartial) of box projection onto viewport (as rect) against the...
TGLBoundingBox fInterestBox
viewport (GL coords - origin bottom left)
Definition TGLCamera.h:105
TGLBoundingBox fInterestFrustumAsBox
frustum basis of current interest box - NOT a true BB! (DEBUG)
Definition TGLCamera.h:63
TGLRect ViewportRect(const TGLBoundingBox &box, TGLBoundingBox::EFace face) const
Calculate viewport rectangle which just contains projection of single 'face' of world frame bounding ...
virtual Bool_t Rotate(Int_t xDelta, Int_t yDelta, Bool_t mod1, Bool_t mod2)
Rotate the camera round view volume center established in Setup().
void SetCenterVecWarp(Double_t x, Double_t y, Double_t z)
Set camera center vector and do not keep the same combined camera transformation matrix.
TGLVector3 fExtCenter
Definition TGLCamera.h:81
TGLRect fViewport
frustum planes (cached)
Definition TGLCamera.h:103
TGLVector3 WorldDeltaToViewport(const TGLVertex3 &worldRef, const TGLVector3 &worldDelta) const
Convert a 3D vector worldDelta (shift) about vertex worldRef to a viewport (screen) '3D' vector.
TGLMatrix fModVM
projection matrix (cached)
Definition TGLCamera.h:99
~TGLCamera() override
Base camera destructor.
Definition TGLCamera.cxx:92
TGLVertex3 WorldToViewport(const TGLVertex3 &worldVertex, TGLMatrix *modviewMat=nullptr) const
Convert a 3D world vertex to '3D' viewport (screen) one.
TGLPlane fFrustumPlanes[kPlanesPerFrustum]
object space clip matrix (cached)
Definition TGLCamera.h:101
Double_t GetTheta() const
Get angle between camera up axis.
Double_t fDollyDistance
Definition TGLCamera.h:91
std::pair< Bool_t, TGLVertex3 > ViewportPlaneIntersection(Double_t viewportX, Double_t viewportY, const TGLPlane &worldPlane) const
Find the intersection of projection of supplied viewport point (a 3D world line - see ViewportToWorld...
Bool_t UpdateInterest(Bool_t force)
Update the internal interest box (fInterestBox) of the camera.
void DrawDebugAids() const
Draw out some debugging aids for the camera:
TGLBoundingBox fInterestFrustum
previous interest box (DEBUG)
Definition TGLCamera.h:62
void UpdateCache() const
largest box diagonal seen in OfInterest() - used when bootstrapping interest box
Bool_t fExternalCenter
Definition TGLCamera.h:78
TGLMatrix fClipM
modelView matrix (cached)
Definition TGLCamera.h:100
@ kPlanesPerFrustum
Definition TGLCamera.h:54
Float_t fVAxisMinAngle
Definition TGLCamera.h:92
void SetExternalCenter(Bool_t x)
Set camera center diffrent than scene center, if enable is kTRUE.
void SetCenterVec(Double_t x, Double_t y, Double_t z)
Set camera center vector.
void SetViewport(const TGLRect &viewport)
Set viewport extents from passed 'viewport' rect.
Definition TGLCamera.cxx:99
TGLBoundingBox fPreviousInterestBox
Definition TGLCamera.h:61
Bool_t fWasArcBalled
Definition TGLCamera.h:80
virtual Bool_t RotateArcBall(Int_t xDelta, Int_t yDelta, Bool_t mod1, Bool_t mod2)
Rotate the camera round view volume center established in Setup().
Double_t fLargestSeen
the interest box - created in UpdateInterest()
Definition TGLCamera.h:106
static const Double_t fgInterestBoxExpansion
frustum basis (as box) of current interest box (DEBUG)
Definition TGLCamera.h:65
Bool_t OfInterest(const TGLBoundingBox &box, Bool_t ignoreSize) const
Calculate if the an object defined by world frame bounding box is 'of interest' to the camera.
TGLVertex3 EyePoint() const
Return the camera eye point (vertex) in world space Camera must have valid frustum cache - call Apply...
Double_t AdjustDelta(Double_t screenShift, Double_t deltaFactor, Bool_t mod1, Bool_t mod2) const
Adjust a passed screen value and apply modifiers.
TGLVertex3 ViewportToWorld(const TGLVertex3 &viewportVertex, TGLMatrix *modviewMat=nullptr) const
Convert a '3D' viewport vertex to 3D world one.
3D space, fixed length, line class, with direction / length 'vector', passing through point 'vertex'.
Definition TGLUtil.h:387
16 component (4x4) transform matrix - column MAJOR as per GL.
Definition TGLUtil.h:598
void RotateLF(Int_t i1, Int_t i2, Double_t amount)
Rotate in local frame.
Definition TGLUtil.cxx:897
Double_t Invert()
Invert the matrix, returns determinant.
Definition TGLUtil.cxx:971
void MoveLF(Int_t ai, Double_t amount)
Translate in local frame.
Definition TGLUtil.cxx:813
Double_t * Arr()
Definition TGLUtil.h:665
void RotatePF(Int_t i1, Int_t i2, Double_t amount)
Rotate in parent frame. Does optimised version of MultLeft.
Definition TGLUtil.cxx:914
void SetBaseVec(Int_t b, Double_t x, Double_t y, Double_t z)
Definition TGLUtil.h:733
TGLVector3 GetBaseVec(Int_t b) const
Definition TGLUtil.h:754
const Double_t * CArr() const
Definition TGLUtil.h:664
TGLVector3 GetTranslation() const
Return the translation component of matrix.
Definition TGLUtil.cxx:794
void RotateIP(TGLVector3 &v) const
Rotate vector in-place. Translation is not applied.
Definition TGLUtil.cxx:1087
void Set(const TGLVertex3 &origin, const TGLVector3 &zAxis, const TGLVector3 &xAxis=nullptr)
Set matrix which when applied puts local origin at 'origin' and the local Z axis in direction 'z'.
Definition TGLUtil.cxx:736
3D plane class - of format Ax + By + Cz + D = 0
Definition TGLUtil.h:525
void Set(const TGLPlane &other)
Assign from other.
Definition TGLUtil.cxx:425
TGLVertex3 NearestOn(const TGLVertex3 &point) const
Return nearest point on plane.
Definition TGLUtil.cxx:500
TGLVector3 Norm() const
Definition TGLUtil.h:558
Viewport (pixel base) 2D rectangle class.
Definition TGLUtil.h:422
const Int_t * CArr() const
Definition TGLUtil.h:443
Int_t Height() const
Definition TGLUtil.h:452
Int_t Width() const
Definition TGLUtil.h:450
Rgl::EOverlap Overlap(const TGLRect &other) const
Return overlap result (kInside, kOutside, kPartial) of this rect with 'other'.
Definition TGLUtil.cxx:297
void Expand(Int_t x, Int_t y)
Expand the rect to encompass point (x,y)
Definition TGLUtil.cxx:261
void SetCorner(Int_t x, Int_t y)
Definition TGLUtil.h:480
3 component (x/y/z) vector class.
Definition TGLUtil.h:248
Double_t Mag() const
Definition TGLUtil.h:298
3 component (x/y/z) vertex class.
Definition TGLUtil.h:84
Double_t X() const
Definition TGLUtil.h:119
Double_t Z() const
Definition TGLUtil.h:123
void Set(Double_t x, Double_t y, Double_t z)
Definition TGLUtil.h:210
Double_t Y() const
Definition TGLUtil.h:121
void Shift(TGLVector3 &shift)
Offset a vertex by vector 'shift'.
Definition TGLUtil.cxx:92
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:987
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:961
SCoord_t GetY() const
Definition TPoint.h:47
SCoord_t GetX() const
Definition TPoint.h:46
void box(Int_t pat, Double_t x1, Double_t y1, Double_t x2, Double_t y2)
Definition fillpatterns.C:1
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
EOverlap
Definition TGLUtil.h:35
@ kInside
Definition TGLUtil.h:36
@ kOutside
Definition TGLUtil.h:38
@ kPartial
Definition TGLUtil.h:37
TMath.
Definition TMathBase.h:35
Double_t ACos(Double_t)
Returns the principal value of the arc cosine of x, expressed in radians.
Definition TMath.h:632
constexpr Double_t Pi()
Definition TMath.h:37
constexpr Double_t TwoPi()
Definition TMath.h:44