diff --git a/changelog.txt b/changelog.txt index e4d53c3..16501ad 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,6 +4,10 @@ See the end of this file for known issues. DD Mmm 20 - 1.53 +add: /imageinfo prints where the image was loaded from and shaders that reference it + +add: /shadermixeduse prints all images referenced in shaders with conflicting global directives + add: cl_demoPlayer <0|1> (default: 1) selects the demo playback system to use cl_demoPlayer 0 = always uses the original demo player cl_demoPlayer 1 = uses the new demo player with time rewind support when the mod supports it diff --git a/code/renderer/tr_image.cpp b/code/renderer/tr_image.cpp index 94b5de8..b05c6cb 100644 --- a/code/renderer/tr_image.cpp +++ b/code/renderer/tr_image.cpp @@ -134,6 +134,45 @@ void R_ImageList_f( void ) } +void R_ImageInfo_f() +{ + if ( Cmd_Argc() <= 1 ) { + ri.Printf( PRINT_ALL, "usage: %s \n", Cmd_Argv(0) ); + return; + } + + const char* const name = Cmd_Argv(1); + const image_t* image = NULL; + for ( int i = 0; i < tr.numImages; i++ ) { + if ( !Q_stricmp( tr.images[i]->name, name ) ) { + image = tr.images[i]; + break; + } + } + + char pakName[256]; + if ( FS_GetPakPath( pakName, sizeof( pakName ), image->pakChecksum ) ) { + ri.Printf( PRINT_ALL, "%s/%s\n", pakName, image->name ); + } + + ri.Printf( PRINT_ALL, "Used in these shaders:\n" ); + for ( int s = 0; s < image->numShaders; ++s ) { + const shader_t* const shader = tr.imageShaders[image->firstShaderIndex + s]; + const qbool nmmS = shader->imgflags & IMG_NOMIPMAP; + const qbool npmS = shader->imgflags & IMG_NOPICMIP; + ri.Printf( PRINT_ALL, "%s %s %s\n", + nmmS ? "NMM" : " ", + npmS ? "NPM" : " ", + shader->name ); + + const char* const shaderPath = R_GetShaderPath( shader ); + if ( shaderPath != NULL ) { + ri.Printf( PRINT_ALL, " -> %s\n", shaderPath ); + } + } +} + + /////////////////////////////////////////////////////////////// @@ -726,7 +765,7 @@ static const imageLoader_t imageLoaders[] = { }; -static void R_LoadImage( const char* name, byte** pic, int* w, int* h, textureFormat_t* format ) +static void R_LoadImage( int* pakChecksum, const char* name, byte** pic, int* w, int* h, textureFormat_t* format ) { *pic = NULL; *w = 0; @@ -736,7 +775,7 @@ static void R_LoadImage( const char* name, byte** pic, int* w, int* h, textureFo char altName[MAX_QPATH]; byte* buffer; - int bufferSize = ri.FS_ReadFile( name, (void**)&buffer ); + int bufferSize = ri.FS_ReadFilePak( name, (void**)&buffer, pakChecksum ); if ( buffer == NULL ) { const char* lastDot = strrchr( name, '.' ); const int nameLength = lastDot != NULL ? (int)(lastDot - name) : (int)strlen( name ); @@ -747,7 +786,7 @@ static void R_LoadImage( const char* name, byte** pic, int* w, int* h, textureFo memcpy( altName, name, nameLength ); altName[nameLength] = '\0'; Q_strcat( altName, sizeof(altName), imageLoaders[i].extension ); - bufferSize = ri.FS_ReadFile( altName, (void**)&buffer ); + bufferSize = ri.FS_ReadFilePak( altName, (void**)&buffer, pakChecksum ); if ( buffer != NULL ) { name = altName; break; @@ -786,7 +825,7 @@ static const forcedLoadImage_t g_forcedLoadImages[] = { // finds or loads the given image - returns NULL if it fails, not a default image -const image_t* R_FindImageFile( const char* name, int flags, textureWrap_t glWrapClampMode ) +image_t* R_FindImageFile( const char* name, int flags, textureWrap_t glWrapClampMode ) { if ( !name ) return NULL; @@ -837,14 +876,15 @@ const image_t* R_FindImageFile( const char* name, int flags, textureWrap_t glWra // load the pic from disk // byte* pic; - int width, height; + int width, height, pakChecksum; textureFormat_t format; - R_LoadImage( name, &pic, &width, &height, &format ); + R_LoadImage( &pakChecksum, name, &pic, &width, &height, &format ); if ( !pic ) return NULL; image_t* const image = R_CreateImage( name, pic, width, height, format, flags, glWrapClampMode ); + image->pakChecksum = pakChecksum; ri.Free( pic ); return image; } @@ -1128,3 +1168,25 @@ void R_SkinList_f( void ) ri.Printf( PRINT_ALL, "%i skins found\n", skinCount ); ri.Printf( PRINT_ALL, "------------------\n" ); } + + +void R_AddImageShader( image_t* image, shader_t* shader ) +{ + assert( shader != NULL ); + if (image == NULL || shader == NULL) + return; + + assert( tr.numImageShaders < ARRAY_LEN( tr.imageShaders ) ); + if (tr.numImageShaders >= ARRAY_LEN( tr.imageShaders )) + return; + + // we consider index 0 to be invalid + if (tr.numImageShaders == 0) + tr.numImageShaders++; + + tr.imageShaders[tr.numImageShaders] = shader; + if (image->firstShaderIndex == 0) + image->firstShaderIndex = tr.numImageShaders; + image->numShaders++; + tr.numImageShaders++; +} diff --git a/code/renderer/tr_init.cpp b/code/renderer/tr_init.cpp index 57c6fa6..3c5786b 100644 --- a/code/renderer/tr_init.cpp +++ b/code/renderer/tr_init.cpp @@ -339,8 +339,10 @@ static const cmdTableItem_t r_cmds[] = { { "gfxinfo", GfxInfo_f, NULL, "prints display mode info" }, { "imagelist", R_ImageList_f, NULL, "prints loaded images" }, + { "imageinfo", R_ImageInfo_f, NULL, "prints info for a specific image" }, { "shaderlist", R_ShaderList_f, NULL, "prints loaded shaders" }, { "shaderinfo", R_ShaderInfo_f, R_CompleteShaderName_f, "prints info for a specific shader" }, + { "shadermixeduse", R_MixedUse_f, NULL, "prints all mixed use issues" }, { "skinlist", R_SkinList_f, NULL, "prints loaded skins" }, { "modellist", R_Modellist_f, NULL, "prints loaded models" }, { "screenshot", R_ScreenShotTGA_f, NULL, "takes a TARGA (.tga) screenshot" }, diff --git a/code/renderer/tr_local.h b/code/renderer/tr_local.h index e3744f6..668f0b2 100644 --- a/code/renderer/tr_local.h +++ b/code/renderer/tr_local.h @@ -87,6 +87,8 @@ typedef unsigned int textureHandle_t; // what this number actually means is up t #define MAX_TEXTURE_SIZE 2048 +struct shader_t; + struct image_t { image_t* next; @@ -98,6 +100,12 @@ struct image_t { textureWrap_t wrapClampMode; char name[MAX_QPATH]; // game path, including extension + + // used to index into tr.imageShaders + int firstShaderIndex; + int numShaders; + + int pakChecksum; }; @@ -272,7 +280,7 @@ typedef struct { #define MAX_IMAGE_ANIMATIONS 8 typedef struct { - const image_t* image[MAX_IMAGE_ANIMATIONS]; + image_t* image[MAX_IMAGE_ANIMATIONS]; int numImageAnimations; double imageAnimationSpeed; qbool isVideoMap; // shit code - no reason to have both of these @@ -344,7 +352,7 @@ typedef enum { typedef struct { float cloudHeight; - const image_t *outerbox[6], *innerbox[6]; + image_t *outerbox[6], *innerbox[6]; } skyParms_t; typedef struct { @@ -904,6 +912,9 @@ typedef struct { int numImages; image_t* images[MAX_DRAWIMAGES]; + shader_t* imageShaders[2048]; + int numImageShaders; + // shader indexes from other modules will be looked up in tr.shaders[] // shader indexes from drawsurfs will be looked up in sortedShaders[] // lower indexed sortedShaders must be rendered first (opaque surfaces before translucent) @@ -1150,15 +1161,18 @@ void R_ConfigureVideoMode( int desktopWidth, int desktopHeight ); // writes to g #define IMG_EXTLMATLAS 0x0010 // external lightmap atlas => no mip-mapping, no anisotropic #define IMG_NOAF 0x0020 // never enable anisotropic filtering -const image_t* R_FindImageFile( const char* name, int flags, textureWrap_t glWrapClampMode ); +image_t* R_FindImageFile( const char* name, int flags, textureWrap_t glWrapClampMode ); image_t* R_CreateImage( const char* name, byte* pic, int width, int height, textureFormat_t format, int flags, textureWrap_t wrapClampMode ); void R_UploadLightmapTile( image_t* image, byte* pic, int x, int y, int width, int height ); void R_SetColorMappings(); void R_ImageList_f( void ); +void R_ImageInfo_f( void ); void R_SkinList_f( void ); +void R_AddImageShader( image_t* image, shader_t* shader ); + void R_InitFogTable(); float R_FogFactor( float s, float t ); void R_InitImages(); @@ -1181,14 +1195,16 @@ void R_MipMap( byte** outD, const byte* inD, int inW, int inH, textureWrap_t tw // qhandle_t RE_RegisterShader( const char* name ); qhandle_t RE_RegisterShaderNoMip( const char* name ); -qhandle_t RE_RegisterShaderFromImage( const char* name, const image_t* image ); +qhandle_t RE_RegisterShaderFromImage( const char* name, image_t* image ); shader_t *R_FindShader( const char *name, int lightmapIndex, qbool mipRawImage ); const shader_t* R_GetShaderByHandle( qhandle_t hShader ); void R_InitShaders(); void R_ShaderList_f( void ); void R_ShaderInfo_f(); +void R_MixedUse_f(); void R_CompleteShaderName_f( int startArg, int compArg ); +const char* R_GetShaderPath( const shader_t* shader ); /* ==================================================================== diff --git a/code/renderer/tr_shader.cpp b/code/renderer/tr_shader.cpp index edb56aa..cd47350 100644 --- a/code/renderer/tr_shader.cpp +++ b/code/renderer/tr_shader.cpp @@ -2319,6 +2319,28 @@ static qbool IsReplaceBlendMode( unsigned int stateBits ) } +static void BuildPerImageShaderList( shader_t* newShader ) +{ + if ( newShader->isSky ) { + image_t** const boxes[2] = { (image_t**)newShader->sky.outerbox, (image_t**)newShader->sky.innerbox }; + for ( int b = 0; b < 2; ++b ) { + image_t** const images = boxes[b]; + for ( int i = 0; i < 6; ++i ) { + R_AddImageShader( images[i], newShader ); + } + } + } else { + for ( int s = 0; s < newShader->numStages; ++s ) { + shaderStage_t* const newStage = newShader->stages[s]; + const int numImages = max( newStage->bundle.numImageAnimations, 1 ); + for ( int i = 0; i < numImages; ++i ) { + R_AddImageShader( newStage->bundle.image[i], newShader ); + } + } + } +} + + // returns a freshly allocated shader with info // copied from the current global working shader @@ -2536,7 +2558,11 @@ static shader_t* FinishShader() ProcessGreyscale(); - return GeneratePermanentShader(); + shader_t* const newShader = GeneratePermanentShader(); + + BuildPerImageShaderList( newShader ); + + return newShader; } @@ -2669,7 +2695,7 @@ shader_t* R_FindShader( const char *name, int lightmapIndex, qbool mipRawImage ) Q_strncpyz( fileName, name, sizeof( fileName ) ); COM_DefaultExtension( fileName, sizeof( fileName ), ".tga" ); - const image_t* image; + image_t* image; if (mipRawImage) image = R_FindImageFile( fileName, 0, TW_REPEAT ); else @@ -2727,7 +2753,7 @@ shader_t* R_FindShader( const char *name, int lightmapIndex, qbool mipRawImage ) // shaders registered from raw data should be "anonymous" and unsearchable // because they don't have the supercession concept of "real" shaders -qhandle_t RE_RegisterShaderFromImage( const char* name, const image_t* image ) +qhandle_t RE_RegisterShaderFromImage( const char* name, image_t* image ) { const shader_t* sh; @@ -2925,17 +2951,9 @@ void R_ShaderInfo_f() return; } - const int fileIndex = shader->fileIndex; - if ( fileIndex >= 0 && fileIndex < s_numShaderFiles ) { - const int nameOffset = s_shaderFileNameOffsets[fileIndex]; - const char* const fileName = s_shaderFileNames + nameOffset; - ri.Printf( PRINT_ALL, "File: scripts/%s\n", fileName ); - - char pakName[256]; - const int pakChecksum = s_shaderPakChecksums[fileIndex]; - if( FS_GetPakPath( pakName, sizeof(pakName), pakChecksum ) ) { - ri.Printf( PRINT_ALL, "Pak : %s\n", pakName ); - } + const char* const shaderPath = R_GetShaderPath( shader ); + if ( shaderPath != NULL ) { + ri.Printf( PRINT_ALL, "%s\n", shaderPath ); } if ( Q_stricmp( Cmd_Argv(2), "code" ) ) { @@ -2970,6 +2988,51 @@ void R_ShaderInfo_f() } +void R_MixedUse_f() +{ + for ( int i = 0; i < tr.numImages; ++i ) { + image_t* const image = tr.images[i]; + if ( image->numShaders < 2 || !Q_stricmp(image->name, "*white") ) { + continue; + } + + const qbool nmm0 = tr.imageShaders[image->firstShaderIndex]->imgflags & IMG_NOMIPMAP; + const qbool npm0 = tr.imageShaders[image->firstShaderIndex]->imgflags & IMG_NOPICMIP; + + qbool mixed = qfalse; + for ( int s = 1; s < image->numShaders; ++s ) { + const shader_t* const shader = tr.imageShaders[image->firstShaderIndex + s]; + const qbool nmmS = shader->imgflags & IMG_NOMIPMAP; + const qbool npmS = shader->imgflags & IMG_NOPICMIP; + if ( nmmS != nmm0 || npmS != npm0 ) { + mixed = qtrue; + break; + } + } + + if ( !mixed ) { + continue; + } + + ri.Printf( PRINT_ALL, "^5%s:\n", image->name ); + for ( int s = 0; s < image->numShaders; ++s ) { + const shader_t* const shader = tr.imageShaders[image->firstShaderIndex + s]; + const qbool nmmS = shader->imgflags & IMG_NOMIPMAP; + const qbool npmS = shader->imgflags & IMG_NOPICMIP; + ri.Printf( PRINT_ALL, "%s %s %s\n", + nmmS ? "NMM" : " ", + npmS ? "NPM" : " ", + shader->name); + + const char* const shaderPath = R_GetShaderPath( shader ); + if ( shaderPath != NULL ) { + ri.Printf( PRINT_ALL, " -> %s\n", shaderPath ); + } + } + } +} + + // finds and loads all .shader files, combining them into // a single large text block that can be scanned for shader names // note that this does a lot of things very badly, e.g. still loads superceded shaders @@ -3012,7 +3075,7 @@ static void ScanAndLoadShaderFiles() ri.Error( ERR_DROP, "Couldn't load %s", filename ); len[i] = COM_Compress( buffers[i] ); sum += len[i]; - sumNames += strlen( shaderFileNames[i] ); + sumNames += strlen( shaderFileNames[i] ) + 1; } s_shaderText = RI_New( sum + numShaderFiles + 1 ); @@ -3112,3 +3175,23 @@ void R_InitShaders() ScanAndLoadShaderFiles(); } + + +const char* R_GetShaderPath( const shader_t* shader ) +{ + const int fileIndex = shader->fileIndex; + if ( fileIndex < 0 || fileIndex >= s_numShaderFiles ) { + return NULL; + } + + const int nameOffset = s_shaderFileNameOffsets[fileIndex]; + const char* const fileName = s_shaderFileNames + nameOffset; + + char pakName[256]; + const int pakChecksum = s_shaderPakChecksums[fileIndex]; + if( FS_GetPakPath( pakName, sizeof(pakName), pakChecksum ) ) { + return va( "%s/scripts/%s", pakName, fileName ); + } + + return va( "scripts/%s", fileName ); +} diff --git a/code/renderer/tr_sky.cpp b/code/renderer/tr_sky.cpp index 2ac5407..5ebc75f 100644 --- a/code/renderer/tr_sky.cpp +++ b/code/renderer/tr_sky.cpp @@ -494,7 +494,7 @@ void R_InitSkyTexCoords( float heightCloud ) static void DrawSkyBox() { - const image_t*const* skyImages = &tess.shader->sky.outerbox[0]; + image_t* const* skyImages = &tess.shader->sky.outerbox[0]; RB_PushSingleStageShader( GLS_DEPTHMASK_TRUE, CT_TWO_SIDED ); shaderStage_t* const stage = tess.shader->stages[0]; stage->rgbGen = CGEN_IDENTITY_LIGHTING;