From 30a1b24e197e467e809128196a9708c5243c1af9 Mon Sep 17 00:00:00 2001 From: osa Date: Mon, 17 Dec 2018 12:01:17 +0300 Subject: [PATCH] 0030412: Visualization, TKV3d - add presentation of camera frustum 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 | 285 +++++++++++++++++++ src/AIS/AIS_CameraFrustum.hxx | 85 ++++++ src/AIS/FILES | 2 + src/Graphic3d/Graphic3d_Camera.cxx | 76 +++++ src/Graphic3d/Graphic3d_Camera.hxx | 22 ++ src/OpenGl/OpenGl_BVHTreeSelector.cxx | 122 +++----- src/OpenGl/OpenGl_BVHTreeSelector.hxx | 85 +++--- src/ViewerTest/ViewerTest_ViewerCommands.cxx | 49 +++- tests/bugs/vis/bug30412 | 33 +++ 9 files changed, 637 insertions(+), 122 deletions(-) create mode 100644 src/AIS/AIS_CameraFrustum.cxx create mode 100644 src/AIS/AIS_CameraFrustum.hxx create mode 100644 tests/bugs/vis/bug30412 diff --git a/src/AIS/AIS_CameraFrustum.cxx b/src/AIS/AIS_CameraFrustum.cxx new file mode 100644 index 0000000000..82c5b98b40 --- /dev/null +++ b/src/AIS/AIS_CameraFrustum.cxx @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000000..ebad3afc6c --- /dev/null +++ b/src/AIS/AIS_CameraFrustum.hxx @@ -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 + +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 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 diff --git a/src/AIS/FILES b/src/AIS/FILES index 57ad772773..1928e82b57 100644 --- a/src/AIS/FILES +++ b/src/AIS/FILES @@ -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 diff --git a/src/Graphic3d/Graphic3d_Camera.cxx b/src/Graphic3d/Graphic3d_Camera.cxx index e46988d692..9525141bd2 100644 --- a/src/Graphic3d/Graphic3d_Camera.cxx +++ b/src/Graphic3d/Graphic3d_Camera.cxx @@ -1388,3 +1388,79 @@ Standard_EXPORT void NCollection_Lerp::Interpolate (co theCamera->SetScale (aScale); } } + +//======================================================================= +//function : FrustumPoints +//purpose : +//======================================================================= +void Graphic3d_Camera::FrustumPoints (NCollection_Array1& 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()); +} diff --git a/src/Graphic3d/Graphic3d_Camera.hxx b/src/Graphic3d/Graphic3d_Camera.hxx index 93b7cdb827..f532763ef2 100644 --- a/src/Graphic3d/Graphic3d_Camera.hxx +++ b/src/Graphic3d/Graphic3d_Camera.hxx @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -630,6 +631,27 @@ private: const NCollection_Vec3& theAxialScale, NCollection_Mat4& 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& thePoints) const; + private: gp_Dir myUp; //!< Camera up direction vector. diff --git a/src/OpenGl/OpenGl_BVHTreeSelector.cxx b/src/OpenGl/OpenGl_BVHTreeSelector.cxx index 396e6efa0a..a3bcd1edd3 100644 --- a/src/OpenGl/OpenGl_BVHTreeSelector.cxx +++ b/src/OpenGl/OpenGl_BVHTreeSelector.cxx @@ -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::max(); Standard_Real aMinProj = std::numeric_limits::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::max(); Standard_Real aMinProj = std::numeric_limits::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); } diff --git a/src/OpenGl/OpenGl_BVHTreeSelector.hxx b/src/OpenGl/OpenGl_BVHTreeSelector.hxx index c659e8bebd..6cf9cfe83e 100644 --- a/src/OpenGl/OpenGl_BVHTreeSelector.hxx +++ b/src/OpenGl/OpenGl_BVHTreeSelector.hxx @@ -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 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 diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx index 913cc34f32..69beafb549 100644 --- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx +++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -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 index 0000000000..bdf43163bf --- /dev/null +++ b/tests/bugs/vis/bug30412 @@ -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 -- 2.20.1