0030412: Visualization, TKV3d - add presentation of camera frustum
authorosa <osa@opencascade.com>
Mon, 17 Dec 2018 09:01:17 +0000 (12:01 +0300)
committerbugmaster <bugmaster@opencascade.com>
Fri, 11 Jan 2019 15:57:52 +0000 (18:57 +0300)
1) Added method Graphic3d_Camera::FrustumPoints() returning corner points of camera frustum.
2) Refactored methods OpenGl_BVHTreeSelector::isFullOut(...) and OpenGl_BVHTreeSelector::CacheClipPtsProjections()
3) Changed computation algorithm of frustum planes (build them by corner points)
4) Added interactive object AIS_CameraFrustum to draw camera frustum.
5) Extended Draw command "vcamera" with option displaying camera frustum.

src/AIS/AIS_CameraFrustum.cxx [new file with mode: 0644]
src/AIS/AIS_CameraFrustum.hxx [new file with mode: 0644]
src/AIS/FILES
src/Graphic3d/Graphic3d_Camera.cxx
src/Graphic3d/Graphic3d_Camera.hxx
src/OpenGl/OpenGl_BVHTreeSelector.cxx
src/OpenGl/OpenGl_BVHTreeSelector.hxx
src/ViewerTest/ViewerTest_ViewerCommands.cxx
tests/bugs/vis/bug30412 [new file with mode: 0644]

