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