0026147: Visualization - restore the ability to pick only fully included objects...
[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 <gp_Pln.hxx>
17 #include <Standard_ShortReal.hxx>
18
19 #include <Graphic3d_Vec4.hxx>
20 #include <Graphic3d_Camera.hxx>
21
22 #include <Standard_Atomic.hxx>
23 #include <Standard_Assert.hxx>
24
25 #include <NCollection_Sequence.hxx>
26
27 IMPLEMENT_STANDARD_HANDLE(Graphic3d_Camera, Standard_Transient)
28 IMPLEMENT_STANDARD_RTTIEXT(Graphic3d_Camera, Standard_Transient)
29
30 namespace
31 {
32   // (degrees -> radians) * 0.5
33   static const Standard_Real DTR_HALF = 0.5 * 0.0174532925;
34
35   // default property values
36   static const Standard_Real DEFAULT_ZNEAR = 0.001;
37   static const Standard_Real DEFAULT_ZFAR  = 3000.0;
38
39   // atomic state counter
40   static volatile Standard_Integer THE_STATE_COUNTER = 0;
41
42   // minimum camera distance
43   static const Standard_Real MIN_DISTANCE = Pow (0.1, ShortRealDigits() - 2);
44 };
45
46 // =======================================================================
47 // function : Graphic3d_Camera
48 // purpose  :
49 // =======================================================================
50 Graphic3d_Camera::Graphic3d_Camera()
51 : myUp (0.0, 1.0, 0.0),
52   myEye (0.0, 0.0, -1500.0),
53   myCenter (0.0, 0.0, 0.0),
54   myAxialScale (1.0, 1.0, 1.0),
55   myProjType (Projection_Orthographic),
56   myFOVy (45.0),
57   myZNear (DEFAULT_ZNEAR),
58   myZFar (DEFAULT_ZFAR),
59   myAspect (1.0),
60   myScale (1000.0),
61   myZFocus (1.0),
62   myZFocusType (FocusType_Relative),
63   myIOD (0.05),
64   myIODType (IODType_Relative)
65 {
66   myProjectionState  = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
67   myOrientationState = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
68 }
69
70 // =======================================================================
71 // function : Graphic3d_Camera
72 // purpose  :
73 // =======================================================================
74 Graphic3d_Camera::Graphic3d_Camera (const Handle(Graphic3d_Camera)& theOther)
75 {
76   Copy (theOther);
77 }
78
79 // =======================================================================
80 // function : CopyMappingData
81 // purpose  :
82 // =======================================================================
83 void Graphic3d_Camera::CopyMappingData (const Handle(Graphic3d_Camera)& theOtherCamera)
84 {
85   myFOVy            = theOtherCamera->myFOVy;
86   myZNear           = theOtherCamera->myZNear;
87   myZFar            = theOtherCamera->myZFar;
88   myAspect          = theOtherCamera->myAspect;
89   myScale           = theOtherCamera->myScale;
90   myZFocus          = theOtherCamera->myZFocus;
91   myZFocusType      = theOtherCamera->myZFocusType;
92   myIOD             = theOtherCamera->myIOD;
93   myIODType         = theOtherCamera->myIODType;
94   myProjType        = theOtherCamera->myProjType;
95   myProjectionState = theOtherCamera->myProjectionState;
96
97   InvalidateProjection();
98 }
99
100 // =======================================================================
101 // function : CopyOrientationData
102 // purpose  :
103 // =======================================================================
104 void Graphic3d_Camera::CopyOrientationData (const Handle(Graphic3d_Camera)& theOtherCamera)
105 {
106   myUp               = theOtherCamera->myUp;
107   myEye              = theOtherCamera->myEye;
108   myCenter           = theOtherCamera->myCenter;
109   myAxialScale       = theOtherCamera->myAxialScale;
110   myOrientationState = theOtherCamera->myOrientationState;
111
112   InvalidateOrientation();
113 }
114
115 // =======================================================================
116 // function : Copy
117 // purpose  :
118 // =======================================================================
119 void Graphic3d_Camera::Copy (const Handle(Graphic3d_Camera)& theOther)
120 {
121   CopyMappingData (theOther);
122   CopyOrientationData (theOther);
123 }
124
125 // =======================================================================
126 // function : SetEye
127 // purpose  :
128 // =======================================================================
129 void Graphic3d_Camera::SetEye (const gp_Pnt& theEye)
130 {
131   myEye = theEye;
132   InvalidateOrientation();
133 }
134
135 // =======================================================================
136 // function : SetCenter
137 // purpose  :
138 // =======================================================================
139 void Graphic3d_Camera::SetCenter (const gp_Pnt& theCenter)
140 {
141   myCenter = theCenter;
142   InvalidateOrientation();
143 }
144
145 // =======================================================================
146 // function : SetUp
147 // purpose  :
148 // =======================================================================
149 void Graphic3d_Camera::SetUp (const gp_Dir& theUp)
150 {
151   myUp = theUp;
152   InvalidateOrientation();
153 }
154
155 // =======================================================================
156 // function : SetAxialScale
157 // purpose  :
158 // =======================================================================
159 void Graphic3d_Camera::SetAxialScale (const gp_XYZ& theAxialScale)
160 {
161   myAxialScale = theAxialScale;
162   InvalidateOrientation();
163 }
164
165 // =======================================================================
166 // function : SetDistance
167 // purpose  :
168 // =======================================================================
169 void Graphic3d_Camera::SetDistance (const Standard_Real theDistance)
170 {
171   gp_Vec aCenter2Eye (Direction());
172   aCenter2Eye.Reverse();
173
174   // Camera should have non-zero distance.
175   aCenter2Eye.Scale (Max (theDistance, MIN_DISTANCE));
176   SetEye (Center().Translated (aCenter2Eye));
177 }
178
179 // =======================================================================
180 // function : Distance
181 // purpose  :
182 // =======================================================================
183 Standard_Real Graphic3d_Camera::Distance() const
184 {
185   return myEye.Distance (myCenter);
186 }
187
188 // =======================================================================
189 // function : SetDirection
190 // purpose  :
191 // =======================================================================
192 void Graphic3d_Camera::SetDirection (const gp_Dir& theDir)
193 {
194   gp_Vec aScaledDir (theDir);
195   aScaledDir.Scale (Distance());
196   aScaledDir.Reverse();
197   SetEye (Center().Translated (aScaledDir));
198 }
199
200 // =======================================================================
201 // function : Direction
202 // purpose  :
203 // =======================================================================
204 gp_Dir Graphic3d_Camera::Direction() const
205 {
206   return gp_Dir (gp_Vec (myEye, myCenter));
207 }
208
209 // =======================================================================
210 // function : SetScale
211 // purpose  :
212 // =======================================================================
213 void Graphic3d_Camera::SetScale (const Standard_Real theScale)
214 {
215   myScale = theScale;
216
217   switch (myProjType)
218   {
219     case Projection_Perspective  :
220     case Projection_Stereo       :
221     case Projection_MonoLeftEye  :
222     case Projection_MonoRightEye :
223     {
224       Standard_Real aDistance = theScale * 0.5 / Tan(myFOVy * M_PI / 360.0);
225       SetDistance (aDistance);
226     }
227
228     default :
229       break;
230   }
231
232   InvalidateProjection();
233 }
234
235 // =======================================================================
236 // function : Scale
237 // purpose  :
238 // =======================================================================
239 Standard_Real Graphic3d_Camera::Scale() const
240 {
241   switch (myProjType)
242   {
243     case Projection_Orthographic :
244       return myScale;
245
246     // case Projection_Perspective  :
247     // case Projection_Stereo       :
248     // case Projection_MonoLeftEye  :
249     // case Projection_MonoRightEye :
250     default :
251       return Distance() * 2.0 * Tan (myFOVy * M_PI / 360.0);
252   }
253 }
254
255 // =======================================================================
256 // function : SetProjectionType
257 // purpose  :
258 // =======================================================================
259 void Graphic3d_Camera::SetProjectionType (const Projection theProjectionType)
260 {
261   Projection anOldType = myProjType;
262
263   if (anOldType == theProjectionType)
264   {
265     return;
266   }
267
268   if (anOldType == Projection_Orthographic)
269   {
270     if (myZNear <= RealEpsilon())
271     {
272       myZNear = DEFAULT_ZNEAR;
273     }
274     if (myZFar <= RealEpsilon())
275     {
276       myZFar = DEFAULT_ZFAR;
277     }
278   }
279
280   myProjType = theProjectionType;
281
282   InvalidateProjection();
283 }
284
285 // =======================================================================
286 // function : SetFOVy
287 // purpose  :
288 // =======================================================================
289 void Graphic3d_Camera::SetFOVy (const Standard_Real theFOVy)
290 {
291   myFOVy = theFOVy;
292   InvalidateProjection();
293 }
294
295 // =======================================================================
296 // function : SetZRange
297 // purpose  :
298 // =======================================================================
299 void Graphic3d_Camera::SetZRange (const Standard_Real theZNear,
300                                   const Standard_Real theZFar)
301 {
302   Standard_ASSERT_RAISE (theZFar > theZNear, "ZFar should be greater than ZNear");
303   if (!IsOrthographic())
304   {
305     Standard_ASSERT_RAISE (theZNear > 0.0, "Only positive Z-Near is allowed for perspective camera");
306     Standard_ASSERT_RAISE (theZFar  > 0.0, "Only positive Z-Far is allowed for perspective camera");
307   }
308
309   myZNear = theZNear;
310   myZFar  = theZFar;
311
312   InvalidateProjection();
313 }
314
315 // =======================================================================
316 // function : SetAspect
317 // purpose  :
318 // =======================================================================
319 void Graphic3d_Camera::SetAspect (const Standard_Real theAspect)
320 {
321   myAspect = theAspect;
322   InvalidateProjection();
323 }
324
325 // =======================================================================
326 // function : SetZFocus
327 // purpose  :
328 // =======================================================================
329 void Graphic3d_Camera::SetZFocus(const FocusType theType, const Standard_Real theZFocus)
330 {
331   myZFocusType = theType;
332   myZFocus = theZFocus;
333   InvalidateProjection();
334 }
335
336 // =======================================================================
337 // function : SetIOD
338 // purpose  :
339 // =======================================================================
340 void Graphic3d_Camera::SetIOD (const IODType theType, const Standard_Real theIOD)
341 {
342   myIODType = theType;
343   myIOD = theIOD;
344   InvalidateProjection();
345 }
346
347 // =======================================================================
348 // function : OrthogonalizeUp
349 // purpose  :
350 // =======================================================================
351 void Graphic3d_Camera::OrthogonalizeUp()
352 {
353   SetUp (OrthogonalizedUp());
354 }
355
356 // =======================================================================
357 // function : OrthogonalizedUp
358 // purpose  :
359 // =======================================================================
360 gp_Dir Graphic3d_Camera::OrthogonalizedUp() const
361 {
362   gp_Dir aDir  = Direction();
363   gp_Dir aLeft = aDir.Crossed (Up());
364
365   // recompute up as: up = left x direction
366   return aLeft.Crossed (aDir);
367 }
368
369 // =======================================================================
370 // function : Transform
371 // purpose  :
372 // =======================================================================
373 void Graphic3d_Camera::Transform (const gp_Trsf& theTrsf)
374 {
375   myUp.Transform (theTrsf);
376   myEye.Transform (theTrsf);
377   myCenter.Transform (theTrsf);
378   InvalidateOrientation();
379 }
380
381 // =======================================================================
382 // function : safePointCast
383 // purpose  :
384 // =======================================================================
385 static Graphic3d_Vec4d safePointCast (const gp_Pnt& thePnt)
386 {
387   Standard_Real aLim = 1e15f;
388
389   // have to deal with values greater then max float
390   gp_Pnt aSafePoint = thePnt;
391   const Standard_Real aBigFloat = aLim * 0.1f;
392   if (Abs (aSafePoint.X()) > aLim)
393     aSafePoint.SetX (aSafePoint.X() >= 0 ? aBigFloat : -aBigFloat);
394   if (Abs (aSafePoint.Y()) > aLim)
395     aSafePoint.SetY (aSafePoint.Y() >= 0 ? aBigFloat : -aBigFloat);
396   if (Abs (aSafePoint.Z()) > aLim)
397     aSafePoint.SetZ (aSafePoint.Z() >= 0 ? aBigFloat : -aBigFloat);
398
399   // convert point
400   Graphic3d_Vec4d aPnt (aSafePoint.X(), aSafePoint.Y(), aSafePoint.Z(), 1.0);
401
402   return aPnt;
403 }
404
405 // =======================================================================
406 // function : Project
407 // purpose  :
408 // =======================================================================
409 gp_Pnt Graphic3d_Camera::Project (const gp_Pnt& thePnt) const
410 {
411   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
412   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
413
414   // use compatible type of point
415   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
416
417   aPnt = aViewMx * aPnt; // convert to view coordinate space
418   aPnt = aProjMx * aPnt; // convert to projection coordinate space
419
420   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
421
422   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
423 }
424
425 // =======================================================================
426 // function : UnProject
427 // purpose  :
428 // =======================================================================
429 gp_Pnt Graphic3d_Camera::UnProject (const gp_Pnt& thePnt) const
430 {
431   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
432   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
433
434   Graphic3d_Mat4d aInvView;
435   Graphic3d_Mat4d aInvProj;
436
437   // this case should never happen
438   if (!aViewMx.Inverted (aInvView) || !aProjMx.Inverted (aInvProj))
439   {
440     return gp_Pnt (0.0, 0.0, 0.0);
441   }
442
443   // use compatible type of point
444   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
445
446   aPnt = aInvProj * aPnt; // convert to view coordinate space
447   aPnt = aInvView * aPnt; // convert to world coordinate space
448
449   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
450
451   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
452 }
453
454 // =======================================================================
455 // function : ConvertView2Proj
456 // purpose  :
457 // =======================================================================
458 gp_Pnt Graphic3d_Camera::ConvertView2Proj (const gp_Pnt& thePnt) const
459 {
460   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
461
462   // use compatible type of point
463   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
464
465   aPnt = aProjMx * aPnt; // convert to projection coordinate space
466
467   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
468
469   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
470 }
471
472 // =======================================================================
473 // function : ConvertProj2View
474 // purpose  :
475 // =======================================================================
476 gp_Pnt Graphic3d_Camera::ConvertProj2View (const gp_Pnt& thePnt) const
477 {
478   const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
479
480   Graphic3d_Mat4d aInvProj;
481
482   // this case should never happen, but...
483   if (!aProjMx.Inverted (aInvProj))
484   {
485     return gp_Pnt (0, 0, 0);
486   }
487
488   // use compatible type of point
489   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
490
491   aPnt = aInvProj * aPnt; // convert to view coordinate space
492
493   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
494
495   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
496 }
497
498 // =======================================================================
499 // function : ConvertWorld2View
500 // purpose  :
501 // =======================================================================
502 gp_Pnt Graphic3d_Camera::ConvertWorld2View (const gp_Pnt& thePnt) const
503 {
504   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
505
506   // use compatible type of point
507   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
508
509   aPnt = aViewMx * aPnt; // convert to view coordinate space
510
511   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
512
513   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
514 }
515
516 // =======================================================================
517 // function : ConvertView2World
518 // purpose  :
519 // =======================================================================
520 gp_Pnt Graphic3d_Camera::ConvertView2World (const gp_Pnt& thePnt) const
521 {
522   const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
523
524   Graphic3d_Mat4d aInvView;
525
526   if (!aViewMx.Inverted (aInvView))
527   {
528     return gp_Pnt(0, 0, 0);
529   }
530
531   // use compatible type of point
532   Graphic3d_Vec4d aPnt = safePointCast (thePnt);
533
534   aPnt = aInvView * aPnt; // convert to world coordinate space
535
536   const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
537
538   return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
539 }
540
541 // =======================================================================
542 // function : ViewDimensions
543 // purpose  :
544 // =======================================================================
545 gp_XYZ Graphic3d_Camera::ViewDimensions() const
546 {
547   // view plane dimensions
548   Standard_Real aSizeY = IsOrthographic() ? myScale : (2.0 * Distance() * Tan (DTR_HALF * myFOVy));
549   Standard_Real aSizeX = myAspect * aSizeY;
550
551   // and frustum depth
552   return gp_XYZ (aSizeX, aSizeY, myZFar - myZNear);
553 }
554
555 // =======================================================================
556 // function : Frustum
557 // purpose  :
558 // =======================================================================
559 void Graphic3d_Camera::Frustum (gp_Pln& theLeft,
560                                 gp_Pln& theRight,
561                                 gp_Pln& theBottom,
562                                 gp_Pln& theTop,
563                                 gp_Pln& theNear,
564                                 gp_Pln& theFar) const
565 {
566   gp_Vec aProjection = gp_Vec (Direction());
567   gp_Vec anUp        = OrthogonalizedUp();
568   gp_Vec aSide       = aProjection ^ anUp;
569
570   Standard_ASSERT_RAISE (
571     !aProjection.IsParallel (anUp, Precision::Angular()),
572      "Can not derive SIDE = PROJ x UP - directions are parallel");
573
574   theNear = gp_Pln (Eye().Translated (aProjection * ZNear()), aProjection);
575   theFar  = gp_Pln (Eye().Translated (aProjection * ZFar()), -aProjection);
576
577   Standard_Real aHScaleHor = Scale() * 0.5 * Aspect();
578   Standard_Real aHScaleVer = Scale() * 0.5;
579
580   gp_Pnt aPntLeft   = Center().Translated (aHScaleHor * -aSide);
581   gp_Pnt aPntRight  = Center().Translated (aHScaleHor *  aSide);
582   gp_Pnt aPntBottom = Center().Translated (aHScaleVer * -anUp);
583   gp_Pnt aPntTop    = Center().Translated (aHScaleVer *  anUp);
584
585   gp_Vec aDirLeft   =  aSide;
586   gp_Vec aDirRight  = -aSide;
587   gp_Vec aDirBottom =  anUp;
588   gp_Vec aDirTop    = -anUp;
589   if (!IsOrthographic())
590   {
591     Standard_Real aHFOVHor = ATan (Tan (DTR_HALF * FOVy()) * Aspect());
592     Standard_Real aHFOVVer = DTR_HALF * FOVy();
593     aDirLeft.Rotate   (gp_Ax1 (gp::Origin(), anUp),   aHFOVHor);
594     aDirRight.Rotate  (gp_Ax1 (gp::Origin(), anUp),  -aHFOVHor);
595     aDirBottom.Rotate (gp_Ax1 (gp::Origin(), aSide), -aHFOVVer);
596     aDirTop.Rotate    (gp_Ax1 (gp::Origin(), aSide),  aHFOVVer);
597   }
598
599   theLeft   = gp_Pln (aPntLeft,   aDirLeft);
600   theRight  = gp_Pln (aPntRight,  aDirRight);
601   theBottom = gp_Pln (aPntBottom, aDirBottom);
602   theTop    = gp_Pln (aPntTop,    aDirTop);
603 }
604
605 // =======================================================================
606 // function : OrientationMatrix
607 // purpose  :
608 // =======================================================================
609 const Graphic3d_Mat4d& Graphic3d_Camera::OrientationMatrix() const
610 {
611   return *UpdateOrientation (myMatricesD).Orientation;
612 }
613
614 // =======================================================================
615 // function : OrientationMatrixF
616 // purpose  :
617 // =======================================================================
618 const Graphic3d_Mat4& Graphic3d_Camera::OrientationMatrixF() const
619 {
620   return *UpdateOrientation (myMatricesF).Orientation;
621 }
622
623 // =======================================================================
624 // function : ProjectionMatrix
625 // purpose  :
626 // =======================================================================
627 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionMatrix() const
628 {
629   return *UpdateProjection (myMatricesD).MProjection;
630 }
631
632 // =======================================================================
633 // function : ProjectionMatrixF
634 // purpose  :
635 // =======================================================================
636 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionMatrixF() const
637 {
638   return *UpdateProjection (myMatricesF).MProjection;
639 }
640
641 // =======================================================================
642 // function : ProjectionStereoLeft
643 // purpose  :
644 // =======================================================================
645 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoLeft() const
646 {
647   return *UpdateProjection (myMatricesD).LProjection;
648 }
649
650 // =======================================================================
651 // function : ProjectionStereoLeftF
652 // purpose  :
653 // =======================================================================
654 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoLeftF() const
655 {
656   return *UpdateProjection (myMatricesF).LProjection;
657 }
658
659 // =======================================================================
660 // function : ProjectionStereoRight
661 // purpose  :
662 // =======================================================================
663 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoRight() const
664 {
665   return *UpdateProjection (myMatricesD).RProjection;
666 }
667
668 // =======================================================================
669 // function : ProjectionStereoRightF
670 // purpose  :
671 // =======================================================================
672 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoRightF() const
673 {
674   return *UpdateProjection (myMatricesF).RProjection;
675 }
676
677 // =======================================================================
678 // function : UpdateProjection
679 // purpose  :
680 // =======================================================================
681 template <typename Elem_t>
682 Graphic3d_Camera::TransformMatrices<Elem_t>&
683   Graphic3d_Camera::UpdateProjection (TransformMatrices<Elem_t>& theMatrices) const
684 {
685   if (theMatrices.IsProjectionValid())
686   {
687     return theMatrices; // for inline accessors
688   }
689
690   theMatrices.InitProjection();
691
692   // sets top of frustum based on FOVy and near clipping plane
693   Elem_t aScale   = static_cast<Elem_t> (myScale);
694   Elem_t aZNear   = static_cast<Elem_t> (myZNear);
695   Elem_t aZFar    = static_cast<Elem_t> (myZFar);
696   Elem_t anAspect = static_cast<Elem_t> (myAspect);
697   Elem_t aDYHalf = 0.0;
698   if (IsOrthographic())
699   {
700     aDYHalf = aScale * Elem_t (0.5);
701   }
702   else
703   {
704     aDYHalf = aZNear * Elem_t (Tan (DTR_HALF * myFOVy));
705   }
706
707   // sets right of frustum based on aspect ratio
708   Elem_t aDXHalf = anAspect * aDYHalf;
709   Elem_t aLeft   = -aDXHalf;
710   Elem_t aRight  =  aDXHalf;
711   Elem_t aBot    = -aDYHalf;
712   Elem_t aTop    =  aDYHalf;
713
714   Elem_t aIOD  = myIODType == IODType_Relative 
715     ? static_cast<Elem_t> (myIOD * Distance())
716     : static_cast<Elem_t> (myIOD);
717
718   Elem_t aFocus = myZFocusType == FocusType_Relative 
719     ? static_cast<Elem_t> (myZFocus * Distance())
720     : static_cast<Elem_t> (myZFocus);
721
722   switch (myProjType)
723   {
724     case Projection_Orthographic :
725       OrthoProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection);
726       break;
727
728     case Projection_Perspective :
729       PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection);
730       break;
731
732     case Projection_MonoLeftEye :
733     {
734       StereoEyeProj (aLeft, aRight, aBot, aTop,
735                      aZNear, aZFar, aIOD, aFocus,
736                      Standard_True, *theMatrices.MProjection);
737       *theMatrices.LProjection = *theMatrices.MProjection;
738       break;
739     }
740
741     case Projection_MonoRightEye :
742     {
743       StereoEyeProj (aLeft, aRight, aBot, aTop,
744                      aZNear, aZFar, aIOD, aFocus,
745                      Standard_False, *theMatrices.MProjection);
746       *theMatrices.RProjection = *theMatrices.MProjection;
747       break;
748     }
749
750     case Projection_Stereo :
751     {
752       PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection);
753
754       StereoEyeProj (aLeft, aRight, aBot, aTop,
755                      aZNear, aZFar, aIOD, aFocus,
756                      Standard_True,
757                      *theMatrices.LProjection);
758
759       StereoEyeProj (aLeft, aRight, aBot, aTop,
760                      aZNear, aZFar, aIOD, aFocus,
761                      Standard_False,
762                      *theMatrices.RProjection);
763       break;
764     }
765   }
766
767   return theMatrices; // for inline accessors
768 }
769
770 // =======================================================================
771 // function : UpdateOrientation
772 // purpose  :
773 // =======================================================================
774 template <typename Elem_t>
775 Graphic3d_Camera::TransformMatrices<Elem_t>&
776   Graphic3d_Camera::UpdateOrientation (TransformMatrices<Elem_t>& theMatrices) const
777 {
778   if (theMatrices.IsOrientationValid())
779   {
780     return theMatrices; // for inline accessors
781   }
782
783   theMatrices.InitOrientation();
784
785   NCollection_Vec3<Elem_t> anEye (static_cast<Elem_t> (myEye.X()),
786                                   static_cast<Elem_t> (myEye.Y()),
787                                   static_cast<Elem_t> (myEye.Z()));
788
789   NCollection_Vec3<Elem_t> aCenter (static_cast<Elem_t> (myCenter.X()),
790                                     static_cast<Elem_t> (myCenter.Y()),
791                                     static_cast<Elem_t> (myCenter.Z()));
792
793   NCollection_Vec3<Elem_t> anUp (static_cast<Elem_t> (myUp.X()),
794                                  static_cast<Elem_t> (myUp.Y()),
795                                  static_cast<Elem_t> (myUp.Z()));
796
797   NCollection_Vec3<Elem_t> anAxialScale (static_cast<Elem_t> (myAxialScale.X()),
798                                          static_cast<Elem_t> (myAxialScale.Y()),
799                                          static_cast<Elem_t> (myAxialScale.Z()));
800
801   LookOrientation (anEye, aCenter, anUp, anAxialScale, *theMatrices.Orientation);
802
803   return theMatrices; // for inline accessors
804 }
805
806 // =======================================================================
807 // function : InvalidateProjection
808 // purpose  :
809 // =======================================================================
810 void Graphic3d_Camera::InvalidateProjection()
811 {
812   myMatricesD.ResetProjection();
813   myMatricesF.ResetProjection();
814   myProjectionState = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
815 }
816
817 // =======================================================================
818 // function : InvalidateOrientation
819 // purpose  :
820 // =======================================================================
821 void Graphic3d_Camera::InvalidateOrientation()
822 {
823   myMatricesD.ResetOrientation();
824   myMatricesF.ResetOrientation();
825   myOrientationState = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
826 }
827
828 // =======================================================================
829 // function : OrthoProj
830 // purpose  :
831 // =======================================================================
832 template <typename Elem_t>
833 void Graphic3d_Camera::OrthoProj (const Elem_t theLeft,
834                                   const Elem_t theRight,
835                                   const Elem_t theBottom,
836                                   const Elem_t theTop,
837                                   const Elem_t theNear,
838                                   const Elem_t theFar,
839                                   NCollection_Mat4<Elem_t>& theOutMx)
840 {
841   // row 0
842   theOutMx.ChangeValue (0, 0) = Elem_t (2.0) / (theRight - theLeft);
843   theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
844   theOutMx.ChangeValue (0, 2) = Elem_t (0.0);
845   theOutMx.ChangeValue (0, 3) = - (theRight + theLeft) / (theRight - theLeft);
846
847   // row 1
848   theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
849   theOutMx.ChangeValue (1, 1) = Elem_t (2.0) / (theTop - theBottom);
850   theOutMx.ChangeValue (1, 2) = Elem_t (0.0);
851   theOutMx.ChangeValue (1, 3) = - (theTop + theBottom) / (theTop - theBottom);
852
853   // row 2
854   theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
855   theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
856   theOutMx.ChangeValue (2, 2) = Elem_t (-2.0) / (theFar - theNear);
857   theOutMx.ChangeValue (2, 3) = - (theFar + theNear) / (theFar - theNear);
858
859   // row 3
860   theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
861   theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
862   theOutMx.ChangeValue (3, 2) = Elem_t (0.0);
863   theOutMx.ChangeValue (3, 3) = Elem_t (1.0);
864 }
865
866 // =======================================================================
867 // function : PerspectiveProj
868 // purpose  :
869 // =======================================================================
870 template <typename Elem_t>
871 void Graphic3d_Camera::PerspectiveProj (const Elem_t theLeft,
872                                         const Elem_t theRight,
873                                         const Elem_t theBottom,
874                                         const Elem_t theTop,
875                                         const Elem_t theNear,
876                                         const Elem_t theFar,
877                                         NCollection_Mat4<Elem_t>& theOutMx)
878 {
879   // column 0
880   theOutMx.ChangeValue (0, 0) = (Elem_t (2.0) * theNear) / (theRight - theLeft);
881   theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
882   theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
883   theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
884
885   // column 1
886   theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
887   theOutMx.ChangeValue (1, 1) = (Elem_t (2.0) * theNear) / (theTop - theBottom);
888   theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
889   theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
890
891   // column 2
892   theOutMx.ChangeValue (0, 2) = (theRight + theLeft) / (theRight - theLeft);
893   theOutMx.ChangeValue (1, 2) = (theTop + theBottom) / (theTop - theBottom);
894   theOutMx.ChangeValue (2, 2) = -(theFar + theNear) / (theFar - theNear);
895   theOutMx.ChangeValue (3, 2) = Elem_t (-1.0);
896
897   // column 3
898   theOutMx.ChangeValue (0, 3) = Elem_t (0.0);
899   theOutMx.ChangeValue (1, 3) = Elem_t (0.0);
900   theOutMx.ChangeValue (2, 3) = -(Elem_t (2.0) * theFar * theNear) / (theFar - theNear);
901   theOutMx.ChangeValue (3, 3) = Elem_t (0.0);
902 }
903
904 // =======================================================================
905 // function : StereoEyeProj
906 // purpose  :
907 // =======================================================================
908 template <typename Elem_t>
909 void Graphic3d_Camera::StereoEyeProj (const Elem_t theLeft,
910                                       const Elem_t theRight,
911                                       const Elem_t theBottom,
912                                       const Elem_t theTop,
913                                       const Elem_t theNear,
914                                       const Elem_t theFar,
915                                       const Elem_t theIOD,
916                                       const Elem_t theZFocus,
917                                       const Standard_Boolean theIsLeft,
918                                       NCollection_Mat4<Elem_t>& theOutMx)
919 {
920   Elem_t aDx = theIsLeft ? Elem_t (0.5) * theIOD : Elem_t (-0.5) * theIOD;
921   Elem_t aDXStereoShift = aDx * theNear / theZFocus;
922
923   // construct eye projection matrix
924   PerspectiveProj (theLeft  + aDXStereoShift,
925                    theRight + aDXStereoShift,
926                    theBottom, theTop, theNear, theFar,
927                    theOutMx);
928
929   if (theIOD != Elem_t (0.0))
930   {
931     // X translation to cancel parallax
932     theOutMx.Translate (NCollection_Vec3<Elem_t> (aDx, Elem_t (0.0), Elem_t (0.0)));
933   }
934 }
935
936 // =======================================================================
937 // function : LookOrientation
938 // purpose  :
939 // =======================================================================
940 template <typename Elem_t>
941 void Graphic3d_Camera::LookOrientation (const NCollection_Vec3<Elem_t>& theEye,
942                                         const NCollection_Vec3<Elem_t>& theLookAt,
943                                         const NCollection_Vec3<Elem_t>& theUpDir,
944                                         const NCollection_Vec3<Elem_t>& theAxialScale,
945                                         NCollection_Mat4<Elem_t>& theOutMx)
946 {
947   NCollection_Vec3<Elem_t> aForward = theLookAt - theEye;
948   aForward.Normalize();
949
950   // side = forward x up
951   NCollection_Vec3<Elem_t> aSide = NCollection_Vec3<Elem_t>::Cross (aForward, theUpDir);
952   aSide.Normalize();
953
954   // recompute up as: up = side x forward
955   NCollection_Vec3<Elem_t> anUp = NCollection_Vec3<Elem_t>::Cross (aSide, aForward);
956
957   NCollection_Mat4<Elem_t> aLookMx;
958   aLookMx.SetRow (0, aSide);
959   aLookMx.SetRow (1, anUp);
960   aLookMx.SetRow (2, -aForward);
961
962   theOutMx.InitIdentity();
963   theOutMx.Multiply (aLookMx);
964   theOutMx.Translate (-theEye);
965
966   NCollection_Mat4<Elem_t> anAxialScaleMx;
967   anAxialScaleMx.ChangeValue (0, 0) = theAxialScale.x();
968   anAxialScaleMx.ChangeValue (1, 1) = theAxialScale.y();
969   anAxialScaleMx.ChangeValue (2, 2) = theAxialScale.z();
970
971   theOutMx.Multiply (anAxialScaleMx);
972 }
973
974 //=============================================================================
975 //function : ZFitAll
976 //purpose  :
977 //=============================================================================
978 void Graphic3d_Camera::ZFitAll (const Standard_Real theScaleFactor, const Bnd_Box& theMinMax, const Bnd_Box& theGraphicBB)
979 {
980   Standard_ASSERT_RAISE (theScaleFactor > 0.0, "Zero or negative scale factor is not allowed.");
981
982   // Method changes ZNear and ZFar planes of camera so as to fit the graphical structures
983   // by their real boundaries (computed ignoring infinite flag) into the viewing volume.
984   // In addition to the graphical boundaries, the usual min max used for fitting perspective
985   // camera. To avoid numeric errors for perspective camera the negative ZNear values are
986   // fixed using tolerance distance, relative to boundaries size. The tolerance distance
987   // should be computed using information on boundaries of primary application actors,
988   // (e.g. representing the displayed model) - to ensure that they are not unreasonably clipped.
989   const Standard_ShortReal anEpsilon = 1e-4f;
990
991   if (theGraphicBB.IsVoid())
992   {
993     // Precision factor used to add meaningful tolerance to
994     // ZNear, ZFar values in order to avoid equality after type conversion
995     // to ShortReal matrices type.
996
997     Standard_Real aZFar  = Distance() * 3.0;
998     Standard_Real aZNear = 0.0;
999
1000     if (!IsOrthographic())
1001     {
1002       if (aZFar < anEpsilon)
1003       {
1004         aZNear = anEpsilon;
1005         aZFar  = anEpsilon * 2.0;
1006       }
1007       else if (aZNear < aZFar * anEpsilon)
1008       {
1009         aZNear = aZFar * anEpsilon;
1010       }
1011     }
1012
1013     SetZRange (aZNear, aZFar);
1014     return;
1015   }
1016
1017   // Measure depth of boundary points from camera eye
1018   NCollection_Sequence<gp_Pnt> aPntsToMeasure;
1019
1020   Standard_Real aGraphicBB[6]; // real graphical boundaries (not accounting infinite flag).
1021   theGraphicBB.Get (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2], aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]);
1022
1023   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2]));
1024   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[5]));
1025   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[2]));
1026   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[5]));
1027   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[2]));
1028   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[5]));
1029   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[2]));
1030   aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]));
1031
1032   if (!theMinMax.IsVoid() && !theMinMax.IsWhole())
1033   {
1034     Standard_Real aMinMax[6]; // applicative min max boundaries
1035     theMinMax.Get (aMinMax[0], aMinMax[1], aMinMax[2], aMinMax[3], aMinMax[4], aMinMax[5]);
1036
1037     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[2]));
1038     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[5]));
1039     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[2]));
1040     aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[5]));
1041     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[2]));
1042     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[5]));
1043     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[2]));
1044     aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[5]));
1045   }
1046
1047   // Camera eye plane
1048   gp_Dir aCamDir = Direction();
1049   gp_Pnt aCamEye = myEye;
1050   gp_Pln aCamPln (aCamEye, aCamDir);
1051
1052   Standard_Real aModelMinDist   = RealLast();
1053   Standard_Real aModelMaxDist   = RealFirst();
1054   Standard_Real aGraphicMinDist = RealLast();
1055   Standard_Real aGraphicMaxDist = RealFirst();
1056
1057   const gp_XYZ& anAxialScale = myAxialScale;
1058
1059   // Get minimum and maximum distances to the eye plane
1060   Standard_Integer aCounter = 0;
1061   NCollection_Sequence<gp_Pnt>::Iterator aPntIt(aPntsToMeasure);
1062   for (; aPntIt.More(); aPntIt.Next())
1063   {
1064     gp_Pnt aMeasurePnt = aPntIt.Value();
1065
1066     aMeasurePnt = gp_Pnt (aMeasurePnt.X() * anAxialScale.X(),
1067                           aMeasurePnt.Y() * anAxialScale.Y(),
1068                           aMeasurePnt.Z() * anAxialScale.Z());
1069
1070     Standard_Real aDistance = aCamPln.Distance (aMeasurePnt);
1071
1072     // Check if the camera is intruded into the scene
1073     if (aCamDir.IsOpposite (gp_Vec (aCamEye, aMeasurePnt), M_PI * 0.5))
1074     {
1075       aDistance *= -1;
1076     }
1077
1078     // the first eight points are from theGraphicBB, the last eight points are from theMinMax
1079     // (they can be absent).
1080     Standard_Real& aChangeMinDist = aCounter >= 8 ? aModelMinDist : aGraphicMinDist;
1081     Standard_Real& aChangeMaxDist = aCounter >= 8 ? aModelMaxDist : aGraphicMaxDist;
1082     aChangeMinDist = Min (aDistance, aChangeMinDist);
1083     aChangeMaxDist = Max (aDistance, aChangeMaxDist);
1084     aCounter++;
1085   }
1086
1087   // Compute depth of bounding box center
1088   Standard_Real aMidDepth  = (aGraphicMinDist + aGraphicMaxDist) * 0.5;
1089   Standard_Real aHalfDepth = (aGraphicMaxDist - aGraphicMinDist) * 0.5;
1090
1091   // Compute enlarged or shrank near and far z ranges
1092   Standard_Real aZNear  = aMidDepth - aHalfDepth * theScaleFactor;
1093   Standard_Real aZFar   = aMidDepth + aHalfDepth * theScaleFactor;
1094   Standard_Real aZRange = Abs (aZFar - aZNear);
1095   Standard_Real aZConf  = Max (static_cast <Standard_Real> (anEpsilon * aZRange),
1096                                static_cast <Standard_Real> (anEpsilon));
1097
1098   aZNear -= Abs (aZNear) * anEpsilon + aZConf;
1099   aZFar  += Abs  (aZFar) * anEpsilon + aZConf;
1100
1101   if (!IsOrthographic())
1102   {
1103     if (aZFar > anEpsilon)
1104     {
1105       // Choose between model distance and graphical distance, as the model boundaries
1106       // might be infinite if all structures have infinite flag.
1107       const Standard_Real aGraphicDepth = aGraphicMaxDist >= aGraphicMinDist
1108         ? aGraphicMaxDist - aGraphicMinDist : RealLast();
1109
1110       const Standard_Real aModelDepth = aModelMaxDist >= aModelMinDist
1111         ? aModelMaxDist - aModelMinDist : RealLast();
1112
1113       const Standard_Real aMinDepth = Min (aModelDepth, aGraphicDepth);
1114       const Standard_Real aZTol     = Max (static_cast<Standard_Real> (anEpsilon * Abs (aMinDepth)),
1115                                            static_cast<Standard_Real> (anEpsilon));
1116       if (aZNear < aZTol)
1117       {
1118         aZNear = aZTol;
1119       }
1120     }
1121     else
1122     {
1123       aZNear = anEpsilon;
1124       aZFar  = anEpsilon * 2.0;
1125     }
1126   }
1127
1128   if (aZFar < (aZNear + Abs (aZFar) * anEpsilon))
1129   {
1130     aZFar = aZNear + Abs (aZFar) * anEpsilon;
1131   }
1132
1133   SetZRange (aZNear, aZFar);
1134 }