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