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