0029570: Visualization, Graphic3d_Aspect - merge Graphic3d_Group aspects
[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
18 #include <OpenGl_ClippingIterator.hxx>
19 #include <OpenGl_Workspace.hxx>
20 #include <OpenGl_Context.hxx>
21 #include <OpenGl_PrimitiveArray.hxx>
22 #include <OpenGl_CappingPlaneResource.hxx>
23 #include <OpenGl_Vec.hxx>
24 #include <OpenGl_Structure.hxx>
25 #include <OpenGl_ShaderManager.hxx>
26
27 namespace
28 {
29   //! Auxiliary sentry object managing stencil test.
30   struct StencilTestSentry
31   {
32     StencilTestSentry() : myDepthFuncPrev (0) {}
33
34     //! Restore previous application state.
35     ~StencilTestSentry()
36     {
37       if (myDepthFuncPrev != 0)
38       {
39         glClear (GL_STENCIL_BUFFER_BIT);
40         glDepthFunc (myDepthFuncPrev);
41         glStencilFunc (GL_ALWAYS, 0, 0xFF);
42         glDisable (GL_STENCIL_TEST);
43       }
44     }
45
46     //! Prepare for rendering the clip planes.
47     void Init()
48     {
49       if (myDepthFuncPrev == 0)
50       {
51         glEnable (GL_STENCIL_TEST);
52         glGetIntegerv (GL_DEPTH_FUNC, &myDepthFuncPrev);
53         glDepthFunc (GL_LESS);
54       }
55     }
56
57   private:
58     GLint myDepthFuncPrev;
59   };
60
61   //! Render infinite capping plane.
62   //! @param theWorkspace [in] the GL workspace, context state.
63   //! @param thePlane [in] the graphical plane, for which the capping surface is rendered.
64   static void renderPlane (const Handle(OpenGl_Workspace)& theWorkspace,
65                            const Handle(OpenGl_CappingPlaneResource)& thePlane)
66   {
67     const Handle(OpenGl_Context)& aContext = theWorkspace->GetGlContext();
68     const bool wasCullAllowed = theWorkspace->SetAllowFaceCulling (true);
69
70     // set identity model matrix
71     aContext->ModelWorldState.Push();
72     aContext->ModelWorldState.SetCurrent (OpenGl_Mat4::Map (*thePlane->Orientation()->mat));
73     aContext->ApplyModelViewMatrix();
74
75     thePlane->Primitives().Render (theWorkspace);
76
77     aContext->ModelWorldState.Pop();
78     aContext->ApplyModelViewMatrix();
79
80     theWorkspace->SetAllowFaceCulling (wasCullAllowed);
81   }
82
83   //! Render capping for specific structure.
84   static void renderCappingForStructure (StencilTestSentry& theStencilSentry,
85                                          const Handle(OpenGl_Workspace)& theWorkspace,
86                                          const OpenGl_Structure&         theStructure,
87                                          const Handle(Graphic3d_ClipPlane)& theClipChain,
88                                          const Standard_Integer          theSubPlaneIndex,
89                                          const Handle(OpenGl_CappingPlaneResource)& thePlane)
90   {
91     const Standard_Integer aPrevFilter = theWorkspace->RenderFilter();
92     const Standard_Integer anAnyFilter = aPrevFilter & ~(Standard_Integer )(OpenGl_RenderFilter_OpaqueOnly | OpenGl_RenderFilter_TransparentOnly);
93
94     const Handle(OpenGl_Context)&      aContext     = theWorkspace->GetGlContext();
95     const Handle(Graphic3d_ClipPlane)& aRenderPlane = thePlane->Plane();
96     for (OpenGl_Structure::GroupIterator aGroupIter (theStructure.Groups()); aGroupIter.More(); aGroupIter.Next())
97     {
98       if (!aGroupIter.Value()->IsClosed())
99       {
100         continue;
101       }
102
103       // clear stencil only if something has been actually drawn
104       theStencilSentry.Init();
105
106       // check if capping plane should be rendered within current pass (only opaque / only transparent)
107       const OpenGl_Aspects* anObjAspectFace = aRenderPlane->ToUseObjectProperties() ? aGroupIter.Value()->GlAspects() : NULL;
108       thePlane->Update (aContext, anObjAspectFace != NULL ? anObjAspectFace->Aspect() : Handle(Graphic3d_Aspects)());
109       theWorkspace->SetAspects (thePlane->AspectFace());
110       theWorkspace->SetRenderFilter (aPrevFilter);
111       if (!theWorkspace->ShouldRender (&thePlane->Primitives()))
112       {
113         continue;
114       }
115
116       // suppress only opaque/transparent filter since for filling stencil the whole geometry should be drawn
117       theWorkspace->SetRenderFilter (anAnyFilter);
118
119       // enable only the rendering plane to generate stencil mask
120       aContext->ChangeClipping().DisableAllExcept (theClipChain, theSubPlaneIndex);
121       aContext->ShaderManager()->UpdateClippingState();
122
123       glClear (GL_STENCIL_BUFFER_BIT);
124       const bool aColorMaskBack = aContext->SetColorMask (false);
125
126       // override aspects, disable culling
127       theWorkspace->SetAspects (&theWorkspace->NoneCulling());
128       theWorkspace->ApplyAspects();
129
130       // evaluate number of pair faces
131       if (theWorkspace->UseZBuffer())
132       {
133         glDisable (GL_DEPTH_TEST);
134       }
135       if (theWorkspace->UseDepthWrite())
136       {
137         glDepthMask (GL_FALSE);
138       }
139       glStencilFunc (GL_ALWAYS, 1, 0x01);
140       glStencilOp (GL_KEEP, GL_INVERT, GL_INVERT);
141
142       // render closed primitives
143       if (aRenderPlane->ToUseObjectProperties())
144       {
145         aGroupIter.Value()->Render (theWorkspace);
146       }
147       else
148       {
149         for (; aGroupIter.More(); aGroupIter.Next())
150         {
151           if (aGroupIter.Value()->IsClosed())
152           {
153             aGroupIter.Value()->Render (theWorkspace);
154           }
155         }
156       }
157
158       // override material, cull back faces
159       theWorkspace->SetAspects (&theWorkspace->FrontCulling());
160       theWorkspace->ApplyAspects();
161
162       // enable all clip plane except the rendered one
163       aContext->ChangeClipping().EnableAllExcept (theClipChain, theSubPlaneIndex);
164       aContext->ShaderManager()->UpdateClippingState();
165
166       // render capping plane using the generated stencil mask
167       aContext->SetColorMask (aColorMaskBack);
168       if (theWorkspace->UseDepthWrite())
169       {
170         glDepthMask (GL_TRUE);
171       }
172       glStencilFunc (GL_EQUAL, 1, 0x01);
173       glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
174       if (theWorkspace->UseZBuffer())
175       {
176         glEnable (GL_DEPTH_TEST);
177       }
178
179       theWorkspace->SetAspects (thePlane->AspectFace());
180       renderPlane (theWorkspace, thePlane);
181
182       // turn on the current plane to restore initial state
183       aContext->ChangeClipping().ResetCappingFilter();
184       aContext->ShaderManager()->RevertClippingState();
185       aContext->ShaderManager()->RevertClippingState();
186     }
187
188     if (theStructure.InstancedStructure() != NULL)
189     {
190       renderCappingForStructure (theStencilSentry, theWorkspace, *theStructure.InstancedStructure(), theClipChain, theSubPlaneIndex, thePlane);
191     }
192   }
193 }
194
195 // =======================================================================
196 // function : RenderCapping
197 // purpose  :
198 // =======================================================================
199 void OpenGl_CappingAlgo::RenderCapping (const Handle(OpenGl_Workspace)& theWorkspace,
200                                         const OpenGl_Structure&         theStructure)
201 {
202   const Handle(OpenGl_Context)& aContext = theWorkspace->GetGlContext();
203   if (!aContext->Clipping().IsCappingOn())
204   {
205     // do not perform algorithm if there is nothing to render
206     return;
207   }
208
209   // remember current aspect face defined in workspace
210   const OpenGl_Aspects* aFaceAsp = theWorkspace->Aspects();
211
212   // only filled primitives should be rendered
213   const Standard_Integer aPrevFilter = theWorkspace->RenderFilter();
214   theWorkspace->SetRenderFilter (aPrevFilter | OpenGl_RenderFilter_FillModeOnly);
215   StencilTestSentry aStencilSentry;
216
217   // generate capping for every clip plane
218   for (OpenGl_ClippingIterator aCappingIt (aContext->Clipping()); aCappingIt.More(); aCappingIt.Next())
219   {
220     // get plane being rendered
221     const Handle(Graphic3d_ClipPlane)& aClipChain = aCappingIt.Value();
222     if (!aClipChain->IsCapping()
223       || aCappingIt.IsDisabled())
224     {
225       continue;
226     }
227
228     Standard_Integer aSubPlaneIndex = 1;
229     for (const Graphic3d_ClipPlane* aSubPlaneIter = aClipChain.get(); aSubPlaneIter != NULL; aSubPlaneIter = aSubPlaneIter->ChainNextPlane().get(), ++aSubPlaneIndex)
230     {
231       // get resource for the plane
232       const TCollection_AsciiString& aResId = aSubPlaneIter->GetId();
233       Handle(OpenGl_CappingPlaneResource) aPlaneRes;
234       if (!aContext->GetResource (aResId, aPlaneRes))
235       {
236         // share and register for release once the resource is no longer used
237         aPlaneRes = new OpenGl_CappingPlaneResource (aSubPlaneIter);
238         aContext->ShareResource (aResId, aPlaneRes);
239       }
240
241       renderCappingForStructure (aStencilSentry, theWorkspace, theStructure, aClipChain, aSubPlaneIndex, aPlaneRes);
242
243       // set delayed resource release
244       aPlaneRes.Nullify();
245       aContext->ReleaseResource (aResId, Standard_True);
246     }
247   }
248
249   // restore rendering aspects
250   theWorkspace->SetAspects (aFaceAsp);
251   theWorkspace->SetRenderFilter (aPrevFilter);
252 }