0029570: Visualization, Graphic3d_Aspect - merge Graphic3d_Group aspects
[occt.git] / src / OpenGl / OpenGl_Structure.cxx
1 // Created on: 2011-08-01
2 // Created by: Sergey ZERCHANINOV
3 // Copyright (c) 2011-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_Context.hxx>
18 #include <OpenGl_GlCore11.hxx>
19 #include <OpenGl_ClippingIterator.hxx>
20 #include <OpenGl_GraphicDriver.hxx>
21 #include <OpenGl_ShaderManager.hxx>
22 #include <OpenGl_ShaderProgram.hxx>
23 #include <OpenGl_StructureShadow.hxx>
24 #include <OpenGl_Vec.hxx>
25 #include <OpenGl_View.hxx>
26 #include <OpenGl_Workspace.hxx>
27
28 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Structure,Graphic3d_CStructure)
29
30 // =======================================================================
31 // function : renderBoundingBox
32 // purpose  :
33 // =======================================================================
34 void OpenGl_Structure::renderBoundingBox (const Handle(OpenGl_Workspace)& theWorkspace) const
35 {
36   if (!myBndBox.IsValid())
37   {
38     return;
39   }
40
41   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
42   const Handle(OpenGl_TextureSet) aPrevTexture = aCtx->BindTextures (Handle(OpenGl_TextureSet)());
43   const Graphic3d_ZLayerSettings& aLayer = myGraphicDriver->ZLayerSettings (myZLayer);
44   const Graphic3d_Vec3d aMoveVec = myTrsfPers.IsNull()
45                                && !aLayer.OriginTransformation().IsNull()
46                                  ? -Graphic3d_Vec3d (aLayer.Origin().X(), aLayer.Origin().Y(), aLayer.Origin().Z())
47                                  :  Graphic3d_Vec3d (0.0, 0.0, 0.0);
48   if (aCtx->core20fwd != NULL
49    && aCtx->ShaderManager()->BindBoundBoxProgram())
50   {
51     const Graphic3d_Vec3d aCenter = myBndBox.Center() + aMoveVec;
52     const Graphic3d_Vec3d aSize   = myBndBox.Size();
53     aCtx->ActiveProgram()->SetUniform (aCtx, "occBBoxCenter", Graphic3d_Vec3 ((float )aCenter.x(), (float )aCenter.y(), (float )aCenter.z()));
54     aCtx->ActiveProgram()->SetUniform (aCtx, "occBBoxSize",   Graphic3d_Vec3 ((float )aSize.x(),   (float )aSize.y(),   (float )aSize.z()));
55     aCtx->SetColor4fv (theWorkspace->InteriorColor());
56
57     const Handle(OpenGl_VertexBuffer)& aBoundBoxVertBuffer = aCtx->ShaderManager()->BoundBoxVertBuffer();
58     aBoundBoxVertBuffer->BindAttribute  (aCtx, Graphic3d_TOA_POS);
59     aCtx->core20fwd->glDrawArrays (GL_LINES, 0, aBoundBoxVertBuffer->GetElemsNb());
60     aBoundBoxVertBuffer->UnbindAttribute(aCtx, Graphic3d_TOA_POS);
61   }
62 #if !defined(GL_ES_VERSION_2_0)
63   else if (aCtx->core11 != NULL)
64   {
65     const Graphic3d_Vec3d aMind = myBndBox.CornerMin() + aMoveVec;
66     const Graphic3d_Vec3d aMaxd = myBndBox.CornerMax() + aMoveVec;
67     const Graphic3d_Vec3 aMin ((float )aMind.x(), (float )aMind.y(), (float )aMind.z());
68     const Graphic3d_Vec3 aMax ((float )aMaxd.x(), (float )aMaxd.y(), (float )aMaxd.z());
69     const OpenGl_Vec3 aVerts[16] =
70     {
71       OpenGl_Vec3 (aMin.x(), aMin.y(), aMin.z()),
72       OpenGl_Vec3 (aMin.x(), aMin.y(), aMax.z()),
73       OpenGl_Vec3 (aMin.x(), aMax.y(), aMax.z()),
74       OpenGl_Vec3 (aMin.x(), aMax.y(), aMin.z()),
75       OpenGl_Vec3 (aMin.x(), aMin.y(), aMin.z()),
76       OpenGl_Vec3 (aMax.x(), aMin.y(), aMin.z()),
77       OpenGl_Vec3 (aMax.x(), aMin.y(), aMax.z()),
78       OpenGl_Vec3 (aMax.x(), aMax.y(), aMax.z()),
79       OpenGl_Vec3 (aMax.x(), aMax.y(), aMin.z()),
80       OpenGl_Vec3 (aMax.x(), aMin.y(), aMin.z()),
81       OpenGl_Vec3 (aMax.x(), aMax.y(), aMin.z()),
82       OpenGl_Vec3 (aMin.x(), aMax.y(), aMin.z()),
83       OpenGl_Vec3 (aMin.x(), aMax.y(), aMax.z()),
84       OpenGl_Vec3 (aMax.x(), aMax.y(), aMax.z()),
85       OpenGl_Vec3 (aMax.x(), aMin.y(), aMax.z()),
86       OpenGl_Vec3 (aMin.x(), aMin.y(), aMax.z())
87     };
88
89     aCtx->ShaderManager()->BindLineProgram (Handle(OpenGl_TextureSet)(), Aspect_TOL_SOLID, Graphic3d_TOSM_UNLIT, Graphic3d_AlphaMode_Opaque, false, Handle(OpenGl_ShaderProgram)());
90     aCtx->SetColor4fv (theWorkspace->InteriorColor());
91     aCtx->core11fwd->glDisable (GL_LIGHTING);
92     aCtx->core11->glEnableClientState (GL_VERTEX_ARRAY);
93     aCtx->core11->glVertexPointer (3, GL_FLOAT, 0, aVerts[0].GetData());
94     aCtx->core11fwd->glDrawArrays (GL_LINE_STRIP, 0, 16);
95     aCtx->core11->glDisableClientState (GL_VERTEX_ARRAY);
96   }
97 #endif
98   aCtx->BindTextures (aPrevTexture);
99 }
100
101 // =======================================================================
102 // function : OpenGl_Structure
103 // purpose  :
104 // =======================================================================
105 OpenGl_Structure::OpenGl_Structure (const Handle(Graphic3d_StructureManager)& theManager)
106 : Graphic3d_CStructure (theManager),
107   myInstancedStructure (NULL),
108   myIsRaytracable      (Standard_False),
109   myModificationState  (0),
110   myIsCulled           (Standard_True),
111   myIsMirrored         (Standard_False)
112 {
113   updateLayerTransformation();
114 }
115
116 // =======================================================================
117 // function : ~OpenGl_Structure
118 // purpose  :
119 // =======================================================================
120 OpenGl_Structure::~OpenGl_Structure()
121 {
122   Release (Handle(OpenGl_Context)());
123 }
124
125 // =======================================================================
126 // function : SetZLayer
127 // purpose  :
128 // =======================================================================
129 void OpenGl_Structure::SetZLayer (const Graphic3d_ZLayerId theLayerIndex)
130 {
131   Graphic3d_CStructure::SetZLayer (theLayerIndex);
132   updateLayerTransformation();
133 }
134
135 // =======================================================================
136 // function : SetTransformation
137 // purpose  :
138 // =======================================================================
139 void OpenGl_Structure::SetTransformation (const Handle(Geom_Transformation)& theTrsf)
140 {
141   myTrsf = theTrsf;
142   myIsMirrored = Standard_False;
143   if (!myTrsf.IsNull())
144   {
145     // Determinant of transform matrix less then 0 means that mirror transform applied.
146     const Standard_Real aDet = myTrsf->Value(1, 1) * (myTrsf->Value (2, 2) * myTrsf->Value (3, 3) - myTrsf->Value (3, 2) * myTrsf->Value (2, 3))
147                              - myTrsf->Value(1, 2) * (myTrsf->Value (2, 1) * myTrsf->Value (3, 3) - myTrsf->Value (3, 1) * myTrsf->Value (2, 3))
148                              + myTrsf->Value(1, 3) * (myTrsf->Value (2, 1) * myTrsf->Value (3, 2) - myTrsf->Value (3, 1) * myTrsf->Value (2, 2));
149     myIsMirrored = aDet < 0.0;
150   }
151
152   updateLayerTransformation();
153   if (IsRaytracable())
154   {
155     ++myModificationState;
156   }
157 }
158
159 // =======================================================================
160 // function : SetTransformPersistence
161 // purpose  :
162 // =======================================================================
163 void OpenGl_Structure::SetTransformPersistence (const Handle(Graphic3d_TransformPers)& theTrsfPers)
164 {
165   myTrsfPers = theTrsfPers;
166   updateLayerTransformation();
167 }
168
169 // =======================================================================
170 // function : updateLayerTransformation
171 // purpose  :
172 // =======================================================================
173 void OpenGl_Structure::updateLayerTransformation()
174 {
175   gp_Trsf aRenderTrsf;
176   if (!myTrsf.IsNull())
177   {
178     aRenderTrsf = myTrsf->Trsf();
179   }
180
181   const Graphic3d_ZLayerSettings& aLayer = myGraphicDriver->ZLayerSettings (myZLayer);
182   if (!aLayer.OriginTransformation().IsNull()
183     && myTrsfPers.IsNull())
184   {
185     aRenderTrsf.SetTranslationPart (aRenderTrsf.TranslationPart() - aLayer.Origin());
186   }
187   aRenderTrsf.GetMat4 (myRenderTrsf);
188 }
189
190 // =======================================================================
191 // function : GraphicHighlight
192 // purpose  :
193 // =======================================================================
194 void OpenGl_Structure::GraphicHighlight (const Handle(Graphic3d_PresentationAttributes)& theStyle)
195 {
196   myHighlightStyle = theStyle;
197   highlight = 1;
198 }
199
200 // =======================================================================
201 // function : GraphicUnhighlight
202 // purpose  :
203 // =======================================================================
204 void OpenGl_Structure::GraphicUnhighlight()
205 {
206   highlight = 0;
207   myHighlightStyle.Nullify();
208 }
209
210 // =======================================================================
211 // function : OnVisibilityChanged
212 // purpose  :
213 // =======================================================================
214 void OpenGl_Structure::OnVisibilityChanged()
215 {
216   if (IsRaytracable())
217   {
218     ++myModificationState;
219   }
220 }
221
222 // =======================================================================
223 // function : IsRaytracable
224 // purpose  :
225 // =======================================================================
226 Standard_Boolean OpenGl_Structure::IsRaytracable() const
227 {
228   if (!myGroups.IsEmpty()
229     && myIsRaytracable)
230   {
231     return Standard_True;
232   }
233
234   return myInstancedStructure != NULL
235      &&  myInstancedStructure->IsRaytracable();
236 }
237
238 // =======================================================================
239 // function : UpdateRaytracableState
240 // purpose  :
241 // =======================================================================
242 void OpenGl_Structure::UpdateStateIfRaytracable (const Standard_Boolean toCheck) const
243 {
244   myIsRaytracable = !toCheck;
245   if (!myIsRaytracable)
246   {
247     for (OpenGl_Structure::GroupIterator anIter (myGroups); anIter.More(); anIter.Next())
248     {
249       if (anIter.Value()->IsRaytracable())
250       {
251         myIsRaytracable = Standard_True;
252         break;
253       }
254     }
255   }
256
257   if (IsRaytracable())
258   {
259     ++myModificationState;
260   }
261 }
262
263 // =======================================================================
264 // function : Connect
265 // purpose  :
266 // =======================================================================
267 void OpenGl_Structure::Connect (Graphic3d_CStructure& theStructure)
268 {
269   OpenGl_Structure* aStruct = static_cast<OpenGl_Structure*> (&theStructure);
270
271   Standard_ASSERT_RAISE (myInstancedStructure == NULL || myInstancedStructure == aStruct,
272     "Error! Instanced structure is already defined");
273
274   myInstancedStructure = aStruct;
275
276   if (aStruct->IsRaytracable())
277   {
278     UpdateStateIfRaytracable (Standard_False);
279   }
280 }
281
282 // =======================================================================
283 // function : Disconnect
284 // purpose  :
285 // =======================================================================
286 void OpenGl_Structure::Disconnect (Graphic3d_CStructure& theStructure)
287 {
288   OpenGl_Structure* aStruct = static_cast<OpenGl_Structure*> (&theStructure);
289
290   if (myInstancedStructure == aStruct)
291   {
292     myInstancedStructure = NULL;
293
294     if (aStruct->IsRaytracable())
295     {
296       UpdateStateIfRaytracable();
297     }
298   }
299 }
300
301 // =======================================================================
302 // function : NewGroup
303 // purpose  :
304 // =======================================================================
305 Handle(Graphic3d_Group) OpenGl_Structure::NewGroup (const Handle(Graphic3d_Structure)& theStruct)
306 {
307   Handle(OpenGl_Group) aGroup = new OpenGl_Group (theStruct);
308   myGroups.Append (aGroup);
309   return aGroup;
310 }
311
312 // =======================================================================
313 // function : RemoveGroup
314 // purpose  :
315 // =======================================================================
316 void OpenGl_Structure::RemoveGroup (const Handle(Graphic3d_Group)& theGroup)
317 {
318   if (theGroup.IsNull())
319   {
320     return;
321   }
322
323   for (Graphic3d_SequenceOfGroup::Iterator aGroupIter (myGroups); aGroupIter.More(); aGroupIter.Next())
324   {
325     // Check for the given group
326     if (aGroupIter.Value() == theGroup)
327     {
328       const Standard_Boolean wasRaytracable =
329         static_cast<const OpenGl_Group&> (*theGroup).IsRaytracable();
330
331       theGroup->Clear (Standard_False);
332
333       if (wasRaytracable)
334       {
335         UpdateStateIfRaytracable();
336       }
337
338       myGroups.Remove (aGroupIter);
339       return;
340     }
341   }
342 }
343
344 // =======================================================================
345 // function : Clear
346 // purpose  :
347 // =======================================================================
348 void OpenGl_Structure::Clear()
349 {
350   Clear (GlDriver()->GetSharedContext());
351 }
352
353 // =======================================================================
354 // function : Clear
355 // purpose  :
356 // =======================================================================
357 void OpenGl_Structure::Clear (const Handle(OpenGl_Context)& theGlCtx)
358 {
359   Standard_Boolean aRaytracableGroupDeleted (Standard_False);
360
361   // Release groups
362   for (OpenGl_Structure::GroupIterator aGroupIter (myGroups); aGroupIter.More(); aGroupIter.Next())
363   {
364     aRaytracableGroupDeleted |= aGroupIter.Value()->IsRaytracable();
365
366     // Delete objects
367     aGroupIter.ChangeValue()->Release (theGlCtx);
368   }
369   myGroups.Clear();
370
371   if (aRaytracableGroupDeleted)
372   {
373     myIsRaytracable = Standard_False;
374   }
375
376   Is2dText       = Standard_False;
377   IsForHighlight = Standard_False;
378 }
379
380 // =======================================================================
381 // function : renderGeometry
382 // purpose  :
383 // =======================================================================
384 void OpenGl_Structure::renderGeometry (const Handle(OpenGl_Workspace)& theWorkspace,
385                                        bool&                           theHasClosed) const
386 {
387   if (myInstancedStructure != NULL)
388   {
389     myInstancedStructure->renderGeometry (theWorkspace, theHasClosed);
390   }
391
392   for (OpenGl_Structure::GroupIterator aGroupIter (myGroups); aGroupIter.More(); aGroupIter.Next())
393   {
394     theHasClosed = theHasClosed || aGroupIter.Value()->IsClosed();
395     aGroupIter.Value()->Render (theWorkspace);
396   }
397 }
398
399 // =======================================================================
400 // function : Render
401 // purpose  :
402 // =======================================================================
403 void OpenGl_Structure::Render (const Handle(OpenGl_Workspace) &theWorkspace) const
404 {
405   // Process the structure only if visible
406   if (!visible)
407   {
408     return;
409   }
410
411   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
412
413   // Render named status
414   if (highlight && !myHighlightStyle.IsNull() && myHighlightStyle->Method() != Aspect_TOHM_BOUNDBOX)
415   {
416     theWorkspace->SetHighlightStyle (myHighlightStyle);
417   }
418
419   // Apply local transformation
420   aCtx->ModelWorldState.Push();
421   OpenGl_Mat4& aModelWorld = aCtx->ModelWorldState.ChangeCurrent();
422   aModelWorld = myRenderTrsf;
423
424   const Standard_Boolean anOldGlNormalize = aCtx->IsGlNormalizeEnabled();
425 #if !defined(GL_ES_VERSION_2_0)
426   // detect scale transform
427   if (aCtx->core11 != NULL
428   && !myTrsf.IsNull())
429   {
430     const Standard_Real aScale = myTrsf->ScaleFactor();
431     if (Abs (aScale - 1.0) > Precision::Confusion())
432     {
433       aCtx->SetGlNormalizeEnabled (Standard_True);
434     }
435   }
436 #endif
437
438   if (!myTrsfPers.IsNull())
439   {
440     aCtx->WorldViewState.Push();
441     OpenGl_Mat4& aWorldView = aCtx->WorldViewState.ChangeCurrent();
442     myTrsfPers->Apply (theWorkspace->View()->Camera(),
443                        aCtx->ProjectionState.Current(), aWorldView,
444                        aCtx->VirtualViewport()[2], aCtx->VirtualViewport()[3]);
445
446   #if !defined(GL_ES_VERSION_2_0)
447     if (!aCtx->IsGlNormalizeEnabled()
448       && aCtx->core11 != NULL)
449     {
450       const Standard_Real aScale = Graphic3d_TransformUtils::ScaleFactor<Standard_ShortReal> (aWorldView);
451       if (Abs (aScale - 1.0) > Precision::Confusion())
452       {
453         aCtx->SetGlNormalizeEnabled (Standard_True);
454       }
455     }
456   #endif
457   }
458
459   // Take into account transform persistence
460   aCtx->ApplyModelViewMatrix();
461
462   // remember aspects
463   const OpenGl_Aspects* aPrevAspectFace = theWorkspace->Aspects();
464
465   // Apply correction for mirror transform
466   if (myIsMirrored)
467   {
468     aCtx->core11fwd->glFrontFace (GL_CW);
469   }
470
471   // Collect clipping planes of structure scope
472   aCtx->ChangeClipping().SetLocalPlanes (myClipPlanes);
473
474   // True if structure is fully clipped
475   bool isClipped = false;
476   bool hasDisabled = false;
477   if (aCtx->Clipping().IsClippingOrCappingOn())
478   {
479     const Graphic3d_BndBox3d& aBBox = BoundingBox();
480     if (!myClipPlanes.IsNull()
481       && myClipPlanes->ToOverrideGlobal())
482     {
483       aCtx->ChangeClipping().DisableGlobal();
484       hasDisabled = aCtx->Clipping().HasDisabled();
485     }
486     else if (!myTrsfPers.IsNull())
487     {
488       if (myTrsfPers->IsZoomOrRotate())
489       {
490         // Zoom/rotate persistence object lives in two worlds at the same time.
491         // Global clipping planes can not be trivially applied without being converted
492         // into local space of transformation persistence object.
493         // As more simple alternative - just clip entire object by its anchor point defined in the world space.
494         const gp_Pnt anAnchor = myTrsfPers->AnchorPoint();
495         for (OpenGl_ClippingIterator aPlaneIt (aCtx->Clipping()); aPlaneIt.More() && aPlaneIt.IsGlobal(); aPlaneIt.Next())
496         {
497           const Handle(Graphic3d_ClipPlane)& aPlane = aPlaneIt.Value();
498           if (!aPlane->IsOn())
499           {
500             continue;
501           }
502
503           // check for clipping
504           const Graphic3d_Vec4d aCheckPnt (anAnchor.X(), anAnchor.Y(), anAnchor.Z(), 1.0);
505           if (aPlane->ProbePoint (aCheckPnt) == Graphic3d_ClipState_Out)
506           {
507             isClipped = true;
508             break;
509           }
510         }
511       }
512
513       aCtx->ChangeClipping().DisableGlobal();
514       hasDisabled = aCtx->Clipping().HasDisabled();
515     }
516
517     // Set of clipping planes that do not intersect the structure,
518     // and thus can be disabled to improve rendering performance
519     if (aBBox.IsValid()
520      && myTrsfPers.IsNull())
521     {
522       for (OpenGl_ClippingIterator aPlaneIt (aCtx->Clipping()); aPlaneIt.More(); aPlaneIt.Next())
523       {
524         const Handle(Graphic3d_ClipPlane)& aPlane = aPlaneIt.Value();
525         if (aPlaneIt.IsDisabled())
526         {
527           continue;
528         }
529
530         const Graphic3d_ClipState aBoxState = aPlane->ProbeBox (aBBox);
531         if (aBoxState == Graphic3d_ClipState_Out)
532         {
533           isClipped = true;
534           break;
535         }
536         else if (aBoxState == Graphic3d_ClipState_In)
537         {
538           aCtx->ChangeClipping().SetEnabled (aPlaneIt, false);
539           hasDisabled = true;
540         }
541       }
542     }
543
544     if ((!myClipPlanes.IsNull() && !myClipPlanes->IsEmpty())
545      || hasDisabled)
546     {
547       // Set OCCT state uniform variables
548       aCtx->ShaderManager()->UpdateClippingState();
549     }
550   }
551
552   // Render groups
553   bool hasClosedPrims = false;
554   if (!isClipped)
555   {
556     renderGeometry (theWorkspace, hasClosedPrims);
557   }
558
559   // Reset correction for mirror transform
560   if (myIsMirrored)
561   {
562     aCtx->core11fwd->glFrontFace (GL_CCW);
563   }
564
565   // Render capping for structure groups
566   if (hasClosedPrims
567    && aCtx->Clipping().IsCappingOn())
568   {
569     OpenGl_CappingAlgo::RenderCapping (theWorkspace, *this);
570   }
571
572   // Revert structure clippings
573   if (hasDisabled)
574   {
575     // enable planes that were previously disabled
576     aCtx->ChangeClipping().RestoreDisabled();
577   }
578   aCtx->ChangeClipping().SetLocalPlanes (Handle(Graphic3d_SequenceOfHClipPlane)());
579   if ((!myClipPlanes.IsNull() && !myClipPlanes->IsEmpty())
580     || hasDisabled)
581   {
582     // Set OCCT state uniform variables
583     aCtx->ShaderManager()->RevertClippingState();
584   }
585
586   // Restore local transformation
587   aCtx->ModelWorldState.Pop();
588   aCtx->SetGlNormalizeEnabled (anOldGlNormalize);
589
590   // Restore aspects
591   theWorkspace->SetAspects (aPrevAspectFace);
592
593   // Apply highlight box
594   if (!isClipped
595    && !myHighlightStyle.IsNull()
596    &&  myHighlightStyle->Method() == Aspect_TOHM_BOUNDBOX)
597   {
598     aCtx->ApplyModelViewMatrix();
599     theWorkspace->SetHighlightStyle (myHighlightStyle);
600     renderBoundingBox (theWorkspace);
601   }
602
603   if (!myTrsfPers.IsNull())
604   {
605     aCtx->WorldViewState.Pop();
606   }
607
608   // Restore named status
609   theWorkspace->SetHighlightStyle (Handle(Graphic3d_PresentationAttributes)());
610 }
611
612 // =======================================================================
613 // function : Release
614 // purpose  :
615 // =======================================================================
616 void OpenGl_Structure::Release (const Handle(OpenGl_Context)& theGlCtx)
617 {
618   // Release groups
619   Clear (theGlCtx);
620   myHighlightStyle.Nullify();
621 }
622
623 // =======================================================================
624 // function : ReleaseGlResources
625 // purpose  :
626 // =======================================================================
627 void OpenGl_Structure::ReleaseGlResources (const Handle(OpenGl_Context)& theGlCtx)
628 {
629   for (OpenGl_Structure::GroupIterator aGroupIter (myGroups); aGroupIter.More(); aGroupIter.Next())
630   {
631     aGroupIter.ChangeValue()->Release (theGlCtx);
632   }
633 }
634
635 //=======================================================================
636 //function : ShadowLink
637 //purpose  :
638 //=======================================================================
639 Handle(Graphic3d_CStructure) OpenGl_Structure::ShadowLink (const Handle(Graphic3d_StructureManager)& theManager) const
640 {
641   return new OpenGl_StructureShadow (theManager, this);
642 }