0031511: Point Cloud Rendering, Volume Rendering - reuse Graphic3d_CullingTool
[occt.git] / src / Graphic3d / Graphic3d_Camera.cxx
index d5e531b..854ec60 100644 (file)
 // Alternatively, this file may be used under the terms of Open CASCADE
 // commercial license or contractual agreement.
 
-#include <gp_Pln.hxx>
-#include <Standard_ShortReal.hxx>
-
 #include <Graphic3d_Camera.hxx>
+
+#include <gp_Pln.hxx>
+#include <gp_QuaternionNLerp.hxx>
+#include <gp_QuaternionSLerp.hxx>
 #include <Graphic3d_Vec4.hxx>
 #include <Graphic3d_WorldViewProjState.hxx>
-
+#include <NCollection_Sequence.hxx>
+#include <Standard_ShortReal.hxx>
 #include <Standard_Atomic.hxx>
 #include <Standard_Assert.hxx>
 
-#include <NCollection_Sequence.hxx>
-
-
 IMPLEMENT_STANDARD_RTTIEXT(Graphic3d_Camera,Standard_Transient)
 
 namespace
@@ -40,9 +39,6 @@ namespace
   // atomic state counter
   static volatile Standard_Integer THE_STATE_COUNTER = 0;
 
-  // minimum camera distance
-  static const Standard_Real MIN_DISTANCE = Pow (0.1, ShortRealDigits() - 2);
-
   // z-range tolerance compatible with for floating point.
   static Standard_Real zEpsilon()
   {
@@ -60,8 +56,18 @@ namespace
     Standard_Real aLogRadix = Log10 (anAbsValue) / Log10 (FLT_RADIX);
     Standard_Real aExp = Floor (aLogRadix);
     return FLT_EPSILON * Pow (FLT_RADIX, aExp);
-  };
-};
+  }
+
+  //! Convert camera definition to Ax3
+  gp_Ax3 cameraToAx3 (const Graphic3d_Camera& theCamera)
+  {
+    const gp_Dir aBackDir = -theCamera.Direction();
+    const gp_Dir anXAxis (theCamera.Up().Crossed (aBackDir));
+    const gp_Dir anYAxis (aBackDir      .Crossed (anXAxis));
+    const gp_Dir aZAxis  (anXAxis       .Crossed (anYAxis));
+    return gp_Ax3 (gp_Pnt (0.0, 0.0, 0.0), aZAxis, anXAxis);
+  }
+}
 
 // =======================================================================
 // function : Graphic3d_Camera
@@ -69,11 +75,13 @@ namespace
 // =======================================================================
 Graphic3d_Camera::Graphic3d_Camera()
 : myUp (0.0, 1.0, 0.0),
+  myDirection (0.0, 0.0, 1.0),
   myEye (0.0, 0.0, -1500.0),
-  myCenter (0.0, 0.0, 0.0),
+  myDistance (1500.0),
   myAxialScale (1.0, 1.0, 1.0),
   myProjType (Projection_Orthographic),
   myFOVy (45.0),
+  myFOVyTan (Tan (DTR_HALF * 45.0)),
   myZNear (DEFAULT_ZNEAR),
   myZFar (DEFAULT_ZFAR),
   myAspect (1.0),
@@ -93,6 +101,22 @@ Graphic3d_Camera::Graphic3d_Camera()
 // purpose  :
 // =======================================================================
 Graphic3d_Camera::Graphic3d_Camera (const Handle(Graphic3d_Camera)& theOther)
