1 // Created on: 2013-05-29
2 // Created by: Anton POLETAEV
3 // Copyright (c) 1999-2014 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
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.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
17 #include <Standard_ShortReal.hxx>
19 #include <Graphic3d_Camera.hxx>
20 #include <Graphic3d_Vec4.hxx>
21 #include <Graphic3d_WorldViewProjState.hxx>
23 #include <Standard_Atomic.hxx>
24 #include <Standard_Assert.hxx>
26 #include <NCollection_Sequence.hxx>
31 // (degrees -> radians) * 0.5
32 static const Standard_Real DTR_HALF = 0.5 * 0.0174532925;
34 // default property values
35 static const Standard_Real DEFAULT_ZNEAR = 0.001;
36 static const Standard_Real DEFAULT_ZFAR = 3000.0;
38 // atomic state counter
39 static volatile Standard_Integer THE_STATE_COUNTER = 0;
41 // minimum camera distance
42 static const Standard_Real MIN_DISTANCE = Pow (0.1, ShortRealDigits() - 2);
44 // z-range tolerance compatible with for floating point.
45 static Standard_Real zEpsilon()
50 // relative z-range tolerance compatible with for floating point.
51 static Standard_Real zEpsilon (const Standard_Real theValue)
57 Standard_Real aLogRadix = Log10 (Abs (theValue)) / Log10 (FLT_RADIX);
58 Standard_Real aExp = Floor (aLogRadix);
59 return FLT_EPSILON * Pow (FLT_RADIX, aExp);
63 // =======================================================================
64 // function : Graphic3d_Camera
66 // =======================================================================
67 Graphic3d_Camera::Graphic3d_Camera()
68 : myUp (0.0, 1.0, 0.0),
69 myEye (0.0, 0.0, -1500.0),
70 myCenter (0.0, 0.0, 0.0),
71 myAxialScale (1.0, 1.0, 1.0),
72 myProjType (Projection_Orthographic),
74 myZNear (DEFAULT_ZNEAR),
75 myZFar (DEFAULT_ZFAR),
79 myZFocusType (FocusType_Relative),
81 myIODType (IODType_Relative)
83 myWorldViewProjState.Initialize ((Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
84 (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
88 // =======================================================================
89 // function : Graphic3d_Camera
91 // =======================================================================
92 Graphic3d_Camera::Graphic3d_Camera (const Handle(Graphic3d_Camera)& theOther)
94 myWorldViewProjState.Initialize (this);
99 // =======================================================================
100 // function : CopyMappingData
102 // =======================================================================
103 void Graphic3d_Camera::CopyMappingData (const Handle(Graphic3d_Camera)& theOtherCamera)
105 myFOVy = theOtherCamera->myFOVy;
106 myZNear = theOtherCamera->myZNear;
107 myZFar = theOtherCamera->myZFar;
108 myAspect = theOtherCamera->myAspect;
109 myScale = theOtherCamera->myScale;
110 myZFocus = theOtherCamera->myZFocus;
111 myZFocusType = theOtherCamera->myZFocusType;
112 myIOD = theOtherCamera->myIOD;
113 myIODType = theOtherCamera->myIODType;
114 myProjType = theOtherCamera->myProjType;
116 myWorldViewProjState.ProjectionState() = theOtherCamera->ProjectionState();
118 InvalidateProjection();
121 // =======================================================================
122 // function : CopyOrientationData
124 // =======================================================================
125 void Graphic3d_Camera::CopyOrientationData (const Handle(Graphic3d_Camera)& theOtherCamera)
127 myUp = theOtherCamera->myUp;
128 myEye = theOtherCamera->myEye;
129 myCenter = theOtherCamera->myCenter;
130 myAxialScale = theOtherCamera->myAxialScale;
132 myWorldViewProjState.WorldViewState() = theOtherCamera->WorldViewState();
134 InvalidateOrientation();
137 // =======================================================================
140 // =======================================================================
141 void Graphic3d_Camera::Copy (const Handle(Graphic3d_Camera)& theOther)
143 CopyMappingData (theOther);
144 CopyOrientationData (theOther);
147 // =======================================================================
150 // =======================================================================
151 void Graphic3d_Camera::SetEye (const gp_Pnt& theEye)
154 InvalidateOrientation();
157 // =======================================================================
158 // function : SetCenter
160 // =======================================================================
161 void Graphic3d_Camera::SetCenter (const gp_Pnt& theCenter)
163 myCenter = theCenter;
164 InvalidateOrientation();
167 // =======================================================================
170 // =======================================================================
171 void Graphic3d_Camera::SetUp (const gp_Dir& theUp)
174 InvalidateOrientation();
177 // =======================================================================
178 // function : SetAxialScale
180 // =======================================================================
181 void Graphic3d_Camera::SetAxialScale (const gp_XYZ& theAxialScale)
183 myAxialScale = theAxialScale;
184 InvalidateOrientation();
187 // =======================================================================
188 // function : SetDistance
190 // =======================================================================
191 void Graphic3d_Camera::SetDistance (const Standard_Real theDistance)
193 gp_Vec aCenter2Eye (Direction());
194 aCenter2Eye.Reverse();
196 // Camera should have non-zero distance.
197 aCenter2Eye.Scale (Max (theDistance, MIN_DISTANCE));
198 SetEye (Center().Translated (aCenter2Eye));
201 // =======================================================================
202 // function : Distance
204 // =======================================================================
205 Standard_Real Graphic3d_Camera::Distance() const
207 return myEye.Distance (myCenter);
210 // =======================================================================
211 // function : SetDirection
213 // =======================================================================
214 void Graphic3d_Camera::SetDirection (const gp_Dir& theDir)
216 gp_Vec aScaledDir (theDir);
217 aScaledDir.Scale (Distance());
218 aScaledDir.Reverse();
219 SetEye (Center().Translated (aScaledDir));
222 // =======================================================================
223 // function : Direction
225 // =======================================================================
226 gp_Dir Graphic3d_Camera::Direction() const
228 return gp_Dir (gp_Vec (myEye, myCenter));
231 // =======================================================================
232 // function : SetScale
234 // =======================================================================
235 void Graphic3d_Camera::SetScale (const Standard_Real theScale)
241 case Projection_Perspective :
242 case Projection_Stereo :
243 case Projection_MonoLeftEye :
244 case Projection_MonoRightEye :
246 Standard_Real aDistance = theScale * 0.5 / Tan(myFOVy * M_PI / 360.0);
247 SetDistance (aDistance);
254 InvalidateProjection();
257 // =======================================================================
260 // =======================================================================
261 Standard_Real Graphic3d_Camera::Scale() const
265 case Projection_Orthographic :
268 // case Projection_Perspective :
269 // case Projection_Stereo :
270 // case Projection_MonoLeftEye :
271 // case Projection_MonoRightEye :
273 return Distance() * 2.0 * Tan (myFOVy * M_PI / 360.0);
277 // =======================================================================
278 // function : SetProjectionType
280 // =======================================================================
281 void Graphic3d_Camera::SetProjectionType (const Projection theProjectionType)
283 Projection anOldType = myProjType;
285 if (anOldType == theProjectionType)
290 if (anOldType == Projection_Orthographic)
292 if (myZNear <= RealEpsilon())
294 myZNear = DEFAULT_ZNEAR;
296 if (myZFar <= RealEpsilon())
298 myZFar = DEFAULT_ZFAR;
302 myProjType = theProjectionType;
304 InvalidateProjection();
307 // =======================================================================
308 // function : SetFOVy
310 // =======================================================================
311 void Graphic3d_Camera::SetFOVy (const Standard_Real theFOVy)
314 InvalidateProjection();
317 // =======================================================================
318 // function : SetZRange
320 // =======================================================================
321 void Graphic3d_Camera::SetZRange (const Standard_Real theZNear,
322 const Standard_Real theZFar)
324 Standard_ASSERT_RAISE (theZFar > theZNear, "ZFar should be greater than ZNear");
325 if (!IsOrthographic())
327 Standard_ASSERT_RAISE (theZNear > 0.0, "Only positive Z-Near is allowed for perspective camera");
328 Standard_ASSERT_RAISE (theZFar > 0.0, "Only positive Z-Far is allowed for perspective camera");
334 InvalidateProjection();
337 // =======================================================================
338 // function : SetAspect
340 // =======================================================================
341 void Graphic3d_Camera::SetAspect (const Standard_Real theAspect)
343 myAspect = theAspect;
344 InvalidateProjection();
347 // =======================================================================
348 // function : SetZFocus
350 // =======================================================================
351 void Graphic3d_Camera::SetZFocus(const FocusType theType, const Standard_Real theZFocus)
353 myZFocusType = theType;
354 myZFocus = theZFocus;
355 InvalidateProjection();
358 // =======================================================================
361 // =======================================================================
362 void Graphic3d_Camera::SetIOD (const IODType theType, const Standard_Real theIOD)
366 InvalidateProjection();
369 // =======================================================================
370 // function : OrthogonalizeUp
372 // =======================================================================
373 void Graphic3d_Camera::OrthogonalizeUp()
375 SetUp (OrthogonalizedUp());
378 // =======================================================================
379 // function : OrthogonalizedUp
381 // =======================================================================
382 gp_Dir Graphic3d_Camera::OrthogonalizedUp() const
384 gp_Dir aDir = Direction();
385 gp_Dir aLeft = aDir.Crossed (Up());
387 // recompute up as: up = left x direction
388 return aLeft.Crossed (aDir);
391 // =======================================================================
392 // function : Transform
394 // =======================================================================
395 void Graphic3d_Camera::Transform (const gp_Trsf& theTrsf)
397 myUp.Transform (theTrsf);
398 myEye.Transform (theTrsf);
399 myCenter.Transform (theTrsf);
400 InvalidateOrientation();
403 // =======================================================================
404 // function : safePointCast
406 // =======================================================================
407 static Graphic3d_Vec4d safePointCast (const gp_Pnt& thePnt)
409 Standard_Real aLim = 1e15f;
411 // have to deal with values greater then max float
412 gp_Pnt aSafePoint = thePnt;
413 const Standard_Real aBigFloat = aLim * 0.1f;
414 if (Abs (aSafePoint.X()) > aLim)
415 aSafePoint.SetX (aSafePoint.X() >= 0 ? aBigFloat : -aBigFloat);
416 if (Abs (aSafePoint.Y()) > aLim)
417 aSafePoint.SetY (aSafePoint.Y() >= 0 ? aBigFloat : -aBigFloat);
418 if (Abs (aSafePoint.Z()) > aLim)
419 aSafePoint.SetZ (aSafePoint.Z() >= 0 ? aBigFloat : -aBigFloat);
422 Graphic3d_Vec4d aPnt (aSafePoint.X(), aSafePoint.Y(), aSafePoint.Z(), 1.0);
427 // =======================================================================
428 // function : Project
430 // =======================================================================
431 gp_Pnt Graphic3d_Camera::Project (const gp_Pnt& thePnt) const
433 const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
434 const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
436 // use compatible type of point
437 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
439 aPnt = aViewMx * aPnt; // convert to view coordinate space
440 aPnt = aProjMx * aPnt; // convert to projection coordinate space
442 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
444 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
447 // =======================================================================
448 // function : UnProject
450 // =======================================================================
451 gp_Pnt Graphic3d_Camera::UnProject (const gp_Pnt& thePnt) const
453 const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
454 const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
456 Graphic3d_Mat4d aInvView;
457 Graphic3d_Mat4d aInvProj;
459 // this case should never happen
460 if (!aViewMx.Inverted (aInvView) || !aProjMx.Inverted (aInvProj))
462 return gp_Pnt (0.0, 0.0, 0.0);
465 // use compatible type of point
466 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
468 aPnt = aInvProj * aPnt; // convert to view coordinate space
469 aPnt = aInvView * aPnt; // convert to world coordinate space
471 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
473 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
476 // =======================================================================
477 // function : ConvertView2Proj
479 // =======================================================================
480 gp_Pnt Graphic3d_Camera::ConvertView2Proj (const gp_Pnt& thePnt) const
482 const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
484 // use compatible type of point
485 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
487 aPnt = aProjMx * aPnt; // convert to projection coordinate space
489 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
491 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
494 // =======================================================================
495 // function : ConvertProj2View
497 // =======================================================================
498 gp_Pnt Graphic3d_Camera::ConvertProj2View (const gp_Pnt& thePnt) const
500 const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
502 Graphic3d_Mat4d aInvProj;
504 // this case should never happen, but...
505 if (!aProjMx.Inverted (aInvProj))
507 return gp_Pnt (0, 0, 0);
510 // use compatible type of point
511 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
513 aPnt = aInvProj * aPnt; // convert to view coordinate space
515 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
517 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
520 // =======================================================================
521 // function : ConvertWorld2View
523 // =======================================================================
524 gp_Pnt Graphic3d_Camera::ConvertWorld2View (const gp_Pnt& thePnt) const
526 const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
528 // use compatible type of point
529 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
531 aPnt = aViewMx * aPnt; // convert to view coordinate space
533 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
535 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
538 // =======================================================================
539 // function : ConvertView2World
541 // =======================================================================
542 gp_Pnt Graphic3d_Camera::ConvertView2World (const gp_Pnt& thePnt) const
544 const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
546 Graphic3d_Mat4d aInvView;
548 if (!aViewMx.Inverted (aInvView))
550 return gp_Pnt(0, 0, 0);
553 // use compatible type of point
554 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
556 aPnt = aInvView * aPnt; // convert to world coordinate space
558 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
560 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
563 // =======================================================================
564 // function : ViewDimensions
566 // =======================================================================
567 gp_XYZ Graphic3d_Camera::ViewDimensions() const
569 // view plane dimensions
570 Standard_Real aSize = IsOrthographic() ? myScale : (2.0 * Distance() * Tan (DTR_HALF * myFOVy));
571 Standard_Real aSizeX, aSizeY;
574 aSizeX = aSize * myAspect;
580 aSizeY = aSize / myAspect;
584 return gp_XYZ (aSizeX, aSizeY, myZFar - myZNear);
587 // =======================================================================
588 // function : Frustum
590 // =======================================================================
591 void Graphic3d_Camera::Frustum (gp_Pln& theLeft,
596 gp_Pln& theFar) const
598 gp_Vec aProjection = gp_Vec (Direction());
599 gp_Vec anUp = OrthogonalizedUp();
600 gp_Vec aSide = aProjection ^ anUp;
602 Standard_ASSERT_RAISE (
603 !aProjection.IsParallel (anUp, Precision::Angular()),
604 "Can not derive SIDE = PROJ x UP - directions are parallel");
606 theNear = gp_Pln (Eye().Translated (aProjection * ZNear()), aProjection);
607 theFar = gp_Pln (Eye().Translated (aProjection * ZFar()), -aProjection);
609 Standard_Real aHScaleHor = Scale() * 0.5 * Aspect();
610 Standard_Real aHScaleVer = Scale() * 0.5;
612 gp_Pnt aPntLeft = Center().Translated (aHScaleHor * -aSide);
613 gp_Pnt aPntRight = Center().Translated (aHScaleHor * aSide);
614 gp_Pnt aPntBottom = Center().Translated (aHScaleVer * -anUp);
615 gp_Pnt aPntTop = Center().Translated (aHScaleVer * anUp);
617 gp_Vec aDirLeft = aSide;
618 gp_Vec aDirRight = -aSide;
619 gp_Vec aDirBottom = anUp;
620 gp_Vec aDirTop = -anUp;
621 if (!IsOrthographic())
623 Standard_Real aHFOVHor = ATan (Tan (DTR_HALF * FOVy()) * Aspect());
624 Standard_Real aHFOVVer = DTR_HALF * FOVy();
625 aDirLeft.Rotate (gp_Ax1 (gp::Origin(), anUp), aHFOVHor);
626 aDirRight.Rotate (gp_Ax1 (gp::Origin(), anUp), -aHFOVHor);
627 aDirBottom.Rotate (gp_Ax1 (gp::Origin(), aSide), -aHFOVVer);
628 aDirTop.Rotate (gp_Ax1 (gp::Origin(), aSide), aHFOVVer);
631 theLeft = gp_Pln (aPntLeft, aDirLeft);
632 theRight = gp_Pln (aPntRight, aDirRight);
633 theBottom = gp_Pln (aPntBottom, aDirBottom);
634 theTop = gp_Pln (aPntTop, aDirTop);
637 // =======================================================================
638 // function : OrientationMatrix
640 // =======================================================================
641 const Graphic3d_Mat4d& Graphic3d_Camera::OrientationMatrix() const
643 return *UpdateOrientation (myMatricesD).Orientation;
646 // =======================================================================
647 // function : OrientationMatrixF
649 // =======================================================================
650 const Graphic3d_Mat4& Graphic3d_Camera::OrientationMatrixF() const
652 return *UpdateOrientation (myMatricesF).Orientation;
655 // =======================================================================
656 // function : ProjectionMatrix
658 // =======================================================================
659 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionMatrix() const
661 return *UpdateProjection (myMatricesD).MProjection;
664 // =======================================================================
665 // function : ProjectionMatrixF
667 // =======================================================================
668 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionMatrixF() const
670 return *UpdateProjection (myMatricesF).MProjection;
673 // =======================================================================
674 // function : ProjectionStereoLeft
676 // =======================================================================
677 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoLeft() const
679 return *UpdateProjection (myMatricesD).LProjection;
682 // =======================================================================
683 // function : ProjectionStereoLeftF
685 // =======================================================================
686 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoLeftF() const
688 return *UpdateProjection (myMatricesF).LProjection;
691 // =======================================================================
692 // function : ProjectionStereoRight
694 // =======================================================================
695 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoRight() const
697 return *UpdateProjection (myMatricesD).RProjection;
700 // =======================================================================
701 // function : ProjectionStereoRightF
703 // =======================================================================
704 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoRightF() const
706 return *UpdateProjection (myMatricesF).RProjection;
709 // =======================================================================
710 // function : UpdateProjection
712 // =======================================================================
713 template <typename Elem_t>
714 Graphic3d_Camera::TransformMatrices<Elem_t>&
715 Graphic3d_Camera::UpdateProjection (TransformMatrices<Elem_t>& theMatrices) const
717 if (theMatrices.IsProjectionValid())
719 return theMatrices; // for inline accessors
722 theMatrices.InitProjection();
724 // sets top of frustum based on FOVy and near clipping plane
725 Elem_t aScale = static_cast<Elem_t> (myScale);
726 Elem_t aZNear = static_cast<Elem_t> (myZNear);
727 Elem_t aZFar = static_cast<Elem_t> (myZFar);
728 Elem_t anAspect = static_cast<Elem_t> (myAspect);
729 Elem_t aDXHalf = 0.0, aDYHalf = 0.0;
730 if (IsOrthographic())
732 aDXHalf = aScale * Elem_t (0.5);
733 aDYHalf = aScale * Elem_t (0.5);
737 aDXHalf = aZNear * Elem_t (Tan (DTR_HALF * myFOVy));
738 aDYHalf = aZNear * Elem_t (Tan (DTR_HALF * myFOVy));
750 // sets right of frustum based on aspect ratio
751 Elem_t aLeft = -aDXHalf;
752 Elem_t aRight = aDXHalf;
753 Elem_t aBot = -aDYHalf;
754 Elem_t aTop = aDYHalf;
756 Elem_t aIOD = myIODType == IODType_Relative
757 ? static_cast<Elem_t> (myIOD * Distance())
758 : static_cast<Elem_t> (myIOD);
760 Elem_t aFocus = myZFocusType == FocusType_Relative
761 ? static_cast<Elem_t> (myZFocus * Distance())
762 : static_cast<Elem_t> (myZFocus);
766 case Projection_Orthographic :
767 OrthoProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection);
770 case Projection_Perspective :
771 PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection);
774 case Projection_MonoLeftEye :
776 StereoEyeProj (aLeft, aRight, aBot, aTop,
777 aZNear, aZFar, aIOD, aFocus,
778 Standard_True, *theMatrices.MProjection);
779 *theMatrices.LProjection = *theMatrices.MProjection;
783 case Projection_MonoRightEye :
785 StereoEyeProj (aLeft, aRight, aBot, aTop,
786 aZNear, aZFar, aIOD, aFocus,
787 Standard_False, *theMatrices.MProjection);
788 *theMatrices.RProjection = *theMatrices.MProjection;
792 case Projection_Stereo :
794 PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection);
796 StereoEyeProj (aLeft, aRight, aBot, aTop,
797 aZNear, aZFar, aIOD, aFocus,
799 *theMatrices.LProjection);
801 StereoEyeProj (aLeft, aRight, aBot, aTop,
802 aZNear, aZFar, aIOD, aFocus,
804 *theMatrices.RProjection);
809 return theMatrices; // for inline accessors
812 // =======================================================================
813 // function : UpdateOrientation
815 // =======================================================================
816 template <typename Elem_t>
817 Graphic3d_Camera::TransformMatrices<Elem_t>&
818 Graphic3d_Camera::UpdateOrientation (TransformMatrices<Elem_t>& theMatrices) const
820 if (theMatrices.IsOrientationValid())
822 return theMatrices; // for inline accessors
825 theMatrices.InitOrientation();
827 NCollection_Vec3<Elem_t> anEye (static_cast<Elem_t> (myEye.X()),
828 static_cast<Elem_t> (myEye.Y()),
829 static_cast<Elem_t> (myEye.Z()));
831 NCollection_Vec3<Elem_t> aCenter (static_cast<Elem_t> (myCenter.X()),
832 static_cast<Elem_t> (myCenter.Y()),
833 static_cast<Elem_t> (myCenter.Z()));
835 NCollection_Vec3<Elem_t> anUp (static_cast<Elem_t> (myUp.X()),
836 static_cast<Elem_t> (myUp.Y()),
837 static_cast<Elem_t> (myUp.Z()));
839 NCollection_Vec3<Elem_t> anAxialScale (static_cast<Elem_t> (myAxialScale.X()),
840 static_cast<Elem_t> (myAxialScale.Y()),
841 static_cast<Elem_t> (myAxialScale.Z()));
843 LookOrientation (anEye, aCenter, anUp, anAxialScale, *theMatrices.Orientation);
845 return theMatrices; // for inline accessors
848 // =======================================================================
849 // function : InvalidateProjection
851 // =======================================================================
852 void Graphic3d_Camera::InvalidateProjection()
854 myMatricesD.ResetProjection();
855 myMatricesF.ResetProjection();
856 myWorldViewProjState.ProjectionState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
859 // =======================================================================
860 // function : InvalidateOrientation
862 // =======================================================================
863 void Graphic3d_Camera::InvalidateOrientation()
865 myMatricesD.ResetOrientation();
866 myMatricesF.ResetOrientation();
867 myWorldViewProjState.WorldViewState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
870 // =======================================================================
871 // function : OrthoProj
873 // =======================================================================
874 template <typename Elem_t>
875 void Graphic3d_Camera::OrthoProj (const Elem_t theLeft,
876 const Elem_t theRight,
877 const Elem_t theBottom,
879 const Elem_t theNear,
881 NCollection_Mat4<Elem_t>& theOutMx)
884 theOutMx.ChangeValue (0, 0) = Elem_t (2.0) / (theRight - theLeft);
885 theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
886 theOutMx.ChangeValue (0, 2) = Elem_t (0.0);
887 theOutMx.ChangeValue (0, 3) = - (theRight + theLeft) / (theRight - theLeft);
890 theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
891 theOutMx.ChangeValue (1, 1) = Elem_t (2.0) / (theTop - theBottom);
892 theOutMx.ChangeValue (1, 2) = Elem_t (0.0);
893 theOutMx.ChangeValue (1, 3) = - (theTop + theBottom) / (theTop - theBottom);
896 theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
897 theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
898 theOutMx.ChangeValue (2, 2) = Elem_t (-2.0) / (theFar - theNear);
899 theOutMx.ChangeValue (2, 3) = - (theFar + theNear) / (theFar - theNear);
902 theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
903 theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
904 theOutMx.ChangeValue (3, 2) = Elem_t (0.0);
905 theOutMx.ChangeValue (3, 3) = Elem_t (1.0);
908 // =======================================================================
909 // function : PerspectiveProj
911 // =======================================================================
912 template <typename Elem_t>
913 void Graphic3d_Camera::PerspectiveProj (const Elem_t theLeft,
914 const Elem_t theRight,
915 const Elem_t theBottom,
917 const Elem_t theNear,
919 NCollection_Mat4<Elem_t>& theOutMx)
922 theOutMx.ChangeValue (0, 0) = (Elem_t (2.0) * theNear) / (theRight - theLeft);
923 theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
924 theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
925 theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
928 theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
929 theOutMx.ChangeValue (1, 1) = (Elem_t (2.0) * theNear) / (theTop - theBottom);
930 theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
931 theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
934 theOutMx.ChangeValue (0, 2) = (theRight + theLeft) / (theRight - theLeft);
935 theOutMx.ChangeValue (1, 2) = (theTop + theBottom) / (theTop - theBottom);
936 theOutMx.ChangeValue (2, 2) = -(theFar + theNear) / (theFar - theNear);
937 theOutMx.ChangeValue (3, 2) = Elem_t (-1.0);
940 theOutMx.ChangeValue (0, 3) = Elem_t (0.0);
941 theOutMx.ChangeValue (1, 3) = Elem_t (0.0);
942 theOutMx.ChangeValue (2, 3) = -(Elem_t (2.0) * theFar * theNear) / (theFar - theNear);
943 theOutMx.ChangeValue (3, 3) = Elem_t (0.0);
946 // =======================================================================
947 // function : StereoEyeProj
949 // =======================================================================
950 template <typename Elem_t>
951 void Graphic3d_Camera::StereoEyeProj (const Elem_t theLeft,
952 const Elem_t theRight,
953 const Elem_t theBottom,
955 const Elem_t theNear,
958 const Elem_t theZFocus,
959 const Standard_Boolean theIsLeft,
960 NCollection_Mat4<Elem_t>& theOutMx)
962 Elem_t aDx = theIsLeft ? Elem_t (0.5) * theIOD : Elem_t (-0.5) * theIOD;
963 Elem_t aDXStereoShift = aDx * theNear / theZFocus;
965 // construct eye projection matrix
966 PerspectiveProj (theLeft + aDXStereoShift,
967 theRight + aDXStereoShift,
968 theBottom, theTop, theNear, theFar,
971 if (theIOD != Elem_t (0.0))
973 // X translation to cancel parallax
974 theOutMx.Translate (NCollection_Vec3<Elem_t> (aDx, Elem_t (0.0), Elem_t (0.0)));
978 // =======================================================================
979 // function : LookOrientation
981 // =======================================================================
982 template <typename Elem_t>
983 void Graphic3d_Camera::LookOrientation (const NCollection_Vec3<Elem_t>& theEye,
984 const NCollection_Vec3<Elem_t>& theLookAt,
985 const NCollection_Vec3<Elem_t>& theUpDir,
986 const NCollection_Vec3<Elem_t>& theAxialScale,
987 NCollection_Mat4<Elem_t>& theOutMx)
989 NCollection_Vec3<Elem_t> aForward = theLookAt - theEye;
990 aForward.Normalize();
992 // side = forward x up
993 NCollection_Vec3<Elem_t> aSide = NCollection_Vec3<Elem_t>::Cross (aForward, theUpDir);
996 // recompute up as: up = side x forward
997 NCollection_Vec3<Elem_t> anUp = NCollection_Vec3<Elem_t>::Cross (aSide, aForward);
999 NCollection_Mat4<Elem_t> aLookMx;
1000 aLookMx.SetRow (0, aSide);
1001 aLookMx.SetRow (1, anUp);
1002 aLookMx.SetRow (2, -aForward);
1004 theOutMx.InitIdentity();
1005 theOutMx.Multiply (aLookMx);
1006 theOutMx.Translate (-theEye);
1008 NCollection_Mat4<Elem_t> anAxialScaleMx;
1009 anAxialScaleMx.ChangeValue (0, 0) = theAxialScale.x();
1010 anAxialScaleMx.ChangeValue (1, 1) = theAxialScale.y();
1011 anAxialScaleMx.ChangeValue (2, 2) = theAxialScale.z();
1013 theOutMx.Multiply (anAxialScaleMx);
1016 //=============================================================================
1017 //function : ZFitAll
1019 //=============================================================================
1020 void Graphic3d_Camera::ZFitAll (const Standard_Real theScaleFactor, const Bnd_Box& theMinMax, const Bnd_Box& theGraphicBB)
1022 Standard_ASSERT_RAISE (theScaleFactor > 0.0, "Zero or negative scale factor is not allowed.");
1024 // Method changes zNear and zFar parameters of camera so as to fit graphical structures
1025 // by their graphical boundaries. It precisely fits min max boundaries of primary application
1026 // objects (second argument), while it can sacrifice the real graphical boundaries of the
1027 // scene with infinite or helper objects (third argument) for the sake of perspective projection.
1028 if (theGraphicBB.IsVoid())
1030 SetZRange (DEFAULT_ZNEAR, DEFAULT_ZFAR);
1034 // Measure depth of boundary points from camera eye.
1035 NCollection_Sequence<gp_Pnt> aPntsToMeasure;
1037 Standard_Real aGraphicBB[6];
1038 theGraphicBB.Get (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2], aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]);
1040 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2]));
1041 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[5]));
1042 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[2]));
1043 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[5]));
1044 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[2]));
1045 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[5]));
1046 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[2]));
1047 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]));
1049 Standard_Boolean isFiniteMinMax = !theMinMax.IsVoid() && !theMinMax.IsWhole();
1053 Standard_Real aMinMax[6];
1054 theMinMax.Get (aMinMax[0], aMinMax[1], aMinMax[2], aMinMax[3], aMinMax[4], aMinMax[5]);
1056 aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[2]));
1057 aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[5]));
1058 aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[2]));
1059 aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[5]));
1060 aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[2]));
1061 aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[5]));
1062 aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[2]));
1063 aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[5]));
1066 // Camera eye plane.
1067 gp_Dir aCamDir = Direction();
1068 gp_Pnt aCamEye = myEye;
1069 gp_Pln aCamPln (aCamEye, aCamDir);
1071 Standard_Real aModelMinDist = RealLast();
1072 Standard_Real aModelMaxDist = RealFirst();
1073 Standard_Real aGraphMinDist = RealLast();
1074 Standard_Real aGraphMaxDist = RealFirst();
1076 const gp_XYZ& anAxialScale = myAxialScale;
1078 // Get minimum and maximum distances to the eye plane.
1079 Standard_Integer aCounter = 0;
1080 NCollection_Sequence<gp_Pnt>::Iterator aPntIt(aPntsToMeasure);
1081 for (; aPntIt.More(); aPntIt.Next())
1083 gp_Pnt aMeasurePnt = aPntIt.Value();
1085 aMeasurePnt = gp_Pnt (aMeasurePnt.X() * anAxialScale.X(),
1086 aMeasurePnt.Y() * anAxialScale.Y(),
1087 aMeasurePnt.Z() * anAxialScale.Z());
1089 Standard_Real aDistance = aCamPln.Distance (aMeasurePnt);
1091 // Check if the camera is intruded into the scene.
1092 if (aCamDir.IsOpposite (gp_Vec (aCamEye, aMeasurePnt), M_PI * 0.5))
1097 // The first eight points are from theGraphicBB, the last eight points are from theMinMax (can be absent).
1098 Standard_Real& aChangeMinDist = aCounter >= 8 ? aModelMinDist : aGraphMinDist;
1099 Standard_Real& aChangeMaxDist = aCounter >= 8 ? aModelMaxDist : aGraphMaxDist;
1100 aChangeMinDist = Min (aDistance, aChangeMinDist);
1101 aChangeMaxDist = Max (aDistance, aChangeMaxDist);
1105 // Compute depth of bounding box center.
1106 Standard_Real aMidDepth = (aGraphMinDist + aGraphMaxDist) * 0.5;
1107 Standard_Real aHalfDepth = (aGraphMaxDist - aGraphMinDist) * 0.5;
1109 // Compute enlarged or shrank near and far z ranges.
1110 Standard_Real aZNear = aMidDepth - aHalfDepth * theScaleFactor;
1111 Standard_Real aZFar = aMidDepth + aHalfDepth * theScaleFactor;
1113 if (!IsOrthographic())
1115 // Everything is behind the perspective camera.
1116 if (aZFar < zEpsilon())
1118 SetZRange (DEFAULT_ZNEAR, DEFAULT_ZFAR);
1124 // Consider clipping errors due to double to single precision floating-point conversion.
1127 // Model to view transformation performs translation of points against eye position
1128 // in three dimensions. Both point coordinate and eye position values are converted from
1129 // double to single precision floating point numbers producing conversion errors.
1130 // Epsilon (Mod) * 3.0 should safely compensate precision error for z coordinate after
1131 // translation assuming that the:
1132 // Epsilon (Eye.Mod()) * 3.0 > Epsilon (Eye.X()) + Epsilon (Eye.Y()) + Epsilon (Eye.Z()).
1133 Standard_Real aEyeConf = 3.0 * zEpsilon (myEye.XYZ().Modulus());
1135 // Model to view transformation performs rotation of points according to view direction.
1136 // New z coordinate is computed as a multiplication of point's x, y, z coordinates by the
1137 // "forward" direction vector's x, y, z coordinates. Both point's and "z" direction vector's
1138 // values are converted from double to single precision floating point numbers producing
1139 // conversion errors.
1140 // Epsilon (Mod) * 6.0 should safely compensate the precision errors for the multiplication
1141 // of point coordinates by direction vector.
1142 gp_Pnt aGraphicMin = theGraphicBB.CornerMin();
1143 gp_Pnt aGraphicMax = theGraphicBB.CornerMax();
1145 Standard_Real aModelConf = 6.0 * zEpsilon (aGraphicMin.XYZ().Modulus()) +
1146 6.0 * zEpsilon (aGraphicMax.XYZ().Modulus());
1148 // Compensate floating point conversion errors by increasing zNear, zFar to avoid clipping.
1149 aZNear -= zEpsilon (aZNear) + aEyeConf + aModelConf;
1150 aZFar += zEpsilon (aZFar) + aEyeConf + aModelConf;
1152 if (!IsOrthographic())
1154 // For perspective projection, the value of z in normalized device coordinates is non-linear
1155 // function of eye z coordinate. For fixed-point depth representation resolution of z in
1156 // model-view space will grow towards zFar plane and its scale depends mostly on how far is zNear
1157 // against camera's eye. The purpose of the code below is to select most appropriate zNear distance
1158 // to balance between clipping (less zNear, more chances to observe closely small models without clipping)
1159 // and resolution of depth. A well applicable criteria to this is a ratio between resolution of z at center
1160 // of model boundaries and the distance to that center point. The ratio is chosen empirically and validated
1161 // by tests database. It is considered to be ~0.001 (0.1%) for 24 bit depth buffer, for less depth bitness
1162 // the zNear will be placed similarly giving lower resolution.
1163 // Approximation of the formula for respectively large z range is:
1164 // zNear = [z * (1 + k) / (k * c)],
1166 // z - distance to center of model boundaries;
1167 // k - chosen ratio, c - capacity of depth buffer;
1168 // k = 0.001, k * c = 1677.216, (1 + k) / (k * c) ~ 5.97E-4
1170 // The function uses center of model boundaries computed from "theMinMax" boundaries (instead of using real
1171 // graphical boundaries of all displayed objects). That means that it can sacrifice resolution of presentation
1172 // of non primary ("infinite") application graphical objects in favor of better perspective projection of the
1173 // small applicative objects measured with "theMinMax" values.
1174 Standard_Real aZRange = isFiniteMinMax ? aModelMaxDist - aModelMinDist : aGraphMaxDist - aGraphMinDist;
1175 Standard_Real aZMin = isFiniteMinMax ? aModelMinDist : aGraphMinDist;
1176 Standard_Real aZ = aZMin < 0 ? aZRange / 2.0 : aZRange / 2.0 + aZMin;
1177 Standard_Real aZNearMin = aZ * 5.97E-4;
1178 if (aZNear < aZNearMin)
1180 // Clip zNear according to the minimum value matching the quality.
1185 // Compensate zNear conversion errors for perspective projection.
1186 aZNear -= aZFar * zEpsilon (aZNear) / (aZFar - zEpsilon (aZNear));
1189 // Compensate zFar conversion errors for perspective projection.
1190 aZFar += zEpsilon (aZFar);
1192 // Ensure that after all the zNear is not a negative value.
1193 if (aZNear < zEpsilon())
1195 aZNear = zEpsilon();
1199 SetZRange (aZNear, aZFar);