diff --git a/src/AIS/AIS_CameraFrustum.cxx b/src/AIS/AIS_CameraFrustum.cxx
new file mode 100644 (file)
index 0000000..82c5b98
--- /dev/null
@@ -0,0 +1,285 @@
+// Created on: 2018-12-12
+// Created by: Olga SURYANINOVA
+// Copyright (c) 2018 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <AIS_CameraFrustum.hxx>
+
+#include <AIS_DisplayMode.hxx>
+#include <Graphic3d_ArrayOfTriangles.hxx>
+#include <Graphic3d_ArrayOfSegments.hxx>
+#include <Prs3d_LineAspect.hxx>
+#include <Prs3d_ShadingAspect.hxx>
+#include <Select3D_SensitiveGroup.hxx>
+#include <Select3D_SensitivePrimitiveArray.hxx>
+#include <Select3D_SensitiveSegment.hxx>
+#include <SelectMgr_EntityOwner.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(AIS_CameraFrustum, AIS_InteractiveObject)
+
+namespace
+{
+  static const Standard_ShortReal THE_DEFAULT_TRANSPARENCY = 0.7f;
+  static const Quantity_Color     THE_DEFAULT_COLOR = Quantity_NOC_WHITE;
+}
+
+//=======================================================================
+//function : Constructor
+//purpose  :
+//=======================================================================
+AIS_CameraFrustum::AIS_CameraFrustum()
+: myPoints (0, Graphic3d_Camera::FrustumVerticesNB)
+{
+  myDrawer->SetLineAspect (new Prs3d_LineAspect (THE_DEFAULT_COLOR, Aspect_TOL_SOLID, 1.0));
+
+  Handle(Prs3d_ShadingAspect) aShadingAspect = new Prs3d_ShadingAspect();
+  aShadingAspect->SetMaterial (Graphic3d_NOM_PLASTIC);
+  aShadingAspect->Aspect()->SetAlphaMode (Graphic3d_AlphaMode_Blend);
+  aShadingAspect->SetTransparency (THE_DEFAULT_TRANSPARENCY);
+  aShadingAspect->SetColor (THE_DEFAULT_COLOR);
+  myDrawer->SetShadingAspect (aShadingAspect);
+
+  myDrawer->SetTransparency (THE_DEFAULT_TRANSPARENCY);
+  SetDisplayMode (AIS_Shaded);
+}
+
+//=======================================================================
+//function : AcceptDisplayMode
+//purpose  :
+//=======================================================================
+Standard_Boolean AIS_CameraFrustum::AcceptDisplayMode (const Standard_Integer theMode) const
+{
+  return theMode == AIS_Shaded || theMode == AIS_WireFrame;
+}
+
+//=======================================================================
+//function : SetCameraFrustum
+//purpose  :
+//=======================================================================
+void AIS_CameraFrustum::SetCameraFrustum (const Handle(Graphic3d_Camera)& theCamera)
+{
+  if (theCamera.IsNull())
+  {
+    return;
+  }
+
+  theCamera->FrustumPoints (myPoints);
+
+  fillTriangles();
+  fillBorders();
+
+  SetToUpdate();
+}
+
+//=======================================================================
+//function : SetColor
+//purpose  :
+//=======================================================================
+void AIS_CameraFrustum::SetColor (const Quantity_Color& theColor)
+{
+  AIS_InteractiveObject::SetColor (theColor);
+  myDrawer->ShadingAspect()->SetColor (theColor);
+  myDrawer->LineAspect()->SetColor (theColor);
+}
+
+//=======================================================================
+//function : UnsetColor
+//purpose  :
+//=======================================================================
+void AIS_CameraFrustum::UnsetColor()
+{
+  if (!HasColor())
+  {
+    return;
+  }
+
+  AIS_InteractiveObject::UnsetColor();
+
+  myDrawer->ShadingAspect()->SetColor (THE_DEFAULT_COLOR);
+  myDrawer->LineAspect()->SetColor (THE_DEFAULT_COLOR);
+}
+
+//=======================================================================
+//function : UnsetColor
+//purpose  :
+//=======================================================================
+void AIS_CameraFrustum::UnsetTransparency()
+{
+  myDrawer->ShadingAspect()->SetTransparency (0.0f);
+  myDrawer->SetTransparency (0.0f);
+}
+
+//=======================================================================
+//function : fillTriangles
+//purpose  :
+//=======================================================================
+void AIS_CameraFrustum::fillTriangles()
+{
+  if (myTriangles.IsNull())
+  {
+    const Standard_Integer aPlaneTriangleVertsNb = 2 * 3;
+    const Standard_Integer aPlanesNb             = 3 * 2;
+
+    myTriangles = new Graphic3d_ArrayOfTriangles (Graphic3d_Camera::FrustumVerticesNB, aPlaneTriangleVertsNb * aPlanesNb);
+    myTriangles->SetVertice (Graphic3d_Camera::FrustumVerticesNB, gp_Pnt (0.0, 0.0, 0.0));
+
+    // Triangles go in order (clockwise vertices traversing for correct normal):
+    // (0, 2, 1), (3, 1, 2)
+    const Standard_Integer aLookup1_clockwise[]     = { 0, 1, 0, 1, 0, 1 };
+    const Standard_Integer aLookup2_clockwise[]     = { 0, 0, 1, 1, 1, 0 };
+    // Triangles go in order (counterclockwise vertices traversing for correct normal):
+    // (1, 2, 0), (2, 1, 3)
+    const Standard_Integer aLookup1_anticlockwise[] = { 0, 1, 0, 1, 0, 1 };
+    const Standard_Integer aLookup2_anticlockwise[] = { 1, 0, 0, 0, 1, 1 };
+    Standard_Integer aShifts[]        = { 0, 0, 0 };
+
+    // Planes go in order:
+    // LEFT, RIGHT, BOTTOM, TOP, NEAR, FAR
+    for (Standard_Integer aFaceIdx = 0; aFaceIdx < 3; ++aFaceIdx)
+    {
+      for (Standard_Integer i = 0; i < 2; ++i)
+      {
+        for (Standard_Integer aPntIter = 0; aPntIter < aPlaneTriangleVertsNb; ++aPntIter)
+        {
+          aShifts[aFaceIdx] = i;
+          if (i == 0)
+          {
+            aShifts[(aFaceIdx + 1) % 3] = aLookup1_clockwise[aPntIter];
+            aShifts[(aFaceIdx + 2) % 3] = aLookup2_clockwise[aPntIter];
+          }
+          else
+          {
+            aShifts[(aFaceIdx + 1) % 3] = aLookup1_anticlockwise[aPntIter];
+            aShifts[(aFaceIdx + 2) % 3] = aLookup2_anticlockwise[aPntIter];
+          }
+
+          Standard_Integer anIndex = aShifts[0] * 2 * 2 + aShifts[1] * 2 + aShifts[2];
+          myTriangles->AddEdge (anIndex + 1);
+        }
+      }
+    }
+  }
+
+  for (Standard_Integer aPointIter = 0; aPointIter < Graphic3d_Camera::FrustumVerticesNB; ++aPointIter)
+  {
+    const Graphic3d_Vec3d aPnt = myPoints[aPointIter];
+    myTriangles->SetVertice (aPointIter + 1, gp_Pnt (aPnt.x(), aPnt.y(), aPnt.z()));
+  }
+}
+
+//=======================================================================
+//function : fillBorders
+//purpose  :
+//=======================================================================
+void AIS_CameraFrustum::fillBorders()
+{
+  if (myBorders.IsNull())
+  {
+    const Standard_Integer aPlaneSegmVertsNb = 2 * 4;
+    const Standard_Integer aPlanesNb         = 3 * 2;
+    myBorders = new Graphic3d_ArrayOfSegments (Graphic3d_Camera::FrustumVerticesNB, aPlaneSegmVertsNb * aPlanesNb);
+    myBorders->SetVertice (Graphic3d_Camera::FrustumVerticesNB, gp_Pnt (0.0, 0.0, 0.0));
+
+    // Segments go in order:
+    // (0, 2), (2, 3), (3, 1), (1, 0)
+    const Standard_Integer aLookup1[] = { 0, 1, 1, 1, 1, 0, 0, 0 };
+    const Standard_Integer aLookup2[] = { 0, 0, 0, 1, 1, 1, 1, 0 };
+    Standard_Integer aShifts[] = { 0, 0, 0 };
+
+    // Planes go in order:
+    // LEFT, RIGHT, BOTTOM, TOP, NEAR, FAR
+    for (Standard_Integer aFaceIdx = 0; aFaceIdx < 3; ++aFaceIdx)
+    {
+      for (Standard_Integer i = 0; i < 2; ++i)
+      {
+        for (Standard_Integer aSegmVertIter = 0; aSegmVertIter < aPlaneSegmVertsNb; ++aSegmVertIter)
+        {
+          aShifts[aFaceIdx] = i;
+          aShifts[(aFaceIdx + 1) % 3] = aLookup1[aSegmVertIter];
+          aShifts[(aFaceIdx + 2) % 3] = aLookup2[aSegmVertIter];
+
+          Standard_Integer anIndex = aShifts[0] * 2 * 2 + aShifts[1] * 2 + aShifts[2];
+          myBorders->AddEdge (anIndex + 1);
+        }
+      }
+    }
+  }
+
+  for (Standard_Integer aPointIter = 0; aPointIter < Graphic3d_Camera::FrustumVerticesNB; ++aPointIter)
+  {
+    const Graphic3d_Vec3d aPnt = myPoints[aPointIter];
+    myBorders->SetVertice (aPointIter + 1, gp_Pnt (aPnt.x(), aPnt.y(), aPnt.z()));
+  }
+}
+
+//=======================================================================
+//function : Compute
+//purpose  :
+//=======================================================================
+void AIS_CameraFrustum::Compute (const Handle(PrsMgr_PresentationManager3d)& ,
+                                 const Handle(Prs3d_Presentation)&           thePrs,
+                                 const Standard_Integer                      theMode)
+{
+  thePrs->SetInfiniteState (true);
+  if (myTriangles.IsNull())
+  {
+    return;
+  }
+
+  Handle(Graphic3d_Group) aGroup = thePrs->NewGroup();
+  switch (theMode)
+  {
+    case AIS_Shaded:
+    {
+      aGroup->SetGroupPrimitivesAspect (myDrawer->ShadingAspect()->Aspect());
+      aGroup->AddPrimitiveArray (myTriangles);
+    }
+    Standard_FALLTHROUGH
+    case AIS_WireFrame:
+    {
+      aGroup->SetGroupPrimitivesAspect (myDrawer->LineAspect()->Aspect());
+      aGroup->AddPrimitiveArray (myBorders);
+      break;
+    }
+  }
+}
+
+//=======================================================================
+//function : ComputeSelection
+//purpose  :
+//=======================================================================
+void AIS_CameraFrustum::ComputeSelection (const Handle(SelectMgr_Selection)& theSelection,
+                                          const Standard_Integer             theMode)
+{
+  Handle(SelectMgr_EntityOwner) anOwner = new SelectMgr_EntityOwner (this);
+  switch (theMode)
+  {
+    case SelectionMode_Edges:
+    {
+      Handle(Select3D_SensitiveGroup) aSensitiveEntity = new Select3D_SensitiveGroup (anOwner);
+      for (Standard_Integer anIter = 1; anIter <= myBorders->EdgeNumber(); anIter += 2)
+      {
+        aSensitiveEntity->Add (new Select3D_SensitiveSegment (anOwner, myBorders->Vertice (myBorders->Edge (anIter)), myBorders->Vertice(myBorders->Edge (anIter + 1))));
+      }
+      theSelection->Add (aSensitiveEntity);
+      break;
+    }
+    case SelectionMode_Volume:
+    {
+      Handle(Select3D_SensitivePrimitiveArray) aSelArray = new Select3D_SensitivePrimitiveArray (anOwner);
+      aSelArray->InitTriangulation (myTriangles->Attributes(), myTriangles->Indices(), TopLoc_Location());
+      theSelection->Add (aSelArray);
+      break;
+    }
+  }
+}
diff --git a/src/AIS/AIS_CameraFrustum.hxx b/src/AIS/AIS_CameraFrustum.hxx
new file mode 100644 (file)
index 0000000..ebad3af
--- /dev/null
@@ -0,0 +1,85 @@
+// Created on: 2018-12-12
+// Created by: Olga SURYANINOVA
+// Copyright (c) 2018 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _AIS_CameraFrustum_HeaderFile
+#define _AIS_CameraFrustum_HeaderFile
+
+#include <AIS_InteractiveObject.hxx>
+
+class Graphic3d_ArrayOfSegments;
+class Graphic3d_ArrayOfTriangles;
+
+//! Presentation for drawing camera frustum.
+//! Default configuration is built with filling and some transparency.
+class AIS_CameraFrustum : public AIS_InteractiveObject
+{
+  DEFINE_STANDARD_RTTIEXT(AIS_CameraFrustum, AIS_InteractiveObject)
+public:
+
+  //! Selection modes supported by this object
+  enum SelectionMode
+  {
+    SelectionMode_Edges  = 0, //!< detect by edges (default)
+    SelectionMode_Volume = 1, //!< detect by volume
+  };
+
+public:
+
+  //! Constructs camera frustum with default configuration.
+  Standard_EXPORT AIS_CameraFrustum();
+
+  //! Sets camera frustum.
+  Standard_EXPORT void SetCameraFrustum (const Handle(Graphic3d_Camera)& theCamera);
+
+  //! Setup custom color.
+  Standard_EXPORT virtual void SetColor (const Quantity_Color& theColor) Standard_OVERRIDE;
+
+  //! Restore default color.
+  Standard_EXPORT virtual void UnsetColor() Standard_OVERRIDE;
+
+  //! Restore transparency setting.
+  Standard_EXPORT virtual void UnsetTransparency() Standard_OVERRIDE;
+
+  //! Return true if specified display mode is supported.
+  Standard_EXPORT virtual Standard_Boolean AcceptDisplayMode (const Standard_Integer theMode) const Standard_OVERRIDE;
+
+protected:
+
+  //! Computes presentation of camera frustum.
+  Standard_EXPORT virtual void Compute (const Handle(PrsMgr_PresentationManager3d)& thePrsMgr,
+                                        const Handle(Prs3d_Presentation)&           thePrs,
+                                        const Standard_Integer                      theMode) Standard_OVERRIDE;
+
+  //! Compute selection.
+  Standard_EXPORT virtual void ComputeSelection (const Handle(SelectMgr_Selection)& theSelection,
+                                                 const Standard_Integer             theMode) Standard_OVERRIDE;
+
+private:
+
+  //! Fills triangles primitive array for camera frustum filling.
+  void fillTriangles();
+
+  //! Fills polylines primitive array for camera frustum borders.
+  void fillBorders();
+
+protected:
+
+  NCollection_Array1<Graphic3d_Vec3d> myPoints;    //!< Array of points
+  Handle(Graphic3d_ArrayOfTriangles)  myTriangles; //!< Triangles for camera frustum filling
+  Handle(Graphic3d_ArrayOfSegments)   myBorders;   //!< Segments for camera frustum borders
+
+};
+
+#endif // _AIS_CameraFrustum_HeaderFile
index 57ad772..1928e82 100644 (file)
@@ -19,6 +19,8 @@ AIS_BadEdgeFilter.cxx
 AIS_BadEdgeFilter.hxx
 AIS_C0RegularityFilter.cxx
 AIS_C0RegularityFilter.hxx
