0026344: Visualization - provide a support of zoom persistent selection
[occt.git] / src / Graphic3d / Graphic3d_Camera.cxx
1 // Created on: 2013-05-29
2 // Created by: Anton POLETAEV
3 // Copyright (c) 1999-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <gp_Pln.hxx>
17 #include <Standard_ShortReal.hxx>
18
19 #include <Graphic3d_Camera.hxx>
20 #include <Graphic3d_Vec4.hxx>
21 #include <Graphic3d_WorldViewProjState.hxx>
22
23 #include <Standard_Atomic.hxx>
24 #include <Standard_Assert.hxx>
25
26 #include <NCollection_Sequence.hxx>
27
28
29 namespace
30 {
31   // (degrees -> radians) * 0.5
32   static const Standard_Real DTR_HALF = 0.5 * 0.0174532925;
33
34   // default property values
35   static const Standard_Real DEFAULT_ZNEAR = 0.001;
36   static const Standard_Real DEFAULT_ZFAR  = 3000.0;
37
38   // atomic state counter
39   static volatile Standard_Integer THE_STATE_COUNTER = 0;
40
41   // minimum camera distance
42   static const Standard_Real MIN_DISTANCE = Pow (0.1, ShortRealDigits() - 2);
43
44   // z-range tolerance compatible with for floating point.
45   static Standard_Real zEpsilon()
46   {
47     return FLT_EPSILON;
48   }
49
50   // relative z-range tolerance compatible with for floating point.
51   static Standard_Real zEpsilon (const Standard_Real theValue)
52   {
53     if (theValue == 0)
54     {
55       return FLT_EPSILON;
56     }
57     Standard_Real aLogRadix = Log10 (Abs (theValue)) / Log10 (FLT_RADIX);
58     Standard_Real aExp = Floor (aLogRadix);
59     return FLT_EPSILON * Pow (FLT_RADIX, aExp);
60   };
61 };
62
63 // =======================================================================
64 // function : Graphic3d_Camera
65 // purpose  :
66 // =======================================================================
67 Graphic3d_Camera::Graphic3d_Camera()
68 : myUp (0.0, 1.0, 0.0),
69   myEye (0.0, 0.0, -1500.0),
70   myCenter (0.0, 0.0, 0.0),
71   myAxialScale (1.0, 1.0, 1.0),
72   myProjType (Projection_Orthographic),
73   myFOVy (45.0),
74   myZNear (DEFAULT_ZNEAR),
75   myZFar (DEFAULT_ZFAR),
76   myAspect (1.0),
77   myScale (1000.0),
78   myZFocus (1.0),
79   myZFocusType (FocusType_Relative),
80   myIOD (0.05),
81   myIODType (IODType_Relative)
82 {
83   myWorldViewProjState.Initialize ((Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
84                                    (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
85                                    this);
86 }
87
88 // =======================================================================
89 // function : Graphic3d_Camera
90 // purpose  :
91 // =======================================================================
92 Graphic3d_Camera::Graphic3d_Camera (const Handle(Graphic3d_Camera)& theOther)
93 {
94   myWorldViewProjState.Initialize (this);
95
96   Copy (theOther);
97 }
98
99 // =======================================================================
100 // function : CopyMappingData
101 // purpose  :
102 // =======================================================================
103 void Graphic3d_Camera::CopyMappingData (const Handle(Graphic3d_Camera)& theOtherCamera)
104 {
105   myFOVy       = theOtherCamera->myFOVy;
106   myZNear      = theOtherCamera->myZNear;
107   myZFar       = theOtherCamera->myZFar;
108   myAspect     = theOtherCamera->myAspect;
109   myScale      = theOtherCamera->myScale;
110   myZFocus     = theOtherCamera->myZFocus;
111   myZFocusType = theOtherCamera->myZFocusType;
112   myIOD        = theOtherCamera->myIOD;
113   myIODType    = theOtherCamera->myIODType;
114   myProjType   = theOtherCamera->myProjType;
115
116   myWorldViewProjState.ProjectionState() = theOtherCamera->ProjectionState();
117
118   InvalidateProjection();
119 }
120
121 // =======================================================================
122 // function : CopyOrientationData
123 // purpose  :
124 // =======================================================================
125 void Graphic3d_Camera::CopyOrientationData (const Handle(Graphic3d_Camera)& theOtherCamera)
126 {
127   myUp         = theOtherCamera->myUp;
128   myEye        = theOtherCamera->myEye;
129   myCenter     = theOtherCamera->myCenter;
130   myAxialScale = theOtherCamera->myAxialScale;
131
132   myWorldViewProjState.WorldViewState() = theOtherCamera->WorldViewState();
133
134   InvalidateOrientation();
135 }
136
137 // =======================================================================
138 // function : Copy
139 // purpose  :
140 // =======================================================================
141 void Graphic3d_Camera::Copy (const Handle(Graphic3d_Camera)& theOther)
142 {
143   CopyMappingData (theOther);
144   CopyOrientationData (theOther);
145 }
146
147 // =======================================================================
148 // function : SetEye
149 // purpose  :
150 // =======================================================================
151 void Graphic3d_Camera::SetEye (const gp_Pnt& theEye)
152 {
153   myEye = theEye;
154   InvalidateOrientation();
155 }
156
157 // =======================================================================
158 // function : SetCenter
159 // purpose  :
160 // =======================================================================
161 void Graphic3d_Camera::SetCenter (const gp_Pnt& theCenter)
162 {
163   myCenter = theCenter;
164   InvalidateOrientation();
165 }
166
167 // =======================================================================
168 // function : SetUp
169 // purpose  :
170 // =======================================================================
171 void Graphic3d_Camera::SetUp (const gp_Dir& theUp)
172 {
173   myUp = theUp;
174   InvalidateOrientation();
175 }
176
177 // =======================================================================
178 // function : SetAxialScale
179 // purpose  :
180 // =======================================================================
181 void Graphic3d_Camera::SetAxialScale (const gp_XYZ& theAxialScale)
182 {
183   myAxialScale = theAxialScale;
184   InvalidateOrientation();
185 }
186
187 // =======================================================================
188 // function : SetDistance
189 // purpose  :
190 // =======================================================================
191 void Graphic3d_Camera::SetDistance (const Standard_Real theDistance)
192 {
193   gp_Vec aCenter2Eye (Direction());
194   aCenter2Eye.Reverse();
195
196   // Camera should have non-zero distance.
197   aCenter2Eye.Scale (Max (theDistance, MIN_DISTANCE));
198   SetEye (Center().Translated (aCenter2Eye));
199 }
200
201 // =======================================================================
202 // function : Distance
203 // purpose  :
204 // =======================================================================
205 Standard_Real Graphic3d_Camera::Distance() const
206 {
207   return myEye.Distance (myCenter);
208 }
209
210 // =======================================================================
211 // function : SetDirection
212 // purpose  :
213 // =======================================================================
214 void Graphic3d_Camera::SetDirection (const gp_Dir& theDir)
215 {
216   gp_Vec aScaledDir (theDir);
217   aScaledDir.Scale (Distance());
218   aScaledDir.Reverse();
219   SetEye (Center().Translated (aScaledDir));
220 }
221
222 // =======================================================================
223 // function : Direction
224 // purpose  :
225 // =======================================================================
226 gp_Dir Graphic3d_Camera::Direction() const
227 {
228   return gp_Dir (gp_Vec (myEye, myCenter));
229 }
230
231 // =======================================================================
232 // function : SetScale
233 // purpose  :
234 // =======================================================================
235 void Graphic3d_Camera::SetScale (const Standard_Real theScale)
236 {
237   myScale = theScale;
238
239   switch (myProjType)
240   {
241     case Projection_Perspective  :
242     case Projection_Stereo       :
243     case Projection_MonoLeftEye  :
244     case Projection_MonoRightEye :
245     {
246       Standard_Real aDistance = theScale * 0.5 / Tan(myFOVy * M_PI / 360.0);
247       SetDistance (aDistance);
248     }
249
250     default :
251       break;
252   }
253
254   InvalidateProjection();
255 }
256
257 // =======================================================================
258 // function : Scale
259 // purpose  :
260 // =======================================================================
261 Standard_Real Graphic3d_Camera::Scale() const
262 {
263   switch (myProjType)
264   {
265     case Projection_Orthographic :
266       return myScale;
267
268     // case Projection_Perspective  :
269     // case Projection_Stereo       :
270     // case Projection_MonoLeftEye  :
271     // case Projection_MonoRightEye :
272     default :
273       return Distance() * 2.0 * Tan (myFOVy * M_PI / 360.0);
274   }
275 }
276
277 // =======================================================================
278 // function : SetProjectionType
279 // purpose  :
280 // =======================================================================
281 void Graphic3d_Camera::SetProjectionType (const Projection theProjectionType)
282 {
283   Projection anOldType = myProjType;
284
285   if (anOldType == theProjectionType)
286   {
287     return;
288   }
289
290   if (anOldType == Projection_Orthographic)
291   {
292     if (myZNear <= RealEpsilon())
293     {
294       myZNear = DEFAULT_ZNEAR;
295     }
296     if (myZFar <= RealEpsilon())
297     {
298       myZFar = DEFAULT_ZFAR;
299     }
300   }
301
302   myProjType = theProjectionType;
303
304   InvalidateProjection();
305 }
306
307 // =======================================================================
308 // function : SetFOVy
309 // purpose  :
310 // =======================================================================
311 void Graphic3d_Camera::SetFOVy (const Standard_Real theFOVy)
312 {
313   myFOVy = theFOVy;
314   InvalidateProjection();
315 }
316
317 // =======================================================================
318 // function : SetZRange
319 // purpose  :
320 // =======================================================================
321 void Graphic3d_Camera::SetZRange (const Standard_Real theZNear,
322                                   const Standard_Real theZFar)
323 {
324   Standard_ASSERT_RAISE (theZFar > theZNear, "ZFar should be greater than ZNear");
325   if (!IsOrthographic())
326   {
327     Standard_ASSERT_RAISE (theZNear > 0.0, "Only positive Z-Near is allowed for perspective camera");
328     Standard_ASSERT_RAISE (theZFar  > 0.0, "Only positive Z-Far is allowed for perspective camera");
329   }
330
331   myZNear = theZNear;
332   myZFar  = theZFar;
333
334   InvalidateProjection();
335 }
336
337 // =======================================================================
338 // function : SetAspect
339 // purpose  :
340 // =======================================================================
341 void Graphic3d_Camera::SetAspect (const Standard_Real theAspect)
342 {
343   myAspect = theAspect;
344   InvalidateProjection();
345 }
346
347 // =======================================================================
348 // function : SetZFocus
349 // purpose  :
350 // =======================================================================
351 void Graphic3d_Camera::SetZFocus(const FocusType theType, const Standard_Real theZFocus)
352 {
353   myZFocusType = theType;
354   myZFocus = theZFocus;
355   InvalidateProjection();
356 }
357
358 // =======================================================================
359 // function : SetIOD
360 // purpose  :
361 // =======================================================================
362 void Graphic3d_Camera::SetIOD (const IODType theType, const Standard_Real theIOD)
363 {
364   myIODType = theType;
365   myIOD = theIOD;
366   InvalidateProjection();
367 }
368
369 // =======================================================================
370 // function : OrthogonalizeUp
371 // purpose  :
372 // =======================================================================
373 void Graphic3d_Camera::OrthogonalizeUp()
374 {
375   SetUp (OrthogonalizedUp());
376 }
377
378 // =======================================================================
379 // function : OrthogonalizedUp
380 // purpose  :
381 // =======================================================================
382 gp_Dir Graphic3d_Camera::OrthogonalizedUp() const
383 {
384   gp_Dir aDir  = Direction();
385   gp_Dir aLeft = aDir.Crossed (Up());
386
387   // recompute up as: up = left x direction
388   return aLeft.Crossed (aDir);
389 }
390
391 // =======================================================================
392 // function : Transform
393 // purpose  :
394 // =======================================================================
395 void Graphic3d_Camera::Transform (const gp_Trsf& theTrsf)
396 {
397   myUp.Transform (theTrsf);
398   myEye.Transform (theTrsf);
399   myCenter.Transform (theTrsf);
400   InvalidateOrientation();
401 }
402
403 // =======================================================================
404 // function : safePointCast
405 // purpose  :
406 // =======================================================================
407 static Graphic3d_Vec4d safePointCast (const gp_Pnt& thePnt)
408 {
409   Standard_Real aLim = 1e15f;
410
411   // have to deal with values greater then max float
412   gp_Pnt aSafePoint = thePnt;
413   const Standard_Real aBigFloat = aLim * 0.1f;
414   if (Abs (aSafePoint.X()) > aLim)
415     aSafePoint.SetX (aSafePoint.X() >= 0 ? aBigFloat : -aBigFloat);
416   if (Abs (aSafePoint.Y()) > aLim)
417     aSafePoint.SetY (aSafePoint.Y() >= 0 ? aBigFloat : -aBigFloat);
418   if (Abs (aSafePoint.Z()) > aLim)
419     aSafePoint.SetZ (aSafePoint.Z() >= 0 ? aBigFloat : -aBigFloat);
420
421   // convert point
422   Graphic3d_Vec4d aPnt (aSafePoint.X(), aSafePoint.Y(), aSafePoint.Z(), 1.0);
423
424   return aPnt;
425 }
426
427 // =======================================================================
428 // function : Project
429 // purpose  :
430 // =======================================================================
431 gp_Pnt Graphic3d_Camera::Project (const gp_Pnt& thePnt) const
432 {
433   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
434   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
435
436   // use compatible type of point
437   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
438
439   aPnt = aViewMx * aPnt; // convert to view coordinate space
440   aPnt = aProjMx * aPnt; // convert to projection coordinate space
441
442   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
443
444   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
445 }
446
447 // =======================================================================
448 // function : UnProject
449 // purpose  :
450 // =======================================================================
451 gp_Pnt Graphic3d_Camera::UnProject (const gp_Pnt& thePnt) const
452 {
453   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
454   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
455
456   Graphic3d_Mat4d aInvView;
457   Graphic3d_Mat4d aInvProj;
458
459   // this case should never happen
460   if (!aViewMx.Inverted (aInvView) || !aProjMx.Inverted (aInvProj))
461   {
462     return gp_Pnt (0.0, 0.0, 0.0);
463   }
464
465   // use compatible type of point
466   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
467
468   aPnt = aInvProj * aPnt; // convert to view coordinate space
469   aPnt = aInvView * aPnt; // convert to world coordinate space
470
471   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
472
473   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
474 }
475
476 // =======================================================================
477 // function : ConvertView2Proj
478 // purpose  :
479 // =======================================================================
480 gp_Pnt Graphic3d_Camera::ConvertView2Proj (const gp_Pnt& thePnt) const
481 {
482   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
483
484   // use compatible type of point
485   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
486
487   aPnt = aProjMx * aPnt; // convert to projection coordinate space
488
489   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
490
491   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
492 }
493
494 // =======================================================================
495 // function : ConvertProj2View
496 // purpose  :
497 // =======================================================================
498 gp_Pnt Graphic3d_Camera::ConvertProj2View (const gp_Pnt& thePnt) const
499 {
500   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
501
502   Graphic3d_Mat4d aInvProj;
503
504   // this case should never happen, but...
505   if (!aProjMx.Inverted (aInvProj))
506   {
507     return gp_Pnt (0, 0, 0);
508   }
509
510   // use compatible type of point
511   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
512
513   aPnt = aInvProj * aPnt; // convert to view coordinate space
514
515   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
516
517   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
518 }
519
520 // =======================================================================
521 // function : ConvertWorld2View
522 // purpose  :
523 // =======================================================================
524 gp_Pnt Graphic3d_Camera::ConvertWorld2View (const gp_Pnt& thePnt) const
525 {
526   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
527
528   // use compatible type of point
529   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
530
531   aPnt = aViewMx * aPnt; // convert to view coordinate space
532
533   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
534
535   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
536 }
537
538 // =======================================================================
539 // function : ConvertView2World
540 // purpose  :
541 // =======================================================================
542 gp_Pnt Graphic3d_Camera::ConvertView2World (const gp_Pnt& thePnt) const
543 {
544   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
545
546   Graphic3d_Mat4d aInvView;
547
548   if (!aViewMx.Inverted (aInvView))
549   {
550     return gp_Pnt(0, 0, 0);
551   }
552
553   // use compatible type of point
554   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
555
556   aPnt = aInvView * aPnt; // convert to world coordinate space
557
558   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
559
560   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
561 }
562
563 // =======================================================================
564 // function : ViewDimensions
565 // purpose  :
566 // =======================================================================
567 gp_XYZ Graphic3d_Camera::ViewDimensions() const
568 {
569   // view plane dimensions
570   Standard_Real aSize = IsOrthographic() ? myScale : (2.0 * Distance() * Tan (DTR_HALF * myFOVy));
571   Standard_Real aSizeX, aSizeY;
572   if (myAspect > 1.0)
573   {
574     aSizeX = aSize * myAspect;
575     aSizeY = aSize;
576   }
577   else
578   {
579     aSizeX = aSize;
580     aSizeY = aSize / myAspect;
581   }
582
583   // and frustum depth
584   return gp_XYZ (aSizeX, aSizeY, myZFar - myZNear);
585 }
586
587 // =======================================================================
588 // function : Frustum
589 // purpose  :
590 // =======================================================================
591 void Graphic3d_Camera::Frustum (gp_Pln& theLeft,
592                                 gp_Pln& theRight,
593                                 gp_Pln& theBottom,
594                                 gp_Pln& theTop,
595                                 gp_Pln& theNear,
596                                 gp_Pln& theFar) const
597 {
598   gp_Vec aProjection = gp_Vec (Direction());
599   gp_Vec anUp        = OrthogonalizedUp();
600   gp_Vec aSide       = aProjection ^ anUp;
601
602   Standard_ASSERT_RAISE (
603     !aProjection.IsParallel (anUp, Precision::Angular()),
604      "Can not derive SIDE = PROJ x UP - directions are parallel");
605
606   theNear = gp_Pln (Eye().Translated (aProjection * ZNear()), aProjection);
607   theFar  = gp_Pln (Eye().Translated (aProjection * ZFar()), -aProjection);
608
609   Standard_Real aHScaleHor = Scale() * 0.5 * Aspect();
610   Standard_Real aHScaleVer = Scale() * 0.5;
611
612   gp_Pnt aPntLeft   = Center().Translated (aHScaleHor * -aSide);
613   gp_Pnt aPntRight  = Center().Translated (aHScaleHor *  aSide);
614   gp_Pnt aPntBottom = Center().Translated (aHScaleVer * -anUp);
615   gp_Pnt aPntTop    = Center().Translated (aHScaleVer *  anUp);
616
617   gp_Vec aDirLeft   =  aSide;
618   gp_Vec aDirRight  = -aSide;
619   gp_Vec aDirBottom =  anUp;
620   gp_Vec aDirTop    = -anUp;
621   if (!IsOrthographic())
622   {
623     Standard_Real aHFOVHor = ATan (Tan (DTR_HALF * FOVy()) * Aspect());
624     Standard_Real aHFOVVer = DTR_HALF * FOVy();
625     aDirLeft.Rotate   (gp_Ax1 (gp::Origin(), anUp),   aHFOVHor);
626     aDirRight.Rotate  (gp_Ax1 (gp::Origin(), anUp),  -aHFOVHor);
627     aDirBottom.Rotate (gp_Ax1 (gp::Origin(), aSide), -aHFOVVer);
628     aDirTop.Rotate    (gp_Ax1 (gp::Origin(), aSide),  aHFOVVer);
629   }
630
631   theLeft   = gp_Pln (aPntLeft,   aDirLeft);
632   theRight  = gp_Pln (aPntRight,  aDirRight);
633   theBottom = gp_Pln (aPntBottom, aDirBottom);
634   theTop    = gp_Pln (aPntTop,    aDirTop);
635 }
636
637 // =======================================================================
638 // function : OrientationMatrix
639 // purpose  :
640 // =======================================================================
641 const Graphic3d_Mat4d& Graphic3d_Camera::OrientationMatrix() const
642 {
643   return *UpdateOrientation (myMatricesD).Orientation;
644 }
645
646 // =======================================================================
647 // function : OrientationMatrixF
648 // purpose  :
649 // =======================================================================
650 const Graphic3d_Mat4& Graphic3d_Camera::OrientationMatrixF() const
651 {
652   return *UpdateOrientation (myMatricesF).Orientation;
653 }
654
655 // =======================================================================
656 // function : ProjectionMatrix
657 // purpose  :
658 // =======================================================================
659 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionMatrix() const
660 {
661   return *UpdateProjection (myMatricesD).MProjection;
662 }
663
664 // =======================================================================
665 // function : ProjectionMatrixF
666 // purpose  :
667 // =======================================================================
668 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionMatrixF() const
669 {
670   return *UpdateProjection (myMatricesF).MProjection;
671 }
672
673 // =======================================================================
674 // function : ProjectionStereoLeft
675 // purpose  :
676 // =======================================================================
677 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoLeft() const
678 {
679   return *UpdateProjection (myMatricesD).LProjection;
680 }
681
682 // =======================================================================
683 // function : ProjectionStereoLeftF
684 // purpose  :
685 // =======================================================================
686 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoLeftF() const
687 {
688   return *UpdateProjection (myMatricesF).LProjection;
689 }
690
691 // =======================================================================
692 // function : ProjectionStereoRight
693 // purpose  :
694 // =======================================================================
695 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoRight() const
696 {
697   return *UpdateProjection (myMatricesD).RProjection;
698 }
699
700 // =======================================================================
701 // function : ProjectionStereoRightF
702 // purpose  :
703 // =======================================================================
704 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoRightF() const
705 {
706   return *UpdateProjection (myMatricesF).RProjection;
707 }
708
709 // =======================================================================
710 // function : UpdateProjection
711 // purpose  :
712 // =======================================================================
713 template <typename Elem_t>
714 Graphic3d_Camera::TransformMatrices<Elem_t>&
715   Graphic3d_Camera::UpdateProjection (TransformMatrices<Elem_t>& theMatrices) const
716 {
717   if (theMatrices.IsProjectionValid())
718   {
719     return theMatrices; // for inline accessors
720   }
721
722   theMatrices.InitProjection();
723
724   // sets top of frustum based on FOVy and near clipping plane
725   Elem_t aScale   = static_cast<Elem_t> (myScale);
726   Elem_t aZNear   = static_cast<Elem_t> (myZNear);
727   Elem_t aZFar    = static_cast<Elem_t> (myZFar);
728   Elem_t anAspect = static_cast<Elem_t> (myAspect);
729   Elem_t aDXHalf = 0.0, aDYHalf = 0.0;
730   if (IsOrthographic())
731   {
732     aDXHalf = aScale * Elem_t (0.5);
733     aDYHalf = aScale * Elem_t (0.5);
734   }
735   else
736   {
737     aDXHalf = aZNear * Elem_t (Tan (DTR_HALF * myFOVy));
738     aDYHalf = aZNear * Elem_t (Tan (DTR_HALF * myFOVy));
739   }
740
741   if (anAspect > 1.0)
742   {
743     aDXHalf *= anAspect;
744   }
745   else
746   {
747     aDYHalf /= anAspect;
748   }
749
750   // sets right of frustum based on aspect ratio
751   Elem_t aLeft   = -aDXHalf;
752   Elem_t aRight  =  aDXHalf;
753   Elem_t aBot    = -aDYHalf;
754   Elem_t aTop    =  aDYHalf;
755
756   Elem_t aIOD  = myIODType == IODType_Relative 
757     ? static_cast<Elem_t> (myIOD * Distance())
758     : static_cast<Elem_t> (myIOD);
759
760   Elem_t aFocus = myZFocusType == FocusType_Relative 
761     ? static_cast<Elem_t> (myZFocus * Distance())
762     : static_cast<Elem_t> (myZFocus);
763
764   switch (myProjType)
765   {
766     case Projection_Orthographic :
767       OrthoProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection);
768       break;
769
770     case Projection_Perspective :
771       PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection);
772       break;
773
774     case Projection_MonoLeftEye :
775     {
776       StereoEyeProj (aLeft, aRight, aBot, aTop,
777                      aZNear, aZFar, aIOD, aFocus,
778                      Standard_True, *theMatrices.MProjection);
779       *theMatrices.LProjection = *theMatrices.MProjection;
780       break;
781     }
782
783     case Projection_MonoRightEye :
784     {
785       StereoEyeProj (aLeft, aRight, aBot, aTop,
786                      aZNear, aZFar, aIOD, aFocus,
787                      Standard_False, *theMatrices.MProjection);
788       *theMatrices.RProjection = *theMatrices.MProjection;
789       break;
790     }
791
792     case Projection_Stereo :
793     {
794       PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection);
795
796       StereoEyeProj (aLeft, aRight, aBot, aTop,
797                      aZNear, aZFar, aIOD, aFocus,
798                      Standard_True,
799                      *theMatrices.LProjection);
800
801       StereoEyeProj (aLeft, aRight, aBot, aTop,
802                      aZNear, aZFar, aIOD, aFocus,
803                      Standard_False,
804                      *theMatrices.RProjection);
805       break;
806     }
807   }
808
809   return theMatrices; // for inline accessors
810 }
811
812 // =======================================================================
813 // function : UpdateOrientation
814 // purpose  :
815 // =======================================================================
816 template <typename Elem_t>
817 Graphic3d_Camera::TransformMatrices<Elem_t>&
818   Graphic3d_Camera::UpdateOrientation (TransformMatrices<Elem_t>& theMatrices) const
819 {
820   if (theMatrices.IsOrientationValid())
821   {
822     return theMatrices; // for inline accessors
823   }
824
825   theMatrices.InitOrientation();
826
827   NCollection_Vec3<Elem_t> anEye (static_cast<Elem_t> (myEye.X()),
828                                   static_cast<Elem_t> (myEye.Y()),
829                                   static_cast<Elem_t> (myEye.Z()));
830
831   NCollection_Vec3<Elem_t> aCenter (static_cast<Elem_t> (myCenter.X()),
832                                     static_cast<Elem_t> (myCenter.Y()),
833                                     static_cast<Elem_t> (myCenter.Z()));
834
835   NCollection_Vec3<Elem_t> anUp (static_cast<Elem_t> (myUp.X()),
836                                  static_cast<Elem_t> (myUp.Y()),
837                                  static_cast<Elem_t> (myUp.Z()));
838
839   NCollection_Vec3<Elem_t> anAxialScale (static_cast<Elem_t> (myAxialScale.X()),
840                                          static_cast<Elem_t> (myAxialScale.Y()),
841                                          static_cast<Elem_t> (myAxialScale.Z()));
842
843   LookOrientation (anEye, aCenter, anUp, anAxialScale, *theMatrices.Orientation);
844
845   return theMatrices; // for inline accessors
846 }
847
848 // =======================================================================
849 // function : InvalidateProjection
850 // purpose  :
851 // =======================================================================
852 void Graphic3d_Camera::InvalidateProjection()
853 {
854   myMatricesD.ResetProjection();
855   myMatricesF.ResetProjection();
856   myWorldViewProjState.ProjectionState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
857 }
858
859 // =======================================================================
860 // function : InvalidateOrientation
861 // purpose  :
862 // =======================================================================
863 void Graphic3d_Camera::InvalidateOrientation()
864 {
865   myMatricesD.ResetOrientation();
866   myMatricesF.ResetOrientation();
867   myWorldViewProjState.WorldViewState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
868 }
869
870 // =======================================================================
871 // function : OrthoProj
872 // purpose  :
873 // =======================================================================
874 template <typename Elem_t>
875 void Graphic3d_Camera::OrthoProj (const Elem_t theLeft,
876                                   const Elem_t theRight,
877                                   const Elem_t theBottom,
878                                   const Elem_t theTop,
879                                   const Elem_t theNear,
880                                   const Elem_t theFar,
881                                   NCollection_Mat4<Elem_t>& theOutMx)
882 {
883   // row 0
884   theOutMx.ChangeValue (0, 0) = Elem_t (2.0) / (theRight - theLeft);
885   theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
886   theOutMx.ChangeValue (0, 2) = Elem_t (0.0);
887   theOutMx.ChangeValue (0, 3) = - (theRight + theLeft) / (theRight - theLeft);
888
889   // row 1
890   theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
891   theOutMx.ChangeValue (1, 1) = Elem_t (2.0) / (theTop - theBottom);
892   theOutMx.ChangeValue (1, 2) = Elem_t (0.0);
893   theOutMx.ChangeValue (1, 3) = - (theTop + theBottom) / (theTop - theBottom);
894
895   // row 2
896   theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
897   theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
898   theOutMx.ChangeValue (2, 2) = Elem_t (-2.0) / (theFar - theNear);
899   theOutMx.ChangeValue (2, 3) = - (theFar + theNear) / (theFar - theNear);
900
901   // row 3
902   theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
903   theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
904   theOutMx.ChangeValue (3, 2) = Elem_t (0.0);
905   theOutMx.ChangeValue (3, 3) = Elem_t (1.0);
906 }
907
908 // =======================================================================
909 // function : PerspectiveProj
910 // purpose  :
911 // =======================================================================
912 template <typename Elem_t>
913 void Graphic3d_Camera::PerspectiveProj (const Elem_t theLeft,
914                                         const Elem_t theRight,
915                                         const Elem_t theBottom,
916                                         const Elem_t theTop,
917                                         const Elem_t theNear,
918                                         const Elem_t theFar,
919                                         NCollection_Mat4<Elem_t>& theOutMx)
920 {
921   // column 0
922   theOutMx.ChangeValue (0, 0) = (Elem_t (2.0) * theNear) / (theRight - theLeft);
923   theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
924   theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
925   theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
926
927   // column 1
928   theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
929   theOutMx.ChangeValue (1, 1) = (Elem_t (2.0) * theNear) / (theTop - theBottom);
930   theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
931   theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
932
933   // column 2
934   theOutMx.ChangeValue (0, 2) = (theRight + theLeft) / (theRight - theLeft);
935   theOutMx.ChangeValue (1, 2) = (theTop + theBottom) / (theTop - theBottom);
936   theOutMx.ChangeValue (2, 2) = -(theFar + theNear) / (theFar - theNear);
937   theOutMx.ChangeValue (3, 2) = Elem_t (-1.0);
938
939   // column 3
940   theOutMx.ChangeValue (0, 3) = Elem_t (0.0);
941   theOutMx.ChangeValue (1, 3) = Elem_t (0.0);
942   theOutMx.ChangeValue (2, 3) = -(Elem_t (2.0) * theFar * theNear) / (theFar - theNear);
943   theOutMx.ChangeValue (3, 3) = Elem_t (0.0);
944 }
945
946 // =======================================================================
947 // function : StereoEyeProj
948 // purpose  :
949 // =======================================================================
950 template <typename Elem_t>
951 void Graphic3d_Camera::StereoEyeProj (const Elem_t theLeft,
952                                       const Elem_t theRight,
953                                       const Elem_t theBottom,
954                                       const Elem_t theTop,
955                                       const Elem_t theNear,
956                                       const Elem_t theFar,
957                                       const Elem_t theIOD,
958                                       const Elem_t theZFocus,
959                                       const Standard_Boolean theIsLeft,
960                                       NCollection_Mat4<Elem_t>& theOutMx)
961 {
962   Elem_t aDx = theIsLeft ? Elem_t (0.5) * theIOD : Elem_t (-0.5) * theIOD;
963   Elem_t aDXStereoShift = aDx * theNear / theZFocus;
964
965   // construct eye projection matrix
966   PerspectiveProj (theLeft  + aDXStereoShift,
967                    theRight + aDXStereoShift,
968                    theBottom, theTop, theNear, theFar,
969                    theOutMx);
970
971   if (theIOD != Elem_t (0.0))
972   {
973     // X translation to cancel parallax
974     theOutMx.Translate (NCollection_Vec3<Elem_t> (aDx, Elem_t (0.0), Elem_t (0.0)));
975   }
976 }
977
978 // =======================================================================
979 // function : LookOrientation
980 // purpose  :
981 // =======================================================================
982 template <typename Elem_t>
983 void Graphic3d_Camera::LookOrientation (const NCollection_Vec3<Elem_t>& theEye,
984                                         const NCollection_Vec3<Elem_t>& theLookAt,
985                                         const NCollection_Vec3<Elem_t>& theUpDir,
986                                         const NCollection_Vec3<Elem_t>& theAxialScale,
987                                         NCollection_Mat4<Elem_t>& theOutMx)
988 {
989   NCollection_Vec3<Elem_t> aForward = theLookAt - theEye;
990   aForward.Normalize();
991
992   // side = forward x up
993   NCollection_Vec3<Elem_t> aSide = NCollection_Vec3<Elem_t>::Cross (aForward, theUpDir);
994   aSide.Normalize();
995
996   // recompute up as: up = side x forward
997   NCollection_Vec3<Elem_t> anUp = NCollection_Vec3<Elem_t>::Cross (aSide, aForward);
998
999   NCollection_Mat4<Elem_t> aLookMx;
1000   aLookMx.SetRow (0, aSide);
1001   aLookMx.SetRow (1, anUp);
1002   aLookMx.SetRow (2, -aForward);
1003
1004   theOutMx.InitIdentity();
1005   theOutMx.Multiply (aLookMx);
1006   theOutMx.Translate (-theEye);
1007
1008   NCollection_Mat4<Elem_t> anAxialScaleMx;
1009   anAxialScaleMx.ChangeValue (0, 0) = theAxialScale.x();
1010   anAxialScaleMx.ChangeValue (1, 1) = theAxialScale.y();
1011   anAxialScaleMx.ChangeValue (2, 2) = theAxialScale.z();
1012
1013   theOutMx.Multiply (anAxialScaleMx);
1014 }
1015
1016 //=============================================================================
1017 //function : ZFitAll
1018 //purpose  :
1019 //=============================================================================
1020 void Graphic3d_Camera::ZFitAll (const Standard_Real theScaleFactor, const Bnd_Box& theMinMax, const Bnd_Box& theGraphicBB)
1021 {
1022   Standard_ASSERT_RAISE (theScaleFactor > 0.0, "Zero or negative scale factor is not allowed.");
1023
1024   // Method changes zNear and zFar parameters of camera so as to fit graphical structures
1025   // by their graphical boundaries. It precisely fits min max boundaries of primary application
1026   // objects (second argument), while it can sacrifice the real graphical boundaries of the
1027   // scene with infinite or helper objects (third argument) for the sake of perspective projection.
1028   if (theGraphicBB.IsVoid())
1029   {
1030     SetZRange (DEFAULT_ZNEAR, DEFAULT_ZFAR);
1031     return;
1032   }
1033
1034   // Measure depth of boundary points from camera eye.
1035   NCollection_Sequence<gp_Pnt> aPntsToMeasure;
1036
1037   Standard_Real aGraphicBB[6];
1038   theGraphicBB.Get (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2], aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]);
1039
1040   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2]));
1041   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[5]));
1042   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[2]));
1043   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[5]));
1044   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[2]));
1045   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[5]));
1046   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[2]));
1047   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]));
1048
1049   Standard_Boolean isFiniteMinMax = !theMinMax.IsVoid() && !theMinMax.IsWhole();
1050
1051   if (isFiniteMinMax)
1052   {
1053     Standard_Real aMinMax[6];
1054     theMinMax.Get (aMinMax[0], aMinMax[1], aMinMax[2], aMinMax[3], aMinMax[4], aMinMax[5]);
1055
1056     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[2]));
1057     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[5]));
1058     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[2]));
1059     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[5]));
1060     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[2]));
1061     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[5]));
1062     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[2]));
1063     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[5]));
1064   }
1065
1066   // Camera eye plane.
1067   gp_Dir aCamDir = Direction();
1068   gp_Pnt aCamEye = myEye;
1069   gp_Pln aCamPln (aCamEye, aCamDir);
1070
1071   Standard_Real aModelMinDist = RealLast();
1072   Standard_Real aModelMaxDist = RealFirst();
1073   Standard_Real aGraphMinDist = RealLast();
1074   Standard_Real aGraphMaxDist = RealFirst();
1075
1076   const gp_XYZ& anAxialScale = myAxialScale;
1077
1078   // Get minimum and maximum distances to the eye plane.
1079   Standard_Integer aCounter = 0;
1080   NCollection_Sequence<gp_Pnt>::Iterator aPntIt(aPntsToMeasure);
1081   for (; aPntIt.More(); aPntIt.Next())
1082   {
1083     gp_Pnt aMeasurePnt = aPntIt.Value();
1084
1085     aMeasurePnt = gp_Pnt (aMeasurePnt.X() * anAxialScale.X(),
1086                           aMeasurePnt.Y() * anAxialScale.Y(),
1087                           aMeasurePnt.Z() * anAxialScale.Z());
1088
1089     Standard_Real aDistance = aCamPln.Distance (aMeasurePnt);
1090
1091     // Check if the camera is intruded into the scene.
1092     if (aCamDir.IsOpposite (gp_Vec (aCamEye, aMeasurePnt), M_PI * 0.5))
1093     {
1094       aDistance *= -1;
1095     }
1096
1097     // The first eight points are from theGraphicBB, the last eight points are from theMinMax (can be absent).
1098     Standard_Real& aChangeMinDist = aCounter >= 8 ? aModelMinDist : aGraphMinDist;
1099     Standard_Real& aChangeMaxDist = aCounter >= 8 ? aModelMaxDist : aGraphMaxDist;
1100     aChangeMinDist = Min (aDistance, aChangeMinDist);
1101     aChangeMaxDist = Max (aDistance, aChangeMaxDist);
1102     aCounter++;
1103   }
1104
1105   // Compute depth of bounding box center.
1106   Standard_Real aMidDepth  = (aGraphMinDist + aGraphMaxDist) * 0.5;
1107   Standard_Real aHalfDepth = (aGraphMaxDist - aGraphMinDist) * 0.5;
1108
1109   // Compute enlarged or shrank near and far z ranges.
1110   Standard_Real aZNear  = aMidDepth - aHalfDepth * theScaleFactor;
1111   Standard_Real aZFar   = aMidDepth + aHalfDepth * theScaleFactor;
1112
1113   if (!IsOrthographic())
1114   {
1115     // Everything is behind the perspective camera.
1116     if (aZFar < zEpsilon())
1117     {
1118       SetZRange (DEFAULT_ZNEAR, DEFAULT_ZFAR);
1119       return;
1120     }
1121   }
1122
1123   //
1124   // Consider clipping errors due to double to single precision floating-point conversion.
1125   //
1126
1127   // Model to view transformation performs translation of points against eye position
1128   // in three dimensions. Both point coordinate and eye position values are converted from
1129   // double to single precision floating point numbers producing conversion errors. 
1130   // Epsilon (Mod) * 3.0 should safely compensate precision error for z coordinate after
1131   // translation assuming that the:
1132   // Epsilon (Eye.Mod()) * 3.0 > Epsilon (Eye.X()) + Epsilon (Eye.Y()) + Epsilon (Eye.Z()).
1133   Standard_Real aEyeConf = 3.0 * zEpsilon (myEye.XYZ().Modulus());
1134
1135   // Model to view transformation performs rotation of points according to view direction.
1136   // New z coordinate is computed as a multiplication of point's x, y, z coordinates by the
1137   // "forward" direction vector's x, y, z coordinates. Both point's and "z" direction vector's
1138   // values are converted from double to single precision floating point numbers producing
1139   // conversion errors.
1140   // Epsilon (Mod) * 6.0 should safely compensate the precision errors for the multiplication
1141   // of point coordinates by direction vector.
1142   gp_Pnt aGraphicMin = theGraphicBB.CornerMin();
1143   gp_Pnt aGraphicMax = theGraphicBB.CornerMax();
1144
1145   Standard_Real aModelConf = 6.0 * zEpsilon (aGraphicMin.XYZ().Modulus()) +
1146                              6.0 * zEpsilon (aGraphicMax.XYZ().Modulus());
1147
1148   // Compensate floating point conversion errors by increasing zNear, zFar to avoid clipping.
1149   aZNear -= zEpsilon (aZNear) + aEyeConf + aModelConf;
1150   aZFar  += zEpsilon (aZFar)  + aEyeConf + aModelConf;
1151
1152   if (!IsOrthographic())
1153   {
1154     // For perspective projection, the value of z in normalized device coordinates is non-linear
1155     // function of eye z coordinate. For fixed-point depth representation resolution of z in
1156     // model-view space will grow towards zFar plane and its scale depends mostly on how far is zNear
1157     // against camera's eye. The purpose of the code below is to select most appropriate zNear distance
1158     // to balance between clipping (less zNear, more chances to observe closely small models without clipping)
1159     // and resolution of depth. A well applicable criteria to this is a ratio between resolution of z at center
1160     // of model boundaries and the distance to that center point. The ratio is chosen empirically and validated
1161     // by tests database. It is considered to be ~0.001 (0.1%) for 24 bit depth buffer, for less depth bitness
1162     // the zNear will be placed similarly giving lower resolution.
1163     // Approximation of the formula for respectively large z range is:
1164     // zNear = [z * (1 + k) / (k * c)],
1165     // where:
1166     // z - distance to center of model boundaries;
1167     // k - chosen ratio, c - capacity of depth buffer;
1168     // k = 0.001, k * c = 1677.216, (1 + k) / (k * c) ~ 5.97E-4
1169     //
1170     // The function uses center of model boundaries computed from "theMinMax" boundaries (instead of using real
1171     // graphical boundaries of all displayed objects). That means that it can sacrifice resolution of presentation
1172     // of non primary ("infinite") application graphical objects in favor of better perspective projection of the
1173     // small applicative objects measured with "theMinMax" values.
1174     Standard_Real aZRange   = isFiniteMinMax ? aModelMaxDist - aModelMinDist : aGraphMaxDist - aGraphMinDist;
1175     Standard_Real aZMin     = isFiniteMinMax ? aModelMinDist : aGraphMinDist;
1176     Standard_Real aZ        = aZMin < 0 ? aZRange / 2.0 : aZRange / 2.0 + aZMin;
1177     Standard_Real aZNearMin = aZ * 5.97E-4;
1178     if (aZNear < aZNearMin)
1179     {
1180       // Clip zNear according to the minimum value matching the quality.
1181       aZNear = aZNearMin;
1182     }
1183     else
1184     {
1185       // Compensate zNear conversion errors for perspective projection.
1186       aZNear -= aZFar * zEpsilon (aZNear) / (aZFar - zEpsilon (aZNear));
1187     }
1188
1189     // Compensate zFar conversion errors for perspective projection.
1190     aZFar += zEpsilon (aZFar);
1191
1192     // Ensure that after all the zNear is not a negative value.
1193     if (aZNear < zEpsilon())
1194     {
1195       aZNear = zEpsilon();
1196     }
1197   }
1198
1199   SetZRange (aZNear, aZFar);
1200 }