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