diff --git a/src/client/cl_cgame.cpp b/src/client/cl_cgame.cpp index d9fa2da0..a59a550b 100644 --- a/src/client/cl_cgame.cpp +++ b/src/client/cl_cgame.cpp @@ -452,6 +452,10 @@ void CL_ConfigstringModified( void ) { CL_SystemInfoChanged(); } + // Shader Remaps + if ( index == cls.cs_remaps ) { + CL_ShaderStateChanged(); + } } diff --git a/src/client/cl_main.cpp b/src/client/cl_main.cpp index 0fd0b60a..c3ec1716 100644 --- a/src/client/cl_main.cpp +++ b/src/client/cl_main.cpp @@ -1324,6 +1324,9 @@ void CL_Vid_Restart_f( void ) { // send pure checksums CL_SendPureChecksums(); } + + // Reapply mvremaps to override classic ones + CL_ShaderStateChanged(); } /* @@ -2788,6 +2791,50 @@ void CL_SetForcePowers_f( void ) { #define G2_VERT_SPACE_CLIENT_SIZE 256 #endif + +/* +================= +MV_UpdateClFlags + +Called by CL_Init. Updates the mv_clFlags accoding to the current settings + +At the time of initial implementation there is only two clFlags, one which is always active and one which could be determined at compile time, so the function is not required, yet. +However in future versions users might be able to disable some of the features, so the clFlags need to be adjusted in such cases +================= +*/ +void MV_UpdateClFlags( void ) +{ + // mv_clFlags - Used to inform the server about available jk2mv clientside features + static cvar_t *mv_clFlags; + char *value; + int intValue = 0; + + // Check for the features and determine the flags + if ( MV_APILEVEL >= 4 ) intValue |= MV_CLFLAG_SUBMODEL_BYPASS; + intValue |= MV_CLFLAG_ADVANCED_REMAPS; + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // !!! Forks of JK2MV should NOT modify the mv_clFlags !!! + // !!! Removal, replacement or adding of new flags might lead to incompatibilities !!! + // !!! Forks should define their own userinfo cvar instead of modifying this !!! + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + // If the current clFlags match the intValue we can return + if ( mv_clFlags && mv_clFlags->integer == intValue ) return; + + // We need a string when registering/setting the cvar + value = va( "%i", intValue ); + + if ( !mv_clFlags ) + { // Register the cvar as rom, internal and userinfo for the server to see, but without users manually changing it + mv_clFlags = Cvar_Get( "mv_clFlags", value, CVAR_ROM|CVAR_INTERNAL|CVAR_USERINFO ); + } + else + { // Update the cvar + Cvar_Set( "mv_clFlags", value ); + } +} + /* ==================== CL_Init @@ -2917,6 +2964,9 @@ void CL_Init( void ) { cl_downloadTime = Cvar_Get("cl_downloadTime", "", CVAR_INTERNAL); cl_downloadProtocol = Cvar_Get("cl_downloadProtocol", "", CVAR_INTERNAL); + // Update cl flags userinfo + MV_UpdateClFlags(); + // // register our commands // @@ -4058,3 +4108,153 @@ void CL_GetVMGLConfig(vmglconfig_t *vmglconfig) { vmglconfig->stereoEnabled = cls.glconfig.stereoEnabled; vmglconfig->smpActive = cls.glconfig.smpActive; } + +void CL_ShaderStateChanged( void ) { + // Originally copied from CG_ShaderStateChanged, but rewritten to avoid issues of the original implementation and to + // support settings + char originalShader[MAX_QPATH]; + char newShader[MAX_QPATH]; + char settings[32]; + char *timeOffset; + char *lightmapMode; + char *styleMode; + + shaderRemapLightmapType_t lightmapModeValue; + shaderRemapStyleType_t styleModeValue; + + const char *curPos; + const char *endPos; + + int length; + + // Make sure it's a valid configstring index + if ( cls.cs_remaps < CS_SYSTEMINFO || cls.cs_remaps >= MAX_CONFIGSTRINGS ) return; + + // Clear any active remaps. We are going to reapply those that should stay below when parsing the string anyway. + re.RemoveAdvancedRemaps(); + + curPos = cl.gameState.stringData + cl.gameState.stringOffsets[ cls.cs_remaps ]; + endPos = curPos + strlen( curPos ); + + // Handling of these is really ugly. I originally wanted to handle them like the base game/cgame modules do, but + // those are prone to injections. Summary of delimeter issues: + // - base uses '=', ':' an '@', all of them can be used in shader names + // - .shader files don't seem to support spaces in shader names, but texture paths support spaces + // - .shader files allow most other characters, including backspace '\' + // -> using a delimeter is not a reliable option + + // The next best thing seemed to be to give the length of the shader before its name to allow all characters to be + // used. As I don't want to waste any space (configstrings containing shader paths are wasteful as it is), I decided + // to encode the length in a single byte (MAX_QPATH is 80, so a single byte can easily hold the length). However the + // netcode and the handling of quotes lead to other issues: + // - 34 maps to 32, thus 32 could mean either of these numbers while 34 doesn't exist + // - 37 and >127 map to 46, thus 37 doesn't exist and 46 could mean 37, 46 or >127 + // -> the biggest amount of consecutive numbers without multiple meanings spans from 47 to 127; as MAX_QPATH is 80 + // this should fix exactly + // -> could also use the range from 38 to 127, because - when isolating the range - there are no duplicates within + // it; this would allow for a size of up to 89, which is unlikely to be useful if MAX_QPATH ever gets increased + // -> using 47 to 127 + + // Example input: "6console4clear929300;p;p;": + // - "6" -> ascii 54 -> 54 - 47 = 7 -> the next 7 characters are the source shader name + // - "console" -> source shader (7 characters) + // - "4" -> ascii 52 -> 52 - 47 = 5 -> the next 5 characters are the destination shader name + // - "clear" -> destination shader (5 characters) + // - "9" -> ascii 57 -> 57 - 47 = 10 -> the next 10 characters are the options + // - "29300;p;p;" + // - "29300" -> timeOffset + // - "p" -> lightmapMode + // - "p" -> styleMode + + // Parse configstring + while ( curPos < endPos ) + { + // Read original shader + length = *(curPos++); + length -= 47; // Length is increased by 47 to workaround netchan limits + if ( length < 1 ) + { + Com_DPrintf( "CL_ShaderStateChanged: invalid length encoding for source shader\n" ); + break; + } + Q_strncpyz( originalShader, curPos, MIN((unsigned int)length+1, sizeof(originalShader)) ); + curPos += length; + + // Read new shader + if ( curPos >= endPos ) break; + length = *(curPos++); + length -= 47; // Length is increased by 47 to workaround netchan limits + if ( length < 1 ) + { + Com_DPrintf( "CL_ShaderStateChanged: invalid length encoding for destination shader\n" ); + break; + } + Q_strncpyz( newShader, curPos, MIN((unsigned int)length+1, sizeof(newShader)) ); + curPos += length; + + // Read settings + if ( curPos >= endPos ) break; + length = *(curPos++); + length -= 47; // Length is increased by 47 to workaround netchan limits + if ( length < 1 ) + { + Com_DPrintf( "CL_ShaderStateChanged: invalid length encoding for settings\n" ); + break; + } + Q_strncpyz( settings, curPos, MIN((unsigned int)length+1, sizeof(settings)) ); + curPos += length; + + // Get values from settings + timeOffset = strtok( settings, ";" ); + lightmapMode = strtok( NULL, ";" ); + styleMode = strtok( NULL, ";" ); + + // Pick lightmap value + if ( lightmapMode && *lightmapMode ) + { + switch( *lightmapMode ) + { + case 'p': // preserve + lightmapModeValue = SHADERREMAP_LIGHTMAP_PRESERVE; + break; + case 'n': // none + lightmapModeValue = SHADERREMAP_LIGHTMAP_NONE; + break; + case 'f': // fullbright + lightmapModeValue = SHADERREMAP_LIGHTMAP_FULLBRIGHT; + break; + case 'v': // vertex + lightmapModeValue = SHADERREMAP_LIGHTMAP_VERTEX; + break; + case '2': // 2d + lightmapModeValue = SHADERREMAP_LIGHTMAP_2D; + break; + default: // fallback to preserve + lightmapModeValue = SHADERREMAP_LIGHTMAP_PRESERVE; + break; + } + } + else lightmapModeValue = SHADERREMAP_LIGHTMAP_PRESERVE; // fallback to preserve + + // Pick style value + if ( styleMode && *styleMode ) + { + switch( *styleMode ) + { + case 'p': // preserve + styleModeValue = SHADERREMAP_STYLE_PRESERVE; + break; + case 'd': // default style + styleModeValue = SHADERREMAP_STYLE_DEFAULT; + break; + default: // fallback to preserve + styleModeValue = SHADERREMAP_STYLE_PRESERVE; + break; + } + } + else styleModeValue = SHADERREMAP_STYLE_PRESERVE; // fallback to preserve + + // Apply remap + re.RemapShaderAdvanced( originalShader, newShader, timeOffset ? atoi(timeOffset) : 0, lightmapModeValue, styleModeValue ); + } +} diff --git a/src/client/cl_parse.cpp b/src/client/cl_parse.cpp index 20f54489..5997ae46 100644 --- a/src/client/cl_parse.cpp +++ b/src/client/cl_parse.cpp @@ -319,6 +319,7 @@ void CL_SystemInfoChanged( void ) { char key[BIG_INFO_KEY]; char value[BIG_INFO_VALUE]; qboolean gameSet; + int old_cs_remaps = cls.cs_remaps; systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ]; cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) ); @@ -327,6 +328,12 @@ void CL_SystemInfoChanged( void ) { t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" ); FS_PureServerSetReferencedPaks( s, t ); + cls.cs_remaps = atoi( Info_ValueForKey(systemInfo, "mv_cs_remaps") ); + if ( cls.cs_remaps != old_cs_remaps ) { + // If the configstring changed remove any active advanced remaps + re.RemoveAdvancedRemaps(); + } + // don't set any other vars when playing a demo if ( clc.demoplaying ) { return; @@ -485,6 +492,9 @@ void CL_ParseGamestate( msg_t *msg ) { // parse serverId and other cvars CL_SystemInfoChanged(); + // Shader Remaps + CL_ShaderStateChanged(); + // reinitialize the filesystem if the game directory has changed if( FS_ConditionalRestart( clc.checksumFeed ) ) { // don't set to true because we yet have to start downloading diff --git a/src/client/client.h b/src/client/client.h index bb69d89e..f5685d57 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -352,6 +352,8 @@ typedef struct { int fixes; qboolean submodelBypass; + + int cs_remaps; } clientStatic_t; #define CON_TEXTSIZE 131072 // increased in jk2mv @@ -502,6 +504,7 @@ int CL_ServerStatus( const char *serverAddress, char *serverStatusString, int ma void CL_GetVMGLConfig(vmglconfig_t *vmglconfig); int CL_ScaledMilliseconds(void); +void CL_ShaderStateChanged( void ); // // cl_input diff --git a/src/game/bg_public.h b/src/game/bg_public.h index dac4beeb..e6274e85 100644 --- a/src/game/bg_public.h +++ b/src/game/bg_public.h @@ -42,6 +42,10 @@ #define MAX_CLIENT_SCORE_SEND 20 +// mv_clflags - set by the client engine in the mv_clFlags userinfo cvar to inform the server about additional features +#define MV_CLFLAG_SUBMODEL_BYPASS (1) // Indicates the client engine supports bypassing the MAX_SUBMODEL limit if the cgame module and the server support it, too. +#define MV_CLFLAG_ADVANCED_REMAPS (1 << 1) // Indicates the client engine supports the new advanced shader remaps. + // // config strings are a general means of communicating variable length strings // from the server to all connected clients. diff --git a/src/renderer/tr_bsp.cpp b/src/renderer/tr_bsp.cpp index d9396ece..71da12df 100644 --- a/src/renderer/tr_bsp.cpp +++ b/src/renderer/tr_bsp.cpp @@ -386,11 +386,6 @@ static shader_t *ShaderForShaderNum( int shaderNum, const int *lightmapNum, cons shader = R_FindShader( dsh->shader, lightmapNum, styles, qtrue ); - // if the shader had errors, just use default shader - if ( shader->defaultShader ) { - return tr.defaultShader; - } - return shader; } diff --git a/src/renderer/tr_init.cpp b/src/renderer/tr_init.cpp index 8d25235f..be85b3dc 100644 --- a/src/renderer/tr_init.cpp +++ b/src/renderer/tr_init.cpp @@ -191,6 +191,7 @@ cvar_t *r_textureLODBias; cvar_t *r_saberGlow; cvar_t *r_environmentMapping; cvar_t *r_printMissingModels; +cvar_t *r_newRemaps; #ifndef DEDICATED PFNGLACTIVETEXTUREARBPROC qglActiveTextureARB; @@ -1081,6 +1082,9 @@ void R_Register( void ) r_uiFullScreen = ri.Cvar_Get( "r_uifullscreen", "0", 0); r_subdivisions = ri.Cvar_Get("r_subdivisions", "4", CVAR_ARCHIVE | CVAR_GLOBAL | CVAR_LATCH); r_ignoreFastPath = ri.Cvar_Get("r_ignoreFastPath", "1", CVAR_ARCHIVE | CVAR_GLOBAL | CVAR_LATCH); + r_newRemaps = ri.Cvar_Get("r_newRemaps", "0", CVAR_CHEAT ); // Only used for testing. Classic remaps are supposed to remain fullbright, + // because that is how they have been used by maps and serverside mods for + // more than 20 years. Servers can set a configstring for "mvremap" now. // // temporary latched variables that can only change over a restart @@ -1537,6 +1541,8 @@ refexport_t *GetRefAPI ( int apiVersion, refimport_t *rimp ) { re.AnyLanguage_ReadCharFromString = AnyLanguage_ReadCharFromString; re.RemapShader = R_RemapShader; + re.RemapShaderAdvanced = R_RemapShaderAdvanced; + re.RemoveAdvancedRemaps = R_RemoveAdvancedRemaps; re.GetEntityToken = R_GetEntityToken; re.inPVS = R_inPVS; diff --git a/src/renderer/tr_local.h b/src/renderer/tr_local.h index b1d8eda5..cd9e7d1b 100644 --- a/src/renderer/tr_local.h +++ b/src/renderer/tr_local.h @@ -492,7 +492,10 @@ Ghoul2 Insert End // True if this shader has a stage with glow in it (just an optimization). qboolean hasGlow; + qboolean isAdvancedRemap; + struct shader_s *remappedShader; // current shader this one is remapped too + struct shader_s *remappedShaderAdvanced; // current shader from the advanced remaps this one is remapped to struct shader_s *next; } shader_t; @@ -1104,6 +1107,9 @@ typedef struct { shader_t *shaders[MAX_SHADERS]; shader_t *sortedShaders[MAX_SHADERS]; + int numAdvancedRemapShaders; + shader_t *advancedRemapShaders[MAX_SHADERS]; + int numSkins; skin_t *skins[MAX_SKINS]; @@ -1297,6 +1303,7 @@ extern cvar_t *r_textureLODBias; extern cvar_t *r_saberGlow; extern cvar_t *r_environmentMapping; extern cvar_t *r_printMissingModels; +extern cvar_t *r_newRemaps; //==================================================================== float R_NoiseGet4f( float x, float y, float z, double t ); @@ -1453,13 +1460,16 @@ qhandle_t RE_RegisterShader( const char *name ); qhandle_t RE_RegisterShaderNoMip( const char *name ); qhandle_t RE_RegisterShaderFromImage(const char *name, int *lightmapIndex, byte *styles, image_t *image, qboolean mipRawImage); -shader_t *R_FindShader( const char *name, const int *lightmapIndex, const byte *styles, qboolean mipRawImage ); +shader_t *R_FindShader( const char *name, const int *lightmapIndex, const byte *styles, qboolean mipRawImage, qboolean isAdvancedRemap = qfalse ); +shader_t *R_FindAdvancedRemapShader( const char *name, const int *lightmapIndex, const byte *styles, qboolean mipRawImage ); shader_t *R_GetShaderByHandle( qhandle_t hShader ); // shader_t *R_GetShaderByState( int index, int *cycleTime ); shader_t *R_FindShaderByName( const char *name ); void R_InitShaders( void ); void R_ShaderList_f( void ); void R_RemapShader(const char *oldShader, const char *newShader, const char *timeOffset); +void R_RemapShaderAdvanced(const char *shaderName, const char *newShaderName, int timeOffset, shaderRemapLightmapType_t lightmapMode, shaderRemapStyleType_t styleMode); +void R_RemoveAdvancedRemaps( void ); /* ==================================================================== diff --git a/src/renderer/tr_public.h b/src/renderer/tr_public.h index 943c67a7..72253854 100644 --- a/src/renderer/tr_public.h +++ b/src/renderer/tr_public.h @@ -5,6 +5,22 @@ #define REF_API_VERSION 8 + +typedef enum +{ + SHADERREMAP_LIGHTMAP_PRESERVE, + SHADERREMAP_LIGHTMAP_NONE, + SHADERREMAP_LIGHTMAP_FULLBRIGHT, + SHADERREMAP_LIGHTMAP_VERTEX, + SHADERREMAP_LIGHTMAP_2D, +} shaderRemapLightmapType_t; + +typedef enum +{ + SHADERREMAP_STYLE_PRESERVE, + SHADERREMAP_STYLE_DEFAULT, +} shaderRemapStyleType_t; + // // these are the functions exported by the refresh module // @@ -91,6 +107,8 @@ typedef struct { unsigned int (*AnyLanguage_ReadCharFromString)( const char *psText, int *piAdvanceCount, qboolean *pbIsTrailingPunctuation/* = NULL*/ ); void (*RemapShader)(const char *oldShader, const char *newShader, const char *offsetTime); + void (*RemapShaderAdvanced)(const char *oldShader, const char *newShader, int offsetTime, shaderRemapLightmapType_t lightmapMode, shaderRemapStyleType_t styleMode); + void (*RemoveAdvancedRemaps)(void); qboolean (*GetEntityToken)( char *buffer, int size ); qboolean (*inPVS)( const vec3_t p1, const vec3_t p2 ); diff --git a/src/renderer/tr_shade.cpp b/src/renderer/tr_shade.cpp index 7e45c6a3..caadf2fc 100644 --- a/src/renderer/tr_shade.cpp +++ b/src/renderer/tr_shade.cpp @@ -321,7 +321,8 @@ to overflow. */ void RB_BeginSurface( shader_t *shader, int fogNum ) { - shader_t *state = (shader->remappedShader) ? shader->remappedShader : shader; + shader_t *state = (shader->remappedShaderAdvanced ? shader->remappedShaderAdvanced : (shader->remappedShader ? shader->remappedShader : shader)); + if ( state->defaultShader ) state = tr.defaultShader; tess.numIndexes = 0; tess.numVertexes = 0; diff --git a/src/renderer/tr_shader.cpp b/src/renderer/tr_shader.cpp index b84fad1e..25c9e6d3 100644 --- a/src/renderer/tr_shader.cpp +++ b/src/renderer/tr_shader.cpp @@ -103,6 +103,7 @@ static qboolean deferLoad; #define FILE_HASH_SIZE 1024 static shader_t* hashTable[FILE_HASH_SIZE]; +static shader_t* advancedRemapShadersHashTable[FILE_HASH_SIZE]; #define MAX_SHADERTEXT_HASH 2048 static const char **shaderTextHashTable[MAX_SHADERTEXT_HASH]; @@ -205,6 +206,11 @@ void R_RemapShader(const char *shaderName, const char *newShaderName, const char shader_t *sh, *sh2; qhandle_t h; + if ( r_newRemaps->integer ) { + R_RemapShaderAdvanced( shaderName, newShaderName, (int)(atof(timeOffset)*1000), SHADERREMAP_LIGHTMAP_PRESERVE, SHADERREMAP_STYLE_PRESERVE ); + return; + } + sh = R_FindShaderByName( shaderName ); if (sh == NULL || sh == tr.defaultShader) { h = RE_RegisterShaderLightMap(shaderName, lightmapsNone, stylesDefault); @@ -244,6 +250,77 @@ void R_RemapShader(const char *shaderName, const char *newShaderName, const char } } +void R_RemapShaderAdvanced(const char *shaderName, const char *newShaderName, int timeOffset, shaderRemapLightmapType_t lightmapMode, shaderRemapStyleType_t styleMode) { + char strippedName[MAX_QPATH]; + int hash; + shader_t *sh, *sh2 = NULL; + int failed = 0; + const int *lightmapIndex = NULL; + const byte *styles = NULL; + qboolean foundShader = qfalse; + + // Lightmap + if ( lightmapMode == SHADERREMAP_LIGHTMAP_FULLBRIGHT ) { + lightmapIndex = lightmapsFullBright; + } else if ( lightmapMode == SHADERREMAP_LIGHTMAP_NONE ) { + lightmapIndex = lightmapsNone; + } else if ( lightmapMode == SHADERREMAP_LIGHTMAP_2D ) { + lightmapIndex = lightmaps2d; + } else if ( lightmapMode == SHADERREMAP_LIGHTMAP_VERTEX ) { + lightmapIndex = lightmapsVertex; + } + + // Style + if ( styleMode == SHADERREMAP_STYLE_DEFAULT ) { + styles = stylesDefault; + } + + if ( lightmapIndex && lightmapMode != SHADERREMAP_LIGHTMAP_VERTEX ) { + // For fullbright, none and 2d we only need one new shader + sh2 = R_FindAdvancedRemapShader( newShaderName, lightmapIndex, stylesDefault, qtrue ); + + if ( !sh2 || sh2 == tr.defaultShader || sh2->defaultShader ) { + ri.Printf( PRINT_WARNING, "WARNING: R_RemapShaderAdvanced: new shader %s not found\n", newShaderName ); + return; + } + } + + // Remap all the shaders with the given name and copy over the lightmap + styles + 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) { + foundShader = qtrue; + + if ( lightmapMode == SHADERREMAP_LIGHTMAP_PRESERVE || lightmapMode == SHADERREMAP_LIGHTMAP_VERTEX ) { + // When preserving lightmaps we need to use the correct lightmap index (+styles) + sh2 = R_FindAdvancedRemapShader( newShaderName, lightmapIndex ? lightmapIndex : sh->lightmapIndex, styles ? styles : sh->styles, (qboolean)!sh->upload.noMipMaps ); + } + + if ( !sh2 || sh2 == tr.defaultShader || sh2->defaultShader ) { + failed++; + continue; + } + if ( timeOffset ) sh2->timeOffset = timeOffset * 0.001; + + if (sh != sh2) { + sh->remappedShaderAdvanced = sh2; + } else { + sh->remappedShaderAdvanced = NULL; + } + } + } + if ( !foundShader ) ri.Printf( PRINT_WARNING, "WARNING: R_RemapShaderAdvanced: shader %s not found\n", shaderName ); + if ( failed ) ri.Printf( PRINT_WARNING, "WARNING: R_RemapShaderAdvanced: new shader %s not found (x%i)\n", newShaderName, failed ); +} + +void R_RemoveAdvancedRemaps( void ) { + int i; + for ( i = 0; i < tr.numShaders; i++ ) { + tr.shaders[i]->remappedShaderAdvanced = NULL; + } +} + /* =============== ParseVector @@ -2798,7 +2875,7 @@ static shader_t *GeneratePermanentShader( void ) { int i, b; int size, hash; - if ( tr.numShaders == MAX_SHADERS ) { + if ( (!shader.isAdvancedRemap && tr.numShaders == MAX_SHADERS) || (shader.isAdvancedRemap && tr.numAdvancedRemapShaders == MAX_SHADERS) ) { ri.Printf( PRINT_WARNING, "WARNING: GeneratePermanentShader - MAX_SHADERS hit\n"); return tr.defaultShader; } @@ -2835,13 +2912,19 @@ static shader_t *GeneratePermanentShader( void ) { newShader->fogPass = FP_LE; } - tr.shaders[ tr.numShaders ] = newShader; - newShader->index = tr.numShaders; + if ( newShader->isAdvancedRemap ) { + // If the shader has been created by the advanced remaps system we store it in a separate list + tr.advancedRemapShaders[ tr.numAdvancedRemapShaders ] = newShader; + tr.numAdvancedRemapShaders++; + } else { + tr.shaders[ tr.numShaders ] = newShader; + newShader->index = tr.numShaders; - tr.sortedShaders[ tr.numShaders ] = newShader; - newShader->sortedIndex = tr.numShaders; + tr.sortedShaders[ tr.numShaders ] = newShader; + newShader->sortedIndex = tr.numShaders; - tr.numShaders++; + tr.numShaders++; + } for ( i = 0 ; i < newShader->numUnfoggedPasses ; i++ ) { if ( !stages[i].active ) { @@ -2859,11 +2942,18 @@ static shader_t *GeneratePermanentShader( void ) { } } - SortNewShader(); - hash = generateHashValue(newShader->name, FILE_HASH_SIZE); - newShader->next = hashTable[hash]; - hashTable[hash] = newShader; + + if ( newShader->isAdvancedRemap ) { + // We want to keep the advanced remaps separate + newShader->next = advancedRemapShadersHashTable[hash]; + advancedRemapShadersHashTable[hash] = newShader; + } else { + SortNewShader(); + + newShader->next = hashTable[hash]; + hashTable[hash] = newShader; + } return newShader; } @@ -3370,18 +3460,15 @@ inline qboolean IsShader(shader_t *sh, const char *name, const int *lightmapInde return qfalse; } - if (!sh->defaultShader) + for(i=0;ilightmapIndex[i] != lightmapIndex[i]) { - if (sh->lightmapIndex[i] != lightmapIndex[i]) - { - return qfalse; - } - if (sh->styles[i] != styles[i]) - { - return qfalse; - } + return qfalse; + } + if (sh->styles[i] != styles[i]) + { + return qfalse; } } @@ -3437,7 +3524,7 @@ most world construction surfaces. =============== */ -shader_t *R_FindShader( const char *name, const int *lightmapIndex, const byte *styles, qboolean mipRawImage ) +shader_t *R_FindShader( const char *name, const int *lightmapIndex, const byte *styles, qboolean mipRawImage, qboolean isAdvancedRemap ) { char strippedName[MAX_QPATH]; char fileName[MAX_QPATH]; @@ -3466,7 +3553,7 @@ shader_t *R_FindShader( const char *name, const int *lightmapIndex, const byte * // // see if the shader is already loaded // - for (sh = hashTable[hash]; sh; sh = sh->next) { + for (sh = isAdvancedRemap ? advancedRemapShadersHashTable[hash] : 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 @@ -3488,6 +3575,9 @@ shader_t *R_FindShader( const char *name, const int *lightmapIndex, const byte * stages[i].bundle[0].texMods = texMods[i]; } + // Mark shader as advanced remap if it is + shader.isAdvancedRemap = isAdvancedRemap; + // FIXME: set these "need" values apropriately shader.needsNormal = qtrue; shader.needsST1 = qtrue; @@ -3542,7 +3632,8 @@ shader_t *R_FindShader( const char *name, const int *lightmapIndex, const byte * if ( !image ) { ri.Printf( PRINT_DEVELOPER, "Couldn't find image for shader %s\n", name ); shader.defaultShader = qtrue; - return FinishShader(); + //return FinishShader(); + image = tr.defaultImage; } // @@ -3600,6 +3691,10 @@ shader_t *R_FindShader( const char *name, const int *lightmapIndex, const byte * #endif //!DEDICATED } +shader_t *R_FindAdvancedRemapShader( const char *name, const int *lightmapIndex, const byte *styles, qboolean mipRawImage ) { + return R_FindShader( name, lightmapIndex, styles, mipRawImage, qtrue ); +} + #if 0 qhandle_t RE_RegisterShaderFromImage(const char *name, int *lightmapIndex, byte *styles, image_t *image, qboolean mipRawImage) { int i, hash; @@ -3716,15 +3811,6 @@ qhandle_t RE_RegisterShaderLightMap( const char *name, const int *lightmapIndex, sh = R_FindShader( name, lightmapIndex, styles, 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; } @@ -3750,15 +3836,6 @@ qhandle_t RE_RegisterShader( const char *name ) { sh = R_FindShader( name, lightmaps2d, stylesDefault, 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; } @@ -3780,15 +3857,6 @@ qhandle_t RE_RegisterShaderNoMip( const char *name ) { sh = R_FindShader( name, lightmaps2d, stylesDefault, 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; } @@ -4133,6 +4201,7 @@ CreateInternalShaders */ static void CreateInternalShaders( void ) { tr.numShaders = 0; + tr.numAdvancedRemapShaders = 0; // init the default shader Com_Memset( &shader, 0, sizeof( shader ) ); @@ -4260,6 +4329,7 @@ void R_InitShaders( void ) { ri.Printf( PRINT_ALL, "Initializing Shaders\n" ); Com_Memset(hashTable, 0, sizeof(hashTable)); + Com_Memset(advancedRemapShadersHashTable, 0, sizeof(advancedRemapShadersHashTable)); deferLoad = qfalse; diff --git a/src/server/sv_init.cpp b/src/server/sv_init.cpp index 0b6c9a0d..eb4c7e00 100644 --- a/src/server/sv_init.cpp +++ b/src/server/sv_init.cpp @@ -842,6 +842,12 @@ void SV_Init (void) { Cvar_Get ("sv_referencedPaks", "", CVAR_SYSTEMINFO | CVAR_ROM ); Cvar_Get ("sv_referencedPakNames", "", CVAR_SYSTEMINFO | CVAR_ROM ); + // The mv_cs_remaps systeminfo cvar can be used by game module mods to + // announce the index of the configstring used for mvremaps. Registering the + // cvar here is not actually required. We just register it here to ensure + // the right flags are set on it. + Cvar_Get ("mv_cs_remaps", "", CVAR_SYSTEMINFO | CVAR_ROM | CVAR_INTERNAL ); + // server vars sv_rconPassword = Cvar_Get ("rconPassword", "", CVAR_TEMP ); sv_privatePassword = Cvar_Get ("sv_privatePassword", "", CVAR_TEMP );