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