0030412: Visualization, TKV3d - add presentation of camera frustum
[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 = Scale() * 0.5 * Aspect();
700   Standard_Real aHScaleVer = Scale() * 0.5;
701
702   gp_Pnt aPntLeft   = Center().Translated (aHScaleHor * -aSide);
703   gp_Pnt aPntRight  = Center().Translated (aHScaleHor *  aSide);
704   gp_Pnt aPntBottom = Center().Translated (aHScaleVer * -anUp);
705   gp_Pnt aPntTop    = Center().Translated (aHScaleVer *  anUp);
706
707   gp_Vec aDirLeft   =  aSide;
708   gp_Vec aDirRight  = -aSide;
709   gp_Vec aDirBottom =  anUp;
710   gp_Vec aDirTop    = -anUp;
711   if (!IsOrthographic())
712   {
713     Standard_Real aHFOVHor = ATan (Tan (DTR_HALF * FOVy()) * Aspect());
714     Standard_Real aHFOVVer = DTR_HALF * FOVy();
715     aDirLeft.Rotate   (gp_Ax1 (gp::Origin(), anUp),   aHFOVHor);
716     aDirRight.Rotate  (gp_Ax1 (gp::Origin(), anUp),  -aHFOVHor);
717     aDirBottom.Rotate (gp_Ax1 (gp::Origin(), aSide), -aHFOVVer);
718     aDirTop.Rotate    (gp_Ax1 (gp::Origin(), aSide),  aHFOVVer);
719   }
720
721   theLeft   = gp_Pln (aPntLeft,   aDirLeft);
722   theRight  = gp_Pln (aPntRight,  aDirRight);
723   theBottom = gp_Pln (aPntBottom, aDirBottom);
724   theTop    = gp_Pln (aPntTop,    aDirTop);
725 }
726
727 // =======================================================================
728 // function : OrientationMatrix
729 // purpose  :
730 // =======================================================================
731 const Graphic3d_Mat4d& Graphic3d_Camera::OrientationMatrix() const
732 {
733   return UpdateOrientation (myMatricesD).Orientation;
734 }
735
736 // =======================================================================
737 // function : OrientationMatrixF
738 // purpose  :
739 // =======================================================================
740 const Graphic3d_Mat4& Graphic3d_Camera::OrientationMatrixF() const
741 {
742   return UpdateOrientation (myMatricesF).Orientation;
743 }
744
745 // =======================================================================
746 // function : ProjectionMatrix
747 // purpose  :
748 // =======================================================================
749 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionMatrix() const
750 {
751   return UpdateProjection (myMatricesD).MProjection;
752 }
753
754 // =======================================================================
755 // function : ProjectionMatrixF
756 // purpose  :
757 // =======================================================================
758 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionMatrixF() const
759 {
760   return UpdateProjection (myMatricesF).MProjection;
761 }
762
763 // =======================================================================
764 // function : ProjectionStereoLeft
765 // purpose  :
766 // =======================================================================
767 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoLeft() const
768 {
769   return UpdateProjection (myMatricesD).LProjection;
770 }
771
772 // =======================================================================
773 // function : ProjectionStereoLeftF
774 // purpose  :
775 // =======================================================================
776 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoLeftF() const
777 {
778   return UpdateProjection (myMatricesF).LProjection;
779 }
780
781 // =======================================================================
782 // function : ProjectionStereoRight
783 // purpose  :
784 // =======================================================================
785 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoRight() const
786 {
787   return UpdateProjection (myMatricesD).RProjection;
788 }
789
790 // =======================================================================
791 // function : ProjectionStereoRightF
792 // purpose  :
793 // =======================================================================
794 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoRightF() const
795 {
796   return UpdateProjection (myMatricesF).RProjection;
797 }
798
799 // =======================================================================
800 // function : UpdateProjection
801 // purpose  :
802 // =======================================================================
803 template <typename Elem_t>
804 Graphic3d_Camera::TransformMatrices<Elem_t>&
805   Graphic3d_Camera::UpdateProjection (TransformMatrices<Elem_t>& theMatrices) const
806 {
807   if (theMatrices.IsProjectionValid())
808   {
809     return theMatrices; // for inline accessors
810   }
811
812   theMatrices.InitProjection();
813
814   // sets top of frustum based on FOVy and near clipping plane
815   Elem_t aScale   = static_cast<Elem_t> (myScale);
816   Elem_t aZNear   = static_cast<Elem_t> (myZNear);
817   Elem_t aZFar    = static_cast<Elem_t> (myZFar);
818   Elem_t anAspect = static_cast<Elem_t> (myAspect);
819   Elem_t aDXHalf = 0.0, aDYHalf = 0.0;
820   if (IsOrthographic())
821   {
822     aDXHalf = aScale * Elem_t (0.5);
823     aDYHalf = aScale * Elem_t (0.5);
824   }
825   else
826   {
827     aDXHalf = aZNear * Elem_t (myFOVyTan);
828     aDYHalf = aZNear * Elem_t (myFOVyTan);
829   }
830
831   if (anAspect > 1.0)
832   {
833     aDXHalf *= anAspect;
834   }
835   else
836   {
837     aDYHalf /= anAspect;
838   }
839
840   // sets right of frustum based on aspect ratio
841   Elem_t aLeft   = -aDXHalf;
842   Elem_t aRight  =  aDXHalf;
843   Elem_t aBot    = -aDYHalf;
844   Elem_t aTop    =  aDYHalf;
845
846   Elem_t aIOD  = myIODType == IODType_Relative 
847     ? static_cast<Elem_t> (myIOD * Distance())
848     : static_cast<Elem_t> (myIOD);
849
850   Elem_t aFocus = myZFocusType == FocusType_Relative 
851     ? static_cast<Elem_t> (myZFocus * Distance())
852     : static_cast<Elem_t> (myZFocus);
853
854   if (myTile.IsValid())
855   {
856     const Elem_t aDXFull = Elem_t(2) * aDXHalf;
857     const Elem_t aDYFull = Elem_t(2) * aDYHalf;
858     const Graphic3d_Vec2i anOffset = myTile.OffsetLowerLeft();
859     aLeft  = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x())                       / static_cast<Elem_t> (myTile.TotalSize.x());
860     aRight = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x() + myTile.TileSize.x()) / static_cast<Elem_t> (myTile.TotalSize.x());
861     aBot   = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y())                       / static_cast<Elem_t> (myTile.TotalSize.y());
862     aTop   = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y() + myTile.TileSize.y()) / static_cast<Elem_t> (myTile.TotalSize.y());
863   }
864
865   switch (myProjType)
866   {
867     case Projection_Orthographic :
868       OrthoProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
869       break;
870
871     case Projection_Perspective :
872       PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
873       break;
874
875     case Projection_MonoLeftEye :
876     {
877       StereoEyeProj (aLeft, aRight, aBot, aTop,
878                      aZNear, aZFar, aIOD, aFocus,
879                      Standard_True, theMatrices.MProjection);
880       theMatrices.LProjection = theMatrices.MProjection;
881       break;
882     }
883
884     case Projection_MonoRightEye :
885     {
886       StereoEyeProj (aLeft, aRight, aBot, aTop,
887                      aZNear, aZFar, aIOD, aFocus,
888                      Standard_False, theMatrices.MProjection);
889       theMatrices.RProjection = theMatrices.MProjection;
890       break;
891     }
892
893     case Projection_Stereo :
894     {
895       PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
896
897       StereoEyeProj (aLeft, aRight, aBot, aTop,
898                      aZNear, aZFar, aIOD, aFocus,
899                      Standard_True,
900                      theMatrices.LProjection);
901
902       StereoEyeProj (aLeft, aRight, aBot, aTop,
903                      aZNear, aZFar, aIOD, aFocus,
904                      Standard_False,
905                      theMatrices.RProjection);
906       break;
907     }
908   }
909
910   return theMatrices; // for inline accessors
911 }
912
913 // =======================================================================
914 // function : UpdateOrientation
915 // purpose  :
916 // =======================================================================
917 template <typename Elem_t>
918 Graphic3d_Camera::TransformMatrices<Elem_t>&
919   Graphic3d_Camera::UpdateOrientation (TransformMatrices<Elem_t>& theMatrices) const
920 {
921   if (theMatrices.IsOrientationValid())
922   {
923     return theMatrices; // for inline accessors
924   }
925
926   theMatrices.InitOrientation();
927
928   NCollection_Vec3<Elem_t> anEye (static_cast<Elem_t> (myEye.X()),
929                                   static_cast<Elem_t> (myEye.Y()),
930                                   static_cast<Elem_t> (myEye.Z()));
931
932   NCollection_Vec3<Elem_t> aCenter (static_cast<Elem_t> (myCenter.X()),
933                                     static_cast<Elem_t> (myCenter.Y()),
934                                     static_cast<Elem_t> (myCenter.Z()));
935
936   NCollection_Vec3<Elem_t> anUp (static_cast<Elem_t> (myUp.X()),
937                                  static_cast<Elem_t> (myUp.Y()),
938                                  static_cast<Elem_t> (myUp.Z()));
939
940   NCollection_Vec3<Elem_t> anAxialScale (static_cast<Elem_t> (myAxialScale.X()),
941                                          static_cast<Elem_t> (myAxialScale.Y()),
942                                          static_cast<Elem_t> (myAxialScale.Z()));
943
944   LookOrientation (anEye, aCenter, anUp, anAxialScale, theMatrices.Orientation);
945
946   return theMatrices; // for inline accessors
947 }
948
949 // =======================================================================
950 // function : InvalidateProjection
951 // purpose  :
952 // =======================================================================
953 void Graphic3d_Camera::InvalidateProjection()
954 {
955   myMatricesD.ResetProjection();
956   myMatricesF.ResetProjection();
957   myWorldViewProjState.ProjectionState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
958 }
959
960 // =======================================================================
961 // function : InvalidateOrientation
962 // purpose  :
963 // =======================================================================
964 void Graphic3d_Camera::InvalidateOrientation()
965 {
966   myMatricesD.ResetOrientation();
967   myMatricesF.ResetOrientation();
968   myWorldViewProjState.WorldViewState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
969 }
970
971 // =======================================================================
972 // function : OrthoProj
973 // purpose  :
974 // =======================================================================
975 template <typename Elem_t>
976 void Graphic3d_Camera::OrthoProj (const Elem_t theLeft,
977                                   const Elem_t theRight,
978                                   const Elem_t theBottom,
979                                   const Elem_t theTop,
980                                   const Elem_t theNear,
981                                   const Elem_t theFar,
982                                   NCollection_Mat4<Elem_t>& theOutMx)
983 {
984   // row 0
985   theOutMx.ChangeValue (0, 0) = Elem_t (2.0) / (theRight - theLeft);
986   theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
987   theOutMx.ChangeValue (0, 2) = Elem_t (0.0);
988   theOutMx.ChangeValue (0, 3) = - (theRight + theLeft) / (theRight - theLeft);
989
990   // row 1
991   theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
992   theOutMx.ChangeValue (1, 1) = Elem_t (2.0) / (theTop - theBottom);
993   theOutMx.ChangeValue (1, 2) = Elem_t (0.0);
994   theOutMx.ChangeValue (1, 3) = - (theTop + theBottom) / (theTop - theBottom);
995
996   // row 2
997   theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
998   theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
999   theOutMx.ChangeValue (2, 2) = Elem_t (-2.0) / (theFar - theNear);
1000   theOutMx.ChangeValue (2, 3) = - (theFar + theNear) / (theFar - theNear);
1001
1002   // row 3
1003   theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
1004   theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
1005   theOutMx.ChangeValue (3, 2) = Elem_t (0.0);
1006   theOutMx.ChangeValue (3, 3) = Elem_t (1.0);
1007 }
1008
1009 // =======================================================================
1010 // function : PerspectiveProj
1011 // purpose  :
1012 // =======================================================================
1013 template <typename Elem_t>
1014 void Graphic3d_Camera::PerspectiveProj (const Elem_t theLeft,
1015                                         const Elem_t theRight,
1016                                         const Elem_t theBottom,
1017                                         const Elem_t theTop,
1018                                         const Elem_t theNear,
1019                                         const Elem_t theFar,
1020                                         NCollection_Mat4<Elem_t>& theOutMx)
1021 {
1022   // column 0
1023   theOutMx.ChangeValue (0, 0) = (Elem_t (2.0) * theNear) / (theRight - theLeft);
1024   theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
1025   theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
1026   theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
1027
1028   // column 1
1029   theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
1030   theOutMx.ChangeValue (1, 1) = (Elem_t (2.0) * theNear) / (theTop - theBottom);
1031   theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
1032   theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
1033
1034   // column 2
1035   theOutMx.ChangeValue (0, 2) = (theRight + theLeft) / (theRight - theLeft);
1036   theOutMx.ChangeValue (1, 2) = (theTop + theBottom) / (theTop - theBottom);
1037   theOutMx.ChangeValue (2, 2) = -(theFar + theNear) / (theFar - theNear);
1038   theOutMx.ChangeValue (3, 2) = Elem_t (-1.0);
1039
1040   // column 3
1041   theOutMx.ChangeValue (0, 3) = Elem_t (0.0);
1042   theOutMx.ChangeValue (1, 3) = Elem_t (0.0);
1043   theOutMx.ChangeValue (2, 3) = -(Elem_t (2.0) * theFar * theNear) / (theFar - theNear);
1044   theOutMx.ChangeValue (3, 3) = Elem_t (0.0);
1045 }
1046
1047 // =======================================================================
1048 // function : StereoEyeProj
1049 // purpose  :
1050 // =======================================================================
1051 template <typename Elem_t>
1052 void Graphic3d_Camera::StereoEyeProj (const Elem_t theLeft,
1053                                       const Elem_t theRight,
1054                                       const Elem_t theBottom,
1055                                       const Elem_t theTop,
1056                                       const Elem_t theNear,
1057                                       const Elem_t theFar,
1058                                       const Elem_t theIOD,
1059                                       const Elem_t theZFocus,
1060                                       const Standard_Boolean theIsLeft,
1061                                       NCollection_Mat4<Elem_t>& theOutMx)
1062 {
1063   Elem_t aDx = theIsLeft ? Elem_t (0.5) * theIOD : Elem_t (-0.5) * theIOD;
1064   Elem_t aDXStereoShift = aDx * theNear / theZFocus;
1065
1066   // construct eye projection matrix
1067   PerspectiveProj (theLeft  + aDXStereoShift,
1068                    theRight + aDXStereoShift,
1069                    theBottom, theTop, theNear, theFar,
1070                    theOutMx);
1071
1072   if (theIOD != Elem_t (0.0))
1073   {
1074     // X translation to cancel parallax
1075     theOutMx.Translate (NCollection_Vec3<Elem_t> (aDx, Elem_t (0.0), Elem_t (0.0)));
1076   }
1077 }
1078
1079 // =======================================================================
1080 // function : LookOrientation
1081 // purpose  :
1082 // =======================================================================
1083 template <typename Elem_t>
1084 void Graphic3d_Camera::LookOrientation (const NCollection_Vec3<Elem_t>& theEye,
1085                                         const NCollection_Vec3<Elem_t>& theLookAt,
1086                                         const NCollection_Vec3<Elem_t>& theUpDir,
1087                                         const NCollection_Vec3<Elem_t>& theAxialScale,
1088                                         NCollection_Mat4<Elem_t>& theOutMx)
1089 {
1090   NCollection_Vec3<Elem_t> aForward = theLookAt - theEye;
1091   aForward.Normalize();
1092
1093   // side = forward x up
1094   NCollection_Vec3<Elem_t> aSide = NCollection_Vec3<Elem_t>::Cross (aForward, theUpDir);
1095   aSide.Normalize();
1096
1097   // recompute up as: up = side x forward
1098   NCollection_Vec3<Elem_t> anUp = NCollection_Vec3<Elem_t>::Cross (aSide, aForward);
1099
1100   NCollection_Mat4<Elem_t> aLookMx;
1101   aLookMx.SetRow (0, aSide);
1102   aLookMx.SetRow (1, anUp);
1103   aLookMx.SetRow (2, -aForward);
1104
1105   theOutMx.InitIdentity();
1106   theOutMx.Multiply (aLookMx);
1107   theOutMx.Translate (-theEye);
1108
1109   NCollection_Mat4<Elem_t> anAxialScaleMx;
1110   anAxialScaleMx.ChangeValue (0, 0) = theAxialScale.x();
1111   anAxialScaleMx.ChangeValue (1, 1) = theAxialScale.y();
1112   anAxialScaleMx.ChangeValue (2, 2) = theAxialScale.z();
1113
1114   theOutMx.Multiply (anAxialScaleMx);
1115 }
1116
1117 //=============================================================================
1118 //function : ZFitAll
1119 //purpose  :
1120 //=============================================================================
1121 bool Graphic3d_Camera::ZFitAll (const Standard_Real theScaleFactor,
1122                                 const Bnd_Box&      theMinMax,
1123                                 const Bnd_Box&      theGraphicBB,
1124                                 Standard_Real&      theZNear,
1125                                 Standard_Real&      theZFar) const
1126 {
1127   Standard_ASSERT_RAISE (theScaleFactor > 0.0, "Zero or negative scale factor is not allowed.");
1128
1129   // Method changes zNear and zFar parameters of camera so as to fit graphical structures
1130   // by their graphical boundaries. It precisely fits min max boundaries of primary application
1131   // objects (second argument), while it can sacrifice the real graphical boundaries of the
1132   // scene with infinite or helper objects (third argument) for the sake of perspective projection.
1133   if (theGraphicBB.IsVoid())
1134   {
1135     theZNear = DEFAULT_ZNEAR;
1136     theZFar  = DEFAULT_ZFAR;
1137     return false;
1138   }
1139
1140   // Measure depth of boundary points from camera eye.
1141   NCollection_Sequence<gp_Pnt> aPntsToMeasure;
1142
1143   Standard_Real aGraphicBB[6];
1144   theGraphicBB.Get (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2], aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]);
1145
1146   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2]));
1147   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[5]));
1148   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[2]));
1149   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[5]));
1150   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[2]));
1151   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[5]));
1152   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[2]));
1153   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]));
1154
1155   Standard_Boolean isFiniteMinMax = !theMinMax.IsVoid() && !theMinMax.IsWhole();
1156
1157   if (isFiniteMinMax)
1158   {
1159     Standard_Real aMinMax[6];
1160     theMinMax.Get (aMinMax[0], aMinMax[1], aMinMax[2], aMinMax[3], aMinMax[4], aMinMax[5]);
1161
1162     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[2]));
1163     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[5]));
1164     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[2]));
1165     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[5]));
1166     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[2]));
1167     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[5]));
1168     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[2]));
1169     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[5]));
1170   }
1171
1172   // Camera eye plane.
1173   gp_Dir aCamDir = Direction();
1174   gp_Pnt aCamEye = myEye;
1175   gp_Pln aCamPln (aCamEye, aCamDir);
1176
1177   Standard_Real aModelMinDist = RealLast();
1178   Standard_Real aModelMaxDist = RealFirst();
1179   Standard_Real aGraphMinDist = RealLast();
1180   Standard_Real aGraphMaxDist = RealFirst();
1181
1182   const gp_XYZ& anAxialScale = myAxialScale;
1183
1184   // Get minimum and maximum distances to the eye plane.
1185   Standard_Integer aCounter = 0;
1186   NCollection_Sequence<gp_Pnt>::Iterator aPntIt(aPntsToMeasure);
1187   for (; aPntIt.More(); aPntIt.Next())
1188   {
1189     gp_Pnt aMeasurePnt = aPntIt.Value();
1190
1191     aMeasurePnt = gp_Pnt (aMeasurePnt.X() * anAxialScale.X(),
1192                           aMeasurePnt.Y() * anAxialScale.Y(),
1193                           aMeasurePnt.Z() * anAxialScale.Z());
1194
1195     Standard_Real aDistance = aCamPln.Distance (aMeasurePnt);
1196
1197     // Check if the camera is intruded into the scene.
1198     if (aCamDir.IsOpposite (gp_Vec (aCamEye, aMeasurePnt), M_PI * 0.5))
1199     {
1200       aDistance *= -1;
1201     }
1202
1203     // The first eight points are from theGraphicBB, the last eight points are from theMinMax (can be absent).
1204     Standard_Real& aChangeMinDist = aCounter >= 8 ? aModelMinDist : aGraphMinDist;
1205     Standard_Real& aChangeMaxDist = aCounter >= 8 ? aModelMaxDist : aGraphMaxDist;
1206     aChangeMinDist = Min (aDistance, aChangeMinDist);
1207     aChangeMaxDist = Max (aDistance, aChangeMaxDist);
1208     aCounter++;
1209   }
1210
1211   // Compute depth of bounding box center.
1212   Standard_Real aMidDepth  = (aGraphMinDist + aGraphMaxDist) * 0.5;
1213   Standard_Real aHalfDepth = (aGraphMaxDist - aGraphMinDist) * 0.5;
1214
1215   // Compute enlarged or shrank near and far z ranges.
1216   Standard_Real aZNear  = aMidDepth - aHalfDepth * theScaleFactor;
1217   Standard_Real aZFar   = aMidDepth + aHalfDepth * theScaleFactor;
1218
1219   if (!IsOrthographic())
1220   {
1221     // Everything is behind the perspective camera.
1222     if (aZFar < zEpsilon())
1223     {
1224       theZNear = DEFAULT_ZNEAR;
1225       theZFar  = DEFAULT_ZFAR;
1226       return false;
1227     }
1228   }
1229
1230   //
1231   // Consider clipping errors due to double to single precision floating-point conversion.
1232   //
1233
1234   // Model to view transformation performs translation of points against eye position
1235   // in three dimensions. Both point coordinate and eye position values are converted from
1236   // double to single precision floating point numbers producing conversion errors. 
1237   // Epsilon (Mod) * 3.0 should safely compensate precision error for z coordinate after
1238   // translation assuming that the:
1239   // Epsilon (Eye.Mod()) * 3.0 > Epsilon (Eye.X()) + Epsilon (Eye.Y()) + Epsilon (Eye.Z()).
1240   Standard_Real aEyeConf = 3.0 * zEpsilon (myEye.XYZ().Modulus());
1241
1242   // Model to view transformation performs rotation of points according to view direction.
1243   // New z coordinate is computed as a multiplication of point's x, y, z coordinates by the
1244   // "forward" direction vector's x, y, z coordinates. Both point's and "z" direction vector's
1245   // values are converted from double to single precision floating point numbers producing
1246   // conversion errors.
1247   // Epsilon (Mod) * 6.0 should safely compensate the precision errors for the multiplication
1248   // of point coordinates by direction vector.
1249   gp_Pnt aGraphicMin = theGraphicBB.CornerMin();
1250   gp_Pnt aGraphicMax = theGraphicBB.CornerMax();
1251
1252   Standard_Real aModelConf = 6.0 * zEpsilon (aGraphicMin.XYZ().Modulus()) +
1253                              6.0 * zEpsilon (aGraphicMax.XYZ().Modulus());
1254
1255   // Compensate floating point conversion errors by increasing zNear, zFar to avoid clipping.
1256   aZNear -= zEpsilon (aZNear) + aEyeConf + aModelConf;
1257   aZFar  += zEpsilon (aZFar)  + aEyeConf + aModelConf;
1258
1259   if (!IsOrthographic())
1260   {
1261     // For perspective projection, the value of z in normalized device coordinates is non-linear
1262     // function of eye z coordinate. For fixed-point depth representation resolution of z in
1263     // model-view space will grow towards zFar plane and its scale depends mostly on how far is zNear
1264     // against camera's eye. The purpose of the code below is to select most appropriate zNear distance
1265     // to balance between clipping (less zNear, more chances to observe closely small models without clipping)
1266     // and resolution of depth. A well applicable criteria to this is a ratio between resolution of z at center
1267     // of model boundaries and the distance to that center point. The ratio is chosen empirically and validated
1268     // by tests database. It is considered to be ~0.001 (0.1%) for 24 bit depth buffer, for less depth bitness
1269     // the zNear will be placed similarly giving lower resolution.
1270     // Approximation of the formula for respectively large z range is:
1271     // zNear = [z * (1 + k) / (k * c)],
1272     // where:
1273     // z - distance to center of model boundaries;
1274     // k - chosen ratio, c - capacity of depth buffer;
1275     // k = 0.001, k * c = 1677.216, (1 + k) / (k * c) ~ 5.97E-4
1276     //
1277     // The function uses center of model boundaries computed from "theMinMax" boundaries (instead of using real
1278     // graphical boundaries of all displayed objects). That means that it can sacrifice resolution of presentation
1279     // of non primary ("infinite") application graphical objects in favor of better perspective projection of the
1280     // small applicative objects measured with "theMinMax" values.
1281     Standard_Real aZRange   = isFiniteMinMax ? aModelMaxDist - aModelMinDist : aGraphMaxDist - aGraphMinDist;
1282     Standard_Real aZMin     = isFiniteMinMax ? aModelMinDist : aGraphMinDist;
1283     Standard_Real aZ        = aZMin < 0 ? aZRange / 2.0 : aZRange / 2.0 + aZMin;
1284     Standard_Real aZNearMin = aZ * 5.97E-4;
1285     if (aZNear < aZNearMin)
1286     {
1287       // Clip zNear according to the minimum value matching the quality.
1288       aZNear = aZNearMin;
1289     }
1290     else
1291     {
1292       // Compensate zNear conversion errors for perspective projection.
1293       aZNear -= aZFar * zEpsilon (aZNear) / (aZFar - zEpsilon (aZNear));
1294     }
1295
1296     // Compensate zFar conversion errors for perspective projection.
1297     aZFar += zEpsilon (aZFar);
1298
1299     // Ensure that after all the zNear is not a negative value.
1300     if (aZNear < zEpsilon())
1301     {
1302       aZNear = zEpsilon();
1303     }
1304   }
1305
1306   theZNear = aZNear;
1307   theZFar  = aZFar;
1308   return true;
1309 }
1310
1311 //=============================================================================
1312 //function : Interpolate
1313 //purpose  :
1314 //=============================================================================
1315 template<>
1316 Standard_EXPORT void NCollection_Lerp<Handle(Graphic3d_Camera)>::Interpolate (const double theT,
1317                                                                               Handle(Graphic3d_Camera)& theCamera) const
1318 {
1319   if (Abs (theT - 1.0) < Precision::Confusion())
1320   {
1321     // just copy end-point transformation
1322     theCamera->Copy (myEnd);
1323     return;
1324   }
1325
1326   theCamera->Copy (myStart);
1327   if (Abs (theT - 0.0) < Precision::Confusion())
1328   {
1329     return;
1330   }
1331
1332   // apply rotation
1333   {
1334     gp_Ax3 aCamStart = cameraToAx3 (*myStart);
1335     gp_Ax3 aCamEnd   = cameraToAx3 (*myEnd);
1336     gp_Trsf aTrsfStart, aTrsfEnd;
1337     aTrsfStart.SetTransformation (aCamStart, gp::XOY());
1338     aTrsfEnd  .SetTransformation (aCamEnd,   gp::XOY());
1339
1340     gp_Quaternion aRotStart = aTrsfStart.GetRotation();
1341     gp_Quaternion aRotEnd   = aTrsfEnd  .GetRotation();
1342     gp_Quaternion aRotDelta = aRotEnd * aRotStart.Inverted();
1343     gp_Quaternion aRot = gp_QuaternionNLerp::Interpolate (gp_Quaternion(), aRotDelta, theT);
1344     gp_Trsf aTrsfRot;
1345     aTrsfRot.SetRotation (aRot);
1346     theCamera->Transform (aTrsfRot);
1347   }
1348
1349   // apply translation
1350   {
1351     gp_XYZ aCenter  = NCollection_Lerp<gp_XYZ>::Interpolate (myStart->Center().XYZ(), myEnd->Center().XYZ(), theT);
1352     gp_XYZ anEye    = NCollection_Lerp<gp_XYZ>::Interpolate (myStart->Eye().XYZ(),    myEnd->Eye().XYZ(),    theT);
1353     gp_XYZ anAnchor = aCenter;
1354     Standard_Real aKc = 0.0;
1355
1356     const Standard_Real aDeltaCenter = myStart->Center().Distance (myEnd->Center());
1357     const Standard_Real aDeltaEye    = myStart->Eye()   .Distance (myEnd->Eye());
1358     if (aDeltaEye <= gp::Resolution())
1359     {
1360       anAnchor = anEye;
1361       aKc = 1.0;
1362     }
1363     else if (aDeltaCenter > gp::Resolution())
1364     {
1365       aKc = aDeltaCenter / (aDeltaCenter + aDeltaEye);
1366
1367       const gp_XYZ anAnchorStart = NCollection_Lerp<gp_XYZ>::Interpolate (myStart->Center().XYZ(), myStart->Eye().XYZ(), aKc);
1368       const gp_XYZ anAnchorEnd   = NCollection_Lerp<gp_XYZ>::Interpolate (myEnd  ->Center().XYZ(), myEnd  ->Eye().XYZ(), aKc);
1369       anAnchor = NCollection_Lerp<gp_XYZ>::Interpolate (anAnchorStart, anAnchorEnd, theT);
1370     }
1371
1372     const gp_Vec        aDirEyeToCenter     = theCamera->Direction();
1373     const Standard_Real aDistEyeCenterStart = myStart->Eye().Distance (myStart->Center());
1374     const Standard_Real aDistEyeCenterEnd   = myEnd  ->Eye().Distance (myEnd  ->Center());
1375     const Standard_Real aDistEyeCenter      = NCollection_Lerp<Standard_Real>::Interpolate (aDistEyeCenterStart, aDistEyeCenterEnd, theT);
1376     aCenter = anAnchor + aDirEyeToCenter.XYZ() * aDistEyeCenter * aKc;
1377     anEye   = anAnchor - aDirEyeToCenter.XYZ() * aDistEyeCenter * (1.0 - aKc);
1378
1379     theCamera->SetCenter (aCenter);
1380     theCamera->SetEye    (anEye);
1381   }
1382
1383   // apply scaling
1384   if (Abs(myStart->Scale() - myEnd->Scale()) > Precision::Confusion()
1385    && myStart->IsOrthographic())
1386   {
1387     const Standard_Real aScale = NCollection_Lerp<Standard_Real>::Interpolate (myStart->Scale(), myEnd->Scale(), theT);
1388     theCamera->SetScale (aScale);
1389   }
1390 }
1391
1392 //=======================================================================
1393 //function : FrustumPoints
1394 //purpose  :
1395 //=======================================================================
1396 void Graphic3d_Camera::FrustumPoints (NCollection_Array1<Graphic3d_Vec3d>& thePoints) const
1397 {
1398   if (thePoints.Length() != FrustumVerticesNB)
1399   {
1400     thePoints.Resize (0, FrustumVerticesNB, Standard_False);
1401   }
1402
1403   const Graphic3d_Mat4d& aProjectionMat = ProjectionMatrix();
1404   const Graphic3d_Mat4d& aWorldViewMat  = OrientationMatrix();
1405
1406   Standard_Real nLeft = 0.0, nRight = 0.0, nTop = 0.0, nBottom = 0.0;
1407   Standard_Real fLeft = 0.0, fRight = 0.0, fTop = 0.0, fBottom = 0.0;
1408   Standard_Real aNear = 0.0, aFar = 0.0;
1409   if (!IsOrthographic())
1410   {
1411     // handle perspective projection
1412     aNear = aProjectionMat.GetValue (2, 3) / (-1.0 + aProjectionMat.GetValue (2, 2));
1413     aFar  = aProjectionMat.GetValue (2, 3) / ( 1.0 + aProjectionMat.GetValue (2, 2));
1414     // Near plane
1415     nLeft   = aNear * (aProjectionMat.GetValue (0, 2) - 1.0) / aProjectionMat.GetValue (0, 0);
1416     nRight  = aNear * (aProjectionMat.GetValue (0, 2) + 1.0) / aProjectionMat.GetValue (0, 0);
1417     nTop    = aNear * (aProjectionMat.GetValue (1, 2) + 1.0) / aProjectionMat.GetValue (1, 1);
1418     nBottom = aNear * (aProjectionMat.GetValue (1, 2) - 1.0) / aProjectionMat.GetValue (1, 1);
1419     // Far plane
1420     fLeft   = aFar  * (aProjectionMat.GetValue (0, 2) - 1.0) / aProjectionMat.GetValue (0, 0);
1421     fRight  = aFar  * (aProjectionMat.GetValue (0, 2) + 1.0) / aProjectionMat.GetValue (0, 0);
1422     fTop    = aFar  * (aProjectionMat.GetValue (1, 2) + 1.0) / aProjectionMat.GetValue (1, 1);
1423     fBottom = aFar  * (aProjectionMat.GetValue (1, 2) - 1.0) / aProjectionMat.GetValue (1, 1);
1424   }
1425   else
1426   {
1427     // handle orthographic projection
1428     aNear = (1.0 / aProjectionMat.GetValue (2, 2)) * (aProjectionMat.GetValue (2, 3) + 1.0);
1429     aFar  = (1.0 / aProjectionMat.GetValue (2, 2)) * (aProjectionMat.GetValue (2, 3) - 1.0);
1430     // Near plane
1431     nLeft   = ( 1.0 + aProjectionMat.GetValue (0, 3)) / (-aProjectionMat.GetValue (0, 0));
1432     fLeft   = nLeft;
1433     nRight  = ( 1.0 - aProjectionMat.GetValue (0, 3)) /   aProjectionMat.GetValue (0, 0);
1434     fRight  = nRight;
1435     nTop    = ( 1.0 - aProjectionMat.GetValue (1, 3)) /   aProjectionMat.GetValue (1, 1);
1436     fTop    = nTop;
1437     nBottom = (-1.0 - aProjectionMat.GetValue (1, 3)) /   aProjectionMat.GetValue (1, 1);
1438     fBottom = nBottom;
1439   }
1440
1441   Graphic3d_Vec4d aLeftTopNear     (nLeft,  nTop,    -aNear, 1.0), aRightBottomFar (fRight, fBottom, -aFar, 1.0);
1442   Graphic3d_Vec4d aLeftBottomNear  (nLeft,  nBottom, -aNear, 1.0), aRightTopFar    (fRight, fTop,    -aFar, 1.0);
1443   Graphic3d_Vec4d aRightBottomNear (nRight, nBottom, -aNear, 1.0), aLeftTopFar     (fLeft,  fTop,    -aFar, 1.0);
1444   Graphic3d_Vec4d aRightTopNear    (nRight, nTop,    -aNear, 1.0), aLeftBottomFar  (fLeft,  fBottom, -aFar, 1.0);
1445
1446   Graphic3d_Mat4d anInvWorldView;
1447   aWorldViewMat.Inverted (anInvWorldView);
1448
1449   Graphic3d_Vec4d aTmpPnt;
1450   aTmpPnt = anInvWorldView * aLeftTopNear;
1451   thePoints.SetValue (FrustumVert_LeftTopNear,     aTmpPnt.xyz() / aTmpPnt.w());
1452   aTmpPnt = anInvWorldView * aRightBottomFar;
1453   thePoints.SetValue (FrustumVert_RightBottomFar,  aTmpPnt.xyz() / aTmpPnt.w());
1454   aTmpPnt = anInvWorldView * aLeftBottomNear;
1455   thePoints.SetValue (FrustumVert_LeftBottomNear,  aTmpPnt.xyz() / aTmpPnt.w());
1456   aTmpPnt = anInvWorldView * aRightTopFar;
1457   thePoints.SetValue (FrustumVert_RightTopFar,     aTmpPnt.xyz() / aTmpPnt.w());
1458   aTmpPnt = anInvWorldView * aRightBottomNear;
1459   thePoints.SetValue (FrustumVert_RightBottomNear, aTmpPnt.xyz() / aTmpPnt.w());
1460   aTmpPnt = anInvWorldView * aLeftTopFar;
1461   thePoints.SetValue (FrustumVert_LeftTopFar,      aTmpPnt.xyz() / aTmpPnt.w());
1462   aTmpPnt = anInvWorldView * aRightTopNear;
1463   thePoints.SetValue (FrustumVert_RightTopNear,    aTmpPnt.xyz() / aTmpPnt.w());
1464   aTmpPnt = anInvWorldView * aLeftBottomFar;
1465   thePoints.SetValue (FrustumVert_LeftBottomFar,   aTmpPnt.xyz() / aTmpPnt.w());
1466 }