+: myUp (0.0, 1.0, 0.0),
+  myDirection (0.0, 0.0, 1.0),
+  myEye (0.0, 0.0, -1500.0),
+  myDistance (1500.0),
+  myAxialScale (1.0, 1.0, 1.0),
+  myProjType (Projection_Orthographic),
+  myFOVy (45.0),
+  myFOVyTan (Tan (DTR_HALF * 45.0)),
+  myZNear (DEFAULT_ZNEAR),
+  myZFar (DEFAULT_ZFAR),
+  myAspect (1.0),
+  myScale (1000.0),
+  myZFocus (1.0),
+  myZFocusType (FocusType_Relative),
+  myIOD (0.05),
+  myIODType (IODType_Relative)
 {
   myWorldViewProjState.Initialize (this);
 
@@ -112,6 +136,7 @@ void Graphic3d_Camera::CopyMappingData (const Handle(Graphic3d_Camera)& theOther
   SetZFocus         (theOtherCamera->ZFocusType(), theOtherCamera->ZFocus());
   SetIOD            (theOtherCamera->GetIODType(), theOtherCamera->IOD());
   SetProjectionType (theOtherCamera->ProjectionType());
+  SetTile           (theOtherCamera->myTile);
 }
 
 // =======================================================================
@@ -120,9 +145,17 @@ void Graphic3d_Camera::CopyMappingData (const Handle(Graphic3d_Camera)& theOther
 // =======================================================================
 void Graphic3d_Camera::CopyOrientationData (const Handle(Graphic3d_Camera)& theOtherCamera)
 {
-  SetUp         (theOtherCamera->Up());
-  SetEye        (theOtherCamera->Eye());
-  SetCenter     (theOtherCamera->Center());
+  if (!myEye.IsEqual (theOtherCamera->Eye(), 0.0)
+   || !myUp.IsEqual (theOtherCamera->Up(), 0.0)
+   || !myDirection.IsEqual (theOtherCamera->Direction(), 0.0)
+   ||  myDistance != theOtherCamera->Distance())
+  {
+    myEye = theOtherCamera->Eye();
+    myUp  = theOtherCamera->Up();
+    myDirection = theOtherCamera->Direction();
+    myDistance = theOtherCamera->Distance();
+    InvalidateOrientation();
+  }
   SetAxialScale (theOtherCamera->AxialScale());
 }
 
@@ -136,6 +169,43 @@ void Graphic3d_Camera::Copy (const Handle(Graphic3d_Camera)& theOther)
   CopyOrientationData (theOther);
 }
 
+// =======================================================================
+// function : MoveEyeTo
+// purpose  :
+// =======================================================================
+void Graphic3d_Camera::MoveEyeTo (const gp_Pnt& theEye)
+{
+  if (myEye.IsEqual (theEye, 0.0))
+  {
+    return;
+  }
+
+  myEye = theEye;
+  InvalidateOrientation();
+}
+
+// =======================================================================
+// function : SetEyeAndCenter
+// purpose  :
+// =======================================================================
+void Graphic3d_Camera::SetEyeAndCenter (const gp_Pnt& theEye,
+                                        const gp_Pnt& theCenter)
+{
+  if (Eye()   .IsEqual (theEye,    0.0)
+   && Center().IsEqual (theCenter, 0.0))
+  {
+    return;
+  }
+
+  myEye = theEye;
+  myDistance = theEye.Distance (theCenter);
+  if (myDistance > gp::Resolution())
+  {
+    myDirection = gp_Dir (theCenter.XYZ() - theEye.XYZ());
+  }
+  InvalidateOrientation();
+}
+
 // =======================================================================
 // function : SetEye
 // purpose  :
@@ -147,7 +217,13 @@ void Graphic3d_Camera::SetEye (const gp_Pnt& theEye)
     return;
   }
 
+  const gp_Pnt aCenter = Center();
   myEye = theEye;
+  myDistance = myEye.Distance (aCenter);
+  if (myDistance > gp::Resolution())
+  {
+    myDirection = gp_Dir (aCenter.XYZ() - myEye.XYZ());
+  }
   InvalidateOrientation();
 }
 
@@ -157,12 +233,17 @@ void Graphic3d_Camera::SetEye (const gp_Pnt& theEye)
 // =======================================================================
 void Graphic3d_Camera::SetCenter (const gp_Pnt& theCenter)
 {
-  if (Center().IsEqual (theCenter, 0.0))
+  const Standard_Real aDistance = myEye.Distance (theCenter);
+  if (myDistance == aDistance)
   {
     return;
   }
 
-  myCenter = theCenter;
+  myDistance = aDistance;
+  if (myDistance > gp::Resolution())
+  {
+    myDirection = gp_Dir (theCenter.XYZ() - myEye.XYZ());
+  }
   InvalidateOrientation();
 }
 
@@ -202,26 +283,30 @@ void Graphic3d_Camera::SetAxialScale (const gp_XYZ& theAxialScale)
 // =======================================================================
 void Graphic3d_Camera::SetDistance (const Standard_Real theDistance)
 {
-  if (Distance() == theDistance)
+  if (myDistance == theDistance)
   {
     return;
   }
 
-  gp_Vec aCenter2Eye (Direction());
-  aCenter2Eye.Reverse();
-
-  // Camera should have non-zero distance.
-  aCenter2Eye.Scale (Max (theDistance, MIN_DISTANCE));
-  SetEye (Center().Translated (aCenter2Eye));
+  const gp_Pnt aCenter = Center();
+  myDistance = theDistance;
+  myEye = aCenter.XYZ() - myDirection.XYZ() * myDistance;
+  InvalidateOrientation();
 }
 
 // =======================================================================
