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