Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

shaders.c (14989B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 
     23 #include <string.h>
     24 #include <math.h>
     25 #include "cmdlib.h"
     26 #include "mathlib.h"
     27 #include "imagelib.h"
     28 #include "scriplib.h"
     29 
     30 #ifdef _TTIMOBUILD
     31 #include "../common/qfiles.h"
     32 #include "../common/surfaceflags.h"
     33 #else
     34 #include "../code/qcommon/qfiles.h"
     35 #include "../code/game/surfaceflags.h"
     36 #endif
     37 
     38 #include "shaders.h"
     39 #ifdef _WIN32
     40 
     41 #ifdef _TTIMOBUILD
     42 #include "pakstuff.h"
     43 #include "jpeglib.h"
     44 #else
     45 #include "../libs/pakstuff.h"
     46 #include "../libs/jpeglib.h"
     47 #endif
     48 
     49 #endif
     50 
     51 
     52 // 5% backsplash by default
     53 #define	DEFAULT_BACKSPLASH_FRACTION		0.05
     54 #define	DEFAULT_BACKSPLASH_DISTANCE		24
     55 
     56 
     57 #define	MAX_SURFACE_INFO	4096
     58 
     59 shaderInfo_t	defaultInfo;
     60 shaderInfo_t	shaderInfo[MAX_SURFACE_INFO];
     61 int				numShaderInfo;
     62 
     63 
     64 typedef struct {
     65 	char	*name;
     66 	int		clearSolid, surfaceFlags, contents;
     67 } infoParm_t;
     68 
     69 infoParm_t	infoParms[] = {
     70 	// server relevant contents
     71 	{"water",		1,	0,	CONTENTS_WATER },
     72 	{"slime",		1,	0,	CONTENTS_SLIME },		// mildly damaging
     73 	{"lava",		1,	0,	CONTENTS_LAVA },		// very damaging
     74 	{"playerclip",	1,	0,	CONTENTS_PLAYERCLIP },
     75 	{"monsterclip",	1,	0,	CONTENTS_MONSTERCLIP },
     76 	{"nodrop",		1,	0,	CONTENTS_NODROP },		// don't drop items or leave bodies (death fog, lava, etc)
     77 	{"nonsolid",	1,	SURF_NONSOLID,	0},						// clears the solid flag
     78 
     79 	// utility relevant attributes
     80 	{"origin",		1,	0,	CONTENTS_ORIGIN },		// center of rotating brushes
     81 	{"trans",		0,	0,	CONTENTS_TRANSLUCENT },	// don't eat contained surfaces
     82 	{"detail",		0,	0,	CONTENTS_DETAIL },		// don't include in structural bsp
     83 	{"structural",	0,	0,	CONTENTS_STRUCTURAL },	// force into structural bsp even if trnas
     84 	{"areaportal",	1,	0,	CONTENTS_AREAPORTAL },	// divides areas
     85 	{"clusterportal",1, 0,  CONTENTS_CLUSTERPORTAL },// for bots
     86 	{"donotenter",  1,  0,  CONTENTS_DONOTENTER },	// for bots
     87 	{"botclip",     1,  0,  CONTENTS_BOTCLIP },		// for bots
     88 	{"nobotclip",	0,	0,	CONTENTS_NOBOTCLIP },	// don't use for bot clipping
     89 
     90 	{"fog",			1,	0,	CONTENTS_FOG},			// carves surfaces entering
     91 	{"sky",			0,	SURF_SKY,		0 },		// emit light from an environment map
     92 	{"lightfilter",	0,	SURF_LIGHTFILTER, 0 },		// filter light going through it
     93 	{"alphashadow",	0,	SURF_ALPHASHADOW, 0 },		// test light on a per-pixel basis
     94 	{"hint",		0,	SURF_HINT,		0 },		// use as a primary splitter
     95 
     96 	// server attributes
     97 	{"slick",		0,	SURF_SLICK,		0 },
     98 	{"noimpact",	0,	SURF_NOIMPACT,	0 },		// don't make impact explosions or marks
     99 	{"nomarks",		0,	SURF_NOMARKS,	0 },		// don't make impact marks, but still explode
    100 	{"ladder",		0,	SURF_LADDER,	0 },
    101 	{"nodamage",	0,	SURF_NODAMAGE,	0 },
    102 	{"metalsteps",	0,	SURF_METALSTEPS,0 },
    103 	{"flesh",		0,	SURF_FLESH,		0 },
    104 	{"nosteps",		0,	SURF_NOSTEPS,	0 },
    105 
    106 	// drawsurf attributes
    107 	{"nodraw",		0,	SURF_NODRAW,	0 },		// don't generate a drawsurface (or a lightmap)
    108 	{"pointlight",	0,	SURF_POINTLIGHT, 0 },		// sample lighting at vertexes
    109 	{"nolightmap",	0,	SURF_NOLIGHTMAP,0 },		// don't generate a lightmap
    110 	{"nodlight",	0,	SURF_NODLIGHT, 0 },			// don't ever add dynamic lights
    111 	{"dust",		0,	SURF_DUST, 0}				// leave dust trail when walking on this surface
    112 };
    113 
    114 
    115 /*
    116 ===============
    117 LoadShaderImage
    118 ===============
    119 */
    120 
    121 byte* LoadImageFile(char *filename, qboolean *bTGA)
    122 {
    123   byte *buffer = NULL;
    124   int nLen = 0;
    125   *bTGA = qtrue;
    126   if (FileExists(filename))
    127   {
    128     LoadFileBlock(filename, &buffer);
    129   }
    130 #ifdef _WIN32
    131   else
    132   {
    133     PakLoadAnyFile(filename, &buffer);
    134   }
    135 #endif
    136   if ( buffer == NULL)
    137   {
    138     nLen = strlen(filename);
    139     filename[nLen-3] = 'j';
    140     filename[nLen-2] = 'p';
    141     filename[nLen-1] = 'g';
    142     if (FileExists(filename))
    143     {
    144       LoadFileBlock(filename, &buffer);
    145     }
    146 #ifdef _WIN32
    147     else
    148     {
    149       PakLoadAnyFile(filename, &buffer);
    150     }
    151 #endif
    152     if ( buffer )
    153     {
    154       *bTGA = qfalse;
    155     }
    156   }
    157   return buffer;
    158 }
    159 
    160 /*
    161 ===============
    162 LoadShaderImage
    163 ===============
    164 */
    165 static void LoadShaderImage( shaderInfo_t *si ) {
    166 	char			filename[1024];
    167 	int				i, count;
    168 	float			color[4];
    169   byte      *buffer;
    170   qboolean  bTGA = qtrue;
    171 
    172 	// look for the lightimage if it is specified
    173 	if ( si->lightimage[0] ) {
    174 		sprintf( filename, "%s%s", gamedir, si->lightimage );
    175 		DefaultExtension( filename, ".tga" );
    176     buffer = LoadImageFile(filename, &bTGA);
    177     if ( buffer != NULL) {
    178       goto loadTga;
    179     }
    180   }
    181 
    182 	// look for the editorimage if it is specified
    183 	if ( si->editorimage[0] ) {
    184 		sprintf( filename, "%s%s", gamedir, si->editorimage );
    185 		DefaultExtension( filename, ".tga" );
    186     buffer = LoadImageFile(filename, &bTGA);
    187     if ( buffer != NULL) {
    188       goto loadTga;
    189     }
    190   }
    191 
    192   // just try the shader name with a .tga
    193 	// on unix, we have case sensitivity problems...
    194   sprintf( filename, "%s%s.tga", gamedir, si->shader );
    195   buffer = LoadImageFile(filename, &bTGA);
    196   if ( buffer != NULL) {
    197 		goto loadTga;
    198 	}
    199 
    200   sprintf( filename, "%s%s.TGA", gamedir, si->shader );
    201   buffer = LoadImageFile(filename, &bTGA);
    202   if ( buffer != NULL) {
    203 		goto loadTga;
    204 	}
    205 
    206 	// couldn't load anything
    207 	_printf("WARNING: Couldn't find image for shader %s\n", si->shader );
    208 
    209 	si->color[0] = 1;
    210 	si->color[1] = 1;
    211 	si->color[2] = 1;
    212 	si->width = 64;
    213 	si->height = 64;
    214 	si->pixels = malloc( si->width * si->height * 4 );
    215 	memset ( si->pixels, 255, si->width * si->height * 4 );
    216 	return;
    217 
    218 	// load the image to get dimensions and color
    219 loadTga:
    220   if ( bTGA) {
    221 	  LoadTGABuffer( buffer, &si->pixels, &si->width, &si->height );
    222   }
    223   else {
    224 #ifdef _WIN32
    225     LoadJPGBuff(buffer, &si->pixels, &si->width, &si->height );
    226 #endif
    227   }
    228 
    229   free(buffer);
    230 
    231 	count = si->width * si->height;
    232 
    233 	VectorClear( color );
    234 	color[ 3 ] = 0;
    235 	for ( i = 0 ; i < count ; i++ ) {
    236 		color[0] += si->pixels[ i * 4 + 0 ];
    237 		color[1] += si->pixels[ i * 4 + 1 ];
    238 		color[2] += si->pixels[ i * 4 + 2 ];
    239 		color[3] += si->pixels[ i * 4 + 3 ];
    240 	}
    241 	ColorNormalize( color, si->color );
    242 	VectorScale( color, 1.0/count, si->averageColor );
    243 }
    244 
    245 /*
    246 ===============
    247 AllocShaderInfo
    248 ===============
    249 */
    250 static shaderInfo_t	*AllocShaderInfo( void ) {
    251 	shaderInfo_t	*si;
    252 
    253 	if ( numShaderInfo == MAX_SURFACE_INFO ) {
    254 		Error( "MAX_SURFACE_INFO" );
    255 	}
    256 	si = &shaderInfo[ numShaderInfo ];
    257 	numShaderInfo++;
    258 
    259 	// set defaults
    260 
    261 	si->contents = CONTENTS_SOLID;
    262 
    263 	si->backsplashFraction = DEFAULT_BACKSPLASH_FRACTION;
    264 	si->backsplashDistance = DEFAULT_BACKSPLASH_DISTANCE;
    265 
    266 	si->lightmapSampleSize = 0;
    267 	si->forceTraceLight = qfalse;
    268 	si->forceVLight = qfalse;
    269 	si->patchShadows = qfalse;
    270 	si->vertexShadows = qfalse;
    271 	si->noVertexShadows = qfalse;
    272 	si->forceSunLight = qfalse;
    273 	si->vertexScale = 1.0;
    274 	si->notjunc = qfalse;
    275 
    276 	return si;
    277 }
    278 
    279 /*
    280 ===============
    281 ShaderInfoForShader
    282 ===============
    283 */
    284 shaderInfo_t	*ShaderInfoForShader( const char *shaderName ) {
    285 	int				i;
    286 	shaderInfo_t	*si;
    287 	char			shader[MAX_QPATH];
    288 
    289 	// strip off extension
    290 	strcpy( shader, shaderName );
    291 	StripExtension( shader );
    292 
    293 	// search for it
    294 	for ( i = 0 ; i < numShaderInfo ; i++ ) {
    295 		si = &shaderInfo[ i ];
    296 		if ( !Q_stricmp( shader, si->shader ) ) {
    297 			if ( !si->width ) {
    298 				LoadShaderImage( si );
    299 			}
    300 			return si;
    301 		}
    302 	}
    303 
    304 	si = AllocShaderInfo();
    305 	strcpy( si->shader, shader );
    306 
    307 	LoadShaderImage( si );
    308 
    309 	return si;
    310 }
    311 
    312 /*
    313 ===============
    314 ParseShaderFile
    315 ===============
    316 */
    317 static void ParseShaderFile( const char *filename ) {
    318 	int		i;
    319 	int		numInfoParms = sizeof(infoParms) / sizeof(infoParms[0]);
    320 	shaderInfo_t	*si;
    321 
    322 //	qprintf( "shaderFile: %s\n", filename );
    323 	LoadScriptFile( filename );
    324 	while ( 1 ) {
    325 		if ( !GetToken( qtrue ) ) {
    326 			break;
    327 		}
    328 
    329 		si = AllocShaderInfo();
    330 		strcpy( si->shader, token );
    331 		MatchToken( "{" );
    332 		while ( 1 ) {
    333 			if ( !GetToken( qtrue ) ) {
    334 				break;
    335 			}
    336 			if ( !strcmp( token, "}" ) ) {
    337 				break;
    338 			}
    339 
    340 			// skip internal braced sections
    341 			if ( !strcmp( token, "{" ) ) {
    342 				si->hasPasses = qtrue;
    343 				while ( 1 ) {
    344 					if ( !GetToken( qtrue ) ) {
    345 						break;
    346 					}
    347 					if ( !strcmp( token, "}" ) ) {
    348 						break;
    349 					}
    350 				}
    351 				continue;
    352 			}
    353 
    354 			if ( !Q_stricmp( token, "surfaceparm" ) ) {
    355 				GetToken( qfalse );
    356 				for ( i = 0 ; i < numInfoParms ; i++ ) {
    357 					if ( !Q_stricmp( token, infoParms[i].name ) ) {
    358 						si->surfaceFlags |= infoParms[i].surfaceFlags;
    359 						si->contents |= infoParms[i].contents;
    360 						if ( infoParms[i].clearSolid ) {
    361 							si->contents &= ~CONTENTS_SOLID;
    362 						}
    363 						break;
    364 					}
    365 				}
    366 				if ( i == numInfoParms ) {
    367 					// we will silently ignore all tokens beginning with qer,
    368 					// which are QuakeEdRadient parameters
    369 					if ( Q_strncasecmp( token, "qer", 3 ) ) {
    370 						_printf( "Unknown surfaceparm: \"%s\"\n", token );
    371 					}
    372 				}
    373 				continue;
    374 			}
    375 
    376 
    377 			// qer_editorimage <image>
    378 			if ( !Q_stricmp( token, "qer_editorimage" ) ) {
    379 				GetToken( qfalse );
    380 				strcpy( si->editorimage, token );
    381 				DefaultExtension( si->editorimage, ".tga" );
    382 				continue;
    383 			}
    384 
    385 			// q3map_lightimage <image>
    386 			if ( !Q_stricmp( token, "q3map_lightimage" ) ) {
    387 				GetToken( qfalse );
    388 				strcpy( si->lightimage, token );
    389 				DefaultExtension( si->lightimage, ".tga" );
    390 				continue;
    391 			}
    392 
    393 			// q3map_surfacelight <value>
    394 			if ( !Q_stricmp( token, "q3map_surfacelight" )  ) {
    395 				GetToken( qfalse );
    396 				si->value = atoi( token );
    397 				continue;
    398 			}
    399 
    400 			// q3map_lightsubdivide <value>
    401 			if ( !Q_stricmp( token, "q3map_lightsubdivide" )  ) {
    402 				GetToken( qfalse );
    403 				si->lightSubdivide = atoi( token );
    404 				continue;
    405 			}
    406 
    407 			// q3map_lightmapsamplesize <value>
    408 			if ( !Q_stricmp( token, "q3map_lightmapsamplesize" ) ) {
    409 				GetToken( qfalse );
    410 				si->lightmapSampleSize = atoi( token );
    411 				continue;
    412 			}
    413 
    414 			// q3map_tracelight
    415 			if ( !Q_stricmp( token, "q3map_tracelight" ) ) {
    416 				si->forceTraceLight = qtrue;
    417 				continue;
    418 			}
    419 
    420 			// q3map_vlight
    421 			if ( !Q_stricmp( token, "q3map_vlight" ) ) {
    422 				si->forceVLight = qtrue;
    423 				continue;
    424 			}
    425 
    426 			// q3map_patchshadows
    427 			if ( !Q_stricmp( token, "q3map_patchshadows" ) ) {
    428 				si->patchShadows = qtrue;
    429 				continue;
    430 			}
    431 
    432 			// q3map_vertexshadows
    433 			if ( !Q_stricmp( token, "q3map_vertexshadows" ) ) {
    434 				si->vertexShadows = qtrue;
    435 				continue;
    436 			}
    437 
    438 			// q3map_novertexshadows
    439 			if ( !Q_stricmp( token, "q3map_novertexshadows" ) ) {
    440 				si->noVertexShadows = qtrue;
    441 				continue;
    442 			}
    443 
    444 			// q3map_forcesunlight
    445 			if ( !Q_stricmp( token, "q3map_forcesunlight" ) ) {
    446 				si->forceSunLight = qtrue;
    447 				continue;
    448 			}
    449 
    450 			// q3map_vertexscale
    451 			if ( !Q_stricmp( token, "q3map_vertexscale" ) ) {
    452 				GetToken( qfalse );
    453 				si->vertexScale = atof(token);
    454 				continue;
    455 			}
    456 
    457 			// q3map_notjunc
    458 			if ( !Q_stricmp( token, "q3map_notjunc" ) ) {
    459 				si->notjunc = qtrue;
    460 				continue;
    461 			}
    462 
    463 			// q3map_globaltexture
    464 			if ( !Q_stricmp( token, "q3map_globaltexture" )  ) {
    465 				si->globalTexture = qtrue;
    466 				continue;
    467 			}
    468 
    469 			// q3map_backsplash <percent> <distance>
    470 			if ( !Q_stricmp( token, "q3map_backsplash" ) ) {
    471 				GetToken( qfalse );
    472 				si->backsplashFraction = atof( token ) * 0.01;
    473 				GetToken( qfalse );
    474 				si->backsplashDistance = atof( token );
    475 				continue;
    476 			}
    477 
    478 			// q3map_backshader <shader>
    479 			if ( !Q_stricmp( token, "q3map_backshader" ) ) {
    480 				GetToken( qfalse );
    481 				strcpy( si->backShader, token );
    482 				continue;
    483 			}
    484 
    485 			// q3map_flare <shader>
    486 			if ( !Q_stricmp( token, "q3map_flare" ) ) {
    487 				GetToken( qfalse );
    488 				strcpy( si->flareShader, token );
    489 				continue;
    490 			}
    491 
    492 			// light <value> 
    493 			// old style flare specification
    494 			if ( !Q_stricmp( token, "light" ) ) {
    495 				GetToken( qfalse );
    496 				strcpy( si->flareShader, "flareshader" );
    497 				continue;
    498 			}
    499 
    500 			// q3map_sun <red> <green> <blue> <intensity> <degrees> <elivation>
    501 			// color will be normalized, so it doesn't matter what range you use
    502 			// intensity falls off with angle but not distance 100 is a fairly bright sun
    503 			// degree of 0 = from the east, 90 = north, etc.  altitude of 0 = sunrise/set, 90 = noon
    504 			if ( !Q_stricmp( token, "q3map_sun" ) ) {
    505 				float	a, b;
    506 
    507 				GetToken( qfalse );
    508 				si->sunLight[0] = atof( token );
    509 				GetToken( qfalse );
    510 				si->sunLight[1] = atof( token );
    511 				GetToken( qfalse );
    512 				si->sunLight[2] = atof( token );
    513 				
    514 				VectorNormalize( si->sunLight, si->sunLight);
    515 
    516 				GetToken( qfalse );
    517 				a = atof( token );
    518 				VectorScale( si->sunLight, a, si->sunLight);
    519 
    520 				GetToken( qfalse );
    521 				a = atof( token );
    522 				a = a / 180 * Q_PI;
    523 
    524 				GetToken( qfalse );
    525 				b = atof( token );
    526 				b = b / 180 * Q_PI;
    527 
    528 				si->sunDirection[0] = cos( a ) * cos( b );
    529 				si->sunDirection[1] = sin( a ) * cos( b );
    530 				si->sunDirection[2] = sin( b );
    531 
    532 				si->surfaceFlags |= SURF_SKY;
    533 				continue;
    534 			}
    535 
    536 			// tesssize is used to force liquid surfaces to subdivide
    537 			if ( !Q_stricmp( token, "tesssize" ) ) {
    538 				GetToken( qfalse );
    539 				si->subdivisions = atof( token );
    540 				continue;
    541 			}
    542 
    543 			// cull none will set twoSided
    544 			if ( !Q_stricmp( token, "cull" ) ) {
    545 				GetToken( qfalse );
    546 				if ( !Q_stricmp( token, "none" ) ) {
    547 					si->twoSided = qtrue;
    548 				}
    549 				continue;
    550 			}
    551 
    552 
    553 			// deformVertexes autosprite[2]
    554 			// we catch this so autosprited surfaces become point
    555 			// lights instead of area lights
    556 			if ( !Q_stricmp( token, "deformVertexes" ) ) {
    557 				GetToken( qfalse );
    558 				if ( !Q_strncasecmp( token, "autosprite", 10 ) ) {
    559 					si->autosprite = qtrue;
    560           si->contents = CONTENTS_DETAIL;
    561 				}
    562 				continue;
    563 			}
    564 
    565 
    566 			// ignore all other tokens on the line
    567 
    568 			while ( TokenAvailable() ) {
    569 				GetToken( qfalse );
    570 			}
    571 		}			
    572 	}
    573 }
    574 
    575 /*
    576 ===============
    577 LoadShaderInfo
    578 ===============
    579 */
    580 #define	MAX_SHADER_FILES	64
    581 void LoadShaderInfo( void ) {
    582 	char			filename[1024];
    583 	int				i;
    584 	char			*shaderFiles[MAX_SHADER_FILES];
    585 	int				numShaderFiles;
    586 
    587 	sprintf( filename, "%sscripts/shaderlist.txt", gamedir );
    588 	LoadScriptFile( filename );
    589 
    590 	numShaderFiles = 0;
    591 	while ( 1 ) {
    592 		if ( !GetToken( qtrue ) ) {
    593 			break;
    594 		}
    595     shaderFiles[numShaderFiles] = malloc(MAX_OS_PATH);
    596 		strcpy( shaderFiles[ numShaderFiles ], token );
    597 		numShaderFiles++;
    598 	}
    599 
    600 	for ( i = 0 ; i < numShaderFiles ; i++ ) {
    601 		sprintf( filename, "%sscripts/%s.shader", gamedir, shaderFiles[i] );
    602 		ParseShaderFile( filename );
    603     free(shaderFiles[i]);
    604 	}
    605 
    606 	qprintf( "%5i shaderInfo\n", numShaderInfo);
    607 }
    608