-// function : Distance
+// function : SetDirectionFromEye
 // purpose  :
 // =======================================================================
-Standard_Real Graphic3d_Camera::Distance() const
+void Graphic3d_Camera::SetDirectionFromEye (const gp_Dir& theDir)
 {
-  return myEye.Distance (myCenter);
+  if (myDirection.IsEqual (theDir, 0.0))
+  {
+    return;
+  }
+
+  myDirection = theDir;
+  InvalidateOrientation();
 }
 
 // =======================================================================
@@ -230,24 +315,15 @@ Standard_Real Graphic3d_Camera::Distance() const
 // =======================================================================
 void Graphic3d_Camera::SetDirection (const gp_Dir& theDir)
 {
-  if (Direction().IsEqual (theDir, 0.0))
+  if (myDirection.IsEqual (theDir, 0.0))
   {
     return;
   }
 
-  gp_Vec aScaledDir (theDir);
-  aScaledDir.Scale (Distance());
-  aScaledDir.Reverse();
-  SetEye (Center().Translated (aScaledDir));
-}
-
-// =======================================================================
-// function : Direction
-// purpose  :
-// =======================================================================
-gp_Dir Graphic3d_Camera::Direction() const
-{
-  return gp_Dir (gp_Vec (myEye, myCenter));
+  const gp_Pnt aCenter = Center();
+  myDirection = theDir;
+  myEye = aCenter.XYZ() - theDir.XYZ() * myDistance;
+  InvalidateOrientation();
 }
 
 // =======================================================================
@@ -270,7 +346,7 @@ void Graphic3d_Camera::SetScale (const Standard_Real theScale)
     case Projection_MonoLeftEye  :
     case Projection_MonoRightEye :
     {
-      Standard_Real aDistance = theScale * 0.5 / Tan(myFOVy * M_PI / 360.0);
+      Standard_Real aDistance = theScale * 0.5 / myFOVyTan;
       SetDistance (aDistance);
     }
 
@@ -297,7 +373,7 @@ Standard_Real Graphic3d_Camera::Scale() const
     // case Projection_MonoLeftEye  :
     // case Projection_MonoRightEye :
     default :
-      return Distance() * 2.0 * Tan (myFOVy * M_PI / 360.0);
+      return Distance() * 2.0 * myFOVyTan;
   }
 }
 
@@ -343,6 +419,7 @@ void Graphic3d_Camera::SetFOVy (const Standard_Real theFOVy)
   }
 
   myFOVy = theFOVy;
+  myFOVyTan = Tan(DTR_HALF * myFOVy);
 
   InvalidateProjection();
 }
@@ -425,6 +502,21 @@ void Graphic3d_Camera::SetIOD (const IODType theType, const Standard_Real theIOD
   InvalidateProjection();
 }
 
+// =======================================================================
+// function : SetTile
+// purpose  :
+// =======================================================================
+void Graphic3d_Camera::SetTile (const Graphic3d_CameraTile& theTile)
+{
+  if (myTile == theTile)
+  {
+    return;
+  }
+
+  myTile = theTile;
+  InvalidateProjection();
+}
+
 // =======================================================================
 // function : OrthogonalizeUp
 // purpose  :
@@ -458,9 +550,10 @@ void Graphic3d_Camera::Transform (const gp_Trsf& theTrsf)
     return;
   }
 
-  SetUp     (myUp.Transformed (theTrsf));
-  SetEye    (myEye.Transformed (theTrsf));
-  SetCenter (myCenter.Transformed (theTrsf));
+  myUp .Transform (theTrsf);
+  myDirection.Transform (theTrsf);
+  myEye.Transform (theTrsf);
+  InvalidateOrientation();
 }
 
 // =======================================================================
@@ -627,10 +720,10 @@ gp_Pnt Graphic3d_Camera::ConvertView2World (const gp_Pnt& thePnt) const
 // function : ViewDimensions
 // purpose  :
 // =======================================================================