+AIS_CameraFrustum.cxx
+AIS_CameraFrustum.hxx
 AIS_Chamf2dDimension.cxx
 AIS_Chamf2dDimension.hxx
 AIS_Chamf2dDimension.lxx
index e46988d..9525141 100644 (file)
@@ -1388,3 +1388,79 @@ Standard_EXPORT void NCollection_Lerp<Handle(Graphic3d_Camera)>::Interpolate (co
     theCamera->SetScale (aScale);
   }
 }
+
+//=======================================================================
+//function : FrustumPoints
+//purpose  :
+//=======================================================================
+void Graphic3d_Camera::FrustumPoints (NCollection_Array1<Graphic3d_Vec3d>& thePoints) const
+{
+  if (thePoints.Length() != FrustumVerticesNB)
+  {
+    thePoints.Resize (0, FrustumVerticesNB, Standard_False);
+  }
+
+  const Graphic3d_Mat4d& aProjectionMat = ProjectionMatrix();
+  const Graphic3d_Mat4d& aWorldViewMat  = OrientationMatrix();
+
+  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());
+}
index 93b7cdb..f532763 100644 (file)
@@ -22,6 +22,7 @@
 #include <Graphic3d_Vec3.hxx>
 #include <Graphic3d_WorldViewProjState.hxx>
 #include <NCollection_Lerp.hxx>
