0025675: Visualization - Fix problems and inefficiencies with frustum culling
[occt.git] / src / OpenGl / OpenGl_CappingAlgo.cxx
1 // Created on: 2013-09-05
2 // Created by: Anton POLETAEV
3 // Copyright (c) 2013-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 <OpenGl_CappingAlgo.hxx>
17 #include <OpenGl_Workspace.hxx>
18 #include <OpenGl_Context.hxx>
19 #include <OpenGl_PrimitiveArray.hxx>
20 #include <OpenGl_CappingPlaneResource.hxx>
21 #include <OpenGl_Vec.hxx>
22 #include <OpenGl_Structure.hxx>
23
24 IMPLEMENT_STANDARD_HANDLE(OpenGl_CappingAlgoFilter, OpenGl_RenderFilter)
25 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_CappingAlgoFilter, OpenGl_RenderFilter)
26
27 Handle(OpenGl_RenderFilter) OpenGl_CappingAlgo::myRenderFilter;
28 OpenGl_AspectFace OpenGl_CappingAlgo::myFrontCulling;
29 OpenGl_AspectFace OpenGl_CappingAlgo::myNoneCulling;
30 Standard_Boolean OpenGl_CappingAlgo::myIsInit = Standard_False;
31
32 namespace
33 {
34
35 #if !defined(GL_ES_VERSION_2_0)
36   static const GLint THE_FILLPRIM_FROM = GL_TRIANGLES;
37   static const GLint THE_FILLPRIM_TO   = GL_POLYGON;
38 #else
39   static const GLint THE_FILLPRIM_FROM = GL_TRIANGLES;
40   static const GLint THE_FILLPRIM_TO   = GL_TRIANGLE_FAN;
41 #endif
42
43   static const OpenGl_Vec4 THE_CAPPING_PLN_VERTS[12] =
44     { OpenGl_Vec4 ( 0.0f, 0.0f, 0.0f, 1.0f),
45       OpenGl_Vec4 ( 1.0f, 0.0f, 0.0f, 0.0f),
46       OpenGl_Vec4 ( 0.0f, 0.0f, 1.0f, 0.0f),
47       OpenGl_Vec4 ( 0.0f, 0.0f, 0.0f, 1.0f),
48       OpenGl_Vec4 ( 0.0f, 0.0f, 1.0f, 0.0f),
49       OpenGl_Vec4 (-1.0f, 0.0f, 0.0f, 0.0f),
50       OpenGl_Vec4 ( 0.0f, 0.0f, 0.0f, 1.0f),
51       OpenGl_Vec4 (-1.0f, 0.0f, 0.0f, 0.0f),
52       OpenGl_Vec4 ( 0.0f, 0.0f,-1.0f, 0.0f),
53       OpenGl_Vec4 ( 0.0f, 0.0f, 0.0f, 1.0f),
54       OpenGl_Vec4 ( 0.0f, 0.0f,-1.0f, 0.0f),
55       OpenGl_Vec4 ( 1.0f, 0.0f, 0.0f, 0.0f) };
56
57   static const OpenGl_Vec4 THE_CAPPING_PLN_TCOORD[12] =
58     { OpenGl_Vec4 ( 0.0f, 0.0f, 0.0f, 1.0f),
59       OpenGl_Vec4 ( 1.0f, 0.0f, 0.0f, 0.0f),
60       OpenGl_Vec4 ( 0.0f, 1.0f, 0.0f, 0.0f),
61       OpenGl_Vec4 ( 0.0f, 0.0f, 0.0f, 1.0f),
62       OpenGl_Vec4 ( 0.0f, 1.0f, 0.0f, 0.0f),
63       OpenGl_Vec4 (-1.0f, 0.0f, 0.0f, 0.0f),
64       OpenGl_Vec4 ( 0.0f, 0.0f, 0.0f, 1.0f),
65       OpenGl_Vec4 (-1.0f, 0.0f, 0.0f, 0.0f),
66       OpenGl_Vec4 ( 0.0f,-1.0f, 0.0f, 0.0f),
67       OpenGl_Vec4 ( 0.0f, 0.0f, 0.0f, 1.0f),
68       OpenGl_Vec4 ( 0.0f,-1.0f, 0.0f, 0.0f),
69       OpenGl_Vec4 ( 1.0f, 0.0f, 0.0f, 0.0f) };
70 }
71
72 // =======================================================================
73 // function : RenderCapping
74 // purpose  :
75 // =======================================================================
76 void OpenGl_CappingAlgo::RenderCapping (const Handle(OpenGl_Workspace)&  theWorkspace,
77                                         const Graphic3d_SequenceOfGroup& theGroups)
78 {
79   const Handle(OpenGl_Context)& aContext = theWorkspace->GetGlContext();
80
81   // check whether algorithm need to be performed
82   Standard_Boolean isCapping = Standard_False;
83   const Graphic3d_SequenceOfHClipPlane& aContextPlanes = aContext->Clipping().Planes();
84   Graphic3d_SequenceOfHClipPlane::Iterator aCappingIt (aContextPlanes);
85   for (; aCappingIt.More(); aCappingIt.Next())
86   {
87     const Handle(Graphic3d_ClipPlane)& aPlane = aCappingIt.Value();
88     if (aPlane->IsCapping())
89     {
90       isCapping = Standard_True;
91       break;
92     }
93   }
94
95   // do not perform algorithm is there is nothing to render
96   if (!isCapping)
97   {
98     return;
99   }
100
101   // init internal data
102   Init();
103
104   // remember current aspect face defined in workspace
105   const OpenGl_AspectFace* aFaceAsp = theWorkspace->AspectFace (Standard_False);
106
107   // replace primitive groups rendering filter
108   static Handle(OpenGl_CappingAlgoFilter) aCappingFilter = new OpenGl_CappingAlgoFilter();
109   Handle(OpenGl_RenderFilter) aRenderFilter = theWorkspace->GetRenderFilter();
110   theWorkspace->SetRenderFilter (aCappingFilter);
111
112   // prepare for rendering the clip planes
113   glEnable (GL_STENCIL_TEST);
114
115   // remember current state of depth
116   // function and change its value
117   GLint aDepthFuncPrev;
118   glGetIntegerv (GL_DEPTH_FUNC, &aDepthFuncPrev);
119   glDepthFunc (GL_LESS);
120
121   // generate capping for every clip plane
122   for (aCappingIt.Init (aContextPlanes); aCappingIt.More(); aCappingIt.Next())
123   {
124     // get plane being rendered
125     const Handle(Graphic3d_ClipPlane)& aRenderPlane = aCappingIt.Value();
126     if (!aRenderPlane->IsCapping())
127     {
128       continue;
129     }
130
131     // enable only the rendering plane to generate stencil mask
132     Graphic3d_SequenceOfHClipPlane::Iterator aPlaneIt (aContextPlanes);
133     for (; aPlaneIt.More(); aPlaneIt.Next())
134     {
135       const Handle(Graphic3d_ClipPlane)& aPlane = aPlaneIt.Value();
136       const Standard_Boolean isOn = (aPlane == aRenderPlane);
137       aContext->ChangeClipping().SetEnabled (aPlane, isOn);
138     }
139
140     glClear (GL_STENCIL_BUFFER_BIT);
141     glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
142
143     // override aspects, disable culling
144     theWorkspace->SetAspectFace (NoneCulling());
145     theWorkspace->AspectFace (Standard_True);
146
147     // evaluate number of pair faces
148     glDisable (GL_DEPTH_TEST);
149     glDepthMask (GL_FALSE);
150     glStencilFunc (GL_ALWAYS, 1, 0x01);
151     glStencilOp (GL_KEEP, GL_INVERT, GL_INVERT);
152
153     for (OpenGl_Structure::GroupIterator aGroupIt (theGroups); aGroupIt.More(); aGroupIt.Next())
154     {
155       if (aGroupIt.Value()->IsClosed())
156       {
157         aGroupIt.Value()->Render (theWorkspace);
158       }
159     }
160
161     // override material, cull back faces
162     theWorkspace->SetAspectFace (FrontCulling());
163     theWorkspace->AspectFace (Standard_True);
164
165     // enable all clip plane except the rendered one
166     for (aPlaneIt.Init (aContextPlanes); aPlaneIt.More(); aPlaneIt.Next())
167     {
168       const Handle(Graphic3d_ClipPlane)& aPlane = aPlaneIt.Value();
169       const Standard_Boolean isOn = (aPlane != aRenderPlane);
170       aContext->ChangeClipping().SetEnabled (aPlane, isOn);
171     }
172
173     // render capping plane using the generated stencil mask
174     glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
175     glDepthMask (GL_TRUE);
176     glStencilFunc (GL_EQUAL, 1, 0x01);
177     glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
178     glEnable (GL_DEPTH_TEST);
179
180     RenderPlane (theWorkspace, aRenderPlane);
181   }
182
183   // restore previous application state
184   glClear (GL_STENCIL_BUFFER_BIT);
185   glDepthFunc (aDepthFuncPrev);
186   glStencilFunc (GL_ALWAYS, 0, 0xFF);
187   glDisable (GL_STENCIL_TEST);
188
189   // enable clipping
190   for (aCappingIt.Init (aContextPlanes); aCappingIt.More(); aCappingIt.Next())
191   {
192     aContext->ChangeClipping().SetEnabled (aCappingIt.Value(), Standard_True);
193   }
194
195   // restore rendering aspects
196   theWorkspace->SetAspectFace (aFaceAsp);
197   theWorkspace->SetRenderFilter (aRenderFilter);
198 }
199
200 // =======================================================================
201 // function : RenderPlane
202 // purpose  :
203 // =======================================================================
204 void OpenGl_CappingAlgo::RenderPlane (const Handle(OpenGl_Workspace)& theWorkspace,
205                                       const Handle(Graphic3d_ClipPlane)& thePlane)
206 {
207   const Handle(OpenGl_Context)& aContext = theWorkspace->GetGlContext();
208
209   // get resource for the plane
210   TCollection_AsciiString aResId = thePlane->GetId();
211
212   Handle(OpenGl_CappingPlaneResource) aPlaneRes;
213   if (!aContext->GetResource (aResId, aPlaneRes))
214   {
215     // share and register for release once the resource is no longer used
216     aPlaneRes = new OpenGl_CappingPlaneResource (thePlane);
217     aContext->ShareResource (aResId, aPlaneRes);
218   }
219
220   aPlaneRes->Update (aContext);
221
222   const OpenGl_AspectFace* aFaceAspect = theWorkspace->AspectFace (Standard_False);
223   const OpenGl_AspectFace* aPlaneAspect = aPlaneRes->AspectFace();
224   if (aPlaneAspect != NULL)
225   {
226     theWorkspace->SetAspectFace (aPlaneAspect);
227   }
228
229   // apply aspect for rendering
230   theWorkspace->AspectFace (Standard_True);
231
232   // set identity model matrix
233   aContext->ModelWorldState.Push();
234   aContext->ModelWorldState.SetCurrent (OpenGl_Mat4::Map (*aPlaneRes->Orientation()->mat));
235   aContext->ApplyModelViewMatrix();
236
237 #if !defined(GL_ES_VERSION_2_0)
238   glNormal3f (0.0f, 1.0f, 0.0f);
239   glEnableClientState (GL_VERTEX_ARRAY);
240   glVertexPointer (4, GL_FLOAT, 0, (GLfloat* )&THE_CAPPING_PLN_VERTS);
241   glEnableClientState (GL_TEXTURE_COORD_ARRAY);
242   glTexCoordPointer (4, GL_FLOAT, 0, (GLfloat*)&THE_CAPPING_PLN_TCOORD);
243   glDrawArrays (GL_TRIANGLES, 0, 12);
244   glDisableClientState (GL_VERTEX_ARRAY);
245   glDisableClientState (GL_TEXTURE_COORD_ARRAY);
246 #endif
247
248   aContext->ModelWorldState.Pop();
249   aContext->ApplyModelViewMatrix();
250
251   theWorkspace->SetAspectFace (aFaceAspect);
252
253   // set delayed resource release
254   aPlaneRes.Nullify();
255   aContext->ReleaseResource (aResId, Standard_True);
256 }
257
258 // =======================================================================
259 // function : Init
260 // purpose  :
261 // =======================================================================
262 void OpenGl_CappingAlgo::Init()
263 {
264   if (myIsInit)
265     return;
266
267   myRenderFilter = new OpenGl_CappingAlgoFilter();
268   myNoneCulling.ChangeCullingMode() = TelCullNone;
269   myNoneCulling.ChangeEdge() = 0;
270
271   myFrontCulling.ChangeCullingMode() = TelCullBack;
272   myFrontCulling.ChangeEdge() = 0;
273
274   myIsInit = Standard_True;
275 }
276
277 // =======================================================================
278 // function : CanRender
279 // purpose  :
280 // =======================================================================
281 Standard_Boolean OpenGl_CappingAlgoFilter::CanRender (const OpenGl_Element* theElement)
282 {
283   const OpenGl_PrimitiveArray* aPArray = dynamic_cast<const OpenGl_PrimitiveArray*> (theElement);
284   return aPArray != NULL
285       && aPArray->DrawMode() >= THE_FILLPRIM_FROM
286       && aPArray->DrawMode() <= THE_FILLPRIM_TO;
287 }