-gp_XYZ Graphic3d_Camera::ViewDimensions() const
+gp_XYZ Graphic3d_Camera::ViewDimensions (const Standard_Real theZValue) const
 {
   // view plane dimensions
-  Standard_Real aSize = IsOrthographic() ? myScale : (2.0 * Distance() * Tan (DTR_HALF * myFOVy));
+  Standard_Real aSize = IsOrthographic() ? myScale : (2.0 * theZValue * myFOVyTan);
   Standard_Real aSizeX, aSizeY;
   if (myAspect > 1.0)
   {
@@ -669,8 +762,17 @@ void Graphic3d_Camera::Frustum (gp_Pln& theLeft,
   theNear = gp_Pln (Eye().Translated (aProjection * ZNear()), aProjection);
   theFar  = gp_Pln (Eye().Translated (aProjection * ZFar()), -aProjection);
 
-  Standard_Real aHScaleHor = Scale() * 0.5 * Aspect();
-  Standard_Real aHScaleVer = Scale() * 0.5;
+  Standard_Real aHScaleHor = 0.0, aHScaleVer = 0.0;
+  if (Aspect() >= 1.0)
+  {
+    aHScaleHor = Scale() * 0.5 * Aspect();
+    aHScaleVer = Scale() * 0.5;
+  }
+  else
+  {
+    aHScaleHor = Scale() * 0.5;
+    aHScaleVer = Scale() * 0.5 / Aspect();
+  }
 
   gp_Pnt aPntLeft   = Center().Translated (aHScaleHor * -aSide);
   gp_Pnt aPntRight  = Center().Translated (aHScaleHor *  aSide);
@@ -703,7 +805,7 @@ void Graphic3d_Camera::Frustum (gp_Pln& theLeft,
 // =======================================================================
 const Graphic3d_Mat4d& Graphic3d_Camera::OrientationMatrix() const
 {
-  return *UpdateOrientation (myMatricesD).Orientation;
+  return UpdateOrientation (myMatricesD).Orientation;
 }
 
 // =======================================================================
@@ -712,7 +814,7 @@ const Graphic3d_Mat4d& Graphic3d_Camera::OrientationMatrix() const
 // =======================================================================
 const Graphic3d_Mat4& Graphic3d_Camera::OrientationMatrixF() const
 {
-  return *UpdateOrientation (myMatricesF).Orientation;
+  return UpdateOrientation (myMatricesF).Orientation;
 }
 
 // =======================================================================
@@ -721,7 +823,7 @@ const Graphic3d_Mat4& Graphic3d_Camera::OrientationMatrixF() const
 // =======================================================================
 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionMatrix() const
 {
-  return *UpdateProjection (myMatricesD).MProjection;
+  return UpdateProjection (myMatricesD).MProjection;
 }
 
 // =======================================================================
@@ -730,7 +832,7 @@ const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionMatrix() const
 // =======================================================================
 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionMatrixF() const
 {
-  return *UpdateProjection (myMatricesF).MProjection;
+  return UpdateProjection (myMatricesF).MProjection;
 }
 
 // =======================================================================
@@ -739,7 +841,7 @@ const Graphic3d_Mat4& Graphic3d_Camera::ProjectionMatrixF() const
 // =======================================================================
 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoLeft() const
 {
-  return *UpdateProjection (myMatricesD).LProjection;
+  return UpdateProjection (myMatricesD).LProjection;
 }
 
 // =======================================================================
@@ -748,7 +850,7 @@ const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoLeft() const
 // =======================================================================
 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoLeftF() const
 {
-  return *UpdateProjection (myMatricesF).LProjection;
+  return UpdateProjection (myMatricesF).LProjection;
 }
 
 // =======================================================================
@@ -757,7 +859,7 @@ const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoLeftF() const
 // =======================================================================
 const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoRight() const
 {
-  return *UpdateProjection (myMatricesD).RProjection;
+  return UpdateProjection (myMatricesD).RProjection;
 }
 
 // =======================================================================
@@ -766,7 +868,7 @@ const Graphic3d_Mat4d& Graphic3d_Camera::ProjectionStereoRight() const
 // =======================================================================
 const Graphic3d_Mat4& Graphic3d_Camera::ProjectionStereoRightF() const
 {
-  return *UpdateProjection (myMatricesF).RProjection;
+  return UpdateProjection (myMatricesF).RProjection;
 }
 
 // =======================================================================
@@ -797,8 +899,8 @@ Graphic3d_Camera::TransformMatrices<Elem_t>&
   }
   else
   {
-    aDXHalf = aZNear * Elem_t (Tan (DTR_HALF * myFOVy));
-    aDYHalf = aZNear * Elem_t (Tan (DTR_HALF * myFOVy));
+    aDXHalf = aZNear * Elem_t (myFOVyTan);
+    aDYHalf = aZNear * Elem_t (myFOVyTan);
   }
 
   if (anAspect > 1.0)
@@ -824,22 +926,33 @@ Graphic3d_Camera::TransformMatrices<Elem_t>&
     ? static_cast<Elem_t> (myZFocus * Distance())
     : static_cast<Elem_t> (myZFocus);
 
+  if (myTile.IsValid())
+  {
+    const Elem_t aDXFull = Elem_t(2) * aDXHalf;
+    const Elem_t aDYFull = Elem_t(2) * aDYHalf;
+    const Graphic3d_Vec2i anOffset = myTile.OffsetLowerLeft();
+    aLeft  = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x())                       / static_cast<Elem_t> (myTile.TotalSize.x());
+    aRight = -aDXHalf + aDXFull * static_cast<Elem_t> (anOffset.x() + myTile.TileSize.x()) / static_cast<Elem_t> (myTile.TotalSize.x());
+    aBot   = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y())                       / static_cast<Elem_t> (myTile.TotalSize.y());
+    aTop   = -aDYHalf + aDYFull * static_cast<Elem_t> (anOffset.y() + myTile.TileSize.y()) / static_cast<Elem_t> (myTile.TotalSize.y());
+  }
+
   switch (myProjType)
   {
     case Projection_Orthographic :
-      OrthoProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection);
+      OrthoProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
       break;
 
     case Projection_Perspective :
-      PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection);
+      PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
       break;
 
     case Projection_MonoLeftEye :
     {
       StereoEyeProj (aLeft, aRight, aBot, aTop,
                      aZNear, aZFar, aIOD, aFocus,
-                     Standard_True, *theMatrices.MProjection);
-      *theMatrices.LProjection = *theMatrices.MProjection;
+                     Standard_True, theMatrices.MProjection);
+      theMatrices.LProjection = theMatrices.MProjection;
       break;
     }
 
