]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/shaders.c
ABToSVK commit
[xonotic/netradiant.git] / tools / quake3 / q3map2 / shaders.c
1 /*
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21 ----------------------------------------------------------------------------------
22
23 This code has been altered significantly from its original form, to support
24 several games based on the Quake III Arena engine, in the form of "Q3Map2."
25
26 ------------------------------------------------------------------------------- */
27
28
29
30 /* marker */
31 #define SHADERS_C
32
33
34
35 /* dependencies */
36 #include "q3map2.h"
37
38
39
40 /*
41 AlphaMod()
42 routines for dealing with vertex alpha modification
43 */
44
45 void AlphaMod( alphaMod_t *am, int numVerts, bspDrawVert_t *drawVerts )
46 {
47         int                             i, j;
48         float                   mult, add, a;
49         bspDrawVert_t   *dv;
50         alphaMod_t              *am2;
51         
52         
53         /* dummy check */
54         if( am == NULL || numVerts < 1 || drawVerts == NULL )
55                 return;
56         
57         
58         /* walk vertex list */
59         for( i = 0; i < numVerts; i++ )
60         {
61                 /* get vertex */
62                 dv = &drawVerts[ i ];
63                 
64                 /* walk alphamod list */
65                 for( am2 = am; am2 != NULL; am2 = am2->next )
66                 {
67                         /* switch on type */
68                         switch( am->type )
69                         {
70                                 case AM_DOT_PRODUCT:
71                                         mult = DotProduct( dv->normal, am2->data );
72                                         add = 0.0f;
73                                         break;
74                                 
75                                 default:
76                                         mult = 1.0f;
77                                         add = 0.0f;
78                                         break;
79                         }
80                         
81                         /* apply mod */
82                         for( j = 0; j < MAX_LIGHTMAPS; j++ )
83                         {
84                                 a = (mult * dv->color[ j ][ 3 ]) + add;
85                                 if( a < 0 )
86                                         a = 0;
87                                 else if( a > 255 )
88                                         a = 255;
89                                 dv->color[ j ][ 3 ] = a;
90                         }
91                 }
92         }
93 }
94
95
96
97 /*
98 TcMod*()
99 routines for dealing with a 3x3 texture mod matrix
100 */
101
102 void TcMod( tcMod_t mod, float st[ 2 ] )
103 {
104         float   old[ 2 ];
105         
106         
107         old[ 0 ] = st[ 0 ];
108         old[ 1 ] = st[ 1 ];
109         st[ 0 ] = (mod[ 0 ][ 0 ] * old[ 0 ]) + (mod[ 0 ][ 1 ] * old[ 1 ]) + mod[ 0 ][ 2 ];
110         st[ 1 ] = (mod[ 1 ][ 0 ] * old[ 0 ]) + (mod[ 1 ][ 1 ] * old[ 1 ]) + mod[ 1 ][ 2 ];
111 }
112
113
114 void TcModIdentity( tcMod_t mod )
115 {
116         mod[ 0 ][ 0 ] = 1.0f;   mod[ 0 ][ 1 ] = 0.0f;   mod[ 0 ][ 2 ] = 0.0f;
117         mod[ 1 ][ 0 ] = 0.0f;   mod[ 1 ][ 1 ] = 1.0f;   mod[ 1 ][ 2 ] = 0.0f;
118         mod[ 2 ][ 0 ] = 0.0f;   mod[ 2 ][ 1 ] = 0.0f;   mod[ 2 ][ 2 ] = 1.0f;   /* this row is only used for multiples, not transformation */
119 }
120
121
122 void TcModMultiply( tcMod_t a, tcMod_t b, tcMod_t out )
123 {
124         int             i;
125         
126         
127         for( i = 0; i < 3; i++ )
128         {
129                 out[ i ][ 0 ] = (a[ i ][ 0 ] * b[ 0 ][ 0 ]) + (a[ i ][ 1 ] * b[ 1 ][ 0 ]) + (a[ i ][ 2 ] * b[ 2 ][ 0 ]);
130                 out[ i ][ 1 ] = (a[ i ][ 0 ] * b[ 0 ][ 1 ]) + (a[ i ][ 1 ] * b[ 1 ][ 1 ]) + (a[ i ][ 2 ] * b[ 2 ][ 1 ]);
131                 out[ i ][ 2 ] = (a[ i ][ 0 ] * b[ 0 ][ 2 ]) + (a[ i ][ 1 ] * b[ 1 ][ 2 ]) + (a[ i ][ 2 ] * b[ 2 ][ 2 ]);
132         }
133 }
134
135
136 void TcModTranslate( tcMod_t mod, float s, float t )
137 {
138         mod[ 0 ][ 2 ] += s;
139         mod[ 1 ][ 2 ] += t;
140 }
141
142
143 void TcModScale( tcMod_t mod, float s, float t )
144 {
145         mod[ 0 ][ 0 ] *= s;
146         mod[ 1 ][ 1 ] *= t;
147 }
148
149
150 void TcModRotate( tcMod_t mod, float euler )
151 {
152         tcMod_t old, temp;
153         float   radians, sinv, cosv;
154         
155         
156         memcpy( old, mod, sizeof( tcMod_t ) );
157         TcModIdentity( temp );
158
159         radians = euler / 180 * Q_PI;
160         sinv = sin( radians );
161         cosv = cos( radians );
162
163         temp[ 0 ][ 0 ] = cosv;  temp[ 0 ][ 1 ] = -sinv;
164         temp[ 1 ][ 0 ] = sinv;  temp[ 1 ][ 1 ] = cosv;
165         
166         TcModMultiply( old, temp, mod );
167 }
168
169
170
171 /*
172 ApplySurfaceParm() - ydnar
173 applies a named surfaceparm to the supplied flags
174 */
175
176 qboolean ApplySurfaceParm( char *name, int *contentFlags, int *surfaceFlags, int *compileFlags )
177 {
178         int                             i, fake;
179         surfaceParm_t   *sp;
180         
181         
182         /* dummy check */
183         if( name == NULL )
184                 name = "";
185         if( contentFlags == NULL )
186                 contentFlags = &fake;
187         if( surfaceFlags == NULL )
188                 surfaceFlags = &fake;
189         if( compileFlags == NULL )
190                 compileFlags = &fake;
191         
192         /* walk the current game's surfaceparms */
193         sp = game->surfaceParms;
194         while( sp->name != NULL )
195         {
196                 /* match? */
197                 if( !Q_stricmp( name, sp->name ) )
198                 {
199                         /* clear and set flags */
200                         *contentFlags &= ~(sp->contentFlagsClear);
201                         *contentFlags |= sp->contentFlags;
202                         *surfaceFlags &= ~(sp->surfaceFlagsClear);
203                         *surfaceFlags |= sp->surfaceFlags;
204                         *compileFlags &= ~(sp->compileFlagsClear);
205                         *compileFlags |= sp->compileFlags;
206                         
207                         /* return ok */
208                         return qtrue;
209                 }
210                 
211                 /* next */
212                 sp++;
213         }
214         
215         /* check custom info parms */
216         for( i = 0; i < numCustSurfaceParms; i++ )
217         {
218                 /* get surfaceparm */
219                 sp = &custSurfaceParms[ i ];
220                 
221                 /* match? */
222                 if( !Q_stricmp( name, sp->name ) )
223                 {
224                         /* clear and set flags */
225                         *contentFlags &= ~(sp->contentFlagsClear);
226                         *contentFlags |= sp->contentFlags;
227                         *surfaceFlags &= ~(sp->surfaceFlagsClear);
228                         *surfaceFlags |= sp->surfaceFlags;
229                         *compileFlags &= ~(sp->compileFlagsClear);
230                         *compileFlags |= sp->compileFlags;
231                         
232                         /* return ok */
233                         return qtrue;
234                 }
235         }
236         
237         /* no matching surfaceparm found */
238         return qfalse;
239 }
240
241
242
243 /*
244 BeginMapShaderFile() - ydnar
245 erases and starts a new map shader script
246 */
247
248 void BeginMapShaderFile( const char *mapFile )
249 {
250         char    base[ 1024 ];
251         int             len;
252         
253
254         /* dummy check */
255         mapName[ 0 ] = '\0';
256         mapShaderFile[ 0 ] = '\0';
257         if( mapFile == NULL || mapFile[ 0 ] == '\0' )
258                 return;
259         
260         /* copy map name */
261         strcpy( base, mapFile );
262         StripExtension( base );
263         
264         /* extract map name */
265         len = strlen( base ) - 1;
266         while( len > 0 && base[ len ] != '/' && base[ len ] != '\\' )
267                 len--;
268         strcpy( mapName, &base[ len + 1 ] );
269         base[ len ] = '\0';
270         if( len <= 0 )
271                 return;
272         
273         /* append ../scripts/q3map2_<mapname>.shader */
274         sprintf( mapShaderFile, "%s/../%s/q3map2_%s.shader", base, game->shaderPath, mapName );
275         Sys_FPrintf( SYS_VRB, "Map has shader script %s\n", mapShaderFile );
276         
277         /* remove it */
278         remove( mapShaderFile );
279         
280         /* stop making warnings about missing images */
281         warnImage = qfalse;
282 }
283
284
285
286 /*
287 WriteMapShaderFile() - ydnar
288 writes a shader to the map shader script
289 */
290
291 void WriteMapShaderFile( void )
292 {
293         FILE                    *file;
294         shaderInfo_t    *si;
295         int                             i, num;
296         
297         
298         /* dummy check */
299         if( mapShaderFile[ 0 ] == '\0' )
300                 return;
301         
302         /* are there any custom shaders? */
303         for( i = 0, num = 0; i < numShaderInfo; i++ )
304         {
305                 if( shaderInfo[ i ].custom ) 
306                         break;
307         }
308         if( i == numShaderInfo )
309                 return;
310         
311         /* note it */
312         Sys_FPrintf( SYS_VRB, "--- WriteMapShaderFile ---\n");
313         Sys_FPrintf( SYS_VRB, "Writing %s", mapShaderFile );
314         
315         /* open shader file */
316         file = fopen( mapShaderFile, "w" );
317         if( file == NULL )
318         {
319                 Sys_Printf( "WARNING: Unable to open map shader file %s for writing\n", mapShaderFile );
320                 return;
321         }
322         
323         /* print header */
324         fprintf( file,
325                 "// Custom shader file for %s.bsp\n"
326                 "// Generated by Q3Map2 (ydnar)\n"
327                 "// Do not edit! This file is overwritten on recompiles.\n\n",
328                 mapName );
329         
330         /* walk the shader list */
331         for( i = 0, num = 0; i < numShaderInfo; i++ )
332         {
333                 /* get the shader and print it */
334                 si = &shaderInfo[ i ];
335                 if( si->custom == qfalse || si->shaderText == NULL || si->shaderText[ 0 ] == '\0' )
336                         continue;
337                 num++;
338
339                 /* print it to the file */
340                 fprintf( file, "%s%s\n", si->shader, si->shaderText );
341                 //Sys_Printf( "%s%s\n", si->shader, si->shaderText ); /* FIXME: remove debugging code */
342                 
343                 Sys_FPrintf( SYS_VRB, "." );
344         }
345         
346         /* close the shader */
347         fflush( file );
348         fclose( file );
349         
350         Sys_FPrintf( SYS_VRB, "\n" );
351         
352         /* print some stats */
353         Sys_Printf( "%9d custom shaders emitted\n", num );
354 }
355
356
357
358 /*
359 CustomShader() - ydnar
360 sets up a custom map shader
361 */
362
363 shaderInfo_t *CustomShader( shaderInfo_t *si, char *find, char *replace )
364 {
365         shaderInfo_t    *csi;
366         char                    shader[ MAX_QPATH ];
367         char                    *s;
368         int                             loc;
369         md5_state_t             md5;
370         byte                    digest[ 16 ];
371         char                    *srcShaderText, temp[ 8192 ], shaderText[ 8192 ];       /* ydnar: fixme (make this bigger?) */
372         
373         
374         /* dummy check */
375         if( si == NULL )
376                 return ShaderInfoForShader( "default" );
377         
378         /* default shader text source */
379         srcShaderText = si->shaderText;
380         
381         /* et: implicitMap */
382         if( si->implicitMap == IM_OPAQUE )
383         {
384                 srcShaderText = temp;
385                 sprintf( temp, "\n"
386                         "{ // Q3Map2 defaulted (implicitMap)\n"
387                         "\t{\n"
388                         "\t\tmap $lightmap\n"
389                         "\t\trgbGen identity\n"
390                         "\t}\n"
391                         "\tq3map_styleMarker\n"
392                         "\t{\n"
393                         "\t\tmap %s\n"
394                         "\t\tblendFunc GL_DST_COLOR GL_ZERO\n"
395                         "\t\trgbGen identity\n"
396                         "\t}\n"
397                         "}\n",
398                         si->implicitImagePath );
399         }
400         
401         /* et: implicitMask */
402         else if( si->implicitMap == IM_MASKED )
403         {
404                 srcShaderText = temp;
405                 sprintf( temp, "\n"
406                         "{ // Q3Map2 defaulted (implicitMask)\n"
407                         "\tcull none\n"
408                         "\t{\n"
409                         "\t\tmap %s\n"
410                         "\t\talphaFunc GE128\n"
411                         "\t\tdepthWrite\n"
412                         "\t}\n"
413                         "\t{\n"
414                         "\t\tmap $lightmap\n"
415                         "\t\trgbGen identity\n"
416                         "\t\tdepthFunc equal\n"
417                         "\t}\n"
418                         "\tq3map_styleMarker\n"
419                         "\t{\n"
420                         "\t\tmap %s\n"
421                         "\t\tblendFunc GL_DST_COLOR GL_ZERO\n"
422                         "\t\tdepthFunc equal\n"
423                         "\t\trgbGen identity\n"
424                         "\t}\n"
425                         "}\n",
426                         si->implicitImagePath,
427                         si->implicitImagePath );
428         }
429         
430         /* et: implicitBlend */
431         else if( si->implicitMap == IM_BLEND )
432         {
433                 srcShaderText = temp;
434                 sprintf( temp, "\n"
435                         "{ // Q3Map2 defaulted (implicitBlend)\n"
436                         "\tcull none\n"
437                         "\t{\n"
438                         "\t\tmap %s\n"
439                         "\t\tblendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA\n"
440                         "\t}\n"
441                         "\t{\n"
442                         "\t\tmap $lightmap\n"
443                         "\t\trgbGen identity\n"
444                         "\t\tblendFunc GL_DST_COLOR GL_ZERO\n"
445                         "\t}\n"
446                         "\tq3map_styleMarker\n"
447                         "}\n",
448                         si->implicitImagePath );
449         }
450         
451         /* default shader text */
452         else if( srcShaderText == NULL )
453         {
454                 srcShaderText = temp;
455                 sprintf( temp, "\n"
456                         "{ // Q3Map2 defaulted\n"
457                         "\t{\n"
458                         "\t\tmap $lightmap\n"
459                         "\t\trgbGen identity\n"
460                         "\t}\n"
461                         "\tq3map_styleMarker\n"
462                         "\t{\n"
463                         "\t\tmap %s.tga\n"
464                         "\t\tblendFunc GL_DST_COLOR GL_ZERO\n"
465                         "\t\trgbGen identity\n"
466                         "\t}\n"
467                         "}\n",
468                         si->shader );
469         }
470         
471         /* error check */
472         if( (strlen( mapName ) + 1 + 32) > MAX_QPATH )
473                 Error( "Custom shader name length (%d) exceeded. Shorten your map name.\n", MAX_QPATH );
474         
475         /* do some bad find-replace */
476         s = strstr( srcShaderText, find );
477         if( s == NULL )
478                 //%     strcpy( shaderText, srcShaderText );
479                 return si;      /* testing just using the existing shader if this fails */
480         else
481         {
482                 /* substitute 'find' with 'replace' */
483                 loc = s - srcShaderText;
484                 strcpy( shaderText, srcShaderText );
485                 shaderText[ loc ] = '\0';
486                 strcat( shaderText, replace );
487                 strcat( shaderText, &srcShaderText[ loc + strlen( find ) ] );
488         }
489         
490         /* make md5 hash of the shader text */
491         md5_init( &md5 );
492         md5_append( &md5, shaderText, strlen( shaderText ) );
493         md5_finish( &md5, digest );
494         
495         /* mangle hash into a shader name */
496         sprintf( shader, "%s/%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", mapName,
497                 digest[ 0 ], digest[ 1 ], digest[ 2 ], digest[ 3 ], digest[ 4 ], digest[ 5 ], digest[ 6 ], digest[ 7 ], 
498                 digest[ 8 ], digest[ 9 ], digest[ 10 ], digest[ 11 ], digest[ 12 ], digest[ 13 ], digest[ 14 ], digest[ 15 ] );
499         
500         /* get shader */
501         csi = ShaderInfoForShader( shader );
502         
503         /* might be a preexisting shader */
504         if( csi->custom )
505                 return csi;
506         
507         /* clone the existing shader and rename */
508         memcpy( csi, si, sizeof( shaderInfo_t ) );
509         strcpy( csi->shader, shader );
510         csi->custom = qtrue;
511         
512         /* store new shader text */
513         csi->shaderText = safe_malloc( strlen( shaderText ) + 1 );
514         strcpy( csi->shaderText, shaderText );  /* LEAK! */
515         
516         /* return it */
517         return csi;
518 }
519
520
521
522 /*
523 EmitVertexRemapShader()
524 adds a vertexremapshader key/value pair to worldspawn
525 */
526
527 void EmitVertexRemapShader( char *from, char *to )
528 {
529         md5_state_t             md5;
530         byte                    digest[ 16 ];
531         char                    key[ 64 ], value[ 256 ];
532         
533         
534         /* dummy check */
535         if( from == NULL || from[ 0 ] == '\0' ||
536                 to == NULL || to[ 0 ] == '\0' )
537                 return;
538         
539         /* build value */
540         sprintf( value, "%s;%s", from, to );
541         
542         /* make md5 hash */
543         md5_init( &md5 );
544         md5_append( &md5, value, strlen( value ) );
545         md5_finish( &md5, digest );
546
547         /* make key (this is annoying, as vertexremapshader is precisely 17 characters,
548            which is one too long, so we leave off the last byte of the md5 digest) */
549         sprintf( key, "vertexremapshader%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
550                 digest[ 0 ], digest[ 1 ], digest[ 2 ], digest[ 3 ], digest[ 4 ], digest[ 5 ], digest[ 6 ], digest[ 7 ], 
551                 digest[ 8 ], digest[ 9 ], digest[ 10 ], digest[ 11 ], digest[ 12 ], digest[ 13 ], digest[ 14 ] );       /* no: digest[ 15 ] */
552         
553         /* add key/value pair to worldspawn */
554         SetKeyValue( &entities[ 0 ], key, value );
555 }
556
557
558
559 /*
560 AllocShaderInfo()
561 allocates and initializes a new shader
562 */
563
564 static shaderInfo_t     *AllocShaderInfo( void )
565 {
566         shaderInfo_t    *si;
567         
568         
569         /* allocate? */
570         if( shaderInfo == NULL )
571         {
572                 shaderInfo = safe_malloc( sizeof( shaderInfo_t ) * MAX_SHADER_INFO );
573                 numShaderInfo = 0;
574         }
575         
576         /* bounds check */
577         if( numShaderInfo == MAX_SHADER_INFO )
578                 Error( "MAX_SHADER_INFO exceeded. Remove some PK3 files or shader scripts from shaderlist.txt and try again." );
579         si = &shaderInfo[ numShaderInfo ];
580         numShaderInfo++;
581         
582         /* ydnar: clear to 0 first */
583         memset( si, 0, sizeof( shaderInfo_t ) );
584         
585         /* set defaults */
586         ApplySurfaceParm( "default", &si->contentFlags, &si->surfaceFlags, &si->compileFlags );
587         
588         si->backsplashFraction = DEF_BACKSPLASH_FRACTION;
589         si->backsplashDistance = DEF_BACKSPLASH_DISTANCE;
590         
591         si->bounceScale = DEF_RADIOSITY_BOUNCE;
592         
593         si->lightStyle = LS_NORMAL;
594         
595         si->polygonOffset = qfalse;
596         
597         si->shadeAngleDegrees = 0.0f;
598         si->lightmapSampleSize = 0;
599         si->lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
600         si->patchShadows = qfalse;
601         si->vertexShadows = qtrue;      /* ydnar: changed default behavior */
602         si->forceSunlight = qfalse;
603         si->vertexScale = 1.0;
604         si->notjunc = qfalse;
605         
606         /* ydnar: set texture coordinate transform matrix to identity */
607         TcModIdentity( si->mod );
608         
609         /* ydnar: lightmaps can now be > 128x128 in an externally generated tga */
610         si->lmCustomWidth = lmCustomSize;       //%     LIGHTMAP_WIDTH;
611         si->lmCustomHeight = lmCustomSize;      //%     LIGHTMAP_HEIGHT;
612         
613         /* return to sender */
614         return si;
615 }
616
617
618
619 /*
620 FinishShader() - ydnar
621 sets a shader's width and height among other things
622 */
623
624 void FinishShader( shaderInfo_t *si )
625 {
626         int             x, y;
627         float   st[ 2 ], o[ 2 ], dist, bestDist;
628         vec4_t  color, bestColor, delta;
629         
630
631         /* don't double-dip */
632         if( si->finished )
633                 return;
634         
635         /* if they're explicitly set, copy from image size */
636         if( si->shaderWidth == 0 && si->shaderHeight == 0 )
637         {
638                 si->shaderWidth = si->shaderImage->width;
639                 si->shaderHeight = si->shaderImage->height;
640         }
641         
642         /* legacy terrain has explicit image-sized texture projection */
643         if( si->legacyTerrain && si->tcGen == qfalse )
644         {
645                 /* set xy texture projection */
646                 si->tcGen = qtrue;
647                 VectorSet( si->vecs[ 0 ], (1.0f / (si->shaderWidth * 0.5f)), 0, 0 );
648                 VectorSet( si->vecs[ 1 ], 0, (1.0f / (si->shaderHeight * 0.5f)), 0 );
649         }
650         
651         /* find pixel coordinates best matching the average color of the image */
652         bestDist = 99999999;
653         o[ 0 ] = 1.0f / si->shaderImage->width;
654         o[ 1 ] = 1.0f / si->shaderImage->height;
655         for( y = 0, st[ 1 ] = 0.0f; y < si->shaderImage->height; y++, st[ 1 ] += o[ 1 ] )
656         {
657                 for( x = 0, st[ 0 ] = 0.0f; x < si->shaderImage->width; x++, st[ 0 ] += o[ 0 ] )
658                 {
659                         /* sample the shader image */
660                         RadSampleImage( si->shaderImage->pixels, si->shaderImage->width, si->shaderImage->height, st, color );
661                         
662                         /* determine error squared */
663                         VectorSubtract( color, si->averageColor, delta );
664                         delta[ 3 ] = color[ 3 ] - si->averageColor[ 3 ];
665                         dist = delta[ 0 ] * delta[ 0 ] + delta[ 1 ] * delta[ 1 ] + delta[ 2 ] * delta[ 2 ] + delta[ 3 ] * delta[ 3 ];
666                         if( dist < bestDist )
667                         {
668                                 VectorCopy( color, bestColor );
669                                 bestColor[ 3 ] = color[ 3 ];
670                                 si->stFlat[ 0 ] = st[ 0 ];
671                                 si->stFlat[ 1 ] = st[ 1 ];
672                         }
673                 }
674         }
675         
676         /* set to finished */
677         si->finished = qtrue;
678 }
679
680
681
682 /*
683 LoadShaderImages()
684 loads a shader's images
685 ydnar: image.c made this a bit simpler
686 */
687
688 static void LoadShaderImages( shaderInfo_t *si )
689 {
690         int                     i, count;
691         float           color[ 4 ];
692         
693         
694         /* nodraw shaders don't need images */
695         if( si->compileFlags & C_NODRAW )
696                 si->shaderImage = ImageLoad( DEFAULT_IMAGE );
697         else
698         {
699                 /* try to load editor image first */
700                 si->shaderImage = ImageLoad( si->editorImagePath );
701                 
702                 /* then try shadername */
703                 if( si->shaderImage == NULL )
704                         si->shaderImage = ImageLoad( si->shader );
705                 
706                 /* then try implicit image path (note: new behavior!) */
707                 if( si->shaderImage == NULL )
708                         si->shaderImage = ImageLoad( si->implicitImagePath );
709                 
710                 /* then try lightimage (note: new behavior!) */
711                 if( si->shaderImage == NULL )
712                         si->shaderImage = ImageLoad( si->lightImagePath );
713                 
714                 /* otherwise, use default image */
715                 if( si->shaderImage == NULL )
716                 {
717                         si->shaderImage = ImageLoad( DEFAULT_IMAGE );
718                         if( warnImage && strcmp( si->shader, "noshader" ) )
719                                 Sys_Printf( "WARNING: Couldn't find image for shader %s\n", si->shader );
720                 }
721                 
722                 /* load light image */
723                 si->lightImage = ImageLoad( si->lightImagePath );
724                 
725                 /* load normalmap image (ok if this is NULL) */
726                 si->normalImage = ImageLoad( si->normalImagePath );
727                 if( si->normalImage != NULL )
728                 {
729                         Sys_FPrintf( SYS_VRB, "Shader %s has\n"
730                                                                   "    NM %s\n", si->shader, si->normalImagePath );
731                 }
732         }
733         
734         /* if no light image, use shader image */
735         if( si->lightImage == NULL )
736                 si->lightImage = ImageLoad( si->shaderImage->name );
737         
738         /* create default and average colors */
739         count = si->lightImage->width * si->lightImage->height;
740         VectorClear( color );
741         color[ 3 ] = 0.0f;
742         for( i = 0; i < count; i++ )
743         {
744                 color[ 0 ] += si->lightImage->pixels[ i * 4 + 0 ];
745                 color[ 1 ] += si->lightImage->pixels[ i * 4 + 1 ];
746                 color[ 2 ] += si->lightImage->pixels[ i * 4 + 2 ];
747                 color[ 3 ] += si->lightImage->pixels[ i * 4 + 3 ];
748         }
749         
750         if( VectorLength( si->color ) <= 0.0f )
751                 ColorNormalize( color, si->color );
752         VectorScale( color, (1.0f / count), si->averageColor );
753 }
754
755
756
757 /*
758 ShaderInfoForShader()
759 finds a shaderinfo for a named shader
760 */
761
762 shaderInfo_t *ShaderInfoForShader( const char *shaderName )
763 {
764         int                             i;
765         shaderInfo_t    *si;
766         char                    shader[ MAX_QPATH ];
767         
768         
769         /* dummy check */
770         if( shaderName == NULL || shaderName[ 0 ] == '\0' )
771         {
772                 Sys_Printf( "WARNING: Null or empty shader name\n" );
773                 shaderName = "missing";
774         }
775         
776         /* strip off extension */
777         strcpy( shader, shaderName );
778         StripExtension( shader );
779         
780         /* search for it */
781         for( i = 0; i < numShaderInfo; i++ )
782         {
783                 si = &shaderInfo[ i ];
784                 if( !Q_stricmp( shader, si->shader ) )
785                 {
786                         /* load image if necessary */
787                         if( si->shaderImage == NULL )
788                         {
789                                 LoadShaderImages( si );
790                                 FinishShader( si );
791                         }
792                         
793                         /* return it */
794                         return si;
795                 }
796         }
797         
798         /* allocate a default shader */
799         si = AllocShaderInfo();
800         strcpy( si->shader, shader );
801         LoadShaderImages( si );
802         FinishShader( si );
803         
804         /* return it */
805         return si;
806 }
807
808
809
810 /*
811 GetTokenAppend() - ydnar
812 gets a token and appends its text to the specified buffer
813 */
814
815 static int      oldScriptLine = 0;
816 static int      tabDepth = 0;
817
818 qboolean GetTokenAppend( char *buffer, qboolean crossline )
819 {
820         qboolean        r;
821         int                     i;
822         
823         
824         /* get the token */
825         r = GetToken( crossline );
826         if( r == qfalse || buffer == NULL || token[ 0 ] == '\0' )
827                 return r;
828         
829         /* pre-tabstops */
830         if( token[ 0 ] == '}' )
831                 tabDepth--;
832         
833         /* append? */
834         if( oldScriptLine != scriptline )
835         {
836                 strcat( buffer, "\n" );
837                 for( i = 0; i < tabDepth; i++ )
838                         strcat( buffer, "\t" );
839         }
840         else
841                 strcat( buffer, " " );
842         oldScriptLine = scriptline;
843         strcat( buffer, token );
844         
845         /* post-tabstops */
846         if( token[ 0 ] == '{' )
847                 tabDepth++;
848         
849         /* return */
850         return r;
851 }
852
853
854 void Parse1DMatrixAppend( char *buffer, int x, vec_t *m )
855 {
856         int             i;
857         
858         
859         if( !GetTokenAppend( buffer, qtrue ) || strcmp( token, "(" ) )
860                 Error( "Parse1DMatrixAppend(): line %d: ( not found!", scriptline );
861         for( i = 0; i < x; i++ )
862         {
863                 if( !GetTokenAppend( buffer, qfalse ) )
864                         Error( "Parse1DMatrixAppend(): line %d: Number not found!", scriptline );
865                 m[ i ] = atof( token );
866         }
867         if( !GetTokenAppend( buffer, qtrue ) || strcmp( token, ")" ) )
868                 Error( "Parse1DMatrixAppend(): line %d: ) not found!", scriptline );
869 }
870
871
872
873
874 /*
875 ParseShaderFile()
876 parses a shader file into discrete shaderInfo_t
877 */
878
879 static void ParseShaderFile( const char *filename )
880 {
881         int                             i, val;
882         shaderInfo_t    *si;
883         char                    *suffix, temp[ 1024 ];
884         char                    shaderText[ 8192 ];     /* ydnar: fixme (make this bigger?) */
885         
886         
887         /* init */
888         si = NULL;
889         shaderText[ 0 ] = '\0';
890         
891         /* load the shader */
892         LoadScriptFile( filename, 0 );
893         
894         /* tokenize it */
895         while( 1 )
896         {
897                 /* copy shader text to the shaderinfo */
898                 if( si != NULL && shaderText[ 0 ] != '\0' )
899                 {
900                         strcat( shaderText, "\n" );
901                         si->shaderText = safe_malloc( strlen( shaderText ) + 1 );
902                         strcpy( si->shaderText, shaderText );
903                         //%     if( VectorLength( si->vecs[ 0 ] ) )
904                         //%             Sys_Printf( "%s\n", shaderText );
905                 }
906                 
907                 /* ydnar: clear shader text buffer */
908                 shaderText[ 0 ] = '\0';
909                 
910                 /* test for end of file */
911                 if( !GetToken( qtrue ) )
912                         break;
913                 
914                 /* shader name is initial token */
915                 si = AllocShaderInfo();
916                 strcpy( si->shader, token );
917                 
918                 /* ignore ":q3map" suffix */
919                 suffix = strstr( si->shader, ":q3map" );
920                 if( suffix != NULL )
921                         *suffix = '\0';
922                 
923                 /* handle { } section */
924                 if( !GetTokenAppend( shaderText, qtrue ) )
925                         break;
926                 if( strcmp( token, "{" ) )
927                 {
928                         if( si != NULL )
929                                 Error( "ParseShaderFile(): %s, line %d: { not found!\nFound instead: %s\nLast known shader: %s",
930                                         filename, scriptline, token, si->shader );
931                         else
932                                 Error( "ParseShaderFile(): %s, line %d: { not found!\nFound instead: %s",
933                                         filename, scriptline, token );
934                 }
935                 
936                 while( 1 )
937                 {
938                         /* get the next token */
939                         if( !GetTokenAppend( shaderText, qtrue ) )
940                                 break;
941                         if( !strcmp( token, "}" ) )
942                                 break;
943                         
944                         
945                         /* -----------------------------------------------------------------
946                            shader stages (passes)
947                            ----------------------------------------------------------------- */
948                         
949                         /* parse stage directives */
950                         if( !strcmp( token, "{" ) )
951                         {
952                                 si->hasPasses = qtrue;
953                                 while( 1 )
954                                 {
955                                         if( !GetTokenAppend( shaderText, qtrue ) )
956                                                 break;
957                                         if( !strcmp( token, "}" ) )
958                                                 break;
959                                         
960                                         /* only care about images if we don't have a editor/light image */
961                                         if( si->editorImagePath[ 0 ] == '\0' && si->lightImagePath[ 0 ] == '\0' && si->implicitImagePath[ 0 ] == '\0' )
962                                         {
963                                                 /* digest any images */
964                                                 if( !Q_stricmp( token, "map" ) ||
965                                                         !Q_stricmp( token, "clampMap" ) ||
966                                                         !Q_stricmp( token, "animMap" ) ||
967                                                         !Q_stricmp( token, "clampAnimMap" ) ||
968                                                         !Q_stricmp( token, "clampMap" ) ||
969                                                         !Q_stricmp( token, "mapComp" ) ||
970                                                         !Q_stricmp( token, "mapNoComp" ) )
971                                                 {
972                                                         /* skip one token for animated stages */
973                                                         if( !Q_stricmp( token, "animMap" ) || !Q_stricmp( token, "clampAnimMap" ) )
974                                                                 GetTokenAppend( shaderText, qfalse );
975                                                         
976                                                         /* get an image */
977                                                         GetTokenAppend( shaderText, qfalse );
978                                                         if( token[ 0 ] != '*' && token[ 0 ] != '$' )
979                                                         {
980                                                                 strcpy( si->lightImagePath, token );
981                                                                 DefaultExtension( si->lightImagePath, ".tga" );
982                                                                 
983                                                                 /* debug code */
984                                                                 //%     Sys_FPrintf( SYS_VRB, "Deduced shader image: %s\n", si->lightImagePath );
985                                                         }
986                                                 }
987                                         }
988                                 }
989                         }
990                         
991                         
992                         /* -----------------------------------------------------------------
993                            surfaceparm * directives
994                            ----------------------------------------------------------------- */
995                         
996                         /* match surfaceparm */
997                         else if( !Q_stricmp( token, "surfaceparm" ) )
998                         {
999                                 GetTokenAppend( shaderText, qfalse );
1000                                 if( ApplySurfaceParm( token, &si->contentFlags, &si->surfaceFlags, &si->compileFlags ) == qfalse )
1001                                         Sys_Printf( "WARNING: Unknown surfaceparm: \"%s\"\n", token );
1002                         }
1003                         
1004                         
1005                         /* -----------------------------------------------------------------
1006                            game-related shader directives
1007                            ----------------------------------------------------------------- */
1008                         
1009                         /* ydnar: fogparms (for determining fog volumes) */
1010                         else if( !Q_stricmp( token, "fogparms" ) )
1011                                 si->fogParms = qtrue;
1012                         
1013                         /* ydnar: polygonoffset (for no culling) */
1014                         else if( !Q_stricmp( token, "polygonoffset" ) )
1015                                 si->polygonOffset = qtrue;
1016                         
1017                         /* tesssize is used to force liquid surfaces to subdivide */
1018                         else if( !Q_stricmp( token, "tessSize" ) || !Q_stricmp( token, "q3map_tessSize" ) /* sof2 */ )
1019                         {
1020                                 GetTokenAppend( shaderText, qfalse );
1021                                 si->subdivisions = atof( token );
1022                         }
1023                         
1024                         /* cull none will set twoSided (ydnar: added disable too) */
1025                         else if ( !Q_stricmp( token, "cull" ) )
1026                         {
1027                                 GetTokenAppend( shaderText, qfalse );
1028                                 if( !Q_stricmp( token, "none" ) || !Q_stricmp( token, "disable" ) || !Q_stricmp( token, "twosided" ) )
1029                                         si->twoSided = qtrue;
1030                         }
1031                         
1032                         /* deformVertexes autosprite[ 2 ]
1033                            we catch this so autosprited surfaces become point
1034                            lights instead of area lights */
1035                         else if( !Q_stricmp( token, "deformVertexes" ) )
1036                         {
1037                                 GetTokenAppend( shaderText, qfalse );
1038                                 
1039                                 /* deformVertexes autosprite(2) */
1040                                 if( !Q_strncasecmp( token, "autosprite", 10 ) )
1041                                 {
1042                                         /* set it as autosprite and detail */
1043                                         si->autosprite = qtrue;
1044                                         ApplySurfaceParm( "detail", &si->contentFlags, &si->surfaceFlags, &si->compileFlags );
1045                                         
1046                                         /* ydnar: gs mods: added these useful things */
1047                                         si->noClip = qtrue;
1048                                         si->notjunc = qtrue;
1049                                 }
1050                                 
1051                                 /* deformVertexes move <x> <y> <z> <func> <base> <amplitude> <phase> <freq> (ydnar: for particle studio support) */
1052                                 if( !Q_stricmp( token, "move") )
1053                                 {
1054                                         vec3_t  amt, mins, maxs;
1055                                         float   base, amp;
1056                                         
1057                                         
1058                                         /* get move amount */
1059                                         GetTokenAppend( shaderText, qfalse );   amt[ 0 ] = atof( token );
1060                                         GetTokenAppend( shaderText, qfalse );   amt[ 1 ] = atof( token );
1061                                         GetTokenAppend( shaderText, qfalse );   amt[ 2 ] = atof( token );
1062                                         
1063                                         /* skip func */
1064                                         GetTokenAppend( shaderText, qfalse );
1065                                         
1066                                         /* get base and amplitude */
1067                                         GetTokenAppend( shaderText, qfalse );   base = atof( token );
1068                                         GetTokenAppend( shaderText, qfalse );   amp = atof( token );
1069                                         
1070                                         /* calculate */
1071                                         VectorScale( amt, base, mins );
1072                                         VectorMA( mins, amp, amt, maxs );
1073                                         VectorAdd( si->mins, mins, si->mins );
1074                                         VectorAdd( si->maxs, maxs, si->maxs );
1075                                 } 
1076                         }
1077                         
1078                         /* light <value> (old-style flare specification) */
1079                         else if( !Q_stricmp( token, "light" ) )
1080                         {
1081                                 GetTokenAppend( shaderText, qfalse );
1082                                 strcpy( si->flareShader, "flareshader" );
1083                         }
1084                         
1085                         /* ydnar: damageShader <shader> <health> (sof2 mods) */
1086                         else if( !Q_stricmp( token, "damageShader" ) )
1087                         {
1088                                 GetTokenAppend( shaderText, qfalse );
1089                                 strcpy( si->damageShader, token );
1090                                 GetTokenAppend( shaderText, qfalse );   /* don't do anything with health */
1091                         }
1092
1093                         /* ydnar: enemy territory implicit shaders */
1094                         else if( !Q_stricmp( token, "implicitMap" ) )
1095                         {
1096                                 si->implicitMap = IM_OPAQUE;
1097                                 GetTokenAppend( shaderText, qfalse );
1098                                 if( token[ 0 ] == '-' && token[ 1 ] == '\0' )
1099                                         sprintf( si->implicitImagePath, "%s.tga", si->shader );
1100                                 else
1101                                         strcpy( si->implicitImagePath, token );
1102                         }
1103
1104                         else if( !Q_stricmp( token, "implicitMask" ) )
1105                         {
1106                                 si->implicitMap = IM_MASKED;
1107                                 GetTokenAppend( shaderText, qfalse );
1108                                 if( token[ 0 ] == '-' && token[ 1 ] == '\0' )
1109                                         sprintf( si->implicitImagePath, "%s.tga", si->shader );
1110                                 else
1111                                         strcpy( si->implicitImagePath, token );
1112                         }
1113
1114                         else if( !Q_stricmp( token, "implicitBlend" ) )
1115                         {
1116                                 si->implicitMap = IM_MASKED;
1117                                 GetTokenAppend( shaderText, qfalse );
1118                                 if( token[ 0 ] == '-' && token[ 1 ] == '\0' )
1119                                         sprintf( si->implicitImagePath, "%s.tga", si->shader );
1120                                 else
1121                                         strcpy( si->implicitImagePath, token );
1122                         }
1123                         
1124                         
1125                         /* -----------------------------------------------------------------
1126                            image directives
1127                            ----------------------------------------------------------------- */
1128                         
1129                         /* qer_editorimage <image> */
1130                         else if( !Q_stricmp( token, "qer_editorImage" ) )
1131                         {
1132                                 GetTokenAppend( shaderText, qfalse );
1133                                 strcpy( si->editorImagePath, token );
1134                                 DefaultExtension( si->editorImagePath, ".tga" );
1135                         }
1136                         
1137                         /* ydnar: q3map_normalimage <image> (bumpmapping normal map) */
1138                         else if( !Q_stricmp( token, "q3map_normalImage" ) )
1139                         {
1140                                 GetTokenAppend( shaderText, qfalse );
1141                                 strcpy( si->normalImagePath, token );
1142                                 DefaultExtension( si->normalImagePath, ".tga" );
1143                         }
1144                         
1145                         /* q3map_lightimage <image> */
1146                         else if( !Q_stricmp( token, "q3map_lightImage" ) )
1147                         {
1148                                 GetTokenAppend( shaderText, qfalse );
1149                                 strcpy( si->lightImagePath, token );
1150                                 DefaultExtension( si->lightImagePath, ".tga" );
1151                         }
1152                         
1153                         /* ydnar: skyparms <outer image> <cloud height> <inner image> */
1154                         else if( !Q_stricmp( token, "skyParms" ) )
1155                         {
1156                                 /* get image base */
1157                                 GetTokenAppend( shaderText, qfalse );
1158                                 
1159                                 /* ignore bogus paths */
1160                                 if( Q_stricmp( token, "-" ) && Q_stricmp( token, "full" ) )
1161                                 {
1162                                         strcpy( si->skyParmsImageBase, token );
1163                                         
1164                                         /* use top image as sky light image */
1165                                         if( si->lightImagePath[ 0 ] == '\0' )
1166                                                 sprintf( si->lightImagePath, "%s_up.tga", si->skyParmsImageBase );
1167                                 }
1168                                 
1169                                 /* skip rest of line */
1170                                 GetTokenAppend( shaderText, qfalse );
1171                                 GetTokenAppend( shaderText, qfalse );
1172                         }
1173                         
1174                         /* -----------------------------------------------------------------
1175                            q3map_* directives
1176                            ----------------------------------------------------------------- */
1177                         
1178                         /* q3map_sun <red> <green> <blue> <intensity> <degrees> <elevation>
1179                            color will be normalized, so it doesn't matter what range you use
1180                            intensity falls off with angle but not distance 100 is a fairly bright sun
1181                            degree of 0 = from the east, 90 = north, etc.  altitude of 0 = sunrise/set, 90 = noon
1182                            ydnar: sof2map has bareword 'sun' token, so we support that as well */
1183                         else if( !Q_stricmp( token, "sun" ) /* sof2 */ || !Q_stricmp( token, "q3map_sun" ) || !Q_stricmp( token, "q3map_sunExt" ) )
1184                         {
1185                                 float           a, b;
1186                                 sun_t           *sun;
1187                                 qboolean        ext;
1188                                 
1189                                 
1190                                 /* ydnar: extended sun directive? */
1191                                 if( !Q_stricmp( token, "q3map_sunext" ) )
1192                                         ext = qtrue;
1193                                 
1194                                 /* allocate sun */
1195                                 sun = safe_malloc( sizeof( *sun ) );
1196                                 memset( sun, 0, sizeof( *sun ) );
1197                                 
1198                                 /* get color */
1199                                 GetTokenAppend( shaderText, qfalse );
1200                                 sun->color[ 0 ] = atof( token );
1201                                 GetTokenAppend( shaderText, qfalse );
1202                                 sun->color[ 1 ] = atof( token );
1203                                 GetTokenAppend( shaderText, qfalse );
1204                                 sun->color[ 2 ] = atof( token );
1205                                 
1206                                 /* normalize it */
1207                                 VectorNormalize( sun->color, sun->color );
1208                                 
1209                                 /* scale color by brightness */
1210                                 GetTokenAppend( shaderText, qfalse );
1211                                 sun->photons = atof( token );
1212                                 
1213                                 /* get sun angle/elevation */
1214                                 GetTokenAppend( shaderText, qfalse );
1215                                 a = atof( token );
1216                                 a = a / 180.0f * Q_PI;
1217                                 
1218                                 GetTokenAppend( shaderText, qfalse );
1219                                 b = atof( token );
1220                                 b = b / 180.0f * Q_PI;
1221                                 
1222                                 sun->direction[ 0 ] = cos( a ) * cos( b );
1223                                 sun->direction[ 1 ] = sin( a ) * cos( b );
1224                                 sun->direction[ 2 ] = sin( b );
1225                                 
1226                                 /* get filter radius from shader */
1227                                 sun->filterRadius = si->lightFilterRadius;
1228                                 
1229                                 /* ydnar: get sun angular deviance/samples */
1230                                 if( ext && TokenAvailable() )
1231                                 {
1232                                         GetTokenAppend( shaderText, qfalse );
1233                                         sun->deviance = atof( token );
1234                                         sun->deviance = sun->deviance / 180.0f * Q_PI;
1235                                         
1236                                         GetTokenAppend( shaderText, qfalse );
1237                                         sun->numSamples = atoi( token );
1238                                 }
1239                                 
1240                                 /* store sun */
1241                                 sun->next = si->sun;
1242                                 si->sun = sun;
1243                                 
1244                                 /* apply sky surfaceparm */
1245                                 ApplySurfaceParm( "sky", &si->contentFlags, &si->surfaceFlags, &si->compileFlags );
1246                                 
1247                                 /* don't process any more tokens on this line */
1248                                 continue;
1249                         }
1250
1251                         /* match q3map_ */
1252                         else if( !Q_strncasecmp( token, "q3map_", 6 ) )
1253                         {
1254                                 /* ydnar: q3map_baseShader <shader> (inherit this shader's parameters) */
1255                                 if( !Q_stricmp( token, "q3map_baseShader" ) )
1256                                 {
1257                                         shaderInfo_t    *si2;
1258                                         qboolean                oldWarnImage;
1259                                         
1260                                         
1261                                         /* get shader */
1262                                         GetTokenAppend( shaderText, qfalse );
1263                                         //%     Sys_FPrintf( SYS_VRB, "Shader %s has base shader %s\n", si->shader, token );
1264                                         oldWarnImage = warnImage;
1265                                         warnImage = qfalse;
1266                                         si2 = ShaderInfoForShader( token );
1267                                         warnImage = oldWarnImage;
1268                                         
1269                                         /* subclass it */
1270                                         if( si2 != NULL )
1271                                         {
1272                                                 /* preserve name */
1273                                                 strcpy( temp, si->shader );
1274                                                 
1275                                                 /* copy shader */
1276                                                 memcpy( si, si2, sizeof( *si ) );
1277                                                 
1278                                                 /* restore name and set to unfinished */
1279                                                 strcpy( si->shader, temp );
1280                                                 si->finished = qfalse;
1281                                         }
1282                                 }
1283                                 
1284                                 /* ydnar: q3map_surfacemodel <path to model> <density> <min scale> <max scale> <min angle> <max angle> <oriented (0 or 1)> */
1285                                 else if( !Q_stricmp( token, "q3map_surfacemodel" ) )
1286                                 {
1287                                         surfaceModel_t  *model;
1288                                         
1289                                         
1290                                         /* allocate new model and attach it */
1291                                         model = safe_malloc( sizeof( *model ) );
1292                                         memset( model, 0, sizeof( *model ) );
1293                                         model->next = si->surfaceModel;
1294                                         si->surfaceModel = model;
1295                                                 
1296                                         /* get parameters */
1297                                         GetTokenAppend( shaderText, qfalse );
1298                                         strcpy( model->model, token );
1299                                         
1300                                         GetTokenAppend( shaderText, qfalse );
1301                                         model->density = atof( token );
1302                                         GetTokenAppend( shaderText, qfalse );
1303                                         model->odds = atof( token );
1304                                         
1305                                         GetTokenAppend( shaderText, qfalse );
1306                                         model->minScale = atof( token );
1307                                         GetTokenAppend( shaderText, qfalse );
1308                                         model->maxScale = atof( token );
1309                                         
1310                                         GetTokenAppend( shaderText, qfalse );
1311                                         model->minAngle = atof( token );
1312                                         GetTokenAppend( shaderText, qfalse );
1313                                         model->maxAngle = atof( token );
1314                                         
1315                                         GetTokenAppend( shaderText, qfalse );
1316                                         model->oriented = (token[ 0 ] == '1' ? qtrue : qfalse);
1317                                 }
1318                                 
1319                                 /* ydnar/sd: q3map_foliage <path to model> <scale> <density> <odds> <invert alpha (1 or 0)> */
1320                                 else if( !Q_stricmp( token, "q3map_foliage" ) )
1321                                 {
1322                                         foliage_t       *foliage;
1323                                         
1324                                         
1325                                         /* allocate new foliage struct and attach it */
1326                                         foliage = safe_malloc( sizeof( *foliage ) );
1327                                         memset( foliage, 0, sizeof( *foliage ) );
1328                                         foliage->next = si->foliage;
1329                                         si->foliage = foliage;
1330                                         
1331                                         /* get parameters */
1332                                         GetTokenAppend( shaderText, qfalse );
1333                                         strcpy( foliage->model, token );
1334                                         
1335                                         GetTokenAppend( shaderText, qfalse );
1336                                         foliage->scale = atof( token );
1337                                         GetTokenAppend( shaderText, qfalse );
1338                                         foliage->density = atof( token );
1339                                         GetTokenAppend( shaderText, qfalse );
1340                                         foliage->odds = atof( token );
1341                                         GetTokenAppend( shaderText, qfalse );
1342                                         foliage->inverseAlpha = atoi( token );
1343                                 }
1344                                 
1345                                 /* ydnar: q3map_bounce <value> (fraction of light to re-emit during radiosity passes) */
1346                                 else if( !Q_stricmp( token, "q3map_bounce" ) || !Q_stricmp( token, "q3map_bounceScale" ) )
1347                                 {
1348                                         GetTokenAppend( shaderText, qfalse );
1349                                         si->bounceScale = atof( token );
1350                                 }
1351
1352                                 /* ydnar/splashdamage: q3map_skylight <value> <iterations> */
1353                                 else if( !Q_stricmp( token, "q3map_skylight" )  )
1354                                 {
1355                                         GetTokenAppend( shaderText, qfalse );
1356                                         si->skyLightValue = atof( token );
1357                                         GetTokenAppend( shaderText, qfalse );
1358                                         si->skyLightIterations = atoi( token );
1359                                         
1360                                         /* clamp */
1361                                         if( si->skyLightValue < 0.0f )
1362                                                 si->skyLightValue = 0.0f;
1363                                         if( si->skyLightIterations < 2 )
1364                                                 si->skyLightIterations = 2;
1365                                 }
1366                                 
1367                                 /* q3map_surfacelight <value> */
1368                                 else if( !Q_stricmp( token, "q3map_surfacelight" )  )
1369                                 {
1370                                         GetTokenAppend( shaderText, qfalse );
1371                                         si->value = atof( token );
1372                                 }
1373                                 
1374                                 
1375                                 /* q3map_lightStyle (sof2/jk2 lightstyle) */
1376                                 else if( !Q_stricmp( token, "q3map_lightStyle" ) )
1377                                 {
1378                                         GetTokenAppend( shaderText, qfalse );
1379                                         val = atoi( token );
1380                                         if( val < 0 )
1381                                                 val = 0;
1382                                         else if( val > LS_NONE )
1383                                                 val = LS_NONE;
1384                                         si->lightStyle = val;
1385                                 }
1386                                 
1387                                 /* wolf: q3map_lightRGB <red> <green> <blue> */
1388                                 else if( !Q_stricmp( token, "q3map_lightRGB" ) )
1389                                 {
1390                                         VectorClear( si->color );
1391                                         GetTokenAppend( shaderText, qfalse );
1392                                         si->color[ 0 ] = atof( token );
1393                                         GetTokenAppend( shaderText, qfalse );
1394                                         si->color[ 1 ] = atof( token );
1395                                         GetTokenAppend( shaderText, qfalse );
1396                                         si->color[ 2 ] = atof( token );
1397                                         ColorNormalize( si->color, si->color );
1398                                 }
1399                                 
1400                                 /* q3map_lightSubdivide <value> */
1401                                 else if( !Q_stricmp( token, "q3map_lightSubdivide" )  )
1402                                 {
1403                                         GetTokenAppend( shaderText, qfalse );
1404                                         si->lightSubdivide = atoi( token );
1405                                 }
1406                                 
1407                                 /* q3map_backsplash <percent> <distance> */
1408                                 else if( !Q_stricmp( token, "q3map_backsplash" ) )
1409                                 {
1410                                         GetTokenAppend( shaderText, qfalse );
1411                                         si->backsplashFraction = atof( token ) * 0.01f;
1412                                         GetTokenAppend( shaderText, qfalse );
1413                                         si->backsplashDistance = atof( token );
1414                                 }
1415                                 
1416                                 /* q3map_lightmapSampleSize <value> */
1417                                 else if( !Q_stricmp( token, "q3map_lightmapSampleSize" ) )
1418                                 {
1419                                         GetTokenAppend( shaderText, qfalse );
1420                                         si->lightmapSampleSize = atoi( token );
1421                                 }
1422                                 
1423                                 /* q3map_lightmapSampleSffset <value> */
1424                                 else if( !Q_stricmp( token, "q3map_lightmapSampleOffset" ) )
1425                                 {
1426                                         GetTokenAppend( shaderText, qfalse );
1427                                         si->lightmapSampleOffset = atof( token );
1428                                 }
1429                                 
1430                                 /* ydnar: q3map_lightmapFilterRadius <self> <other> */
1431                                 else if( !Q_stricmp( token, "q3map_lightmapFilterRadius" ) )
1432                                 {
1433                                         GetTokenAppend( shaderText, qfalse );
1434                                         si->lmFilterRadius = atof( token );
1435                                         GetTokenAppend( shaderText, qfalse );
1436                                         si->lightFilterRadius = atof( token );
1437                                 }
1438                                 
1439                                 /* ydnar: q3map_lightmapAxis [xyz] */
1440                                 else if( !Q_stricmp( token, "q3map_lightmapAxis" ) )
1441                                 {
1442                                         GetTokenAppend( shaderText, qfalse );
1443                                         if( !Q_stricmp( token, "x" ) )
1444                                                 VectorSet( si->lightmapAxis, 1, 0, 0 );
1445                                         else if( !Q_stricmp( token, "y" ) )
1446                                                 VectorSet( si->lightmapAxis, 0, 1, 0 );
1447                                         else if( !Q_stricmp( token, "z" ) )
1448                                                 VectorSet( si->lightmapAxis, 0, 0, 1 );
1449                                         else
1450                                         {
1451                                                 Sys_Printf( "WARNING: Unknown value for lightmap axis: %s\n", token );
1452                                                 VectorClear( si->lightmapAxis );
1453                                         }
1454                                 }
1455                                 
1456                                 /* ydnar: q3map_lightmapSize <width> <height> (for autogenerated shaders + external tga lightmaps) */
1457                                 else if( !Q_stricmp( token, "q3map_lightmapSize" ) )
1458                                 {
1459                                         GetTokenAppend( shaderText, qfalse );
1460                                         si->lmCustomWidth = atoi( token );
1461                                         GetTokenAppend( shaderText, qfalse );
1462                                         si->lmCustomHeight = atoi( token );
1463                                         
1464                                         /* must be a power of 2 */
1465                                         if( ((si->lmCustomWidth - 1) & si->lmCustomWidth) ||
1466                                                 ((si->lmCustomHeight - 1) & si->lmCustomHeight) )
1467                                         {
1468                                                 Sys_Printf( "WARNING: Non power-of-two lightmap size specified (%d, %d)\n",
1469                                                          si->lmCustomWidth, si->lmCustomHeight );
1470                                                 si->lmCustomWidth = LIGHTMAP_WIDTH;
1471                                                 si->lmCustomHeight = LIGHTMAP_HEIGHT;
1472                                         }
1473                                 }
1474
1475                                 /* ydnar: q3map_lightmapGamma N (for autogenerated shaders + external tga lightmaps) */
1476                                 else if( !Q_stricmp( token, "q3map_lightmapGamma" ) )
1477                                 {
1478                                         GetTokenAppend( shaderText, qfalse );
1479                                         si->lmGamma = atof( token );
1480                                         if( si->lmGamma < 0 )
1481                                                 si->lmGamma = 1.0;
1482                                 }
1483                                 
1484                                 /* q3map_vertexScale (scale vertex lighting by this fraction) */
1485                                 else if( !Q_stricmp( token, "q3map_vertexScale" ) )
1486                                 {
1487                                         GetTokenAppend( shaderText, qfalse );
1488                                         si->vertexScale = atof( token );
1489                                 }
1490                                 
1491                                 /* q3map_flare <shader> */
1492                                 else if( !Q_stricmp( token, "q3map_flare" ) || !Q_stricmp( token, "q3map_flareShader" ) )
1493                                 {
1494                                         GetTokenAppend( shaderText, qfalse );
1495                                         strcpy( si->flareShader, token );
1496                                 }
1497                                 
1498                                 /* q3map_backShader <shader> */
1499                                 else if( !Q_stricmp( token, "q3map_backShader" ) )
1500                                 {
1501                                         GetTokenAppend( shaderText, qfalse );
1502                                         strcpy( si->backShader, token );
1503                                 }
1504                                 
1505                                 /* ydnar: q3map_offset <value> */
1506                                 else if( !Q_stricmp( token, "q3map_offset" ) )
1507                                 {
1508                                         GetTokenAppend( shaderText, qfalse );
1509                                         si->offset = atof( token );
1510                                 }
1511                                 
1512                                 /* ydnar: q3map_cloneShader <shader> */
1513                                 else if ( !Q_stricmp( token, "q3map_cloneShader" ) )
1514                                 {
1515                                         GetTokenAppend( shaderText, qfalse );
1516                                         strcpy( si->cloneShader, token );
1517                                 }
1518                                 
1519                                 /* ydnar: q3map_textureSize <width> <height> (substitute for q3map_lightimage derivation for terrain) */
1520                                 else if( !Q_stricmp( token, "q3map_fur" ) )
1521                                 {
1522                                         GetTokenAppend( shaderText, qfalse );
1523                                         si->furNumLayers = atoi( token );
1524                                         GetTokenAppend( shaderText, qfalse );
1525                                         si->furOffset = atof( token );
1526                                         GetTokenAppend( shaderText, qfalse );
1527                                         si->furFade = atof( token );
1528                                 }
1529                                 
1530                                 /* ydnar: gs mods: legacy support for terrain/terrain2 shaders */
1531                                 else if( !Q_stricmp( token, "q3map_terrain" ) )
1532                                 {
1533                                         /* team arena terrain is assumed to be nonplanar, with full normal averaging,
1534                                            passed through the metatriangle surface pipeline, with a lightmap axis on z */
1535                                         si->legacyTerrain = qtrue;
1536                                         si->noClip = qtrue;
1537                                         si->notjunc = qtrue;
1538                                         si->indexed = qtrue;
1539                                         si->nonplanar = qtrue;
1540                                         si->forceMeta = qtrue;
1541                                         si->shadeAngleDegrees = 179.0f;
1542                                         //%     VectorSet( si->lightmapAxis, 0, 0, 1 ); /* ydnar 2002-09-21: turning this off for better lightmapping of cliff faces */
1543                                 }
1544                                 
1545                                 /* ydnar: picomodel: q3map_forceMeta (forces brush faces and/or triangle models to go through the metasurface pipeline) */
1546                                 else if( !Q_stricmp( token, "q3map_forceMeta" ) )
1547                                 {
1548                                         si->forceMeta = qtrue;
1549                                 }
1550                                 
1551                                 /* ydnar: gs mods: q3map_shadeAngle <degrees> */
1552                                 else if( !Q_stricmp( token, "q3map_shadeAngle" ) )
1553                                 {
1554                                         GetTokenAppend( shaderText, qfalse );
1555                                         si->shadeAngleDegrees = atof( token );
1556                                 }
1557                                 
1558                                 /* ydnar: q3map_textureSize <width> <height> (substitute for q3map_lightimage derivation for terrain) */
1559                                 else if( !Q_stricmp( token, "q3map_textureSize" ) )
1560                                 {
1561                                         GetTokenAppend( shaderText, qfalse );
1562                                         si->shaderWidth = atoi( token );
1563                                         GetTokenAppend( shaderText, qfalse );
1564                                         si->shaderHeight = atoi( token );
1565                                 }
1566                                 
1567                                 /* ydnar: gs mods: q3map_tcGen <style> <parameters> */
1568                                 else if( !Q_stricmp( token, "q3map_tcGen" ) )
1569                                 {
1570                                         si->tcGen = qtrue;
1571                                         GetTokenAppend( shaderText, qfalse );
1572                                         
1573                                         /* q3map_tcGen vector <s vector> <t vector> */
1574                                         if( !Q_stricmp( token, "vector" ) )
1575                                         {
1576                                                 Parse1DMatrixAppend( shaderText, 3, si->vecs[ 0 ] );
1577                                                 Parse1DMatrixAppend( shaderText, 3, si->vecs[ 1 ] );
1578                                         }
1579                                         
1580                                         /* q3map_tcGen ivector <1.0/s vector> <1.0/t vector> (inverse vector, easier for mappers to understand) */
1581                                         else if( !Q_stricmp( token, "ivector" ) )
1582                                         {
1583                                                 Parse1DMatrixAppend( shaderText, 3, si->vecs[ 0 ] );
1584                                                 Parse1DMatrixAppend( shaderText, 3, si->vecs[ 1 ] );
1585                                                 for( i = 0; i < 3; i++ )
1586                                                 {
1587                                                         si->vecs[ 0 ][ i ] = si->vecs[ 0 ][ i ] ? 1.0 / si->vecs[ 0 ][ i ] : 0;
1588                                                         si->vecs[ 1 ][ i ] = si->vecs[ 1 ][ i ] ? 1.0 / si->vecs[ 1 ][ i ] : 0;
1589                                                 }
1590                                         }
1591                                         else
1592                                         {
1593                                                 Sys_Printf( "WARNING: Unknown q3map_tcGen method: %s\n", token );
1594                                                 VectorClear( si->vecs[ 0 ] );
1595                                                 VectorClear( si->vecs[ 1 ] );
1596                                         }
1597                                 }
1598                                 
1599                                 /* ydnar: gs mods: q3map_alphaMod <style> <parameters> */
1600                                 else if( !Q_stricmp( token, "q3map_alphaMod" ) )
1601                                 {
1602                                         alphaMod_t      *am, *am2;
1603                                         
1604                                         
1605                                         /* allocate new alpha mod */
1606                                         am = safe_malloc( sizeof( *am ) );
1607                                         memset( am, 0, sizeof( *am ) );
1608                                         
1609                                         /* attach to shader */
1610                                         if( si->alphaMod == NULL )
1611                                                 si->alphaMod = am;
1612                                         else
1613                                         {
1614                                                 for( am2 = si->alphaMod; am2 != NULL; am2 = am2->next )
1615                                                 {
1616                                                         if( am2->next == NULL )
1617                                                         {
1618                                                                 am2->next = am;
1619                                                                 break;
1620                                                         }
1621                                                 }
1622                                         }
1623                                         
1624                                         /* get type */
1625                                         GetTokenAppend( shaderText, qfalse );
1626                                         
1627                                         /* q3map_alphaMod dotproduct ( X Y Z ) */
1628                                         if( !Q_stricmp( token, "dotproduct" ) )
1629                                         {
1630                                                 am->type = AM_DOT_PRODUCT;
1631                                                 Parse1DMatrixAppend( shaderText, 3, am->data );
1632                                         }
1633                                         else
1634                                                 Sys_Printf( "WARNING: Unknown q3map_alphaMod method: %s\n", token );
1635                                 }
1636                                 
1637                                 /* ydnar: gs mods: q3map_tcMod <style> <parameters> */
1638                                 else if( !Q_stricmp( token, "q3map_tcMod" ) )
1639                                 {
1640                                         float   a, b;
1641                                         
1642                                         
1643                                         GetTokenAppend( shaderText, qfalse );
1644                                         
1645                                         /* q3map_tcMod [translate | shift | offset] <s> <t> */
1646                                         if( !Q_stricmp( token, "translate" ) || !Q_stricmp( token, "shift" ) || !Q_stricmp( token, "offset" ) )
1647                                         {
1648                                                 GetTokenAppend( shaderText, qfalse );
1649                                                 a = atof( token );
1650                                                 GetTokenAppend( shaderText, qfalse );
1651                                                 b = atof( token );
1652                                                 
1653                                                 TcModTranslate( si->mod, a, b );
1654                                         }
1655
1656                                         /* q3map_tcMod scale <s> <t> */
1657                                         else if( !Q_stricmp( token, "scale" ) )
1658                                         {
1659                                                 GetTokenAppend( shaderText, qfalse );
1660                                                 a = atof( token );
1661                                                 GetTokenAppend( shaderText, qfalse );
1662                                                 b = atof( token );
1663                                                 
1664                                                 TcModScale( si->mod, a, b );
1665                                         }
1666                                         
1667                                         /* q3map_tcMod rotate <s> <t> (fixme: make this communitive) */
1668                                         else if( !Q_stricmp( token, "rotate" ) )
1669                                         {
1670                                                 GetTokenAppend( shaderText, qfalse );
1671                                                 a = atof( token );
1672                                                 TcModRotate( si->mod, a );
1673                                         }
1674                                         else
1675                                                 Sys_Printf( "WARNING: Unknown q3map_tcMod method: %s\n", token );
1676                                 }
1677                                 
1678                                 /* q3map_fogDir (direction a fog shader fades from transparent to opaque) */
1679                                 else if( !Q_stricmp( token, "q3map_fogDir" ) )
1680                                 {
1681                                         Parse1DMatrixAppend( shaderText, 3, si->fogDir );
1682                                         VectorNormalize( si->fogDir, si->fogDir );
1683                                 }
1684                                 
1685                                 /* q3map_globaltexture */
1686                                 else if( !Q_stricmp( token, "q3map_globaltexture" )  )
1687                                         si->globalTexture = qtrue;
1688                                 
1689                                 /* ydnar: gs mods: q3map_nonplanar (make it a nonplanar merge candidate for meta surfaces) */
1690                                 else if( !Q_stricmp( token, "q3map_nonplanar" ) )
1691                                         si->nonplanar = qtrue;
1692                                 
1693                                 /* ydnar: gs mods: q3map_noclip (preserve original face winding, don't clip by bsp tree) */
1694                                 else if( !Q_stricmp( token, "q3map_noclip" ) )
1695                                         si->noClip = qtrue;
1696                                 
1697                                 /* q3map_notjunc */
1698                                 else if( !Q_stricmp( token, "q3map_notjunc" ) )
1699                                         si->notjunc = qtrue;
1700                                 
1701                                 /* q3map_nofog */
1702                                 else if( !Q_stricmp( token, "q3map_nofog" ) )
1703                                         si->noFog = qtrue;
1704                                 
1705                                 /* ydnar: gs mods: q3map_indexed (for explicit terrain-style indexed mapping) */
1706                                 else if( !Q_stricmp( token, "q3map_indexed" ) )
1707                                         si->indexed = qtrue;
1708                                 
1709                                 /* ydnar: q3map_invert (inverts a drawsurface's facing) */
1710                                 else if( !Q_stricmp( token, "q3map_invert" ) )
1711                                         si->invert = qtrue;
1712                                 
1713                                 /* ydnar: gs mods: q3map_lightmapMergable (ok to merge non-planar */
1714                                 else if( !Q_stricmp( token, "q3map_lightmapMergable" ) )
1715                                         si->lmMergable = qtrue;
1716                                 
1717                                 /* ydnar: q3map_nofast */
1718                                 else if( !Q_stricmp( token, "q3map_noFast" ) )
1719                                         si->noFast = qtrue;
1720                                 
1721                                 /* q3map_patchshadows */
1722                                 else if( !Q_stricmp( token, "q3map_patchShadows" ) )
1723                                         si->patchShadows = qtrue;
1724                                 
1725                                 /* q3map_vertexshadows */
1726                                 else if( !Q_stricmp( token, "q3map_vertexShadows" ) )
1727                                         si->vertexShadows = qtrue;      /* ydnar */
1728                                 
1729                                 /* q3map_novertexshadows */
1730                                 else if( !Q_stricmp( token, "q3map_noVertexShadows" ) )
1731                                         si->vertexShadows = qfalse;     /* ydnar */
1732                                 
1733                                 /* q3map_splotchfix (filter dark lightmap luxels on lightmapped models) */
1734                                 else if( !Q_stricmp( token, "q3map_splotchfix" ) )
1735                                         si->splotchFix = qtrue; /* ydnar */
1736                                 
1737                                 /* q3map_forcesunlight */
1738                                 else if( !Q_stricmp( token, "q3map_forceSunlight" ) )
1739                                         si->forceSunlight = qtrue;
1740                                 
1741                                 /* q3map_onlyvertexlighting (sof2) */
1742                                 else if( !Q_stricmp( token, "q3map_onlyVertexLighting" ) )
1743                                         ApplySurfaceParm( "pointlight", &si->contentFlags, &si->surfaceFlags, &si->compileFlags );
1744                                 
1745                                 /* q3map_material (sof2) */
1746                                 else if( !Q_stricmp( token, "q3map_material" ) )
1747                                 {
1748                                         GetTokenAppend( shaderText, qfalse );
1749                                         sprintf( temp, "*mat_%s", token );
1750                                         if( ApplySurfaceParm( temp, &si->contentFlags, &si->surfaceFlags, &si->compileFlags ) == qfalse )
1751                                                 Sys_Printf( "WARNING: Unknown material \"%s\"\n", token );
1752                                 }
1753                                 
1754                                 /* ydnar: q3map_clipmodel (autogenerate clip brushes for model triangles using this shader) */
1755                                 else if( !Q_stricmp( token, "q3map_clipmodel" )  )
1756                                         si->clipModel = qtrue;
1757                                 
1758                                 /* ydnar: q3map_styleMarker[2] */
1759                                 else if( !Q_stricmp( token, "q3map_styleMarker" ) )
1760                                         si->styleMarker = 1;
1761                                 else if( !Q_stricmp( token, "q3map_styleMarker2" ) )    /* uses depthFunc equal */
1762                                         si->styleMarker = 2;
1763                                 
1764                                 /* ydnar: default to searching for q3map_<surfaceparm> */
1765                                 else
1766                                 {
1767                                         //%     Sys_FPrintf( SYS_VRB, "Attempting to match %s with a known surfaceparm\n", token );
1768                                         if( ApplySurfaceParm( &token[ 6 ], &si->contentFlags, &si->surfaceFlags, &si->compileFlags ) == qfalse )
1769                                                 ;//%    Sys_Printf( "WARNING: Unknown q3map_* directive \"%s\"\n", token );
1770                                 }
1771                         }
1772                         
1773                         
1774                         /* -----------------------------------------------------------------
1775                            skip
1776                            ----------------------------------------------------------------- */
1777                         
1778                         /* ignore all other tokens on the line */
1779                         while( TokenAvailable() && GetTokenAppend( shaderText, qfalse ) );
1780                 }                       
1781         }
1782 }
1783
1784
1785
1786 /*
1787 ParseCustomInfoParms() - rr2do2
1788 loads custom info parms file for mods
1789 */
1790
1791 static void ParseCustomInfoParms( void )
1792 {
1793         qboolean parsedContent, parsedSurface;
1794         
1795         
1796         /* file exists? */
1797         if( vfsGetFileCount( "scripts/custinfoparms.txt" ) == 0 )
1798                 return;
1799         
1800         /* load it */
1801         LoadScriptFile( "scripts/custinfoparms.txt", 0 );
1802         
1803         /* clear the array */
1804         memset( custSurfaceParms, 0, sizeof( custSurfaceParms ) );
1805         numCustSurfaceParms = 0;
1806         parsedContent = parsedSurface = qfalse;
1807         
1808         /* parse custom contentflags */
1809         MatchToken( "{" );
1810         while ( 1 )
1811         {
1812                 if ( !GetToken( qtrue ) )
1813                         break;
1814
1815                 if ( !strcmp( token, "}" ) ) {
1816                         parsedContent = qtrue;
1817                         break;
1818                 }
1819
1820                 custSurfaceParms[ numCustSurfaceParms ].name = safe_malloc( MAX_OS_PATH );
1821                 strcpy( custSurfaceParms[ numCustSurfaceParms ].name, token );
1822                 GetToken( qfalse );
1823                 sscanf( token, "%x", &custSurfaceParms[ numCustSurfaceParms ].contentFlags );
1824                 numCustSurfaceParms++;
1825         }
1826         
1827         /* any content? */
1828         if( !parsedContent )
1829         {
1830                 Sys_Printf( "WARNING: Couldn't find valid custom contentsflag section\n" );
1831                 return;
1832         }
1833         
1834         /* parse custom surfaceflags */
1835         MatchToken( "{" );
1836         while( 1 )
1837         {
1838                 if( !GetToken( qtrue ) )
1839                         break;
1840
1841                 if( !strcmp( token, "}" ) )
1842                 {
1843                         parsedSurface = qtrue;
1844                         break;
1845                 }
1846
1847                 custSurfaceParms[ numCustSurfaceParms ].name = safe_malloc( MAX_OS_PATH );
1848                 strcpy( custSurfaceParms[ numCustSurfaceParms ].name, token );
1849                 GetToken( qfalse );
1850                 sscanf( token, "%x", &custSurfaceParms[ numCustSurfaceParms ].surfaceFlags );
1851                 numCustSurfaceParms++;
1852         }
1853         
1854         /* any content? */
1855         if( !parsedContent )
1856                 Sys_Printf( "WARNING: Couldn't find valid custom surfaceflag section\n" );
1857 }
1858
1859         
1860
1861 /*
1862 LoadShaderInfo()
1863 the shaders are parsed out of shaderlist.txt from a main directory
1864 that is, if using -fs_game we ignore the shader scripts that might be in baseq3/
1865 on linux there's an additional twist, we actually merge the stuff from ~/.q3a/ and from the base dir
1866 */
1867
1868 #define MAX_SHADER_FILES        1024
1869
1870 void LoadShaderInfo( void )
1871 {
1872         int                             i, j, numShaderFiles, count;
1873         char                    filename[ 1024 ];
1874         char                    *shaderFiles[ MAX_SHADER_FILES ];
1875         
1876         
1877         /* rr2do2: parse custom infoparms first */
1878         if( useCustomInfoParms )
1879                 ParseCustomInfoParms();
1880         
1881         /* start with zero */
1882         numShaderFiles = 0;
1883         
1884         /* we can pile up several shader files, the one in baseq3 and ones in the mod dir or other spots */
1885         sprintf( filename, "%s/shaderlist.txt", game->shaderPath );
1886         count = vfsGetFileCount( filename );
1887         
1888         /* load them all */
1889         for( i = 0; i < count; i++ )
1890         {
1891                 /* load shader list */
1892                 sprintf( filename, "%s/shaderlist.txt", game->shaderPath );
1893                 LoadScriptFile( filename, i );
1894                 
1895                 /* parse it */
1896                 while( GetToken( qtrue ) )
1897                 {
1898                         /* check for duplicate entries */
1899                         for( j = 0; j < numShaderFiles; j++ )
1900                                 if( !strcmp( shaderFiles[ j ], token ) )
1901                                         break;
1902                         
1903                         /* test limit */
1904                         if( j >= MAX_SHADER_FILES )
1905                                 Error( "MAX_SHADER_FILES (%d) reached, trim your shaderlist.txt!", (int) MAX_SHADER_FILES );
1906                         
1907                         /* new shader file */
1908                         if( j == numShaderFiles )
1909                         {
1910                                 shaderFiles[ numShaderFiles ] = safe_malloc( MAX_OS_PATH );
1911                                 strcpy( shaderFiles[ numShaderFiles ], token );
1912                                 numShaderFiles++;
1913                         }
1914                 }
1915         }
1916         
1917         /* parse the shader files */
1918         for( i = 0; i < numShaderFiles; i++ )
1919         {
1920                 sprintf( filename, "%s/%s.shader", game->shaderPath, shaderFiles[ i ] );
1921                 ParseShaderFile( filename );
1922                 free( shaderFiles[ i ] );
1923         }
1924         
1925         /* emit some statistics */
1926         Sys_FPrintf( SYS_VRB, "%9d shaderInfo\n", numShaderInfo );
1927 }