From 4a9b46465680ac503b061731836c3d167c51dd46 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Sat, 26 Oct 2024 17:13:33 +0200 Subject: [PATCH] tr_image: do not use R_FindImageLoader with cube maps, test multifile cubemaps first (or preview may be loaded as cubemap instead) - do not use R_FindImageLoader with cube maps - test multifile cubemaps first (or preview may be loaded as cubemap instead) - reword image loader lookup code --- src/engine/renderer/tr_image.cpp | 147 ++++++++++++++++++------------ src/engine/renderer/tr_local.h | 2 +- src/engine/renderer/tr_shader.cpp | 2 +- 3 files changed, 90 insertions(+), 61 deletions(-) diff --git a/src/engine/renderer/tr_image.cpp b/src/engine/renderer/tr_image.cpp index 5f788fb853..2ff78066b9 100644 --- a/src/engine/renderer/tr_image.cpp +++ b/src/engine/renderer/tr_image.cpp @@ -1616,15 +1616,17 @@ image_t *R_Create3DImage( const char *name, const byte *pic, int width, int heig return image; } -struct imageExtToLoaderMap_t +using imageLoader_t = void( * )( const char *, unsigned char **, int *, int *, int *, int *, int *, byte ); + +struct imageExtLoader_t { const char *ext; - void ( *ImageLoader )( const char *, unsigned char **, int *, int *, int *, int *, int *, byte ); + imageLoader_t imageLoader; }; // Note that the ordering indicates the order of preference used // when there are multiple images of different formats available -static const imageExtToLoaderMap_t imageLoaders[] = +static const imageExtLoader_t imageLoaders[] = { { "webp", LoadWEBP }, { "png", LoadPNG }, @@ -1636,8 +1638,6 @@ static const imageExtToLoaderMap_t imageLoaders[] = { "ktx", LoadKTX }, }; -static int numImageLoaders = ARRAY_LEN( imageLoaders ); - /* ================= R_FindImageLoader @@ -1646,17 +1646,17 @@ Finds and returns an image loader for a given basename, tells the extra prefix that may be required to load the image. ================= */ -static int R_FindImageLoader( const char *baseName, const char **prefix ) { +static const imageExtLoader_t* R_FindImageLoader( const char *baseName, const char **prefix ) { const FS::PakInfo* bestPak = nullptr; - int i; + const imageExtLoader_t* bestLoader = nullptr; - int bestLoader = -1; *prefix = ""; + // try and find a suitable match using all the image formats supported // prioritize with the pak priority - for ( i = 0; i < numImageLoaders; i++ ) + for ( const imageExtLoader_t &loader : imageLoaders ) { - std::string altName = Str::Format( "%s.%s", baseName, imageLoaders[i].ext ); + std::string altName = Str::Format( "%s.%s", baseName, loader.ext ); const FS::PakInfo* pak = FS::PakPath::LocateFile( altName ); // We found a file and its pak is better than the best pak we have @@ -1665,25 +1665,25 @@ static int R_FindImageLoader( const char *baseName, const char **prefix ) { if ( pak != nullptr && ( bestPak == nullptr || pak < bestPak ) ) { bestPak = pak; - bestLoader = i; + bestLoader = &loader; } if( pak == nullptr ) { if ( FS::HomePath::FileExists( altName ) ) { - bestLoader = i; + bestLoader = &loader; } } // DarkPlaces or Doom3 packages can ship alternative texture path in the form of // dds/.dds - if ( bestPak == nullptr && !Q_stricmp( "dds", imageLoaders[i].ext ) ) + if ( bestPak == nullptr && !Q_stricmp( "dds", loader.ext ) ) { std::string prefixedName = Str::Format( "dds/%s.dds", baseName ); bestPak = FS::PakPath::LocateFile( prefixedName ); if ( bestPak != nullptr ) { *prefix = "dds/"; bestPak = pak; - bestLoader = i; + bestLoader = &loader; } } } @@ -1691,11 +1691,11 @@ static int R_FindImageLoader( const char *baseName, const char **prefix ) { return bestLoader; } -int R_FindImageLoader( const char *baseName ) { +bool R_HasImageLoader( const char *baseName ) { // not used but required by R_FindImageLoader const char *prefix; - return R_FindImageLoader( baseName, &prefix ); + return R_FindImageLoader( baseName, &prefix ) != nullptr; } /* @@ -1722,7 +1722,6 @@ static void R_LoadImage( const char **buffer, byte **pic, int *width, int *heigh return; } - int i; const char *ext; char filename[ MAX_QPATH ]; byte alphaByte; @@ -1738,9 +1737,10 @@ static void R_LoadImage( const char **buffer, byte **pic, int *width, int *heigh if ( *ext ) { // look for the correct loader and use it - for ( i = 0; i < numImageLoaders; i++ ) + bool found = false; + for ( const auto &loader : imageLoaders ) { - if ( !Q_stricmp( ext, imageLoaders[ i ].ext ) ) + if ( !Q_stricmp( ext, loader.ext ) ) { // do not complain on missing file if extension is hardcoded to a wrong one // since file can exist with another extension and it will tested right after @@ -1750,15 +1750,16 @@ static void R_LoadImage( const char **buffer, byte **pic, int *width, int *heigh if ( FS::PakPath::FileExists( filename ) ) { // load - imageLoaders[ i ].ImageLoader( filename, pic, width, height, numLayers, numMips, bits, alphaByte ); + loader.imageLoader( filename, pic, width, height, numLayers, numMips, bits, alphaByte ); } // we still have to break because a loader was found, so we can strip the extension + found = true; break; } } // a loader was found - if ( i < numImageLoaders ) + if ( found ) { if ( *pic != nullptr ) { @@ -1772,21 +1773,21 @@ static void R_LoadImage( const char **buffer, byte **pic, int *width, int *heigh // and it may be possible the file has a dot in its name that was mistakenly // taken as an extension const char *prefix; - int bestLoader = R_FindImageLoader( filename, &prefix ); + const imageExtLoader_t* loader = R_FindImageLoader( filename, &prefix ); - if ( *ext && bestLoader == -1 ) + if ( *ext && loader ) { // if there is no file with such extension // or there is no codec available for this file format COM_StripExtension3( token, filename, sizeof(filename) ); - bestLoader = R_FindImageLoader( filename, &prefix ); + loader = R_FindImageLoader( filename, &prefix ); } - if ( bestLoader >= 0 ) + if ( loader ) { - char *altName = va( "%s%s.%s", prefix, filename, imageLoaders[ bestLoader ].ext ); - imageLoaders[ bestLoader ].ImageLoader( altName, pic, width, height, numLayers, numMips, bits, alphaByte ); + char *altName = va( "%s%s.%s", prefix, filename, loader->ext ); + loader->imageLoader( altName, pic, width, height, numLayers, numMips, bits, alphaByte ); } } @@ -2009,15 +2010,9 @@ static void R_FreeCubePics( byte **pic, int count ) } } -struct cubeMapLoader_t -{ - const char *ext; - void ( *ImageLoader )( const char *, unsigned char **, int *, int *, int *, int *, int *, byte ); -}; - // Note that the ordering indicates the order of preference used // when there are multiple images of different formats available -static const cubeMapLoader_t cubeMapLoaders[] = +static const imageExtLoader_t cubeMapLoaders[] = { { "crn", LoadCRN }, { "ktx", LoadKTX }, @@ -2075,9 +2070,37 @@ struct face_t int height; }; +static const imageExtLoader_t* R_FindCubeImageLoader( const char *baseName ) +{ + const FS::PakInfo* bestPak = nullptr; + const imageExtLoader_t* bestLoader = nullptr; + + for ( const auto &loader : cubeMapLoaders ) + { + std::string altName = Str::Format( "%s.%s", baseName, loader.ext ); + const FS::PakInfo* pak = FS::PakPath::LocateFile( altName ); + + // We found a file and its pak is better than the best pak we have + // this relies on how the filesystem works internally and should be moved + // to a more explicit interface once there is one. (FIXME) + if ( pak != nullptr && ( bestPak == nullptr || pak < bestPak ) ) + { + bestPak = pak; + bestLoader = &loader; + } + + if ( pak == nullptr ) { + if ( FS::HomePath::FileExists( altName ) ) { + bestLoader = &loader; + } + } + } + + return bestLoader; +} + image_t *R_FindCubeImage( const char *imageName, imageParams_t &imageParams ) { - int i, j; image_t *image = nullptr; int width = 0, height = 0, numLayers = 0, numMips = 0; byte *pic[ MAX_TEXTURE_MIPS * MAX_TEXTURE_LAYERS ]; @@ -2102,28 +2125,9 @@ image_t *R_FindCubeImage( const char *imageName, imageParams_t &imageParams ) } } - char cubeMapBaseName[ MAX_QPATH ]; - COM_StripExtension3( buffer, cubeMapBaseName, sizeof( cubeMapBaseName ) ); - - for ( const cubeMapLoader_t &loader : cubeMapLoaders ) - { - std::string cubeMapName = Str::Format( "%s.%s", cubeMapBaseName, loader.ext ); - if( R_FindImageLoader( cubeMapBaseName ) >= 0 ) - { - Log::Debug( "found %s cube map '%s'", loader.ext, cubeMapBaseName ); - loader.ImageLoader( cubeMapName.c_str(), pic, &width, &height, &numLayers, &numMips, &imageParams.bits, 0 ); + // Look for 6-files skybox. - if( numLayers == 6 && pic[0] ) { - image = R_CreateCubeImage( ( char * ) buffer, ( const byte ** ) pic, width, height, imageParams ); - R_FreeCubePics( pic, 1 ); - return image; - } - - R_FreeCubePics( pic, numLayers ); - } - } - - for ( i = 0; i < 6; i++ ) + for ( int i = 0; i < 6; i++ ) { pic[ i ] = nullptr; } @@ -2133,7 +2137,8 @@ image_t *R_FindCubeImage( const char *imageName, imageParams_t &imageParams ) int greatestEdge = 0; face_t faces[6]; - for ( i = 0; i < 6; i++ ) + int i = 0; + for ( ; i < 6; i++ ) { Com_sprintf( filename, sizeof( filename ), "%s%s%s", buffer, format.sep, format.suffixes[ i ] ); @@ -2179,8 +2184,7 @@ image_t *R_FindCubeImage( const char *imageName, imageParams_t &imageParams ) if ( i == 6 ) { - - for ( j = 0; j < 6; j++ ) + for ( int j = 0; j < 6; j++ ) { width = faces[ j ].width; height = faces[ j ].height; @@ -2232,6 +2236,31 @@ image_t *R_FindCubeImage( const char *imageName, imageParams_t &imageParams ) R_FreeCubePics( pic, i ); } + // Look for 6-faces single-file DDS/KTX skybox. + // TODO: Respect explicit extension. + + char baseName[ MAX_QPATH ]; + COM_StripExtension3( buffer, baseName, sizeof( baseName ) ); + + const imageExtLoader_t *loader = R_FindCubeImageLoader( baseName ); + + if ( loader ) + { + std::string altName = Str::Format( "%s.%s", baseName, loader->ext ); + + Log::Debug( "found %s cube map '%s' candidate: %s", loader->ext, baseName, altName ); + loader->imageLoader( altName.c_str(), pic, &width, &height, &numLayers, &numMips, &imageParams.bits, 0 ); + + if ( numLayers == 6 && pic[0] ) { + Log::Debug( "found %s cube map '%s': %s", loader->ext, baseName, altName ); + image = R_CreateCubeImage( ( char * ) buffer, ( const byte ** ) pic, width, height, imageParams ); + R_FreeCubePics( pic, 1 ); + return image; + } + + R_FreeCubePics( pic, numLayers ); + } + return nullptr; } diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index 920548762a..3dd4186040 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -3265,7 +3265,7 @@ inline bool checkGLErrors() void R_InitImages(); void R_ShutdownImages(); - int R_FindImageLoader( const char *baseName ); + bool R_HasImageLoader( const char *baseName ); image_t *R_FindImageFile( const char *name, imageParams_t &imageParams ); image_t *R_FindCubeImage( const char *name, imageParams_t &imageParams ); diff --git a/src/engine/renderer/tr_shader.cpp b/src/engine/renderer/tr_shader.cpp index b2d59ecc75..66123f0a62 100644 --- a/src/engine/renderer/tr_shader.cpp +++ b/src/engine/renderer/tr_shader.cpp @@ -1965,7 +1965,7 @@ void LoadExtraMaps( shaderStage_t *stage, const char *colorMapName ) for ( const extraMapParser_t parser: dpExtraMapParsers ) { std::string extraMapName = Str::Format( "%s%s", colorMapBaseName, parser.suffix ); - if( R_FindImageLoader( extraMapName.c_str() ) >= 0 ) + if ( R_HasImageLoader( extraMapName.c_str() ) ) { foundExtraMap = true; Log::Debug( "found extra %s '%s'", parser.description, extraMapName.c_str() );