diff options
author | Tim Angus <tim@ngus.net> | 2013-02-15 23:46:37 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2013-02-16 21:55:58 +0000 |
commit | 1fba10104e76e937eeac60bc207a74012ab936dc (patch) | |
tree | 7bcb5507cd1aa13e6f07b74c008e9391abbf26ae /src/rend2/tr_shader.c | |
parent | c1ad10c57be23f89f658a13729e4349b400a8734 (diff) |
renderer -> renderergl1, rend2 -> renderergl2
Diffstat (limited to 'src/rend2/tr_shader.c')
-rw-r--r-- | src/rend2/tr_shader.c | 3741 |
1 files changed, 0 insertions, 3741 deletions
diff --git a/src/rend2/tr_shader.c b/src/rend2/tr_shader.c deleted file mode 100644 index 8cad4b6b..00000000 --- a/src/rend2/tr_shader.c +++ /dev/null @@ -1,3741 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "tr_local.h" - -// tr_shader.c -- this file deals with the parsing and definition of shaders - -static char *s_shaderText; - -// the shader is parsed into these global variables, then copied into -// dynamically allocated memory if it is valid. -static shaderStage_t stages[MAX_SHADER_STAGES]; -static shader_t shader; -static texModInfo_t texMods[MAX_SHADER_STAGES][TR_MAX_TEXMODS]; - -#define FILE_HASH_SIZE 1024 -static shader_t* hashTable[FILE_HASH_SIZE]; - -#define MAX_SHADERTEXT_HASH 2048 -static char **shaderTextHashTable[MAX_SHADERTEXT_HASH]; - -/* -================ -return a hash value for the filename -================ -*/ -#ifdef __GNUCC__ - #warning TODO: check if long is ok here -#endif -static long generateHashValue( const char *fname, const int size ) { - int i; - long hash; - char letter; - - hash = 0; - i = 0; - while (fname[i] != '\0') { - letter = tolower(fname[i]); - if (letter =='.') break; // don't include extension - if (letter =='\\') letter = '/'; // damn path names - if (letter == PATH_SEP) letter = '/'; // damn path names - hash+=(long)(letter)*(i+119); - i++; - } - hash = (hash ^ (hash >> 10) ^ (hash >> 20)); - hash &= (size-1); - return hash; -} - -void R_RemapShader(const char *shaderName, const char *newShaderName, const char *timeOffset) { - char strippedName[MAX_QPATH]; - int hash; - shader_t *sh, *sh2; - qhandle_t h; - - sh = R_FindShaderByName( shaderName ); - if (sh == NULL || sh == tr.defaultShader) { - h = RE_RegisterShaderLightMap(shaderName, 0); - sh = R_GetShaderByHandle(h); - } - if (sh == NULL || sh == tr.defaultShader) { - ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: shader %s not found\n", shaderName ); - return; - } - - sh2 = R_FindShaderByName( newShaderName ); - if (sh2 == NULL || sh2 == tr.defaultShader) { - h = RE_RegisterShaderLightMap(newShaderName, 0); - sh2 = R_GetShaderByHandle(h); - } - - if (sh2 == NULL || sh2 == tr.defaultShader) { - ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: new shader %s not found\n", newShaderName ); - return; - } - - // remap all the shaders with the given name - // even tho they might have different lightmaps - COM_StripExtension(shaderName, strippedName, sizeof(strippedName)); - hash = generateHashValue(strippedName, FILE_HASH_SIZE); - for (sh = hashTable[hash]; sh; sh = sh->next) { - if (Q_stricmp(sh->name, strippedName) == 0) { - if (sh != sh2) { - sh->remappedShader = sh2; - } else { - sh->remappedShader = NULL; - } - } - } - if (timeOffset) { - sh2->timeOffset = atof(timeOffset); - } -} - -/* -=============== -ParseVector -=============== -*/ -static qboolean ParseVector( char **text, int count, float *v ) { - char *token; - int i; - - // FIXME: spaces are currently required after parens, should change parseext... - token = COM_ParseExt( text, qfalse ); - if ( strcmp( token, "(" ) ) { - ri.Printf( PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name ); - return qfalse; - } - - for ( i = 0 ; i < count ; i++ ) { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) { - ri.Printf( PRINT_WARNING, "WARNING: missing vector element in shader '%s'\n", shader.name ); - return qfalse; - } - v[i] = atof( token ); - } - - token = COM_ParseExt( text, qfalse ); - if ( strcmp( token, ")" ) ) { - ri.Printf( PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name ); - return qfalse; - } - - return qtrue; -} - - -/* -=============== -NameToAFunc -=============== -*/ -static unsigned NameToAFunc( const char *funcname ) -{ - if ( !Q_stricmp( funcname, "GT0" ) ) - { - return GLS_ATEST_GT_0; - } - else if ( !Q_stricmp( funcname, "LT128" ) ) - { - return GLS_ATEST_LT_80; - } - else if ( !Q_stricmp( funcname, "GE128" ) ) - { - return GLS_ATEST_GE_80; - } - - ri.Printf( PRINT_WARNING, "WARNING: invalid alphaFunc name '%s' in shader '%s'\n", funcname, shader.name ); - return 0; -} - - -/* -=============== -NameToSrcBlendMode -=============== -*/ -static int NameToSrcBlendMode( const char *name ) -{ - if ( !Q_stricmp( name, "GL_ONE" ) ) - { - return GLS_SRCBLEND_ONE; - } - else if ( !Q_stricmp( name, "GL_ZERO" ) ) - { - return GLS_SRCBLEND_ZERO; - } - else if ( !Q_stricmp( name, "GL_DST_COLOR" ) ) - { - return GLS_SRCBLEND_DST_COLOR; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_COLOR" ) ) - { - return GLS_SRCBLEND_ONE_MINUS_DST_COLOR; - } - else if ( !Q_stricmp( name, "GL_SRC_ALPHA" ) ) - { - return GLS_SRCBLEND_SRC_ALPHA; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_ALPHA" ) ) - { - return GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA; - } - else if ( !Q_stricmp( name, "GL_DST_ALPHA" ) ) - { - return GLS_SRCBLEND_DST_ALPHA; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_ALPHA" ) ) - { - return GLS_SRCBLEND_ONE_MINUS_DST_ALPHA; - } - else if ( !Q_stricmp( name, "GL_SRC_ALPHA_SATURATE" ) ) - { - return GLS_SRCBLEND_ALPHA_SATURATE; - } - - ri.Printf( PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name, shader.name ); - return GLS_SRCBLEND_ONE; -} - -/* -=============== -NameToDstBlendMode -=============== -*/ -static int NameToDstBlendMode( const char *name ) -{ - if ( !Q_stricmp( name, "GL_ONE" ) ) - { - return GLS_DSTBLEND_ONE; - } - else if ( !Q_stricmp( name, "GL_ZERO" ) ) - { - return GLS_DSTBLEND_ZERO; - } - else if ( !Q_stricmp( name, "GL_SRC_ALPHA" ) ) - { - return GLS_DSTBLEND_SRC_ALPHA; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_ALPHA" ) ) - { - return GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - } - else if ( !Q_stricmp( name, "GL_DST_ALPHA" ) ) - { - return GLS_DSTBLEND_DST_ALPHA; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_ALPHA" ) ) - { - return GLS_DSTBLEND_ONE_MINUS_DST_ALPHA; - } - else if ( !Q_stricmp( name, "GL_SRC_COLOR" ) ) - { - return GLS_DSTBLEND_SRC_COLOR; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_COLOR" ) ) - { - return GLS_DSTBLEND_ONE_MINUS_SRC_COLOR; - } - - ri.Printf( PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name, shader.name ); - return GLS_DSTBLEND_ONE; -} - -/* -=============== -NameToGenFunc -=============== -*/ -static genFunc_t NameToGenFunc( const char *funcname ) -{ - if ( !Q_stricmp( funcname, "sin" ) ) - { - return GF_SIN; - } - else if ( !Q_stricmp( funcname, "square" ) ) - { - return GF_SQUARE; - } - else if ( !Q_stricmp( funcname, "triangle" ) ) - { - return GF_TRIANGLE; - } - else if ( !Q_stricmp( funcname, "sawtooth" ) ) - { - return GF_SAWTOOTH; - } - else if ( !Q_stricmp( funcname, "inversesawtooth" ) ) - { - return GF_INVERSE_SAWTOOTH; - } - else if ( !Q_stricmp( funcname, "noise" ) ) - { - return GF_NOISE; - } - - ri.Printf( PRINT_WARNING, "WARNING: invalid genfunc name '%s' in shader '%s'\n", funcname, shader.name ); - return GF_SIN; -} - - -/* -=================== -ParseWaveForm -=================== -*/ -static void ParseWaveForm( char **text, waveForm_t *wave ) -{ - char *token; - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->func = NameToGenFunc( token ); - - // BASE, AMP, PHASE, FREQ - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->base = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->amplitude = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->phase = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->frequency = atof( token ); -} - - -/* -=================== -ParseTexMod -=================== -*/ -static void ParseTexMod( char *_text, shaderStage_t *stage ) -{ - const char *token; - char **text = &_text; - texModInfo_t *tmi; - - if ( stage->bundle[0].numTexMods == TR_MAX_TEXMODS ) { - ri.Error( ERR_DROP, "ERROR: too many tcMod stages in shader '%s'", shader.name ); - return; - } - - tmi = &stage->bundle[0].texMods[stage->bundle[0].numTexMods]; - stage->bundle[0].numTexMods++; - - token = COM_ParseExt( text, qfalse ); - - // - // turb - // - if ( !Q_stricmp( token, "turb" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.base = atof( token ); - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); - return; - } - tmi->wave.amplitude = atof( token ); - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); - return; - } - tmi->wave.phase = atof( token ); - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); - return; - } - tmi->wave.frequency = atof( token ); - - tmi->type = TMOD_TURBULENT; - } - // - // scale - // - else if ( !Q_stricmp( token, "scale" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name ); - return; - } - tmi->scale[0] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name ); - return; - } - tmi->scale[1] = atof( token ); - tmi->type = TMOD_SCALE; - } - // - // scroll - // - else if ( !Q_stricmp( token, "scroll" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name ); - return; - } - tmi->scroll[0] = atof( token ); - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name ); - return; - } - tmi->scroll[1] = atof( token ); - tmi->type = TMOD_SCROLL; - } - // - // stretch - // - else if ( !Q_stricmp( token, "stretch" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.func = NameToGenFunc( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.base = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.amplitude = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.phase = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.frequency = atof( token ); - - tmi->type = TMOD_STRETCH; - } - // - // transform - // - else if ( !Q_stricmp( token, "transform" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->matrix[0][0] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->matrix[0][1] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->matrix[1][0] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->matrix[1][1] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->translate[0] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->translate[1] = atof( token ); - - tmi->type = TMOD_TRANSFORM; - } - // - // rotate - // - else if ( !Q_stricmp( token, "rotate" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod rotate parms in shader '%s'\n", shader.name ); - return; - } - tmi->rotateSpeed = atof( token ); - tmi->type = TMOD_ROTATE; - } - // - // entityTranslate - // - else if ( !Q_stricmp( token, "entityTranslate" ) ) - { - tmi->type = TMOD_ENTITY_TRANSLATE; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown tcMod '%s' in shader '%s'\n", token, shader.name ); - } -} - - -/* -=================== -ParseStage -=================== -*/ -static qboolean ParseStage( shaderStage_t *stage, char **text ) -{ - char *token; - int depthMaskBits = GLS_DEPTHMASK_TRUE, blendSrcBits = 0, blendDstBits = 0, atestBits = 0, depthFuncBits = 0; - qboolean depthMaskExplicit = qfalse; - - stage->active = qtrue; - - while ( 1 ) - { - token = COM_ParseExt( text, qtrue ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: no matching '}' found\n" ); - return qfalse; - } - - if ( token[0] == '}' ) - { - break; - } - // - // map <name> - // - else if ( !Q_stricmp( token, "map" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'map' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - - if ( !Q_stricmp( token, "$whiteimage" ) ) - { - stage->bundle[0].image[0] = tr.whiteImage; - continue; - } - else if ( !Q_stricmp( token, "$lightmap" ) ) - { - stage->bundle[0].isLightmap = qtrue; - if ( shader.lightmapIndex < 0 ) { - stage->bundle[0].image[0] = tr.whiteImage; - } else { - stage->bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; - } - continue; - } - else if ( !Q_stricmp( token, "$deluxemap" ) ) - { - if (!tr.worldDeluxeMapping) - { - ri.Printf( PRINT_WARNING, "WARNING: shader '%s' wants a deluxe map in a map compiled without them\n", shader.name ); - return qfalse; - } - - stage->bundle[0].isLightmap = qtrue; - if ( shader.lightmapIndex < 0 ) { - stage->bundle[0].image[0] = tr.whiteImage; - } else { - stage->bundle[0].image[0] = tr.deluxemaps[shader.lightmapIndex]; - } - continue; - } - else - { - imgType_t type = IMGTYPE_COLORALPHA; - imgFlags_t flags = IMGFLAG_NONE; - - if (!shader.noMipMaps) - flags |= IMGFLAG_MIPMAP; - - if (!shader.noPicMip) - flags |= IMGFLAG_PICMIP; - - if (stage->type == ST_NORMALMAP || stage->type == ST_NORMALPARALLAXMAP) - { - type = IMGTYPE_NORMAL; - flags |= IMGFLAG_NOLIGHTSCALE; - - if (stage->type == ST_NORMALPARALLAXMAP) - type = IMGTYPE_NORMALHEIGHT; - } - else - { - if (r_genNormalMaps->integer) - flags |= IMGFLAG_GENNORMALMAP; - - if (r_srgb->integer) - flags |= IMGFLAG_SRGB; - } - - stage->bundle[0].image[0] = R_FindImageFile( token, type, flags ); - - if ( !stage->bundle[0].image[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); - return qfalse; - } - } - } - // - // clampmap <name> - // - else if ( !Q_stricmp( token, "clampmap" ) ) - { - imgType_t type = IMGTYPE_COLORALPHA; - imgFlags_t flags = IMGFLAG_CLAMPTOEDGE | IMGFLAG_SRGB; - - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'clampmap' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - - if (!shader.noMipMaps) - flags |= IMGFLAG_MIPMAP; - - if (!shader.noPicMip) - flags |= IMGFLAG_PICMIP; - - if (stage->type == ST_NORMALMAP || stage->type == ST_NORMALPARALLAXMAP) - { - type = IMGTYPE_NORMAL; - flags |= IMGFLAG_NOLIGHTSCALE; - - if (stage->type == ST_NORMALPARALLAXMAP) - type = IMGTYPE_NORMALHEIGHT; - } - else - { - if (r_genNormalMaps->integer) - flags |= IMGFLAG_GENNORMALMAP; - - if (r_srgb->integer) - flags |= IMGFLAG_SRGB; - } - - - stage->bundle[0].image[0] = R_FindImageFile( token, type, flags ); - if ( !stage->bundle[0].image[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); - return qfalse; - } - } - // - // animMap <frequency> <image1> .... <imageN> - // - else if ( !Q_stricmp( token, "animMap" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'animMmap' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - stage->bundle[0].imageAnimationSpeed = atof( token ); - - // parse up to MAX_IMAGE_ANIMATIONS animations - while ( 1 ) { - int num; - - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) { - break; - } - num = stage->bundle[0].numImageAnimations; - if ( num < MAX_IMAGE_ANIMATIONS ) { - imgFlags_t flags = IMGFLAG_SRGB; - - if (!shader.noMipMaps) - flags |= IMGFLAG_MIPMAP; - - if (!shader.noPicMip) - flags |= IMGFLAG_PICMIP; - - stage->bundle[0].image[num] = R_FindImageFile( token, IMGTYPE_COLORALPHA, flags ); - if ( !stage->bundle[0].image[num] ) - { - ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); - return qfalse; - } - stage->bundle[0].numImageAnimations++; - } - } - } - else if ( !Q_stricmp( token, "videoMap" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'videoMmap' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - stage->bundle[0].videoMapHandle = ri.CIN_PlayCinematic( token, 0, 0, 256, 256, (CIN_loop | CIN_silent | CIN_shader)); - if (stage->bundle[0].videoMapHandle != -1) { - stage->bundle[0].isVideoMap = qtrue; - stage->bundle[0].image[0] = tr.scratchImage[stage->bundle[0].videoMapHandle]; - } - } - // - // alphafunc <func> - // - else if ( !Q_stricmp( token, "alphaFunc" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'alphaFunc' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - - atestBits = NameToAFunc( token ); - } - // - // depthFunc <func> - // - else if ( !Q_stricmp( token, "depthfunc" ) ) - { - token = COM_ParseExt( text, qfalse ); - - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'depthfunc' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - - if ( !Q_stricmp( token, "lequal" ) ) - { - depthFuncBits = 0; - } - else if ( !Q_stricmp( token, "equal" ) ) - { - depthFuncBits = GLS_DEPTHFUNC_EQUAL; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown depthfunc '%s' in shader '%s'\n", token, shader.name ); - continue; - } - } - // - // detail - // - else if ( !Q_stricmp( token, "detail" ) ) - { - stage->isDetail = qtrue; - } - // - // blendfunc <srcFactor> <dstFactor> - // or blendfunc <add|filter|blend> - // - else if ( !Q_stricmp( token, "blendfunc" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n", shader.name ); - continue; - } - // check for "simple" blends first - if ( !Q_stricmp( token, "add" ) ) { - blendSrcBits = GLS_SRCBLEND_ONE; - blendDstBits = GLS_DSTBLEND_ONE; - } else if ( !Q_stricmp( token, "filter" ) ) { - blendSrcBits = GLS_SRCBLEND_DST_COLOR; - blendDstBits = GLS_DSTBLEND_ZERO; - } else if ( !Q_stricmp( token, "blend" ) ) { - blendSrcBits = GLS_SRCBLEND_SRC_ALPHA; - blendDstBits = GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - } else { - // complex double blends - blendSrcBits = NameToSrcBlendMode( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n", shader.name ); - continue; - } - blendDstBits = NameToDstBlendMode( token ); - } - - // clear depth mask for blended surfaces - if ( !depthMaskExplicit ) - { - depthMaskBits = 0; - } - } - // - // stage <type> - // - else if(!Q_stricmp(token, "stage")) - { - token = COM_ParseExt(text, qfalse); - if(token[0] == 0) - { - ri.Printf(PRINT_WARNING, "WARNING: missing parameters for stage in shader '%s'\n", shader.name); - continue; - } - - if(!Q_stricmp(token, "diffuseMap")) - { - stage->type = ST_DIFFUSEMAP; - } - else if(!Q_stricmp(token, "normalMap") || !Q_stricmp(token, "bumpMap")) - { - stage->type = ST_NORMALMAP; - } - else if(!Q_stricmp(token, "normalParallaxMap") || !Q_stricmp(token, "bumpParallaxMap")) - { - if (r_parallaxMapping->integer) - stage->type = ST_NORMALPARALLAXMAP; - else - stage->type = ST_NORMALMAP; - } - else if(!Q_stricmp(token, "specularMap")) - { - stage->type = ST_SPECULARMAP; - stage->materialInfo[0] = 0.04f; - stage->materialInfo[1] = 256.0f; - } - else - { - ri.Printf(PRINT_WARNING, "WARNING: unknown stage parameter '%s' in shader '%s'\n", token, shader.name); - continue; - } - } - // - // specularReflectance <value> - // - else if (!Q_stricmp(token, "specularreflectance")) - { - token = COM_ParseExt(text, qfalse); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for specular reflectance in shader '%s'\n", shader.name ); - continue; - } - stage->materialInfo[0] = atof( token ); - } - // - // specularExponent <value> - // - else if (!Q_stricmp(token, "specularexponent")) - { - token = COM_ParseExt(text, qfalse); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for specular exponent in shader '%s'\n", shader.name ); - continue; - } - stage->materialInfo[1] = atof( token ); - } - // - // rgbGen - // - else if ( !Q_stricmp( token, "rgbGen" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameters for rgbGen in shader '%s'\n", shader.name ); - continue; - } - - if ( !Q_stricmp( token, "wave" ) ) - { - ParseWaveForm( text, &stage->rgbWave ); - stage->rgbGen = CGEN_WAVEFORM; - } - else if ( !Q_stricmp( token, "const" ) ) - { - vec3_t color; - - ParseVector( text, 3, color ); - stage->constantColor[0] = 255 * color[0]; - stage->constantColor[1] = 255 * color[1]; - stage->constantColor[2] = 255 * color[2]; - - stage->rgbGen = CGEN_CONST; - } - else if ( !Q_stricmp( token, "identity" ) ) - { - stage->rgbGen = CGEN_IDENTITY; - } - else if ( !Q_stricmp( token, "identityLighting" ) ) - { - stage->rgbGen = CGEN_IDENTITY_LIGHTING; - } - else if ( !Q_stricmp( token, "entity" ) ) - { - stage->rgbGen = CGEN_ENTITY; - } - else if ( !Q_stricmp( token, "oneMinusEntity" ) ) - { - stage->rgbGen = CGEN_ONE_MINUS_ENTITY; - } - else if ( !Q_stricmp( token, "vertex" ) ) - { - stage->rgbGen = CGEN_VERTEX; - if ( stage->alphaGen == 0 ) { - stage->alphaGen = AGEN_VERTEX; - } - } - else if ( !Q_stricmp( token, "exactVertex" ) ) - { - stage->rgbGen = CGEN_EXACT_VERTEX; - } - else if ( !Q_stricmp( token, "vertexLit" ) ) - { - stage->rgbGen = CGEN_VERTEX_LIT; - if ( stage->alphaGen == 0 ) { - stage->alphaGen = AGEN_VERTEX; - } - } - else if ( !Q_stricmp( token, "exactVertexLit" ) ) - { - stage->rgbGen = CGEN_EXACT_VERTEX_LIT; - } - else if ( !Q_stricmp( token, "lightingDiffuse" ) ) - { - stage->rgbGen = CGEN_LIGHTING_DIFFUSE; - } - else if ( !Q_stricmp( token, "oneMinusVertex" ) ) - { - stage->rgbGen = CGEN_ONE_MINUS_VERTEX; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown rgbGen parameter '%s' in shader '%s'\n", token, shader.name ); - continue; - } - } - // - // alphaGen - // - else if ( !Q_stricmp( token, "alphaGen" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameters for alphaGen in shader '%s'\n", shader.name ); - continue; - } - - if ( !Q_stricmp( token, "wave" ) ) - { - ParseWaveForm( text, &stage->alphaWave ); - stage->alphaGen = AGEN_WAVEFORM; - } - else if ( !Q_stricmp( token, "const" ) ) - { - token = COM_ParseExt( text, qfalse ); - stage->constantColor[3] = 255 * atof( token ); - stage->alphaGen = AGEN_CONST; - } - else if ( !Q_stricmp( token, "identity" ) ) - { - stage->alphaGen = AGEN_IDENTITY; - } - else if ( !Q_stricmp( token, "entity" ) ) - { - stage->alphaGen = AGEN_ENTITY; - } - else if ( !Q_stricmp( token, "oneMinusEntity" ) ) - { - stage->alphaGen = AGEN_ONE_MINUS_ENTITY; - } - else if ( !Q_stricmp( token, "vertex" ) ) - { - stage->alphaGen = AGEN_VERTEX; - } - else if ( !Q_stricmp( token, "lightingSpecular" ) ) - { - stage->alphaGen = AGEN_LIGHTING_SPECULAR; - } - else if ( !Q_stricmp( token, "oneMinusVertex" ) ) - { - stage->alphaGen = AGEN_ONE_MINUS_VERTEX; - } - else if ( !Q_stricmp( token, "portal" ) ) - { - stage->alphaGen = AGEN_PORTAL; - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - shader.portalRange = 256; - ri.Printf( PRINT_WARNING, "WARNING: missing range parameter for alphaGen portal in shader '%s', defaulting to 256\n", shader.name ); - } - else - { - shader.portalRange = atof( token ); - } - } - else if ( !Q_stricmp( token, "fresnel" ) ) - { - stage->alphaGen = AGEN_FRESNEL; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown alphaGen parameter '%s' in shader '%s'\n", token, shader.name ); - continue; - } - } - // - // tcGen <function> - // - else if ( !Q_stricmp(token, "texgen") || !Q_stricmp( token, "tcGen" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing texgen parm in shader '%s'\n", shader.name ); - continue; - } - - if ( !Q_stricmp( token, "environment" ) ) - { - stage->bundle[0].tcGen = TCGEN_ENVIRONMENT_MAPPED; - } - else if ( !Q_stricmp( token, "lightmap" ) ) - { - stage->bundle[0].tcGen = TCGEN_LIGHTMAP; - } - else if ( !Q_stricmp( token, "texture" ) || !Q_stricmp( token, "base" ) ) - { - stage->bundle[0].tcGen = TCGEN_TEXTURE; - } - else if ( !Q_stricmp( token, "vector" ) ) - { - ParseVector( text, 3, stage->bundle[0].tcGenVectors[0] ); - ParseVector( text, 3, stage->bundle[0].tcGenVectors[1] ); - - stage->bundle[0].tcGen = TCGEN_VECTOR; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown texgen parm in shader '%s'\n", shader.name ); - } - } - // - // tcMod <type> <...> - // - else if ( !Q_stricmp( token, "tcMod" ) ) - { - char buffer[1024] = ""; - - while ( 1 ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - break; - strcat( buffer, token ); - strcat( buffer, " " ); - } - - ParseTexMod( buffer, stage ); - - continue; - } - // - // depthmask - // - else if ( !Q_stricmp( token, "depthwrite" ) ) - { - depthMaskBits = GLS_DEPTHMASK_TRUE; - depthMaskExplicit = qtrue; - - continue; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown parameter '%s' in shader '%s'\n", token, shader.name ); - return qfalse; - } - } - - // - // if cgen isn't explicitly specified, use either identity or identitylighting - // - if ( stage->rgbGen == CGEN_BAD ) { - if ( blendSrcBits == 0 || - blendSrcBits == GLS_SRCBLEND_ONE || - blendSrcBits == GLS_SRCBLEND_SRC_ALPHA ) { - stage->rgbGen = CGEN_IDENTITY_LIGHTING; - } else { - stage->rgbGen = CGEN_IDENTITY; - } - } - - - // - // implicitly assume that a GL_ONE GL_ZERO blend mask disables blending - // - if ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && - ( blendDstBits == GLS_DSTBLEND_ZERO ) ) - { - blendDstBits = blendSrcBits = 0; - depthMaskBits = GLS_DEPTHMASK_TRUE; - } - - // decide which agens we can skip - if ( stage->alphaGen == AGEN_IDENTITY ) { - if ( stage->rgbGen == CGEN_IDENTITY - || stage->rgbGen == CGEN_LIGHTING_DIFFUSE ) { - stage->alphaGen = AGEN_SKIP; - } - } - - // - // compute state bits - // - stage->stateBits = depthMaskBits | - blendSrcBits | blendDstBits | - atestBits | - depthFuncBits; - - return qtrue; -} - -/* -=============== -ParseDeform - -deformVertexes wave <spread> <waveform> <base> <amplitude> <phase> <frequency> -deformVertexes normal <frequency> <amplitude> -deformVertexes move <vector> <waveform> <base> <amplitude> <phase> <frequency> -deformVertexes bulge <bulgeWidth> <bulgeHeight> <bulgeSpeed> -deformVertexes projectionShadow -deformVertexes autoSprite -deformVertexes autoSprite2 -deformVertexes text[0-7] -=============== -*/ -static void ParseDeform( char **text ) { - char *token; - deformStage_t *ds; - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deform parm in shader '%s'\n", shader.name ); - return; - } - - if ( shader.numDeforms == MAX_SHADER_DEFORMS ) { - ri.Printf( PRINT_WARNING, "WARNING: MAX_SHADER_DEFORMS in '%s'\n", shader.name ); - return; - } - - ds = &shader.deforms[ shader.numDeforms ]; - shader.numDeforms++; - - if ( !Q_stricmp( token, "projectionShadow" ) ) { - ds->deformation = DEFORM_PROJECTION_SHADOW; - return; - } - - if ( !Q_stricmp( token, "autosprite" ) ) { - ds->deformation = DEFORM_AUTOSPRITE; - return; - } - - if ( !Q_stricmp( token, "autosprite2" ) ) { - ds->deformation = DEFORM_AUTOSPRITE2; - return; - } - - if ( !Q_stricmpn( token, "text", 4 ) ) { - int n; - - n = token[4] - '0'; - if ( n < 0 || n > 7 ) { - n = 0; - } - ds->deformation = DEFORM_TEXT0 + n; - return; - } - - if ( !Q_stricmp( token, "bulge" ) ) { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); - return; - } - ds->bulgeWidth = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); - return; - } - ds->bulgeHeight = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); - return; - } - ds->bulgeSpeed = atof( token ); - - ds->deformation = DEFORM_BULGE; - return; - } - - if ( !Q_stricmp( token, "wave" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); - return; - } - - if ( atof( token ) != 0 ) - { - ds->deformationSpread = 1.0f / atof( token ); - } - else - { - ds->deformationSpread = 100.0f; - ri.Printf( PRINT_WARNING, "WARNING: illegal div value of 0 in deformVertexes command for shader '%s'\n", shader.name ); - } - - ParseWaveForm( text, &ds->deformationWave ); - ds->deformation = DEFORM_WAVE; - return; - } - - if ( !Q_stricmp( token, "normal" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); - return; - } - ds->deformationWave.amplitude = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); - return; - } - ds->deformationWave.frequency = atof( token ); - - ds->deformation = DEFORM_NORMALS; - return; - } - - if ( !Q_stricmp( token, "move" ) ) { - int i; - - for ( i = 0 ; i < 3 ; i++ ) { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); - return; - } - ds->moveVector[i] = atof( token ); - } - - ParseWaveForm( text, &ds->deformationWave ); - ds->deformation = DEFORM_MOVE; - return; - } - - ri.Printf( PRINT_WARNING, "WARNING: unknown deformVertexes subtype '%s' found in shader '%s'\n", token, shader.name ); -} - - -/* -=============== -ParseSkyParms - -skyParms <outerbox> <cloudheight> <innerbox> -=============== -*/ -static void ParseSkyParms( char **text ) { - char *token; - static char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; - char pathname[MAX_QPATH]; - int i; - - // outerbox - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); - return; - } - if ( strcmp( token, "-" ) ) { - for (i=0 ; i<6 ; i++) { - Com_sprintf( pathname, sizeof(pathname), "%s_%s.tga" - , token, suf[i] ); - shader.sky.outerbox[i] = R_FindImageFile( ( char * ) pathname, IMGTYPE_COLORALPHA, IMGFLAG_SRGB | IMGFLAG_MIPMAP | IMGFLAG_PICMIP | IMGFLAG_CLAMPTOEDGE ); - - if ( !shader.sky.outerbox[i] ) { - shader.sky.outerbox[i] = tr.defaultImage; - } - } - } - - // cloudheight - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); - return; - } - shader.sky.cloudHeight = atof( token ); - if ( !shader.sky.cloudHeight ) { - shader.sky.cloudHeight = 512; - } - R_InitSkyTexCoords( shader.sky.cloudHeight ); - - - // innerbox - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); - return; - } - if ( strcmp( token, "-" ) ) { - for (i=0 ; i<6 ; i++) { - Com_sprintf( pathname, sizeof(pathname), "%s_%s.tga" - , token, suf[i] ); - shader.sky.innerbox[i] = R_FindImageFile( ( char * ) pathname, IMGTYPE_COLORALPHA, IMGFLAG_SRGB | IMGFLAG_MIPMAP | IMGFLAG_PICMIP ); - if ( !shader.sky.innerbox[i] ) { - shader.sky.innerbox[i] = tr.defaultImage; - } - } - } - - shader.isSky = qtrue; -} - - -/* -================= -ParseSort -================= -*/ -void ParseSort( char **text ) { - char *token; - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: missing sort parameter in shader '%s'\n", shader.name ); - return; - } - - if ( !Q_stricmp( token, "portal" ) ) { - shader.sort = SS_PORTAL; - } else if ( !Q_stricmp( token, "sky" ) ) { - shader.sort = SS_ENVIRONMENT; - } else if ( !Q_stricmp( token, "opaque" ) ) { - shader.sort = SS_OPAQUE; - }else if ( !Q_stricmp( token, "decal" ) ) { - shader.sort = SS_DECAL; - } else if ( !Q_stricmp( token, "seeThrough" ) ) { - shader.sort = SS_SEE_THROUGH; - } else if ( !Q_stricmp( token, "banner" ) ) { - shader.sort = SS_BANNER; - } else if ( !Q_stricmp( token, "additive" ) ) { - shader.sort = SS_BLEND1; - } else if ( !Q_stricmp( token, "nearest" ) ) { - shader.sort = SS_NEAREST; - } else if ( !Q_stricmp( token, "underwater" ) ) { - shader.sort = SS_UNDERWATER; - } else { - shader.sort = atof( token ); - } -} - - - -// this table is also present in q3map - -typedef struct { - char *name; - int clearSolid, surfaceFlags, contents; -} infoParm_t; - -infoParm_t infoParms[] = { - // server relevant contents - {"water", 1, 0, CONTENTS_WATER }, - {"slime", 1, 0, CONTENTS_SLIME }, // mildly damaging - {"lava", 1, 0, CONTENTS_LAVA }, // very damaging - {"playerclip", 1, 0, CONTENTS_PLAYERCLIP }, - {"monsterclip", 1, 0, CONTENTS_MONSTERCLIP }, - {"nodrop", 1, 0, CONTENTS_NODROP }, // don't drop items or leave bodies (death fog, lava, etc) - {"nonsolid", 1, SURF_NONSOLID, 0}, // clears the solid flag - - // utility relevant attributes - {"origin", 1, 0, CONTENTS_ORIGIN }, // center of rotating brushes - {"trans", 0, 0, CONTENTS_TRANSLUCENT }, // don't eat contained surfaces - {"detail", 0, 0, CONTENTS_DETAIL }, // don't include in structural bsp - {"structural", 0, 0, CONTENTS_STRUCTURAL }, // force into structural bsp even if trnas - {"areaportal", 1, 0, CONTENTS_AREAPORTAL }, // divides areas - {"clusterportal", 1,0, CONTENTS_CLUSTERPORTAL }, // for bots - {"donotenter", 1, 0, CONTENTS_DONOTENTER }, // for bots - - {"fog", 1, 0, CONTENTS_FOG}, // carves surfaces entering - {"sky", 0, SURF_SKY, 0 }, // emit light from an environment map - {"lightfilter", 0, SURF_LIGHTFILTER, 0 }, // filter light going through it - {"alphashadow", 0, SURF_ALPHASHADOW, 0 }, // test light on a per-pixel basis - {"hint", 0, SURF_HINT, 0 }, // use as a primary splitter - - // server attributes - {"slick", 0, SURF_SLICK, 0 }, - {"noimpact", 0, SURF_NOIMPACT, 0 }, // don't make impact explosions or marks - {"nomarks", 0, SURF_NOMARKS, 0 }, // don't make impact marks, but still explode - {"ladder", 0, SURF_LADDER, 0 }, - {"nodamage", 0, SURF_NODAMAGE, 0 }, - {"metalsteps", 0, SURF_METALSTEPS,0 }, - {"flesh", 0, SURF_FLESH, 0 }, - {"nosteps", 0, SURF_NOSTEPS, 0 }, - - // drawsurf attributes - {"nodraw", 0, SURF_NODRAW, 0 }, // don't generate a drawsurface (or a lightmap) - {"pointlight", 0, SURF_POINTLIGHT, 0 }, // sample lighting at vertexes - {"nolightmap", 0, SURF_NOLIGHTMAP,0 }, // don't generate a lightmap - {"nodlight", 0, SURF_NODLIGHT, 0 }, // don't ever add dynamic lights - {"dust", 0, SURF_DUST, 0} // leave a dust trail when walking on this surface -}; - - -/* -=============== -ParseSurfaceParm - -surfaceparm <name> -=============== -*/ -static void ParseSurfaceParm( char **text ) { - char *token; - int numInfoParms = ARRAY_LEN( infoParms ); - int i; - - token = COM_ParseExt( text, qfalse ); - for ( i = 0 ; i < numInfoParms ; i++ ) { - if ( !Q_stricmp( token, infoParms[i].name ) ) { - shader.surfaceFlags |= infoParms[i].surfaceFlags; - shader.contentFlags |= infoParms[i].contents; -#if 0 - if ( infoParms[i].clearSolid ) { - si->contents &= ~CONTENTS_SOLID; - } -#endif - break; - } - } -} - -/* -================= -ParseShader - -The current text pointer is at the explicit text definition of the -shader. Parse it into the global shader variable. Later functions -will optimize it. -================= -*/ -static qboolean ParseShader( char **text ) -{ - char *token; - int s; - - s = 0; - - token = COM_ParseExt( text, qtrue ); - if ( token[0] != '{' ) - { - ri.Printf( PRINT_WARNING, "WARNING: expecting '{', found '%s' instead in shader '%s'\n", token, shader.name ); - return qfalse; - } - - while ( 1 ) - { - token = COM_ParseExt( text, qtrue ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: no concluding '}' in shader %s\n", shader.name ); - return qfalse; - } - - // end of shader definition - if ( token[0] == '}' ) - { - break; - } - // stage definition - else if ( token[0] == '{' ) - { - if ( s >= MAX_SHADER_STAGES ) { - ri.Printf( PRINT_WARNING, "WARNING: too many stages in shader %s\n", shader.name ); - return qfalse; - } - - if ( !ParseStage( &stages[s], text ) ) - { - return qfalse; - } - stages[s].active = qtrue; - s++; - - continue; - } - // skip stuff that only the QuakeEdRadient needs - else if ( !Q_stricmpn( token, "qer", 3 ) ) { - SkipRestOfLine( text ); - continue; - } - // sun parms - else if ( !Q_stricmp( token, "q3map_sun" ) || !Q_stricmp( token, "q3map_sunExt" ) || !Q_stricmp( token, "q3gl2_sun" ) ) { - float a, b; - qboolean isGL2Sun = qfalse; - - if (!Q_stricmp( token, "q3gl2_sun" ) && r_sunShadows->integer ) - { - isGL2Sun = qtrue; - tr.sunShadows = qtrue; - } - - token = COM_ParseExt( text, qfalse ); - tr.sunLight[0] = atof( token ); - token = COM_ParseExt( text, qfalse ); - tr.sunLight[1] = atof( token ); - token = COM_ParseExt( text, qfalse ); - tr.sunLight[2] = atof( token ); - - VectorNormalize( tr.sunLight ); - - token = COM_ParseExt( text, qfalse ); - a = atof( token ); - VectorScale( tr.sunLight, a, tr.sunLight); - - VectorSet( tr.sunAmbient, 0.0f, 0.0f, 0.0f); - - token = COM_ParseExt( text, qfalse ); - a = atof( token ); - a = a / 180 * M_PI; - - token = COM_ParseExt( text, qfalse ); - b = atof( token ); - b = b / 180 * M_PI; - - tr.sunDirection[0] = cos( a ) * cos( b ); - tr.sunDirection[1] = sin( a ) * cos( b ); - tr.sunDirection[2] = sin( b ); - - if (isGL2Sun) - { - token = COM_ParseExt( text, qfalse ); - tr.mapLightScale = atof(token); - - token = COM_ParseExt( text, qfalse ); - VectorScale( tr.sunLight, atof(token), tr.sunAmbient ); - } - - SkipRestOfLine( text ); - continue; - } - // tonemap parms - else if ( !Q_stricmp( token, "q3gl2_tonemap" ) ) { - token = COM_ParseExt( text, qfalse ); - tr.toneMinAvgMaxLevel[0] = atof( token ); - token = COM_ParseExt( text, qfalse ); - tr.toneMinAvgMaxLevel[1] = atof( token ); - token = COM_ParseExt( text, qfalse ); - tr.toneMinAvgMaxLevel[2] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - tr.autoExposureMinMax[0] = atof( token ); - token = COM_ParseExt( text, qfalse ); - tr.autoExposureMinMax[1] = atof( token ); - - SkipRestOfLine( text ); - continue; - } - else if ( !Q_stricmp( token, "deformVertexes" ) ) { - ParseDeform( text ); - continue; - } - else if ( !Q_stricmp( token, "tesssize" ) ) { - SkipRestOfLine( text ); - continue; - } - else if ( !Q_stricmp( token, "clampTime" ) ) { - token = COM_ParseExt( text, qfalse ); - if (token[0]) { - shader.clampTime = atof(token); - } - } - // skip stuff that only the q3map needs - else if ( !Q_stricmpn( token, "q3map", 5 ) ) { - SkipRestOfLine( text ); - continue; - } - // skip stuff that only q3map or the server needs - else if ( !Q_stricmp( token, "surfaceParm" ) ) { - ParseSurfaceParm( text ); - continue; - } - // no mip maps - else if ( !Q_stricmp( token, "nomipmaps" ) ) - { - shader.noMipMaps = qtrue; - shader.noPicMip = qtrue; - continue; - } - // no picmip adjustment - else if ( !Q_stricmp( token, "nopicmip" ) ) - { - shader.noPicMip = qtrue; - continue; - } - // polygonOffset - else if ( !Q_stricmp( token, "polygonOffset" ) ) - { - shader.polygonOffset = qtrue; - continue; - } - // entityMergable, allowing sprite surfaces from multiple entities - // to be merged into one batch. This is a savings for smoke - // puffs and blood, but can't be used for anything where the - // shader calcs (not the surface function) reference the entity color or scroll - else if ( !Q_stricmp( token, "entityMergable" ) ) - { - shader.entityMergable = qtrue; - continue; - } - // fogParms - else if ( !Q_stricmp( token, "fogParms" ) ) - { - if ( !ParseVector( text, 3, shader.fogParms.color ) ) { - return qfalse; - } - - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parm for 'fogParms' keyword in shader '%s'\n", shader.name ); - continue; - } - shader.fogParms.depthForOpaque = atof( token ); - - // skip any old gradient directions - SkipRestOfLine( text ); - continue; - } - // portal - else if ( !Q_stricmp(token, "portal") ) - { - shader.sort = SS_PORTAL; - shader.isPortal = qtrue; - continue; - } - // skyparms <cloudheight> <outerbox> <innerbox> - else if ( !Q_stricmp( token, "skyparms" ) ) - { - ParseSkyParms( text ); - continue; - } - // light <value> determines flaring in q3map, not needed here - else if ( !Q_stricmp(token, "light") ) - { - token = COM_ParseExt( text, qfalse ); - continue; - } - // cull <face> - else if ( !Q_stricmp( token, "cull") ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing cull parms in shader '%s'\n", shader.name ); - continue; - } - - if ( !Q_stricmp( token, "none" ) || !Q_stricmp( token, "twosided" ) || !Q_stricmp( token, "disable" ) ) - { - shader.cullType = CT_TWO_SIDED; - } - else if ( !Q_stricmp( token, "back" ) || !Q_stricmp( token, "backside" ) || !Q_stricmp( token, "backsided" ) ) - { - shader.cullType = CT_BACK_SIDED; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: invalid cull parm '%s' in shader '%s'\n", token, shader.name ); - } - continue; - } - // sort - else if ( !Q_stricmp( token, "sort" ) ) - { - ParseSort( text ); - continue; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown general shader parameter '%s' in '%s'\n", token, shader.name ); - return qfalse; - } - } - - // - // ignore shaders that don't have any stages, unless it is a sky or fog - // - if ( s == 0 && !shader.isSky && !(shader.contentFlags & CONTENTS_FOG ) ) { - return qfalse; - } - - shader.explicitlyDefined = qtrue; - - return qtrue; -} - -/* -======================================================================================== - -SHADER OPTIMIZATION AND FOGGING - -======================================================================================== -*/ - -/* -=================== -ComputeStageIteratorFunc - -See if we can use on of the simple fastpath stage functions, -otherwise set to the generic stage function -=================== -*/ -static void ComputeStageIteratorFunc( void ) -{ - shader.optimalStageIteratorFunc = RB_StageIteratorGeneric; - - // - // see if this should go into the sky path - // - if ( shader.isSky ) - { - shader.optimalStageIteratorFunc = RB_StageIteratorSky; - return; - } -} - -/* -=================== -ComputeVertexAttribs - -Check which vertex attributes we only need, so we -don't need to submit/copy all of them. -=================== -*/ -static void ComputeVertexAttribs(void) -{ - int i, stage; - - // dlights always need ATTR_NORMAL - shader.vertexAttribs = ATTR_POSITION | ATTR_NORMAL; - - // portals always need normals, for SurfIsOffscreen() - if (shader.isPortal) - { - shader.vertexAttribs |= ATTR_NORMAL; - } - - if (shader.defaultShader) - { - shader.vertexAttribs |= ATTR_TEXCOORD; - return; - } - - if(shader.numDeforms) - { - for ( i = 0; i < shader.numDeforms; i++) - { - deformStage_t *ds = &shader.deforms[i]; - - switch (ds->deformation) - { - case DEFORM_BULGE: - shader.vertexAttribs |= ATTR_NORMAL | ATTR_TEXCOORD; - break; - - case DEFORM_AUTOSPRITE: - shader.vertexAttribs |= ATTR_NORMAL | ATTR_COLOR; - break; - - case DEFORM_WAVE: - case DEFORM_NORMALS: - case DEFORM_TEXT0: - case DEFORM_TEXT1: - case DEFORM_TEXT2: - case DEFORM_TEXT3: - case DEFORM_TEXT4: - case DEFORM_TEXT5: - case DEFORM_TEXT6: - case DEFORM_TEXT7: - shader.vertexAttribs |= ATTR_NORMAL; - break; - - default: - case DEFORM_NONE: - case DEFORM_MOVE: - case DEFORM_PROJECTION_SHADOW: - case DEFORM_AUTOSPRITE2: - break; - } - } - } - - for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) - { - shaderStage_t *pStage = &stages[stage]; - - if ( !pStage->active ) - { - break; - } - - if (pStage->glslShaderGroup == tr.lightallShader) - { - shader.vertexAttribs |= ATTR_NORMAL; - -#ifdef USE_VERT_TANGENT_SPACE - if (pStage->glslShaderIndex & LIGHTDEF_USE_NORMALMAP) - { - shader.vertexAttribs |= ATTR_BITANGENT | ATTR_TANGENT; - } -#endif - - switch (pStage->glslShaderIndex & LIGHTDEF_LIGHTTYPE_MASK) - { - case LIGHTDEF_USE_LIGHTMAP: - case LIGHTDEF_USE_LIGHT_VERTEX: - shader.vertexAttribs |= ATTR_LIGHTDIRECTION; - break; - default: - break; - } - } - - for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) - { - if ( pStage->bundle[i].image[0] == 0 ) - { - continue; - } - - switch(pStage->bundle[i].tcGen) - { - case TCGEN_TEXTURE: - shader.vertexAttribs |= ATTR_TEXCOORD; - break; - case TCGEN_LIGHTMAP: - shader.vertexAttribs |= ATTR_LIGHTCOORD; - break; - case TCGEN_ENVIRONMENT_MAPPED: - shader.vertexAttribs |= ATTR_NORMAL; - break; - - default: - break; - } - } - - switch(pStage->rgbGen) - { - case CGEN_EXACT_VERTEX: - case CGEN_VERTEX: - case CGEN_EXACT_VERTEX_LIT: - case CGEN_VERTEX_LIT: - case CGEN_ONE_MINUS_VERTEX: - shader.vertexAttribs |= ATTR_COLOR; - break; - - case CGEN_LIGHTING_DIFFUSE: - shader.vertexAttribs |= ATTR_NORMAL; - break; - - default: - break; - } - - switch(pStage->alphaGen) - { - case AGEN_LIGHTING_SPECULAR: - case AGEN_FRESNEL: - shader.vertexAttribs |= ATTR_NORMAL; - break; - - case AGEN_VERTEX: - case AGEN_ONE_MINUS_VERTEX: - shader.vertexAttribs |= ATTR_COLOR; - break; - - default: - break; - } - } -} - -typedef struct { - int blendA; - int blendB; - - int multitextureEnv; - int multitextureBlend; -} collapse_t; - -static collapse_t collapse[] = { - { 0, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, - GL_MODULATE, 0 }, - - { 0, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, - GL_MODULATE, 0 }, - - { GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, - GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, - - { GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, - GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, - - { GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, - GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, - - { GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, - GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, - - { 0, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, - GL_ADD, 0 }, - - { GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, - GL_ADD, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE }, -#if 0 - { 0, GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_SRCBLEND_SRC_ALPHA, - GL_DECAL, 0 }, -#endif - { -1 } -}; - -/* -================ -CollapseMultitexture - -Attempt to combine two stages into a single multitexture stage -FIXME: I think modulated add + modulated add collapses incorrectly -================= -*/ -static qboolean CollapseMultitexture( void ) { - int abits, bbits; - int i; - textureBundle_t tmpBundle; - - if ( !qglActiveTextureARB ) { - return qfalse; - } - - // make sure both stages are active - if ( !stages[0].active || !stages[1].active ) { - return qfalse; - } - - // on voodoo2, don't combine different tmus - if ( glConfig.driverType == GLDRV_VOODOO ) { - if ( stages[0].bundle[0].image[0]->TMU == - stages[1].bundle[0].image[0]->TMU ) { - return qfalse; - } - } - - abits = stages[0].stateBits; - bbits = stages[1].stateBits; - - // make sure that both stages have identical state other than blend modes - if ( ( abits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) != - ( bbits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) ) { - return qfalse; - } - - abits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - bbits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - - // search for a valid multitexture blend function - for ( i = 0; collapse[i].blendA != -1 ; i++ ) { - if ( abits == collapse[i].blendA - && bbits == collapse[i].blendB ) { - break; - } - } - - // nothing found - if ( collapse[i].blendA == -1 ) { - return qfalse; - } - - // GL_ADD is a separate extension - if ( collapse[i].multitextureEnv == GL_ADD && !glConfig.textureEnvAddAvailable ) { - return qfalse; - } - - // make sure waveforms have identical parameters - if ( ( stages[0].rgbGen != stages[1].rgbGen ) || - ( stages[0].alphaGen != stages[1].alphaGen ) ) { - return qfalse; - } - - // an add collapse can only have identity colors - if ( collapse[i].multitextureEnv == GL_ADD && stages[0].rgbGen != CGEN_IDENTITY ) { - return qfalse; - } - - if ( stages[0].rgbGen == CGEN_WAVEFORM ) - { - if ( memcmp( &stages[0].rgbWave, - &stages[1].rgbWave, - sizeof( stages[0].rgbWave ) ) ) - { - return qfalse; - } - } - if ( stages[0].alphaGen == AGEN_WAVEFORM ) - { - if ( memcmp( &stages[0].alphaWave, - &stages[1].alphaWave, - sizeof( stages[0].alphaWave ) ) ) - { - return qfalse; - } - } - - - // make sure that lightmaps are in bundle 1 for 3dfx - if ( stages[0].bundle[0].isLightmap ) - { - tmpBundle = stages[0].bundle[0]; - stages[0].bundle[0] = stages[1].bundle[0]; - stages[0].bundle[1] = tmpBundle; - } - else - { - stages[0].bundle[1] = stages[1].bundle[0]; - } - - // set the new blend state bits - shader.multitextureEnv = collapse[i].multitextureEnv; - stages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - stages[0].stateBits |= collapse[i].multitextureBlend; - - // - // move down subsequent shaders - // - memmove( &stages[1], &stages[2], sizeof( stages[0] ) * ( MAX_SHADER_STAGES - 2 ) ); - Com_Memset( &stages[MAX_SHADER_STAGES-1], 0, sizeof( stages[0] ) ); - - return qtrue; -} - -static void CollapseStagesToLightall(shaderStage_t *diffuse, - shaderStage_t *normal, shaderStage_t *specular, shaderStage_t *lightmap, - qboolean useLightVector, qboolean useLightVertex, qboolean parallax, qboolean environment) -{ - int defs = 0; - - //ri.Printf(PRINT_ALL, "shader %s has diffuse %s", shader.name, diffuse->bundle[0].image[0]->imgName); - - // reuse diffuse, mark others inactive - diffuse->type = ST_GLSL; - - if (lightmap) - { - //ri.Printf(PRINT_ALL, ", lightmap"); - diffuse->bundle[TB_LIGHTMAP] = lightmap->bundle[0]; - defs |= LIGHTDEF_USE_LIGHTMAP; - } - else if (useLightVector) - { - defs |= LIGHTDEF_USE_LIGHT_VECTOR; - } - else if (useLightVertex) - { - defs |= LIGHTDEF_USE_LIGHT_VERTEX; - } - - if (r_deluxeMapping->integer && tr.worldDeluxeMapping && lightmap) - { - //ri.Printf(PRINT_ALL, ", deluxemap"); - diffuse->bundle[TB_DELUXEMAP] = lightmap->bundle[0]; - diffuse->bundle[TB_DELUXEMAP].image[0] = tr.deluxemaps[shader.lightmapIndex]; - defs |= LIGHTDEF_USE_DELUXEMAP; - } - - if (r_normalMapping->integer) - { - image_t *diffuseImg; - if (normal) - { - //ri.Printf(PRINT_ALL, ", normalmap %s", normal->bundle[0].image[0]->imgName); - diffuse->bundle[TB_NORMALMAP] = normal->bundle[0]; - defs |= LIGHTDEF_USE_NORMALMAP; - if (parallax && r_parallaxMapping->integer) - defs |= LIGHTDEF_USE_PARALLAXMAP; - } - else if ((lightmap || useLightVector || useLightVertex) && (diffuseImg = diffuse->bundle[TB_DIFFUSEMAP].image[0])) - { - char normalName[MAX_QPATH]; - image_t *normalImg; - imgFlags_t normalFlags = (diffuseImg->flags & ~(IMGFLAG_GENNORMALMAP | IMGFLAG_SRGB)) | IMGFLAG_NOLIGHTSCALE; - - COM_StripExtension(diffuseImg->imgName, normalName, MAX_QPATH); - Q_strcat(normalName, MAX_QPATH, "_n"); - - normalImg = R_FindImageFile(normalName, IMGTYPE_NORMAL, normalFlags); - - if (normalImg) - { - diffuse->bundle[TB_NORMALMAP] = diffuse->bundle[0]; - diffuse->bundle[TB_NORMALMAP].image[0] = normalImg; - - defs |= LIGHTDEF_USE_NORMALMAP; - if (parallax && r_parallaxMapping->integer) - defs |= LIGHTDEF_USE_PARALLAXMAP; - } - } - } - - if (r_specularMapping->integer) - { - if (specular) - { - //ri.Printf(PRINT_ALL, ", specularmap %s", specular->bundle[0].image[0]->imgName); - diffuse->bundle[TB_SPECULARMAP] = specular->bundle[0]; - diffuse->materialInfo[0] = specular->materialInfo[0]; - diffuse->materialInfo[1] = specular->materialInfo[1]; - defs |= LIGHTDEF_USE_SPECULARMAP; - } - } - - if (environment || diffuse->bundle[0].numTexMods) - { - defs |= LIGHTDEF_USE_TCGEN_AND_TCMOD; - } - - //ri.Printf(PRINT_ALL, ".\n"); - - diffuse->glslShaderGroup = tr.lightallShader; - diffuse->glslShaderIndex = defs; -} - - -static qboolean CollapseStagesToGLSL(void) -{ - int i, j, numStages; - qboolean skip = qfalse; - - // skip shaders with deforms - if (shader.numDeforms != 0) - { - skip = qtrue; - } - - if (!skip) - { - // if 2+ stages and first stage is lightmap, switch them - // this makes it easier for the later bits to process - if (stages[0].active && stages[0].bundle[0].isLightmap && stages[1].active) - { - int blendBits = stages[1].stateBits & ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - - if (blendBits == (GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO) - || blendBits == (GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR)) - { - int stateBits0 = stages[0].stateBits; - int stateBits1 = stages[1].stateBits; - shaderStage_t swapStage; - - swapStage = stages[0]; - stages[0] = stages[1]; - stages[1] = swapStage; - - stages[0].stateBits = stateBits0; - stages[1].stateBits = stateBits1; - } - } - } - - if (!skip) - { - // scan for shaders that aren't supported - for (i = 0; i < MAX_SHADER_STAGES; i++) - { - shaderStage_t *pStage = &stages[i]; - - if (!pStage->active) - continue; - - if (pStage->adjustColorsForFog) - { - skip = qtrue; - break; - } - - if (pStage->bundle[0].isLightmap) - { - int blendBits = pStage->stateBits & ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - - if (blendBits != (GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO) - && blendBits != (GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR)) - { - skip = qtrue; - break; - } - } - - switch(pStage->bundle[0].tcGen) - { - case TCGEN_TEXTURE: - case TCGEN_LIGHTMAP: - case TCGEN_ENVIRONMENT_MAPPED: - break; - default: - skip = qtrue; - break; - } - - switch(pStage->alphaGen) - { - case AGEN_LIGHTING_SPECULAR: - case AGEN_PORTAL: - case AGEN_FRESNEL: - skip = qtrue; - break; - default: - break; - } - } - } - - if (!skip) - { - for (i = 0; i < MAX_SHADER_STAGES; i++) - { - shaderStage_t *pStage = &stages[i]; - shaderStage_t *diffuse, *normal, *specular, *lightmap; - qboolean parallax, environment, diffuselit, vertexlit; - - if (!pStage->active) - continue; - - // skip normal and specular maps - if (pStage->type != ST_COLORMAP) - continue; - - // skip lightmaps - if (pStage->bundle[0].isLightmap) - continue; - - diffuse = pStage; - normal = NULL; - parallax = qfalse; - specular = NULL; - lightmap = NULL; - - // we have a diffuse map, find matching normal, specular, and lightmap - for (j = i + 1; j < MAX_SHADER_STAGES; j++) - { - shaderStage_t *pStage2 = &stages[j]; - - if (!pStage2->active) - continue; - - switch(pStage2->type) - { - case ST_NORMALMAP: - if (!normal) - { - normal = pStage2; - } - break; - - case ST_NORMALPARALLAXMAP: - if (!normal) - { - normal = pStage2; - parallax = qtrue; - } - break; - - case ST_SPECULARMAP: - if (!specular) - { - specular = pStage2; - } - break; - - case ST_COLORMAP: - if (pStage2->bundle[0].isLightmap) - { - lightmap = pStage2; - } - break; - - default: - break; - } - } - - environment = qfalse; - if (diffuse->bundle[0].tcGen == TCGEN_ENVIRONMENT_MAPPED) - { - environment = qtrue; - } - - diffuselit = qfalse; - if (diffuse->rgbGen == CGEN_LIGHTING_DIFFUSE) - { - diffuselit = qtrue; - } - - vertexlit = qfalse; - if (diffuse->rgbGen == CGEN_VERTEX_LIT || diffuse->rgbGen == CGEN_EXACT_VERTEX_LIT) - { - vertexlit = qtrue; - } - - CollapseStagesToLightall(diffuse, normal, specular, lightmap, diffuselit, vertexlit, parallax, environment); - } - - // deactivate lightmap stages - for (i = 0; i < MAX_SHADER_STAGES; i++) - { - shaderStage_t *pStage = &stages[i]; - - if (!pStage->active) - continue; - - if (pStage->bundle[0].isLightmap) - { - pStage->active = qfalse; - } - } - } - - // deactivate normal and specular stages - for (i = 0; i < MAX_SHADER_STAGES; i++) - { - shaderStage_t *pStage = &stages[i]; - - if (!pStage->active) - continue; - - if (pStage->type == ST_NORMALMAP) - { - pStage->active = qfalse; - } - - if (pStage->type == ST_NORMALPARALLAXMAP) - { - pStage->active = qfalse; - } - - if (pStage->type == ST_SPECULARMAP) - { - pStage->active = qfalse; - } - } - - // remove inactive stages - numStages = 0; - for (i = 0; i < MAX_SHADER_STAGES; i++) - { - if (!stages[i].active) - continue; - - if (i == numStages) - { - numStages++; - continue; - } - - stages[numStages] = stages[i]; - stages[i].active = qfalse; - numStages++; - } - - if (numStages == i && i >= 2 && CollapseMultitexture()) - numStages--; - - return numStages; -} - -/* -============= - -FixRenderCommandList -https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493 -Arnout: this is a nasty issue. Shaders can be registered after drawsurfaces are generated -but before the frame is rendered. This will, for the duration of one frame, cause drawsurfaces -to be rendered with bad shaders. To fix this, need to go through all render commands and fix -sortedIndex. -============== -*/ -static void FixRenderCommandList( int newShader ) { - renderCommandList_t *cmdList = &backEndData->commands; - - if( cmdList ) { - const void *curCmd = cmdList->cmds; - - while ( 1 ) { - curCmd = PADP(curCmd, sizeof(void *)); - - switch ( *(const int *)curCmd ) { - case RC_SET_COLOR: - { - const setColorCommand_t *sc_cmd = (const setColorCommand_t *)curCmd; - curCmd = (const void *)(sc_cmd + 1); - break; - } - case RC_STRETCH_PIC: - { - const stretchPicCommand_t *sp_cmd = (const stretchPicCommand_t *)curCmd; - curCmd = (const void *)(sp_cmd + 1); - break; - } - case RC_DRAW_SURFS: - { - int i; - drawSurf_t *drawSurf; - shader_t *shader; - int fogNum; - int entityNum; - int dlightMap; - int pshadowMap; - int sortedIndex; - const drawSurfsCommand_t *ds_cmd = (const drawSurfsCommand_t *)curCmd; - - for( i = 0, drawSurf = ds_cmd->drawSurfs; i < ds_cmd->numDrawSurfs; i++, drawSurf++ ) { - R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlightMap, &pshadowMap ); - sortedIndex = (( drawSurf->sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1)); - if( sortedIndex >= newShader ) { - sortedIndex++; - drawSurf->sort = (sortedIndex << QSORT_SHADERNUM_SHIFT) | entityNum | ( fogNum << QSORT_FOGNUM_SHIFT ) | ( (int)pshadowMap << QSORT_PSHADOW_SHIFT) | (int)dlightMap; - } - } - curCmd = (const void *)(ds_cmd + 1); - break; - } - case RC_DRAW_BUFFER: - { - const drawBufferCommand_t *db_cmd = (const drawBufferCommand_t *)curCmd; - curCmd = (const void *)(db_cmd + 1); - break; - } - case RC_SWAP_BUFFERS: - { - const swapBuffersCommand_t *sb_cmd = (const swapBuffersCommand_t *)curCmd; - curCmd = (const void *)(sb_cmd + 1); - break; - } - case RC_END_OF_LIST: - default: - return; - } - } - } -} - -/* -============== -SortNewShader - -Positions the most recently created shader in the tr.sortedShaders[] -array so that the shader->sort key is sorted reletive to the other -shaders. - -Sets shader->sortedIndex -============== -*/ -static void SortNewShader( void ) { - int i; - float sort; - shader_t *newShader; - - newShader = tr.shaders[ tr.numShaders - 1 ]; - sort = newShader->sort; - - for ( i = tr.numShaders - 2 ; i >= 0 ; i-- ) { - if ( tr.sortedShaders[ i ]->sort <= sort ) { - break; - } - tr.sortedShaders[i+1] = tr.sortedShaders[i]; - tr.sortedShaders[i+1]->sortedIndex++; - } - - // Arnout: fix rendercommandlist - // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493 - FixRenderCommandList( i+1 ); - - newShader->sortedIndex = i+1; - tr.sortedShaders[i+1] = newShader; -} - - -/* -==================== -GeneratePermanentShader -==================== -*/ -static shader_t *GeneratePermanentShader( void ) { - shader_t *newShader; - int i, b; - int size, hash; - - if ( tr.numShaders == MAX_SHADERS ) { - ri.Printf( PRINT_WARNING, "WARNING: GeneratePermanentShader - MAX_SHADERS hit\n"); - return tr.defaultShader; - } - - newShader = ri.Hunk_Alloc( sizeof( shader_t ), h_low ); - - *newShader = shader; - - if ( shader.sort <= SS_OPAQUE ) { - newShader->fogPass = FP_EQUAL; - } else if ( shader.contentFlags & CONTENTS_FOG ) { - newShader->fogPass = FP_LE; - } - - tr.shaders[ tr.numShaders ] = newShader; - newShader->index = tr.numShaders; - - tr.sortedShaders[ tr.numShaders ] = newShader; - newShader->sortedIndex = tr.numShaders; - - tr.numShaders++; - - for ( i = 0 ; i < newShader->numUnfoggedPasses ; i++ ) { - if ( !stages[i].active ) { - break; - } - newShader->stages[i] = ri.Hunk_Alloc( sizeof( stages[i] ), h_low ); - *newShader->stages[i] = stages[i]; - - for ( b = 0 ; b < NUM_TEXTURE_BUNDLES ; b++ ) { - size = newShader->stages[i]->bundle[b].numTexMods * sizeof( texModInfo_t ); - newShader->stages[i]->bundle[b].texMods = ri.Hunk_Alloc( size, h_low ); - Com_Memcpy( newShader->stages[i]->bundle[b].texMods, stages[i].bundle[b].texMods, size ); - } - } - - SortNewShader(); - - hash = generateHashValue(newShader->name, FILE_HASH_SIZE); - newShader->next = hashTable[hash]; - hashTable[hash] = newShader; - - return newShader; -} - -/* -================= -VertexLightingCollapse - -If vertex lighting is enabled, only render a single -pass, trying to guess which is the correct one to best aproximate -what it is supposed to look like. -================= -*/ -static void VertexLightingCollapse( void ) { - int stage; - shaderStage_t *bestStage; - int bestImageRank; - int rank; - - // if we aren't opaque, just use the first pass - if ( shader.sort == SS_OPAQUE ) { - - // pick the best texture for the single pass - bestStage = &stages[0]; - bestImageRank = -999999; - - for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) { - shaderStage_t *pStage = &stages[stage]; - - if ( !pStage->active ) { - break; - } - rank = 0; - - if ( pStage->bundle[0].isLightmap ) { - rank -= 100; - } - if ( pStage->bundle[0].tcGen != TCGEN_TEXTURE ) { - rank -= 5; - } - if ( pStage->bundle[0].numTexMods ) { - rank -= 5; - } - if ( pStage->rgbGen != CGEN_IDENTITY && pStage->rgbGen != CGEN_IDENTITY_LIGHTING ) { - rank -= 3; - } - - if ( rank > bestImageRank ) { - bestImageRank = rank; - bestStage = pStage; - } - } - - stages[0].bundle[0] = bestStage->bundle[0]; - stages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - stages[0].stateBits |= GLS_DEPTHMASK_TRUE; - if ( shader.lightmapIndex == LIGHTMAP_NONE ) { - stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; - } else { - stages[0].rgbGen = CGEN_EXACT_VERTEX; - } - stages[0].alphaGen = AGEN_SKIP; - } else { - // don't use a lightmap (tesla coils) - if ( stages[0].bundle[0].isLightmap ) { - stages[0] = stages[1]; - } - - // if we were in a cross-fade cgen, hack it to normal - if ( stages[0].rgbGen == CGEN_ONE_MINUS_ENTITY || stages[1].rgbGen == CGEN_ONE_MINUS_ENTITY ) { - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - } - if ( ( stages[0].rgbGen == CGEN_WAVEFORM && stages[0].rgbWave.func == GF_SAWTOOTH ) - && ( stages[1].rgbGen == CGEN_WAVEFORM && stages[1].rgbWave.func == GF_INVERSE_SAWTOOTH ) ) { - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - } - if ( ( stages[0].rgbGen == CGEN_WAVEFORM && stages[0].rgbWave.func == GF_INVERSE_SAWTOOTH ) - && ( stages[1].rgbGen == CGEN_WAVEFORM && stages[1].rgbWave.func == GF_SAWTOOTH ) ) { - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - } - } - - for ( stage = 1; stage < MAX_SHADER_STAGES; stage++ ) { - shaderStage_t *pStage = &stages[stage]; - - if ( !pStage->active ) { - break; - } - - Com_Memset( pStage, 0, sizeof( *pStage ) ); - } -} - -/* -========================= -FinishShader - -Returns a freshly allocated shader with all the needed info -from the current global working shader -========================= -*/ -static shader_t *FinishShader( void ) { - int stage; - qboolean hasLightmapStage; - qboolean vertexLightmap; - - hasLightmapStage = qfalse; - vertexLightmap = qfalse; - - // - // set sky stuff appropriate - // - if ( shader.isSky ) { - shader.sort = SS_ENVIRONMENT; - } - - // - // set polygon offset - // - if ( shader.polygonOffset && !shader.sort ) { - shader.sort = SS_DECAL; - } - - // - // set appropriate stage information - // - for ( stage = 0; stage < MAX_SHADER_STAGES; ) { - shaderStage_t *pStage = &stages[stage]; - - if ( !pStage->active ) { - break; - } - - // check for a missing texture - if ( !pStage->bundle[0].image[0] ) { - ri.Printf( PRINT_WARNING, "Shader %s has a stage with no image\n", shader.name ); - pStage->active = qfalse; - stage++; - continue; - } - - // - // ditch this stage if it's detail and detail textures are disabled - // - if ( pStage->isDetail && !r_detailTextures->integer ) - { - int index; - - for(index = stage + 1; index < MAX_SHADER_STAGES; index++) - { - if(!stages[index].active) - break; - } - - if(index < MAX_SHADER_STAGES) - memmove(pStage, pStage + 1, sizeof(*pStage) * (index - stage)); - else - { - if(stage + 1 < MAX_SHADER_STAGES) - memmove(pStage, pStage + 1, sizeof(*pStage) * (index - stage - 1)); - - Com_Memset(&stages[index - 1], 0, sizeof(*stages)); - } - - continue; - } - - // - // default texture coordinate generation - // - if ( pStage->bundle[0].isLightmap ) { - if ( pStage->bundle[0].tcGen == TCGEN_BAD ) { - pStage->bundle[0].tcGen = TCGEN_LIGHTMAP; - } - hasLightmapStage = qtrue; - } else { - if ( pStage->bundle[0].tcGen == TCGEN_BAD ) { - pStage->bundle[0].tcGen = TCGEN_TEXTURE; - } - } - - - // not a true lightmap but we want to leave existing - // behaviour in place and not print out a warning - //if (pStage->rgbGen == CGEN_VERTEX) { - // vertexLightmap = qtrue; - //} - - - - // - // determine sort order and fog color adjustment - // - if ( ( pStage->stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) && - ( stages[0].stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) ) { - int blendSrcBits = pStage->stateBits & GLS_SRCBLEND_BITS; - int blendDstBits = pStage->stateBits & GLS_DSTBLEND_BITS; - - // fog color adjustment only works for blend modes that have a contribution - // that aproaches 0 as the modulate values aproach 0 -- - // GL_ONE, GL_ONE - // GL_ZERO, GL_ONE_MINUS_SRC_COLOR - // GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA - - // modulate, additive - if ( ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && ( blendDstBits == GLS_DSTBLEND_ONE ) ) || - ( ( blendSrcBits == GLS_SRCBLEND_ZERO ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR ) ) ) { - pStage->adjustColorsForFog = ACFF_MODULATE_RGB; - } - // strict blend - else if ( ( blendSrcBits == GLS_SRCBLEND_SRC_ALPHA ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ) ) - { - pStage->adjustColorsForFog = ACFF_MODULATE_ALPHA; - } - // premultiplied alpha - else if ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ) ) - { - pStage->adjustColorsForFog = ACFF_MODULATE_RGBA; - } else { - // we can't adjust this one correctly, so it won't be exactly correct in fog - } - - // don't screw with sort order if this is a portal or environment - if ( !shader.sort ) { - // see through item, like a grill or grate - if ( pStage->stateBits & GLS_DEPTHMASK_TRUE ) { - shader.sort = SS_SEE_THROUGH; - } else { - shader.sort = SS_BLEND0; - } - } - } - - stage++; - } - - // there are times when you will need to manually apply a sort to - // opaque alpha tested shaders that have later blend passes - if ( !shader.sort ) { - shader.sort = SS_OPAQUE; - } - - // - // if we are in r_vertexLight mode, never use a lightmap texture - // - if ( stage > 1 && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) ) { - VertexLightingCollapse(); - stage = 1; - hasLightmapStage = qfalse; - } - - // - // look for multitexture potential - // - stage = CollapseStagesToGLSL(); - - if ( shader.lightmapIndex >= 0 && !hasLightmapStage ) { - if (vertexLightmap) { - ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has VERTEX forced lightmap!\n", shader.name ); - } else { - ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has lightmap but no lightmap stage!\n", shader.name ); - // Don't set this, it will just add duplicate shaders to the hash - //shader.lightmapIndex = LIGHTMAP_NONE; - } - } - - - // - // compute number of passes - // - shader.numUnfoggedPasses = stage; - - // fogonly shaders don't have any normal passes - if (stage == 0 && !shader.isSky) - shader.sort = SS_FOG; - - // determine which stage iterator function is appropriate - ComputeStageIteratorFunc(); - - // determine which vertex attributes this shader needs - ComputeVertexAttribs(); - - return GeneratePermanentShader(); -} - -//======================================================================================== - -/* -==================== -FindShaderInShaderText - -Scans the combined text description of all the shader files for -the given shader name. - -return NULL if not found - -If found, it will return a valid shader -===================== -*/ -static char *FindShaderInShaderText( const char *shadername ) { - - char *token, *p; - - int i, hash; - - hash = generateHashValue(shadername, MAX_SHADERTEXT_HASH); - - if(shaderTextHashTable[hash]) - { - for (i = 0; shaderTextHashTable[hash][i]; i++) - { - p = shaderTextHashTable[hash][i]; - token = COM_ParseExt(&p, qtrue); - - if(!Q_stricmp(token, shadername)) - return p; - } - } - - p = s_shaderText; - - if ( !p ) { - return NULL; - } - - // look for label - while ( 1 ) { - token = COM_ParseExt( &p, qtrue ); - if ( token[0] == 0 ) { - break; - } - - if ( !Q_stricmp( token, shadername ) ) { - return p; - } - else { - // skip the definition - SkipBracedSection( &p ); - } - } - - return NULL; -} - - -/* -================== -R_FindShaderByName - -Will always return a valid shader, but it might be the -default shader if the real one can't be found. -================== -*/ -shader_t *R_FindShaderByName( const char *name ) { - char strippedName[MAX_QPATH]; - int hash; - shader_t *sh; - - if ( (name==NULL) || (name[0] == 0) ) { - return tr.defaultShader; - } - - COM_StripExtension(name, strippedName, sizeof(strippedName)); - - hash = generateHashValue(strippedName, FILE_HASH_SIZE); - - // - // see if the shader is already loaded - // - for (sh=hashTable[hash]; sh; sh=sh->next) { - // NOTE: if there was no shader or image available with the name strippedName - // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we - // have to check all default shaders otherwise for every call to R_FindShader - // with that same strippedName a new default shader is created. - if (Q_stricmp(sh->name, strippedName) == 0) { - // match found - return sh; - } - } - - return tr.defaultShader; -} - - -/* -=============== -R_FindShader - -Will always return a valid shader, but it might be the -default shader if the real one can't be found. - -In the interest of not requiring an explicit shader text entry to -be defined for every single image used in the game, three default -shader behaviors can be auto-created for any image: - -If lightmapIndex == LIGHTMAP_NONE, then the image will have -dynamic diffuse lighting applied to it, as apropriate for most -entity skin surfaces. - -If lightmapIndex == LIGHTMAP_2D, then the image will be used -for 2D rendering unless an explicit shader is found - -If lightmapIndex == LIGHTMAP_BY_VERTEX, then the image will use -the vertex rgba modulate values, as apropriate for misc_model -pre-lit surfaces. - -Other lightmapIndex values will have a lightmap stage created -and src*dest blending applied with the texture, as apropriate for -most world construction surfaces. - -=============== -*/ -shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage ) { - char strippedName[MAX_QPATH]; - int i, hash; - char *shaderText; - image_t *image; - shader_t *sh; - - if ( name[0] == 0 ) { - return tr.defaultShader; - } - - // use (fullbright) vertex lighting if the bsp file doesn't have - // lightmaps - if ( lightmapIndex >= 0 && lightmapIndex >= tr.numLightmaps ) { - lightmapIndex = LIGHTMAP_BY_VERTEX; - } else if ( lightmapIndex < LIGHTMAP_2D ) { - // negative lightmap indexes cause stray pointers (think tr.lightmaps[lightmapIndex]) - ri.Printf( PRINT_WARNING, "WARNING: shader '%s' has invalid lightmap index of %d\n", name, lightmapIndex ); - lightmapIndex = LIGHTMAP_BY_VERTEX; - } - - COM_StripExtension(name, strippedName, sizeof(strippedName)); - - hash = generateHashValue(strippedName, FILE_HASH_SIZE); - - // - // see if the shader is already loaded - // - for (sh = hashTable[hash]; sh; sh = sh->next) { - // NOTE: if there was no shader or image available with the name strippedName - // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we - // have to check all default shaders otherwise for every call to R_FindShader - // with that same strippedName a new default shader is created. - if ( (sh->lightmapIndex == lightmapIndex || sh->defaultShader) && - !Q_stricmp(sh->name, strippedName)) { - // match found - return sh; - } - } - - // clear the global shader - Com_Memset( &shader, 0, sizeof( shader ) ); - Com_Memset( &stages, 0, sizeof( stages ) ); - Q_strncpyz(shader.name, strippedName, sizeof(shader.name)); - shader.lightmapIndex = lightmapIndex; - for ( i = 0 ; i < MAX_SHADER_STAGES ; i++ ) { - stages[i].bundle[0].texMods = texMods[i]; - } - - // - // attempt to define shader from an explicit parameter file - // - shaderText = FindShaderInShaderText( strippedName ); - if ( shaderText ) { - // enable this when building a pak file to get a global list - // of all explicit shaders - if ( r_printShaders->integer ) { - ri.Printf( PRINT_ALL, "*SHADER* %s\n", name ); - } - - if ( !ParseShader( &shaderText ) ) { - // had errors, so use default shader - shader.defaultShader = qtrue; - } - sh = FinishShader(); - return sh; - } - - - // - // if not defined in the in-memory shader descriptions, - // look for a single supported image file - // - { - imgFlags_t flags; - - flags = IMGFLAG_NONE; - - if (r_srgb->integer) - flags |= IMGFLAG_SRGB; - - if (mipRawImage) - { - flags |= IMGFLAG_MIPMAP | IMGFLAG_PICMIP; - - if (r_genNormalMaps->integer) - flags |= IMGFLAG_GENNORMALMAP; - } - else - { - flags |= IMGFLAG_CLAMPTOEDGE; - } - - image = R_FindImageFile( name, IMGTYPE_COLORALPHA, flags ); - if ( !image ) { - ri.Printf( PRINT_DEVELOPER, "Couldn't find image file for shader %s\n", name ); - shader.defaultShader = qtrue; - return FinishShader(); - } - } - - // - // create the default shading commands - // - if ( shader.lightmapIndex == LIGHTMAP_NONE ) { - // dynamic colors at vertexes - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; - stages[0].stateBits = GLS_DEFAULT; - } else if ( shader.lightmapIndex == LIGHTMAP_BY_VERTEX ) { - // explicit colors at vertexes - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_EXACT_VERTEX; - stages[0].alphaGen = AGEN_SKIP; - stages[0].stateBits = GLS_DEFAULT; - } else if ( shader.lightmapIndex == LIGHTMAP_2D ) { - // GUI elements - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_VERTEX; - stages[0].alphaGen = AGEN_VERTEX; - stages[0].stateBits = GLS_DEPTHTEST_DISABLE | - GLS_SRCBLEND_SRC_ALPHA | - GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - } else if ( shader.lightmapIndex == LIGHTMAP_WHITEIMAGE ) { - // fullbright level - stages[0].bundle[0].image[0] = tr.whiteImage; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - stages[0].stateBits = GLS_DEFAULT; - - stages[1].bundle[0].image[0] = image; - stages[1].active = qtrue; - stages[1].rgbGen = CGEN_IDENTITY; - stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; - } else { - // two pass lightmap - stages[0].bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; - stages[0].bundle[0].isLightmap = qtrue; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_IDENTITY; // lightmaps are scaled on creation - // for identitylight - stages[0].stateBits = GLS_DEFAULT; - - stages[1].bundle[0].image[0] = image; - stages[1].active = qtrue; - stages[1].rgbGen = CGEN_IDENTITY; - stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; - } - - return FinishShader(); -} - - -qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_t *image, qboolean mipRawImage) { - int i, hash; - shader_t *sh; - - hash = generateHashValue(name, FILE_HASH_SIZE); - - // probably not necessary since this function - // only gets called from tr_font.c with lightmapIndex == LIGHTMAP_2D - // but better safe than sorry. - if ( lightmapIndex >= tr.numLightmaps ) { - lightmapIndex = LIGHTMAP_WHITEIMAGE; - } - - // - // see if the shader is already loaded - // - for (sh=hashTable[hash]; sh; sh=sh->next) { - // NOTE: if there was no shader or image available with the name strippedName - // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we - // have to check all default shaders otherwise for every call to R_FindShader - // with that same strippedName a new default shader is created. - if ( (sh->lightmapIndex == lightmapIndex || sh->defaultShader) && - // index by name - !Q_stricmp(sh->name, name)) { - // match found - return sh->index; - } - } - - // clear the global shader - Com_Memset( &shader, 0, sizeof( shader ) ); - Com_Memset( &stages, 0, sizeof( stages ) ); - Q_strncpyz(shader.name, name, sizeof(shader.name)); - shader.lightmapIndex = lightmapIndex; - for ( i = 0 ; i < MAX_SHADER_STAGES ; i++ ) { - stages[i].bundle[0].texMods = texMods[i]; - } - - // - // create the default shading commands - // - if ( shader.lightmapIndex == LIGHTMAP_NONE ) { - // dynamic colors at vertexes - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; - stages[0].stateBits = GLS_DEFAULT; - } else if ( shader.lightmapIndex == LIGHTMAP_BY_VERTEX ) { - // explicit colors at vertexes - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_EXACT_VERTEX; - stages[0].alphaGen = AGEN_SKIP; - stages[0].stateBits = GLS_DEFAULT; - } else if ( shader.lightmapIndex == LIGHTMAP_2D ) { - // GUI elements - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_VERTEX; - stages[0].alphaGen = AGEN_VERTEX; - stages[0].stateBits = GLS_DEPTHTEST_DISABLE | - GLS_SRCBLEND_SRC_ALPHA | - GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - } else if ( shader.lightmapIndex == LIGHTMAP_WHITEIMAGE ) { - // fullbright level - stages[0].bundle[0].image[0] = tr.whiteImage; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - stages[0].stateBits = GLS_DEFAULT; - - stages[1].bundle[0].image[0] = image; - stages[1].active = qtrue; - stages[1].rgbGen = CGEN_IDENTITY; - stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; - } else { - // two pass lightmap - stages[0].bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; - stages[0].bundle[0].isLightmap = qtrue; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_IDENTITY; // lightmaps are scaled on creation - // for identitylight - stages[0].stateBits = GLS_DEFAULT; - - stages[1].bundle[0].image[0] = image; - stages[1].active = qtrue; - stages[1].rgbGen = CGEN_IDENTITY; - stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; - } - - sh = FinishShader(); - return sh->index; -} - - -/* -==================== -RE_RegisterShader - -This is the exported shader entry point for the rest of the system -It will always return an index that will be valid. - -This should really only be used for explicit shaders, because there is no -way to ask for different implicit lighting modes (vertex, lightmap, etc) -==================== -*/ -qhandle_t RE_RegisterShaderLightMap( const char *name, int lightmapIndex ) { - shader_t *sh; - - if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); - return 0; - } - - sh = R_FindShader( name, lightmapIndex, qtrue ); - - // we want to return 0 if the shader failed to - // load for some reason, but R_FindShader should - // still keep a name allocated for it, so if - // something calls RE_RegisterShader again with - // the same name, we don't try looking for it again - if ( sh->defaultShader ) { - return 0; - } - - return sh->index; -} - - -/* -==================== -RE_RegisterShader - -This is the exported shader entry point for the rest of the system -It will always return an index that will be valid. - -This should really only be used for explicit shaders, because there is no -way to ask for different implicit lighting modes (vertex, lightmap, etc) -==================== -*/ -qhandle_t RE_RegisterShader( const char *name ) { - shader_t *sh; - - if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); - return 0; - } - - sh = R_FindShader( name, LIGHTMAP_2D, qtrue ); - - // we want to return 0 if the shader failed to - // load for some reason, but R_FindShader should - // still keep a name allocated for it, so if - // something calls RE_RegisterShader again with - // the same name, we don't try looking for it again - if ( sh->defaultShader ) { - return 0; - } - - return sh->index; -} - - -/* -==================== -RE_RegisterShaderNoMip - -For menu graphics that should never be picmiped -==================== -*/ -qhandle_t RE_RegisterShaderNoMip( const char *name ) { - shader_t *sh; - - if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); - return 0; - } - - sh = R_FindShader( name, LIGHTMAP_2D, qfalse ); - - // we want to return 0 if the shader failed to - // load for some reason, but R_FindShader should - // still keep a name allocated for it, so if - // something calls RE_RegisterShader again with - // the same name, we don't try looking for it again - if ( sh->defaultShader ) { - return 0; - } - - return sh->index; -} - -/* -==================== -R_GetShaderByHandle - -When a handle is passed in by another module, this range checks -it and returns a valid (possibly default) shader_t to be used internally. -==================== -*/ -shader_t *R_GetShaderByHandle( qhandle_t hShader ) { - if ( hShader < 0 ) { - ri.Printf( PRINT_WARNING, "R_GetShaderByHandle: out of range hShader '%d'\n", hShader ); - return tr.defaultShader; - } - if ( hShader >= tr.numShaders ) { - ri.Printf( PRINT_WARNING, "R_GetShaderByHandle: out of range hShader '%d'\n", hShader ); - return tr.defaultShader; - } - return tr.shaders[hShader]; -} - -/* -=============== -R_ShaderList_f - -Dump information on all valid shaders to the console -A second parameter will cause it to print in sorted order -=============== -*/ -void R_ShaderList_f (void) { - int i; - int count; - shader_t *shader; - - ri.Printf (PRINT_ALL, "-----------------------\n"); - - count = 0; - for ( i = 0 ; i < tr.numShaders ; i++ ) { - if ( ri.Cmd_Argc() > 1 ) { - shader = tr.sortedShaders[i]; - } else { - shader = tr.shaders[i]; - } - - ri.Printf( PRINT_ALL, "%i ", shader->numUnfoggedPasses ); - - if (shader->lightmapIndex >= 0 ) { - ri.Printf (PRINT_ALL, "L "); - } else { - ri.Printf (PRINT_ALL, " "); - } - if ( shader->multitextureEnv == GL_ADD ) { - ri.Printf( PRINT_ALL, "MT(a) " ); - } else if ( shader->multitextureEnv == GL_MODULATE ) { - ri.Printf( PRINT_ALL, "MT(m) " ); - } else if ( shader->multitextureEnv == GL_DECAL ) { - ri.Printf( PRINT_ALL, "MT(d) " ); - } else { - ri.Printf( PRINT_ALL, " " ); - } - if ( shader->explicitlyDefined ) { - ri.Printf( PRINT_ALL, "E " ); - } else { - ri.Printf( PRINT_ALL, " " ); - } - - if ( shader->optimalStageIteratorFunc == RB_StageIteratorGeneric ) { - ri.Printf( PRINT_ALL, "gen " ); - } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorSky ) { - ri.Printf( PRINT_ALL, "sky " ); - } else { - ri.Printf( PRINT_ALL, " " ); - } - - if ( shader->defaultShader ) { - ri.Printf (PRINT_ALL, ": %s (DEFAULTED)\n", shader->name); - } else { - ri.Printf (PRINT_ALL, ": %s\n", shader->name); - } - count++; - } - ri.Printf (PRINT_ALL, "%i total shaders\n", count); - ri.Printf (PRINT_ALL, "------------------\n"); -} - -/* -==================== -ScanAndLoadShaderFiles - -Finds and loads all .shader files, combining them into -a single large text block that can be scanned for shader names -===================== -*/ -#define MAX_SHADER_FILES 4096 -static void ScanAndLoadShaderFiles( void ) -{ - char **shaderFiles; - char *buffers[MAX_SHADER_FILES]; - char *p; - int numShaderFiles; - int i; - char *oldp, *token, *hashMem, *textEnd; - int shaderTextHashTableSizes[MAX_SHADERTEXT_HASH], hash, size; - - long sum = 0, summand; - // scan for shader files - shaderFiles = ri.FS_ListFiles( "scripts", ".shader", &numShaderFiles ); - - if ( !shaderFiles || !numShaderFiles ) - { - ri.Printf( PRINT_WARNING, "WARNING: no shader files found\n" ); - return; - } - - if ( numShaderFiles > MAX_SHADER_FILES ) { - numShaderFiles = MAX_SHADER_FILES; - } - - // load and parse shader files - for ( i = 0; i < numShaderFiles; i++ ) - { - char filename[MAX_QPATH]; - - // look for a .mtr file first - { - char *ext; - Com_sprintf( filename, sizeof( filename ), "scripts/%s", shaderFiles[i] ); - if ( (ext = strrchr(filename, '.')) ) - { - strcpy(ext, ".mtr"); - } - - if ( ri.FS_ReadFile( filename, NULL ) <= 0 ) - { - Com_sprintf( filename, sizeof( filename ), "scripts/%s", shaderFiles[i] ); - } - } - - ri.Printf( PRINT_DEVELOPER, "...loading '%s'\n", filename ); - summand = ri.FS_ReadFile( filename, (void **)&buffers[i] ); - - if ( !buffers[i] ) - ri.Error( ERR_DROP, "Couldn't load %s", filename ); - - // Do a simple check on the shader structure in that file to make sure one bad shader file cannot fuck up all other shaders. - p = buffers[i]; - while(1) - { - token = COM_ParseExt(&p, qtrue); - - if(!*token) - break; - - oldp = p; - - token = COM_ParseExt(&p, qtrue); - if(token[0] != '{' && token[1] != '\0') - { - ri.Printf(PRINT_WARNING, "WARNING: Bad shader file %s has incorrect syntax.\n", filename); - ri.FS_FreeFile(buffers[i]); - buffers[i] = NULL; - break; - } - - SkipBracedSection(&oldp); - p = oldp; - } - - - if (buffers[i]) - sum += summand; - } - - // build single large buffer - s_shaderText = ri.Hunk_Alloc( sum + numShaderFiles*2, h_low ); - s_shaderText[ 0 ] = '\0'; - textEnd = s_shaderText; - - // free in reverse order, so the temp files are all dumped - for ( i = numShaderFiles - 1; i >= 0 ; i-- ) - { - if ( !buffers[i] ) - continue; - - strcat( textEnd, buffers[i] ); - strcat( textEnd, "\n" ); - textEnd += strlen( textEnd ); - ri.FS_FreeFile( buffers[i] ); - } - - COM_Compress( s_shaderText ); - - // free up memory - ri.FS_FreeFileList( shaderFiles ); - - Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); - size = 0; - - p = s_shaderText; - // look for shader names - while ( 1 ) { - token = COM_ParseExt( &p, qtrue ); - if ( token[0] == 0 ) { - break; - } - - hash = generateHashValue(token, MAX_SHADERTEXT_HASH); - shaderTextHashTableSizes[hash]++; - size++; - SkipBracedSection(&p); - } - - size += MAX_SHADERTEXT_HASH; - - hashMem = ri.Hunk_Alloc( size * sizeof(char *), h_low ); - - for (i = 0; i < MAX_SHADERTEXT_HASH; i++) { - shaderTextHashTable[i] = (char **) hashMem; - hashMem = ((char *) hashMem) + ((shaderTextHashTableSizes[i] + 1) * sizeof(char *)); - } - - Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); - - p = s_shaderText; - // look for shader names - while ( 1 ) { - oldp = p; - token = COM_ParseExt( &p, qtrue ); - if ( token[0] == 0 ) { - break; - } - - hash = generateHashValue(token, MAX_SHADERTEXT_HASH); - shaderTextHashTable[hash][shaderTextHashTableSizes[hash]++] = oldp; - - SkipBracedSection(&p); - } - - return; - -} - - -/* -==================== -CreateInternalShaders -==================== -*/ -static void CreateInternalShaders( void ) { - tr.numShaders = 0; - - // init the default shader - Com_Memset( &shader, 0, sizeof( shader ) ); - Com_Memset( &stages, 0, sizeof( stages ) ); - - Q_strncpyz( shader.name, "<default>", sizeof( shader.name ) ); - - shader.lightmapIndex = LIGHTMAP_NONE; - stages[0].bundle[0].image[0] = tr.defaultImage; - stages[0].active = qtrue; - stages[0].stateBits = GLS_DEFAULT; - tr.defaultShader = FinishShader(); - - // shadow shader is just a marker - Q_strncpyz( shader.name, "<stencil shadow>", sizeof( shader.name ) ); - shader.sort = SS_STENCIL_SHADOW; - tr.shadowShader = FinishShader(); -} - -static void CreateExternalShaders( void ) { - tr.projectionShadowShader = R_FindShader( "projectionShadow", LIGHTMAP_NONE, qtrue ); - tr.flareShader = R_FindShader( "flareShader", LIGHTMAP_NONE, qtrue ); - - // Hack to make fogging work correctly on flares. Fog colors are calculated - // in tr_flare.c already. - if(!tr.flareShader->defaultShader) - { - int index; - - for(index = 0; index < tr.flareShader->numUnfoggedPasses; index++) - { - tr.flareShader->stages[index]->adjustColorsForFog = ACFF_NONE; - tr.flareShader->stages[index]->stateBits |= GLS_DEPTHTEST_DISABLE; - } - } - - tr.sunShader = R_FindShader( "sun", LIGHTMAP_NONE, qtrue ); - - tr.sunFlareShader = R_FindShader( "gfx/2d/sunflare", LIGHTMAP_NONE, qtrue); - - // HACK: if sunflare is missing, make one using the flare image or dlight image - if (tr.sunFlareShader->defaultShader) - { - image_t *image; - - if (!tr.flareShader->defaultShader && tr.flareShader->stages[0] && tr.flareShader->stages[0]->bundle[0].image[0]) - image = tr.flareShader->stages[0]->bundle[0].image[0]; - else - image = tr.dlightImage; - - Com_Memset( &shader, 0, sizeof( shader ) ); - Com_Memset( &stages, 0, sizeof( stages ) ); - - Q_strncpyz( shader.name, "gfx/2d/sunflare", sizeof( shader.name ) ); - - shader.lightmapIndex = LIGHTMAP_NONE; - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].stateBits = GLS_DEFAULT; - tr.sunFlareShader = FinishShader(); - } - -} - -/* -================== -R_InitShaders -================== -*/ -void R_InitShaders( void ) { - ri.Printf( PRINT_ALL, "Initializing Shaders\n" ); - - Com_Memset(hashTable, 0, sizeof(hashTable)); - - CreateInternalShaders(); - - ScanAndLoadShaderFiles(); - - CreateExternalShaders(); -} |