@@ -847,24 +960,24 @@ Graphic3d_Camera::TransformMatrices<Elem_t>&
     {
       StereoEyeProj (aLeft, aRight, aBot, aTop,
                      aZNear, aZFar, aIOD, aFocus,
-                     Standard_False, *theMatrices.MProjection);
-      *theMatrices.RProjection = *theMatrices.MProjection;
+                     Standard_False, theMatrices.MProjection);
+      theMatrices.RProjection = theMatrices.MProjection;
       break;
     }
 
     case Projection_Stereo :
     {
-      PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, *theMatrices.MProjection);
+      PerspectiveProj (aLeft, aRight, aBot, aTop, aZNear, aZFar, theMatrices.MProjection);
 
       StereoEyeProj (aLeft, aRight, aBot, aTop,
                      aZNear, aZFar, aIOD, aFocus,
                      Standard_True,
-                     *theMatrices.LProjection);
+                     theMatrices.LProjection);
 
       StereoEyeProj (aLeft, aRight, aBot, aTop,
                      aZNear, aZFar, aIOD, aFocus,
                      Standard_False,
-                     *theMatrices.RProjection);
+                     theMatrices.RProjection);
       break;
     }
   }
@@ -891,9 +1004,9 @@ Graphic3d_Camera::TransformMatrices<Elem_t>&
                                   static_cast<Elem_t> (myEye.Y()),
                                   static_cast<Elem_t> (myEye.Z()));
 
-  NCollection_Vec3<Elem_t> aCenter (static_cast<Elem_t> (myCenter.X()),
-                                    static_cast<Elem_t> (myCenter.Y()),
-                                    static_cast<Elem_t> (myCenter.Z()));
+  NCollection_Vec3<Elem_t> aViewDir (static_cast<Elem_t> (myDirection.X()),
+                                     static_cast<Elem_t> (myDirection.Y()),
+                                     static_cast<Elem_t> (myDirection.Z()));
 
   NCollection_Vec3<Elem_t> anUp (static_cast<Elem_t> (myUp.X()),
                                  static_cast<Elem_t> (myUp.Y()),
@@ -903,7 +1016,7 @@ Graphic3d_Camera::TransformMatrices<Elem_t>&
                                          static_cast<Elem_t> (myAxialScale.Y()),
                                          static_cast<Elem_t> (myAxialScale.Z()));
 
-  LookOrientation (anEye, aCenter, anUp, anAxialScale, *theMatrices.Orientation);
+  LookOrientation (anEye, aViewDir, anUp, anAxialScale, theMatrices.Orientation);
 
   return theMatrices; // for inline accessors
 }
