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