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