@@ -1044,12 +1157,12 @@ void Graphic3d_Camera::StereoEyeProj (const Elem_t theLeft,
 // =======================================================================
 template <typename Elem_t>
 void Graphic3d_Camera::LookOrientation (const NCollection_Vec3<Elem_t>& theEye,
-                                        const NCollection_Vec3<Elem_t>& theLookAt,
+                                        const NCollection_Vec3<Elem_t>& theFwdDir,
                                         const NCollection_Vec3<Elem_t>& theUpDir,
                                         const NCollection_Vec3<Elem_t>& theAxialScale,
                                         NCollection_Mat4<Elem_t>& theOutMx)
 {
-  NCollection_Vec3<Elem_t> aForward = theLookAt - theEye;
+  NCollection_Vec3<Elem_t> aForward = theFwdDir;
   aForward.Normalize();
 
   // side = forward x up
@@ -1157,7 +1270,9 @@ bool Graphic3d_Camera::ZFitAll (const Standard_Real theScaleFactor,
     Standard_Real aDistance = aCamPln.Distance (aMeasurePnt);
 
     // Check if the camera is intruded into the scene.
-    if (aCamDir.IsOpposite (gp_Vec (aCamEye, aMeasurePnt), M_PI * 0.5))
+    gp_Vec aVecToMeasurePnt (aCamEye, aMeasurePnt);
+    if (aVecToMeasurePnt.Magnitude() > gp::Resolution()
+     && aCamDir.IsOpposite (aVecToMeasurePnt, M_PI * 0.5))
     {
       aDistance *= -1;
     }
@@ -1248,6 +1363,10 @@ bool Graphic3d_Camera::ZFitAll (const Standard_Real theScaleFactor,
     {
       // Clip zNear according to the minimum value matching the quality.
       aZNear = aZNearMin;
+      if (aZFar < aZNear)
+      {
+        aZFar = aZNear;
+      }
     }
     else
     {
@@ -1263,9 +1382,183 @@ bool Graphic3d_Camera::ZFitAll (const Standard_Real theScaleFactor,
     {
       aZNear = zEpsilon();
     }
+    Standard_ASSERT_RAISE (aZFar > aZNear, "ZFar should be greater than ZNear");
   }
 
   theZNear = aZNear;
   theZFar  = aZFar;
+  Standard_ASSERT_RAISE (aZFar > aZNear, "ZFar should be greater than ZNear");
   return true;
 }
+
+//=============================================================================
+//function : Interpolate
+//purpose  :
+//=============================================================================
+template<>
+Standard_EXPORT void NCollection_Lerp<Handle(Graphic3d_Camera)>::Interpolate (const double theT,
+                                                                              Handle(Graphic3d_Camera)& theCamera) const
+{
+  if (Abs (theT - 1.0) < Precision::Confusion())
+  {
+    // just copy end-point transformation
+    theCamera->Copy (myEnd);
+    return;
+  }
+
+  theCamera->Copy (myStart);
+  if (Abs (theT - 0.0) < Precision::Confusion())
+  {
+    return;
+  }
+
+  // apply rotation
+  {
+    gp_Ax3 aCamStart = cameraToAx3 (*myStart);
+    gp_Ax3 aCamEnd   = cameraToAx3 (*myEnd);
+    gp_Trsf aTrsfStart, aTrsfEnd;
+    aTrsfStart.SetTransformation (aCamStart, gp::XOY());
+    aTrsfEnd  .SetTransformation (aCamEnd,   gp::XOY());
+
+    gp_Quaternion aRotStart = aTrsfStart.GetRotation();
+    gp_Quaternion aRotEnd   = aTrsfEnd  .GetRotation();
+    gp_Quaternion aRotDelta = aRotEnd * aRotStart.Inverted();
+    gp_Quaternion aRot = gp_QuaternionNLerp::Interpolate (gp_Quaternion(), aRotDelta, theT);
+    gp_Trsf aTrsfRot;
+    aTrsfRot.SetRotation (aRot);
+    theCamera->Transform (aTrsfRot);
+  }
+
+  // apply translation
+  {
+    gp_XYZ aCenter  = NCollection_Lerp<gp_XYZ>::Interpolate (myStart->Center().XYZ(), myEnd->Center().XYZ(), theT);
+    gp_XYZ anEye    = NCollection_Lerp<gp_XYZ>::Interpolate (myStart->Eye().XYZ(),    myEnd->Eye().XYZ(),    theT);
+    gp_XYZ anAnchor = aCenter;
+    Standard_Real aKc = 0.0;
+
+    const Standard_Real aDeltaCenter = myStart->Center().Distance (myEnd->Center());
+    const Standard_Real aDeltaEye    = myStart->Eye()   .Distance (myEnd->Eye());
+    if (aDeltaEye <= gp::Resolution())
+    {
+      anAnchor = anEye;
+      aKc = 1.0;
+    }
+    else if (aDeltaCenter > gp::Resolution())
+    {
+      aKc = aDeltaCenter / (aDeltaCenter + aDeltaEye);
+
+      const gp_XYZ anAnchorStart = NCollection_Lerp<gp_XYZ>::Interpolate (myStart->Center().XYZ(), myStart->Eye().XYZ(), aKc);
+      const gp_XYZ anAnchorEnd   = NCollection_Lerp<gp_XYZ>::Interpolate (myEnd  ->Center().XYZ(), myEnd  ->Eye().XYZ(), aKc);
+      anAnchor = NCollection_Lerp<gp_XYZ>::Interpolate (anAnchorStart, anAnchorEnd, theT);
+    }
+
+    const gp_Vec        aDirEyeToCenter     = theCamera->Direction();
+    const Standard_Real aDistEyeCenterStart = myStart->Eye().Distance (myStart->Center());
+    const Standard_Real aDistEyeCenterEnd   = myEnd  ->Eye().Distance (myEnd  ->Center());
+    const Standard_Real aDistEyeCenter      = NCollection_Lerp<Standard_Real>::Interpolate (aDistEyeCenterStart, aDistEyeCenterEnd, theT);
+    aCenter = anAnchor + aDirEyeToCenter.XYZ() * aDistEyeCenter * aKc;
+    anEye   = anAnchor - aDirEyeToCenter.XYZ() * aDistEyeCenter * (1.0 - aKc);
+
+    theCamera->SetEyeAndCenter (anEye, aCenter);
+  }
+
+  // apply scaling
+  if (Abs(myStart->Scale() - myEnd->Scale()) > Precision::Confusion()
+   && myStart->IsOrthographic())
+  {
+    const Standard_Real aScale = NCollection_Lerp<Standard_Real>::Interpolate (myStart->Scale(), myEnd->Scale(), theT);
+    theCamera->SetScale (aScale);
+  }
+}
+
+//=======================================================================
+//function : FrustumPoints
+//purpose  :
+//=======================================================================
+void Graphic3d_Camera::FrustumPoints (NCollection_Array1<Graphic3d_Vec3d>& thePoints,
+                                      const Graphic3d_Mat4d& theModelWorld) const
+{
+  if (thePoints.Length() != FrustumVerticesNB)
+  {
+    thePoints.Resize (0, FrustumVerticesNB, Standard_False);
+  }
+
+  const Graphic3d_Mat4d& aProjectionMat = ProjectionMatrix();
+  const Graphic3d_Mat4d aWorldViewMat = OrientationMatrix() * theModelWorld;
+
+  Standard_Real nLeft = 0.0, nRight = 0.0, nTop = 0.0, nBottom = 0.0;
+  Standard_Real fLeft = 0.0, fRight = 0.0, fTop = 0.0, fBottom = 0.0;
+  Standard_Real aNear = 0.0, aFar = 0.0;
+  if (!IsOrthographic())
+  {
+    // handle perspective projection
+    aNear = aProjectionMat.GetValue (2, 3) / (-1.0 + aProjectionMat.GetValue (2, 2));
+    aFar  = aProjectionMat.GetValue (2, 3) / ( 1.0 + aProjectionMat.GetValue (2, 2));
+    // Near plane
+    nLeft   = aNear * (aProjectionMat.GetValue (0, 2) - 1.0) / aProjectionMat.GetValue (0, 0);
+    nRight  = aNear * (aProjectionMat.GetValue (0, 2) + 1.0) / aProjectionMat.GetValue (0, 0);
+    nTop    = aNear * (aProjectionMat.GetValue (1, 2) + 1.0) / aProjectionMat.GetValue (1, 1);
+    nBottom = aNear * (aProjectionMat.GetValue (1, 2) - 1.0) / aProjectionMat.GetValue (1, 1);
+    // Far plane
+    fLeft   = aFar  * (aProjectionMat.GetValue (0, 2) - 1.0) / aProjectionMat.GetValue (0, 0);
+    fRight  = aFar  * (aProjectionMat.GetValue (0, 2) + 1.0) / aProjectionMat.GetValue (0, 0);
+    fTop    = aFar  * (aProjectionMat.GetValue (1, 2) + 1.0) / aProjectionMat.GetValue (1, 1);
+    fBottom = aFar  * (aProjectionMat.GetValue (1, 2) - 1.0) / aProjectionMat.GetValue (1, 1);
+  }
+  else
+  {
+    // handle orthographic projection
+    aNear = (1.0 / aProjectionMat.GetValue (2, 2)) * (aProjectionMat.GetValue (2, 3) + 1.0);
+    aFar  = (1.0 / aProjectionMat.GetValue (2, 2)) * (aProjectionMat.GetValue (2, 3) - 1.0);
+    // Near plane
+    nLeft   = ( 1.0 + aProjectionMat.GetValue (0, 3)) / (-aProjectionMat.GetValue (0, 0));
+    fLeft   = nLeft;
+    nRight  = ( 1.0 - aProjectionMat.GetValue (0, 3)) /   aProjectionMat.GetValue (0, 0);
+    fRight  = nRight;
+    nTop    = ( 1.0 - aProjectionMat.GetValue (1, 3)) /   aProjectionMat.GetValue (1, 1);
+    fTop    = nTop;
+    nBottom = (-1.0 - aProjectionMat.GetValue (1, 3)) /   aProjectionMat.GetValue (1, 1);
+    fBottom = nBottom;
+  }
+
+  Graphic3d_Vec4d aLeftTopNear     (nLeft,  nTop,    -aNear, 1.0), aRightBottomFar (fRight, fBottom, -aFar, 1.0);
+  Graphic3d_Vec4d aLeftBottomNear  (nLeft,  nBottom, -aNear, 1.0), aRightTopFar    (fRight, fTop,    -aFar, 1.0);
+  Graphic3d_Vec4d aRightBottomNear (nRight, nBottom, -aNear, 1.0), aLeftTopFar     (fLeft,  fTop,    -aFar, 1.0);
+  Graphic3d_Vec4d aRightTopNear    (nRight, nTop,    -aNear, 1.0), aLeftBottomFar  (fLeft,  fBottom, -aFar, 1.0);
+
+  Graphic3d_Mat4d anInvWorldView;
+  aWorldViewMat.Inverted (anInvWorldView);
+
+  Graphic3d_Vec4d aTmpPnt;
+  aTmpPnt = anInvWorldView * aLeftTopNear;
+  thePoints.SetValue (FrustumVert_LeftTopNear,     aTmpPnt.xyz() / aTmpPnt.w());
+  aTmpPnt = anInvWorldView * aRightBottomFar;
+  thePoints.SetValue (FrustumVert_RightBottomFar,  aTmpPnt.xyz() / aTmpPnt.w());
+  aTmpPnt = anInvWorldView * aLeftBottomNear;
+  thePoints.SetValue (FrustumVert_LeftBottomNear,  aTmpPnt.xyz() / aTmpPnt.w());
+  aTmpPnt = anInvWorldView * aRightTopFar;
+  thePoints.SetValue (FrustumVert_RightTopFar,     aTmpPnt.xyz() / aTmpPnt.w());
+  aTmpPnt = anInvWorldView * aRightBottomNear;
+  thePoints.SetValue (FrustumVert_RightBottomNear, aTmpPnt.xyz() / aTmpPnt.w());
+  aTmpPnt = anInvWorldView * aLeftTopFar;
+  thePoints.SetValue (FrustumVert_LeftTopFar,      aTmpPnt.xyz() / aTmpPnt.w());
+  aTmpPnt = anInvWorldView * aRightTopNear;
+  thePoints.SetValue (FrustumVert_RightTopNear,    aTmpPnt.xyz() / aTmpPnt.w());
+  aTmpPnt = anInvWorldView * aLeftBottomFar;
+  thePoints.SetValue (FrustumVert_LeftBottomFar,   aTmpPnt.xyz() / aTmpPnt.w());
+}
+
+//=======================================================================
+//function : DumpJson
+//purpose  : 
+//=======================================================================
+void Graphic3d_Camera::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const
+{
+  OCCT_DUMP_TRANSIENT_CLASS_BEGIN (theOStream)
+
+  OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myUp)
+  OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myDirection)
+  OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myEye)
+
+  OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myDistance)
+}