120bbef95169f3600f6c6664ee81f070688f7a04
[occt.git] / src / OpenGl / OpenGl_Layer.cxx
1 // Created on: 2014-03-31
2 // Created by: Danila ULYANOV
3 // Copyright (c) 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_Layer.hxx>
17
18 #include <OpenGl_BVHTreeSelector.hxx>
19 #include <OpenGl_Structure.hxx>
20 #include <OpenGl_ShaderManager.hxx>
21 #include <OpenGl_View.hxx>
22 #include <OpenGl_Workspace.hxx>
23 #include <Graphic3d_GraphicDriver.hxx>
24
25 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Layer, Standard_Transient)
26
27 // =======================================================================
28 // function : OpenGl_Layer
29 // purpose  :
30 // =======================================================================
31 OpenGl_Layer::OpenGl_Layer (const Standard_Integer theNbPriorities,
32                             const Handle(Select3D_BVHBuilder3d)& theBuilder)
33 : myArray                     (0, theNbPriorities - 1),
34   myNbStructures              (0),
35   myNbStructuresNotCulled     (0),
36   myBVHPrimitivesTrsfPers     (theBuilder),
37   myBVHIsLeftChildQueuedFirst (Standard_True),
38   myIsBVHPrimitivesNeedsReset (Standard_False)
39 {
40   myIsBoundingBoxNeedsReset[0] = myIsBoundingBoxNeedsReset[1] = true;
41 }
42
43 // =======================================================================
44 // function : ~OpenGl_Layer
45 // purpose  :
46 // =======================================================================
47 OpenGl_Layer::~OpenGl_Layer()
48 {
49   //
50 }
51
52 // =======================================================================
53 // function : Add
54 // purpose  :
55 // =======================================================================
56 void OpenGl_Layer::Add (const OpenGl_Structure* theStruct,
57                         const Standard_Integer  thePriority,
58                         Standard_Boolean        isForChangePriority)
59 {
60   const Standard_Integer anIndex = Min (Max (thePriority, 0), myArray.Length() - 1);
61   if (theStruct == NULL)
62   {
63     return;
64   }
65
66   myArray (anIndex).Add (theStruct);
67   if (theStruct->IsAlwaysRendered())
68   {
69     theStruct->MarkAsNotCulled();
70     if (!isForChangePriority)
71     {
72       myAlwaysRenderedMap.Add (theStruct);
73     }
74   }
75   else if (!isForChangePriority)
76   {
77     if (theStruct->TransformPersistence().IsNull())
78     {
79       myBVHPrimitives.Add (theStruct);
80     }
81     else
82     {
83       myBVHPrimitivesTrsfPers.Add (theStruct);
84     }
85   }
86   ++myNbStructures;
87 }
88
89 // =======================================================================
90 // function : Remove
91 // purpose  :
92 // =======================================================================
93 bool OpenGl_Layer::Remove (const OpenGl_Structure* theStruct,
94                            Standard_Integer&       thePriority,
95                            Standard_Boolean        isForChangePriority)
96 {
97   if (theStruct == NULL)
98   {
99     thePriority = -1;
100     return false;
101   }
102
103   const Standard_Integer aNbPriorities = myArray.Length();
104   for (Standard_Integer aPriorityIter = 0; aPriorityIter < aNbPriorities; ++aPriorityIter)
105   {
106     OpenGl_IndexedMapOfStructure& aStructures = myArray (aPriorityIter);
107
108     const Standard_Integer anIndex = aStructures.FindIndex (theStruct);
109     if (anIndex != 0)
110     {
111       aStructures.Swap (anIndex, aStructures.Size());
112       aStructures.RemoveLast();
113
114       if (!isForChangePriority)
115       {
116         Standard_Boolean isAlwaysRend = theStruct->IsAlwaysRendered();
117         if (!isAlwaysRend)
118         {
119           if (!myBVHPrimitives.Remove (theStruct))
120           {
121             if (!myBVHPrimitivesTrsfPers.Remove (theStruct))
122             {
123               isAlwaysRend = Standard_True;
124             }
125           }
126         }
127         if (isAlwaysRend)
128         {
129           const Standard_Integer anIndex2 = myAlwaysRenderedMap.FindIndex (theStruct);
130           if (anIndex2 != 0)
131           {
132             myAlwaysRenderedMap.Swap (myAlwaysRenderedMap.Size(), anIndex2);
133             myAlwaysRenderedMap.RemoveLast();
134           }
135         }
136       }
137       --myNbStructures;
138       thePriority = aPriorityIter;
139       return true;
140     }
141   }
142
143   thePriority = -1;
144   return false;
145 }
146
147 // =======================================================================
148 // function : InvalidateBVHData
149 // purpose  :
150 // =======================================================================
151 void OpenGl_Layer::InvalidateBVHData()
152 {
153   myIsBVHPrimitivesNeedsReset = Standard_True;
154 }
155
156 //! Calculate a finite bounding box of infinite object as its middle point.
157 inline Graphic3d_BndBox3d centerOfinfiniteBndBox (const Graphic3d_BndBox3d& theBndBox)
158 {
159   // bounding borders of infinite line has been calculated as own point in center of this line
160   const Graphic3d_Vec3d aDiagVec = theBndBox.CornerMax() - theBndBox.CornerMin();
161   return aDiagVec.SquareModulus() >= 500000.0 * 500000.0
162        ? Graphic3d_BndBox3d ((theBndBox.CornerMin() + theBndBox.CornerMax()) * 0.5)
163        : Graphic3d_BndBox3d();
164 }
165
166 //! Return true if at least one vertex coordinate out of float range.
167 inline bool isInfiniteBndBox (const Graphic3d_BndBox3d& theBndBox)
168 {
169   return Abs (theBndBox.CornerMax().x()) >= ShortRealLast()
170       || Abs (theBndBox.CornerMax().y()) >= ShortRealLast()
171       || Abs (theBndBox.CornerMax().z()) >= ShortRealLast()
172       || Abs (theBndBox.CornerMin().x()) >= ShortRealLast()
173       || Abs (theBndBox.CornerMin().y()) >= ShortRealLast()
174       || Abs (theBndBox.CornerMin().z()) >= ShortRealLast();
175 }
176
177 // =======================================================================
178 // function : BoundingBox
179 // purpose  :
180 // =======================================================================
181 Bnd_Box OpenGl_Layer::BoundingBox (const Standard_Integer          theViewId,
182                                    const Handle(Graphic3d_Camera)& theCamera,
183                                    const Standard_Integer          theWindowWidth,
184                                    const Standard_Integer          theWindowHeight,
185                                    const Standard_Boolean          theToIncludeAuxiliary) const
186 {
187   updateBVH();
188
189   const Standard_Integer aBoxId = !theToIncludeAuxiliary ? 0 : 1;
190   const Graphic3d_Mat4d& aProjectionMat = theCamera->ProjectionMatrix();
191   const Graphic3d_Mat4d& aWorldViewMat  = theCamera->OrientationMatrix();
192   if (myIsBoundingBoxNeedsReset[aBoxId])
193   {
194     // Recompute layer bounding box
195     myBoundingBox[aBoxId].SetVoid();
196
197     for (OpenGl_ArrayOfIndexedMapOfStructure::Iterator aMapIter (myArray); aMapIter.More(); aMapIter.Next())
198     {
199       const OpenGl_IndexedMapOfStructure& aStructures = aMapIter.Value();
200       for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (aStructures); aStructIter.More(); aStructIter.Next())
201       {
202         const OpenGl_Structure* aStructure = aStructIter.Value();
203         if (!aStructure->IsVisible (theViewId))
204         {
205           continue;
206         }
207
208         // "FitAll" operation ignores object with transform persistence parameter
209         // but adds transform persistence point in a bounding box of layer (only zoom pers. objects).
210         if (!aStructure->TransformPersistence().IsNull())
211         {
212           if (!theToIncludeAuxiliary
213             && aStructure->TransformPersistence()->IsZoomOrRotate())
214           {
215             const gp_Pnt anAnchor = aStructure->TransformPersistence()->AnchorPoint();
216             myBoundingBox[aBoxId].Add (anAnchor);
217             continue;
218           }
219           // Panning and 2d persistence apply changes to projection or/and its translation components.
220           // It makes them incompatible with z-fitting algorithm. Ignored by now.
221           else if (!theToIncludeAuxiliary
222                  || aStructure->TransformPersistence()->IsTrihedronOr2d())
223           {
224             continue;
225           }
226         }
227
228         Graphic3d_BndBox3d aBox = aStructure->BoundingBox();
229         if (!aBox.IsValid())
230         {
231           continue;
232         }
233
234         if (aStructure->IsInfinite
235         && !theToIncludeAuxiliary)
236         {
237           // include center of infinite object
238           aBox = centerOfinfiniteBndBox (aBox);
239         }
240
241         if (!aStructure->TransformPersistence().IsNull())
242         {
243           aStructure->TransformPersistence()->Apply (theCamera, aProjectionMat, aWorldViewMat, theWindowWidth, theWindowHeight, aBox);
244         }
245
246         // skip too big boxes to prevent float overflow at camera parameters calculation
247         if (aBox.IsValid()
248         && !isInfiniteBndBox (aBox))
249         {
250           myBoundingBox[aBoxId].Add (gp_Pnt (aBox.CornerMin().x(), aBox.CornerMin().y(), aBox.CornerMin().z()));
251           myBoundingBox[aBoxId].Add (gp_Pnt (aBox.CornerMax().x(), aBox.CornerMax().y(), aBox.CornerMax().z()));
252         }
253       }
254     }
255
256     myIsBoundingBoxNeedsReset[aBoxId] = false;
257   }
258
259   Bnd_Box aResBox = myBoundingBox[aBoxId];
260   if (!theToIncludeAuxiliary
261     || myAlwaysRenderedMap.IsEmpty())
262   {
263     return aResBox;
264   }
265
266   // add transformation-persistent objects which depend on camera position (and thus can not be cached) for operations like Z-fit
267   for (NCollection_IndexedMap<const OpenGl_Structure*>::Iterator aStructIter (myAlwaysRenderedMap); aStructIter.More(); aStructIter.Next())
268   {
269     const OpenGl_Structure* aStructure = aStructIter.Value();
270     if (!aStructure->IsVisible (theViewId))
271     {
272       continue;
273     }
274     else if (aStructure->TransformPersistence().IsNull()
275          || !aStructure->TransformPersistence()->IsTrihedronOr2d())
276     {
277       continue;
278     }
279
280     Graphic3d_BndBox3d aBox = aStructure->BoundingBox();
281     if (!aBox.IsValid())
282     {
283       continue;
284     }
285
286     aStructure->TransformPersistence()->Apply (theCamera, aProjectionMat, aWorldViewMat, theWindowWidth, theWindowHeight, aBox);
287     if (aBox.IsValid()
288     && !isInfiniteBndBox (aBox))
289     {
290       aResBox.Add (gp_Pnt (aBox.CornerMin().x(), aBox.CornerMin().y(), aBox.CornerMin().z()));
291       aResBox.Add (gp_Pnt (aBox.CornerMax().x(), aBox.CornerMax().y(), aBox.CornerMax().z()));
292     }
293   }
294
295   return aResBox;
296 }
297
298 // =======================================================================
299 // function : considerZoomPersistenceObjects
300 // purpose  :
301 // =======================================================================
302 Standard_Real OpenGl_Layer::considerZoomPersistenceObjects (const Standard_Integer          theViewId,
303                                                             const Handle(Graphic3d_Camera)& theCamera,
304                                                             Standard_Integer                theWindowWidth,
305                                                             Standard_Integer                theWindowHeight) const
306 {
307   if (NbOfTransformPersistenceObjects() == 0)
308   {
309     return 1.0;
310   }
311
312   const Graphic3d_Mat4d& aProjectionMat = theCamera->ProjectionMatrix();
313   const Graphic3d_Mat4d& aWorldViewMat  = theCamera->OrientationMatrix();
314   Standard_Real          aMaxCoef       = -std::numeric_limits<double>::max();
315
316   for (OpenGl_ArrayOfIndexedMapOfStructure::Iterator aMapIter (myArray); aMapIter.More(); aMapIter.Next())
317   {
318     const OpenGl_IndexedMapOfStructure& aStructures = aMapIter.Value();
319     for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (aStructures); aStructIter.More(); aStructIter.Next())
320     {
321       const OpenGl_Structure* aStructure = aStructIter.Value();
322       if (!aStructure->IsVisible (theViewId)
323        ||  aStructure->TransformPersistence().IsNull()
324        || !aStructure->TransformPersistence()->IsZoomOrRotate())
325       {
326         continue;
327       }
328
329       Graphic3d_BndBox3d aBox = aStructure->BoundingBox();
330       if (!aBox.IsValid())
331       {
332         continue;
333       }
334
335       aStructure->TransformPersistence()->Apply (theCamera, aProjectionMat, aWorldViewMat, theWindowWidth, theWindowHeight, aBox);
336
337       const BVH_Vec3d&       aCornerMin           = aBox.CornerMin();
338       const BVH_Vec3d&       aCornerMax           = aBox.CornerMax();
339       const Standard_Integer aNbOfPoints          = 8;
340       const gp_Pnt           aPoints[aNbOfPoints] = { gp_Pnt (aCornerMin.x(), aCornerMin.y(), aCornerMin.z()),
341                                                       gp_Pnt (aCornerMin.x(), aCornerMin.y(), aCornerMax.z()),
342                                                       gp_Pnt (aCornerMin.x(), aCornerMax.y(), aCornerMin.z()),
343                                                       gp_Pnt (aCornerMin.x(), aCornerMax.y(), aCornerMax.z()),
344                                                       gp_Pnt (aCornerMax.x(), aCornerMin.y(), aCornerMin.z()),
345                                                       gp_Pnt (aCornerMax.x(), aCornerMin.y(), aCornerMax.z()),
346                                                       gp_Pnt (aCornerMax.x(), aCornerMax.y(), aCornerMin.z()),
347                                                       gp_Pnt (aCornerMax.x(), aCornerMax.y(), aCornerMax.z()) };
348       gp_Pnt aConvertedPoints[aNbOfPoints];
349       Standard_Real aConvertedMinX =  std::numeric_limits<double>::max();
350       Standard_Real aConvertedMaxX = -std::numeric_limits<double>::max();
351       Standard_Real aConvertedMinY =  std::numeric_limits<double>::max();
352       Standard_Real aConvertedMaxY = -std::numeric_limits<double>::max();
353       for (Standard_Integer anIdx = 0; anIdx < aNbOfPoints; ++anIdx)
354       {
355         aConvertedPoints[anIdx] = theCamera->Project (aPoints[anIdx]);
356
357         aConvertedMinX          = Min (aConvertedMinX, aConvertedPoints[anIdx].X());
358         aConvertedMaxX          = Max (aConvertedMaxX, aConvertedPoints[anIdx].X());
359
360         aConvertedMinY          = Min (aConvertedMinY, aConvertedPoints[anIdx].Y());
361         aConvertedMaxY          = Max (aConvertedMaxY, aConvertedPoints[anIdx].Y());
362       }
363
364       const Standard_Boolean isBigObject  = (Abs (aConvertedMaxX - aConvertedMinX) > 2.0)  // width  of zoom pers. object greater than width  of window
365                                          || (Abs (aConvertedMaxY - aConvertedMinY) > 2.0); // height of zoom pers. object greater than height of window
366       const Standard_Boolean isAlreadyInScreen = (aConvertedMinX > -1.0 && aConvertedMinX < 1.0)
367                                               && (aConvertedMaxX > -1.0 && aConvertedMaxX < 1.0)
368                                               && (aConvertedMinY > -1.0 && aConvertedMinY < 1.0)
369                                               && (aConvertedMaxY > -1.0 && aConvertedMaxY < 1.0);
370       if (isBigObject || isAlreadyInScreen)
371       {
372         continue;
373       }
374
375       const gp_Pnt aTPPoint = aStructure->TransformPersistence()->AnchorPoint();
376       gp_Pnt aConvertedTPPoint = theCamera->Project (aTPPoint);
377       aConvertedTPPoint.SetZ (0.0);
378
379       if (aConvertedTPPoint.Coord().Modulus() < Precision::Confusion())
380       {
381         continue;
382       }
383
384       Standard_Real aShiftX = 0.0;
385       if (aConvertedMinX < -1.0)
386       {
387         aShiftX = ((aConvertedMaxX < -1.0) ? (-(1.0 + aConvertedMaxX) + (aConvertedMaxX - aConvertedMinX)) : -(1.0 + aConvertedMinX));
388       }
389       else if (aConvertedMaxX > 1.0)
390       {
391         aShiftX = ((aConvertedMinX > 1.0) ? ((aConvertedMinX - 1.0) + (aConvertedMaxX - aConvertedMinX)) : (aConvertedMaxX - 1.0));
392       }
393
394       Standard_Real aShiftY = 0.0;
395       if (aConvertedMinY < -1.0)
396       {
397         aShiftY = ((aConvertedMaxY < -1.0) ? (-(1.0 + aConvertedMaxY) + (aConvertedMaxY - aConvertedMinY)) : -(1.0 + aConvertedMinY));
398       }
399       else if (aConvertedMaxY > 1.0)
400       {
401         aShiftY = ((aConvertedMinY > 1.0) ? ((aConvertedMinY - 1.0) + (aConvertedMaxY - aConvertedMinY)) : (aConvertedMaxY - 1.0));
402       }
403
404       const Standard_Real aDifX = Abs (aConvertedTPPoint.X()) - aShiftX;
405       const Standard_Real aDifY = Abs (aConvertedTPPoint.Y()) - aShiftY;
406       if (aDifX > Precision::Confusion())
407       {
408         aMaxCoef = Max (aMaxCoef, Abs (aConvertedTPPoint.X()) / aDifX);
409       }
410       if (aDifY > Precision::Confusion())
411       {
412         aMaxCoef = Max (aMaxCoef, Abs (aConvertedTPPoint.Y()) / aDifY);
413       }
414     }
415   }
416
417   return (aMaxCoef > 0.0) ? aMaxCoef : 1.0;
418 }
419
420 // =======================================================================
421 // function : renderAll
422 // purpose  :
423 // =======================================================================
424 void OpenGl_Layer::renderAll (const Handle(OpenGl_Workspace)& theWorkspace) const
425 {
426   const Standard_Integer aViewId = theWorkspace->View()->Identification();
427   for (OpenGl_ArrayOfIndexedMapOfStructure::Iterator aMapIter (myArray); aMapIter.More(); aMapIter.Next())
428   {
429     const OpenGl_IndexedMapOfStructure& aStructures = aMapIter.Value();
430     for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (aStructures); aStructIter.More(); aStructIter.Next())
431     {
432       const OpenGl_Structure* aStruct = aStructIter.Value();
433       if (aStruct->IsCulled()
434       || !aStruct->IsVisible (aViewId))
435       {
436         continue;
437       }
438
439       aStruct->Render (theWorkspace);
440     }
441   }
442 }
443
444 // =======================================================================
445 // function : updateBVH
446 // purpose  :
447 // =======================================================================
448 void OpenGl_Layer::updateBVH() const
449 {
450   if (!myIsBVHPrimitivesNeedsReset)
451   {
452     return;
453   }
454
455   myBVHPrimitives.Clear();
456   myBVHPrimitivesTrsfPers.Clear();
457   myAlwaysRenderedMap.Clear();
458   myIsBVHPrimitivesNeedsReset = Standard_False;
459   for (OpenGl_ArrayOfIndexedMapOfStructure::Iterator aMapIter (myArray); aMapIter.More(); aMapIter.Next())
460   {
461     const OpenGl_IndexedMapOfStructure& aStructures = aMapIter.Value();
462     for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (aStructures); aStructIter.More(); aStructIter.Next())
463     {
464       const OpenGl_Structure* aStruct = aStructIter.Value();
465       if (aStruct->IsAlwaysRendered())
466       {
467         aStruct->MarkAsNotCulled();
468         myAlwaysRenderedMap.Add (aStruct);
469       }
470       else if (aStruct->TransformPersistence().IsNull())
471       {
472         myBVHPrimitives.Add (aStruct);
473       }
474       else
475       {
476         myBVHPrimitivesTrsfPers.Add (aStruct);
477       }
478     }
479   }
480 }
481
482 // =======================================================================
483 // function : UpdateCulling
484 // purpose  :
485 // =======================================================================
486 void OpenGl_Layer::UpdateCulling (const Standard_Integer theViewId,
487                                   const OpenGl_BVHTreeSelector& theSelector,
488                                   const Standard_Boolean theToTraverse)
489 {
490   updateBVH();
491
492   myNbStructuresNotCulled = myNbStructures;
493   for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (myBVHPrimitives.Structures()); aStructIter.More(); aStructIter.Next())
494   {
495     const OpenGl_Structure* aStruct = aStructIter.Value();
496     aStruct->SetCulled (theToTraverse);
497   }
498   for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (myBVHPrimitivesTrsfPers.Structures()); aStructIter.More(); aStructIter.Next())
499   {
500     const OpenGl_Structure* aStruct = aStructIter.Value();
501     aStruct->SetCulled (theToTraverse);
502   }
503
504   if (!theToTraverse)
505   {
506     return;
507   }
508   if (myBVHPrimitives        .Size() == 0
509    && myBVHPrimitivesTrsfPers.Size() == 0)
510   {
511     return;
512   }
513
514   myNbStructuresNotCulled = myAlwaysRenderedMap.Extent();
515   OpenGl_BVHTreeSelector::CullingContext aCullCtx;
516   theSelector.SetCullingDistance(aCullCtx, myLayerSettings.CullingDistance());
517   theSelector.SetCullingSize    (aCullCtx, myLayerSettings.CullingSize());
518   for (Standard_Integer aBVHTreeIdx = 0; aBVHTreeIdx < 2; ++aBVHTreeIdx)
519   {
520     const Standard_Boolean isTrsfPers = aBVHTreeIdx == 1;
521     opencascade::handle<BVH_Tree<Standard_Real, 3> > aBVHTree;
522     if (isTrsfPers)
523     {
524       if (myBVHPrimitivesTrsfPers.Size() == 0)
525         continue;
526
527       const OpenGl_Mat4d& aProjection               = theSelector.ProjectionMatrix();
528       const OpenGl_Mat4d& aWorldView                = theSelector.WorldViewMatrix();
529       const Graphic3d_WorldViewProjState& aWVPState = theSelector.WorldViewProjState();
530       const Standard_Integer aViewportWidth         = theSelector.ViewportWidth();
531       const Standard_Integer aViewportHeight        = theSelector.ViewportHeight();
532
533       aBVHTree = myBVHPrimitivesTrsfPers.BVH (theSelector.Camera(), aProjection, aWorldView, aViewportWidth, aViewportHeight, aWVPState);
534     }
535     else
536     {
537       if (myBVHPrimitives.Size() == 0)
538         continue;
539
540       aBVHTree = myBVHPrimitives.BVH();
541     }
542
543     if (theSelector.IsCulled (aCullCtx, aBVHTree->MinPoint (0), aBVHTree->MaxPoint (0)))
544     {
545       continue;
546     }
547
548     Standard_Integer aStack[BVH_Constants_MaxTreeDepth];
549     Standard_Integer aHead = -1;
550     Standard_Integer aNode = 0; // a root node
551     for (;;)
552     {
553       if (!aBVHTree->IsOuter (aNode))
554       {
555         const Standard_Integer aLeftChildIdx  = aBVHTree->Child<0> (aNode);
556         const Standard_Integer aRightChildIdx = aBVHTree->Child<1> (aNode);
557         const Standard_Boolean isLeftChildIn  = !theSelector.IsCulled (aCullCtx, aBVHTree->MinPoint (aLeftChildIdx),  aBVHTree->MaxPoint (aLeftChildIdx));
558         const Standard_Boolean isRightChildIn = !theSelector.IsCulled (aCullCtx, aBVHTree->MinPoint (aRightChildIdx), aBVHTree->MaxPoint (aRightChildIdx));
559         if (isLeftChildIn
560          && isRightChildIn)
561         {
562           aNode = myBVHIsLeftChildQueuedFirst ? aLeftChildIdx : aRightChildIdx;
563           aStack[++aHead] = myBVHIsLeftChildQueuedFirst ? aRightChildIdx : aLeftChildIdx;
564           myBVHIsLeftChildQueuedFirst = !myBVHIsLeftChildQueuedFirst;
565         }
566         else if (isLeftChildIn
567               || isRightChildIn)
568         {
569           aNode = isLeftChildIn ? aLeftChildIdx : aRightChildIdx;
570         }
571         else
572         {
573           if (aHead < 0)
574           {
575             break;
576           }
577
578           aNode = aStack[aHead--];
579         }
580       }
581       else
582       {
583         Standard_Integer aIdx = aBVHTree->BegPrimitive (aNode);
584         const OpenGl_Structure* aStruct = isTrsfPers
585                                         ? myBVHPrimitivesTrsfPers.GetStructureById (aIdx)
586                                         : myBVHPrimitives.GetStructureById (aIdx);
587         if (aStruct->IsVisible (theViewId))
588         {
589           aStruct->MarkAsNotCulled();
590           ++myNbStructuresNotCulled;
591         }
592         if (aHead < 0)
593         {
594           break;
595         }
596
597         aNode = aStack[aHead--];
598       }
599     }
600   }
601 }
602
603 // =======================================================================
604 // function : Append
605 // purpose  :
606 // =======================================================================
607 Standard_Boolean OpenGl_Layer::Append (const OpenGl_Layer& theOther)
608 {
609   // the source priority list shouldn't have more priorities
610   const Standard_Integer aNbPriorities = theOther.NbPriorities();
611   if (aNbPriorities > NbPriorities())
612   {
613     return Standard_False;
614   }
615
616   // add all structures to destination priority list
617   for (Standard_Integer aPriorityIter = 0; aPriorityIter < aNbPriorities; ++aPriorityIter)
618   {
619     const OpenGl_IndexedMapOfStructure& aStructures = theOther.myArray (aPriorityIter);
620     for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (aStructures); aStructIter.More(); aStructIter.Next())
621     {
622       Add (aStructIter.Value(), aPriorityIter);
623     }
624   }
625
626   return Standard_True;
627 }
628
629 //=======================================================================
630 //function : SetLayerSettings
631 //purpose  :
632 //=======================================================================
633 void OpenGl_Layer::SetLayerSettings (const Graphic3d_ZLayerSettings& theSettings)
634 {
635   const Standard_Boolean toUpdateTrsf = !myLayerSettings.Origin().IsEqual (theSettings.Origin(), gp::Resolution());
636   myLayerSettings = theSettings;
637   if (toUpdateTrsf)
638   {
639     for (OpenGl_ArrayOfIndexedMapOfStructure::Iterator aMapIter (myArray); aMapIter.More(); aMapIter.Next())
640     {
641       OpenGl_IndexedMapOfStructure& aStructures = aMapIter.ChangeValue();
642       for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (aStructures); aStructIter.More(); aStructIter.Next())
643       {
644         OpenGl_Structure* aStructure = const_cast<OpenGl_Structure*> (aStructIter.Value());
645         aStructure->updateLayerTransformation();
646       }
647     }
648   }
649 }
650
651 //=======================================================================
652 //function : Render
653 //purpose  :
654 //=======================================================================
655 void OpenGl_Layer::Render (const Handle(OpenGl_Workspace)&   theWorkspace,
656                            const OpenGl_GlobalLayerSettings& theDefaultSettings) const
657 {
658   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
659   // myLayerSettings.ToClearDepth() is handled outside
660
661   // handle depth test
662   if (myLayerSettings.ToEnableDepthTest())
663   {
664     // assuming depth test is enabled by default
665     glDepthFunc (theDefaultSettings.DepthFunc);
666   }
667   else
668   {
669     glDepthFunc (GL_ALWAYS);
670   }
671
672   // save environment texture
673   Handle(OpenGl_TextureSet) anEnvironmentTexture = theWorkspace->EnvironmentTexture();
674   if (!myLayerSettings.UseEnvironmentTexture())
675   {
676     theWorkspace->SetEnvironmentTexture (Handle(OpenGl_TextureSet)());
677   }
678
679   // handle depth offset
680   const Graphic3d_PolygonOffset anAppliedOffsetParams = theWorkspace->SetDefaultPolygonOffset (myLayerSettings.PolygonOffset());
681
682   // handle depth write
683   theWorkspace->UseDepthWrite() = myLayerSettings.ToEnableDepthWrite() && theDefaultSettings.DepthMask == GL_TRUE;
684   glDepthMask (theWorkspace->UseDepthWrite() ? GL_TRUE : GL_FALSE);
685
686   const Standard_Boolean hasLocalCS = !myLayerSettings.OriginTransformation().IsNull();
687   const Handle(OpenGl_ShaderManager)& aManager = aCtx->ShaderManager();
688   Handle(Graphic3d_LightSet) aLightsBack = aManager->LightSourceState().LightSources();
689   const bool hasOwnLights = aCtx->ColorMask() && !myLayerSettings.Lights().IsNull() && myLayerSettings.Lights() != aLightsBack;
690   if (hasOwnLights)
691   {
692     myLayerSettings.Lights()->UpdateRevision();
693     aManager->UpdateLightSourceStateTo (myLayerSettings.Lights());
694   }
695
696   const Handle(Graphic3d_Camera)& aWorldCamera = theWorkspace->View()->Camera();
697   if (hasLocalCS)
698   {
699     // Apply local camera transformation.
700     // The vertex position is computed by the following formula in GLSL program:
701     //   gl_Position = occProjectionMatrix * occWorldViewMatrix * occModelWorldMatrix * occVertex;
702     // where:
703     //   occProjectionMatrix - matrix defining orthographic/perspective/stereographic projection
704     //   occWorldViewMatrix  - world-view  matrix defining Camera position and orientation
705     //   occModelWorldMatrix - model-world matrix defining Object transformation from local coordinate system to the world coordinate system
706     //   occVertex           - input vertex position
707     //
708     // Since double precision is quite expensive on modern GPUs, and not available on old hardware,
709     // all these values are passed with single float precision to the shader.
710     // As result, single precision become insufficient for handling objects far from the world origin.
711     //
712     // Several approaches can be used to solve precision issues:
713     //  - [Broute force] migrate to double precision for all matrices and vertex position.
714     //    This is too expensive for most hardware.
715     //  - Store only translation part with double precision and pass it to GLSL program.
716     //    This requires modified GLSL programs for computing transformation
717     //    and extra packing mechanism for hardware not supporting double precision natively.
718     //    This solution is less expensive then previous one.
719     //  - Move translation part of occModelWorldMatrix into occWorldViewMatrix.
720     //    The main idea here is that while moving Camera towards the object,
721     //    Camera translation part and Object translation part will compensate each other
722     //    to fit into single float precision.
723     //    But this operation should be performed with double precision - this is why we are moving
724     //    translation part of occModelWorldMatrix to occWorldViewMatrix.
725     //
726     // All approaches might be useful in different scenarios, but for the moment we consider the last one as main scenario.
727     // Here we do the trick:
728     //  - OpenGl_Layer defines the Local Origin, which is expected to be the center of objects stored within it.
729     //    This Local Origin is included into occWorldViewMatrix during rendering.
730     //  - OpenGl_Structure defines Object local transformation occModelWorldMatrix with subtracted Local Origin of the Layer.
731     //    This means that Object itself should be defined within either Local Transformation equal or near to Local Origin of the Layer.
732     theWorkspace->View()->SetLocalOrigin (myLayerSettings.Origin());
733
734     NCollection_Mat4<Standard_Real> aWorldView = aWorldCamera->OrientationMatrix();
735     Graphic3d_TransformUtils::Translate (aWorldView, myLayerSettings.Origin().X(), myLayerSettings.Origin().Y(), myLayerSettings.Origin().Z());
736
737     NCollection_Mat4<Standard_ShortReal> aWorldViewF;
738     aWorldViewF.ConvertFrom (aWorldView);
739     aCtx->WorldViewState.SetCurrent (aWorldViewF);
740     aCtx->ShaderManager()->UpdateClippingState();
741     aCtx->ShaderManager()->UpdateLightSourceState();
742   }
743
744   // render priority list
745   renderAll (theWorkspace);
746
747   if (hasOwnLights)
748   {
749     aManager->UpdateLightSourceStateTo (aLightsBack);
750   }
751   if (hasLocalCS)
752   {
753     aCtx->ShaderManager()->RevertClippingState();
754     aCtx->ShaderManager()->UpdateLightSourceState();
755
756     aCtx->WorldViewState.SetCurrent (aWorldCamera->OrientationMatrixF());
757     theWorkspace->View() ->SetLocalOrigin (gp_XYZ (0.0, 0.0, 0.0));
758   }
759
760   // always restore polygon offset between layers rendering
761   theWorkspace->SetDefaultPolygonOffset (anAppliedOffsetParams);
762
763   // restore environment texture
764   if (!myLayerSettings.UseEnvironmentTexture())
765   {
766     theWorkspace->SetEnvironmentTexture (anEnvironmentTexture);
767   }
768 }