0028760: Visualization, TKOpenGl - avoid excessive frustum culling traverse within...
[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   myBVHPrimitivesTrsfPers     (theBuilder),
36   myBVHIsLeftChildQueuedFirst (Standard_True),
37   myIsBVHPrimitivesNeedsReset (Standard_False),
38   myIsCulled (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 OpenGl_BVHTreeSelector& theSelector,
487                                   const Standard_Boolean theToTraverse)
488 {
489   updateBVH();
490
491   myIsCulled = false;
492   for (OpenGl_ArrayOfIndexedMapOfStructure::Iterator aMapIter (myArray); aMapIter.More(); aMapIter.Next())
493   {
494     const OpenGl_IndexedMapOfStructure& aStructures = aMapIter.Value();
495     for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (aStructures); aStructIter.More(); aStructIter.Next())
496     {
497       const OpenGl_Structure* aStruct = aStructIter.Value();
498       aStruct->SetCulled (theToTraverse);
499     }
500   }
501
502   if (!theToTraverse)
503   {
504     return;
505   }
506   if (myBVHPrimitives        .Size() == 0
507    && myBVHPrimitivesTrsfPers.Size() == 0)
508   {
509     return;
510   }
511
512   myIsCulled = myAlwaysRenderedMap.IsEmpty();
513   OpenGl_BVHTreeSelector::CullingContext aCullCtx;
514   theSelector.SetCullingDistance(aCullCtx, myLayerSettings.CullingDistance());
515   theSelector.SetCullingSize    (aCullCtx, myLayerSettings.CullingSize());
516   for (Standard_Integer aBVHTreeIdx = 0; aBVHTreeIdx < 2; ++aBVHTreeIdx)
517   {
518     const Standard_Boolean isTrsfPers = aBVHTreeIdx == 1;
519     opencascade::handle<BVH_Tree<Standard_Real, 3> > aBVHTree;
520     if (isTrsfPers)
521     {
522       if (myBVHPrimitivesTrsfPers.Size() == 0)
523         continue;
524
525       const OpenGl_Mat4d& aProjection               = theSelector.ProjectionMatrix();
526       const OpenGl_Mat4d& aWorldView                = theSelector.WorldViewMatrix();
527       const Graphic3d_WorldViewProjState& aWVPState = theSelector.WorldViewProjState();
528       const Standard_Integer aViewportWidth         = theSelector.ViewportWidth();
529       const Standard_Integer aViewportHeight        = theSelector.ViewportHeight();
530
531       aBVHTree = myBVHPrimitivesTrsfPers.BVH (theSelector.Camera(), aProjection, aWorldView, aViewportWidth, aViewportHeight, aWVPState);
532     }
533     else
534     {
535       if (myBVHPrimitives.Size() == 0)
536         continue;
537
538       aBVHTree = myBVHPrimitives.BVH();
539     }
540
541     if (theSelector.IsCulled (aCullCtx, aBVHTree->MinPoint (0), aBVHTree->MaxPoint (0)))
542     {
543       continue;
544     }
545
546     myIsCulled = false;
547     Standard_Integer aStack[BVH_Constants_MaxTreeDepth];
548     Standard_Integer aHead = -1;
549     Standard_Integer aNode = 0; // a root node
550     for (;;)
551     {
552       if (!aBVHTree->IsOuter (aNode))
553       {
554         const Standard_Integer aLeftChildIdx  = aBVHTree->Child<0> (aNode);
555         const Standard_Integer aRightChildIdx = aBVHTree->Child<1> (aNode);
556         const Standard_Boolean isLeftChildIn  = !theSelector.IsCulled (aCullCtx, aBVHTree->MinPoint (aLeftChildIdx),  aBVHTree->MaxPoint (aLeftChildIdx));
557         const Standard_Boolean isRightChildIn = !theSelector.IsCulled (aCullCtx, aBVHTree->MinPoint (aRightChildIdx), aBVHTree->MaxPoint (aRightChildIdx));
558         if (isLeftChildIn
559          && isRightChildIn)
560         {
561           aNode = myBVHIsLeftChildQueuedFirst ? aLeftChildIdx : aRightChildIdx;
562           aStack[++aHead] = myBVHIsLeftChildQueuedFirst ? aRightChildIdx : aLeftChildIdx;
563           myBVHIsLeftChildQueuedFirst = !myBVHIsLeftChildQueuedFirst;
564         }
565         else if (isLeftChildIn
566               || isRightChildIn)
567         {
568           aNode = isLeftChildIn ? aLeftChildIdx : aRightChildIdx;
569         }
570         else
571         {
572           if (aHead < 0)
573           {
574             break;
575           }
576
577           aNode = aStack[aHead--];
578         }
579       }
580       else
581       {
582         Standard_Integer aIdx = aBVHTree->BegPrimitive (aNode);
583         const OpenGl_Structure* aStruct = isTrsfPers
584                                         ? myBVHPrimitivesTrsfPers.GetStructureById (aIdx)
585                                         : myBVHPrimitives.GetStructureById (aIdx);
586         aStruct->MarkAsNotCulled();
587         if (aHead < 0)
588         {
589           break;
590         }
591
592         aNode = aStack[aHead--];
593       }
594     }
595   }
596 }
597
598 // =======================================================================
599 // function : Append
600 // purpose  :
601 // =======================================================================
602 Standard_Boolean OpenGl_Layer::Append (const OpenGl_Layer& theOther)
603 {
604   // the source priority list shouldn't have more priorities
605   const Standard_Integer aNbPriorities = theOther.NbPriorities();
606   if (aNbPriorities > NbPriorities())
607   {
608     return Standard_False;
609   }
610
611   // add all structures to destination priority list
612   for (Standard_Integer aPriorityIter = 0; aPriorityIter < aNbPriorities; ++aPriorityIter)
613   {
614     const OpenGl_IndexedMapOfStructure& aStructures = theOther.myArray (aPriorityIter);
615     for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (aStructures); aStructIter.More(); aStructIter.Next())
616     {
617       Add (aStructIter.Value(), aPriorityIter);
618     }
619   }
620
621   return Standard_True;
622 }
623
624 //=======================================================================
625 //function : SetLayerSettings
626 //purpose  :
627 //=======================================================================
628 void OpenGl_Layer::SetLayerSettings (const Graphic3d_ZLayerSettings& theSettings)
629 {
630   const Standard_Boolean toUpdateTrsf = !myLayerSettings.Origin().IsEqual (theSettings.Origin(), gp::Resolution());
631   myLayerSettings = theSettings;
632   if (toUpdateTrsf)
633   {
634     for (OpenGl_ArrayOfIndexedMapOfStructure::Iterator aMapIter (myArray); aMapIter.More(); aMapIter.Next())
635     {
636       OpenGl_IndexedMapOfStructure& aStructures = aMapIter.ChangeValue();
637       for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (aStructures); aStructIter.More(); aStructIter.Next())
638       {
639         OpenGl_Structure* aStructure = const_cast<OpenGl_Structure*> (aStructIter.Value());
640         aStructure->updateLayerTransformation();
641       }
642     }
643   }
644 }
645
646 //=======================================================================
647 //function : Render
648 //purpose  :
649 //=======================================================================
650 void OpenGl_Layer::Render (const Handle(OpenGl_Workspace)&   theWorkspace,
651                            const OpenGl_GlobalLayerSettings& theDefaultSettings) const
652 {
653   const Graphic3d_PolygonOffset anAppliedOffsetParams = theWorkspace->AppliedPolygonOffset();
654   // myLayerSettings.ToClearDepth() is handled outside
655
656   // handle depth test
657   if (myLayerSettings.ToEnableDepthTest())
658   {
659     // assuming depth test is enabled by default
660     glDepthFunc (theDefaultSettings.DepthFunc);
661   }
662   else
663   {
664     glDepthFunc (GL_ALWAYS);
665   }
666
667   // save environment texture
668   Handle(OpenGl_TextureSet) anEnvironmentTexture = theWorkspace->EnvironmentTexture();
669   if (!myLayerSettings.UseEnvironmentTexture())
670   {
671     theWorkspace->SetEnvironmentTexture (Handle(OpenGl_TextureSet)());
672   }
673
674   // handle depth offset
675   theWorkspace->SetPolygonOffset (myLayerSettings.PolygonOffset());
676
677   // handle depth write
678   theWorkspace->UseDepthWrite() = myLayerSettings.ToEnableDepthWrite() && theDefaultSettings.DepthMask == GL_TRUE;
679   glDepthMask (theWorkspace->UseDepthWrite() ? GL_TRUE : GL_FALSE);
680
681   const Standard_Boolean hasLocalCS = !myLayerSettings.OriginTransformation().IsNull();
682   const Handle(OpenGl_Context)&   aCtx         = theWorkspace->GetGlContext();
683   const Handle(Graphic3d_Camera)& aWorldCamera = theWorkspace->View()->Camera();
684   if (hasLocalCS)
685   {
686     // Apply local camera transformation.
687     // The vertex position is computed by the following formula in GLSL program:
688     //   gl_Position = occProjectionMatrix * occWorldViewMatrix * occModelWorldMatrix * occVertex;
689     // where:
690     //   occProjectionMatrix - matrix defining orthographic/perspective/stereographic projection
691     //   occWorldViewMatrix  - world-view  matrix defining Camera position and orientation
692     //   occModelWorldMatrix - model-world matrix defining Object transformation from local coordinate system to the world coordinate system
693     //   occVertex           - input vertex position
694     //
695     // Since double precision is quite expensive on modern GPUs, and not available on old hardware,
696     // all these values are passed with single float precision to the shader.
697     // As result, single precision become insufficient for handling objects far from the world origin.
698     //
699     // Several approaches can be used to solve precision issues:
700     //  - [Broute force] migrate to double precision for all matrices and vertex position.
701     //    This is too expensive for most hardware.
702     //  - Store only translation part with double precision and pass it to GLSL program.
703     //    This requires modified GLSL programs for computing transformation
704     //    and extra packing mechanism for hardware not supporting double precision natively.
705     //    This solution is less expensive then previous one.
706     //  - Move translation part of occModelWorldMatrix into occWorldViewMatrix.
707     //    The main idea here is that while moving Camera towards the object,
708     //    Camera translation part and Object translation part will compensate each other
709     //    to fit into single float precision.
710     //    But this operation should be performed with double precision - this is why we are moving
711     //    translation part of occModelWorldMatrix to occWorldViewMatrix.
712     //
713     // All approaches might be useful in different scenarios, but for the moment we consider the last one as main scenario.
714     // Here we do the trick:
715     //  - OpenGl_Layer defines the Local Origin, which is expected to be the center of objects stored within it.
716     //    This Local Origin is included into occWorldViewMatrix during rendering.
717     //  - OpenGl_Structure defines Object local transformation occModelWorldMatrix with subtracted Local Origin of the Layer.
718     //    This means that Object itself should be defined within either Local Transformation equal or near to Local Origin of the Layer.
719     theWorkspace->View()->SetLocalOrigin (myLayerSettings.Origin());
720
721     NCollection_Mat4<Standard_Real> aWorldView = aWorldCamera->OrientationMatrix();
722     Graphic3d_TransformUtils::Translate (aWorldView, myLayerSettings.Origin().X(), myLayerSettings.Origin().Y(), myLayerSettings.Origin().Z());
723
724     NCollection_Mat4<Standard_ShortReal> aWorldViewF;
725     aWorldViewF.ConvertFrom (aWorldView);
726     aCtx->WorldViewState.SetCurrent (aWorldViewF);
727     aCtx->ShaderManager()->UpdateClippingState();
728     aCtx->ShaderManager()->UpdateLightSourceState();
729   }
730
731   // render priority list
732   renderAll (theWorkspace);
733
734   if (hasLocalCS)
735   {
736     aCtx->ShaderManager()->RevertClippingState();
737     aCtx->ShaderManager()->UpdateLightSourceState();
738
739     aCtx->WorldViewState.SetCurrent (aWorldCamera->OrientationMatrixF());
740     theWorkspace->View() ->SetLocalOrigin (gp_XYZ (0.0, 0.0, 0.0));
741   }
742
743   // always restore polygon offset between layers rendering
744   theWorkspace->SetPolygonOffset (anAppliedOffsetParams);
745
746   // restore environment texture
747   if (!myLayerSettings.UseEnvironmentTexture())
748   {
749     theWorkspace->SetEnvironmentTexture (anEnvironmentTexture);
750   }
751 }