0025854: Visualization, TKOpenGl - add option to request Core profile 3.2+
[occt.git] / src / ViewerTest / ViewerTest_OpenGlCommands.cxx
1 // Created on: 2012-04-09
2 // Created by: Sergey ANIKIN
3 // Copyright (c) 2012-2014 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 <ViewerTest.hxx>
17
18 #include <AIS_InteractiveContext.hxx>
19 #include <AIS_InteractiveObject.hxx>
20 #include <Draw.hxx>
21 #include <Draw_Interpretor.hxx>
22 #include <Graphic3d_Group.hxx>
23 #include <Graphic3d_ShaderObject.hxx>
24 #include <Graphic3d_ShaderProgram.hxx>
25 #include <OpenGl_AspectFace.hxx>
26 #include <OpenGl_AspectLine.hxx>
27 #include <OpenGl_AspectMarker.hxx>
28 #include <OpenGl_AspectText.hxx>
29 #include <OpenGl_Context.hxx>
30 #include <OpenGl_Element.hxx>
31 #include <OpenGl_GlCore20.hxx>
32 #include <OpenGl_GraphicDriver.hxx>
33 #include <OpenGl_Workspace.hxx>
34 #include <OSD_Environment.hxx>
35 #include <OSD_File.hxx>
36 #include <Prs3d_Drawer.hxx>
37 #include <Prs3d_Presentation.hxx>
38 #include <Prs3d_Root.hxx>
39 #include <Prs3d_ShadingAspect.hxx>
40 #include <Select3D_SensitiveCurve.hxx>
41 #include <SelectMgr_EntityOwner.hxx>
42 #include <SelectMgr_Selection.hxx>
43 #include <TCollection_AsciiString.hxx>
44 #include <V3d_View.hxx>
45 #include <V3d_Viewer.hxx>
46 #include <Visual3d_View.hxx>
47 #include <ViewerTest_DoubleMapOfInteractiveAndName.hxx>
48 #include <ViewerTest_DoubleMapIteratorOfDoubleMapOfInteractiveAndName.hxx>
49
50 extern Standard_Boolean VDisplayAISObject (const TCollection_AsciiString& theName,
51                                            const Handle(AIS_InteractiveObject)& theAISObj,
52                                            Standard_Boolean theReplaceIfExists = Standard_True);
53 extern ViewerTest_DoubleMapOfInteractiveAndName& GetMapOfAIS();
54
55 //=======================================================================
56 //function : VUserDraw
57 //purpose  : Checks availability and operation of UserDraw feature
58 //=======================================================================
59 DEFINE_STANDARD_HANDLE(VUserDrawObj, AIS_InteractiveObject)
60
61 class VUserDrawObj : public AIS_InteractiveObject
62 {
63 public:
64     // CASCADE RTTI
65     DEFINE_STANDARD_RTTI(VUserDrawObj);
66
67     VUserDrawObj()
68     {
69       myCoords[0] = -10.;
70       myCoords[1] = -20.;
71       myCoords[2] = -30.;
72       myCoords[3] =  10.;
73       myCoords[4] =  20.;
74       myCoords[5] =  30.;
75     }
76
77 public:
78   class Element : public OpenGl_Element
79   {
80   private:
81     Handle(VUserDrawObj) myIObj;
82
83   public:
84     Element (const Handle(VUserDrawObj)& theIObj,
85              Graphic3d_BndBox4f* theBounds)
86     : myIObj( theIObj )
87     {
88       if (!myIObj.IsNull())
89         myIObj->GetBounds(theBounds);
90     }
91
92     virtual ~Element ()
93     {
94     }
95
96     virtual void Render (const Handle(OpenGl_Workspace)& theWorkspace) const
97     {
98       if (!myIObj.IsNull())
99         myIObj->Render(theWorkspace);
100     }
101
102     virtual void Release (OpenGl_Context*)
103     {
104       //
105     }
106
107   public:
108     DEFINE_STANDARD_ALLOC
109   };
110
111 private:
112     // Virtual methods implementation
113     void Compute (const Handle(PrsMgr_PresentationManager3d)& thePresentationManager,
114                   const Handle(Prs3d_Presentation)& thePresentation,
115                   const Standard_Integer theMode);
116
117     void ComputeSelection (const Handle(SelectMgr_Selection)& theSelection,
118                            const Standard_Integer theMode);
119
120     // Called by VUserDrawElement
121     void Render(const Handle(OpenGl_Workspace)& theWorkspace) const;
122     void GetBounds(Graphic3d_BndBox4f* theBounds);
123
124     GLfloat myCoords[6];
125
126     friend class Element;
127 };
128 IMPLEMENT_STANDARD_HANDLE(VUserDrawObj, AIS_InteractiveObject)
129 IMPLEMENT_STANDARD_RTTIEXT(VUserDrawObj, AIS_InteractiveObject)
130
131 void VUserDrawObj::Compute(const Handle(PrsMgr_PresentationManager3d)& /*thePresentationManager*/,
132                            const Handle(Prs3d_Presentation)& thePresentation,
133                            const Standard_Integer /*theMode*/)
134 {
135   thePresentation->Clear();
136
137   Handle(Graphic3d_Group) aGrp = Prs3d_Root::CurrentGroup(thePresentation);
138   aGrp->UserDraw(this, Standard_True, Standard_True);
139 }
140
141 void VUserDrawObj::ComputeSelection (const Handle(SelectMgr_Selection)& theSelection,
142                                      const Standard_Integer /*theMode*/)
143 {
144   Handle(SelectMgr_EntityOwner) anEntityOwner = new SelectMgr_EntityOwner(this);
145   Handle(TColgp_HArray1OfPnt) aPnts = new TColgp_HArray1OfPnt(1, 5);
146   aPnts->SetValue(1, gp_Pnt(myCoords[0], myCoords[1], myCoords[2]));
147   aPnts->SetValue(2, gp_Pnt(myCoords[3], myCoords[4], myCoords[2]));
148   aPnts->SetValue(3, gp_Pnt(myCoords[3], myCoords[4], myCoords[5]));
149   aPnts->SetValue(4, gp_Pnt(myCoords[0], myCoords[1], myCoords[5]));
150   aPnts->SetValue(5, gp_Pnt(myCoords[0], myCoords[1], myCoords[2]));
151   Handle(Select3D_SensitiveCurve) aSensitive = new Select3D_SensitiveCurve(anEntityOwner, aPnts);
152   theSelection->Add(aSensitive);
153 }
154
155 void VUserDrawObj::GetBounds(Graphic3d_BndBox4f* theBounds)
156 {
157   if (theBounds)
158   {
159     Graphic3d_Vec4 aMinPt (myCoords[0], myCoords[1], myCoords[2], 1.0f);
160     Graphic3d_Vec4 aMaxPt (myCoords[3], myCoords[4], myCoords[5], 1.0f);
161     if (!theBounds->IsValid())
162     {
163       theBounds->Combine (Graphic3d_BndBox4f (aMinPt, aMaxPt));
164     }
165     else
166     {
167       theBounds->CornerMin() = aMinPt;
168       theBounds->CornerMax() = aMaxPt;
169     }
170   }
171 }
172
173 void VUserDrawObj::Render(const Handle(OpenGl_Workspace)& theWorkspace) const
174 {
175   // To test linking against OpenGl_Workspace and all aspect classes
176   const OpenGl_AspectLine* aLA = theWorkspace->AspectLine(0);
177   const OpenGl_AspectMarker* aMA = theWorkspace->AspectMarker(0);
178   aMA->Type();
179   const OpenGl_AspectText* aTA = theWorkspace->AspectText(0);
180   aTA->FontName();
181   TEL_COLOUR aColor = theWorkspace->NamedStatus & OPENGL_NS_HIGHLIGHT ?
182     *(theWorkspace->HighlightColor) : aLA->Color();
183
184   // To test OpenGl_Window
185   //Handle(OpenGl_Context) aCtx = theWorkspace->GetGlContext();
186
187   // Finally draw something to make sure UserDraw really works
188   glPushAttrib(GL_ENABLE_BIT);
189   glDisable(GL_LIGHTING);
190   glColor4fv(aColor.rgb);
191   glBegin(GL_LINE_LOOP);
192   glVertex3f(myCoords[0], myCoords[1], myCoords[2]);
193   glVertex3f(myCoords[3], myCoords[4], myCoords[2]);
194   glVertex3f(myCoords[3], myCoords[4], myCoords[5]);
195   glVertex3f(myCoords[0], myCoords[1], myCoords[5]);
196   glEnd();
197   glPopAttrib();
198 }
199
200 OpenGl_Element* VUserDrawCallback(const CALL_DEF_USERDRAW * theUserDraw)
201 {
202   Handle(VUserDrawObj) anIObj = (VUserDrawObj*)theUserDraw->Data;
203   if (anIObj.IsNull())
204   {
205     std::cout << "VUserDrawCallback error: null object passed, the custom scene element will not be rendered" << std::endl;
206   }
207
208   return new VUserDrawObj::Element(anIObj, theUserDraw->Bounds);
209 }
210
211 static Standard_Integer VUserDraw (Draw_Interpretor& di,
212                                     Standard_Integer argc,
213                                     const char ** argv)
214 {
215   Handle(AIS_InteractiveContext) aContext = ViewerTest::GetAISContext();
216   if (aContext.IsNull())
217   {
218     di << argv[0] << "Call 'vinit' before!\n";
219     return 1;
220   }
221
222   Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (aContext->CurrentViewer()->Driver());
223   if (aDriver.IsNull())
224   {
225     std::cerr << "Graphic driver not available.\n";
226     return 1;
227   }
228
229   if (argc > 2)
230   {
231     di << argv[0] << "Wrong number of arguments, only the object name expected\n";
232     return 1;
233   }
234
235   TCollection_AsciiString aName (argv[1]);
236   VDisplayAISObject(aName, Handle(AIS_InteractiveObject)());
237
238   // register the custom element factory function
239   aDriver->UserDrawCallback() = VUserDrawCallback;
240
241   Handle(VUserDrawObj) anIObj = new VUserDrawObj();
242   VDisplayAISObject(aName, anIObj);
243
244   return 0;
245 }
246
247 //==============================================================================
248 //function : VFeedback
249 //purpose  :
250 //==============================================================================
251
252 static int VFeedback (Draw_Interpretor& theDI,
253                       Standard_Integer  /*theArgNb*/,
254                       const char**      /*theArgVec*/)
255 {
256   // get the active view
257   Handle(V3d_View) aView = ViewerTest::CurrentView();
258   if (aView.IsNull())
259   {
260     std::cerr << "No active view. Please call vinit.\n";
261     return 1;
262   }
263
264   unsigned int aBufferSize = 1024 * 1024;
265   for (;;)
266   {
267     size_t aBytes = (size_t )aBufferSize * sizeof(GLfloat);
268     if (aBytes / sizeof(GLfloat) != (size_t )aBufferSize)
269     {
270       // finito la commedia
271       std::cerr << "Can not allocate buffer - requested size ("
272                 << (double(aBufferSize / (1024 * 1024)) * double(sizeof(GLfloat)))
273                 << " MiB) is out of address space\n";
274       return 1;
275     }
276
277     GLfloat* aBuffer = (GLfloat* )Standard::Allocate (aBytes);
278     if (aBuffer == NULL)
279     {
280       // finito la commedia
281       std::cerr << "Can not allocate buffer with size ("
282                 << (double(aBufferSize / (1024 * 1024)) * double(sizeof(GLfloat)))
283                 << " MiB)\n";
284       return 1;
285     }
286
287     glFeedbackBuffer ((GLsizei )aBufferSize, GL_2D, aBuffer);
288     glRenderMode (GL_FEEDBACK);
289
290     aView->Redraw();
291
292     GLint aResult = glRenderMode (GL_RENDER);
293     if (aResult < 0)
294     {
295       aBufferSize *= 2;
296
297       void* aPtr = aBuffer;
298       Standard::Free (aPtr);
299       aBuffer = NULL;
300       continue;
301     }
302
303     std::cout << "FeedBack result= " << aResult << "\n";
304     GLint aPntNb     = 0;
305     GLint aTriNb     = 0;
306     GLint aQuadsNb   = 0;
307     GLint aPolyNb    = 0;
308     GLint aNodesNb   = 0;
309     GLint aLinesNb   = 0;
310     GLint aBitmapsNb = 0;
311     GLint aPassThrNb = 0;
312     GLint aUnknownNb = 0;
313     const GLint NODE_VALUES = 2; // GL_2D
314     for (GLint anIter = 0; anIter < aResult;)
315     {
316         const GLfloat aPos = aBuffer[anIter];
317         switch ((GLint )aPos)
318         {
319           case GL_POINT_TOKEN:
320           {
321             ++aPntNb;
322             ++aNodesNb;
323             anIter += 1 + NODE_VALUES;
324             break;
325           }
326           case GL_LINE_RESET_TOKEN:
327           case GL_LINE_TOKEN:
328           {
329             ++aLinesNb;
330             aNodesNb += 2;
331             anIter += 1 + 2 * NODE_VALUES;
332             break;
333           }
334           case GL_POLYGON_TOKEN:
335           {
336             const GLint aCount = (GLint )aBuffer[++anIter];
337             aNodesNb += aCount;
338             anIter += aCount * NODE_VALUES + 1;
339             if (aCount == 3)
340             {
341               ++aTriNb;
342             }
343             else if (aCount == 4)
344             {
345               ++aQuadsNb;
346             }
347             else
348             {
349               ++aPolyNb;
350             }
351             break;
352           }
353           case GL_BITMAP_TOKEN:
354           case GL_DRAW_PIXEL_TOKEN:
355           case GL_COPY_PIXEL_TOKEN:
356           {
357             ++aBitmapsNb;
358             anIter += 1 + NODE_VALUES;
359             break;
360           }
361           case GL_PASS_THROUGH_TOKEN:
362           {
363             ++aPassThrNb;
364             anIter += 2; // header + value
365             break;
366           }
367           default:
368           {
369             ++anIter;
370             ++aUnknownNb;
371             break;
372           }
373        }
374     }
375     void* aPtr = aBuffer;
376     Standard::Free (aPtr);
377
378     // return statistics
379     theDI << "Total nodes:   " << aNodesNb   << "\n"
380           << "Points:        " << aPntNb     << "\n"
381           << "Line segments: " << aLinesNb   << "\n"
382           << "Triangles:     " << aTriNb     << "\n"
383           << "Quads:         " << aQuadsNb   << "\n"
384           << "Polygons:      " << aPolyNb    << "\n"
385           << "Bitmap tokens: " << aBitmapsNb << "\n"
386           << "Pass through:  " << aPassThrNb << "\n"
387           << "UNKNOWN:       " << aUnknownNb << "\n";
388
389     double aLen2D      = double(aNodesNb * 2 + aPntNb + aLinesNb * 2 + (aTriNb + aQuadsNb + aPolyNb) * 2 + aBitmapsNb + aPassThrNb);
390     double aLen3D      = double(aNodesNb * 3 + aPntNb + aLinesNb * 2 + (aTriNb + aQuadsNb + aPolyNb) * 2 + aBitmapsNb + aPassThrNb);
391     double aLen3D_rgba = double(aNodesNb * 7 + aPntNb + aLinesNb * 2 + (aTriNb + aQuadsNb + aPolyNb) * 2 + aBitmapsNb + aPassThrNb);
392     theDI << "Buffer size GL_2D:       " << aLen2D      * double(sizeof(GLfloat)) / double(1024 * 1024) << " MiB\n"
393           << "Buffer size GL_3D:       " << aLen3D      * double(sizeof(GLfloat)) / double(1024 * 1024) << " MiB\n"
394           << "Buffer size GL_3D_COLOR: " << aLen3D_rgba * double(sizeof(GLfloat)) / double(1024 * 1024) << " MiB\n";
395     return 0;
396   }
397 }
398
399 //==============================================================================
400 //function : VImmediateFront
401 //purpose  :
402 //==============================================================================
403
404 static int VImmediateFront (Draw_Interpretor& /*theDI*/,
405                             Standard_Integer  theArgNb,
406                             const char**      theArgVec)
407 {
408   // get the context
409   Handle(AIS_InteractiveContext) aContextAIS = ViewerTest::GetAISContext();
410   if (aContextAIS.IsNull())
411   {
412     std::cerr << "No active view. Please call vinit.\n";
413     return 1;
414   }
415
416   Handle(Graphic3d_GraphicDriver) aDriver = aContextAIS->CurrentViewer()->Driver();
417
418   if (aDriver.IsNull())
419   {
420     std::cerr << "Graphic driver not available.\n";
421     return 1;
422   }
423
424   if (theArgNb < 2)
425   {
426     std::cerr << "Wrong number of arguments.\n";
427     return 1;
428   }
429
430   Graphic3d_CView* aCView = (Graphic3d_CView* )(ViewerTest::CurrentView()->View()->CView());
431   aDriver->SetImmediateModeDrawToFront (*aCView, atoi(theArgVec[1]) != 0);
432   return 0;
433 }
434
435 //==============================================================================
436 //function : VGlInfo
437 //purpose  :
438 //==============================================================================
439
440 static int VGlInfo (Draw_Interpretor& theDI,
441                     Standard_Integer  theArgNb,
442                     const char**      theArgVec)
443 {
444   // get the active view
445   Handle(V3d_View) aView = ViewerTest::CurrentView();
446   if (aView.IsNull())
447   {
448     std::cerr << "No active view. Please call vinit.\n";
449     return 1;
450   }
451
452   if (theArgNb <= 1)
453   {
454     Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (aView->View()->GraphicDriver());
455     if (aDriver.IsNull())
456     {
457       std::cerr << "Error: view does not use OpenGL.\n";
458       return 1;
459     }
460     Handle(OpenGl_Context) aCtx = aDriver->GetSharedContext();
461     Standard_CString aDebugInfo = !aCtx.IsNull() && aCtx->IsDebugContext()
462                                 ? "  GLdebug     =  ON\n"
463                                 : "";
464     theDI << "OpenGL info:\n"
465           << "  GLvendor    = '" << (const char* )glGetString(GL_VENDOR)   << "'\n"
466           << "  GLdevice    = '" << (const char* )glGetString(GL_RENDERER) << "'\n"
467           << "  GLversion   = '" << (const char* )glGetString(GL_VERSION)  << "'\n"
468           << "  GLSLversion = '" << (const char* )glGetString(GL_SHADING_LANGUAGE_VERSION) << "'\n"
469           << aDebugInfo;
470     return 0;
471   }
472
473   const Standard_Boolean isList = theArgNb >= 3;
474   for (Standard_Integer anIter = 1; anIter < theArgNb; ++anIter)
475   {
476     TCollection_AsciiString aName (theArgVec[anIter]);
477     aName.UpperCase();
478     const char* aValue = NULL;
479     if (aName.Search ("VENDOR") != -1)
480     {
481       aValue = (const char* )glGetString (GL_VENDOR);
482     }
483     else if (aName.Search ("RENDERER") != -1)
484     {
485       aValue = (const char* )glGetString (GL_RENDERER);
486     }
487     else if (aName.Search ("SHADING_LANGUAGE_VERSION") != -1
488           || aName.Search ("GLSL") != -1)
489     {
490       aValue = (const char* )glGetString (GL_SHADING_LANGUAGE_VERSION);
491     }
492     else if (aName.Search ("VERSION") != -1)
493     {
494       aValue = (const char* )glGetString (GL_VERSION);
495     }
496     else if (aName.Search ("EXTENSIONS") != -1)
497     {
498       aValue = (const char* )glGetString (GL_EXTENSIONS);
499     }
500     else
501     {
502       std::cerr << "Unknown key '" << aName.ToCString() << "'\n";
503       return 1;
504     }
505
506     if (isList)
507     {
508       theDI << "{" << aValue << "} ";
509     }
510     else
511     {
512       theDI << aValue;
513     }
514   }
515
516   return 0;
517 }
518
519
520 //==============================================================================
521 //function : VShaderProg
522 //purpose  : Sets the pair of vertex and fragment shaders for the object
523 //==============================================================================
524 static Standard_Integer VShaderProg (Draw_Interpretor& /*theDI*/,
525                                      Standard_Integer  theArgNb,
526                                      const char**      theArgVec)
527 {
528   Handle(AIS_InteractiveContext) aCtx = ViewerTest::GetAISContext();
529   if (aCtx.IsNull())
530   {
531     std::cerr << "Use 'vinit' command before " << theArgVec[0] << "\n";
532     return 1;
533   }
534   else if (theArgNb < 2)
535   {
536     std::cerr << theArgVec[0] << " syntax error: lack of arguments\n";
537     return 1;
538   }
539
540   TCollection_AsciiString aLastArg (theArgVec[theArgNb - 1]);
541   aLastArg.UpperCase();
542   const Standard_Boolean toTurnOff = aLastArg == "OFF";
543   Standard_Integer       anArgsNb  = theArgNb - 1;
544   Handle(Graphic3d_ShaderProgram) aProgram;
545   if (!toTurnOff
546    && aLastArg == "PHONG")
547   {
548     aProgram = new Graphic3d_ShaderProgram (Graphic3d_ShaderProgram::ShaderName_Phong);
549   }
550   if (!toTurnOff
551    && aProgram.IsNull())
552   {
553     if (theArgNb < 3)
554     {
555       std::cerr << theArgVec[0] << " syntax error: lack of arguments\n";
556       return 1;
557     }
558
559     const TCollection_AsciiString aSrcVert = theArgVec[theArgNb - 2];
560     const TCollection_AsciiString aSrcFrag = theArgVec[theArgNb - 1];
561     if (!aSrcVert.IsEmpty()
562      && !OSD_File (aSrcVert).Exists())
563     {
564       std::cerr << "Non-existing vertex shader source\n";
565       return 1;
566     }
567     if (!aSrcFrag.IsEmpty()
568      && !OSD_File (aSrcFrag).Exists())
569     {
570       std::cerr << "Non-existing fragment shader source\n";
571       return 1;
572     }
573
574     aProgram = new Graphic3d_ShaderProgram();
575     aProgram->AttachShader (Graphic3d_ShaderObject::CreateFromFile (Graphic3d_TOS_VERTEX,   aSrcVert));
576     aProgram->AttachShader (Graphic3d_ShaderObject::CreateFromFile (Graphic3d_TOS_FRAGMENT, aSrcFrag));
577     anArgsNb = theArgNb - 2;
578   }
579
580   Handle(AIS_InteractiveObject) anIO;
581   if (anArgsNb <= 1
582    || *theArgVec[1] == '*')
583   {
584     for (ViewerTest_DoubleMapIteratorOfDoubleMapOfInteractiveAndName anIter (GetMapOfAIS());
585           anIter.More(); anIter.Next())
586     {
587       anIO = Handle(AIS_InteractiveObject)::DownCast (anIter.Key1());
588       if (!anIO.IsNull())
589       {
590         anIO->Attributes()->ShadingAspect()->Aspect()->SetShaderProgram (aProgram);
591         aCtx->Redisplay (anIO, Standard_False);
592       }
593     }
594     aCtx->UpdateCurrentViewer();
595     return 0;
596   }
597
598   for (Standard_Integer anArgIter = 1; anArgIter < anArgsNb; ++anArgIter)
599   {
600     const TCollection_AsciiString aName (theArgVec[anArgIter]);
601     if (!GetMapOfAIS().IsBound2 (aName))
602     {
603       std::cerr << "Warning: " << aName.ToCString() << " is not displayed\n";
604       continue;
605     }
606     anIO = Handle(AIS_InteractiveObject)::DownCast (GetMapOfAIS().Find2 (aName));
607     if (anIO.IsNull())
608     {
609       std::cerr << "Warning: " << aName.ToCString() << " is not an AIS object\n";
610       continue;
611     }
612     anIO->Attributes()->ShadingAspect()->Aspect()->SetShaderProgram (aProgram);
613     aCtx->Redisplay (anIO, Standard_False);
614   }
615
616   aCtx->UpdateCurrentViewer();
617   return 0;
618 }
619
620 //=======================================================================
621 //function : OpenGlCommands
622 //purpose  :
623 //=======================================================================
624
625 void ViewerTest::OpenGlCommands(Draw_Interpretor& theCommands)
626 {
627   const char* aGroup ="Commands for low-level TKOpenGl features";
628
629   theCommands.Add("vuserdraw",
630     "vuserdraw : name - simulates drawing with help of UserDraw",
631     __FILE__, VUserDraw, aGroup);
632   theCommands.Add("vfeedback",
633     "vfeedback       : perform test GL feedback rendering",
634     __FILE__, VFeedback, aGroup);
635   theCommands.Add("vimmediatefront",
636     "vimmediatefront : render immediate mode to front buffer or to back buffer",
637     __FILE__, VImmediateFront, aGroup);
638   theCommands.Add("vglinfo",
639     "vglinfo [GL_VENDOR] [GL_RENDERER] [GL_VERSION] [GL_SHADING_LANGUAGE_VERSION] [GL_EXTENSIONS]"
640     " : prints GL info",
641     __FILE__, VGlInfo, aGroup);
642   theCommands.Add("vshaderprog",
643             "   'vshaderprog [name] pathToVertexShader pathToFragmentShader'"
644     "\n\t\t: or 'vshaderprog [name] off'   to disable GLSL program"
645     "\n\t\t: or 'vshaderprog [name] phong' to enable per-pixel lighting calculations"
646     "\n\t\t: * might be used to specify all displayed objects",
647     __FILE__, VShaderProg, aGroup);
648 }