1 // Created on: 2013-08-27
2 // Created by: Denis BOGOLEPOV
3 // Copyright (c) 2013 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
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.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
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>
25 //! Use this macro to output BVH profiling info
26 // #define RAY_TRACE_PRINT_INFO
30 //! Useful constant for null floating-point 4D vector.
31 static const BVH_Vec4f ZERO_VEC_4F;
34 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_TriangleSet, OpenGl_BVHTriangulation3f)
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)
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)
62 // =======================================================================
64 // purpose : Returns quad BVH (QBVH) tree produced from binary BVH
65 // =======================================================================
66 const QuadBvhHandle& OpenGl_TriangleSet::QuadBVH()
70 Standard_ASSERT_RAISE (!myQuadBVH.IsNull(), "Error! BVH was not collapsed into QBVH");
74 myQuadBVH = BVH()->CollapseToQuadTree(); // build binary BVH and collapse it
76 myBVH->Clear(); // erase binary BVH
82 // =======================================================================
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
89 // Note: Experiments show that the use of the box centroid (instead
90 // of the triangle centroid) increases render performance up to 12%
92 const BVH_Vec4i& aTriangle = Elements[theIndex];
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);
101 return (Min (Min (aVertex0, aVertex1), aVertex2) +
102 Max (Max (aVertex0, aVertex1), aVertex2)) * 0.5f;
105 // =======================================================================
107 // purpose : Returns AABB of primitive set
108 // =======================================================================
109 OpenGl_TriangleSet::BVH_BoxNt OpenGl_TriangleSet::Box() const
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)
118 BVH_BoxNt aTransformedBox;
119 for (Standard_Integer aX = 0; aX <= 1; ++aX)
121 for (Standard_Integer aY = 0; aY <= 1; ++aY)
123 for (Standard_Integer aZ = 0; aZ <= 1; ++aZ)
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(),
131 aTransformedBox.Add (aCorner.xyz());
135 return aTransformedBox;
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)
150 // =======================================================================
152 // purpose : Clears ray-tracing geometry
153 // =======================================================================
154 void OpenGl_RaytraceGeometry::Clear()
156 BVH_Geometry<Standard_ShortReal, 3>::BVH_Geometry::Clear();
158 std::vector<OpenGl_RaytraceLight,
159 NCollection_StdAllocator<OpenGl_RaytraceLight> > anEmptySources;
161 Sources.swap (anEmptySources);
163 std::vector<OpenGl_RaytraceMaterial,
164 NCollection_StdAllocator<OpenGl_RaytraceMaterial> > anEmptyMaterials;
166 Materials.swap (anEmptyMaterials);
169 struct OpenGL_BVHParallelBuilder
171 BVH_ObjectSet<Standard_ShortReal, 3>* Set;
173 OpenGL_BVHParallelBuilder (BVH_ObjectSet<Standard_ShortReal, 3>* theSet)
179 void operator() (const Standard_Integer theObjectIdx) const
181 OpenGl_TriangleSet* aTriangleSet = dynamic_cast<OpenGl_TriangleSet*> (
182 Set->Objects().ChangeValue (static_cast<Standard_Integer> (theObjectIdx)).operator->());
184 if (aTriangleSet != NULL)
185 aTriangleSet->QuadBVH();
189 // =======================================================================
190 // function : ProcessAcceleration
191 // purpose : Performs post-processing of high-level BVH
192 // =======================================================================
193 Standard_Boolean OpenGl_RaytraceGeometry::ProcessAcceleration()
195 #ifdef RAY_TRACE_PRINT_INFO
199 MarkDirty(); // force BVH rebuilding
201 #ifdef RAY_TRACE_PRINT_INFO
206 OSD_Parallel::For (0, Size(), OpenGL_BVHParallelBuilder (this));
208 myBotLevelTreeDepth = 1;
210 for (Standard_Integer anObjectIdx = 0; anObjectIdx < Size(); ++anObjectIdx)
212 OpenGl_TriangleSet* aTriangleSet = dynamic_cast<OpenGl_TriangleSet*> (
213 myObjects.ChangeValue (anObjectIdx).operator->());
215 Standard_ASSERT_RETURN (aTriangleSet != NULL,
216 "Error! Failed to get triangulation of OpenGL element", Standard_False);
218 Standard_ASSERT_RETURN (!aTriangleSet->QuadBVH().IsNull(),
219 "Error! Failed to update bottom-level BVH of OpenGL element", Standard_False);
221 QuadBvhHandle aBVH = aTriangleSet->QuadBVH();
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)
227 if (aBVH->IsOuter (aNodeIdx))
229 aBVH->NodeInfoBuffer()[aNodeIdx].x() = -1;
233 myBotLevelTreeDepth = Max (myBotLevelTreeDepth, aTriangleSet->QuadBVH()->Depth());
236 #ifdef RAY_TRACE_PRINT_INFO
239 std::cout << "Updating bottom-level BVHs (sec): " <<
240 aTimer.ElapsedTime() << std::endl;
243 #ifdef RAY_TRACE_PRINT_INFO
248 QuadBvhHandle aBVH = QuadBVH();
250 Standard_ASSERT_RETURN (!aBVH.IsNull(),
251 "Error! Failed to update high-level BVH of ray-tracing scene", Standard_False);
253 myTopLevelTreeDepth = aBVH->Depth();
255 #ifdef RAY_TRACE_PRINT_INFO
258 std::cout << "Updating high-level BVH (sec): " <<
259 aTimer.ElapsedTime() << std::endl;
262 Standard_Integer aVerticesOffset = 0;
263 Standard_Integer aElementsOffset = 0;
264 Standard_Integer aBvhNodesOffset = QuadBVH()->Length();
266 for (Standard_Integer aNodeIdx = 0; aNodeIdx < aBVH->Length(); ++aNodeIdx)
268 if (aBVH->IsOuter (aNodeIdx))
270 Standard_ASSERT_RETURN (aBVH->BegPrimitive (aNodeIdx) == aBVH->EndPrimitive (aNodeIdx),
271 "Error! Invalid leaf node in high-level BVH (contains several objects)", Standard_False);
273 const Standard_Integer anObjectIdx = aBVH->BegPrimitive (aNodeIdx);
275 Standard_ASSERT_RETURN (anObjectIdx < myObjects.Size(),
276 "Error! Invalid leaf node in high-level BVH (contains out-of-range object)", Standard_False);
278 OpenGl_TriangleSet* aTriangleSet = dynamic_cast<OpenGl_TriangleSet*> (myObjects (anObjectIdx).get());
280 // Note: We overwrite node info record to store parameters
281 // of bottom-level BVH and triangulation of OpenGL element
283 aBVH->NodeInfoBuffer()[aNodeIdx] = BVH_Vec4i (anObjectIdx + 1, // to keep leaf flag
288 aVerticesOffset += static_cast<Standard_Integer> (aTriangleSet->Vertices.size());
289 aElementsOffset += static_cast<Standard_Integer> (aTriangleSet->Elements.size());
291 aBvhNodesOffset += aTriangleSet->QuadBVH()->Length();
295 return Standard_True;
298 // =======================================================================
299 // function : QuadBVH
300 // purpose : Returns quad BVH (QBVH) tree produced from binary BVH
301 // =======================================================================
302 const QuadBvhHandle& OpenGl_RaytraceGeometry::QuadBVH()
306 Standard_ASSERT_RAISE (!myQuadBVH.IsNull(), "Error! BVH was not collapsed into QBVH");
310 myQuadBVH = BVH()->CollapseToQuadTree(); // build binary BVH and collapse it
312 myBVH->Clear(); // erase binary BVH
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)
324 const QuadBvhHandle& aBVH = QuadBVH();
326 if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
327 return INVALID_OFFSET;
329 return aBVH->NodeInfoBuffer().at (theNodeIdx).y();
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)
338 const QuadBvhHandle& aBVH = QuadBVH();
340 if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
341 return INVALID_OFFSET;
343 return aBVH->NodeInfoBuffer().at (theNodeIdx).z();
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)
352 const QuadBvhHandle& aBVH = QuadBVH();
354 if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
355 return INVALID_OFFSET;
357 return aBVH->NodeInfoBuffer().at (theNodeIdx).w();
360 // =======================================================================
361 // function : TriangleSet
362 // purpose : Returns triangulation data for given leaf node
363 // =======================================================================
364 OpenGl_TriangleSet* OpenGl_RaytraceGeometry::TriangleSet (Standard_Integer theNodeIdx)
366 const QuadBvhHandle& aBVH = QuadBVH();
368 if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
371 if (aBVH->NodeInfoBuffer().at (theNodeIdx).x() > myObjects.Size())
374 return dynamic_cast<OpenGl_TriangleSet*> (
375 myObjects (aBVH->NodeInfoBuffer().at (theNodeIdx).x() - 1).get());
378 // =======================================================================
379 // function : AcquireTextures
380 // purpose : Makes the OpenGL texture handles resident
381 // =======================================================================
382 Standard_Boolean OpenGl_RaytraceGeometry::AcquireTextures (const Handle(OpenGl_Context)& theContext)
384 if (theContext->arbTexBindless == NULL)
386 return Standard_True;
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)
393 GLuint64& aHandle = myTextureHandles[aTexIter];
394 const Handle(OpenGl_Texture)& aTexture = aTexSrcIter.Value();
395 if (!aTexture->Sampler()->IsValid()
396 || !aTexture->Sampler()->IsImmutable())
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))
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)
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;
417 theContext->arbTexBindless->glMakeTextureHandleResidentARB (aHandle);
418 const GLenum anErr = glGetError();
419 if (anErr != GL_NO_ERROR)
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;
428 return Standard_True;
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
437 if (theContext->arbTexBindless == NULL)
439 return Standard_True;
442 #if !defined(GL_ES_VERSION_2_0)
443 for (size_t aTexIter = 0; aTexIter < myTextureHandles.size(); ++aTexIter)
445 theContext->arbTexBindless->glMakeTextureHandleNonResidentARB (myTextureHandles[aTexIter]);
446 const GLenum anErr = glGetError();
447 if (anErr != GL_NO_ERROR)
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;
456 return Standard_True;
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)
465 if (theTexture->TextureId() == OpenGl_Texture::NO_TEXTURE)
470 NCollection_Vector<Handle (OpenGl_Texture)>::iterator anIter =
471 std::find (myTextures.begin(), myTextures.end(), theTexture);
473 if (anIter == myTextures.end())
475 if (myTextures.Size() >= MAX_TEX_NUMBER)
480 myTextures.Append (theTexture);
483 return static_cast<Standard_Integer> (anIter - myTextures.begin());
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)
492 if (theContext->arbTexBindless == NULL)
494 return Standard_False;
497 myTextureHandles.clear();
498 myTextureHandles.resize (myTextures.Size());
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)
504 GLuint64& aHandle = myTextureHandles[aTexIter];
505 aHandle = GLuint64(-1); // specs do not define value for invalid handle, set -1 to initialize something
507 const Handle(OpenGl_Texture)& aTexture = aTexSrcIter.Value();
508 if (!aTexture->Sampler()->IsValid()
509 && !aTexture->InitSamplerObject (theContext))
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)
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;
527 return Standard_True;
530 namespace OpenGl_Raytrace
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)
538 OpenGl_PrimitiveArray* anArray = dynamic_cast<OpenGl_PrimitiveArray*> (theNode->elem);
539 return anArray != NULL
540 && anArray->DrawMode() >= GL_TRIANGLES;
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)
549 const OpenGl_PrimitiveArray* anArray = dynamic_cast<const OpenGl_PrimitiveArray*> (theElement);
550 return anArray != NULL
551 && anArray->DrawMode() >= GL_TRIANGLES;
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)
560 if (theGroup->HasPersistence())
562 return Standard_False;
565 for (const OpenGl_ElementNode* aNode = theGroup->FirstNode(); aNode != NULL; aNode = aNode->next)
567 if (IsRaytracedElement (aNode))
569 return Standard_True;
572 return Standard_False;