0030223: Visualization, TKOpenGl - frustum culling does not clip objects within persp...
[occt.git] / src / OpenGl / OpenGl_BVHTreeSelector.cxx
1 // Created on: 2013-12-25
2 // Created by: Varvara POSKONINA
3 // Copyright (c) 1999-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <limits>
17
18 #include <OpenGl_BVHTreeSelector.hxx>
19 #include <OpenGl_BVHClipPrimitiveSet.hxx>
20 #include <Graphic3d_GraphicDriver.hxx>
21
22 // =======================================================================
23 // function : OpenGl_BVHTreeSelector
24 // purpose  :
25 // =======================================================================
26 OpenGl_BVHTreeSelector::OpenGl_BVHTreeSelector()
27 : myIsProjectionParallel (Standard_True),
28   myCamScale (1.0),
29   myPixelSize (1.0)
30 {
31   //
32 }
33
34 // =======================================================================
35 // function : SetViewVolume
36 // purpose  : Retrieves view volume's planes equations and its vertices from projection and world-view matrices.
37 // =======================================================================
38 void OpenGl_BVHTreeSelector::SetViewVolume (const Handle(Graphic3d_Camera)& theCamera)
39 {
40   if (!myWorldViewProjState.IsChanged (theCamera->WorldViewProjState()))
41     return;
42
43   myIsProjectionParallel = theCamera->IsOrthographic();
44   const gp_Dir aCamDir = theCamera->Direction();
45
46   myCamera             = theCamera;
47   myProjectionMat      = theCamera->ProjectionMatrix();
48   myWorldViewMat       = theCamera->OrientationMatrix();
49   myWorldViewProjState = theCamera->WorldViewProjState();
50   myCamEye.SetValues (theCamera->Eye().X(), theCamera->Eye().Y(), theCamera->Eye().Z());
51   myCamDir.SetValues (aCamDir.X(), aCamDir.Y(), aCamDir.Z());
52   myCamScale = theCamera->IsOrthographic()
53              ? theCamera->Scale()
54              : 2.0 * Tan (theCamera->FOVy() * M_PI / 360.0); // same as theCamera->Scale()/theCamera->Distance()
55
56   Standard_Real nLeft = 0.0, nRight = 0.0, nTop = 0.0, nBottom = 0.0;
57   Standard_Real fLeft = 0.0, fRight = 0.0, fTop = 0.0, fBottom = 0.0;
58   Standard_Real aNear = 0.0, aFar   = 0.0;
59   if (!myIsProjectionParallel)
60   {
61     // handle perspective projection
62     aNear   = myProjectionMat.GetValue (2, 3) / (- 1.0 + myProjectionMat.GetValue (2, 2));
63     aFar    = myProjectionMat.GetValue (2, 3) / (  1.0 + myProjectionMat.GetValue (2, 2));
64     // Near plane
65     nLeft   = aNear * (myProjectionMat.GetValue (0, 2) - 1.0) / myProjectionMat.GetValue (0, 0);
66     nRight  = aNear * (myProjectionMat.GetValue (0, 2) + 1.0) / myProjectionMat.GetValue (0, 0);
67     nTop    = aNear * (myProjectionMat.GetValue (1, 2) + 1.0) / myProjectionMat.GetValue (1, 1);
68     nBottom = aNear * (myProjectionMat.GetValue (1, 2) - 1.0) / myProjectionMat.GetValue (1, 1);
69     // Far plane
70     fLeft   = aFar  * (myProjectionMat.GetValue (0, 2) - 1.0) / myProjectionMat.GetValue (0, 0);
71     fRight  = aFar  * (myProjectionMat.GetValue (0, 2) + 1.0) / myProjectionMat.GetValue (0, 0);
72     fTop    = aFar  * (myProjectionMat.GetValue (1, 2) + 1.0) / myProjectionMat.GetValue (1, 1);
73     fBottom = aFar  * (myProjectionMat.GetValue (1, 2) - 1.0) / myProjectionMat.GetValue (1, 1);
74   }
75   else
76   {
77     // handle orthographic projection
78     aNear   = (1.0 / myProjectionMat.GetValue (2, 2)) * (myProjectionMat.GetValue (2, 3) + 1.0);
79     aFar    = (1.0 / myProjectionMat.GetValue (2, 2)) * (myProjectionMat.GetValue (2, 3) - 1.0);
80     // Near plane
81     nLeft   = ( 1.0 + myProjectionMat.GetValue (0, 3)) / (-myProjectionMat.GetValue (0, 0));
82     fLeft   = nLeft;
83     nRight  = ( 1.0 - myProjectionMat.GetValue (0, 3)) /   myProjectionMat.GetValue (0, 0);
84     fRight  = nRight;
85     nTop    = ( 1.0 - myProjectionMat.GetValue (1, 3)) /   myProjectionMat.GetValue (1, 1);
86     fTop    = nTop;
87     nBottom = (-1.0 - myProjectionMat.GetValue (1, 3)) /   myProjectionMat.GetValue (1, 1);
88     fBottom = nBottom;
89   }
90
91   OpenGl_Vec4d aLeftTopNear     (nLeft,  nTop,    -aNear, 1.0), aRightBottomFar (fRight, fBottom, -aFar, 1.0);
92   OpenGl_Vec4d aLeftBottomNear  (nLeft,  nBottom, -aNear, 1.0), aRightTopFar    (fRight, fTop,    -aFar, 1.0);
93   OpenGl_Vec4d aRightBottomNear (nRight, nBottom, -aNear, 1.0), aLeftTopFar     (fLeft,  fTop,    -aFar, 1.0);
94   OpenGl_Vec4d aRightTopNear    (nRight, nTop,    -aNear, 1.0), aLeftBottomFar  (fLeft,  fBottom, -aFar, 1.0);
95
96   const OpenGl_Mat4d aViewProj = myProjectionMat * myWorldViewMat;
97   OpenGl_Mat4d anInvWorldView;
98   myWorldViewMat.Inverted (anInvWorldView);
99
100   myClipVerts[ClipVert_LeftTopNear]     = anInvWorldView * aLeftTopNear;
101   myClipVerts[ClipVert_RightBottomFar]  = anInvWorldView * aRightBottomFar;
102   myClipVerts[ClipVert_LeftBottomNear]  = anInvWorldView * aLeftBottomNear;
103   myClipVerts[ClipVert_RightTopFar]     = anInvWorldView * aRightTopFar;
104   myClipVerts[ClipVert_RightBottomNear] = anInvWorldView * aRightBottomNear;
105   myClipVerts[ClipVert_LeftTopFar]      = anInvWorldView * aLeftTopFar;
106   myClipVerts[ClipVert_RightTopNear]    = anInvWorldView * aRightTopNear;
107   myClipVerts[ClipVert_LeftBottomFar]   = anInvWorldView * aLeftBottomFar;
108
109   // UNNORMALIZED!
110   myClipPlanes[Plane_Left]   = aViewProj.GetRow (3) + aViewProj.GetRow (0);
111   myClipPlanes[Plane_Right]  = aViewProj.GetRow (3) - aViewProj.GetRow (0);
112   myClipPlanes[Plane_Top]    = aViewProj.GetRow (3) - aViewProj.GetRow (1);
113   myClipPlanes[Plane_Bottom] = aViewProj.GetRow (3) + aViewProj.GetRow (1);
114   myClipPlanes[Plane_Near]   = aViewProj.GetRow (3) + aViewProj.GetRow (2);
115   myClipPlanes[Plane_Far]    = aViewProj.GetRow (3) - aViewProj.GetRow (2);
116
117   gp_Pnt aPtCenter = theCamera->Center();
118   OpenGl_Vec4d aCenter (aPtCenter.X(), aPtCenter.Y(), aPtCenter.Z(), 1.0);
119
120   for (Standard_Integer aPlaneIter = 0; aPlaneIter < PlanesNB; ++aPlaneIter)
121   {
122     OpenGl_Vec4d anEq = myClipPlanes[aPlaneIter];
123     if (SignedPlanePointDistance (anEq, aCenter) > 0)
124     {
125       anEq *= -1.0;
126       myClipPlanes[aPlaneIter] = anEq;
127     }
128   }
129 }
130
131 // =======================================================================
132 // function : SetViewportSize
133 // purpose  :
134 // =======================================================================
135 void OpenGl_BVHTreeSelector::SetViewportSize (Standard_Integer theViewportWidth,
136                                               Standard_Integer theViewportHeight,
137                                               Standard_Real theResolutionRatio)
138 {
139   myViewportHeight = theViewportHeight;
140   myViewportWidth  = theViewportWidth;
141   myPixelSize = Max (theResolutionRatio / theViewportHeight,
142                      theResolutionRatio / theViewportWidth);
143 }
144
145 // =======================================================================
146 // function : SignedPlanePointDistance
147 // purpose  :
148 // =======================================================================
149 Standard_Real OpenGl_BVHTreeSelector::SignedPlanePointDistance (const OpenGl_Vec4d& theNormal,
150                                                                 const OpenGl_Vec4d& thePnt)
151 {
152   const Standard_Real aNormLength = std::sqrt (theNormal.x() * theNormal.x()
153                                              + theNormal.y() * theNormal.y()
154                                              + theNormal.z() * theNormal.z());
155
156   if (aNormLength < gp::Resolution())
157     return 0.0;
158
159   const Standard_Real anInvNormLength = 1.0 / aNormLength;
160   const Standard_Real aD  = theNormal.w() * anInvNormLength;
161   const Standard_Real anA = theNormal.x() * anInvNormLength;
162   const Standard_Real aB  = theNormal.y() * anInvNormLength;
163   const Standard_Real aC  = theNormal.z() * anInvNormLength;
164   return aD + (anA * thePnt.x() + aB * thePnt.y() + aC * thePnt.z());
165 }
166
167 // =======================================================================
168 // function : SetCullingDistance
169 // purpose  :
170 // =======================================================================
171 void OpenGl_BVHTreeSelector::SetCullingDistance (CullingContext& theCtx,
172                                                  Standard_Real theDistance) const
173 {
174   theCtx.DistCull = -1.0;
175   if (!myIsProjectionParallel)
176   {
177     theCtx.DistCull = theDistance > 0.0 && !Precision::IsInfinite (theDistance)
178                     ? theDistance
179                     : -1.0;
180   }
181 }
182
183 // =======================================================================
184 // function : SetCullingSize
185 // purpose  :
186 // =======================================================================
187 void OpenGl_BVHTreeSelector::SetCullingSize (CullingContext& theCtx,
188                                              Standard_Real theSize) const
189 {
190   theCtx.SizeCull2 = -1.0;
191   if (theSize > 0.0 && !Precision::IsInfinite (theSize))
192   {
193     theCtx.SizeCull2 = myPixelSize * theSize;
194     theCtx.SizeCull2 *= myCamScale;
195     theCtx.SizeCull2 *= theCtx.SizeCull2;
196   }
197 }
198
199 // =======================================================================
200 // function : CacheClipPtsProjections
201 // purpose  :
202 // =======================================================================
203 void OpenGl_BVHTreeSelector::CacheClipPtsProjections()
204 {
205   const Standard_Integer anIncFactor = myIsProjectionParallel ? 2 : 1;
206   for (Standard_Integer aPlaneIter = 0; aPlaneIter < 5; aPlaneIter += anIncFactor)
207   {
208     const OpenGl_Vec4d aPlane = myClipPlanes[aPlaneIter];
209     Standard_Real aMaxProj = -std::numeric_limits<Standard_Real>::max();
210     Standard_Real aMinProj =  std::numeric_limits<Standard_Real>::max();
211     for (Standard_Integer aCornerIter = 0; aCornerIter < ClipVerticesNB; ++aCornerIter)
212     {
213       Standard_Real aProjection = aPlane.x() * myClipVerts[aCornerIter].x()
214                                 + aPlane.y() * myClipVerts[aCornerIter].y()
215                                 + aPlane.z() * myClipVerts[aCornerIter].z();
216       aMaxProj = Max (aProjection, aMaxProj);
217       aMinProj = Min (aProjection, aMinProj);
218     }
219     myMaxClipProjectionPts[aPlaneIter] = aMaxProj;
220     myMinClipProjectionPts[aPlaneIter] = aMinProj;
221   }
222
223   for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
224   {
225     Standard_Real aMaxProj = -std::numeric_limits<Standard_Real>::max();
226     Standard_Real aMinProj =  std::numeric_limits<Standard_Real>::max();
227     for (Standard_Integer aCornerIter = 0; aCornerIter < ClipVerticesNB; ++aCornerIter)
228     {
229       Standard_Real aProjection = aDim == 0
230                                 ? myClipVerts[aCornerIter].x()
231                                 : (aDim == 1
232                                  ? myClipVerts[aCornerIter].y()
233                                  : myClipVerts[aCornerIter].z());
234       aMaxProj = Max (aProjection, aMaxProj);
235       aMinProj = Min (aProjection, aMinProj);
236     }
237     myMaxOrthoProjectionPts[aDim] = aMaxProj;
238     myMinOrthoProjectionPts[aDim] = aMinProj;
239   }
240 }