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.
16 #include <Graphic3d_Camera.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>
28 IMPLEMENT_STANDARD_RTTIEXT(Graphic3d_Camera,Standard_Transient)
32 // (degrees -> radians) * 0.5
33 static const Standard_Real DTR_HALF = 0.5 * 0.0174532925;
35 // default property values
36 static const Standard_Real DEFAULT_ZNEAR = 0.001;
37 static const Standard_Real DEFAULT_ZFAR = 3000.0;
39 // atomic state counter
40 static volatile Standard_Integer THE_STATE_COUNTER = 0;
42 // minimum camera distance
43 static const Standard_Real MIN_DISTANCE = Pow (0.1, ShortRealDigits() - 2);
45 // z-range tolerance compatible with for floating point.
46 static Standard_Real zEpsilon()
51 // relative z-range tolerance compatible with for floating point.
52 static Standard_Real zEpsilon (const Standard_Real theValue)
54 Standard_Real anAbsValue = Abs (theValue);
55 if (anAbsValue <= (double)FLT_MIN)
59 Standard_Real aLogRadix = Log10 (anAbsValue) / Log10 (FLT_RADIX);
60 Standard_Real aExp = Floor (aLogRadix);
61 return FLT_EPSILON * Pow (FLT_RADIX, aExp);
64 //! Convert camera definition to Ax3
65 gp_Ax3 cameraToAx3 (const Graphic3d_Camera& theCamera)
67 const gp_Dir aBackDir(gp_Vec(theCamera.Center(), theCamera.Eye()));
68 const gp_Dir anXAxis (theCamera.Up().Crossed (aBackDir));
69 const gp_Dir anYAxis (aBackDir .Crossed (anXAxis));
70 const gp_Dir aZAxis (anXAxis .Crossed (anYAxis));
71 return gp_Ax3 (gp_Pnt (0.0, 0.0, 0.0), aZAxis, anXAxis);
75 // =======================================================================
76 // function : Graphic3d_Camera
78 // =======================================================================
79 Graphic3d_Camera::Graphic3d_Camera()
80 : myUp (0.0, 1.0, 0.0),
81 myEye (0.0, 0.0, -1500.0),
82 myCenter (0.0, 0.0, 0.0),
83 myAxialScale (1.0, 1.0, 1.0),
84 myProjType (Projection_Orthographic),
86 myFOVyTan (Tan (DTR_HALF * 45.0)),
87 myZNear (DEFAULT_ZNEAR),
88 myZFar (DEFAULT_ZFAR),
92 myZFocusType (FocusType_Relative),
94 myIODType (IODType_Relative)
96 myWorldViewProjState.Initialize ((Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
97 (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER),
101 // =======================================================================
102 // function : Graphic3d_Camera
104 // =======================================================================
105 Graphic3d_Camera::Graphic3d_Camera (const Handle(Graphic3d_Camera)& theOther)
107 myWorldViewProjState.Initialize (this);
112 // =======================================================================
113 // function : CopyMappingData
115 // =======================================================================
116 void Graphic3d_Camera::CopyMappingData (const Handle(Graphic3d_Camera)& theOtherCamera)
118 SetFOVy (theOtherCamera->FOVy());
119 SetZRange (theOtherCamera->ZNear(), theOtherCamera->ZFar());
120 SetAspect (theOtherCamera->Aspect());
121 SetScale (theOtherCamera->Scale());
122 SetZFocus (theOtherCamera->ZFocusType(), theOtherCamera->ZFocus());
123 SetIOD (theOtherCamera->GetIODType(), theOtherCamera->IOD());
124 SetProjectionType (theOtherCamera->ProjectionType());
125 SetTile (theOtherCamera->myTile);
128 // =======================================================================
129 // function : CopyOrientationData
131 // =======================================================================
132 void Graphic3d_Camera::CopyOrientationData (const Handle(Graphic3d_Camera)& theOtherCamera)
134 SetUp (theOtherCamera->Up());
135 SetEye (theOtherCamera->Eye());
136 SetCenter (theOtherCamera->Center());
137 SetAxialScale (theOtherCamera->AxialScale());
140 // =======================================================================
143 // =======================================================================
144 void Graphic3d_Camera::Copy (const Handle(Graphic3d_Camera)& theOther)
146 CopyMappingData (theOther);
147 CopyOrientationData (theOther);
150 // =======================================================================
153 // =======================================================================
154 void Graphic3d_Camera::SetEye (const gp_Pnt& theEye)
156 if (Eye().IsEqual (theEye, 0.0))
162 InvalidateOrientation();
165 // =======================================================================
166 // function : SetCenter
168 // =======================================================================
169 void Graphic3d_Camera::SetCenter (const gp_Pnt& theCenter)
171 if (Center().IsEqual (theCenter, 0.0))
176 myCenter = theCenter;
177 InvalidateOrientation();
180 // =======================================================================
183 // =======================================================================
184 void Graphic3d_Camera::SetUp (const gp_Dir& theUp)
186 if (Up().IsEqual (theUp, 0.0))
192 InvalidateOrientation();
195 // =======================================================================
196 // function : SetAxialScale
198 // =======================================================================
199 void Graphic3d_Camera::SetAxialScale (const gp_XYZ& theAxialScale)
201 if (AxialScale().IsEqual (theAxialScale, 0.0))
206 myAxialScale = theAxialScale;
207 InvalidateOrientation();
210 // =======================================================================
211 // function : SetDistance
213 // =======================================================================
214 void Graphic3d_Camera::SetDistance (const Standard_Real theDistance)
216 if (Distance() == theDistance)
221 gp_Vec aCenter2Eye (Direction());
222 aCenter2Eye.Reverse();
224 // Camera should have non-zero distance.
225 aCenter2Eye.Scale (Max (theDistance, MIN_DISTANCE));
226 SetEye (Center().Translated (aCenter2Eye));
229 // =======================================================================
230 // function : Distance
232 // =======================================================================
233 Standard_Real Graphic3d_Camera::Distance() const
235 return myEye.Distance (myCenter);
238 // =======================================================================
239 // function : SetDirection
241 // =======================================================================
242 void Graphic3d_Camera::SetDirection (const gp_Dir& theDir)
244 if (Direction().IsEqual (theDir, 0.0))
249 gp_Vec aScaledDir (theDir);
250 aScaledDir.Scale (Distance());
251 aScaledDir.Reverse();
252 SetEye (Center().Translated (aScaledDir));
255 // =======================================================================
256 // function : Direction
258 // =======================================================================
259 gp_Dir Graphic3d_Camera::Direction() const
261 return gp_Dir (gp_Vec (myEye, myCenter));
264 // =======================================================================
265 // function : SetScale
267 // =======================================================================
268 void Graphic3d_Camera::SetScale (const Standard_Real theScale)
270 if (Scale() == theScale)
279 case Projection_Perspective :
280 case Projection_Stereo :
281 case Projection_MonoLeftEye :
282 case Projection_MonoRightEye :
284 Standard_Real aDistance = theScale * 0.5 / myFOVyTan;
285 SetDistance (aDistance);
292 InvalidateProjection();
295 // =======================================================================
298 // =======================================================================
299 Standard_Real Graphic3d_Camera::Scale() const
303 case Projection_Orthographic :
306 // case Projection_Perspective :
307 // case Projection_Stereo :
308 // case Projection_MonoLeftEye :
309 // case Projection_MonoRightEye :
311 return Distance() * 2.0 * myFOVyTan;
315 // =======================================================================
316 // function : SetProjectionType
318 // =======================================================================
319 void Graphic3d_Camera::SetProjectionType (const Projection theProjectionType)
321 Projection anOldType = ProjectionType();
323 if (anOldType == theProjectionType)
328 if (anOldType == Projection_Orthographic)
330 if (myZNear <= RealEpsilon())
332 myZNear = DEFAULT_ZNEAR;
334 if (myZFar <= RealEpsilon())
336 myZFar = DEFAULT_ZFAR;
340 myProjType = theProjectionType;
342 InvalidateProjection();
345 // =======================================================================
346 // function : SetFOVy
348 // =======================================================================
349 void Graphic3d_Camera::SetFOVy (const Standard_Real theFOVy)
351 if (FOVy() == theFOVy)
357 myFOVyTan = Tan(DTR_HALF * myFOVy);
359 InvalidateProjection();
362 // =======================================================================
363 // function : SetZRange
365 // =======================================================================
366 void Graphic3d_Camera::SetZRange (const Standard_Real theZNear,
367 const Standard_Real theZFar)
369 Standard_ASSERT_RAISE (theZFar > theZNear, "ZFar should be greater than ZNear");
370 if (!IsOrthographic())
372 Standard_ASSERT_RAISE (theZNear > 0.0, "Only positive Z-Near is allowed for perspective camera");
373 Standard_ASSERT_RAISE (theZFar > 0.0, "Only positive Z-Far is allowed for perspective camera");
376 if (ZNear() == theZNear
377 && ZFar () == theZFar)
385 InvalidateProjection();
388 // =======================================================================
389 // function : SetAspect
391 // =======================================================================
392 void Graphic3d_Camera::SetAspect (const Standard_Real theAspect)
394 if (Aspect() == theAspect)
399 myAspect = theAspect;
401 InvalidateProjection();
404 // =======================================================================
405 // function : SetZFocus
407 // =======================================================================
408 void Graphic3d_Camera::SetZFocus(const FocusType theType, const Standard_Real theZFocus)
410 if (ZFocusType() == theType
411 && ZFocus () == theZFocus)
416 myZFocusType = theType;
417 myZFocus = theZFocus;
419 InvalidateProjection();
422 // =======================================================================
425 // =======================================================================
426 void Graphic3d_Camera::SetIOD (const IODType theType, const Standard_Real theIOD)
428 if (GetIODType() == theType
437 InvalidateProjection();
440 // =======================================================================
441 // function : SetTile
443 // =======================================================================
444 void Graphic3d_Camera::SetTile (const Graphic3d_CameraTile& theTile)
446 if (myTile == theTile)
452 InvalidateProjection();
455 // =======================================================================
456 // function : OrthogonalizeUp
458 // =======================================================================
459 void Graphic3d_Camera::OrthogonalizeUp()
461 SetUp (OrthogonalizedUp());
464 // =======================================================================
465 // function : OrthogonalizedUp
467 // =======================================================================
468 gp_Dir Graphic3d_Camera::OrthogonalizedUp() const
470 gp_Dir aDir = Direction();
471 gp_Dir aLeft = aDir.Crossed (Up());
473 // recompute up as: up = left x direction
474 return aLeft.Crossed (aDir);
477 // =======================================================================
478 // function : Transform
480 // =======================================================================
481 void Graphic3d_Camera::Transform (const gp_Trsf& theTrsf)
483 if (theTrsf.Form() == gp_Identity)
488 SetUp (myUp.Transformed (theTrsf));
489 SetEye (myEye.Transformed (theTrsf));
490 SetCenter (myCenter.Transformed (theTrsf));
493 // =======================================================================
494 // function : safePointCast
496 // =======================================================================
497 static Graphic3d_Vec4d safePointCast (const gp_Pnt& thePnt)
499 Standard_Real aLim = 1e15f;
501 // have to deal with values greater then max float
502 gp_Pnt aSafePoint = thePnt;
503 const Standard_Real aBigFloat = aLim * 0.1f;
504 if (Abs (aSafePoint.X()) > aLim)
505 aSafePoint.SetX (aSafePoint.X() >= 0 ? aBigFloat : -aBigFloat);
506 if (Abs (aSafePoint.Y()) > aLim)
507 aSafePoint.SetY (aSafePoint.Y() >= 0 ? aBigFloat : -aBigFloat);
508 if (Abs (aSafePoint.Z()) > aLim)
509 aSafePoint.SetZ (aSafePoint.Z() >= 0 ? aBigFloat : -aBigFloat);
512 Graphic3d_Vec4d aPnt (aSafePoint.X(), aSafePoint.Y(), aSafePoint.Z(), 1.0);
517 // =======================================================================
518 // function : Project
520 // =======================================================================
521 gp_Pnt Graphic3d_Camera::Project (const gp_Pnt& thePnt) const
523 const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
524 const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
526 // use compatible type of point
527 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
529 aPnt = aViewMx * aPnt; // convert to view coordinate space
530 aPnt = aProjMx * aPnt; // convert to projection coordinate space
532 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
534 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
537 // =======================================================================
538 // function : UnProject
540 // =======================================================================
541 gp_Pnt Graphic3d_Camera::UnProject (const gp_Pnt& thePnt) const
543 const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
544 const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
546 Graphic3d_Mat4d aInvView;
547 Graphic3d_Mat4d aInvProj;
549 // this case should never happen
550 if (!aViewMx.Inverted (aInvView) || !aProjMx.Inverted (aInvProj))
552 return gp_Pnt (0.0, 0.0, 0.0);
555 // use compatible type of point
556 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
558 aPnt = aInvProj * aPnt; // convert to view coordinate space
559 aPnt = aInvView * aPnt; // convert to world coordinate space
561 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
563 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
566 // =======================================================================
567 // function : ConvertView2Proj
569 // =======================================================================
570 gp_Pnt Graphic3d_Camera::ConvertView2Proj (const gp_Pnt& thePnt) const
572 const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
574 // use compatible type of point
575 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
577 aPnt = aProjMx * aPnt; // convert to projection coordinate space
579 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
581 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
584 // =======================================================================
585 // function : ConvertProj2View
587 // =======================================================================
588 gp_Pnt Graphic3d_Camera::ConvertProj2View (const gp_Pnt& thePnt) const
590 const Graphic3d_Mat4d& aProjMx = ProjectionMatrix();
592 Graphic3d_Mat4d aInvProj;
594 // this case should never happen, but...
595 if (!aProjMx.Inverted (aInvProj))
597 return gp_Pnt (0, 0, 0);
600 // use compatible type of point
601 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
603 aPnt = aInvProj * aPnt; // convert to view coordinate space
605 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
607 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
610 // =======================================================================
611 // function : ConvertWorld2View
613 // =======================================================================
614 gp_Pnt Graphic3d_Camera::ConvertWorld2View (const gp_Pnt& thePnt) const
616 const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
618 // use compatible type of point
619 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
621 aPnt = aViewMx * aPnt; // convert to view coordinate space
623 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
625 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
628 // =======================================================================
629 // function : ConvertView2World
631 // =======================================================================
632 gp_Pnt Graphic3d_Camera::ConvertView2World (const gp_Pnt& thePnt) const
634 const Graphic3d_Mat4d& aViewMx = OrientationMatrix();
636 Graphic3d_Mat4d aInvView;
638 if (!aViewMx.Inverted (aInvView))
640 return gp_Pnt(0, 0, 0);
643 // use compatible type of point
644 Graphic3d_Vec4d aPnt = safePointCast (thePnt);
646 aPnt = aInvView * aPnt; // convert to world coordinate space
648 const Standard_Real aInvW = 1.0 / Standard_Real (aPnt.w());
650 return gp_Pnt (aPnt.x() * aInvW, aPnt.y() * aInvW, aPnt.z() * aInvW);
653 // =======================================================================
654 // function : ViewDimensions
656 // =======================================================================
657 gp_XYZ Graphic3d_Camera::ViewDimensions (const Standard_Real theZValue) const
659 // view plane dimensions
660 Standard_Real aSize = IsOrthographic() ? myScale : (2.0 * theZValue * myFOVyTan);
661 Standard_Real aSizeX, aSizeY;
664 aSizeX = aSize * myAspect;
670 aSizeY = aSize / myAspect;
674 return gp_XYZ (aSizeX, aSizeY, myZFar - myZNear);
677 // =======================================================================
678 // function : Frustum
680 // =======================================================================
681 void Graphic3d_Camera::Frustum (gp_Pln& theLeft,
686 gp_Pln& theFar) const
688 gp_Vec aProjection = gp_Vec (Direction());
689 gp_Vec anUp = OrthogonalizedUp();
690 gp_Vec aSide = aProjection ^ anUp;
692 Standard_ASSERT_RAISE (
693 !aProjection.IsParallel (anUp, Precision::Angular()),
694 "Can not derive SIDE = PROJ x UP - directions are parallel");
696 theNear = gp_Pln (Eye().Translated (aProjection * ZNear()), aProjection);
697 theFar = gp_Pln (Eye().Translated (aProjection * ZFar()), -aProjection);
699 Standard_Real aHScaleHor = Scale() * 0.5 * Aspect();
700 Standard_Real aHScaleVer = Scale() * 0.5;
702 gp_Pnt aPntLeft = Center().Translated (aHScaleHor * -aSide);
703 gp_Pnt aPntRight = Center().Translated (aHScaleHor * aSide);
704 gp_Pnt aPntBottom = Center().Translated (aHScaleVer * -anUp);
705 gp_Pnt aPntTop = Center().Translated (aHScaleVer * anUp);
707 gp_Vec aDirLeft = aSide;
708 gp_Vec aDirRight = -aSide;
709 gp_Vec aDirBottom = anUp;
710 gp_Vec aDirTop = -anUp;
711 if (!IsOrthographic())
713 Standard_Real aHFOVHor = ATan (Tan (DTR_HALF * FOVy()) * Aspect());
714 Standard_Real aHFOVVer = DTR_HALF * FOVy();
715 aDirLeft.Rotate (gp_Ax1 (gp::Origin(), anUp), aHFOVHor);
716 aDirRight.Rotate (gp_Ax1 (gp::Origin(), anUp), -aHFOVHor);
717 aDirBottom.Rotate (gp_Ax1 (gp::Origin(), aSide), -aHFOVVer);
718 aDirTop.Rotate (gp_Ax1 (gp::Origin(), aSide), aHFOVVer);
721 theLeft = gp_Pln (aPntLeft, aDirLeft);
722 theRight = gp_Pln (aPntRight, aDirRight);
723 theBottom = gp_Pln (aPntBottom, aDirBottom);
724 theTop = gp_Pln (aPntTop, aDirTop);
727 // =======================================================================
728 // function : OrientationMatrix
730 // =======================================================================
731 const Graphic3d_Mat4d& Graphic3d_Camera::OrientationMatrix() const
733 return UpdateOrientation (myMatricesD).Orientation;
736 // =======================================================================
737 // function : OrientationMatrixF
739 // =======================================================================
740 const Graphic3d_Mat4& Graphic3d_Camera::OrientationMatrixF() const
742 return UpdateOrientation (myMatricesF).Orientation;
745 // =======================================================================
746 // function : ProjectionMatrix
748 // =======================================================================
749 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionMatrix() const
751 return UpdateProjection (myMatricesD).MProjection;
754 // =======================================================================
755 // function : ProjectionMatrixF
757 // =======================================================================
758 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionMatrixF() const
760 return UpdateProjection (myMatricesF).MProjection;
763 // =======================================================================
764 // function : ProjectionStereoLeft
766 // =======================================================================
767 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoLeft() const
769 return UpdateProjection (myMatricesD).LProjection;
772 // =======================================================================
773 // function : ProjectionStereoLeftF
775 // =======================================================================
776 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoLeftF() const
778 return UpdateProjection (myMatricesF).LProjection;
781 // =======================================================================
782 // function : ProjectionStereoRight
784 // =======================================================================
785 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoRight() const
787 return UpdateProjection (myMatricesD).RProjection;
790 // =======================================================================
791 // function : ProjectionStereoRightF
793 // =======================================================================
794 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoRightF() const
796 return UpdateProjection (myMatricesF).RProjection;
799 // =======================================================================
800 // function : UpdateProjection
802 // =======================================================================
803 template <typename Elem_t>
804 Graphic3d_Camera::TransformMatrices<Elem_t>&
805 Graphic3d_Camera::UpdateProjection (TransformMatrices<Elem_t>& theMatrices) const
807 if (theMatrices.IsProjectionValid())
809 return theMatrices; // for inline accessors
812 theMatrices.InitProjection();
814 // sets top of frustum based on FOVy and near clipping plane
815 Elem_t aScale = static_cast<Elem_t> (myScale);
816 Elem_t aZNear = static_cast<Elem_t> (myZNear);
817 Elem_t aZFar = static_cast<Elem_t> (myZFar);
818 Elem_t anAspect = static_cast<Elem_t> (myAspect);
819 Elem_t aDXHalf = 0.0, aDYHalf = 0.0;
820 if (IsOrthographic())
822 aDXHalf = aScale * Elem_t (0.5);
823 aDYHalf = aScale * Elem_t (0.5);
827 aDXHalf = aZNear * Elem_t (myFOVyTan);
828 aDYHalf = aZNear * Elem_t (myFOVyTan);
840 // sets right of frustum based on aspect ratio
841 Elem_t aLeft = -aDXHalf;
842 Elem_t aRight = aDXHalf;
843 Elem_t aBot = -aDYHalf;
844 Elem_t aTop = aDYHalf;
846 Elem_t aIOD = myIODType == IODType_Relative
847 ? static_cast<Elem_t> (myIOD * Distance())
848 : static_cast<Elem_t> (myIOD);
850 Elem_t aFocus = myZFocusType == FocusType_Relative
851 ? static_cast<Elem_t> (myZFocus * Distance())
852 : static_cast<Elem_t> (myZFocus);
854 if (myTile.IsValid())
856 const Elem_t aDXFull = Elem_t(2) * aDXHalf;
857 const Elem_t aDYFull = Elem_t(2) * aDYHalf;
858 const Graphic3d_Vec2i anOffset = myTile.OffsetLowerLeft();
859 aLeft = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x()) / static_cast<Elem_t> (myTile.TotalSize.x());
860 aRight = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x() + myTile.TileSize.x()) / static_cast<Elem_t> (myTile.TotalSize.x());
861 aBot = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y()) / static_cast<Elem_t> (myTile.TotalSize.y());
862 aTop = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y() + myTile.TileSize.y()) / static_cast<Elem_t> (myTile.TotalSize.y());
867 case Projection_Orthographic :
868 OrthoProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
871 case Projection_Perspective :
872 PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
875 case Projection_MonoLeftEye :
877 StereoEyeProj (aLeft, aRight, aBot, aTop,
878 aZNear, aZFar, aIOD, aFocus,
879 Standard_True, theMatrices.MProjection);
880 theMatrices.LProjection = theMatrices.MProjection;
884 case Projection_MonoRightEye :
886 StereoEyeProj (aLeft, aRight, aBot, aTop,
887 aZNear, aZFar, aIOD, aFocus,
888 Standard_False, theMatrices.MProjection);
889 theMatrices.RProjection = theMatrices.MProjection;
893 case Projection_Stereo :
895 PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
897 StereoEyeProj (aLeft, aRight, aBot, aTop,
898 aZNear, aZFar, aIOD, aFocus,
900 theMatrices.LProjection);
902 StereoEyeProj (aLeft, aRight, aBot, aTop,
903 aZNear, aZFar, aIOD, aFocus,
905 theMatrices.RProjection);
910 return theMatrices; // for inline accessors
913 // =======================================================================
914 // function : UpdateOrientation
916 // =======================================================================
917 template <typename Elem_t>
918 Graphic3d_Camera::TransformMatrices<Elem_t>&
919 Graphic3d_Camera::UpdateOrientation (TransformMatrices<Elem_t>& theMatrices) const
921 if (theMatrices.IsOrientationValid())
923 return theMatrices; // for inline accessors
926 theMatrices.InitOrientation();
928 NCollection_Vec3<Elem_t> anEye (static_cast<Elem_t> (myEye.X()),
929 static_cast<Elem_t> (myEye.Y()),
930 static_cast<Elem_t> (myEye.Z()));
932 NCollection_Vec3<Elem_t> aCenter (static_cast<Elem_t> (myCenter.X()),
933 static_cast<Elem_t> (myCenter.Y()),
934 static_cast<Elem_t> (myCenter.Z()));
936 NCollection_Vec3<Elem_t> anUp (static_cast<Elem_t> (myUp.X()),
937 static_cast<Elem_t> (myUp.Y()),
938 static_cast<Elem_t> (myUp.Z()));
940 NCollection_Vec3<Elem_t> anAxialScale (static_cast<Elem_t> (myAxialScale.X()),
941 static_cast<Elem_t> (myAxialScale.Y()),
942 static_cast<Elem_t> (myAxialScale.Z()));
944 LookOrientation (anEye, aCenter, anUp, anAxialScale, theMatrices.Orientation);
946 return theMatrices; // for inline accessors
949 // =======================================================================
950 // function : InvalidateProjection
952 // =======================================================================
953 void Graphic3d_Camera::InvalidateProjection()
955 myMatricesD.ResetProjection();
956 myMatricesF.ResetProjection();
957 myWorldViewProjState.ProjectionState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
960 // =======================================================================
961 // function : InvalidateOrientation
963 // =======================================================================
964 void Graphic3d_Camera::InvalidateOrientation()
966 myMatricesD.ResetOrientation();
967 myMatricesF.ResetOrientation();
968 myWorldViewProjState.WorldViewState() = (Standard_Size)Standard_Atomic_Increment (&THE_STATE_COUNTER);
971 // =======================================================================
972 // function : OrthoProj
974 // =======================================================================
975 template <typename Elem_t>
976 void Graphic3d_Camera::OrthoProj (const Elem_t theLeft,
977 const Elem_t theRight,
978 const Elem_t theBottom,
980 const Elem_t theNear,
982 NCollection_Mat4<Elem_t>& theOutMx)
985 theOutMx.ChangeValue (0, 0) = Elem_t (2.0) / (theRight - theLeft);
986 theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
987 theOutMx.ChangeValue (0, 2) = Elem_t (0.0);
988 theOutMx.ChangeValue (0, 3) = - (theRight + theLeft) / (theRight - theLeft);
991 theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
992 theOutMx.ChangeValue (1, 1) = Elem_t (2.0) / (theTop - theBottom);
993 theOutMx.ChangeValue (1, 2) = Elem_t (0.0);
994 theOutMx.ChangeValue (1, 3) = - (theTop + theBottom) / (theTop - theBottom);
997 theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
998 theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
999 theOutMx.ChangeValue (2, 2) = Elem_t (-2.0) / (theFar - theNear);
1000 theOutMx.ChangeValue (2, 3) = - (theFar + theNear) / (theFar - theNear);
1003 theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
1004 theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
1005 theOutMx.ChangeValue (3, 2) = Elem_t (0.0);
1006 theOutMx.ChangeValue (3, 3) = Elem_t (1.0);
1009 // =======================================================================
1010 // function : PerspectiveProj
1012 // =======================================================================
1013 template <typename Elem_t>
1014 void Graphic3d_Camera::PerspectiveProj (const Elem_t theLeft,
1015 const Elem_t theRight,
1016 const Elem_t theBottom,
1017 const Elem_t theTop,
1018 const Elem_t theNear,
1019 const Elem_t theFar,
1020 NCollection_Mat4<Elem_t>& theOutMx)
1023 theOutMx.ChangeValue (0, 0) = (Elem_t (2.0) * theNear) / (theRight - theLeft);
1024 theOutMx.ChangeValue (1, 0) = Elem_t (0.0);
1025 theOutMx.ChangeValue (2, 0) = Elem_t (0.0);
1026 theOutMx.ChangeValue (3, 0) = Elem_t (0.0);
1029 theOutMx.ChangeValue (0, 1) = Elem_t (0.0);
1030 theOutMx.ChangeValue (1, 1) = (Elem_t (2.0) * theNear) / (theTop - theBottom);
1031 theOutMx.ChangeValue (2, 1) = Elem_t (0.0);
1032 theOutMx.ChangeValue (3, 1) = Elem_t (0.0);
1035 theOutMx.ChangeValue (0, 2) = (theRight + theLeft) / (theRight - theLeft);
1036 theOutMx.ChangeValue (1, 2) = (theTop + theBottom) / (theTop - theBottom);
1037 theOutMx.ChangeValue (2, 2) = -(theFar + theNear) / (theFar - theNear);
1038 theOutMx.ChangeValue (3, 2) = Elem_t (-1.0);
1041 theOutMx.ChangeValue (0, 3) = Elem_t (0.0);
1042 theOutMx.ChangeValue (1, 3) = Elem_t (0.0);
1043 theOutMx.ChangeValue (2, 3) = -(Elem_t (2.0) * theFar * theNear) / (theFar - theNear);
1044 theOutMx.ChangeValue (3, 3) = Elem_t (0.0);
1047 // =======================================================================
1048 // function : StereoEyeProj
1050 // =======================================================================
1051 template <typename Elem_t>
1052 void Graphic3d_Camera::StereoEyeProj (const Elem_t theLeft,
1053 const Elem_t theRight,
1054 const Elem_t theBottom,
1055 const Elem_t theTop,
1056 const Elem_t theNear,
1057 const Elem_t theFar,
1058 const Elem_t theIOD,
1059 const Elem_t theZFocus,
1060 const Standard_Boolean theIsLeft,
1061 NCollection_Mat4<Elem_t>& theOutMx)
1063 Elem_t aDx = theIsLeft ? Elem_t (0.5) * theIOD : Elem_t (-0.5) * theIOD;
1064 Elem_t aDXStereoShift = aDx * theNear / theZFocus;
1066 // construct eye projection matrix
1067 PerspectiveProj (theLeft + aDXStereoShift,
1068 theRight + aDXStereoShift,
1069 theBottom, theTop, theNear, theFar,
1072 if (theIOD != Elem_t (0.0))
1074 // X translation to cancel parallax
1075 theOutMx.Translate (NCollection_Vec3<Elem_t> (aDx, Elem_t (0.0), Elem_t (0.0)));
1079 // =======================================================================
1080 // function : LookOrientation
1082 // =======================================================================
1083 template <typename Elem_t>
1084 void Graphic3d_Camera::LookOrientation (const NCollection_Vec3<Elem_t>& theEye,
1085 const NCollection_Vec3<Elem_t>& theLookAt,
1086 const NCollection_Vec3<Elem_t>& theUpDir,
1087 const NCollection_Vec3<Elem_t>& theAxialScale,
1088 NCollection_Mat4<Elem_t>& theOutMx)
1090 NCollection_Vec3<Elem_t> aForward = theLookAt - theEye;
1091 aForward.Normalize();
1093 // side = forward x up
1094 NCollection_Vec3<Elem_t> aSide = NCollection_Vec3<Elem_t>::Cross (aForward, theUpDir);
1097 // recompute up as: up = side x forward
1098 NCollection_Vec3<Elem_t> anUp = NCollection_Vec3<Elem_t>::Cross (aSide, aForward);
1100 NCollection_Mat4<Elem_t> aLookMx;
1101 aLookMx.SetRow (0, aSide);
1102 aLookMx.SetRow (1, anUp);
1103 aLookMx.SetRow (2, -aForward);
1105 theOutMx.InitIdentity();
1106 theOutMx.Multiply (aLookMx);
1107 theOutMx.Translate (-theEye);
1109 NCollection_Mat4<Elem_t> anAxialScaleMx;
1110 anAxialScaleMx.ChangeValue (0, 0) = theAxialScale.x();
1111 anAxialScaleMx.ChangeValue (1, 1) = theAxialScale.y();
1112 anAxialScaleMx.ChangeValue (2, 2) = theAxialScale.z();
1114 theOutMx.Multiply (anAxialScaleMx);
1117 //=============================================================================
1118 //function : ZFitAll
1120 //=============================================================================
1121 bool Graphic3d_Camera::ZFitAll (const Standard_Real theScaleFactor,
1122 const Bnd_Box& theMinMax,
1123 const Bnd_Box& theGraphicBB,
1124 Standard_Real& theZNear,
1125 Standard_Real& theZFar) const
1127 Standard_ASSERT_RAISE (theScaleFactor > 0.0, "Zero or negative scale factor is not allowed.");
1129 // Method changes zNear and zFar parameters of camera so as to fit graphical structures
1130 // by their graphical boundaries. It precisely fits min max boundaries of primary application
1131 // objects (second argument), while it can sacrifice the real graphical boundaries of the
1132 // scene with infinite or helper objects (third argument) for the sake of perspective projection.
1133 if (theGraphicBB.IsVoid())
1135 theZNear = DEFAULT_ZNEAR;
1136 theZFar = DEFAULT_ZFAR;
1140 // Measure depth of boundary points from camera eye.
1141 NCollection_Sequence<gp_Pnt> aPntsToMeasure;
1143 Standard_Real aGraphicBB[6];
1144 theGraphicBB.Get (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2], aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]);
1146 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[2]));
1147 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[1], aGraphicBB[5]));
1148 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[2]));
1149 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[0], aGraphicBB[4], aGraphicBB[5]));
1150 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[2]));
1151 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[1], aGraphicBB[5]));
1152 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[2]));
1153 aPntsToMeasure.Append (gp_Pnt (aGraphicBB[3], aGraphicBB[4], aGraphicBB[5]));
1155 Standard_Boolean isFiniteMinMax = !theMinMax.IsVoid() && !theMinMax.IsWhole();
1159 Standard_Real aMinMax[6];
1160 theMinMax.Get (aMinMax[0], aMinMax[1], aMinMax[2], aMinMax[3], aMinMax[4], aMinMax[5]);
1162 aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[2]));
1163 aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[1], aMinMax[5]));
1164 aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[2]));
1165 aPntsToMeasure.Append (gp_Pnt (aMinMax[0], aMinMax[4], aMinMax[5]));
1166 aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[2]));
1167 aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[1], aMinMax[5]));
1168 aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[2]));
1169 aPntsToMeasure.Append (gp_Pnt (aMinMax[3], aMinMax[4], aMinMax[5]));
1172 // Camera eye plane.
1173 gp_Dir aCamDir = Direction();
1174 gp_Pnt aCamEye = myEye;
1175 gp_Pln aCamPln (aCamEye, aCamDir);
1177 Standard_Real aModelMinDist = RealLast();
1178 Standard_Real aModelMaxDist = RealFirst();
1179 Standard_Real aGraphMinDist = RealLast();
1180 Standard_Real aGraphMaxDist = RealFirst();
1182 const gp_XYZ& anAxialScale = myAxialScale;
1184 // Get minimum and maximum distances to the eye plane.
1185 Standard_Integer aCounter = 0;
1186 NCollection_Sequence<gp_Pnt>::Iterator aPntIt(aPntsToMeasure);
1187 for (; aPntIt.More(); aPntIt.Next())
1189 gp_Pnt aMeasurePnt = aPntIt.Value();
1191 aMeasurePnt = gp_Pnt (aMeasurePnt.X() * anAxialScale.X(),
1192 aMeasurePnt.Y() * anAxialScale.Y(),
1193 aMeasurePnt.Z() * anAxialScale.Z());
1195 Standard_Real aDistance = aCamPln.Distance (aMeasurePnt);
1197 // Check if the camera is intruded into the scene.
1198 if (aCamDir.IsOpposite (gp_Vec (aCamEye, aMeasurePnt), M_PI * 0.5))
1203 // The first eight points are from theGraphicBB, the last eight points are from theMinMax (can be absent).
1204 Standard_Real& aChangeMinDist = aCounter >= 8 ? aModelMinDist : aGraphMinDist;
1205 Standard_Real& aChangeMaxDist = aCounter >= 8 ? aModelMaxDist : aGraphMaxDist;
1206 aChangeMinDist = Min (aDistance, aChangeMinDist);
1207 aChangeMaxDist = Max (aDistance, aChangeMaxDist);
1211 // Compute depth of bounding box center.
1212 Standard_Real aMidDepth = (aGraphMinDist + aGraphMaxDist) * 0.5;
1213 Standard_Real aHalfDepth = (aGraphMaxDist - aGraphMinDist) * 0.5;
1215 // Compute enlarged or shrank near and far z ranges.
1216 Standard_Real aZNear = aMidDepth - aHalfDepth * theScaleFactor;
1217 Standard_Real aZFar = aMidDepth + aHalfDepth * theScaleFactor;
1219 if (!IsOrthographic())
1221 // Everything is behind the perspective camera.
1222 if (aZFar < zEpsilon())
1224 theZNear = DEFAULT_ZNEAR;
1225 theZFar = DEFAULT_ZFAR;
1231 // Consider clipping errors due to double to single precision floating-point conversion.
1234 // Model to view transformation performs translation of points against eye position
1235 // in three dimensions. Both point coordinate and eye position values are converted from
1236 // double to single precision floating point numbers producing conversion errors.
1237 // Epsilon (Mod) * 3.0 should safely compensate precision error for z coordinate after
1238 // translation assuming that the:
1239 // Epsilon (Eye.Mod()) * 3.0 > Epsilon (Eye.X()) + Epsilon (Eye.Y()) + Epsilon (Eye.Z()).
1240 Standard_Real aEyeConf = 3.0 * zEpsilon (myEye.XYZ().Modulus());
1242 // Model to view transformation performs rotation of points according to view direction.
1243 // New z coordinate is computed as a multiplication of point's x, y, z coordinates by the
1244 // "forward" direction vector's x, y, z coordinates. Both point's and "z" direction vector's
1245 // values are converted from double to single precision floating point numbers producing
1246 // conversion errors.
1247 // Epsilon (Mod) * 6.0 should safely compensate the precision errors for the multiplication
1248 // of point coordinates by direction vector.
1249 gp_Pnt aGraphicMin = theGraphicBB.CornerMin();
1250 gp_Pnt aGraphicMax = theGraphicBB.CornerMax();
1252 Standard_Real aModelConf = 6.0 * zEpsilon (aGraphicMin.XYZ().Modulus()) +
1253 6.0 * zEpsilon (aGraphicMax.XYZ().Modulus());
1255 // Compensate floating point conversion errors by increasing zNear, zFar to avoid clipping.
1256 aZNear -= zEpsilon (aZNear) + aEyeConf + aModelConf;
1257 aZFar += zEpsilon (aZFar) + aEyeConf + aModelConf;
1259 if (!IsOrthographic())
1261 // For perspective projection, the value of z in normalized device coordinates is non-linear
1262 // function of eye z coordinate. For fixed-point depth representation resolution of z in
1263 // model-view space will grow towards zFar plane and its scale depends mostly on how far is zNear
1264 // against camera's eye. The purpose of the code below is to select most appropriate zNear distance
1265 // to balance between clipping (less zNear, more chances to observe closely small models without clipping)
1266 // and resolution of depth. A well applicable criteria to this is a ratio between resolution of z at center
1267 // of model boundaries and the distance to that center point. The ratio is chosen empirically and validated
1268 // by tests database. It is considered to be ~0.001 (0.1%) for 24 bit depth buffer, for less depth bitness
1269 // the zNear will be placed similarly giving lower resolution.
1270 // Approximation of the formula for respectively large z range is:
1271 // zNear = [z * (1 + k) / (k * c)],
1273 // z - distance to center of model boundaries;
1274 // k - chosen ratio, c - capacity of depth buffer;
1275 // k = 0.001, k * c = 1677.216, (1 + k) / (k * c) ~ 5.97E-4
1277 // The function uses center of model boundaries computed from "theMinMax" boundaries (instead of using real
1278 // graphical boundaries of all displayed objects). That means that it can sacrifice resolution of presentation
1279 // of non primary ("infinite") application graphical objects in favor of better perspective projection of the
1280 // small applicative objects measured with "theMinMax" values.
1281 Standard_Real aZRange = isFiniteMinMax ? aModelMaxDist - aModelMinDist : aGraphMaxDist - aGraphMinDist;
1282 Standard_Real aZMin = isFiniteMinMax ? aModelMinDist : aGraphMinDist;
1283 Standard_Real aZ = aZMin < 0 ? aZRange / 2.0 : aZRange / 2.0 + aZMin;
1284 Standard_Real aZNearMin = aZ * 5.97E-4;
1285 if (aZNear < aZNearMin)
1287 // Clip zNear according to the minimum value matching the quality.
1292 // Compensate zNear conversion errors for perspective projection.
1293 aZNear -= aZFar * zEpsilon (aZNear) / (aZFar - zEpsilon (aZNear));
1296 // Compensate zFar conversion errors for perspective projection.
1297 aZFar += zEpsilon (aZFar);
1299 // Ensure that after all the zNear is not a negative value.
1300 if (aZNear < zEpsilon())
1302 aZNear = zEpsilon();
1311 //=============================================================================
1312 //function : Interpolate
1314 //=============================================================================
1316 Standard_EXPORT void NCollection_Lerp<Handle(Graphic3d_Camera)>::Interpolate (const double theT,
1317 Handle(Graphic3d_Camera)& theCamera) const
1319 if (Abs (theT - 1.0) < Precision::Confusion())
1321 // just copy end-point transformation
1322 theCamera->Copy (myEnd);
1326 theCamera->Copy (myStart);
1327 if (Abs (theT - 0.0) < Precision::Confusion())
1334 gp_Ax3 aCamStart = cameraToAx3 (*myStart);
1335 gp_Ax3 aCamEnd = cameraToAx3 (*myEnd);
1336 gp_Trsf aTrsfStart, aTrsfEnd;
1337 aTrsfStart.SetTransformation (aCamStart, gp::XOY());
1338 aTrsfEnd .SetTransformation (aCamEnd, gp::XOY());
1340 gp_Quaternion aRotStart = aTrsfStart.GetRotation();
1341 gp_Quaternion aRotEnd = aTrsfEnd .GetRotation();
1342 gp_Quaternion aRotDelta = aRotEnd * aRotStart.Inverted();
1343 gp_Quaternion aRot = gp_QuaternionNLerp::Interpolate (gp_Quaternion(), aRotDelta, theT);
1345 aTrsfRot.SetRotation (aRot);
1346 theCamera->Transform (aTrsfRot);
1349 // apply translation
1351 gp_XYZ aCenter = NCollection_Lerp<gp_XYZ>::Interpolate (myStart->Center().XYZ(), myEnd->Center().XYZ(), theT);
1352 gp_XYZ anEye = NCollection_Lerp<gp_XYZ>::Interpolate (myStart->Eye().XYZ(), myEnd->Eye().XYZ(), theT);
1353 gp_XYZ anAnchor = aCenter;
1354 Standard_Real aKc = 0.0;
1356 const Standard_Real aDeltaCenter = myStart->Center().Distance (myEnd->Center());
1357 const Standard_Real aDeltaEye = myStart->Eye() .Distance (myEnd->Eye());
1358 if (aDeltaEye <= gp::Resolution())
1363 else if (aDeltaCenter > gp::Resolution())
1365 aKc = aDeltaCenter / (aDeltaCenter + aDeltaEye);
1367 const gp_XYZ anAnchorStart = NCollection_Lerp<gp_XYZ>::Interpolate (myStart->Center().XYZ(), myStart->Eye().XYZ(), aKc);
1368 const gp_XYZ anAnchorEnd = NCollection_Lerp<gp_XYZ>::Interpolate (myEnd ->Center().XYZ(), myEnd ->Eye().XYZ(), aKc);
1369 anAnchor = NCollection_Lerp<gp_XYZ>::Interpolate (anAnchorStart, anAnchorEnd, theT);
1372 const gp_Vec aDirEyeToCenter = theCamera->Direction();
1373 const Standard_Real aDistEyeCenterStart = myStart->Eye().Distance (myStart->Center());
1374 const Standard_Real aDistEyeCenterEnd = myEnd ->Eye().Distance (myEnd ->Center());
1375 const Standard_Real aDistEyeCenter = NCollection_Lerp<Standard_Real>::Interpolate (aDistEyeCenterStart, aDistEyeCenterEnd, theT);
1376 aCenter = anAnchor + aDirEyeToCenter.XYZ() * aDistEyeCenter * aKc;
1377 anEye = anAnchor - aDirEyeToCenter.XYZ() * aDistEyeCenter * (1.0 - aKc);
1379 theCamera->SetCenter (aCenter);
1380 theCamera->SetEye (anEye);
1384 if (Abs(myStart->Scale() - myEnd->Scale()) > Precision::Confusion()
1385 && myStart->IsOrthographic())
1387 const Standard_Real aScale = NCollection_Lerp<Standard_Real>::Interpolate (myStart->Scale(), myEnd->Scale(), theT);
1388 theCamera->SetScale (aScale);