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