diff --git a/common/com_image.h b/common/com_image.h index 098a0d12..c65b43a1 100644 --- a/common/com_image.h +++ b/common/com_image.h @@ -70,6 +70,7 @@ typedef enum IL_OVERVIEW = BIT(6), // overview required some unque operations IL_LOAD_PLAYER_DECAL = BIT(7), // special mode for player decals IL_KTX2_RAW = BIT(8), // renderer can consume raw KTX2 files (e.g. ref_vk) + IL_ALLOW_WAD3_LUMA = BIT(9), // allow usage of luma textures in wad3 (tilde textures) } ilFlags_t; // goes into rgbdata_t->encode diff --git a/engine/common/imagelib/img_wad.c b/engine/common/imagelib/img_wad.c index 50425e78..165fd0e2 100644 --- a/engine/common/imagelib/img_wad.c +++ b/engine/common/imagelib/img_wad.c @@ -358,6 +358,25 @@ qboolean Image_LoadLMP( const char *name, const byte *buffer, fs_offset_t filesi return Image_AddIndexedImageToPack( fin, image.width, image.height ); } +static int Image_FindBestBlack( const uint *pal ) +{ + int min_color = 32; + int best_black = -1; + + for( int i = 0; i < 256; i++ ) + { + int color = pal[i] & 0xFFFFFF; + + if( color < min_color ) + { + min_color = color; + best_black = i; + } + } + + return best_black; +} + /* ============= Image_LoadMIP @@ -416,29 +435,27 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, fs_offset_t filesi } else { - int pal_type; - // NOTE: we can have luma-pixels if quake1 texture // converted into the hl texture but palette leave unchanged // this is a good reason for using fullbright pixels - pal_type = Image_ComparePalette( pal ); - - // check for luma pixels (but ignore liquid textures because they have no lightmap) - if( mip.name[0] != '*' && mip.name[0] != '!' && pal_type == PAL_QUAKE1 ) - { - for( i = 0; i < image.width * image.height; i++ ) - { - if( fin[i] > 224 ) - { - SetBits( image.flags, IMAGE_HAS_LUMA ); - image.black_pixel = 0; - break; - } - } - } + const int pal_type = Image_ComparePalette( pal ); if( pal_type == PAL_QUAKE1 ) { + // check for luma pixels (but ignore liquid textures because they have no lightmap) + if( mip.name[0] != '*' && mip.name[0] != '!' ) + { + for( i = 0; i < image.width * image.height; i++ ) + { + if( fin[i] > 224 ) + { + SetBits( image.flags, IMAGE_HAS_LUMA ); + image.black_pixel = 0; + break; + } + } + } + SetBits( image.flags, IMAGE_QUAKEPAL ); // if texture was converted from quake to half-life with no palette changes @@ -449,11 +466,30 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, fs_offset_t filesi { // half-life mips need texgamma applied rendermode = LUMP_TEXGAMMA; + + // three checks here: check if we can load luma, check the texture name and, finally, + // validate that palette isn't NULL + if( Image_CheckFlag( IL_ALLOW_WAD3_LUMA ) + && ( mip.name[0] == '~' || ( mip.name[0] == '+' && isdigit( mip.name[1] ) && mip.name[2] == '~' )) + && pal != NULL ) + { + SetBits( image.flags, IMAGE_HAS_LUMA ); + } } } Image_GetPaletteLMP( pal, rendermode ); image.d_currentpal[255] &= 0xFFFFFF; + + if( rendermode == LUMP_TEXGAMMA && FBitSet( image.flags, IMAGE_HAS_LUMA )) + { + // thanks to Unkle Mike for an idea of looking best black pixel + image.black_pixel = Image_FindBestBlack( image.d_currentpal ); + + // failed to find good pixel, refuse to load luma + if( image.black_pixel == -1 ) + ClearBits( image.flags, IMAGE_HAS_LUMA ); + } } else if( image.hint != IL_HINT_HL && filesize >= (int)sizeof(mip) + ((pixels * 85)>>6)) { @@ -466,7 +502,7 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, fs_offset_t filesi // check for luma and alpha pixels // don't apply luma to water surfaces because they have no lightmap - if( !image.custom_palette && mip.name[0] != '*' && mip.name != '!' ) + if( !image.custom_palette && mip.name[0] != '*' && mip.name[0] != '!' ) { for( i = 0; i < image.width * image.height; i++ ) { diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index c232c9b7..99bc60a9 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -2583,6 +2583,7 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd const mip_t *mipTex = Mod_GetMipTexForTexture( bmod, textureIndex ); const qboolean usesCustomPalette = Mod_CalcMipTexUsesCustomPalette( mod, bmod, textureIndex ); const qboolean iswater = Mod_LooksLikeWaterTexture( mipTex->name ); + const uint texture_force_flags = r_allow_wad3_luma.value ? IL_ALLOW_WAD3_LUMA : 0; // check for multi-layered sky texture (quake1 specific) if( bmod->isworld && Q_strncmp( mipTex->name, "sky", 3 ) == 0 && ( mipTex->width / mipTex->height ) == 2 ) @@ -2633,6 +2634,7 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd #if !XASH_DEDICATED if( !Host_IsDedicated( ) && pic != NULL ) { + Image_SetForceFlags( texture_force_flags ); texture->gl_texturenum = ref.dllFuncs.GL_LoadTextureFromBuffer( texpath, pic, txFlags, false ); FS_FreeImage( pic ); } @@ -2652,6 +2654,7 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd const size_t size = Mod_CalculateMipTexSize( mipTex, usesCustomPalette ); Q_snprintf( texName, sizeof( texName ), "#%s:%s.mip", loadstat.name, mipTex->name ); + Image_SetForceFlags( texture_force_flags ); texture->gl_texturenum = ref.dllFuncs.GL_LoadTexture( texName, (byte *)mipTex, size, txFlags ); } @@ -2684,6 +2687,8 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd Q_snprintf( texName, sizeof( texName ), "#%s:%s_luma.mip", loadstat.name, mipTex->name ); + Image_SetForceFlags( texture_force_flags ); + if( mipTex->offsets[0] > 0 ) { const size_t size = Mod_CalculateMipTexSize( mipTex, usesCustomPalette ); diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index 9ee1ae18..464bc541 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -129,11 +129,12 @@ typedef struct world_static_s } world_static_t; #ifndef REF_DLL -extern world_static_t world; -extern poolhandle_t com_studiocache; -extern convar_t mod_studiocache; -extern convar_t r_wadtextures; -extern convar_t r_showhull; +extern world_static_t world; +extern poolhandle_t com_studiocache; +extern convar_t mod_studiocache; +extern convar_t r_wadtextures; +extern convar_t r_showhull; +extern convar_t r_allow_wad3_luma; extern const mclipnode16_t box_clipnodes16[6]; extern const mclipnode32_t box_clipnodes32[6]; diff --git a/engine/common/model.c b/engine/common/model.c index a67eaa44..447ffcea 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -31,6 +31,7 @@ poolhandle_t com_studiocache; // cache for submodels CVAR_DEFINE( mod_studiocache, "r_studiocache", "1", FCVAR_ARCHIVE, "enables studio cache for speedup tracing hitboxes" ); CVAR_DEFINE_AUTO( r_wadtextures, "0", 0, "completely ignore textures in the bsp-file if enabled" ); CVAR_DEFINE_AUTO( r_showhull, "0", 0, "draw collision hulls 1-3" ); +CVAR_DEFINE_AUTO( r_allow_wad3_luma, "0", FCVAR_ARCHIVE, "allow usage of luma textures in wad3 (tilde textures)" ); /* =============================================================================== @@ -142,6 +143,7 @@ void Mod_Init( void ) Cvar_RegisterVariable( &mod_studiocache ); Cvar_RegisterVariable( &r_wadtextures ); Cvar_RegisterVariable( &r_showhull ); + Cvar_RegisterVariable( &r_allow_wad3_luma ); Cmd_AddCommand( "mapstats", Mod_PrintWorldStats_f, "show stats for currently loaded map" ); Cmd_AddCommand( "modellist", Mod_Modellist_f, "display loaded models list" );