+#include <NCollection_Array1.hxx>
 
 #include <gp_Dir.hxx>
 #include <gp_Pnt.hxx>
@@ -630,6 +631,27 @@ private:
                      const NCollection_Vec3<Elem_t>& theAxialScale,
                      NCollection_Mat4<Elem_t>&       theOutMx);
 
+public:
+
+  //! Enumerates vertices of view volume.
+  enum
+  {
+    FrustumVert_LeftBottomNear,
+    FrustumVert_LeftBottomFar,
+    FrustumVert_LeftTopNear,
+    FrustumVert_LeftTopFar,
+    FrustumVert_RightBottomNear,
+    FrustumVert_RightBottomFar,
+    FrustumVert_RightTopNear,
+    FrustumVert_RightTopFar,
+    FrustumVerticesNB
+  };
+
+  //! Fill array of current view frustum corners.
+  //! The size of this array is equal to FrustumVerticesNB.
+  //! The order of vertices is as defined in FrustumVert_* enumeration.
+  Standard_EXPORT void FrustumPoints (NCollection_Array1<Graphic3d_Vec3d>& thePoints) const;
+
 private:
 
   gp_Dir myUp;     //!< Camera up direction vector.
index 396e6ef..a3bcd1e 100644 (file)
@@ -24,7 +24,8 @@
 // purpose  :
 // =======================================================================
 OpenGl_BVHTreeSelector::OpenGl_BVHTreeSelector()
