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