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