0027764: Visualization - add functionality for animation of 3D camera and interactive...
[occt.git] / src / Graphic3d / Graphic3d_Camera.cxx
index f9f5c03..e46988d 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
@@ -61,6 +60,16 @@ namespace
     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(gp_Vec(theCamera.Center(), theCamera.Eye()));
+    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);
+  }
 }
 
 // =======================================================================
@@ -1298,3 +1307,84 @@ bool Graphic3d_Camera::ZFitAll (const Standard_Real theScaleFactor,
   theZFar  = aZFar;
   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->SetCenter (aCenter);
+    theCamera->SetEye    (anEye);
+  }
+
+  // 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);
+  }
+}