]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/renderer/draw_arb2.cpp
Use the same OpenAL headers on all platforms.
[icculus/iodoom3.git] / neo / renderer / draw_arb2.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "tr_local.h"
33
34 #include "cg_explicit.h"
35
36 CGcontext cg_context;
37
38 static void cg_error_callback( void ) {
39         CGerror i = cgGetError();
40         common->Printf( "Cg error (%d): %s\n", i, cgGetErrorString(i) );
41 }
42
43 /*
44 =========================================================================================
45
46 GENERAL INTERACTION RENDERING
47
48 =========================================================================================
49 */
50
51 /*
52 ====================
53 GL_SelectTextureNoClient
54 ====================
55 */
56 static void GL_SelectTextureNoClient( int unit ) {
57         backEnd.glState.currenttmu = unit;
58         qglActiveTextureARB( GL_TEXTURE0_ARB + unit );
59         RB_LogComment( "glActiveTextureARB( %i )\n", unit );
60 }
61
62 /*
63 ==================
64 RB_ARB2_DrawInteraction
65 ==================
66 */
67 void    RB_ARB2_DrawInteraction( const drawInteraction_t *din ) {
68         // load all the vertex program parameters
69         qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, din->localLightOrigin.ToFloatPtr() );
70         qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_VIEW_ORIGIN, din->localViewOrigin.ToFloatPtr() );
71         qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_S, din->lightProjection[0].ToFloatPtr() );
72         qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_T, din->lightProjection[1].ToFloatPtr() );
73         qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_Q, din->lightProjection[2].ToFloatPtr() );
74         qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_FALLOFF_S, din->lightProjection[3].ToFloatPtr() );
75         qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_S, din->bumpMatrix[0].ToFloatPtr() );
76         qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_T, din->bumpMatrix[1].ToFloatPtr() );
77         qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_S, din->diffuseMatrix[0].ToFloatPtr() );
78         qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_T, din->diffuseMatrix[1].ToFloatPtr() );
79         qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_S, din->specularMatrix[0].ToFloatPtr() );
80         qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_T, din->specularMatrix[1].ToFloatPtr() );
81
82         // testing fragment based normal mapping
83         if ( r_testARBProgram.GetBool() ) {
84                 qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 2, din->localLightOrigin.ToFloatPtr() );
85                 qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, din->localViewOrigin.ToFloatPtr() );
86         }
87
88         static const float zero[4] = { 0, 0, 0, 0 };
89         static const float one[4] = { 1, 1, 1, 1 };
90         static const float negOne[4] = { -1, -1, -1, -1 };
91
92         switch ( din->vertexColor ) {
93         case SVC_IGNORE:
94                 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, zero );
95                 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
96                 break;
97         case SVC_MODULATE:
98                 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, one );
99                 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, zero );
100                 break;
101         case SVC_INVERSE_MODULATE:
102                 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, negOne );
103                 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
104                 break;
105         }
106
107         // set the constant colors
108         qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, din->diffuseColor.ToFloatPtr() );
109         qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, din->specularColor.ToFloatPtr() );
110
111         // set the textures
112
113         // texture 1 will be the per-surface bump map
114         GL_SelectTextureNoClient( 1 );
115         din->bumpImage->Bind();
116
117         // texture 2 will be the light falloff texture
118         GL_SelectTextureNoClient( 2 );
119         din->lightFalloffImage->Bind();
120
121         // texture 3 will be the light projection texture
122         GL_SelectTextureNoClient( 3 );
123         din->lightImage->Bind();
124
125         // texture 4 is the per-surface diffuse map
126         GL_SelectTextureNoClient( 4 );
127         din->diffuseImage->Bind();
128
129         // texture 5 is the per-surface specular map
130         GL_SelectTextureNoClient( 5 );
131         din->specularImage->Bind();
132
133         // draw it
134         RB_DrawElementsWithCounters( din->surf->geo );
135 }
136
137
138 /*
139 =============
140 RB_ARB2_CreateDrawInteractions
141
142 =============
143 */
144 void RB_ARB2_CreateDrawInteractions( const drawSurf_t *surf ) {
145         if ( !surf ) {
146                 return;
147         }
148
149         // perform setup here that will be constant for all interactions
150         GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc );
151
152         // bind the vertex program
153         if ( r_testARBProgram.GetBool() ) {
154                 qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_TEST );
155                 qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_TEST );
156         } else {
157                 qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_INTERACTION );
158                 qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_INTERACTION );
159         }
160
161         qglEnable(GL_VERTEX_PROGRAM_ARB);
162         qglEnable(GL_FRAGMENT_PROGRAM_ARB);
163
164         // enable the vertex arrays
165         qglEnableVertexAttribArrayARB( 8 );
166         qglEnableVertexAttribArrayARB( 9 );
167         qglEnableVertexAttribArrayARB( 10 );
168         qglEnableVertexAttribArrayARB( 11 );
169         qglEnableClientState( GL_COLOR_ARRAY );
170
171         // texture 0 is the normalization cube map for the vector towards the light
172         GL_SelectTextureNoClient( 0 );
173         if ( backEnd.vLight->lightShader->IsAmbientLight() ) {
174                 globalImages->ambientNormalMap->Bind();
175         } else {
176                 globalImages->normalCubeMapImage->Bind();
177         }
178
179         // texture 6 is the specular lookup table
180         GL_SelectTextureNoClient( 6 );
181         if ( r_testARBProgram.GetBool() ) {
182                 globalImages->specular2DTableImage->Bind();     // variable specularity in alpha channel
183         } else {
184                 globalImages->specularTableImage->Bind();
185         }
186
187
188         for ( ; surf ; surf=surf->nextOnLight ) {
189                 // perform setup here that will not change over multiple interaction passes
190
191                 // set the vertex pointers
192                 idDrawVert      *ac = (idDrawVert *)vertexCache.Position( surf->geo->ambientCache );
193                 qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), ac->color );
194                 qglVertexAttribPointerARB( 11, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->normal.ToFloatPtr() );
195                 qglVertexAttribPointerARB( 10, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[1].ToFloatPtr() );
196                 qglVertexAttribPointerARB( 9, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[0].ToFloatPtr() );
197                 qglVertexAttribPointerARB( 8, 2, GL_FLOAT, false, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
198                 qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
199
200                 // this may cause RB_ARB2_DrawInteraction to be exacuted multiple
201                 // times with different colors and images if the surface or light have multiple layers
202                 RB_CreateSingleDrawInteractions( surf, RB_ARB2_DrawInteraction );
203         }
204
205         qglDisableVertexAttribArrayARB( 8 );
206         qglDisableVertexAttribArrayARB( 9 );
207         qglDisableVertexAttribArrayARB( 10 );
208         qglDisableVertexAttribArrayARB( 11 );
209         qglDisableClientState( GL_COLOR_ARRAY );
210
211         // disable features
212         GL_SelectTextureNoClient( 6 );
213         globalImages->BindNull();
214
215         GL_SelectTextureNoClient( 5 );
216         globalImages->BindNull();
217
218         GL_SelectTextureNoClient( 4 );
219         globalImages->BindNull();
220
221         GL_SelectTextureNoClient( 3 );
222         globalImages->BindNull();
223
224         GL_SelectTextureNoClient( 2 );
225         globalImages->BindNull();
226
227         GL_SelectTextureNoClient( 1 );
228         globalImages->BindNull();
229
230         backEnd.glState.currenttmu = -1;
231         GL_SelectTexture( 0 );
232
233         qglDisable(GL_VERTEX_PROGRAM_ARB);
234         qglDisable(GL_FRAGMENT_PROGRAM_ARB);
235 }
236
237
238 /*
239 ==================
240 RB_ARB2_DrawInteractions
241 ==================
242 */
243 void RB_ARB2_DrawInteractions( void ) {
244         viewLight_t             *vLight;
245         const idMaterial        *lightShader;
246
247         GL_SelectTexture( 0 );
248         qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
249
250         //
251         // for each light, perform adding and shadowing
252         //
253         for ( vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
254                 backEnd.vLight = vLight;
255
256                 // do fogging later
257                 if ( vLight->lightShader->IsFogLight() ) {
258                         continue;
259                 }
260                 if ( vLight->lightShader->IsBlendLight() ) {
261                         continue;
262                 }
263
264                 if ( !vLight->localInteractions && !vLight->globalInteractions
265                         && !vLight->translucentInteractions ) {
266                         continue;
267                 }
268
269                 lightShader = vLight->lightShader;
270
271                 // clear the stencil buffer if needed
272                 if ( vLight->globalShadows || vLight->localShadows ) {
273                         backEnd.currentScissor = vLight->scissorRect;
274                         if ( r_useScissor.GetBool() ) {
275                                 qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1, 
276                                         backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
277                                         backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
278                                         backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
279                         }
280                         qglClear( GL_STENCIL_BUFFER_BIT );
281                 } else {
282                         // no shadows, so no need to read or write the stencil buffer
283                         // we might in theory want to use GL_ALWAYS instead of disabling
284                         // completely, to satisfy the invarience rules
285                         qglStencilFunc( GL_ALWAYS, 128, 255 );
286                 }
287
288                 if ( r_useShadowVertexProgram.GetBool() ) {
289                         qglEnable( GL_VERTEX_PROGRAM_ARB );
290                         qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
291                         RB_StencilShadowPass( vLight->globalShadows );
292                         RB_ARB2_CreateDrawInteractions( vLight->localInteractions );
293                         qglEnable( GL_VERTEX_PROGRAM_ARB );
294                         qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
295                         RB_StencilShadowPass( vLight->localShadows );
296                         RB_ARB2_CreateDrawInteractions( vLight->globalInteractions );
297                         qglDisable( GL_VERTEX_PROGRAM_ARB );    // if there weren't any globalInteractions, it would have stayed on
298                 } else {
299                         RB_StencilShadowPass( vLight->globalShadows );
300                         RB_ARB2_CreateDrawInteractions( vLight->localInteractions );
301                         RB_StencilShadowPass( vLight->localShadows );
302                         RB_ARB2_CreateDrawInteractions( vLight->globalInteractions );
303                 }
304
305                 // translucent surfaces never get stencil shadowed
306                 if ( r_skipTranslucent.GetBool() ) {
307                         continue;
308                 }
309
310                 qglStencilFunc( GL_ALWAYS, 128, 255 );
311
312                 backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
313                 RB_ARB2_CreateDrawInteractions( vLight->translucentInteractions );
314
315                 backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
316         }
317
318         // disable stencil shadow test
319         qglStencilFunc( GL_ALWAYS, 128, 255 );
320
321         GL_SelectTexture( 0 );
322         qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
323 }
324
325 //===================================================================================
326
327
328 typedef struct {
329         GLenum                  target;
330         GLuint                  ident;
331         char                    name[64];
332 } progDef_t;
333
334 static  const int       MAX_GLPROGS = 200;
335
336 // a single file can have both a vertex program and a fragment program
337 static progDef_t        progs[MAX_GLPROGS] = {
338         { GL_VERTEX_PROGRAM_ARB, VPROG_TEST, "test.vfp" },
339         { GL_FRAGMENT_PROGRAM_ARB, FPROG_TEST, "test.vfp" },
340         { GL_VERTEX_PROGRAM_ARB, VPROG_INTERACTION, "interaction.vfp" },
341         { GL_FRAGMENT_PROGRAM_ARB, FPROG_INTERACTION, "interaction.vfp" },
342         { GL_VERTEX_PROGRAM_ARB, VPROG_BUMPY_ENVIRONMENT, "bumpyEnvironment.vfp" },
343         { GL_FRAGMENT_PROGRAM_ARB, FPROG_BUMPY_ENVIRONMENT, "bumpyEnvironment.vfp" },
344         { GL_VERTEX_PROGRAM_ARB, VPROG_AMBIENT, "ambientLight.vfp" },
345         { GL_FRAGMENT_PROGRAM_ARB, FPROG_AMBIENT, "ambientLight.vfp" },
346         { GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW, "shadow.vp" },
347         { GL_VERTEX_PROGRAM_ARB, VPROG_R200_INTERACTION, "R200_interaction.vp" },
348         { GL_VERTEX_PROGRAM_ARB, VPROG_NV20_BUMP_AND_LIGHT, "nv20_bumpAndLight.vp" },
349         { GL_VERTEX_PROGRAM_ARB, VPROG_NV20_DIFFUSE_COLOR, "nv20_diffuseColor.vp" },
350         { GL_VERTEX_PROGRAM_ARB, VPROG_NV20_SPECULAR_COLOR, "nv20_specularColor.vp" },
351         { GL_VERTEX_PROGRAM_ARB, VPROG_NV20_DIFFUSE_AND_SPECULAR_COLOR, "nv20_diffuseAndSpecularColor.vp" },
352         { GL_VERTEX_PROGRAM_ARB, VPROG_ENVIRONMENT, "environment.vfp" },
353         { GL_FRAGMENT_PROGRAM_ARB, FPROG_ENVIRONMENT, "environment.vfp" },
354         { GL_VERTEX_PROGRAM_ARB, VPROG_GLASSWARP, "arbVP_glasswarp.txt" },
355         { GL_FRAGMENT_PROGRAM_ARB, FPROG_GLASSWARP, "arbFP_glasswarp.txt" },
356
357         // additional programs can be dynamically specified in materials
358 };
359
360 /*
361 =================
362 R_LoadARBProgram
363 =================
364 */
365 void R_LoadARBProgram( int progIndex ) {
366         int             ofs;
367         int             err;
368         idStr   fullPath = "glprogs/";
369         fullPath += progs[progIndex].name;
370         char    *fileBuffer;
371         char    *buffer;
372         char    *start, *end;
373
374         common->Printf( "%s", fullPath.c_str() );
375
376         // load the program even if we don't support it, so
377         // fs_copyfiles can generate cross-platform data dumps
378         fileSystem->ReadFile( fullPath.c_str(), (void **)&fileBuffer, NULL );
379         if ( !fileBuffer ) {
380                 common->Printf( ": File not found\n" );
381                 return;
382         }
383
384         // copy to stack memory and free
385         buffer = (char *)_alloca( strlen( fileBuffer ) + 1 );
386         strcpy( buffer, fileBuffer );
387         fileSystem->FreeFile( fileBuffer );
388
389         if ( !glConfig.isInitialized ) {
390                 return;
391         }
392
393         //
394         // submit the program string at start to GL
395         //
396         if ( progs[progIndex].ident == 0 ) {
397                 // allocate a new identifier for this program
398                 progs[progIndex].ident = PROG_USER + progIndex;
399         }
400
401         // vertex and fragment programs can both be present in a single file, so
402         // scan for the proper header to be the start point, and stamp a 0 in after the end
403
404         if ( progs[progIndex].target == GL_VERTEX_PROGRAM_ARB ) {
405                 if ( !glConfig.ARBVertexProgramAvailable ) {
406                         common->Printf( ": GL_VERTEX_PROGRAM_ARB not available\n" );
407                         return;
408                 }
409                 start = strstr( (char *)buffer, "!!ARBvp" );
410         }
411         if ( progs[progIndex].target == GL_FRAGMENT_PROGRAM_ARB ) {
412                 if ( !glConfig.ARBFragmentProgramAvailable ) {
413                         common->Printf( ": GL_FRAGMENT_PROGRAM_ARB not available\n" );
414                         return;
415                 }
416                 start = strstr( (char *)buffer, "!!ARBfp" );
417         }
418         if ( !start ) {
419                 common->Printf( ": !!ARB not found\n" );
420                 return;
421         }
422         end = strstr( start, "END" );
423
424         if ( !end ) {
425                 common->Printf( ": END not found\n" );
426                 return;
427         }
428         end[3] = 0;
429
430         qglBindProgramARB( progs[progIndex].target, progs[progIndex].ident );
431         qglGetError();
432
433         qglProgramStringARB( progs[progIndex].target, GL_PROGRAM_FORMAT_ASCII_ARB,
434                 strlen( start ), (unsigned char *)start );
435
436         err = qglGetError();
437         qglGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, (GLint *)&ofs );
438         if ( err == GL_INVALID_OPERATION ) {
439                 const GLubyte *str = qglGetString( GL_PROGRAM_ERROR_STRING_ARB );
440                 common->Printf( "\nGL_PROGRAM_ERROR_STRING_ARB: %s\n", str );
441                 if ( ofs < 0 ) {
442                         common->Printf( "GL_PROGRAM_ERROR_POSITION_ARB < 0 with error\n" );
443                 } else if ( ofs >= (int)strlen( (char *)start ) ) {
444                         common->Printf( "error at end of program\n" );
445                 } else {
446                         common->Printf( "error at %i:\n%s", ofs, start + ofs );
447                 }
448                 return;
449         }
450         if ( ofs != -1 ) {
451                 common->Printf( "\nGL_PROGRAM_ERROR_POSITION_ARB != -1 without error\n" );
452                 return;
453         }
454
455         common->Printf( "\n" );
456 }
457
458 /*
459 ==================
460 R_FindARBProgram
461
462 Returns a GL identifier that can be bound to the given target, parsing
463 a text file if it hasn't already been loaded.
464 ==================
465 */
466 int R_FindARBProgram( GLenum target, const char *program ) {
467         int             i;
468         idStr   stripped = program;
469
470         stripped.StripFileExtension();
471
472         // see if it is already loaded
473         for ( i = 0 ; progs[i].name[0] ; i++ ) {
474                 if ( progs[i].target != target ) {
475                         continue;
476                 }
477
478                 idStr   compare = progs[i].name;
479                 compare.StripFileExtension();
480
481                 if ( !idStr::Icmp( stripped.c_str(), compare.c_str() ) ) {
482                         return progs[i].ident;
483                 }
484         }
485
486         if ( i == MAX_GLPROGS ) {
487                 common->Error( "R_FindARBProgram: MAX_GLPROGS" );
488         }
489
490         // add it to the list and load it
491         progs[i].ident = (program_t)0;  // will be gen'd by R_LoadARBProgram
492         progs[i].target = target;
493         strncpy( progs[i].name, program, sizeof( progs[i].name ) - 1 );
494
495         R_LoadARBProgram( i );
496
497         return progs[i].ident;
498 }
499
500 /*
501 ==================
502 R_ReloadARBPrograms_f
503 ==================
504 */
505 void R_ReloadARBPrograms_f( const idCmdArgs &args ) {
506         int             i;
507
508         common->Printf( "----- R_ReloadARBPrograms -----\n" );
509         for ( i = 0 ; progs[i].name[0] ; i++ ) {
510                 R_LoadARBProgram( i );
511         }
512         common->Printf( "-------------------------------\n" );
513 }
514
515 /*
516 ==================
517 R_ARB2_Init
518
519 ==================
520 */
521 void R_ARB2_Init( void ) {
522         glConfig.allowARB2Path = false;
523
524         common->Printf( "---------- R_ARB2_Init ----------\n" );
525
526         if ( !glConfig.ARBVertexProgramAvailable || !glConfig.ARBFragmentProgramAvailable ) {
527                 common->Printf( "Not available.\n" );
528                 return;
529         }
530
531         common->Printf( "Available.\n" );
532
533         common->Printf( "---------------------------------\n" );
534
535         glConfig.allowARB2Path = true;
536 }
537