0027130: Visualization, Ray tracing - skip structures with transformation persistence...
[occt.git] / src / OpenGl / OpenGl_SceneGeometry.cxx
1 // Created on: 2013-08-27
2 // Created by: Denis BOGOLEPOV
3 // Copyright (c) 2013 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 <OSD_Timer.hxx>
17 #include <OSD_Parallel.hxx>
18 #include <Standard_Assert.hxx>
19 #include <OpenGl_ArbTexBindless.hxx>
20 #include <OpenGl_PrimitiveArray.hxx>
21 #include <OpenGl_SceneGeometry.hxx>
22 #include <OpenGl_Structure.hxx>
23 #include <Graphic3d_GraphicDriver.hxx>
24
25 //! Use this macro to output BVH profiling info
26 // #define RAY_TRACE_PRINT_INFO
27
28 namespace
29 {
30   //! Useful constant for null floating-point 4D vector.
31   static const BVH_Vec4f ZERO_VEC_4F;
32 }
33
34 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_TriangleSet, OpenGl_BVHTriangulation3f)
35
36 // =======================================================================
37 // function : OpenGl_RaytraceMaterial
38 // purpose  : Creates new default material
39 // =======================================================================
40 OpenGl_RaytraceMaterial::OpenGl_RaytraceMaterial()
41 : Ambient      (ZERO_VEC_4F),
42   Diffuse      (ZERO_VEC_4F),
43   Specular     (ZERO_VEC_4F),
44   Emission     (ZERO_VEC_4F),
45   Reflection   (ZERO_VEC_4F),
46   Refraction   (ZERO_VEC_4F),
47   Transparency (ZERO_VEC_4F)
48 { }
49
50 // =======================================================================
51 // function : OpenGl_RaytraceLight
52 // purpose  : Creates new light source
53 // =======================================================================
54 OpenGl_RaytraceLight::OpenGl_RaytraceLight (const BVH_Vec4f& theEmission,
55                                             const BVH_Vec4f& thePosition)
56 : Emission (theEmission),
57   Position (thePosition)
58 {
59   //
60 }
61
62 // =======================================================================
63 // function : QuadBVH
64 // purpose  : Returns quad BVH (QBVH) tree produced from binary BVH
65 // =======================================================================
66 const QuadBvhHandle& OpenGl_TriangleSet::QuadBVH()
67 {
68   if (!myIsDirty)
69   {
70     Standard_ASSERT_RAISE (!myQuadBVH.IsNull(), "Error! BVH was not collapsed into QBVH");
71   }
72   else
73   {
74     myQuadBVH = BVH()->CollapseToQuadTree(); // build binary BVH and collapse it
75
76     myBVH->Clear(); // erase binary BVH
77   }
78
79   return myQuadBVH;
80 }
81
82 // =======================================================================
83 // function : Center
84 // purpose  : Returns centroid position along the given axis
85 // =======================================================================
86 Standard_ShortReal OpenGl_TriangleSet::Center (
87   const Standard_Integer theIndex, const Standard_Integer theAxis) const
88 {
89   // Note: Experiments show that the use of the box centroid (instead
90   // of the triangle centroid) increases render performance up to 12%
91
92   const BVH_Vec4i& aTriangle = Elements[theIndex];
93
94   const Standard_ShortReal aVertex0 =
95     BVH::VecComp<Standard_ShortReal, 3>::Get (Vertices[aTriangle.x()], theAxis);
96   const Standard_ShortReal aVertex1 =
97     BVH::VecComp<Standard_ShortReal, 3>::Get (Vertices[aTriangle.y()], theAxis);
98   const Standard_ShortReal aVertex2 =
99     BVH::VecComp<Standard_ShortReal, 3>::Get (Vertices[aTriangle.z()], theAxis);
100
101   return (Min (Min (aVertex0, aVertex1), aVertex2) +
102           Max (Max (aVertex0, aVertex1), aVertex2)) * 0.5f;
103 }
104
105 // =======================================================================
106 // function : Box
107 // purpose  : Returns AABB of primitive set
108 // =======================================================================
109 OpenGl_TriangleSet::BVH_BoxNt OpenGl_TriangleSet::Box() const
110 {
111   BVH_BoxNt aBox = BVH_PrimitiveSet<Standard_ShortReal, 3>::Box();
112   const BVH_Transform<Standard_ShortReal, 4>* aTransform = dynamic_cast<const BVH_Transform<Standard_ShortReal, 4>* > (Properties().get());
113   if (aTransform == NULL)
114   {
115     return aBox;
116   }
117
118   BVH_BoxNt aTransformedBox;
119   for (Standard_Integer aX = 0; aX <= 1; ++aX)
120   {
121     for (Standard_Integer aY = 0; aY <= 1; ++aY)
122     {
123       for (Standard_Integer aZ = 0; aZ <= 1; ++aZ)
124       {
125         BVH_Vec4f aCorner = aTransform->Transform() * BVH_Vec4f (
126           aX == 0 ? aBox.CornerMin().x() : aBox.CornerMax().x(),
127           aY == 0 ? aBox.CornerMin().y() : aBox.CornerMax().y(),
128           aZ == 0 ? aBox.CornerMin().z() : aBox.CornerMax().z(),
129           1.f);
130
131         aTransformedBox.Add (aCorner.xyz());
132       }
133     }
134   }
135   return aTransformedBox;
136 }
137
138 // =======================================================================
139 // function : OpenGl_TriangleSet
140 // purpose  : Creates new OpenGL element triangulation
141 // =======================================================================
142 OpenGl_TriangleSet::OpenGl_TriangleSet (const Standard_Size theArrayID,
143                                         const opencascade::handle<BVH_Builder<Standard_ShortReal, 3> >& theBuilder)
144 : BVH_Triangulation<Standard_ShortReal, 3> (theBuilder),
145   myArrayID (theArrayID)
146 {
147   //
148 }
149
150 // =======================================================================
151 // function : Clear
152 // purpose  : Clears ray-tracing geometry
153 // =======================================================================
154 void OpenGl_RaytraceGeometry::Clear()
155 {
156   BVH_Geometry<Standard_ShortReal, 3>::BVH_Geometry::Clear();
157
158   std::vector<OpenGl_RaytraceLight,
159     NCollection_StdAllocator<OpenGl_RaytraceLight> > anEmptySources;
160
161   Sources.swap (anEmptySources);
162
163   std::vector<OpenGl_RaytraceMaterial,
164     NCollection_StdAllocator<OpenGl_RaytraceMaterial> > anEmptyMaterials;
165
166   Materials.swap (anEmptyMaterials);
167 }
168
169 struct OpenGL_BVHParallelBuilder
170 {
171   BVH_ObjectSet<Standard_ShortReal, 3>* Set;
172
173   OpenGL_BVHParallelBuilder (BVH_ObjectSet<Standard_ShortReal, 3>* theSet)
174   : Set (theSet)
175   {
176     //
177   }
178
179   void operator() (const Standard_Integer theObjectIdx) const
180   {
181     OpenGl_TriangleSet* aTriangleSet = dynamic_cast<OpenGl_TriangleSet*> (
182       Set->Objects().ChangeValue (static_cast<Standard_Integer> (theObjectIdx)).operator->());
183
184     if (aTriangleSet != NULL)
185       aTriangleSet->QuadBVH();
186   }
187 };
188
189 // =======================================================================
190 // function : ProcessAcceleration
191 // purpose  : Performs post-processing of high-level BVH
192 // =======================================================================
193 Standard_Boolean OpenGl_RaytraceGeometry::ProcessAcceleration()
194 {
195 #ifdef RAY_TRACE_PRINT_INFO
196   OSD_Timer aTimer;
197 #endif
198
199   MarkDirty(); // force BVH rebuilding
200
201 #ifdef RAY_TRACE_PRINT_INFO
202   aTimer.Reset();
203   aTimer.Start();
204 #endif
205
206   OSD_Parallel::For (0, Size(), OpenGL_BVHParallelBuilder (this));
207
208   myBotLevelTreeDepth = 1;
209
210   for (Standard_Integer anObjectIdx = 0; anObjectIdx < Size(); ++anObjectIdx)
211   {
212     OpenGl_TriangleSet* aTriangleSet = dynamic_cast<OpenGl_TriangleSet*> (
213       myObjects.ChangeValue (anObjectIdx).operator->());
214
215     Standard_ASSERT_RETURN (aTriangleSet != NULL,
216       "Error! Failed to get triangulation of OpenGL element", Standard_False);
217
218     Standard_ASSERT_RETURN (!aTriangleSet->QuadBVH().IsNull(),
219       "Error! Failed to update bottom-level BVH of OpenGL element", Standard_False);
220
221     QuadBvhHandle aBVH = aTriangleSet->QuadBVH();
222
223     // correct data array of bottom-level BVH to set special flag for outer
224     // nodes in order to distinguish them from outer nodes of top-level BVH
225     for (Standard_Integer aNodeIdx = 0; aNodeIdx < aBVH->Length(); ++aNodeIdx)
226     {
227       if (aBVH->IsOuter (aNodeIdx))
228       {
229         aBVH->NodeInfoBuffer()[aNodeIdx].x() = -1;
230       }
231     }
232
233     myBotLevelTreeDepth = Max (myBotLevelTreeDepth, aTriangleSet->QuadBVH()->Depth());
234   }
235
236 #ifdef RAY_TRACE_PRINT_INFO
237   aTimer.Stop();
238
239   std::cout << "Updating bottom-level BVHs (sec): " <<
240     aTimer.ElapsedTime() << std::endl;
241 #endif
242
243 #ifdef RAY_TRACE_PRINT_INFO
244   aTimer.Reset();
245   aTimer.Start();
246 #endif
247
248   QuadBvhHandle aBVH = QuadBVH();
249
250   Standard_ASSERT_RETURN (!aBVH.IsNull(),
251     "Error! Failed to update high-level BVH of ray-tracing scene", Standard_False);
252
253   myTopLevelTreeDepth = aBVH->Depth();
254
255 #ifdef RAY_TRACE_PRINT_INFO
256   aTimer.Stop();
257
258   std::cout << "Updating high-level BVH (sec): " <<
259     aTimer.ElapsedTime() << std::endl;
260 #endif
261
262   Standard_Integer aVerticesOffset = 0;
263   Standard_Integer aElementsOffset = 0;
264   Standard_Integer aBvhNodesOffset = QuadBVH()->Length();
265
266   for (Standard_Integer aNodeIdx = 0; aNodeIdx < aBVH->Length(); ++aNodeIdx)
267   {
268     if (aBVH->IsOuter (aNodeIdx))
269     {
270       Standard_ASSERT_RETURN (aBVH->BegPrimitive (aNodeIdx) == aBVH->EndPrimitive (aNodeIdx),
271         "Error! Invalid leaf node in high-level BVH (contains several objects)", Standard_False);
272
273       const Standard_Integer anObjectIdx = aBVH->BegPrimitive (aNodeIdx);
274
275       Standard_ASSERT_RETURN (anObjectIdx < myObjects.Size(),
276         "Error! Invalid leaf node in high-level BVH (contains out-of-range object)", Standard_False);
277
278       OpenGl_TriangleSet* aTriangleSet = dynamic_cast<OpenGl_TriangleSet*> (myObjects (anObjectIdx).get());
279
280       // Note: We overwrite node info record to store parameters
281       // of bottom-level BVH and triangulation of OpenGL element
282
283       aBVH->NodeInfoBuffer()[aNodeIdx] = BVH_Vec4i (anObjectIdx + 1, // to keep leaf flag
284                                                     aBvhNodesOffset,
285                                                     aVerticesOffset,
286                                                     aElementsOffset);
287
288       aVerticesOffset += static_cast<Standard_Integer> (aTriangleSet->Vertices.size());
289       aElementsOffset += static_cast<Standard_Integer> (aTriangleSet->Elements.size());
290
291       aBvhNodesOffset += aTriangleSet->QuadBVH()->Length();
292     }
293   }
294
295   return Standard_True;
296 }
297
298 // =======================================================================
299 // function : QuadBVH
300 // purpose  : Returns quad BVH (QBVH) tree produced from binary BVH
301 // =======================================================================
302 const QuadBvhHandle& OpenGl_RaytraceGeometry::QuadBVH()
303 {
304   if (!myIsDirty)
305   {
306     Standard_ASSERT_RAISE (!myQuadBVH.IsNull(), "Error! BVH was not collapsed into QBVH");
307   }
308   else
309   {
310     myQuadBVH = BVH()->CollapseToQuadTree(); // build binary BVH and collapse it
311
312     myBVH->Clear(); // erase binary BVH
313   }
314
315   return myQuadBVH;
316 }
317
318 // =======================================================================
319 // function : AccelerationOffset
320 // purpose  : Returns offset of bottom-level BVH for given leaf node
321 // =======================================================================
322 Standard_Integer OpenGl_RaytraceGeometry::AccelerationOffset (Standard_Integer theNodeIdx)
323 {
324   const QuadBvhHandle& aBVH = QuadBVH();
325
326   if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
327     return INVALID_OFFSET;
328
329   return aBVH->NodeInfoBuffer().at (theNodeIdx).y();
330 }
331
332 // =======================================================================
333 // function : VerticesOffset
334 // purpose  : Returns offset of triangulation vertices for given leaf node
335 // =======================================================================
336 Standard_Integer OpenGl_RaytraceGeometry::VerticesOffset (Standard_Integer theNodeIdx)
337 {
338   const QuadBvhHandle& aBVH = QuadBVH();
339
340   if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
341     return INVALID_OFFSET;
342
343   return aBVH->NodeInfoBuffer().at (theNodeIdx).z();
344 }
345
346 // =======================================================================
347 // function : ElementsOffset
348 // purpose  : Returns offset of triangulation elements for given leaf node
349 // =======================================================================
350 Standard_Integer OpenGl_RaytraceGeometry::ElementsOffset (Standard_Integer theNodeIdx)
351 {
352   const QuadBvhHandle& aBVH = QuadBVH();
353
354   if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
355     return INVALID_OFFSET;
356
357   return aBVH->NodeInfoBuffer().at (theNodeIdx).w();
358 }
359
360 // =======================================================================
361 // function : TriangleSet
362 // purpose  : Returns triangulation data for given leaf node
363 // =======================================================================
364 OpenGl_TriangleSet* OpenGl_RaytraceGeometry::TriangleSet (Standard_Integer theNodeIdx)
365 {
366   const QuadBvhHandle& aBVH = QuadBVH();
367
368   if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
369     return NULL;
370
371   if (aBVH->NodeInfoBuffer().at (theNodeIdx).x() > myObjects.Size())
372     return NULL;
373
374   return dynamic_cast<OpenGl_TriangleSet*> (
375     myObjects (aBVH->NodeInfoBuffer().at (theNodeIdx).x() - 1).get());
376 }
377
378 // =======================================================================
379 // function : AcquireTextures
380 // purpose  : Makes the OpenGL texture handles resident
381 // =======================================================================
382 Standard_Boolean OpenGl_RaytraceGeometry::AcquireTextures (const Handle(OpenGl_Context)& theContext)
383 {
384   if (theContext->arbTexBindless == NULL)
385   {
386     return Standard_True;
387   }
388
389 #if !defined(GL_ES_VERSION_2_0)
390   Standard_Integer aTexIter = 0;
391   for (NCollection_Vector<Handle(OpenGl_Texture)>::Iterator aTexSrcIter (myTextures); aTexSrcIter.More(); aTexSrcIter.Next(), ++aTexIter)
392   {
393     GLuint64& aHandle = myTextureHandles[aTexIter];
394     const Handle(OpenGl_Texture)& aTexture = aTexSrcIter.Value();
395     if (!aTexture->Sampler()->IsValid()
396      || !aTexture->Sampler()->IsImmutable())
397     {
398       // need to recreate texture sampler handle
399       aHandle = GLuint64(-1); // specs do not define value for invalid handle, set -1 to initialize something
400       if (!aTexture->InitSamplerObject (theContext))
401       {
402         continue;
403       }
404
405       aTexture->Sampler()->SetImmutable();
406       aHandle = theContext->arbTexBindless->glGetTextureSamplerHandleARB (aTexture->TextureId(), aTexture->Sampler()->SamplerID());
407       const GLenum anErr = glGetError();
408       if (anErr != GL_NO_ERROR)
409       {
410         theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
411                                  TCollection_AsciiString ("Error: Failed to get 64-bit handle of OpenGL texture #") + int(anErr));
412         myTextureHandles.clear();
413         return Standard_False;
414       }
415     }
416
417     theContext->arbTexBindless->glMakeTextureHandleResidentARB (aHandle);
418     const GLenum anErr = glGetError();
419     if (anErr != GL_NO_ERROR)
420     {
421       theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
422                                TCollection_AsciiString ("Error: Failed to make OpenGL texture resident #") + int(anErr));
423       return Standard_False;
424     }
425   }
426 #endif
427
428   return Standard_True;
429 }
430
431 // =======================================================================
432 // function : ReleaseTextures
433 // purpose  : Makes the OpenGL texture handles non-resident
434 // =======================================================================
435 Standard_Boolean OpenGl_RaytraceGeometry::ReleaseTextures (const Handle(OpenGl_Context)& theContext) const
436 {
437   if (theContext->arbTexBindless == NULL)
438   {
439     return Standard_True;
440   }
441
442 #if !defined(GL_ES_VERSION_2_0)
443   for (size_t aTexIter = 0; aTexIter < myTextureHandles.size(); ++aTexIter)
444   {
445     theContext->arbTexBindless->glMakeTextureHandleNonResidentARB (myTextureHandles[aTexIter]);
446     const GLenum anErr = glGetError();
447     if (anErr != GL_NO_ERROR)
448     {
449       theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
450                                TCollection_AsciiString("Error: Failed to make OpenGL texture non-resident #") + int(anErr));
451       return Standard_False;
452     }
453   }
454 #endif
455
456   return Standard_True;
457 }
458
459 // =======================================================================
460 // function : AddTexture
461 // purpose  : Adds new OpenGL texture to the scene and returns its index
462 // =======================================================================
463 Standard_Integer OpenGl_RaytraceGeometry::AddTexture (const Handle(OpenGl_Texture)& theTexture)
464 {
465   if (theTexture->TextureId() == OpenGl_Texture::NO_TEXTURE)
466   {
467     return -1;
468   }
469
470   NCollection_Vector<Handle (OpenGl_Texture)>::iterator anIter =
471     std::find (myTextures.begin(), myTextures.end(), theTexture);
472
473   if (anIter == myTextures.end())
474   {
475     if (myTextures.Size() >= MAX_TEX_NUMBER)
476     {
477       return -1;
478     }
479
480     myTextures.Append (theTexture);
481   }
482
483   return static_cast<Standard_Integer> (anIter - myTextures.begin());
484 }
485
486 // =======================================================================
487 // function : UpdateTextureHandles
488 // purpose  : Updates unique 64-bit texture handles to use in shaders
489 // =======================================================================
490 Standard_Boolean OpenGl_RaytraceGeometry::UpdateTextureHandles (const Handle(OpenGl_Context)& theContext)
491 {
492   if (theContext->arbTexBindless == NULL)
493   {
494     return Standard_False;
495   }
496
497   myTextureHandles.clear();
498   myTextureHandles.resize (myTextures.Size());
499
500 #if !defined(GL_ES_VERSION_2_0)
501   Standard_Integer aTexIter = 0;
502   for (NCollection_Vector<Handle(OpenGl_Texture)>::Iterator aTexSrcIter (myTextures); aTexSrcIter.More(); aTexSrcIter.Next(), ++aTexIter)
503   {
504     GLuint64& aHandle = myTextureHandles[aTexIter];
505     aHandle = GLuint64(-1); // specs do not define value for invalid handle, set -1 to initialize something
506
507     const Handle(OpenGl_Texture)& aTexture = aTexSrcIter.Value();
508     if (!aTexture->Sampler()->IsValid()
509      && !aTexture->InitSamplerObject (theContext))
510     {
511       continue;
512     }
513
514     aTexture->Sampler()->SetImmutable();
515     aHandle = theContext->arbTexBindless->glGetTextureSamplerHandleARB (aTexture->TextureId(), aTexture->Sampler()->SamplerID());
516     const GLenum anErr = glGetError();
517     if (anErr != GL_NO_ERROR)
518     {
519       theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
520                                TCollection_AsciiString ("Error: Failed to get 64-bit handle of OpenGL texture #") + int(anErr));
521       myTextureHandles.clear();
522       return Standard_False;
523     }
524   }
525 #endif
526
527   return Standard_True;
528 }
529
530 namespace OpenGl_Raytrace
531 {
532   // =======================================================================
533   // function : IsRaytracedElement
534   // purpose  : Checks to see if the element contains ray-trace geometry
535   // =======================================================================
536   Standard_Boolean IsRaytracedElement (const OpenGl_ElementNode* theNode)
537   {
538     OpenGl_PrimitiveArray* anArray = dynamic_cast<OpenGl_PrimitiveArray*> (theNode->elem);
539     return anArray != NULL
540         && anArray->DrawMode() >= GL_TRIANGLES;
541   }
542
543   // =======================================================================
544   // function : IsRaytracedElement
545   // purpose  : Checks to see if the element contains ray-trace geometry
546   // =======================================================================
547   Standard_Boolean IsRaytracedElement (const OpenGl_Element* theElement)
548   {
549     const OpenGl_PrimitiveArray* anArray = dynamic_cast<const OpenGl_PrimitiveArray*> (theElement);
550     return anArray != NULL
551         && anArray->DrawMode() >= GL_TRIANGLES;
552   }
553
554   // =======================================================================
555   // function : IsRaytracedGroup
556   // purpose  : Checks to see if the group contains ray-trace geometry
557   // =======================================================================
558   Standard_Boolean IsRaytracedGroup (const OpenGl_Group* theGroup)
559   {
560     if (theGroup->HasPersistence())
561     {
562       return Standard_False;
563     }
564
565     for (const OpenGl_ElementNode* aNode = theGroup->FirstNode(); aNode != NULL; aNode = aNode->next)
566     {
567       if (IsRaytracedElement (aNode))
568       {
569         return Standard_True;
570       }
571     }
572     return Standard_False;
573   }
574 }