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