-: myIsProjectionParallel (Standard_True),
+: myClipVerts (0, Graphic3d_Camera::FrustumVerticesNB),
+  myIsProjectionParallel (Standard_True),
   myCamScale (1.0),
   myPixelSize (1.0)
 {
@@ -53,78 +54,37 @@ void OpenGl_BVHTreeSelector::SetViewVolume (const Handle(Graphic3d_Camera)& theC
              ? theCamera->Scale()
              : 2.0 * Tan (theCamera->FOVy() * M_PI / 360.0); // same as theCamera->Scale()/theCamera->Distance()
 
-  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 (!myIsProjectionParallel)
-  {
-    // handle perspective projection
-    aNear   = myProjectionMat.GetValue (2, 3) / (- 1.0 + myProjectionMat.GetValue (2, 2));
-    aFar    = myProjectionMat.GetValue (2, 3) / (  1.0 + myProjectionMat.GetValue (2, 2));
-    // Near plane
-    nLeft   = aNear * (myProjectionMat.GetValue (0, 2) - 1.0) / myProjectionMat.GetValue (0, 0);
-    nRight  = aNear * (myProjectionMat.GetValue (0, 2) + 1.0) / myProjectionMat.GetValue (0, 0);
-    nTop    = aNear * (myProjectionMat.GetValue (1, 2) + 1.0) / myProjectionMat.GetValue (1, 1);
-    nBottom = aNear * (myProjectionMat.GetValue (1, 2) - 1.0) / myProjectionMat.GetValue (1, 1);
-    // Far plane
-    fLeft   = aFar  * (myProjectionMat.GetValue (0, 2) - 1.0) / myProjectionMat.GetValue (0, 0);
-    fRight  = aFar  * (myProjectionMat.GetValue (0, 2) + 1.0) / myProjectionMat.GetValue (0, 0);
-    fTop    = aFar  * (myProjectionMat.GetValue (1, 2) + 1.0) / myProjectionMat.GetValue (1, 1);
-    fBottom = aFar  * (myProjectionMat.GetValue (1, 2) - 1.0) / myProjectionMat.GetValue (1, 1);
-  }
-  else
-  {
-    // handle orthographic projection
-    aNear   = (1.0 / myProjectionMat.GetValue (2, 2)) * (myProjectionMat.GetValue (2, 3) + 1.0);
-    aFar    = (1.0 / myProjectionMat.GetValue (2, 2)) * (myProjectionMat.GetValue (2, 3) - 1.0);
-    // Near plane
-    nLeft   = ( 1.0 + myProjectionMat.GetValue (0, 3)) / (-myProjectionMat.GetValue (0, 0));
-    fLeft   = nLeft;
-    nRight  = ( 1.0 - myProjectionMat.GetValue (0, 3)) /   myProjectionMat.GetValue (0, 0);
-    fRight  = nRight;
-    nTop    = ( 1.0 - myProjectionMat.GetValue (1, 3)) /   myProjectionMat.GetValue (1, 1);
-    fTop    = nTop;
-    nBottom = (-1.0 - myProjectionMat.GetValue (1, 3)) /   myProjectionMat.GetValue (1, 1);
-    fBottom = nBottom;
-  }
+  // Compute frustum points
+  theCamera->FrustumPoints (myClipVerts);
+
+  // Compute frustum planes
+  // Vertices go in order:
+  // 0, 2, 1
+  const Standard_Integer aLookup1[] = { 0, 1, 0 };
+  const Standard_Integer aLookup2[] = { 0, 0, 1 };
+  Standard_Integer aShifts[]        = { 0, 0, 0 };
 
-  OpenGl_Vec4d aLeftTopNear     (nLeft,  nTop,    -aNear, 1.0), aRightBottomFar (fRight, fBottom, -aFar, 1.0);
-  OpenGl_Vec4d aLeftBottomNear  (nLeft,  nBottom, -aNear, 1.0), aRightTopFar    (fRight, fTop,    -aFar, 1.0);
-  OpenGl_Vec4d aRightBottomNear (nRight, nBottom, -aNear, 1.0), aLeftTopFar     (fLeft,  fTop,    -aFar, 1.0);
-  OpenGl_Vec4d aRightTopNear    (nRight, nTop,    -aNear, 1.0), aLeftBottomFar  (fLeft,  fBottom, -aFar, 1.0);
-
-  const OpenGl_Mat4d aViewProj = myProjectionMat * myWorldViewMat;
-  OpenGl_Mat4d anInvWorldView;
-  myWorldViewMat.Inverted (anInvWorldView);
-
-  myClipVerts[ClipVert_LeftTopNear]     = anInvWorldView * aLeftTopNear;
-  myClipVerts[ClipVert_RightBottomFar]  = anInvWorldView * aRightBottomFar;
-  myClipVerts[ClipVert_LeftBottomNear]  = anInvWorldView * aLeftBottomNear;
-  myClipVerts[ClipVert_RightTopFar]     = anInvWorldView * aRightTopFar;
-  myClipVerts[ClipVert_RightBottomNear] = anInvWorldView * aRightBottomNear;
-  myClipVerts[ClipVert_LeftTopFar]      = anInvWorldView * aLeftTopFar;
-  myClipVerts[ClipVert_RightTopNear]    = anInvWorldView * aRightTopNear;
-  myClipVerts[ClipVert_LeftBottomFar]   = anInvWorldView * aLeftBottomFar;
-
-  // UNNORMALIZED!
-  myClipPlanes[Plane_Left]   = aViewProj.GetRow (3) + aViewProj.GetRow (0);
-  myClipPlanes[Plane_Right]  = aViewProj.GetRow (3) - aViewProj.GetRow (0);
-  myClipPlanes[Plane_Top]    = aViewProj.GetRow (3) - aViewProj.GetRow (1);
-  myClipPlanes[Plane_Bottom] = aViewProj.GetRow (3) + aViewProj.GetRow (1);
-  myClipPlanes[Plane_Near]   = aViewProj.GetRow (3) + aViewProj.GetRow (2);
-  myClipPlanes[Plane_Far]    = aViewProj.GetRow (3) - aViewProj.GetRow (2);
-
-  gp_Pnt aPtCenter = theCamera->Center();
-  OpenGl_Vec4d aCenter (aPtCenter.X(), aPtCenter.Y(), aPtCenter.Z(), 1.0);
-
-  for (Standard_Integer aPlaneIter = 0; aPlaneIter < PlanesNB; ++aPlaneIter)
+  // Planes go in order:
+  // LEFT, RIGHT, BOTTOM, TOP, NEAR, FAR
+  for (Standard_Integer aFaceIdx = 0; aFaceIdx < 3; ++aFaceIdx)
   {
-    OpenGl_Vec4d anEq = myClipPlanes[aPlaneIter];
-    if (SignedPlanePointDistance (anEq, aCenter) > 0)
+    for (Standard_Integer i = 0; i < 2; ++i)
     {
-      anEq *= -1.0;
-      myClipPlanes[aPlaneIter] = anEq;
-    }
+      OpenGl_Vec3d aPlanePnts[3];
+      for (Standard_Integer aPntIter = 0; aPntIter < 3; ++aPntIter)
+      {
+        aShifts[aFaceIdx] = i;
+        aShifts[(aFaceIdx + 1) % 3] = aLookup1[aPntIter];
+        aShifts[(aFaceIdx + 2) % 3] = aLookup2[aPntIter];
+        
+        aPlanePnts[aPntIter] = myClipVerts[aShifts[0] * 2 * 2 + aShifts[1] * 2 + aShifts[2]];
+      }
+      
+      myClipPlanes[aFaceIdx * 2 + i].Origin = aPlanePnts[0];
+      myClipPlanes[aFaceIdx * 2 + i].Normal =
+          OpenGl_Vec3d::Cross (aPlanePnts[1] - aPlanePnts[0],
+                               aPlanePnts[2] - aPlanePnts[0]).Normalized() * (i == 0 ? -1.f : 1.f);
+      }
   }
 }
 
