From 4e2708c1aca76de2def283983ef9963d7a3ff8fa Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 11 Jun 2024 06:16:21 +0300 Subject: [PATCH] engine: bring back texture replacement --- Documentation/hd-textures.md | 40 +++++++++++ common/enginefeatures.h | 3 +- engine/client/cl_tent.c | 41 ++++++++++- engine/common/common.h | 4 +- engine/common/host.c | 5 ++ engine/common/mod_bmodel.c | 135 +++++++++++++++++++++++++++++++---- engine/ref_api.h | 2 + ref/gl/gl_alias.c | 2 + ref/gl/gl_context.c | 9 +++ ref/gl/gl_image.c | 36 ++++++++++ ref/gl/gl_local.h | 3 + ref/gl/gl_sprite.c | 17 ++++- ref/gl/gl_studio.c | 31 +++++--- ref/soft/r_context.c | 9 +++ 14 files changed, 309 insertions(+), 28 deletions(-) create mode 100644 Documentation/hd-textures.md diff --git a/Documentation/hd-textures.md b/Documentation/hd-textures.md new file mode 100644 index 00000000..3af012f9 --- /dev/null +++ b/Documentation/hd-textures.md @@ -0,0 +1,40 @@ +### HD (external) textures support + +Xash3D supports loading texture replacements in TGA format for almost all types of models in the game, except alias models at this time. + +Textures are expected to be located at: +* `modfolder/materials/` - for a specific map +* `modfolder/materials/common` - common for all maps +* `modfolder/materials/decals` - for decals +* `modfolder/materials/models/` - for models (texture name must match the internal texture name in the model) + +Support for high-resolution textures is enabled setting `host_allow_materials` cvar to `1` or in the menu, in "Video options" section. + +#### Xash3D FWGS additions + +In addition to paths above, Xash3D FWGS checks following paths: + +* `modfolder/materials/sprites/` - for sprites, except HUD sprites + +Also, to check which texture replacements are loaded successfully, failed or weren't found, a mod developer can set `host_allow_materials` cvar value to `2`. The engine will spew log at any developer level in the following format: + +``` +Looking for replacement... () +``` + +Status codes: +* `OK` - texture replacement file was found and loaded into GPU memory successfully +* `FAIL` - texture file was found but hasn't been parsed or loaded successfully. Refer to engine log for more details. +* `MISS` - texture file wasn't found + +Example: +``` +Looking for maps/bounce.bsp:!waterblue tex replacement...OK (materials/common/!waterblue.tga) +Looking for maps/bounce.bsp:!waterblue_luma tex replacement...MISS (not found) +Looking for {shot2 decal replacement...MISS (materials/decals/{shot2.tga) +Looking for {shot4 decal replacement...MISS (materials/decals/{shot4.tga) +Looking for {shot3 decal replacement...MISS (materials/decals/{shot3.tga) +Looking for models/gman tex replacement...FAIL (materials/models/gman/GMan_Case1.tga) +Looking for models/gman tex replacement...FAIL (materials/models/gman/inside_1.tga) +``` + diff --git a/common/enginefeatures.h b/common/enginefeatures.h index f2f9e199..3f71e7ec 100644 --- a/common/enginefeatures.h +++ b/common/enginefeatures.h @@ -1,5 +1,5 @@ /* -features.h - engine features that can be enabled by mod-maker request +enginefeatures.h - engine features that can be enabled by mod-maker request Copyright (C) 2012 Uncle Mike This program is free software: you can redistribute it and/or modify @@ -27,6 +27,7 @@ GNU General Public License for more details. #define ENGINE_COMPUTE_STUDIO_LERP (1<<7) // enable MOVETYPE_STEP lerping back in engine #define ENGINE_LINEAR_GAMMA_SPACE (1<<8) // disable influence of gamma/brightness cvars to textures/lightmaps, for mods with custom renderer +#define ENGINE_DISABLE_HDTEXTURES (1U<<30) // disable support of HD-textures in case custom renderer have separate way to load them #define ENGINE_STEP_POSHISTORY_LERP (1U<<31) // enable MOVETYPE_STEP interpolation based on position history. Incompatible with ENGINE_COMPUTE_STUDIO_LERP! // adjust the mask when features will be added or removed diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 86235c3d..86bd14cc 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -2981,8 +2981,47 @@ int GAME_EXPORT CL_DecalIndex( int id ) if( cl.decal_index[id] == 0 ) { + int gl_texturenum = 0; + Image_SetForceFlags( IL_LOAD_DECAL ); - cl.decal_index[id] = ref.dllFuncs.GL_LoadTexture( host.draw_decals[id], NULL, 0, TF_DECAL ); + + if( Mod_AllowMaterials( )) + { + string decalname; + + if( Q_snprintf( decalname, sizeof( decalname ), "materials/decals/%s.tga", host.draw_decals[id] ) > 0 ) + { + if( g_fsapi.FileExists( decalname, false )) + { + gl_texturenum = ref.dllFuncs.GL_LoadTexture( decalname, NULL, 0, TF_DECAL ); + if( host_allow_materials.value == 2.0f ) + Con_Printf( "Looking for %s decal replacement...%s (%s)\n", host.draw_decals[id], gl_texturenum != 0 ? S_GREEN "OK" : S_RED "FAIL", decalname ); + } + else if( host_allow_materials.value == 2.0f ) + Con_Printf( "Looking for %s decal replacement..." S_YELLOW "MISS (%s)\n", host.draw_decals[id], decalname ); + } + else if( host_allow_materials.value == 2.0f ) + Con_Printf( "Looking for %s decal replacement..." S_YELLOW "MISS (overflow)\n", host.draw_decals[id] ); + + if( gl_texturenum ) + { + byte *fin; + + Q_snprintf( decalname, sizeof( decalname ), "decals.wad/%s", host.draw_decals[id] ); + + if(( fin = g_fsapi.LoadFile( decalname, NULL, false )) != NULL ) + { + mip_t *mip = (mip_t *)fin; + ref.dllFuncs.R_OverrideTextureSourceSize( gl_texturenum, mip->width, mip->height ); + Mem_Free( fin ); + } + } + } + + if( !gl_texturenum ) + gl_texturenum = ref.dllFuncs.GL_LoadTexture( host.draw_decals[id], NULL, 0, TF_DECAL ); + + cl.decal_index[id] = gl_texturenum; Image_ClearForceFlags(); } diff --git a/engine/common/common.h b/engine/common/common.h index bb87155e..dd580cb2 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -157,7 +157,7 @@ extern convar_t gl_vsync; extern convar_t scr_loading; extern convar_t scr_download; extern convar_t cmd_scripting; -extern convar_t cl_allow_levelshots; +extern convar_t host_allow_materials; extern convar_t host_developer; extern convar_t host_limitlocal; extern convar_t host_maxfps; @@ -165,6 +165,8 @@ extern convar_t sys_timescale; extern convar_t cl_filterstuffcmd; extern convar_t rcon_password; +#define Mod_AllowMaterials() ( host_allow_materials.value != 0.0f && !FBitSet( host.features, ENGINE_DISABLE_HDTEXTURES )) + /* ============================================================== diff --git a/engine/common/host.c b/engine/common/host.c index 78239bb2..84955d4c 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -63,6 +63,7 @@ CVAR_DEFINE( host_maxfps, "fps_max", "72", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "host static CVAR_DEFINE_AUTO( host_framerate, "0", FCVAR_FILTERABLE, "locks frame timing to this value in seconds" ); static CVAR_DEFINE( host_sleeptime, "sleeptime", "1", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "milliseconds to sleep for each frame. higher values reduce fps accuracy" ); static CVAR_DEFINE_AUTO( host_sleeptime_debug, "0", 0, "print sleeps between frames" ); +CVAR_DEFINE_AUTO( host_allow_materials, "0", FCVAR_LATCH|FCVAR_ARCHIVE, "allow texture replacements from materials/ folder" ); CVAR_DEFINE( con_gamemaps, "con_mapfilter", "1", FCVAR_ARCHIVE, "when true show only maps in game folder" ); typedef struct feature_message_s @@ -1327,6 +1328,7 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa Cmd_AddRestrictedCommand ( "crash", Host_Crash_f, "a way to force a bus error for development reasons"); } + Cvar_RegisterVariable( &host_allow_materials ); Cvar_RegisterVariable( &host_serverstate ); Cvar_RegisterVariable( &host_maxfps ); Cvar_RegisterVariable( &host_framerate ); @@ -1372,6 +1374,9 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa Wcon_InitConsoleCommands (); #endif + // disable texture replacements for dedicated + Cvar_FullSet( "host_allow_materials", "0", FCVAR_READ_ONLY ); + Cmd_AddRestrictedCommand( "quit", Sys_Quit, "quit the game" ); Cmd_AddRestrictedCommand( "exit", Sys_Quit, "quit the game" ); } diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index 1724fdfc..9af9843e 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -2066,7 +2066,39 @@ static qboolean Mod_LooksLikeWaterTexture( const char *name ) return false; } -static void Mod_InitSkyClouds( mip_t *mt, texture_t *tx, qboolean custom_palette ) +static void Mod_TextureReplacementReport( const char *modelname, const char *texname, const char *type, int gl_texturenum, const char *foundpath ) +{ + if( host_allow_materials.value != 2.0f ) + return; + + if( gl_texturenum > 0 ) // found and loaded successfully + Con_Printf( "Looking for %s:%s%s tex replacement..." S_GREEN "OK (%s)\n", modelname, texname, type, foundpath ); + else if( gl_texturenum < 0 ) // not found + Con_Printf( "Looking for %s:%s%s tex replacement..." S_YELLOW "MISS (%s)\n", modelname, texname, type, foundpath ); + else // found but not loaded + Con_Printf( "Looking for %s:%s%s tex replacement..." S_RED "FAIL (%s)\n", modelname, texname, type, foundpath ); +} + +static qboolean Mod_SearchForTextureReplacement( char *out, size_t size, const char *modelname, const char *texname, const char *type ) +{ + const char *subdirs[] = { modelname, "common" }; + int i; + + for( i = 0; i < ARRAYSIZE( subdirs ); i++ ) + { + if( Q_snprintf( out, size, "materials/%s/%s%s.tga", subdirs[i], texname, type ) < 0 ) + continue; // truncated name + + if( g_fsapi.FileExists( out, false )) + return true; // found, load it + } + + Mod_TextureReplacementReport( modelname, texname, type, -1, "not found" ); + + return false; +} + +static void Mod_InitSkyClouds( model_t *mod, mip_t *mt, texture_t *tx, qboolean custom_palette ) { #if !XASH_DEDICATED rgbdata_t r_temp, *r_sky; @@ -2074,12 +2106,47 @@ static void Mod_InitSkyClouds( mip_t *mt, texture_t *tx, qboolean custom_palette uint transpix; int r, g, b; int i, j, p; - char texname[32]; - int solidskyTexture, alphaskyTexture; + string texname; + int solidskyTexture = 0, alphaskyTexture = 0; if( !ref.initialized ) return; + if( Mod_AllowMaterials( )) + { + rgbdata_t *pic; + + if( Mod_SearchForTextureReplacement( texname, sizeof( texname ), mod->name, mt->name, "_solid" )) + { + pic = FS_LoadImage( texname, NULL, 0 ); + if( pic ) + { + // need to do rename texture to properly cleanup these textures on reload + solidskyTexture = GL_LoadTextureInternal( "solid_sky", pic, TF_NOMIPMAP ); + Mod_TextureReplacementReport( mod->name, mt->name, "_solid", solidskyTexture, texname ); + FS_FreeImage( pic ); + } + } + + if( Mod_SearchForTextureReplacement( texname, sizeof( texname ), mod->name, mt->name, "_alpha" )) + { + pic = FS_LoadImage( texname, NULL, 0 ); + if( pic ) + { + alphaskyTexture = GL_LoadTextureInternal( "alpha_sky", pic, TF_NOMIPMAP ); + Mod_TextureReplacementReport( mod->name, mt->name, "_alpha", alphaskyTexture, texname ); + FS_FreeImage( pic ); + } + } + + if( !solidskyTexture || !alphaskyTexture ) + { + ref.dllFuncs.GL_FreeTexture( solidskyTexture ); + ref.dllFuncs.GL_FreeTexture( alphaskyTexture ); + } + else goto done; // replacements found, notify the renderer and exit + } + Q_snprintf( texname, sizeof( texname ), "%s%s.mip", ( mt->offsets[0] > 0 ) ? "#" : "", tx->name ); if( mt->offsets[0] > 0 ) @@ -2170,6 +2237,14 @@ static void Mod_InitSkyClouds( mip_t *mt, texture_t *tx, qboolean custom_palette FS_FreeImage( r_sky ); Mem_Free( trans ); + if( !solidskyTexture || !alphaskyTexture ) + { + ref.dllFuncs.GL_FreeTexture( solidskyTexture ); + ref.dllFuncs.GL_FreeTexture( alphaskyTexture ); + return; + } + +done: // notify the renderer ref.dllFuncs.R_SetSkyCloudsTextures( solidskyTexture, alphaskyTexture ); @@ -2184,6 +2259,9 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd mip_t *mipTex = NULL; qboolean usesCustomPalette = false; uint32_t txFlags = 0; + char texpath[MAX_VA_STRING]; + char safemtname[16]; // only for external textures + qboolean load_external = false; // don't load texture data on dedicated server, as there is no renderer. // but count the wadusage for automatic precache @@ -2192,6 +2270,14 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd // but there is no facility for this yet texture = mod->textures[textureIndex]; mipTex = Mod_GetMipTexForTexture( bmod, textureIndex ); + usesCustomPalette = Mod_CalcMipTexUsesCustomPalette( mod, bmod, textureIndex ); + + // check for multi-layered sky texture (quake1 specific) + if( bmod->isworld && Q_strncmp( mipTex->name, "sky", 3 ) == 0 && ( mipTex->width / mipTex->height ) == 2 ) + { + Mod_InitSkyClouds( mod, mipTex, texture, usesCustomPalette ); // load quake sky + return; + } if( FBitSet( host.features, ENGINE_IMPROVED_LINETRACE ) && mipTex->name[0] == '{' ) SetBits( txFlags, TF_KEEP_SOURCE ); // Paranoia2 texture alpha-tracing @@ -2200,23 +2286,31 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd if( Mod_LooksLikeWaterTexture( mipTex->name )) SetBits( txFlags, TF_KEEP_SOURCE | TF_EXPAND_SOURCE ); - usesCustomPalette = Mod_CalcMipTexUsesCustomPalette( mod, bmod, textureIndex ); + // Texture loading order: + // 1. HQ from disk + // 2. From WAD + // 3. Internal from map - // check for multi-layered sky texture (quake1 specific) - if( bmod->isworld && Q_strncmp( mipTex->name, "sky", 3 ) == 0 && ( mipTex->width / mipTex->height ) == 2 ) + texture->gl_texturenum = 0; + Q_strncpy( safemtname, mipTex->name, sizeof( safemtname )); + if( safemtname[0] == '*' ) + safemtname[0] = '!'; // replace unexpected symbol + + if( Mod_AllowMaterials( )) { - Mod_InitSkyClouds( mipTex, texture, usesCustomPalette ); // load quake sky - return; +#if !XASH_DEDICATED + if( Mod_SearchForTextureReplacement( texpath, sizeof( texpath ), mod->name, safemtname, "" )) + { + texture->gl_texturenum = ref.dllFuncs.GL_LoadTexture( texpath, NULL, 0, txFlags ); + load_external = texture->gl_texturenum != 0; + Mod_TextureReplacementReport( mod->name, safemtname, "", texture->gl_texturenum, texpath ); + } +#endif // !XASH_DEDICATED } - // Texture loading order: - // 1. From WAD - // 2. Internal from map - // Try WAD texture (force while r_wadtextures is 1) - if(( r_wadtextures.value && bmod->wadlist.count > 0 ) || mipTex->offsets[0] <= 0 ) + if( !texture->gl_texturenum && (( r_wadtextures.value && bmod->wadlist.count > 0 ) || mipTex->offsets[0] <= 0 )) { - char texpath[MAX_VA_STRING]; int wadIndex = Mod_FindTextureInWadList( &bmod->wadlist, mipTex->name, texpath, sizeof( texpath )); if( wadIndex >= 0 ) @@ -2251,9 +2345,20 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd } // Check for luma texture - if( FBitSet( REF_GET_PARM( PARM_TEX_FLAGS, texture->gl_texturenum ), TF_HAS_LUMA )) + texture->fb_texturenum = 0; + if( load_external ) // external textures will not have TF_HAS_LUMA flag because it set only from WAD images loader + { + if( Mod_SearchForTextureReplacement( texpath, sizeof( texpath ), mod->name, safemtname, "_luma" )) + { + texture->fb_texturenum = ref.dllFuncs.GL_LoadTexture( texpath, NULL, 0, TF_MAKELUMA ); + Mod_TextureReplacementReport( mod->name, safemtname, "_luma", texture->fb_texturenum, texpath ); + } + } + + if( FBitSet( REF_GET_PARM( PARM_TEX_FLAGS, texture->gl_texturenum ), TF_HAS_LUMA ) && !texture->fb_texturenum ) { char texName[64]; + Q_snprintf( texName, sizeof( texName ), "#%s:%s_luma.mip", loadstat.name, mipTex->name ); if( mipTex->offsets[0] > 0 ) diff --git a/engine/ref_api.h b/engine/ref_api.h index 76433fde..156182bc 100644 --- a/engine/ref_api.h +++ b/engine/ref_api.h @@ -568,6 +568,7 @@ typedef struct ref_interface_s int (*GL_LoadTextureArray)( const char **names, int flags ); int (*GL_CreateTextureArray)( const char *name, int width, int height, int depth, const void *buffer, texFlags_t flags ); void (*GL_FreeTexture)( unsigned int texnum ); + void (*R_OverrideTextureSourceSize)( unsigned int texnum, unsigned int srcWidth, unsigned int srcHeight ); // used to override decal size for texture replacement // Decals manipulating (draw & remove) void (*DrawSingleDecal)( struct decal_s *pDecal, struct msurface_s *fa ); @@ -673,6 +674,7 @@ typedef int (*REFAPI)( int version, ref_interface_t *pFunctionTable, ref_api_t* ENGINE_SHARED_CVAR( f, r_sprite_lighting ) \ ENGINE_SHARED_CVAR( f, r_drawviewmodel ) \ ENGINE_SHARED_CVAR( f, r_glowshellfreq ) \ + ENGINE_SHARED_CVAR( f, host_allow_materials ) \ #define DECLARE_ENGINE_SHARED_CVAR_LIST() \ ENGINE_SHARED_CVAR_LIST( DECLARE_ENGINE_SHARED_CVAR ) diff --git a/ref/gl/gl_alias.c b/ref/gl/gl_alias.c index f3a2c323..110cd0fc 100644 --- a/ref/gl/gl_alias.c +++ b/ref/gl/gl_alias.c @@ -561,6 +561,8 @@ static void *Mod_LoadAllSkins( model_t *mod, int numskins, daliasskintype_t *psk size = m_pAliasHeader->skinwidth * m_pAliasHeader->skinheight; + // TODO: texture replacement support here + for( i = 0; i < numskins; i++ ) { if( pskintype->type == ALIAS_SKIN_SINGLE ) diff --git a/ref/gl/gl_context.c b/ref/gl/gl_context.c index 660eb9d4..ba3880f6 100644 --- a/ref/gl/gl_context.c +++ b/ref/gl/gl_context.c @@ -400,6 +400,14 @@ static void GAME_EXPORT VGUI_SetupDrawing( qboolean rect ) } } +static void GAME_EXPORT R_OverrideTextureSourceSize( unsigned int texnum, uint srcWidth, uint srcHeight ) +{ + gl_texture_t *tx = R_GetTexture( texnum ); + + tx->srcWidth = srcWidth; + tx->srcHeight = srcHeight; +} + static void* GAME_EXPORT R_GetProcAddress( const char *name ) { #ifdef XASH_GL4ES @@ -504,6 +512,7 @@ static ref_interface_t gReffuncs = GL_LoadTextureArray, GL_CreateTextureArray, GL_FreeTexture, + R_OverrideTextureSourceSize, DrawSingleDecal, R_DecalSetupVerts, diff --git a/ref/gl/gl_image.c b/ref/gl/gl_image.c index 97476516..2b91d075 100644 --- a/ref/gl/gl_image.c +++ b/ref/gl/gl_image.c @@ -13,6 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +#include #include "gl_local.h" #include "crclib.h" @@ -2349,3 +2350,38 @@ void R_ShutdownImages( void ) memset( gl_textures, 0, sizeof( gl_textures )); gl_numTextures = 0; } + +void R_TextureReplacementReport( const char *modelname, int gl_texturenum, const char *foundpath ) +{ + if( host_allow_materials->value != 2.0f ) + return; + + if( gl_texturenum > 0 ) + gEngfuncs.Con_Printf( "Looking for %s tex replacement..." S_GREEN "OK (%s)\n", modelname, foundpath ); + else if( gl_texturenum < 0 ) + gEngfuncs.Con_Printf( "Looking for %s tex replacement..." S_YELLOW "MISS (%s)\n", modelname, foundpath ); + else + gEngfuncs.Con_Printf( "Looking for %s tex replacement..." S_RED "FAIL (%s)\n", modelname, foundpath ); +} + +qboolean R_SearchForTextureReplacement( char *out, size_t size, const char *modelname, const char *fmt, ... ) +{ + va_list ap; + int ret; + + va_start( ap, fmt ); + ret = Q_vsnprintf( out, size, fmt, ap ); + va_end( ap ); + + if( ret < 0 ) + { + R_TextureReplacementReport( modelname, -1, "overflow" ); + return false; + } + + if( gEngfuncs.fsapi->FileExists( out, false )) + return true; + + R_TextureReplacementReport( modelname, -1, out ); + return false; +} diff --git a/ref/gl/gl_local.h b/ref/gl/gl_local.h index 69face67..ccc0d5cf 100644 --- a/ref/gl/gl_local.h +++ b/ref/gl/gl_local.h @@ -288,6 +288,7 @@ extern gl_globals_t tr; extern float gldepthmin, gldepthmax; #define r_numEntities (tr.draw_list->num_solid_entities + tr.draw_list->num_trans_entities) #define r_numStatics (r_stats.c_client_ents) +#define Mod_AllowMaterials() (host_allow_materials->value && !FBitSet( gp_host->features, ENGINE_DISABLE_HDTEXTURES )) // // gl_backend.c @@ -373,6 +374,8 @@ void R_TextureList_f( void ); void R_InitImages( void ); void R_ShutdownImages( void ); int GL_TexMemory( void ); +qboolean R_SearchForTextureReplacement( char *out, size_t size, const char *modelname, const char *fmt, ... ) _format( 4 ); +void R_TextureReplacementReport( const char *modelname, int gl_texturenum, const char *foundpath ); // // gl_rlight.c diff --git a/ref/gl/gl_sprite.c b/ref/gl/gl_sprite.c index 9a1060a0..30fa2e59 100644 --- a/ref/gl/gl_sprite.c +++ b/ref/gl/gl_sprite.c @@ -67,8 +67,21 @@ static const byte *R_SpriteLoadFrame( model_t *mod, const void *pin, mspritefram } else { - Q_snprintf( texname, sizeof( texname ), "#%s(%s:%i%i).spr", sprite_name, group_suffix, num / 10, num % 10 ); - gl_texturenum = GL_LoadTexture( texname, pin, pinframe.width * pinframe.height * bytes, r_texFlags ); + // partial HD-textures support + if( Mod_AllowMaterials( )) + { + if( R_SearchForTextureReplacement( texname, sizeof( texname ), sprite_name, "materials/%s/%s%i%i.tga", sprite_name, group_suffix, num / 10, num % 10 )) + { + gl_texturenum = GL_LoadTexture( texname, NULL, 0, r_texFlags ); + R_TextureReplacementReport( sprite_name, gl_texturenum, texname ); + } + } + + if( gl_texturenum == 0 ) + { + Q_snprintf( texname, sizeof( texname ), "#%s(%s:%i%i).spr", sprite_name, group_suffix, num / 10, num % 10 ); + gl_texturenum = GL_LoadTexture( texname, pin, pinframe.width * pinframe.height * bytes, r_texFlags ); + } } // setup frame description diff --git a/ref/gl/gl_studio.c b/ref/gl/gl_studio.c index 01282ae2..c70ae875 100644 --- a/ref/gl/gl_studio.c +++ b/ref/gl/gl_studio.c @@ -3729,6 +3729,7 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture int flags = 0; char texname[128], name[128], mdlname[128]; texture_t *tx = NULL; + qboolean load_external = false; if( ptexture->flags & STUDIO_NF_NORMALMAP ) flags |= (TF_NORMALMAP); @@ -3787,17 +3788,31 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture if( FBitSet( ptexture->flags, STUDIO_NF_NOMIPS )) SetBits( flags, TF_NOMIPMAP ); - // NOTE: replace index with pointer to start of imagebuffer, ImageLib expected it - //ptexture->index = (int)((byte *)phdr) + ptexture->index; - gEngfuncs.Image_SetMDLPointer((byte *)phdr + ptexture->index); - size = sizeof( mstudiotexture_t ) + ptexture->width * ptexture->height + 768; - if( FBitSet( gp_host->features, ENGINE_IMPROVED_LINETRACE ) && FBitSet( ptexture->flags, STUDIO_NF_MASKED )) flags |= TF_KEEP_SOURCE; // Paranoia2 texture alpha-tracing - // build the texname - Q_snprintf( texname, sizeof( texname ), "#%s/%s.mdl", mdlname, name ); - ptexture->index = GL_LoadTexture( texname, (byte *)ptexture, size, flags ); + // NOTE: colormaps must have the palette for properly work. Ignore them + if( Mod_AllowMaterials( ) && !FBitSet( ptexture->flags, STUDIO_NF_COLORMAP )) + { + if( R_SearchForTextureReplacement( texname, sizeof( texname ), mdlname, "materials/%s/%s.tga", mdlname, name )) + { + int gl_texturenum = GL_LoadTexture( texname, NULL, 0, flags ); + R_TextureReplacementReport( mdlname, gl_texturenum, texname ); + if(( load_external = gl_texturenum != 0 )) + ptexture->index = gl_texturenum; + } + } + + if( !load_external ) + { + // NOTE: replace index with pointer to start of imagebuffer, ImageLib expected it + gEngfuncs.Image_SetMDLPointer((byte *)phdr + ptexture->index); + size = sizeof( mstudiotexture_t ) + ptexture->width * ptexture->height + 768; + + // build the texname + Q_snprintf( texname, sizeof( texname ), "#%s/%s.mdl", mdlname, name ); + ptexture->index = GL_LoadTexture( texname, (byte *)ptexture, size, flags ); + } if( !ptexture->index ) { diff --git a/ref/soft/r_context.c b/ref/soft/r_context.c index 2be8fc5d..d2855dad 100644 --- a/ref/soft/r_context.c +++ b/ref/soft/r_context.c @@ -398,6 +398,14 @@ static void GAME_EXPORT VGUI_SetupDrawing( qboolean rect ) { } +static void GAME_EXPORT R_OverrideTextureSourceSize( unsigned int texnum, uint srcWidth, uint srcHeight ) +{ + image_t *tx = R_GetTexture( texnum ); + + tx->srcWidth = srcWidth; + tx->srcHeight = srcHeight; +} + static const char *R_GetConfigName( void ) { return "ref_soft"; // software specific cvars will go to ref_soft.cfg @@ -498,6 +506,7 @@ static ref_interface_t gReffuncs = GL_LoadTextureArray, GL_CreateTextureArray, GL_FreeTexture, + R_OverrideTextureSourceSize, DrawSingleDecal, R_DecalSetupVerts,