b97d4d572299337799ba3b3e0e60adffd38d4ada
[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   // z-range tolerance compatible with for floating point.
43   static Standard_Real zEpsilon()
44   {
45     return FLT_EPSILON;
46   }
47
48   // relative z-range tolerance compatible with for floating point.
49   static Standard_Real zEpsilon (const Standard_Real theValue)
50   {
51     Standard_Real anAbsValue = Abs (theValue);
52     if (anAbsValue <= (double)FLT_MIN)
53     {
54       return FLT_MIN;
55     }
56     Standard_Real aLogRadix = Log10 (anAbsValue) / Log10 (FLT_RADIX);
57     Standard_Real aExp = Floor (aLogRadix);
58     return FLT_EPSILON * Pow (FLT_RADIX, aExp);
59   }
60
61   //! Convert camera definition to Ax3
62   gp_Ax3 cameraToAx3 (const Graphic3d_Camera& theCamera)
63   {
64     const gp_Dir aBackDir = -theCamera.Direction();
65     const gp_Dir anXAxis (theCamera.Up().Crossed (aBackDir));
66     const gp_Dir anYAxis (aBackDir      .Crossed (anXAxis));
67     const gp_Dir aZAxis  (anXAxis       .Crossed (anYAxis));
68     return gp_Ax3 (gp_Pnt (0.0, 0.0, 0.0), aZAxis, anXAxis);
69   }
70 }
71
72 // =======================================================================
73 // function : Graphic3d_Camera
74 // purpose  :
75 // =======================================================================
76 Graphic3d_Camera::Graphic3d_Camera()
77 : myUp (0.0, 1.0, 0.0),
78   myDirection (0.0, 0.0, 1.0),
79   myEye (0.0, 0.0, -1500.0),
80   myDistance (1500.0),
81   myAxialScale (1.0, 1.0, 1.0),
82   myProjType (Projection_Orthographic),
83   myFOVy (45.0),
84   myFOVx (45.0),
85   myFOV2d (180.0),
86   myFOVyTan (Tan (DTR_HALF * 45.0)),
87   myZNear (DEFAULT_ZNEAR),
88   myZFar (DEFAULT_ZFAR),
89   myAspect (1.0),
90   myIsZeroToOneDepth (false),
91   myScale (1000.0),
92   myZFocus (1.0),
93   myZFocusType (FocusType_Relative),
94   myIOD (0.05),
95   myIODType (IODType_Relative),
96   myIsCustomProjMatM (false),
97   myIsCustomProjMatLR(false),
98   myIsCustomFrustomLR(false)
99 {
100   myWorldViewProjState.Initialize ((Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
101                                    (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
102                                    this);
103 }
104
105 // =======================================================================
106 // function : Graphic3d_Camera
107 // purpose  :
108 // =======================================================================
109 Graphic3d_Camera::Graphic3d_Camera (const Handle(Graphic3d_Camera)& theOther)
110 : myUp (0.0, 1.0, 0.0),
111   myDirection (0.0, 0.0, 1.0),
112   myEye (0.0, 0.0, -1500.0),
113   myDistance (1500.0),
114   myAxialScale (1.0, 1.0, 1.0),
115   myProjType (Projection_Orthographic),
116   myFOVy (45.0),
117   myFOVx (45.0),
118   myFOV2d (180.0),
119   myFOVyTan (Tan (DTR_HALF * 45.0)),
120   myZNear (DEFAULT_ZNEAR),
121   myZFar (DEFAULT_ZFAR),
122   myAspect (1.0),
123   myIsZeroToOneDepth (false),
124   myScale (1000.0),
125   myZFocus (1.0),
126   myZFocusType (FocusType_Relative),
127   myIOD (0.05),
128   myIODType (IODType_Relative),
129   myIsCustomProjMatM (false),
130   myIsCustomProjMatLR(false),
131   myIsCustomFrustomLR(false)
132 {
133   myWorldViewProjState.Initialize (this);
134
135   Copy (theOther);
136 }
137
138 // =======================================================================
139 // function : CopyMappingData
140 // purpose  :
141 // =======================================================================
142 void Graphic3d_Camera::CopyMappingData (const Handle(Graphic3d_Camera)& theOtherCamera)
143 {
144   SetZeroToOneDepth (theOtherCamera->IsZeroToOneDepth());
145   SetProjectionType (theOtherCamera->ProjectionType());
146   SetFOVy           (theOtherCamera->FOVy());
147   SetFOV2d          (theOtherCamera->FOV2d());
148   SetZRange         (theOtherCamera->ZNear(), theOtherCamera->ZFar());
149   SetAspect         (theOtherCamera->Aspect());
150   SetScale          (theOtherCamera->Scale());
151   SetZFocus         (theOtherCamera->ZFocusType(), theOtherCamera->ZFocus());
152   SetIOD            (theOtherCamera->GetIODType(), theOtherCamera->IOD());
153   SetTile           (theOtherCamera->myTile);
154
155   ResetCustomProjection();
156   if (theOtherCamera->IsCustomStereoProjection())
157   {
158     SetCustomStereoProjection (theOtherCamera->myCustomProjMatL,
159                                theOtherCamera->myCustomHeadToEyeMatL,
160                                theOtherCamera->myCustomProjMatR,
161                                theOtherCamera->myCustomHeadToEyeMatR);
162   }
163   else if (theOtherCamera->IsCustomStereoFrustum())
164   {
165     SetCustomStereoFrustums (theOtherCamera->myCustomFrustumL, theOtherCamera->myCustomFrustumR);
166   }
167   if (theOtherCamera->IsCustomMonoProjection())
168   {
169     SetCustomMonoProjection (theOtherCamera->myCustomProjMatM);
170   }
171 }
172
173 // =======================================================================
174 // function : CopyOrientationData
175 // purpose  :
176 // =======================================================================
177 void Graphic3d_Camera::CopyOrientationData (const Handle(Graphic3d_Camera)& theOtherCamera)
178 {
179   if (!myEye.IsEqual (theOtherCamera->Eye(), 0.0)
180    || !myUp.IsEqual (theOtherCamera->Up(), 0.0)
181    || !myDirection.IsEqual (theOtherCamera->Direction(), 0.0)
182    ||  myDistance != theOtherCamera->Distance())
183   {
184     myEye = theOtherCamera->Eye();
185     myUp  = theOtherCamera->Up();
186     myDirection = theOtherCamera->Direction();
187     myDistance = theOtherCamera->Distance();
188     InvalidateOrientation();
189   }
190   SetAxialScale (theOtherCamera->AxialScale());
191 }
192
193 // =======================================================================
194 // function : Copy
195 // purpose  :
196 // =======================================================================
197 void Graphic3d_Camera::Copy (const Handle(Graphic3d_Camera)& theOther)
198 {
199   CopyMappingData (theOther);
200   CopyOrientationData (theOther);
201 }
202
203 // =======================================================================
204 // function : MoveEyeTo
205 // purpose  :
206 // =======================================================================
207 void Graphic3d_Camera::MoveEyeTo (const gp_Pnt& theEye)
208 {
209   if (myEye.IsEqual (theEye, 0.0))
210   {
211     return;
212   }
213
214   myEye = theEye;
215   InvalidateOrientation();
216 }
217
218 // =======================================================================
219 // function : SetEyeAndCenter
220 // purpose  :
221 // =======================================================================
222 void Graphic3d_Camera::SetEyeAndCenter (const gp_Pnt& theEye,
223                                         const gp_Pnt& theCenter)
224 {
225   if (Eye()   .IsEqual (theEye,    0.0)
226    && Center().IsEqual (theCenter, 0.0))
227   {
228     return;
229   }
230
231   myEye = theEye;
232   myDistance = theEye.Distance (theCenter);
233   if (myDistance > gp::Resolution())
234   {
235     myDirection = gp_Dir (theCenter.XYZ() - theEye.XYZ());
236   }
237   InvalidateOrientation();
238 }
239
240 // =======================================================================
241 // function : SetEye
242 // purpose  :
243 // =======================================================================
244 void Graphic3d_Camera::SetEye (const gp_Pnt& theEye)
245 {
246   if (Eye().IsEqual (theEye, 0.0))
247   {
248     return;
249   }
250
251   const gp_Pnt aCenter = Center();
252   myEye = theEye;
253   myDistance = myEye.Distance (aCenter);
254   if (myDistance > gp::Resolution())
255   {
256     myDirection = gp_Dir (aCenter.XYZ() - myEye.XYZ());
257   }
258   InvalidateOrientation();
259 }
260
261 // =======================================================================
262 // function : SetCenter
263 // purpose  :
264 // =======================================================================
265 void Graphic3d_Camera::SetCenter (const gp_Pnt& theCenter)
266 {
267   const Standard_Real aDistance = myEye.Distance (theCenter);
268   if (myDistance == aDistance)
269   {
270     return;
271   }
272
273   myDistance = aDistance;
274   if (myDistance > gp::Resolution())
275   {
276     myDirection = gp_Dir (theCenter.XYZ() - myEye.XYZ());
277   }
278   InvalidateOrientation();
279 }
280
281 // =======================================================================
282 // function : SetUp
283 // purpose  :
284 // =======================================================================
285 void Graphic3d_Camera::SetUp (const gp_Dir& theUp)
286 {
287   if (Up().IsEqual (theUp, 0.0))
288   {
289     return;
290   }
291
292   myUp = theUp;
293   InvalidateOrientation();
294 }
295
296 // =======================================================================
297 // function : SetAxialScale
298 // purpose  :
299 // =======================================================================
300 void Graphic3d_Camera::SetAxialScale (const gp_XYZ& theAxialScale)
301 {
302   if (AxialScale().IsEqual (theAxialScale, 0.0))
303   {
304     return;
305   }
306
307   myAxialScale = theAxialScale;
308   InvalidateOrientation();
309 }
310
311 // =======================================================================
312 // function : SetDistance
313 // purpose  :
314 // =======================================================================
315 void Graphic3d_Camera::SetDistance (const Standard_Real theDistance)
316 {
317   if (myDistance == theDistance)
318   {
319     return;
320   }
321
322   const gp_Pnt aCenter = Center();
323   myDistance = theDistance;
324   myEye = aCenter.XYZ() - myDirection.XYZ() * myDistance;
325   InvalidateOrientation();
326 }
327
328 // =======================================================================
329 // function : SetDirectionFromEye
330 // purpose  :
331 // =======================================================================
332 void Graphic3d_Camera::SetDirectionFromEye (const gp_Dir& theDir)
333 {
334   if (myDirection.IsEqual (theDir, 0.0))
335   {
336     return;
337   }
338
339   myDirection = theDir;
340   InvalidateOrientation();
341 }
342
343 // =======================================================================
344 // function : SetDirection
345 // purpose  :
346 // =======================================================================
347 void Graphic3d_Camera::SetDirection (const gp_Dir& theDir)
348 {
349   if (myDirection.IsEqual (theDir, 0.0))
350   {
351     return;
352   }
353
354   const gp_Pnt aCenter = Center();
355   myDirection = theDir;
356   myEye = aCenter.XYZ() - theDir.XYZ() * myDistance;
357   InvalidateOrientation();
358 }
359
360 // =======================================================================
361 // function : SetScale
362 // purpose  :
363 // =======================================================================
364 void Graphic3d_Camera::SetScale (const Standard_Real theScale)
365 {
366   if (Scale() == theScale)
367   {
368     return;
369   }
370
371   myScale = theScale;
372
373   switch (myProjType)
374   {
375     case Projection_Perspective  :
376     case Projection_Stereo       :
377     case Projection_MonoLeftEye  :
378     case Projection_MonoRightEye :
379     {
380       Standard_Real aDistance = theScale * 0.5 / myFOVyTan;
381       SetDistance (aDistance);
382     }
383
384     default :
385       break;
386   }
387
388   InvalidateProjection();
389 }
390
391 // =======================================================================
392 // function : Scale
393 // purpose  :
394 // =======================================================================
395 Standard_Real Graphic3d_Camera::Scale() const
396 {
397   switch (myProjType)
398   {
399     case Projection_Orthographic :
400       return myScale;
401
402     // case Projection_Perspective  :
403     // case Projection_Stereo       :
404     // case Projection_MonoLeftEye  :
405     // case Projection_MonoRightEye :
406     default :
407       return Distance() * 2.0 * myFOVyTan;
408   }
409 }
410
411 // =======================================================================
412 // function : SetProjectionType
413 // purpose  :
414 // =======================================================================
415 void Graphic3d_Camera::SetProjectionType (const Projection theProjectionType)
416 {
417   Projection anOldType = ProjectionType();
418
419   if (anOldType == theProjectionType)
420   {
421     return;
422   }
423
424   if (anOldType == Projection_Orthographic)
425   {
426     if (myZNear <= RealEpsilon())
427     {
428       myZNear = DEFAULT_ZNEAR;
429     }
430     if (myZFar <= RealEpsilon())
431     {
432       myZFar = DEFAULT_ZFAR;
433     }
434   }
435
436   myProjType = theProjectionType;
437
438   InvalidateProjection();
439 }
440
441 // =======================================================================
442 // function : SetFOVy
443 // purpose  :
444 // =======================================================================
445 void Graphic3d_Camera::SetFOVy (const Standard_Real theFOVy)
446 {
447   if (FOVy() == theFOVy)
448   {
449     return;
450   }
451
452   myFOVy = theFOVy;
453   myFOVx = theFOVy * myAspect;
454   myFOVyTan = Tan(DTR_HALF * myFOVy);
455
456   InvalidateProjection();
457 }
458
459 // =======================================================================
460 // function : SetFOV2d
461 // purpose  :
462 // =======================================================================
463 void Graphic3d_Camera::SetFOV2d (const Standard_Real theFOV)
464 {
465   if (FOV2d() == theFOV)
466   {
467     return;
468   }
469
470   myFOV2d = theFOV;
471   InvalidateProjection();
472 }
473
474 // =======================================================================
475 // function : SetZRange
476 // purpose  :
477 // =======================================================================
478 void Graphic3d_Camera::SetZRange (const Standard_Real theZNear,
479                                   const Standard_Real theZFar)
480 {
481   Standard_ASSERT_RAISE (theZFar > theZNear, "ZFar should be greater than ZNear");
482   if (!IsOrthographic())
483   {
484     Standard_ASSERT_RAISE (theZNear > 0.0, "Only positive Z-Near is allowed for perspective camera");
485     Standard_ASSERT_RAISE (theZFar  > 0.0, "Only positive Z-Far is allowed for perspective camera");
486   }
487
488   if (ZNear() == theZNear
489    && ZFar () == theZFar)
490   {
491     return;
492   }
493
494   myZNear = theZNear;
495   myZFar  = theZFar;
496
497   InvalidateProjection();
498 }
499
500 // =======================================================================
501 // function : SetAspect
502 // purpose  :
503 // =======================================================================
504 void Graphic3d_Camera::SetAspect (const Standard_Real theAspect)
505 {
506   if (Aspect() == theAspect)
507   {
508     return;
509   }
510
511   myAspect = theAspect;
512   myFOVx = myFOVy * theAspect;
513
514   InvalidateProjection();
515 }
516
517 // =======================================================================
518 // function : SetZFocus
519 // purpose  :
520 // =======================================================================
521 void Graphic3d_Camera::SetZFocus(const FocusType theType, const Standard_Real theZFocus)
522 {
523   if (ZFocusType() == theType
524    && ZFocus    () == theZFocus)
525   {
526     return;
527   }
528
529   myZFocusType = theType;
530   myZFocus     = theZFocus;
531
532   InvalidateProjection();
533 }
534
535 // =======================================================================
536 // function : SetIOD
537 // purpose  :
538 // =======================================================================
539 void Graphic3d_Camera::SetIOD (const IODType theType, const Standard_Real theIOD)
540 {
541   if (GetIODType() == theType
542    && IOD       () == theIOD)
543   {
544     return;
545   }
546
547   myIODType = theType;
548   myIOD     = theIOD;
549
550   InvalidateProjection();
551 }
552
553 // =======================================================================
554 // function : SetTile
555 // purpose  :
556 // =======================================================================
557 void Graphic3d_Camera::SetTile (const Graphic3d_CameraTile& theTile)
558 {
559   if (myTile == theTile)
560   {
561     return;
562   }
563
564   myTile = theTile;
565   InvalidateProjection();
566 }
567
568 // =======================================================================
569 // function : OrthogonalizeUp
570 // purpose  :
571 // =======================================================================
572 void Graphic3d_Camera::OrthogonalizeUp()
573 {
574   SetUp (OrthogonalizedUp());
575 }
576
577 // =======================================================================
578 // function : OrthogonalizedUp
579 // purpose  :
580 // =======================================================================
581 gp_Dir Graphic3d_Camera::OrthogonalizedUp() const
582 {
583   gp_Dir aDir  = Direction();
584   gp_Dir aLeft = aDir.Crossed (Up());
585
586   // recompute up as: up = left x direction
587   return aLeft.Crossed (aDir);
588 }
589
590 // =======================================================================
591 // function : Transform
592 // purpose  :
593 // =======================================================================
594 void Graphic3d_Camera::Transform (const gp_Trsf& theTrsf)
595 {
596   if (theTrsf.Form() == gp_Identity)
597   {
598     return;
599   }
600
601   myUp .Transform (theTrsf);
602   myDirection.Transform (theTrsf);
603   myEye.Transform (theTrsf);
604   InvalidateOrientation();
605 }
606
607 // =======================================================================
608 // function : safePointCast
609 // purpose  :
610 // =======================================================================
611 static Graphic3d_Vec4d safePointCast (const gp_Pnt& thePnt)
612 {
613   Standard_Real aLim = 1e15f;
614
615   // have to deal with values greater then max float
616   gp_Pnt aSafePoint = thePnt;
617   const Standard_Real aBigFloat = aLim * 0.1f;
618   if (Abs (aSafePoint.X()) > aLim)
619     aSafePoint.SetX (aSafePoint.X() >= 0 ? aBigFloat : -aBigFloat);
620   if (Abs (aSafePoint.Y()) > aLim)
621     aSafePoint.SetY (aSafePoint.Y() >= 0 ? aBigFloat : -aBigFloat);
622   if (Abs (aSafePoint.Z()) > aLim)
623     aSafePoint.SetZ (aSafePoint.Z() >= 0 ? aBigFloat : -aBigFloat);
624
625   // convert point
626   Graphic3d_Vec4d aPnt (aSafePoint.X(), aSafePoint.Y(), aSafePoint.Z(), 1.0);
627
628   return aPnt;
629 }
630
631 // =======================================================================
632 // function : Project
633 // purpose  :
634 // =======================================================================
635 gp_Pnt Graphic3d_Camera::Project (const gp_Pnt& thePnt) const
636 {
637   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
638   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
639
640   // use compatible type of point
641   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
642
643   aPnt = aViewMx * aPnt; // convert to view coordinate space
644   aPnt = aProjMx * aPnt; // convert to projection coordinate space
645
646   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
647
648   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
649 }
650
651 // =======================================================================
652 // function : UnProject
653 // purpose  :
654 // =======================================================================
655 gp_Pnt Graphic3d_Camera::UnProject (const gp_Pnt& thePnt) const
656 {
657   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
658   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
659
660   Graphic3d_Mat4d aInvView;
661   Graphic3d_Mat4d aInvProj;
662
663   // this case should never happen
664   if (!aViewMx.Inverted (aInvView) || !aProjMx.Inverted (aInvProj))
665   {
666     return gp_Pnt (0.0, 0.0, 0.0);
667   }
668
669   // use compatible type of point
670   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
671
672   aPnt = aInvProj * aPnt; // convert to view coordinate space
673   aPnt = aInvView * aPnt; // convert to world coordinate space
674
675   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
676
677   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
678 }
679
680 // =======================================================================
681 // function : ConvertView2Proj
682 // purpose  :
683 // =======================================================================
684 gp_Pnt Graphic3d_Camera::ConvertView2Proj (const gp_Pnt& thePnt) const
685 {
686   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
687
688   // use compatible type of point
689   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
690
691   aPnt = aProjMx * aPnt; // convert to projection coordinate space
692
693   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
694
695   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
696 }
697
698 // =======================================================================
699 // function : ConvertProj2View
700 // purpose  :
701 // =======================================================================
702 gp_Pnt Graphic3d_Camera::ConvertProj2View (const gp_Pnt& thePnt) const
703 {
704   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
705
706   Graphic3d_Mat4d aInvProj;
707
708   // this case should never happen, but...
709   if (!aProjMx.Inverted (aInvProj))
710   {
711     return gp_Pnt (0, 0, 0);
712   }
713
714   // use compatible type of point
715   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
716
717   aPnt = aInvProj * aPnt; // convert to view coordinate space
718
719   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
720
721   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
722 }
723
724 // =======================================================================
725 // function : ConvertWorld2View
726 // purpose  :
727 // =======================================================================
728 gp_Pnt Graphic3d_Camera::ConvertWorld2View (const gp_Pnt& thePnt) const
729 {
730   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
731
732   // use compatible type of point
733   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
734
735   aPnt = aViewMx * aPnt; // convert to view coordinate space
736
737   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
738
739   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
740 }
741
742 // =======================================================================
743 // function : ConvertView2World
744 // purpose  :
745 // =======================================================================
746 gp_Pnt Graphic3d_Camera::ConvertView2World (const gp_Pnt& thePnt) const
747 {
748   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
749
750   Graphic3d_Mat4d aInvView;
751
752   if (!aViewMx.Inverted (aInvView))
753   {
754     return gp_Pnt(0, 0, 0);
755   }
756
757   // use compatible type of point
758   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
759
760   aPnt = aInvView * aPnt; // convert to world coordinate space
761
762   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
763
764   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
765 }
766
767 // =======================================================================
768 // function : ViewDimensions
769 // purpose  :
770 // =======================================================================
771 gp_XYZ Graphic3d_Camera::ViewDimensions (const Standard_Real theZValue) const
772 {
773   // view plane dimensions
774   Standard_Real aSize = IsOrthographic() ? myScale : (2.0 * theZValue * myFOVyTan);
775   Standard_Real aSizeX, aSizeY;
776   if (myAspect > 1.0)
777   {
778     aSizeX = aSize * myAspect;
779     aSizeY = aSize;
780   }
781   else
782   {
783     aSizeX = aSize;
784     aSizeY = aSize / myAspect;
785   }
786
787   // and frustum depth
788   return gp_XYZ (aSizeX, aSizeY, myZFar - myZNear);
789 }
790
791 // =======================================================================
792 // function : Frustum
793 // purpose  :
794 // =======================================================================
795 void Graphic3d_Camera::Frustum (gp_Pln& theLeft,
796                                 gp_Pln& theRight,
797                                 gp_Pln& theBottom,
798                                 gp_Pln& theTop,
799                                 gp_Pln& theNear,
800                                 gp_Pln& theFar) const
801 {
802   gp_Vec aProjection = gp_Vec (Direction());
803   gp_Vec anUp        = OrthogonalizedUp();
804   gp_Vec aSide       = aProjection ^ anUp;
805
806   Standard_ASSERT_RAISE (
807     !aProjection.IsParallel (anUp, Precision::Angular()),
808      "Can not derive SIDE = PROJ x UP - directions are parallel");
809
810   theNear = gp_Pln (Eye().Translated (aProjection * ZNear()), aProjection);
811   theFar  = gp_Pln (Eye().Translated (aProjection * ZFar()), -aProjection);
812
813   Standard_Real aHScaleHor = 0.0, aHScaleVer = 0.0;
814   if (Aspect() >= 1.0)
815   {
816     aHScaleHor = Scale() * 0.5 * Aspect();
817     aHScaleVer = Scale() * 0.5;
818   }
819   else
820   {
821     aHScaleHor = Scale() * 0.5;
822     aHScaleVer = Scale() * 0.5 / Aspect();
823   }
824
825   gp_Pnt aPntLeft   = Center().Translated (aHScaleHor * -aSide);
826   gp_Pnt aPntRight  = Center().Translated (aHScaleHor *  aSide);
827   gp_Pnt aPntBottom = Center().Translated (aHScaleVer * -anUp);
828   gp_Pnt aPntTop    = Center().Translated (aHScaleVer *  anUp);
829
830   gp_Vec aDirLeft   =  aSide;
831   gp_Vec aDirRight  = -aSide;
832   gp_Vec aDirBottom =  anUp;
833   gp_Vec aDirTop    = -anUp;
834   if (!IsOrthographic())
835   {
836     Standard_Real aHFOVHor = ATan (Tan (DTR_HALF * FOVy()) * Aspect());
837     Standard_Real aHFOVVer = DTR_HALF * FOVy();
838     aDirLeft.Rotate   (gp_Ax1 (gp::Origin(), anUp),   aHFOVHor);
839     aDirRight.Rotate  (gp_Ax1 (gp::Origin(), anUp),  -aHFOVHor);
840     aDirBottom.Rotate (gp_Ax1 (gp::Origin(), aSide), -aHFOVVer);
841     aDirTop.Rotate    (gp_Ax1 (gp::Origin(), aSide),  aHFOVVer);
842   }
843
844   theLeft   = gp_Pln (aPntLeft,   aDirLeft);
845   theRight  = gp_Pln (aPntRight,  aDirRight);
846   theBottom = gp_Pln (aPntBottom, aDirBottom);
847   theTop    = gp_Pln (aPntTop,    aDirTop);
848 }
849
850 // =======================================================================
851 // function : OrientationMatrix
852 // purpose  :
853 // =======================================================================
854 const Graphic3d_Mat4d& Graphic3d_Camera::OrientationMatrix() const
855 {
856   return UpdateOrientation (myMatricesD).Orientation;
857 }
858
859 // =======================================================================
860 // function : OrientationMatrixF
861 // purpose  :
862 // =======================================================================
863 const Graphic3d_Mat4& Graphic3d_Camera::OrientationMatrixF() const
864 {
865   return UpdateOrientation (myMatricesF).Orientation;
866 }
867
868 // =======================================================================
869 // function : ProjectionMatrix
870 // purpose  :
871 // =======================================================================
872 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionMatrix() const
873 {
874   return UpdateProjection (myMatricesD).MProjection;
875 }
876
877 // =======================================================================
878 // function : ProjectionMatrixF
879 // purpose  :
880 // =======================================================================
881 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionMatrixF() const
882 {
883   return UpdateProjection (myMatricesF).MProjection;
884 }
885
886 // =======================================================================
887 // function : ProjectionStereoLeft
888 // purpose  :
889 // =======================================================================
890 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoLeft() const
891 {
892   return UpdateProjection (myMatricesD).LProjection;
893 }
894
895 // =======================================================================
896 // function : ProjectionStereoLeftF
897 // purpose  :
898 // =======================================================================
899 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoLeftF() const
900 {
901   return UpdateProjection (myMatricesF).LProjection;
902 }
903
904 // =======================================================================
905 // function : ProjectionStereoRight
906 // purpose  :
907 // =======================================================================
908 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoRight() const
909 {
910   return UpdateProjection (myMatricesD).RProjection;
911 }
912
913 // =======================================================================
914 // function : ProjectionStereoRightF
915 // purpose  :
916 // =======================================================================
917 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoRightF() const
918 {
919   return UpdateProjection (myMatricesF).RProjection;
920 }
921
922 // =======================================================================
923 // function : ResetCustomProjection
924 // purpose  :
925 // =======================================================================
926 void Graphic3d_Camera::ResetCustomProjection()
927 {
928   if (myIsCustomFrustomLR
929    || myIsCustomProjMatLR
930    || myIsCustomProjMatM)
931   {
932     myIsCustomFrustomLR = false;
933     myIsCustomProjMatLR = false;
934     myIsCustomProjMatM  = false;
935     InvalidateProjection();
936   }
937 }
938
939 // =======================================================================
940 // function : StereoProjection
941 // purpose  :
942 // =======================================================================
943 void Graphic3d_Camera::StereoProjection (Graphic3d_Mat4d& theProjL,
944                                          Graphic3d_Mat4d& theHeadToEyeL,
945                                          Graphic3d_Mat4d& theProjR,
946                                          Graphic3d_Mat4d& theHeadToEyeR) const
947 {
948   stereoProjection (theProjL, theHeadToEyeL, theProjR, theHeadToEyeR);
949 }
950
951 // =======================================================================
952 // function : StereoProjectionF
953 // purpose  :
954 // =======================================================================
955 void Graphic3d_Camera::StereoProjectionF (Graphic3d_Mat4& theProjL,
956                                           Graphic3d_Mat4& theHeadToEyeL,
957                                           Graphic3d_Mat4& theProjR,
958                                           Graphic3d_Mat4& theHeadToEyeR) const
959 {
960   stereoProjection (theProjL, theHeadToEyeL, theProjR, theHeadToEyeR);
961 }
962
963 // =======================================================================
964 // function : stereoProjection
965 // purpose  :
966 // =======================================================================
967 template <typename Elem_t>
968 void Graphic3d_Camera::stereoProjection (NCollection_Mat4<Elem_t>& theProjL,
969                                          NCollection_Mat4<Elem_t>& theHeadToEyeL,
970                                          NCollection_Mat4<Elem_t>& theProjR,
971                                          NCollection_Mat4<Elem_t>& theHeadToEyeR) const
972 {
973   if (myIsCustomProjMatLR)
974   {
975     theProjL     .ConvertFrom (myCustomProjMatL);
976     theHeadToEyeL.ConvertFrom (myCustomHeadToEyeMatL);
977     theProjR     .ConvertFrom (myCustomProjMatR);
978     theHeadToEyeR.ConvertFrom (myCustomHeadToEyeMatR);
979     return;
980   }
981
982   NCollection_Mat4<Elem_t> aDummy;
983   computeProjection (aDummy, theProjL, theProjR, false);
984
985   const Standard_Real aIOD = myIODType == IODType_Relative
986                            ? myIOD * Distance()
987                            : myIOD;
988   if (aIOD != 0.0)
989   {
990     // X translation to cancel parallax
991     theHeadToEyeL.InitIdentity();
992     theHeadToEyeL.SetColumn (3, NCollection_Vec3<Elem_t> (Elem_t ( 0.5 * aIOD), Elem_t (0.0), Elem_t (0.0)));
993     theHeadToEyeR.InitIdentity();
994     theHeadToEyeR.SetColumn (3, NCollection_Vec3<Elem_t> (Elem_t (-0.5 * aIOD), Elem_t (0.0), Elem_t (0.0)));
995   }
996 }
997
998 // =======================================================================
999 // function : SetCustomStereoFrustums
1000 // purpose  :
1001 // =======================================================================
1002 void Graphic3d_Camera::SetCustomStereoFrustums (const Aspect_FrustumLRBT<Standard_Real>& theFrustumL,
1003                                                 const Aspect_FrustumLRBT<Standard_Real>& theFrustumR)
1004 {
1005   myCustomFrustumL = theFrustumL;
1006   myCustomFrustumR = theFrustumR;
1007   myIsCustomFrustomLR = true;
1008   myIsCustomProjMatLR = false;
1009   InvalidateProjection();
1010 }
1011
1012 // =======================================================================
1013 // function : SetCustomStereoProjection
1014 // purpose  :
1015 // =======================================================================
1016 void Graphic3d_Camera::SetCustomStereoProjection (const Graphic3d_Mat4d& theProjL,
1017                                                   const Graphic3d_Mat4d& theHeadToEyeL,
1018                                                   const Graphic3d_Mat4d& theProjR,
1019                                                   const Graphic3d_Mat4d& theHeadToEyeR)
1020 {
1021   myCustomProjMatL = theProjL;
1022   myCustomProjMatR = theProjR;
1023   myCustomHeadToEyeMatL = theHeadToEyeL;
1024   myCustomHeadToEyeMatR = theHeadToEyeR;
1025   myIsCustomProjMatLR = true;
1026   myIsCustomFrustomLR = false;
1027   InvalidateProjection();
1028 }
1029
1030 // =======================================================================
1031 // function : SetCustomMonoProjection
1032 // purpose  :
1033 // =======================================================================
1034 void Graphic3d_Camera::SetCustomMonoProjection (const Graphic3d_Mat4d& theProj)
1035 {
1036   myCustomProjMatM = theProj;
1037   myIsCustomProjMatM = true;
1038   InvalidateProjection();
1039 }
1040
1041 // =======================================================================
1042 // function : computeProjection
1043 // purpose  :
1044 // =======================================================================
1045 template <typename Elem_t>
1046 void Graphic3d_Camera::computeProjection (NCollection_Mat4<Elem_t>& theProjM,
1047                                           NCollection_Mat4<Elem_t>& theProjL,
1048                                           NCollection_Mat4<Elem_t>& theProjR,
1049                                           bool theToAddHeadToEye) const
1050 {
1051   theProjM.InitIdentity();
1052   theProjL.InitIdentity();
1053   theProjR.InitIdentity();
1054
1055   // sets top of frustum based on FOVy and near clipping plane
1056   Elem_t aScale   = static_cast<Elem_t> (myScale);
1057   Elem_t aZNear   = static_cast<Elem_t> (myZNear);
1058   Elem_t aZFar    = static_cast<Elem_t> (myZFar);
1059   Elem_t anAspect = static_cast<Elem_t> (myAspect);
1060   Elem_t aDXHalf = 0.0, aDYHalf = 0.0;
1061   if (IsOrthographic())
1062   {
1063     aDXHalf = aDYHalf = aScale * Elem_t (0.5);
1064   }
1065   else
1066   {
1067     aDXHalf = aDYHalf = aZNear * Elem_t (myFOVyTan);
1068   }
1069
1070   if (anAspect > 1.0)
1071   {
1072     aDXHalf *= anAspect;
1073   }
1074   else
1075   {
1076     aDYHalf /= anAspect;
1077   }
1078
1079   // sets right of frustum based on aspect ratio
1080   Aspect_FrustumLRBT<Elem_t> anLRBT;
1081   anLRBT.Left   = -aDXHalf;
1082   anLRBT.Right  =  aDXHalf;
1083   anLRBT.Bottom = -aDYHalf;
1084   anLRBT.Top    =  aDYHalf;
1085
1086   Elem_t aIOD  = myIODType == IODType_Relative 
1087     ? static_cast<Elem_t> (myIOD * Distance())
1088     : static_cast<Elem_t> (myIOD);
1089
1090   Elem_t aFocus = myZFocusType == FocusType_Relative 
1091     ? static_cast<Elem_t> (myZFocus * Distance())
1092     : static_cast<Elem_t> (myZFocus);
1093
1094   if (myTile.IsValid())
1095   {
1096     const Elem_t aDXFull = Elem_t(2) * aDXHalf;
1097     const Elem_t aDYFull = Elem_t(2) * aDYHalf;
1098     const Graphic3d_Vec2i anOffset = myTile.OffsetLowerLeft();
1099     anLRBT.Left   = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x())                       / static_cast<Elem_t> (myTile.TotalSize.x());
1100     anLRBT.Right  = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x() + myTile.TileSize.x()) / static_cast<Elem_t> (myTile.TotalSize.x());
1101     anLRBT.Bottom = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y())                       / static_cast<Elem_t> (myTile.TotalSize.y());
1102     anLRBT.Top    = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y() + myTile.TileSize.y()) / static_cast<Elem_t> (myTile.TotalSize.y());
1103   }
1104
1105   if (myIsCustomProjMatM)
1106   {
1107     theProjM.ConvertFrom (myCustomProjMatM);
1108   }
1109   switch (myProjType)
1110   {
1111     case Projection_Orthographic:
1112     {
1113       if (!myIsCustomProjMatM)
1114       {
1115         orthoProj (theProjM, anLRBT, aZNear, aZFar);
1116       }
1117       break;
1118     }
1119     case Projection_Perspective:
1120     {
1121       if (!myIsCustomProjMatM)
1122       {
1123         perspectiveProj (theProjM, anLRBT, aZNear, aZFar);
1124       }
1125       break;
1126     }
1127     case Projection_MonoLeftEye:
1128     case Projection_MonoRightEye:
1129     case Projection_Stereo:
1130     {
1131       if (!myIsCustomProjMatM)
1132       {
1133         perspectiveProj (theProjM, anLRBT, aZNear, aZFar);
1134       }
1135       if (myIsCustomProjMatLR)
1136       {
1137         if (theToAddHeadToEye)
1138         {
1139           theProjL.ConvertFrom (myCustomProjMatL * myCustomHeadToEyeMatL);
1140           theProjR.ConvertFrom (myCustomProjMatR * myCustomHeadToEyeMatR);
1141         }
1142         else
1143         {
1144           theProjL.ConvertFrom (myCustomProjMatL);
1145           theProjR.ConvertFrom (myCustomProjMatR);
1146         }
1147       }
1148       else if (myIsCustomFrustomLR)
1149       {
1150         anLRBT = Aspect_FrustumLRBT<Elem_t> (myCustomFrustumL).Multiplied (aZNear);
1151         perspectiveProj (theProjL, anLRBT, aZNear, aZFar);
1152
1153         anLRBT = Aspect_FrustumLRBT<Elem_t> (myCustomFrustumR).Multiplied (aZNear);
1154         perspectiveProj (theProjR, anLRBT, aZNear, aZFar);
1155       }
1156       else
1157       {
1158         stereoEyeProj (theProjL,
1159                        anLRBT, aZNear, aZFar, aIOD, aFocus,
1160                        Aspect_Eye_Left);
1161         stereoEyeProj (theProjR,
1162                        anLRBT, aZNear, aZFar, aIOD, aFocus,
1163                        Aspect_Eye_Right);
1164       }
1165
1166       if (theToAddHeadToEye
1167       && !myIsCustomProjMatLR
1168       &&  aIOD != Elem_t (0.0))
1169       {
1170         // X translation to cancel parallax
1171         theProjL.Translate (NCollection_Vec3<Elem_t> (Elem_t ( 0.5) * aIOD, Elem_t (0.0), Elem_t (0.0)));
1172         theProjR.Translate (NCollection_Vec3<Elem_t> (Elem_t (-0.5) * aIOD, Elem_t (0.0), Elem_t (0.0)));
1173       }
1174       break;
1175     }
1176   }
1177   if (myProjType == Projection_MonoLeftEye)
1178   {
1179     theProjM = theProjL;
1180   }
1181   else if (myProjType == Projection_MonoRightEye)
1182   {
1183     theProjM = theProjR;
1184   }
1185 }
1186
1187 // =======================================================================
1188 // function : UpdateOrientation
1189 // purpose  :
1190 // =======================================================================
1191 template <typename Elem_t>
1192 Graphic3d_Camera::TransformMatrices<Elem_t>&
1193   Graphic3d_Camera::UpdateOrientation (TransformMatrices<Elem_t>& theMatrices) const
1194 {
1195   if (theMatrices.IsOrientationValid())
1196   {
1197     return theMatrices; // for inline accessors
1198   }
1199
1200   theMatrices.InitOrientation();
1201
1202   NCollection_Vec3<Elem_t> anEye (static_cast<Elem_t> (myEye.X()),
1203                                   static_cast<Elem_t> (myEye.Y()),
1204                                   static_cast<Elem_t> (myEye.Z()));
1205
1206   NCollection_Vec3<Elem_t> aViewDir (static_cast<Elem_t> (myDirection.X()),
1207                                      static_cast<Elem_t> (myDirection.Y()),
1208                                      static_cast<Elem_t> (myDirection.Z()));
1209
1210   NCollection_Vec3<Elem_t> anUp (static_cast<Elem_t> (myUp.X()),
1211                                  static_cast<Elem_t> (myUp.Y()),
1212                                  static_cast<Elem_t> (myUp.Z()));
1213
1214   NCollection_Vec3<Elem_t> anAxialScale (static_cast<Elem_t> (myAxialScale.X()),
1215                                          static_cast<Elem_t> (myAxialScale.Y()),
1216                                          static_cast<Elem_t> (myAxialScale.Z()));
1217
1218   LookOrientation (anEye, aViewDir, anUp, anAxialScale, theMatrices.Orientation);
1219
1220   return theMatrices; // for inline accessors
1221 }
1222
1223 // =======================================================================
1224 // function : InvalidateProjection
1225 // purpose  :
1226 // =======================================================================
1227 void Graphic3d_Camera::InvalidateProjection()
1228 {
1229   myMatricesD.ResetProjection();
1230   myMatricesF.ResetProjection();
1231   myWorldViewProjState.ProjectionState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
1232 }
1233
1234 // =======================================================================
1235 // function : InvalidateOrientation
1236 // purpose  :
1237 // =======================================================================
1238 void Graphic3d_Camera::InvalidateOrientation()
1239 {
1240   myMatricesD.ResetOrientation();
1241   myMatricesF.ResetOrientation();
1242   myWorldViewProjState.WorldViewState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
1243 }
1244
1245 // =======================================================================
1246 // function : orthoProj
1247 // purpose  :
1248 // =======================================================================
1249 template <typename Elem_t>
1250 void Graphic3d_Camera::orthoProj (NCollection_Mat4<Elem_t>& theOutMx,
1251                                   const Aspect_FrustumLRBT<Elem_t>& theLRBT,
1252                                   const Elem_t theNear,
1253                                   const Elem_t theFar) const
1254 {
1255   // row 0
1256   theOutMx.ChangeValue (0, 0) = Elem_t (2.0) / (theLRBT.Right - theLRBT.Left);
1257   theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
1258   theOutMx.ChangeValue (0, 2) = Elem_t (0.0);
1259   theOutMx.ChangeValue (0, 3) = - (theLRBT.Right + theLRBT.Left) / (theLRBT.Right - theLRBT.Left);
1260
1261   // row 1
1262   theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
1263   theOutMx.ChangeValue (1, 1) = Elem_t (2.0) / (theLRBT.Top - theLRBT.Bottom);
1264   theOutMx.ChangeValue (1, 2) = Elem_t (0.0);
1265   theOutMx.ChangeValue (1, 3) = - (theLRBT.Top + theLRBT.Bottom) / (theLRBT.Top - theLRBT.Bottom);
1266
1267   // row 2
1268   theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
1269   theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
1270   if (myIsZeroToOneDepth)
1271   {
1272     theOutMx.ChangeValue (2, 2) = Elem_t (-1.0) / (theFar - theNear);
1273     theOutMx.ChangeValue (2, 3) = -theNear / (theFar - theNear);
1274   }
1275   else
1276   {
1277     theOutMx.ChangeValue (2, 2) = Elem_t (-2.0) / (theFar - theNear);
1278     theOutMx.ChangeValue (2, 3) = - (theFar + theNear) / (theFar - theNear);
1279   }
1280
1281   // row 3
1282   theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
1283   theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
1284   theOutMx.ChangeValue (3, 2) = Elem_t (0.0);
1285   theOutMx.ChangeValue (3, 3) = Elem_t (1.0);
1286 }
1287
1288 // =======================================================================
1289 // function : PerspectiveProj
1290 // purpose  :
1291 // =======================================================================
1292 template <typename Elem_t>
1293 void Graphic3d_Camera::perspectiveProj (NCollection_Mat4<Elem_t>& theOutMx,
1294                                         const Aspect_FrustumLRBT<Elem_t>& theLRBT,
1295                                         const Elem_t theNear,
1296                                         const Elem_t theFar) const
1297 {
1298   // column 0
1299   theOutMx.ChangeValue (0, 0) = (Elem_t (2.0) * theNear) / (theLRBT.Right - theLRBT.Left);
1300   theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
1301   theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
1302   theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
1303
1304   // column 1
1305   theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
1306   theOutMx.ChangeValue (1, 1) = (Elem_t (2.0) * theNear) / (theLRBT.Top - theLRBT.Bottom);
1307   theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
1308   theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
1309
1310   // column 2
1311   theOutMx.ChangeValue (0, 2) = (theLRBT.Right + theLRBT.Left) / (theLRBT.Right - theLRBT.Left);
1312   theOutMx.ChangeValue (1, 2) = (theLRBT.Top + theLRBT.Bottom) / (theLRBT.Top - theLRBT.Bottom);
1313   if (myIsZeroToOneDepth)
1314   {
1315     theOutMx.ChangeValue (2, 2) = theFar / (theNear - theFar);
1316   }
1317   else
1318   {
1319     theOutMx.ChangeValue (2, 2) = -(theFar + theNear) / (theFar - theNear);
1320   }
1321   theOutMx.ChangeValue (3, 2) = Elem_t (-1.0);
1322
1323   // column 3
1324   theOutMx.ChangeValue (0, 3) = Elem_t (0.0);
1325   theOutMx.ChangeValue (1, 3) = Elem_t (0.0);
1326   if (myIsZeroToOneDepth)
1327   {
1328     theOutMx.ChangeValue (2, 3) = -(theFar * theNear) / (theFar - theNear);
1329   }
1330   else
1331   {
1332     theOutMx.ChangeValue (2, 3) = -(Elem_t (2.0) * theFar * theNear) / (theFar - theNear);
1333   }
1334   theOutMx.ChangeValue (3, 3) = Elem_t (0.0);
1335 }
1336
1337 // =======================================================================
1338 // function : StereoEyeProj
1339 // purpose  :
1340 // =======================================================================
1341 template <typename Elem_t>
1342 void Graphic3d_Camera::stereoEyeProj (NCollection_Mat4<Elem_t>& theOutMx,
1343                                       const Aspect_FrustumLRBT<Elem_t>& theLRBT,
1344                                       const Elem_t theNear,
1345                                       const Elem_t theFar,
1346                                       const Elem_t theIOD,
1347                                       const Elem_t theZFocus,
1348                                       const Aspect_Eye theEyeIndex) const
1349 {
1350   Elem_t aDx = theEyeIndex == Aspect_Eye_Left ? Elem_t (0.5) * theIOD : Elem_t (-0.5) * theIOD;
1351   Elem_t aDXStereoShift = aDx * theNear / theZFocus;
1352
1353   // construct eye projection matrix
1354   Aspect_FrustumLRBT<Elem_t> aLRBT = theLRBT;
1355   aLRBT.Left  = theLRBT.Left  + aDXStereoShift;
1356   aLRBT.Right = theLRBT.Right + aDXStereoShift;
1357   perspectiveProj (theOutMx, aLRBT, theNear, theFar);
1358 }
1359
1360 // =======================================================================
1361 // function : LookOrientation
1362 // purpose  :
1363 // =======================================================================
1364 template <typename Elem_t>
1365 void Graphic3d_Camera::LookOrientation (const NCollection_Vec3<Elem_t>& theEye,
1366                                         const NCollection_Vec3<Elem_t>& theFwdDir,
1367                                         const NCollection_Vec3<Elem_t>& theUpDir,
1368                                         const NCollection_Vec3<Elem_t>& theAxialScale,
1369                                         NCollection_Mat4<Elem_t>& theOutMx)
1370 {
1371   NCollection_Vec3<Elem_t> aForward = theFwdDir;
1372   aForward.Normalize();
1373
1374   // side = forward x up
1375   NCollection_Vec3<Elem_t> aSide = NCollection_Vec3<Elem_t>::Cross (aForward, theUpDir);
1376   aSide.Normalize();
1377
1378   // recompute up as: up = side x forward
1379   NCollection_Vec3<Elem_t> anUp = NCollection_Vec3<Elem_t>::Cross (aSide, aForward);
1380
1381   NCollection_Mat4<Elem_t> aLookMx;
1382   aLookMx.SetRow (0, aSide);
1383   aLookMx.SetRow (1, anUp);
1384   aLookMx.SetRow (2, -aForward);
1385
1386   theOutMx.InitIdentity();
1387   theOutMx.Multiply (aLookMx);
1388   theOutMx.Translate (-theEye);
1389
1390   NCollection_Mat4<Elem_t> anAxialScaleMx;
1391   anAxialScaleMx.ChangeValue (0, 0) = theAxialScale.x();
1392   anAxialScaleMx.ChangeValue (1, 1) = theAxialScale.y();
1393   anAxialScaleMx.ChangeValue (2, 2) = theAxialScale.z();
1394
1395   theOutMx.Multiply (anAxialScaleMx);
1396 }
1397
1398 // =======================================================================
1399 // function : FitMinMax
1400 // purpose  :
1401 // =======================================================================
1402 bool Graphic3d_Camera::FitMinMax (const Bnd_Box& theBox,
1403                                   const Standard_Real theResolution,
1404                                   const bool theToEnlargeIfLine)
1405 {
1406   // Check bounding box for validness
1407   if (theBox.IsVoid())
1408   {
1409     return false; // bounding box is out of bounds...
1410   }
1411
1412   // Apply "axial scaling" to the bounding points.
1413   // It is not the best approach to make this scaling as a part of fit all operation,
1414   // but the axial scale is integrated into camera orientation matrix and the other
1415   // option is to perform frustum plane adjustment algorithm in view camera space,
1416   // which will lead to a number of additional world-view space conversions and
1417   // loosing precision as well.
1418   const gp_Pnt aBndMin = theBox.CornerMin().XYZ().Multiplied (myAxialScale);
1419   const gp_Pnt aBndMax = theBox.CornerMax().XYZ().Multiplied (myAxialScale);
1420   if (aBndMax.IsEqual (aBndMin, RealEpsilon()))
1421   {
1422     return false; // nothing to fit all
1423   }
1424
1425   // Prepare camera frustum planes.
1426   gp_Pln aFrustumPlaneArray[6];
1427   NCollection_Array1<gp_Pln> aFrustumPlane (aFrustumPlaneArray[0], 1, 6);
1428   Frustum (aFrustumPlane[1], aFrustumPlane[2], aFrustumPlane[3],
1429            aFrustumPlane[4], aFrustumPlane[5], aFrustumPlane[6]);
1430
1431   // Prepare camera up, side, direction vectors.
1432   const gp_Dir aCamUp  = OrthogonalizedUp();
1433   const gp_Dir aCamDir = Direction();
1434   const gp_Dir aCamSide = aCamDir ^ aCamUp;
1435
1436   // Prepare scene bounding box parameters.
1437   const gp_Pnt aBndCenter = (aBndMin.XYZ() + aBndMax.XYZ()) / 2.0;
1438
1439   gp_Pnt aBndCornerArray[8];
1440   NCollection_Array1<gp_Pnt> aBndCorner (aBndCornerArray[0], 1, 8);
1441   aBndCorner[1].SetCoord (aBndMin.X(), aBndMin.Y(), aBndMin.Z());
1442   aBndCorner[2].SetCoord (aBndMin.X(), aBndMin.Y(), aBndMax.Z());
1443   aBndCorner[3].SetCoord (aBndMin.X(), aBndMax.Y(), aBndMin.Z());
1444   aBndCorner[4].SetCoord (aBndMin.X(), aBndMax.Y(), aBndMax.Z());
1445   aBndCorner[5].SetCoord (aBndMax.X(), aBndMin.Y(), aBndMin.Z());
1446   aBndCorner[6].SetCoord (aBndMax.X(), aBndMin.Y(), aBndMax.Z());
1447   aBndCorner[7].SetCoord (aBndMax.X(), aBndMax.Y(), aBndMin.Z());
1448   aBndCorner[8].SetCoord (aBndMax.X(), aBndMax.Y(), aBndMax.Z());
1449
1450   // Perspective-correct camera projection vector, matching the bounding box is determined geometrically.
1451   // Knowing the initial shape of a frustum it is possible to match it to a bounding box.
1452   // Then, knowing the relation of camera projection vector to the frustum shape it is possible to
1453   // set up perspective-correct camera projection matching the bounding box.
1454   // These steps support non-asymmetric transformations of view-projection space provided by camera.
1455   // The zooming can be done by calculating view plane size matching the bounding box at center of
1456   // the bounding box. The only limitation here is that the scale of camera should define size of
1457   // its view plane passing through the camera center, and the center of camera should be on the
1458   // same line with the center of bounding box.
1459
1460   // The following method is applied:
1461   // 1) Determine normalized asymmetry of camera projection vector by frustum planes.
1462   // 2) Determine new location of frustum planes, "matching" the bounding box.
1463   // 3) Determine new camera projection vector using the normalized asymmetry.
1464   // 4) Determine new zooming in view space.
1465
1466   // 1. Determine normalized projection asymmetry (if any).
1467   Standard_Real anAssymX = Tan (( aCamSide).Angle (aFrustumPlane[1].Axis().Direction()))
1468                          - Tan ((-aCamSide).Angle (aFrustumPlane[2].Axis().Direction()));
1469   Standard_Real anAssymY = Tan (( aCamUp)  .Angle (aFrustumPlane[3].Axis().Direction()))
1470                          - Tan ((-aCamUp)  .Angle (aFrustumPlane[4].Axis().Direction()));
1471
1472   // 2. Determine how far should be the frustum planes placed from center
1473   //    of bounding box, in order to match the bounding box closely.
1474   Standard_Real aFitDistanceArray[6];
1475   NCollection_Array1<Standard_Real> aFitDistance (aFitDistanceArray[0], 1, 6);
1476   aFitDistance.Init (0.0);
1477   for (Standard_Integer anI = aFrustumPlane.Lower(); anI <= aFrustumPlane.Upper(); ++anI)
1478   {
1479     // Measure distances from center of bounding box to its corners towards the frustum plane.
1480     const gp_Dir& aPlaneN = aFrustumPlane[anI].Axis().Direction();
1481
1482     Standard_Real& aFitDist = aFitDistance[anI];
1483     for (Standard_Integer aJ = aBndCorner.Lower(); aJ <= aBndCorner.Upper(); ++aJ)
1484     {
1485       aFitDist = Max (aFitDist, gp_Vec (aBndCenter, aBndCorner[aJ]).Dot (aPlaneN));
1486     }
1487   }
1488   // The center of camera is placed on the same line with center of bounding box.
1489   // The view plane section crosses the bounding box at its center.
1490   // To compute view plane size, evaluate coefficients converting "point -> plane distance"
1491   // into view section size between the point and the frustum plane.
1492   //       proj
1493   //       /|\   right half of frame     //
1494   //        |                           //
1495   //  point o<--  distance * coeff  -->//---- (view plane section)
1496   //         \                        //
1497   //      (distance)                 //
1498   //                ~               //
1499   //                 (distance)    //
1500   //                           \/\//
1501   //                            \//
1502   //                            //
1503   //                      (frustum plane)
1504   aFitDistance[1] *= Sqrt(1 + Pow (Tan (  aCamSide .Angle (aFrustumPlane[1].Axis().Direction())), 2.0));
1505   aFitDistance[2] *= Sqrt(1 + Pow (Tan ((-aCamSide).Angle (aFrustumPlane[2].Axis().Direction())), 2.0));
1506   aFitDistance[3] *= Sqrt(1 + Pow (Tan (  aCamUp   .Angle (aFrustumPlane[3].Axis().Direction())), 2.0));
1507   aFitDistance[4] *= Sqrt(1 + Pow (Tan ((-aCamUp)  .Angle (aFrustumPlane[4].Axis().Direction())), 2.0));
1508   aFitDistance[5] *= Sqrt(1 + Pow (Tan (  aCamDir  .Angle (aFrustumPlane[5].Axis().Direction())), 2.0));
1509   aFitDistance[6] *= Sqrt(1 + Pow (Tan ((-aCamDir) .Angle (aFrustumPlane[6].Axis().Direction())), 2.0));
1510
1511   Standard_Real aViewSizeXv = aFitDistance[1] + aFitDistance[2];
1512   Standard_Real aViewSizeYv = aFitDistance[3] + aFitDistance[4];
1513   Standard_Real aViewSizeZv = aFitDistance[5] + aFitDistance[6];
1514
1515   // 3. Place center of camera on the same line with center of bounding
1516   //    box applying corresponding projection asymmetry (if any).
1517   Standard_Real anAssymXv = anAssymX * aViewSizeXv * 0.5;
1518   Standard_Real anAssymYv = anAssymY * aViewSizeYv * 0.5;
1519   Standard_Real anOffsetXv = (aFitDistance[2] - aFitDistance[1]) * 0.5 + anAssymXv;
1520   Standard_Real anOffsetYv = (aFitDistance[4] - aFitDistance[3]) * 0.5 + anAssymYv;
1521   gp_Vec aTranslateSide = gp_Vec (aCamSide) * anOffsetXv;
1522   gp_Vec aTranslateUp   = gp_Vec (aCamUp)   * anOffsetYv;
1523   gp_Pnt aCamNewCenter  = aBndCenter.Translated (aTranslateSide).Translated (aTranslateUp);
1524
1525   gp_Trsf aCenterTrsf;
1526   aCenterTrsf.SetTranslation (Center(), aCamNewCenter);
1527   Transform (aCenterTrsf);
1528   SetDistance (aFitDistance[6] + aFitDistance[5]);
1529
1530   if (aViewSizeXv < theResolution
1531    && aViewSizeYv < theResolution)
1532   {
1533     // Bounding box collapses to a point or thin line going in depth of the screen
1534     if (aViewSizeXv < theResolution || !theToEnlargeIfLine)
1535     {
1536       return false; // This is just one point or line and zooming has no effect.
1537     }
1538
1539     // Looking along line and "theToEnlargeIfLine" is requested.
1540     // Fit view to see whole scene on rotation.
1541     aViewSizeXv = aViewSizeZv;
1542     aViewSizeYv = aViewSizeZv;
1543   }
1544
1545   const Standard_Real anAspect = Aspect();
1546   if (anAspect > 1.0)
1547   {
1548     SetScale (Max (aViewSizeXv / anAspect, aViewSizeYv));
1549   }
1550   else
1551   {
1552     SetScale (Max (aViewSizeXv, aViewSizeYv * anAspect));
1553   }
1554   return true;
1555 }
1556
1557 //=============================================================================
1558 //function : ZFitAll
1559 //purpose  :
1560 //=============================================================================
1561 bool Graphic3d_Camera::ZFitAll (const Standard_Real theScaleFactor,
1562                                 const Bnd_Box&      theMinMax,
1563                                 const Bnd_Box&      theGraphicBB,
1564                                 Standard_Real&      theZNear,
1565                                 Standard_Real&      theZFar) const
1566 {
1567   Standard_ASSERT_RAISE (theScaleFactor > 0.0, "Zero or negative scale factor is not allowed.");
1568
1569   // Method changes zNear and zFar parameters of camera so as to fit graphical structures
1570   // by their graphical boundaries. It precisely fits min max boundaries of primary application
1571   // objects (second argument), while it can sacrifice the real graphical boundaries of the
1572   // scene with infinite or helper objects (third argument) for the sake of perspective projection.
1573   if (theGraphicBB.IsVoid())
1574   {
1575     theZNear = DEFAULT_ZNEAR;
1576     theZFar  = DEFAULT_ZFAR;
1577     return false;
1578   }
1579
1580   // Measure depth of boundary points from camera eye.
1581   NCollection_Sequence<gp_Pnt> aPntsToMeasure;
1582
1583   Standard_Real aGraphicBB[6];
1584   theGraphicBB.Get (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2], aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]);
1585
1586   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2]));
1587   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[5]));
1588   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[2]));
1589   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[5]));
1590   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[2]));
1591   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[5]));
1592   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[2]));
1593   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]));
1594
1595   Standard_Boolean isFiniteMinMax = !theMinMax.IsVoid() && !theMinMax.IsWhole();
1596
1597   if (isFiniteMinMax)
1598   {
1599     Standard_Real aMinMax[6];
1600     theMinMax.Get (aMinMax[0], aMinMax[1], aMinMax[2], aMinMax[3], aMinMax[4], aMinMax[5]);
1601
1602     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[2]));
1603     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[5]));
1604     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[2]));
1605     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[5]));
1606     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[2]));
1607     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[5]));
1608     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[2]));
1609     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[5]));
1610   }
1611
1612   // Camera eye plane.
1613   gp_Dir aCamDir = Direction();
1614   gp_Pnt aCamEye = myEye;
1615   gp_Pln aCamPln (aCamEye, aCamDir);
1616
1617   Standard_Real aModelMinDist = RealLast();
1618   Standard_Real aModelMaxDist = RealFirst();
1619   Standard_Real aGraphMinDist = RealLast();
1620   Standard_Real aGraphMaxDist = RealFirst();
1621
1622   const gp_XYZ& anAxialScale = myAxialScale;
1623
1624   // Get minimum and maximum distances to the eye plane.
1625   Standard_Integer aCounter = 0;
1626   NCollection_Sequence<gp_Pnt>::Iterator aPntIt(aPntsToMeasure);
1627   for (; aPntIt.More(); aPntIt.Next())
1628   {
1629     gp_Pnt aMeasurePnt = aPntIt.Value();
1630
1631     aMeasurePnt = gp_Pnt (aMeasurePnt.X() * anAxialScale.X(),
1632                           aMeasurePnt.Y() * anAxialScale.Y(),
1633                           aMeasurePnt.Z() * anAxialScale.Z());
1634
1635     Standard_Real aDistance = aCamPln.Distance (aMeasurePnt);
1636
1637     // Check if the camera is intruded into the scene.
1638     gp_Vec aVecToMeasurePnt (aCamEye, aMeasurePnt);
1639     if (aVecToMeasurePnt.Magnitude() > gp::Resolution()
1640      && aCamDir.IsOpposite (aVecToMeasurePnt, M_PI * 0.5))
1641     {
1642       aDistance *= -1;
1643     }
1644
1645     // The first eight points are from theGraphicBB, the last eight points are from theMinMax (can be absent).
1646     Standard_Real& aChangeMinDist = aCounter >= 8 ? aModelMinDist : aGraphMinDist;
1647     Standard_Real& aChangeMaxDist = aCounter >= 8 ? aModelMaxDist : aGraphMaxDist;
1648     aChangeMinDist = Min (aDistance, aChangeMinDist);
1649     aChangeMaxDist = Max (aDistance, aChangeMaxDist);
1650     aCounter++;
1651   }
1652
1653   // Compute depth of bounding box center.
1654   Standard_Real aMidDepth  = (aGraphMinDist + aGraphMaxDist) * 0.5;
1655   Standard_Real aHalfDepth = (aGraphMaxDist - aGraphMinDist) * 0.5;
1656
1657   // Compute enlarged or shrank near and far z ranges.
1658   Standard_Real aZNear  = aMidDepth - aHalfDepth * theScaleFactor;
1659   Standard_Real aZFar   = aMidDepth + aHalfDepth * theScaleFactor;
1660
1661   if (!IsOrthographic())
1662   {
1663     // Everything is behind the perspective camera.
1664     if (aZFar < zEpsilon())
1665     {
1666       theZNear = DEFAULT_ZNEAR;
1667       theZFar  = DEFAULT_ZFAR;
1668       return false;
1669     }
1670   }
1671
1672   //
1673   // Consider clipping errors due to double to single precision floating-point conversion.
1674   //
1675
1676   // Model to view transformation performs translation of points against eye position
1677   // in three dimensions. Both point coordinate and eye position values are converted from
1678   // double to single precision floating point numbers producing conversion errors. 
1679   // Epsilon (Mod) * 3.0 should safely compensate precision error for z coordinate after
1680   // translation assuming that the:
1681   // Epsilon (Eye.Mod()) * 3.0 > Epsilon (Eye.X()) + Epsilon (Eye.Y()) + Epsilon (Eye.Z()).
1682   Standard_Real aEyeConf = 3.0 * zEpsilon (myEye.XYZ().Modulus());
1683
1684   // Model to view transformation performs rotation of points according to view direction.
1685   // New z coordinate is computed as a multiplication of point's x, y, z coordinates by the
1686   // "forward" direction vector's x, y, z coordinates. Both point's and "z" direction vector's
1687   // values are converted from double to single precision floating point numbers producing
1688   // conversion errors.
1689   // Epsilon (Mod) * 6.0 should safely compensate the precision errors for the multiplication
1690   // of point coordinates by direction vector.
1691   gp_Pnt aGraphicMin = theGraphicBB.CornerMin();
1692   gp_Pnt aGraphicMax = theGraphicBB.CornerMax();
1693
1694   Standard_Real aModelConf = 6.0 * zEpsilon (aGraphicMin.XYZ().Modulus()) +
1695                              6.0 * zEpsilon (aGraphicMax.XYZ().Modulus());
1696
1697   // Compensate floating point conversion errors by increasing zNear, zFar to avoid clipping.
1698   aZNear -= zEpsilon (aZNear) + aEyeConf + aModelConf;
1699   aZFar  += zEpsilon (aZFar)  + aEyeConf + aModelConf;
1700
1701   if (!IsOrthographic())
1702   {
1703     // For perspective projection, the value of z in normalized device coordinates is non-linear
1704     // function of eye z coordinate. For fixed-point depth representation resolution of z in
1705     // model-view space will grow towards zFar plane and its scale depends mostly on how far is zNear
1706     // against camera's eye. The purpose of the code below is to select most appropriate zNear distance
1707     // to balance between clipping (less zNear, more chances to observe closely small models without clipping)
1708     // and resolution of depth. A well applicable criteria to this is a ratio between resolution of z at center
1709     // of model boundaries and the distance to that center point. The ratio is chosen empirically and validated
1710     // by tests database. It is considered to be ~0.001 (0.1%) for 24 bit depth buffer, for less depth bitness
1711     // the zNear will be placed similarly giving lower resolution.
1712     // Approximation of the formula for respectively large z range is:
1713     // zNear = [z * (1 + k) / (k * c)],
1714     // where:
1715     // z - distance to center of model boundaries;
1716     // k - chosen ratio, c - capacity of depth buffer;
1717     // k = 0.001, k * c = 1677.216, (1 + k) / (k * c) ~ 5.97E-4
1718     //
1719     // The function uses center of model boundaries computed from "theMinMax" boundaries (instead of using real
1720     // graphical boundaries of all displayed objects). That means that it can sacrifice resolution of presentation
1721     // of non primary ("infinite") application graphical objects in favor of better perspective projection of the
1722     // small applicative objects measured with "theMinMax" values.
1723     Standard_Real aZRange   = isFiniteMinMax ? aModelMaxDist - aModelMinDist : aGraphMaxDist - aGraphMinDist;
1724     Standard_Real aZMin     = isFiniteMinMax ? aModelMinDist : aGraphMinDist;
1725     Standard_Real aZ        = aZMin < 0 ? aZRange / 2.0 : aZRange / 2.0 + aZMin;
1726     Standard_Real aZNearMin = aZ * 5.97E-4;
1727     if (aZNear < aZNearMin)
1728     {
1729       // Clip zNear according to the minimum value matching the quality.
1730       aZNear = aZNearMin;
1731       if (aZFar < aZNear)
1732       {
1733         aZFar = aZNear;
1734       }
1735     }
1736     else
1737     {
1738       // Compensate zNear conversion errors for perspective projection.
1739       aZNear -= aZFar * zEpsilon (aZNear) / (aZFar - zEpsilon (aZNear));
1740     }
1741
1742     // Compensate zFar conversion errors for perspective projection.
1743     aZFar += zEpsilon (aZFar);
1744
1745     // Ensure that after all the zNear is not a negative value.
1746     if (aZNear < zEpsilon())
1747     {
1748       aZNear = zEpsilon();
1749     }
1750     Standard_ASSERT_RAISE (aZFar > aZNear, "ZFar should be greater than ZNear");
1751   }
1752
1753   theZNear = aZNear;
1754   theZFar  = aZFar;
1755   Standard_ASSERT_RAISE (aZFar > aZNear, "ZFar should be greater than ZNear");
1756   return true;
1757 }
1758
1759 //=============================================================================
1760 //function : Interpolate
1761 //purpose  :
1762 //=============================================================================
1763 void Graphic3d_Camera::Interpolate (const Handle(Graphic3d_Camera)& theStart,
1764                                     const Handle(Graphic3d_Camera)& theEnd,
1765                                     const double theT,
1766                                     Handle(Graphic3d_Camera)& theCamera)
1767 {
1768   if (Abs (theT - 1.0) < Precision::Confusion())
1769   {
1770     // just copy end-point transformation
1771     theCamera->Copy (theEnd);
1772     return;
1773   }
1774
1775   theCamera->Copy (theStart);
1776   if (Abs (theT - 0.0) < Precision::Confusion())
1777   {
1778     return;
1779   }
1780
1781   // apply rotation
1782   {
1783     gp_Ax3 aCamStart = cameraToAx3 (*theStart);
1784     gp_Ax3 aCamEnd   = cameraToAx3 (*theEnd);
1785     gp_Trsf aTrsfStart, aTrsfEnd;
1786     aTrsfStart.SetTransformation (aCamStart, gp::XOY());
1787     aTrsfEnd  .SetTransformation (aCamEnd,   gp::XOY());
1788
1789     gp_Quaternion aRotStart = aTrsfStart.GetRotation();
1790     gp_Quaternion aRotEnd   = aTrsfEnd  .GetRotation();
1791     gp_Quaternion aRotDelta = aRotEnd * aRotStart.Inverted();
1792     gp_Quaternion aRot = gp_QuaternionNLerp::Interpolate (gp_Quaternion(), aRotDelta, theT);
1793     gp_Trsf aTrsfRot;
1794     aTrsfRot.SetRotation (aRot);
1795     theCamera->Transform (aTrsfRot);
1796   }
1797
1798   // apply translation
1799   {
1800     gp_XYZ aCenter  = NCollection_Lerp<gp_XYZ>::Interpolate (theStart->Center().XYZ(), theEnd->Center().XYZ(), theT);
1801     gp_XYZ anEye    = NCollection_Lerp<gp_XYZ>::Interpolate (theStart->Eye().XYZ(),    theEnd->Eye().XYZ(),    theT);
1802     gp_XYZ anAnchor = aCenter;
1803     Standard_Real aKc = 0.0;
1804
1805     const Standard_Real aDeltaCenter = theStart->Center().Distance (theEnd->Center());
1806     const Standard_Real aDeltaEye    = theStart->Eye()   .Distance (theEnd->Eye());
1807     if (aDeltaEye <= gp::Resolution())
1808     {
1809       anAnchor = anEye;
1810       aKc = 1.0;
1811     }
1812     else if (aDeltaCenter > gp::Resolution())
1813     {
1814       aKc = aDeltaCenter / (aDeltaCenter + aDeltaEye);
1815
1816       const gp_XYZ anAnchorStart = NCollection_Lerp<gp_XYZ>::Interpolate (theStart->Center().XYZ(), theStart->Eye().XYZ(), aKc);
1817       const gp_XYZ anAnchorEnd   = NCollection_Lerp<gp_XYZ>::Interpolate (theEnd  ->Center().XYZ(), theEnd  ->Eye().XYZ(), aKc);
1818       anAnchor = NCollection_Lerp<gp_XYZ>::Interpolate (anAnchorStart, anAnchorEnd, theT);
1819     }
1820
1821     const gp_Vec        aDirEyeToCenter     = theCamera->Direction();
1822     const Standard_Real aDistEyeCenterStart = theStart->Eye().Distance (theStart->Center());
1823     const Standard_Real aDistEyeCenterEnd   = theEnd  ->Eye().Distance (theEnd  ->Center());
1824     const Standard_Real aDistEyeCenter      = NCollection_Lerp<Standard_Real>::Interpolate (aDistEyeCenterStart, aDistEyeCenterEnd, theT);
1825     aCenter = anAnchor + aDirEyeToCenter.XYZ() * aDistEyeCenter * aKc;
1826     anEye   = anAnchor - aDirEyeToCenter.XYZ() * aDistEyeCenter * (1.0 - aKc);
1827
1828     theCamera->SetEyeAndCenter (anEye, aCenter);
1829   }
1830
1831   // apply scaling
1832   if (Abs(theStart->Scale() - theEnd->Scale()) > Precision::Confusion()
1833    && theStart->IsOrthographic())
1834   {
1835     const Standard_Real aScale = NCollection_Lerp<Standard_Real>::Interpolate (theStart->Scale(), theEnd->Scale(), theT);
1836     theCamera->SetScale (aScale);
1837   }
1838 }
1839
1840 //=======================================================================
1841 //function : FrustumPoints
1842 //purpose  :
1843 //=======================================================================
1844 void Graphic3d_Camera::FrustumPoints (NCollection_Array1<Graphic3d_Vec3d>& thePoints,
1845                                       const Graphic3d_Mat4d& theModelWorld) const
1846 {
1847   if (thePoints.Length() != FrustumVerticesNB)
1848   {
1849     thePoints.Resize (0, FrustumVerticesNB, Standard_False);
1850   }
1851
1852   const Graphic3d_Mat4d& aProjectionMat = ProjectionMatrix();
1853   const Graphic3d_Mat4d aWorldViewMat = OrientationMatrix() * theModelWorld;
1854
1855   Standard_Real nLeft = 0.0, nRight = 0.0, nTop = 0.0, nBottom = 0.0;
1856   Standard_Real fLeft = 0.0, fRight = 0.0, fTop = 0.0, fBottom = 0.0;
1857   Standard_Real aNear = myZNear, aFar = myZFar;
1858   if (!IsOrthographic())
1859   {
1860     // handle perspective projection
1861     // Near plane
1862     nLeft   = aNear * (aProjectionMat.GetValue (0, 2) - 1.0) / aProjectionMat.GetValue (0, 0);
1863     nRight  = aNear * (aProjectionMat.GetValue (0, 2) + 1.0) / aProjectionMat.GetValue (0, 0);
1864     nTop    = aNear * (aProjectionMat.GetValue (1, 2) + 1.0) / aProjectionMat.GetValue (1, 1);
1865     nBottom = aNear * (aProjectionMat.GetValue (1, 2) - 1.0) / aProjectionMat.GetValue (1, 1);
1866     // Far plane
1867     fLeft   = aFar  * (aProjectionMat.GetValue (0, 2) - 1.0) / aProjectionMat.GetValue (0, 0);
1868     fRight  = aFar  * (aProjectionMat.GetValue (0, 2) + 1.0) / aProjectionMat.GetValue (0, 0);
1869     fTop    = aFar  * (aProjectionMat.GetValue (1, 2) + 1.0) / aProjectionMat.GetValue (1, 1);
1870     fBottom = aFar  * (aProjectionMat.GetValue (1, 2) - 1.0) / aProjectionMat.GetValue (1, 1);
1871   }
1872   else
1873   {
1874     // handle orthographic projection
1875     // Near plane
1876     nLeft   = ( 1.0 + aProjectionMat.GetValue (0, 3)) / (-aProjectionMat.GetValue (0, 0));
1877     fLeft   = nLeft;
1878     nRight  = ( 1.0 - aProjectionMat.GetValue (0, 3)) /   aProjectionMat.GetValue (0, 0);
1879     fRight  = nRight;
1880     nTop    = ( 1.0 - aProjectionMat.GetValue (1, 3)) /   aProjectionMat.GetValue (1, 1);
1881     fTop    = nTop;
1882     nBottom = (-1.0 - aProjectionMat.GetValue (1, 3)) /   aProjectionMat.GetValue (1, 1);
1883     fBottom = nBottom;
1884   }
1885
1886   Graphic3d_Vec4d aLeftTopNear     (nLeft,  nTop,    -aNear, 1.0), aRightBottomFar (fRight, fBottom, -aFar, 1.0);
1887   Graphic3d_Vec4d aLeftBottomNear  (nLeft,  nBottom, -aNear, 1.0), aRightTopFar    (fRight, fTop,    -aFar, 1.0);
1888   Graphic3d_Vec4d aRightBottomNear (nRight, nBottom, -aNear, 1.0), aLeftTopFar     (fLeft,  fTop,    -aFar, 1.0);
1889   Graphic3d_Vec4d aRightTopNear    (nRight, nTop,    -aNear, 1.0), aLeftBottomFar  (fLeft,  fBottom, -aFar, 1.0);
1890
1891   Graphic3d_Mat4d anInvWorldView;
1892   aWorldViewMat.Inverted (anInvWorldView);
1893
1894   Graphic3d_Vec4d aTmpPnt;
1895   aTmpPnt = anInvWorldView * aLeftTopNear;
1896   thePoints.SetValue (FrustumVert_LeftTopNear,     aTmpPnt.xyz() / aTmpPnt.w());
1897   aTmpPnt = anInvWorldView * aRightBottomFar;
1898   thePoints.SetValue (FrustumVert_RightBottomFar,  aTmpPnt.xyz() / aTmpPnt.w());
1899   aTmpPnt = anInvWorldView * aLeftBottomNear;
1900   thePoints.SetValue (FrustumVert_LeftBottomNear,  aTmpPnt.xyz() / aTmpPnt.w());
1901   aTmpPnt = anInvWorldView * aRightTopFar;
1902   thePoints.SetValue (FrustumVert_RightTopFar,     aTmpPnt.xyz() / aTmpPnt.w());
1903   aTmpPnt = anInvWorldView * aRightBottomNear;
1904   thePoints.SetValue (FrustumVert_RightBottomNear, aTmpPnt.xyz() / aTmpPnt.w());
1905   aTmpPnt = anInvWorldView * aLeftTopFar;
1906   thePoints.SetValue (FrustumVert_LeftTopFar,      aTmpPnt.xyz() / aTmpPnt.w());
1907   aTmpPnt = anInvWorldView * aRightTopNear;
1908   thePoints.SetValue (FrustumVert_RightTopNear,    aTmpPnt.xyz() / aTmpPnt.w());
1909   aTmpPnt = anInvWorldView * aLeftBottomFar;
1910   thePoints.SetValue (FrustumVert_LeftBottomFar,   aTmpPnt.xyz() / aTmpPnt.w());
1911 }
1912
1913 //=======================================================================
1914 //function : DumpJson
1915 //purpose  : 
1916 //=======================================================================
1917 void Graphic3d_Camera::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const
1918 {
1919   OCCT_DUMP_TRANSIENT_CLASS_BEGIN (theOStream)
1920
1921   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myUp)
1922   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myDirection)
1923   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myEye)
1924
1925   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myDistance)
1926
1927   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myAxialScale)
1928   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myProjType)
1929   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myFOVy)
1930   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myZNear)
1931   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myZFar)
1932   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myAspect)
1933
1934   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myScale)
1935   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myZFocus)
1936   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myZFocusType)
1937   
1938   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myIOD)
1939   OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myIODType)
1940   
1941   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myTile)
1942   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myMatricesD)
1943   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myMatricesF)
1944   OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myWorldViewProjState)
1945 }