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