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 <OpenGl_SceneGeometry.hxx>
18 #include <Graphic3d_GraphicDriver.hxx>
19 #include <OSD_Parallel.hxx>
20 #include <OSD_Timer.hxx>
21 #include <Standard_Assert.hxx>
22 #include <OpenGl_ArbTexBindless.hxx>
23 #include <OpenGl_PrimitiveArray.hxx>
24 #include <OpenGl_Structure.hxx>
25 #include <OpenGl_TextureBuffer.hxx>
27 // Use this macro to output BVH profiling info
28 // #define RAY_TRACE_PRINT_INFO
32 //! Useful constant for null floating-point 4D vector.
33 static const BVH_Vec4f ZERO_VEC_4F;
36 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_TriangleSet, OpenGl_BVHTriangulation3f)
38 // =======================================================================
39 // function : OpenGl_RaytraceMaterial
40 // purpose : Creates new default material
41 // =======================================================================
42 OpenGl_RaytraceMaterial::OpenGl_RaytraceMaterial()
43 : Ambient (ZERO_VEC_4F),
44 Diffuse (ZERO_VEC_4F),
45 Specular (ZERO_VEC_4F),
46 Emission (ZERO_VEC_4F),
47 Reflection (ZERO_VEC_4F),
48 Refraction (ZERO_VEC_4F),
49 Transparency (ZERO_VEC_4F)
52 // =======================================================================
53 // function : OpenGl_RaytraceLight
54 // purpose : Creates new light source
55 // =======================================================================
56 OpenGl_RaytraceLight::OpenGl_RaytraceLight (const BVH_Vec4f& theEmission,
57 const BVH_Vec4f& thePosition)
58 : Emission (theEmission),
59 Position (thePosition)
64 // =======================================================================
66 // purpose : Returns quad BVH (QBVH) tree produced from binary BVH
67 // =======================================================================
68 const QuadBvhHandle& OpenGl_TriangleSet::QuadBVH()
72 Standard_ASSERT_RAISE (!myQuadBVH.IsNull(), "Error! BVH was not collapsed into QBVH");
76 myQuadBVH = BVH()->CollapseToQuadTree(); // build binary BVH and collapse it
78 myBVH->Clear(); // erase binary BVH
84 // =======================================================================
86 // purpose : Returns centroid position along the given axis
87 // =======================================================================
88 Standard_ShortReal OpenGl_TriangleSet::Center (
89 const Standard_Integer theIndex, const Standard_Integer theAxis) const
91 // Note: Experiments show that the use of the box centroid (instead
92 // of the triangle centroid) increases render performance up to 12%
94 const BVH_Vec4i& aTriangle = Elements[theIndex];
96 const Standard_ShortReal aVertex0 =
97 BVH::VecComp<Standard_ShortReal, 3>::Get (Vertices[aTriangle.x()], theAxis);
98 const Standard_ShortReal aVertex1 =
99 BVH::VecComp<Standard_ShortReal, 3>::Get (Vertices[aTriangle.y()], theAxis);
100 const Standard_ShortReal aVertex2 =
101 BVH::VecComp<Standard_ShortReal, 3>::Get (Vertices[aTriangle.z()], theAxis);
103 return (Min (Min (aVertex0, aVertex1), aVertex2) +
104 Max (Max (aVertex0, aVertex1), aVertex2)) * 0.5f;
107 // =======================================================================
109 // purpose : Returns AABB of primitive set
110 // =======================================================================
111 OpenGl_TriangleSet::BVH_BoxNt OpenGl_TriangleSet::Box() const
113 BVH_BoxNt aBox = BVH_PrimitiveSet<Standard_ShortReal, 3>::Box();
114 const BVH_Transform<Standard_ShortReal, 4>* aTransform = dynamic_cast<const BVH_Transform<Standard_ShortReal, 4>* > (Properties().get());
115 if (aTransform == NULL)
120 BVH_BoxNt aTransformedBox;
121 for (Standard_Integer aX = 0; aX <= 1; ++aX)
123 for (Standard_Integer aY = 0; aY <= 1; ++aY)
125 for (Standard_Integer aZ = 0; aZ <= 1; ++aZ)
127 BVH_Vec4f aCorner = aTransform->Transform() * BVH_Vec4f (
128 aX == 0 ? aBox.CornerMin().x() : aBox.CornerMax().x(),
129 aY == 0 ? aBox.CornerMin().y() : aBox.CornerMax().y(),
130 aZ == 0 ? aBox.CornerMin().z() : aBox.CornerMax().z(),
133 aTransformedBox.Add (aCorner.xyz());
137 return aTransformedBox;
140 // =======================================================================
141 // function : OpenGl_TriangleSet
142 // purpose : Creates new OpenGL element triangulation
143 // =======================================================================
144 OpenGl_TriangleSet::OpenGl_TriangleSet (const Standard_Size theArrayID,
145 const opencascade::handle<BVH_Builder<Standard_ShortReal, 3> >& theBuilder)
146 : BVH_Triangulation<Standard_ShortReal, 3> (theBuilder),
147 myArrayID (theArrayID)
152 // =======================================================================
154 // purpose : Clears ray-tracing geometry
155 // =======================================================================
156 void OpenGl_RaytraceGeometry::Clear()
158 BVH_Geometry<Standard_ShortReal, 3>::BVH_Geometry::Clear();
160 std::vector<OpenGl_RaytraceLight,
161 NCollection_StdAllocator<OpenGl_RaytraceLight> > anEmptySources;
163 Sources.swap (anEmptySources);
165 std::vector<OpenGl_RaytraceMaterial,
166 NCollection_StdAllocator<OpenGl_RaytraceMaterial> > anEmptyMaterials;
168 Materials.swap (anEmptyMaterials);
171 struct OpenGL_BVHParallelBuilder
173 BVH_ObjectSet<Standard_ShortReal, 3>* Set;
175 OpenGL_BVHParallelBuilder (BVH_ObjectSet<Standard_ShortReal, 3>* theSet)
181 void operator() (const Standard_Integer theObjectIdx) const
183 OpenGl_TriangleSet* aTriangleSet = dynamic_cast<OpenGl_TriangleSet*> (
184 Set->Objects().ChangeValue (static_cast<Standard_Integer> (theObjectIdx)).operator->());
186 if (aTriangleSet != NULL)
187 aTriangleSet->QuadBVH();
191 // =======================================================================
192 // function : ProcessAcceleration
193 // purpose : Performs post-processing of high-level BVH
194 // =======================================================================
195 Standard_Boolean OpenGl_RaytraceGeometry::ProcessAcceleration()
197 #ifdef RAY_TRACE_PRINT_INFO
201 MarkDirty(); // force BVH rebuilding
203 #ifdef RAY_TRACE_PRINT_INFO
208 OSD_Parallel::For (0, Size(), OpenGL_BVHParallelBuilder (this));
210 myBotLevelTreeDepth = 1;
212 for (Standard_Integer anObjectIdx = 0; anObjectIdx < Size(); ++anObjectIdx)
214 OpenGl_TriangleSet* aTriangleSet = dynamic_cast<OpenGl_TriangleSet*> (
215 myObjects.ChangeValue (anObjectIdx).operator->());
217 Standard_ASSERT_RETURN (aTriangleSet != NULL,
218 "Error! Failed to get triangulation of OpenGL element", Standard_False);
220 Standard_ASSERT_RETURN (!aTriangleSet->QuadBVH().IsNull(),
221 "Error! Failed to update bottom-level BVH of OpenGL element", Standard_False);
223 QuadBvhHandle aBVH = aTriangleSet->QuadBVH();
225 // correct data array of bottom-level BVH to set special flag for outer
226 // nodes in order to distinguish them from outer nodes of top-level BVH
227 for (Standard_Integer aNodeIdx = 0; aNodeIdx < aBVH->Length(); ++aNodeIdx)
229 if (aBVH->IsOuter (aNodeIdx))
231 aBVH->NodeInfoBuffer()[aNodeIdx].x() = -1;
235 myBotLevelTreeDepth = Max (myBotLevelTreeDepth, aTriangleSet->QuadBVH()->Depth());
238 #ifdef RAY_TRACE_PRINT_INFO
241 std::cout << "Updating bottom-level BVHs (sec): " <<
242 aTimer.ElapsedTime() << std::endl;
245 #ifdef RAY_TRACE_PRINT_INFO
250 QuadBvhHandle aBVH = QuadBVH();
252 Standard_ASSERT_RETURN (!aBVH.IsNull(),
253 "Error! Failed to update high-level BVH of ray-tracing scene", Standard_False);
255 myTopLevelTreeDepth = aBVH->Depth();
257 #ifdef RAY_TRACE_PRINT_INFO
260 std::cout << "Updating high-level BVH (sec): " <<
261 aTimer.ElapsedTime() << std::endl;
264 Standard_Integer aVerticesOffset = 0;
265 Standard_Integer aElementsOffset = 0;
266 Standard_Integer aBvhNodesOffset = QuadBVH()->Length();
268 for (Standard_Integer aNodeIdx = 0; aNodeIdx < aBVH->Length(); ++aNodeIdx)
270 if (aBVH->IsOuter (aNodeIdx))
272 Standard_ASSERT_RETURN (aBVH->BegPrimitive (aNodeIdx) == aBVH->EndPrimitive (aNodeIdx),
273 "Error! Invalid leaf node in high-level BVH (contains several objects)", Standard_False);
275 const Standard_Integer anObjectIdx = aBVH->BegPrimitive (aNodeIdx);
277 Standard_ASSERT_RETURN (anObjectIdx < myObjects.Size(),
278 "Error! Invalid leaf node in high-level BVH (contains out-of-range object)", Standard_False);
280 OpenGl_TriangleSet* aTriangleSet = dynamic_cast<OpenGl_TriangleSet*> (myObjects (anObjectIdx).get());
282 // Note: We overwrite node info record to store parameters
283 // of bottom-level BVH and triangulation of OpenGL element
285 aBVH->NodeInfoBuffer()[aNodeIdx] = BVH_Vec4i (anObjectIdx + 1, // to keep leaf flag
290 aVerticesOffset += static_cast<Standard_Integer> (aTriangleSet->Vertices.size());
291 aElementsOffset += static_cast<Standard_Integer> (aTriangleSet->Elements.size());
293 aBvhNodesOffset += aTriangleSet->QuadBVH()->Length();
297 return Standard_True;
300 // =======================================================================
301 // function : QuadBVH
302 // purpose : Returns quad BVH (QBVH) tree produced from binary BVH
303 // =======================================================================
304 const QuadBvhHandle& OpenGl_RaytraceGeometry::QuadBVH()
308 Standard_ASSERT_RAISE (!myQuadBVH.IsNull(), "Error! BVH was not collapsed into QBVH");
312 myQuadBVH = BVH()->CollapseToQuadTree(); // build binary BVH and collapse it
314 myBVH->Clear(); // erase binary BVH
320 // =======================================================================
321 // function : AccelerationOffset
322 // purpose : Returns offset of bottom-level BVH for given leaf node
323 // =======================================================================
324 Standard_Integer OpenGl_RaytraceGeometry::AccelerationOffset (Standard_Integer theNodeIdx)
326 const QuadBvhHandle& aBVH = QuadBVH();
328 if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
329 return INVALID_OFFSET;
331 return aBVH->NodeInfoBuffer().at (theNodeIdx).y();
334 // =======================================================================
335 // function : VerticesOffset
336 // purpose : Returns offset of triangulation vertices for given leaf node
337 // =======================================================================
338 Standard_Integer OpenGl_RaytraceGeometry::VerticesOffset (Standard_Integer theNodeIdx)
340 const QuadBvhHandle& aBVH = QuadBVH();
342 if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
343 return INVALID_OFFSET;
345 return aBVH->NodeInfoBuffer().at (theNodeIdx).z();
348 // =======================================================================
349 // function : ElementsOffset
350 // purpose : Returns offset of triangulation elements for given leaf node
351 // =======================================================================
352 Standard_Integer OpenGl_RaytraceGeometry::ElementsOffset (Standard_Integer theNodeIdx)
354 const QuadBvhHandle& aBVH = QuadBVH();
356 if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
357 return INVALID_OFFSET;
359 return aBVH->NodeInfoBuffer().at (theNodeIdx).w();
362 // =======================================================================
363 // function : TriangleSet
364 // purpose : Returns triangulation data for given leaf node
365 // =======================================================================
366 OpenGl_TriangleSet* OpenGl_RaytraceGeometry::TriangleSet (Standard_Integer theNodeIdx)
368 const QuadBvhHandle& aBVH = QuadBVH();
370 if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
373 if (aBVH->NodeInfoBuffer().at (theNodeIdx).x() > myObjects.Size())
376 return dynamic_cast<OpenGl_TriangleSet*> (
377 myObjects (aBVH->NodeInfoBuffer().at (theNodeIdx).x() - 1).get());
380 // =======================================================================
381 // function : AcquireTextures
382 // purpose : Makes the OpenGL texture handles resident
383 // =======================================================================
384 Standard_Boolean OpenGl_RaytraceGeometry::AcquireTextures (const Handle(OpenGl_Context)& theContext)
386 if (theContext->arbTexBindless == NULL)
388 return Standard_True;
391 Standard_Integer aTexIter = 0;
392 for (NCollection_Vector<Handle(OpenGl_Texture)>::Iterator aTexSrcIter (myTextures); aTexSrcIter.More(); aTexSrcIter.Next(), ++aTexIter)
394 GLuint64& aHandle = myTextureHandles[aTexIter];
395 const Handle(OpenGl_Texture)& aTexture = aTexSrcIter.Value();
396 if (!aTexture->Sampler()->IsValid()
397 || !aTexture->Sampler()->IsImmutable())
399 // need to recreate texture sampler handle
400 aHandle = GLuint64(-1); // specs do not define value for invalid handle, set -1 to initialize something
401 if (!aTexture->InitSamplerObject (theContext))
406 aTexture->Sampler()->SetImmutable();
407 aHandle = theContext->arbTexBindless->glGetTextureSamplerHandleARB (aTexture->TextureId(), aTexture->Sampler()->SamplerID());
408 const GLenum anErr = theContext->core11fwd->glGetError();
409 if (anErr != GL_NO_ERROR)
411 theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
412 TCollection_AsciiString ("Error: Failed to get 64-bit handle of OpenGL texture ") + OpenGl_Context::FormatGlError (anErr));
413 myTextureHandles.clear();
414 return Standard_False;
418 theContext->arbTexBindless->glMakeTextureHandleResidentARB (aHandle);
419 const GLenum anErr = theContext->core11fwd->glGetError();
420 if (anErr != GL_NO_ERROR)
422 theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
423 TCollection_AsciiString ("Error: Failed to make OpenGL texture resident ") + OpenGl_Context::FormatGlError (anErr));
424 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 for (size_t aTexIter = 0; aTexIter < myTextureHandles.size(); ++aTexIter)
444 theContext->arbTexBindless->glMakeTextureHandleNonResidentARB (myTextureHandles[aTexIter]);
445 const GLenum anErr = theContext->core11fwd->glGetError();
446 if (anErr != GL_NO_ERROR)
448 theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
449 TCollection_AsciiString("Error: Failed to make OpenGL texture non-resident ") + OpenGl_Context::FormatGlError (anErr));
450 return Standard_False;
454 return Standard_True;
457 // =======================================================================
458 // function : AddTexture
459 // purpose : Adds new OpenGL texture to the scene and returns its index
460 // =======================================================================
461 Standard_Integer OpenGl_RaytraceGeometry::AddTexture (const Handle(OpenGl_Texture)& theTexture)
463 if (theTexture->TextureId() == OpenGl_Texture::NO_TEXTURE)
468 NCollection_Vector<Handle (OpenGl_Texture)>::iterator anIter =
469 std::find (myTextures.begin(), myTextures.end(), theTexture);
471 if (anIter == myTextures.end())
473 if (myTextures.Size() >= MAX_TEX_NUMBER)
478 myTextures.Append (theTexture);
481 return static_cast<Standard_Integer> (anIter - myTextures.begin());
484 // =======================================================================
485 // function : UpdateTextureHandles
486 // purpose : Updates unique 64-bit texture handles to use in shaders
487 // =======================================================================
488 Standard_Boolean OpenGl_RaytraceGeometry::UpdateTextureHandles (const Handle(OpenGl_Context)& theContext)
490 if (theContext->arbTexBindless == NULL)
492 return Standard_False;
495 myTextureHandles.clear();
496 myTextureHandles.resize (myTextures.Size());
498 Standard_Integer aTexIter = 0;
499 for (NCollection_Vector<Handle(OpenGl_Texture)>::Iterator aTexSrcIter (myTextures); aTexSrcIter.More(); aTexSrcIter.Next(), ++aTexIter)
501 GLuint64& aHandle = myTextureHandles[aTexIter];
502 aHandle = GLuint64(-1); // specs do not define value for invalid handle, set -1 to initialize something
504 const Handle(OpenGl_Texture)& aTexture = aTexSrcIter.Value();
505 if (!aTexture->Sampler()->IsValid()
506 && !aTexture->InitSamplerObject (theContext))
511 aTexture->Sampler()->SetImmutable();
512 aHandle = theContext->arbTexBindless->glGetTextureSamplerHandleARB (aTexture->TextureId(), aTexture->Sampler()->SamplerID());
513 const GLenum anErr = theContext->core11fwd->glGetError();
514 if (anErr != GL_NO_ERROR)
516 theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
517 TCollection_AsciiString ("Error: Failed to get 64-bit handle of OpenGL texture ") + OpenGl_Context::FormatGlError(anErr));
518 myTextureHandles.clear();
519 return Standard_False;
523 return Standard_True;
526 namespace OpenGl_Raytrace
528 // =======================================================================
529 // function : IsRaytracedElement
530 // purpose : Checks to see if the element contains ray-trace geometry
531 // =======================================================================
532 Standard_Boolean IsRaytracedElement (const OpenGl_ElementNode* theNode)
534 OpenGl_PrimitiveArray* anArray = dynamic_cast<OpenGl_PrimitiveArray*> (theNode->elem);
535 return anArray != NULL
536 && anArray->DrawMode() >= GL_TRIANGLES;
539 // =======================================================================
540 // function : IsRaytracedElement
541 // purpose : Checks to see if the element contains ray-trace geometry
542 // =======================================================================
543 Standard_Boolean IsRaytracedElement (const OpenGl_Element* theElement)
545 const OpenGl_PrimitiveArray* anArray = dynamic_cast<const OpenGl_PrimitiveArray*> (theElement);
546 return anArray != NULL
547 && anArray->DrawMode() >= GL_TRIANGLES;
550 // =======================================================================
551 // function : IsRaytracedGroup
552 // purpose : Checks to see if the group contains ray-trace geometry
553 // =======================================================================
554 Standard_Boolean IsRaytracedGroup (const OpenGl_Group* theGroup)
556 if (theGroup->HasPersistence())
558 return Standard_False;
561 for (const OpenGl_ElementNode* aNode = theGroup->FirstNode(); aNode != NULL; aNode = aNode->next)
563 if (IsRaytracedElement (aNode))
565 return Standard_True;
568 return Standard_False;