@@ -202,17 +162,15 @@ void OpenGl_BVHTreeSelector::SetCullingSize (CullingContext& theCtx,
 // =======================================================================
 void OpenGl_BVHTreeSelector::CacheClipPtsProjections()
 {
+  // project frustum onto its own normals
   const Standard_Integer anIncFactor = myIsProjectionParallel ? 2 : 1;
-  for (Standard_Integer aPlaneIter = 0; aPlaneIter < 5; aPlaneIter += anIncFactor)
+  for (Standard_Integer aPlaneIter = 0; aPlaneIter < PlanesNB - 1; aPlaneIter += anIncFactor)
   {
-    const OpenGl_Vec4d aPlane = myClipPlanes[aPlaneIter];
     Standard_Real aMaxProj = -std::numeric_limits<Standard_Real>::max();
     Standard_Real aMinProj =  std::numeric_limits<Standard_Real>::max();
-    for (Standard_Integer aCornerIter = 0; aCornerIter < ClipVerticesNB; ++aCornerIter)
+    for (Standard_Integer aCornerIter = 0; aCornerIter < Graphic3d_Camera::FrustumVerticesNB; ++aCornerIter)
     {
-      Standard_Real aProjection = aPlane.x() * myClipVerts[aCornerIter].x()
-                                + aPlane.y() * myClipVerts[aCornerIter].y()
-                                + aPlane.z() * myClipVerts[aCornerIter].z();
+      Standard_Real aProjection = myClipVerts[aCornerIter].Dot (myClipPlanes[aPlaneIter].Normal);
       aMaxProj = Max (aProjection, aMaxProj);
       aMinProj = Min (aProjection, aMinProj);
     }
@@ -220,17 +178,17 @@ void OpenGl_BVHTreeSelector::CacheClipPtsProjections()
     myMinClipProjectionPts[aPlaneIter] = aMinProj;
   }
 
+  // project frustum onto main axes
+  OpenGl_Vec3d anAxes[] = { OpenGl_Vec3d (1.0, 0.0, 0.0),
+                            OpenGl_Vec3d (0.0, 1.0, 0.0),
+                            OpenGl_Vec3d (0.0, 0.0, 1.0) };
   for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
   {
     Standard_Real aMaxProj = -std::numeric_limits<Standard_Real>::max();
     Standard_Real aMinProj =  std::numeric_limits<Standard_Real>::max();
-    for (Standard_Integer aCornerIter = 0; aCornerIter < ClipVerticesNB; ++aCornerIter)
+    for (Standard_Integer aCornerIter = 0; aCornerIter < Graphic3d_Camera::FrustumVerticesNB; ++aCornerIter)
     {
-      Standard_Real aProjection = aDim == 0
-                                ? myClipVerts[aCornerIter].x()
-                                : (aDim == 1
-                                 ? myClipVerts[aCornerIter].y()
-                                 : myClipVerts[aCornerIter].z());
+      Standard_Real aProjection = myClipVerts[aCornerIter].Dot (anAxes[aDim]);
       aMaxProj = Max (aProjection, aMaxProj);
       aMinProj = Min (aProjection, aMinProj);
     }
index c659e8b..6cf9cfe 100644 (file)
@@ -35,6 +35,25 @@ public:
     //! Empty constructor.
     CullingContext() : DistCull (-1.0), SizeCull2 (-1.0) {}
   };
+
+  //! Auxiliary structure representing 3D plane.
+  struct Plane
+  {
+    //! Creates default plane.
+    Plane()
+    : Origin (0.0, 0.0, 0.0),
+      Normal (0.0, 0.0, 1.0) {}
+
+    //! Creates plane with specific parameters.
+    Plane (const OpenGl_Vec3d& theOrigin,
+           const OpenGl_Vec3d& theNormal)
+    : Origin (theOrigin),
+      Normal (theNormal) {}
+
+    OpenGl_Vec3d Origin;
+    OpenGl_Vec3d Normal;
+  };
+
 public:
 
   //! Creates an empty selector object with parallel projection type by default.
@@ -124,46 +143,54 @@ protected:
     //   /
     //    E2
 
-    // E0 test
+    // E0 test (x axis)
     if (theMinPt.x() > myMaxOrthoProjectionPts[0]
      || theMaxPt.x() < myMinOrthoProjectionPts[0])
     {
       return true;
     }
 
-    // E1 test
+    // E1 test (y axis)
     if (theMinPt.y() > myMaxOrthoProjectionPts[1]
      || theMaxPt.y() < myMinOrthoProjectionPts[1])
     {
       return true;
     }
 
-    // E2 test
+    // E2 test (z axis)
     if (theMinPt.z() > myMaxOrthoProjectionPts[2]
      || theMaxPt.z() < myMinOrthoProjectionPts[2])
     {
       return true;
     }
 
-    Standard_Real aBoxProjMax = 0.0, aBoxProjMin = 0.0;
     const Standard_Integer anIncFactor = myIsProjectionParallel ? 2 : 1;
-    for (Standard_Integer aPlaneIter = 0; aPlaneIter < 5; aPlaneIter += anIncFactor)
+    for (Standard_Integer aPlaneIter = 0; aPlaneIter < PlanesNB - 1; aPlaneIter += anIncFactor)
     {
-      OpenGl_Vec4d aPlane = myClipPlanes[aPlaneIter];
-      aBoxProjMax = (aPlane.x() > 0.0 ? (aPlane.x() * theMaxPt.x()) : aPlane.x() * theMinPt.x())
-                  + (aPlane.y() > 0.0 ? (aPlane.y() * theMaxPt.y()) : aPlane.y() * theMinPt.y())
-                  + (aPlane.z() > 0.0 ? (aPlane.z() * theMaxPt.z()) : aPlane.z() * theMinPt.z());
-      if (aBoxProjMax > myMinClipProjectionPts[aPlaneIter]
-       && aBoxProjMax < myMaxClipProjectionPts[aPlaneIter])
+      // frustum normals
+      const OpenGl_Vec3d anAxis = myClipPlanes[aPlaneIter].Normal;
+
+      const OpenGl_Vec3d aPVertex (anAxis.x() > 0.0 ? theMaxPt.x() : theMinPt.x(),
+                                   anAxis.y() > 0.0 ? theMaxPt.y() : theMinPt.y(),
+                                   anAxis.z() > 0.0 ? theMaxPt.z() : theMinPt.z());
+      Standard_Real aPnt0 = aPVertex.Dot (anAxis);
+
+      if (aPnt0 >= myMinClipProjectionPts[aPlaneIter]
+       && aPnt0 <= myMaxClipProjectionPts[aPlaneIter])
       {
         continue;
       }
-
-      aBoxProjMin = (aPlane.x() < 0.0 ? aPlane.x() * theMaxPt.x() : aPlane.x() * theMinPt.x())
-                  + (aPlane.y() < 0.0 ? aPlane.y() * theMaxPt.y() : aPlane.y() * theMinPt.y())
-                  + (aPlane.z() < 0.0 ? aPlane.z() * theMaxPt.z() : aPlane.z() * theMinPt.z());
-      if (aBoxProjMin > myMaxClipProjectionPts[aPlaneIter]
-       || aBoxProjMax < myMinClipProjectionPts[aPlaneIter])
+      
+      const OpenGl_Vec3d aNVertex (anAxis.x() > 0.0 ? theMinPt.x() : theMaxPt.x(),
+                                   anAxis.y() > 0.0 ? theMinPt.y() : theMaxPt.y(),
+                                   anAxis.z() > 0.0 ? theMinPt.z() : theMaxPt.z());
+      Standard_Real aPnt1 = aNVertex.Dot (anAxis);
+
+      const Standard_Real aMin = aPnt0 < aPnt1 ? aPnt0 : aPnt1;
+      const Standard_Real aMax = aPnt0 > aPnt1 ? aPnt0 : aPnt1;
+      
+      if (aMin > myMaxClipProjectionPts[aPlaneIter]
+       || aMax < myMinClipProjectionPts[aPlaneIter])
       {
         return true;
       }
@@ -215,38 +242,24 @@ protected:
   //! Enumerates planes of view volume.
   enum
   {
-    Plane_Top,
-    Plane_Bottom,
     Plane_Left,
     Plane_Right,
+    Plane_Bottom,
+    Plane_Top,
     Plane_Near,
     Plane_Far,
     PlanesNB
   };
 
-  //! Enumerates vertices of view volume.
-  enum
-  {
-    ClipVert_LeftTopNear,
-    ClipVert_LeftBottomNear,
-    ClipVert_RightTopNear,
-    ClipVert_RightBottomNear,
-    ClipVert_LeftTopFar,
-    ClipVert_LeftBottomFar,
-    ClipVert_RightTopFar,
-    ClipVert_RightBottomFar,
-    ClipVerticesNB
-  };
-
 protected:
 
-  OpenGl_Vec4d myClipPlanes[PlanesNB];      //!< Plane equations
-  OpenGl_Vec4d myClipVerts[ClipVerticesNB]; //!< Vertices
+  Plane                            myClipPlanes[PlanesNB]; //!< Planes
+  NCollection_Array1<OpenGl_Vec3d> myClipVerts;            //!< Vertices
 
   Handle(Graphic3d_Camera) myCamera; //!< camera definition
 
   // for caching clip points projections onto viewing area normals once per traverse
-  // ORDER: TOP, BOTTOM, LEFT, RIGHT, NEAR, FAR
+  // ORDER: LEFT, RIGHT, BOTTOM, TOP, NEAR, FAR
   Standard_Real myMaxClipProjectionPts[PlanesNB]; //!< Max view volume's vertices projections onto its normals
   Standard_Real myMinClipProjectionPts[PlanesNB]; //!< Min view volume's vertices projections onto its normals
 
index 913cc34..69beafb 100644 (file)
@@ -19,6 +19,7 @@
 #include <AIS_Animation.hxx>
 #include <AIS_AnimationCamera.hxx>
 #include <AIS_AnimationObject.hxx>
+#include <AIS_CameraFrustum.hxx>
 #include <AIS_ColorScale.hxx>
 #include <AIS_Manipulator.hxx>
 #include <AIS_RubberBand.hxx>
@@ -8974,6 +8975,7 @@ static int VCamera (Draw_Interpretor& theDI,
     return 0;
   }
 
+  TCollection_AsciiString aPrsName;
   for (Standard_Integer anArgIter = 1; anArgIter < theArgsNb; ++anArgIter)
   {
     Standard_CString        anArg = theArgVec[anArgIter];
@@ -9133,6 +9135,11 @@ static int VCamera (Draw_Interpretor& theDI,
       }
       theDI << aCamera->FOVy() << " ";
     }
+    else if (aPrsName.IsEmpty()
+         && !anArgCase.StartsWith ("-"))
+    {
+      aPrsName = anArg;
+    }
     else
     {
       std::cout << "Error: unknown argument '" << anArg << "'\n";
@@ -9140,8 +9147,41 @@ static int VCamera (Draw_Interpretor& theDI,
     }
   }
 
-  aView->AutoZFit();
-  aView->Redraw();
+  if (aPrsName.IsEmpty()
+   || theArgsNb > 2)
+  {
+    aView->AutoZFit();
+    aView->Redraw();
+  }
+
+  if (!aPrsName.IsEmpty())
+  {
+    Handle(AIS_CameraFrustum) aCameraFrustum;
+    if (GetMapOfAIS().IsBound2 (aPrsName))
+    {
+      // find existing object
+      aCameraFrustum = Handle(AIS_CameraFrustum)::DownCast (GetMapOfAIS().Find2 (theArgVec[1]));
+      if (aCameraFrustum.IsNull())
+      {
+        std::cout << "Error: object '" << aPrsName << "'is already defined and is not a camera frustum!\n";
+        return 1;
+      }
+    }
+
+    if (aCameraFrustum.IsNull())
+    {
+      aCameraFrustum = new AIS_CameraFrustum();
+    }
+    else
+    {
+      // not include displayed object of old camera frustum in the new one.
+      ViewerTest::GetAISContext()->Erase (aCameraFrustum, false);
+      aView->ZFitAll();
+    }
+    aCameraFrustum->SetCameraFrustum (aView->Camera());
+
+    ViewerTest::Display (aPrsName, aCameraFrustum);
+  }
 
   return 0;
 }
@@ -12404,13 +12444,14 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
     "vnbselected"
     "\n\t\t: Returns number of selected objects", __FILE__, VNbSelected, group);
   theCommands.Add ("vcamera",
-              "vcamera [-ortho] [-projtype]"
+              "vcamera [PrsName] [-ortho] [-projtype]"
       "\n\t\t:         [-persp]"
       "\n\t\t:         [-fovy   [Angle]] [-distance [Distance]]"
       "\n\t\t:         [-stereo] [-leftEye] [-rightEye]"
       "\n\t\t:         [-iod [Distance]] [-iodType    [absolute|relative]]"
       "\n\t\t:         [-zfocus [Value]] [-zfocusType [absolute|relative]]"
-      "\n\t\t: Manage camera parameters."
+      "\n\t\t: Manages camera parameters."
+      "\n\t\t: Displays frustum when presntation name PrsName is specified."
       "\n\t\t: Prints current value when option called without argument."
       "\n\t\t: Orthographic camera:"
       "\n\t\t:   -ortho      activate orthographic projection"
diff --git a/tests/bugs/vis/bug30412 b/tests/bugs/vis/bug30412
new file mode 100644 (file)
index 0000000..bdf4316
--- /dev/null
@@ -0,0 +1,33 @@
+puts "============="
+puts "0030412: Visualization, TKV3d - add presentation of camera frustum"
+puts "============="
+
+pload MODELING VISUALIZATION
+vclear
+vinit View1
+
+vfront
+vcamera -persp
+
+set THE_NB_BOXES 5
+puts "Creating [expr $THE_NB_BOXES * $THE_NB_BOXES * $THE_NB_BOXES] boxes..."
+for {set i 0} {$i < $THE_NB_BOXES} {incr i} {
+  for {set j 0} {$j < $THE_NB_BOXES} {incr j} {
+    for {set k 0} {$k < $THE_NB_BOXES} {incr k} {
+      box b$i$j$k 3.*$i 3.*$j 3.*$k 1 1 1
+      vdisplay -noupdate -dispMode 1 b$i$j$k
+    }
+  }
+}
+
+vfit
+vzoom 1.5
+vcamera cam
+
+vright
+vfit
+vdump $::imagedir/${::casename}_cam_right.png
+
+vaxo
+vfit
+vdump $::imagedir/${::casename}_cam_axo.png
\ No newline at end of file