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