2020-05-24 14:11:10 +00:00
/*
* * texturemanager . cpp
* * The texture manager class
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright 2004 - 2008 Randy Heit
* * Copyright 2006 - 2008 Christoph Oelckers
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
* *
*/
# include "filesystem.h"
# include "printf.h"
# include "c_cvars.h"
2021-10-30 08:51:03 +00:00
2020-05-24 14:11:10 +00:00
# include "gstrings.h"
# include "textures.h"
# include "texturemanager.h"
# include "c_dispatch.h"
# include "sc_man.h"
# include "image.h"
# include "vectors.h"
2020-06-14 18:27:13 +00:00
# include "animtexture.h"
2020-05-24 14:11:10 +00:00
# include "formats/multipatchtexture.h"
FTextureManager TexMan ;
//==========================================================================
//
// FTextureManager :: FTextureManager
//
//==========================================================================
FTextureManager : : FTextureManager ( )
{
memset ( HashFirst , - 1 , sizeof ( HashFirst ) ) ;
for ( int i = 0 ; i < 2048 ; + + i )
{
sintable [ i ] = short ( sin ( i * ( pi : : pi ( ) / 1024 ) ) * 16384 ) ;
}
}
//==========================================================================
//
// FTextureManager :: ~FTextureManager
//
//==========================================================================
FTextureManager : : ~ FTextureManager ( )
{
DeleteAll ( ) ;
}
//==========================================================================
//
// FTextureManager :: DeleteAll
//
//==========================================================================
void FTextureManager : : DeleteAll ( )
{
for ( unsigned int i = 0 ; i < Textures . Size ( ) ; + + i )
{
delete Textures [ i ] . Texture ;
}
2021-12-02 20:40:43 +00:00
FImageSource : : ClearImages ( ) ;
2020-05-24 14:11:10 +00:00
Textures . Clear ( ) ;
Translation . Clear ( ) ;
FirstTextureForFile . Clear ( ) ;
memset ( HashFirst , - 1 , sizeof ( HashFirst ) ) ;
DefaultTexture . SetInvalid ( ) ;
BuildTileData . Clear ( ) ;
tmanips . Clear ( ) ;
}
//==========================================================================
//
// Flushes all hardware dependent data.
2020-05-25 21:59:07 +00:00
// This must not, under any circumstances, delete the wipe textures, because
2020-05-24 14:11:10 +00:00
// all CCMDs triggering a flush can be executed while a wipe is in progress
//
2020-06-11 07:15:44 +00:00
// This now also deletes the software textures because the software
2020-05-25 21:59:07 +00:00
// renderer can also use the texture scalers and that is the
2020-05-24 14:11:10 +00:00
// main reason to call this outside of the destruction code.
//
//==========================================================================
void FTextureManager : : FlushAll ( )
{
for ( int i = TexMan . NumTextures ( ) - 1 ; i > = 0 ; i - - )
{
for ( int j = 0 ; j < 2 ; j + + )
{
2020-05-25 21:59:07 +00:00
Textures [ i ] . Texture - > CleanHardwareData ( ) ;
delete Textures [ i ] . Texture - > GetSoftwareTexture ( ) ;
calcShouldUpscale ( Textures [ i ] . Texture ) ;
2020-06-11 07:15:44 +00:00
Textures [ i ] . Texture - > SetSoftwareTexture ( nullptr ) ;
2020-05-24 14:11:10 +00:00
}
}
}
2020-05-25 21:59:07 +00:00
//==========================================================================
//
// Examines the lump contents to decide what type of texture to create,
// and creates the texture.
//
//==========================================================================
static FTexture * CreateTextureFromLump ( int lumpnum , bool allowflats = false )
{
if ( lumpnum = = - 1 ) return nullptr ;
auto image = FImageSource : : GetImage ( lumpnum , allowflats ) ;
if ( image ! = nullptr )
{
return new FImageTexture ( image ) ;
}
return nullptr ;
}
2020-05-24 14:11:10 +00:00
//==========================================================================
//
// FTextureManager :: CheckForTexture
//
//==========================================================================
FTextureID FTextureManager : : CheckForTexture ( const char * name , ETextureType usetype , BITFIELD flags )
{
int i ;
int firstfound = - 1 ;
auto firsttype = ETextureType : : Null ;
if ( name = = NULL | | name [ 0 ] = = ' \0 ' )
{
return FTextureID ( - 1 ) ;
}
// [RH] Doom counted anything beginning with '-' as "no texture".
// Hopefully nobody made use of that and had textures like "-EMPTY",
// because -NOFLAT- is a valid graphic for ZDoom.
if ( name [ 0 ] = = ' - ' & & name [ 1 ] = = ' \0 ' )
{
return FTextureID ( 0 ) ;
}
for ( i = HashFirst [ MakeKey ( name ) % HASH_SIZE ] ; i ! = HASH_END ; i = Textures [ i ] . HashNext )
{
2020-05-25 21:59:07 +00:00
auto tex = Textures [ i ] . Texture ;
2020-05-24 14:11:10 +00:00
2020-05-25 21:59:07 +00:00
if ( stricmp ( tex - > GetName ( ) , name ) = = 0 )
2020-05-24 14:11:10 +00:00
{
// If we look for short names, we must ignore any long name texture.
2020-05-25 21:59:07 +00:00
if ( ( flags & TEXMAN_ShortNameOnly ) & & tex - > isFullNameTexture ( ) )
2020-05-24 14:11:10 +00:00
{
continue ;
}
2020-05-25 21:59:07 +00:00
auto texUseType = tex - > GetUseType ( ) ;
2020-05-24 14:11:10 +00:00
// The name matches, so check the texture type
if ( usetype = = ETextureType : : Any )
{
2022-12-07 16:10:27 +00:00
if ( flags & TEXMAN_ReturnAll ) return FTextureID ( i ) ; // user asked to skip all checks, including null textures.
2020-05-24 14:11:10 +00:00
// All NULL textures should actually return 0
2020-05-25 21:59:07 +00:00
if ( texUseType = = ETextureType : : FirstDefined & & ! ( flags & TEXMAN_ReturnFirst ) ) return 0 ;
if ( texUseType = = ETextureType : : SkinGraphic & & ! ( flags & TEXMAN_AllowSkins ) ) return 0 ;
return FTextureID ( texUseType = = ETextureType : : Null ? 0 : i ) ;
2020-05-24 14:11:10 +00:00
}
2020-05-25 21:59:07 +00:00
else if ( ( flags & TEXMAN_Overridable ) & & texUseType = = ETextureType : : Override )
2020-05-24 14:11:10 +00:00
{
return FTextureID ( i ) ;
}
2020-05-25 21:59:07 +00:00
else if ( texUseType = = usetype )
2020-05-24 14:11:10 +00:00
{
return FTextureID ( i ) ;
}
2020-05-25 21:59:07 +00:00
else if ( texUseType = = ETextureType : : FirstDefined & & usetype = = ETextureType : : Wall )
2020-05-24 14:11:10 +00:00
{
if ( ! ( flags & TEXMAN_ReturnFirst ) ) return FTextureID ( 0 ) ;
else return FTextureID ( i ) ;
}
2020-05-25 21:59:07 +00:00
else if ( texUseType = = ETextureType : : Null & & usetype = = ETextureType : : Wall )
2020-05-24 14:11:10 +00:00
{
// We found a NULL texture on a wall -> return 0
return FTextureID ( 0 ) ;
}
else
{
if ( firsttype = = ETextureType : : Null | |
( firsttype = = ETextureType : : MiscPatch & &
2020-05-25 21:59:07 +00:00
texUseType ! = firsttype & &
texUseType ! = ETextureType : : Null )
2020-05-24 14:11:10 +00:00
)
{
firstfound = i ;
2020-05-25 21:59:07 +00:00
firsttype = texUseType ;
2020-05-24 14:11:10 +00:00
}
}
}
}
if ( ( flags & TEXMAN_TryAny ) & & usetype ! = ETextureType : : Any )
{
// Never return the index of NULL textures.
if ( firstfound ! = - 1 )
{
2022-12-07 16:10:27 +00:00
if ( flags & TEXMAN_ReturnAll ) return FTextureID ( i ) ; // user asked to skip all checks, including null textures.
2020-05-24 14:11:10 +00:00
if ( firsttype = = ETextureType : : Null ) return FTextureID ( 0 ) ;
if ( firsttype = = ETextureType : : FirstDefined & & ! ( flags & TEXMAN_ReturnFirst ) ) return FTextureID ( 0 ) ;
return FTextureID ( firstfound ) ;
}
}
2021-12-30 09:30:21 +00:00
2020-05-24 14:11:10 +00:00
if ( ! ( flags & TEXMAN_ShortNameOnly ) )
{
// We intentionally only look for textures in subdirectories.
// Any graphic being placed in the zip's root directory can not be found by this.
2020-06-28 12:41:44 +00:00
if ( strchr ( name , ' / ' ) | | ( flags & TEXMAN_ForceLookup ) )
2020-05-24 14:11:10 +00:00
{
2020-05-25 21:59:07 +00:00
FGameTexture * const NO_TEXTURE = ( FGameTexture * ) - 1 ;
2020-05-24 14:11:10 +00:00
int lump = fileSystem . CheckNumForFullName ( name ) ;
if ( lump > = 0 )
{
2020-05-25 21:59:07 +00:00
FGameTexture * tex = fileSystem . GetLinkedTexture ( lump ) ;
2020-05-24 14:11:10 +00:00
if ( tex = = NO_TEXTURE ) return FTextureID ( - 1 ) ;
2020-05-25 21:59:07 +00:00
if ( tex ! = NULL ) return tex - > GetID ( ) ;
2020-05-24 14:11:10 +00:00
if ( flags & TEXMAN_DontCreate ) return FTextureID ( - 1 ) ; // we only want to check, there's no need to create a texture if we don't have one yet.
2020-05-25 21:59:07 +00:00
tex = MakeGameTexture ( CreateTextureFromLump ( lump ) , nullptr , ETextureType : : Override ) ;
2020-05-24 14:11:10 +00:00
if ( tex ! = NULL )
{
2020-05-24 19:19:33 +00:00
tex - > AddAutoMaterials ( ) ;
2020-05-24 14:11:10 +00:00
fileSystem . SetLinkedTexture ( lump , tex ) ;
2020-05-25 21:59:07 +00:00
return AddGameTexture ( tex ) ;
2020-05-24 14:11:10 +00:00
}
else
{
// mark this lump as having no valid texture so that we don't have to retry creating one later.
fileSystem . SetLinkedTexture ( lump , NO_TEXTURE ) ;
}
}
}
}
2020-10-06 17:26:22 +00:00
if ( ! ( flags & TEXMAN_NoAlias ) )
{
int * alias = aliases . CheckKey ( name ) ;
if ( alias ) return FTextureID ( * alias ) ;
}
2020-05-24 14:11:10 +00:00
return FTextureID ( - 1 ) ;
}
//==========================================================================
//
// FTextureManager :: ListTextures
//
//==========================================================================
int FTextureManager : : ListTextures ( const char * name , TArray < FTextureID > & list , bool listall )
{
int i ;
if ( name = = NULL | | name [ 0 ] = = ' \0 ' )
{
return 0 ;
}
// [RH] Doom counted anything beginning with '-' as "no texture".
// Hopefully nobody made use of that and had textures like "-EMPTY",
// because -NOFLAT- is a valid graphic for ZDoom.
if ( name [ 0 ] = = ' - ' & & name [ 1 ] = = ' \0 ' )
{
return 0 ;
}
i = HashFirst [ MakeKey ( name ) % HASH_SIZE ] ;
while ( i ! = HASH_END )
{
2020-05-25 21:59:07 +00:00
auto tex = Textures [ i ] . Texture ;
2020-05-24 14:11:10 +00:00
2020-05-25 21:59:07 +00:00
if ( stricmp ( tex - > GetName ( ) , name ) = = 0 )
2020-05-24 14:11:10 +00:00
{
2020-05-25 21:59:07 +00:00
auto texUseType = tex - > GetUseType ( ) ;
2020-05-24 14:11:10 +00:00
// NULL textures must be ignored.
2020-05-25 21:59:07 +00:00
if ( texUseType ! = ETextureType : : Null )
2020-05-24 14:11:10 +00:00
{
unsigned int j = list . Size ( ) ;
if ( ! listall )
{
for ( j = 0 ; j < list . Size ( ) ; j + + )
{
// Check for overriding definitions from newer WADs
2020-05-25 21:59:07 +00:00
if ( Textures [ list [ j ] . GetIndex ( ) ] . Texture - > GetUseType ( ) = = texUseType ) break ;
2020-05-24 14:11:10 +00:00
}
}
if ( j = = list . Size ( ) ) list . Push ( FTextureID ( i ) ) ;
}
}
i = Textures [ i ] . HashNext ;
}
return list . Size ( ) ;
}
//==========================================================================
//
// FTextureManager :: GetTextures
//
//==========================================================================
FTextureID FTextureManager : : GetTextureID ( const char * name , ETextureType usetype , BITFIELD flags )
{
FTextureID i ;
if ( name = = NULL | | name [ 0 ] = = 0 )
{
return FTextureID ( 0 ) ;
}
else
{
i = CheckForTexture ( name , usetype , flags | TEXMAN_TryAny ) ;
}
if ( ! i . Exists ( ) )
{
// Use a default texture instead of aborting like Doom did
Printf ( " Unknown texture: \" %s \" \n " , name ) ;
i = DefaultTexture ;
}
return FTextureID ( i ) ;
}
//==========================================================================
//
// FTextureManager :: FindTexture
//
//==========================================================================
2020-05-25 21:59:07 +00:00
FGameTexture * FTextureManager : : FindGameTexture ( const char * texname , ETextureType usetype , BITFIELD flags )
2020-05-24 14:11:10 +00:00
{
FTextureID texnum = CheckForTexture ( texname , usetype , flags ) ;
2020-05-25 21:59:07 +00:00
return GetGameTexture ( texnum . GetIndex ( ) ) ;
2020-05-24 14:11:10 +00:00
}
//==========================================================================
//
// FTextureManager :: OkForLocalization
//
//==========================================================================
bool FTextureManager : : OkForLocalization ( FTextureID texnum , const char * substitute , int locmode )
{
uint32_t langtable = 0 ;
if ( * substitute = = ' $ ' ) substitute = GStrings . GetString ( substitute + 1 , & langtable ) ;
else return true ; // String literals from the source data should never override graphics from the same definition.
if ( substitute = = nullptr ) return true ; // The text does not exist.
2021-12-30 09:30:21 +00:00
2020-05-24 14:11:10 +00:00
// Modes 2, 3 and 4 must not replace localized textures.
int localizedTex = ResolveLocalizedTexture ( texnum . GetIndex ( ) ) ;
if ( localizedTex ! = texnum . GetIndex ( ) ) return true ; // Do not substitute a localized variant of the graphics patch.
2021-12-30 09:30:21 +00:00
2020-05-24 14:11:10 +00:00
// For mode 4 we are done now.
if ( locmode = = 4 ) return false ;
2021-12-30 09:30:21 +00:00
2020-05-24 14:11:10 +00:00
// Mode 2 and 3 must reject any text replacement from the default language tables.
if ( ( langtable & MAKE_ID ( 255 , 0 , 0 , 0 ) ) = = MAKE_ID ( ' * ' , 0 , 0 , 0 ) ) return true ; // Do not substitute if the string comes from the default table.
if ( locmode = = 2 ) return false ;
2021-12-30 09:30:21 +00:00
2020-05-24 14:11:10 +00:00
// Mode 3 must also reject substitutions for non-IWAD content.
2020-05-25 21:59:07 +00:00
int file = fileSystem . GetFileContainer ( Textures [ texnum . GetIndex ( ) ] . Texture - > GetSourceLump ( ) ) ;
2020-05-24 14:11:10 +00:00
if ( file > fileSystem . GetMaxIwadNum ( ) ) return true ;
return false ;
}
//==========================================================================
//
// FTextureManager :: AddTexture
//
//==========================================================================
2020-05-25 21:59:07 +00:00
FTextureID FTextureManager : : AddGameTexture ( FGameTexture * texture , bool addtohash )
2020-05-24 14:11:10 +00:00
{
int bucket ;
int hash ;
if ( texture = = NULL ) return FTextureID ( - 1 ) ;
2020-05-25 21:59:07 +00:00
if ( texture - > GetTexture ( ) )
{
// Later textures take precedence over earlier ones
calcShouldUpscale ( texture ) ; // calculate this once at insertion
}
2020-05-24 14:11:10 +00:00
// Textures without name can't be looked for
2020-05-25 21:59:07 +00:00
if ( addtohash & & texture - > GetName ( ) . IsNotEmpty ( ) )
2020-05-24 14:11:10 +00:00
{
2020-05-25 21:59:07 +00:00
bucket = int ( MakeKey ( texture - > GetName ( ) ) % HASH_SIZE ) ;
2020-05-24 14:11:10 +00:00
hash = HashFirst [ bucket ] ;
}
else
{
bucket = - 1 ;
hash = - 1 ;
}
2022-11-25 09:03:04 +00:00
TextureDescriptor hasher = { texture , - 1 , - 1 , - 1 , hash } ;
2020-05-24 14:11:10 +00:00
int trans = Textures . Push ( hasher ) ;
Translation . Push ( trans ) ;
if ( bucket > = 0 ) HashFirst [ bucket ] = trans ;
2020-05-25 21:59:07 +00:00
auto id = FTextureID ( trans ) ;
texture - > SetID ( id ) ;
return id ;
2020-05-24 14:11:10 +00:00
}
//==========================================================================
//
// FTextureManager :: CreateTexture
//
// Calls FTexture::CreateTexture and adds the texture to the manager.
//
//==========================================================================
FTextureID FTextureManager : : CreateTexture ( int lumpnum , ETextureType usetype )
{
if ( lumpnum ! = - 1 )
{
FString str ;
2021-07-05 20:39:24 +00:00
if ( ! usefullnames )
fileSystem . GetFileShortName ( str , lumpnum ) ;
else
{
auto fn = fileSystem . GetFileFullName ( lumpnum ) ;
str = ExtractFileBase ( fn ) ;
}
2020-05-25 21:59:07 +00:00
auto out = MakeGameTexture ( CreateTextureFromLump ( lumpnum , usetype = = ETextureType : : Flat ) , str , usetype ) ;
2020-05-24 14:11:10 +00:00
2020-05-25 21:59:07 +00:00
if ( out ! = NULL )
{
if ( usetype = = ETextureType : : Flat )
{
int w = out - > GetTexelWidth ( ) ;
int h = out - > GetTexelHeight ( ) ;
// Auto-scale flats with dimensions 128x128 and 256x256.
// In hindsight, a bad idea, but RandomLag made it sound better than it really is.
// Now we're stuck with this stupid behaviour.
if ( w = = 128 & & h = = 128 )
{
out - > SetScale ( 2 , 2 ) ;
out - > SetWorldPanning ( true ) ;
}
else if ( w = = 256 & & h = = 256 )
{
out - > SetScale ( 4 , 4 ) ;
out - > SetWorldPanning ( true ) ;
}
}
return AddGameTexture ( out ) ;
}
2020-05-24 14:11:10 +00:00
else
{
Printf ( TEXTCOLOR_ORANGE " Invalid data encountered for texture %s \n " , fileSystem . GetFileFullPath ( lumpnum ) . GetChars ( ) ) ;
return FTextureID ( - 1 ) ;
}
}
return FTextureID ( - 1 ) ;
}
//==========================================================================
//
// FTextureManager :: ReplaceTexture
//
//==========================================================================
2020-05-25 21:59:07 +00:00
void FTextureManager : : ReplaceTexture ( FTextureID picnum , FGameTexture * newtexture , bool free )
2020-05-24 14:11:10 +00:00
{
int index = picnum . GetIndex ( ) ;
if ( unsigned ( index ) > = Textures . Size ( ) )
return ;
2021-02-12 13:44:54 +00:00
if ( newtexture - > GetTexture ( ) )
{
calcShouldUpscale ( newtexture ) ; // calculate this once at insertion
}
2020-05-25 21:59:07 +00:00
auto oldtexture = Textures [ index ] . Texture ;
2020-05-24 14:11:10 +00:00
2020-05-25 21:59:07 +00:00
newtexture - > SetName ( oldtexture - > GetName ( ) ) ;
newtexture - > SetUseType ( oldtexture - > GetUseType ( ) ) ;
2020-05-24 14:11:10 +00:00
Textures [ index ] . Texture = newtexture ;
2020-05-25 21:59:07 +00:00
newtexture - > SetID ( oldtexture - > GetID ( ) ) ;
oldtexture - > SetName ( " " ) ;
AddGameTexture ( oldtexture ) ;
2020-05-24 14:11:10 +00:00
}
//==========================================================================
//
// FTextureManager :: AreTexturesCompatible
//
// Checks if 2 textures are compatible for a ranged animation
//
//==========================================================================
bool FTextureManager : : AreTexturesCompatible ( FTextureID picnum1 , FTextureID picnum2 )
{
int index1 = picnum1 . GetIndex ( ) ;
int index2 = picnum2 . GetIndex ( ) ;
if ( unsigned ( index1 ) > = Textures . Size ( ) | | unsigned ( index2 ) > = Textures . Size ( ) )
return false ;
2020-05-25 21:59:07 +00:00
auto texture1 = Textures [ index1 ] . Texture ;
auto texture2 = Textures [ index2 ] . Texture ;
2020-05-24 14:11:10 +00:00
// both textures must be the same type.
2020-05-25 21:59:07 +00:00
if ( texture1 = = NULL | | texture2 = = NULL | | texture1 - > GetUseType ( ) ! = texture2 - > GetUseType ( ) )
2020-05-24 14:11:10 +00:00
return false ;
// both textures must be from the same file
for ( unsigned i = 0 ; i < FirstTextureForFile . Size ( ) - 1 ; i + + )
{
if ( index1 > = FirstTextureForFile [ i ] & & index1 < FirstTextureForFile [ i + 1 ] )
{
return ( index2 > = FirstTextureForFile [ i ] & & index2 < FirstTextureForFile [ i + 1 ] ) ;
}
}
return false ;
}
//==========================================================================
//
// FTextureManager :: AddGroup
//
//==========================================================================
void FTextureManager : : AddGroup ( int wadnum , int ns , ETextureType usetype )
{
int firsttx = fileSystem . GetFirstEntry ( wadnum ) ;
int lasttx = fileSystem . GetLastEntry ( wadnum ) ;
FString Name ;
2021-07-05 20:39:24 +00:00
if ( ! usefullnames )
2020-05-24 14:11:10 +00:00
{
2021-07-05 20:39:24 +00:00
// Go from first to last so that ANIMDEFS work as expected. However,
// to avoid duplicates (and to keep earlier entries from overriding
// later ones), the texture is only inserted if it is the one returned
// by doing a check by name in the list of wads.
for ( ; firsttx < = lasttx ; + + firsttx )
2020-05-24 14:11:10 +00:00
{
2021-07-05 20:39:24 +00:00
if ( fileSystem . GetFileNamespace ( firsttx ) = = ns )
{
fileSystem . GetFileShortName ( Name , firsttx ) ;
2020-05-24 14:11:10 +00:00
2021-07-05 20:39:24 +00:00
if ( fileSystem . CheckNumForName ( Name , ns ) = = firsttx )
{
CreateTexture ( firsttx , usetype ) ;
}
progressFunc ( ) ;
}
else if ( ns = = ns_flats & & fileSystem . GetFileFlags ( firsttx ) & LUMPF_MAYBEFLAT )
2020-05-24 14:11:10 +00:00
{
2021-07-05 20:39:24 +00:00
if ( fileSystem . CheckNumForName ( Name , ns ) < firsttx )
{
CreateTexture ( firsttx , usetype ) ;
}
progressFunc ( ) ;
2020-05-24 14:11:10 +00:00
}
}
2021-07-05 20:39:24 +00:00
}
else
{
// The duplicate check does not work with this (yet.)
for ( ; firsttx < = lasttx ; + + firsttx )
2020-05-24 14:11:10 +00:00
{
2021-07-05 20:39:24 +00:00
if ( fileSystem . GetFileNamespace ( firsttx ) = = ns )
2020-05-24 14:11:10 +00:00
{
2021-07-05 20:39:24 +00:00
CreateTexture ( firsttx , usetype ) ;
2020-05-24 14:11:10 +00:00
}
}
}
}
//==========================================================================
//
// Adds all hires texture definitions.
//
//==========================================================================
void FTextureManager : : AddHiresTextures ( int wadnum )
{
int firsttx = fileSystem . GetFirstEntry ( wadnum ) ;
int lasttx = fileSystem . GetLastEntry ( wadnum ) ;
FString Name ;
TArray < FTextureID > tlist ;
if ( firsttx = = - 1 | | lasttx = = - 1 )
{
return ;
}
for ( ; firsttx < = lasttx ; + + firsttx )
{
if ( fileSystem . GetFileNamespace ( firsttx ) = = ns_hires )
{
fileSystem . GetFileShortName ( Name , firsttx ) ;
if ( fileSystem . CheckNumForName ( Name , ns_hires ) = = firsttx )
{
tlist . Clear ( ) ;
int amount = ListTextures ( Name , tlist ) ;
if ( amount = = 0 )
{
// A texture with this name does not yet exist
2020-05-25 21:59:07 +00:00
auto newtex = MakeGameTexture ( CreateTextureFromLump ( firsttx ) , Name , ETextureType : : Override ) ;
2020-05-24 14:11:10 +00:00
if ( newtex ! = NULL )
{
2020-05-25 21:59:07 +00:00
AddGameTexture ( newtex ) ;
2020-05-24 14:11:10 +00:00
}
}
else
{
for ( unsigned int i = 0 ; i < tlist . Size ( ) ; i + + )
{
2020-05-25 21:59:07 +00:00
FTexture * newtex = CreateTextureFromLump ( firsttx ) ;
2020-05-24 14:11:10 +00:00
if ( newtex ! = NULL )
{
2020-05-25 21:59:07 +00:00
auto oldtex = Textures [ tlist [ i ] . GetIndex ( ) ] . Texture ;
2020-05-24 14:11:10 +00:00
// Replace the entire texture and adjust the scaling and offset factors.
2020-05-25 21:59:07 +00:00
auto gtex = MakeGameTexture ( newtex , nullptr , ETextureType : : Override ) ;
gtex - > SetWorldPanning ( true ) ;
gtex - > SetDisplaySize ( oldtex - > GetDisplayWidth ( ) , oldtex - > GetDisplayHeight ( ) ) ;
2020-06-07 13:02:54 +00:00
double xscale1 = oldtex - > GetTexelLeftOffset ( 0 ) * gtex - > GetScaleX ( ) / oldtex - > GetScaleX ( ) ;
double xscale2 = oldtex - > GetTexelLeftOffset ( 1 ) * gtex - > GetScaleX ( ) / oldtex - > GetScaleX ( ) ;
double yscale1 = oldtex - > GetTexelTopOffset ( 0 ) * gtex - > GetScaleY ( ) / oldtex - > GetScaleY ( ) ;
double yscale2 = oldtex - > GetTexelTopOffset ( 1 ) * gtex - > GetScaleY ( ) / oldtex - > GetScaleY ( ) ;
gtex - > SetOffsets ( 0 , xs_RoundToInt ( xscale1 ) , xs_RoundToInt ( yscale1 ) ) ;
gtex - > SetOffsets ( 1 , xs_RoundToInt ( xscale2 ) , xs_RoundToInt ( yscale2 ) ) ;
2020-05-25 21:59:07 +00:00
ReplaceTexture ( tlist [ i ] , gtex , true ) ;
2020-05-24 14:11:10 +00:00
}
}
}
progressFunc ( ) ;
}
}
}
}
//==========================================================================
//
// Loads the HIRESTEX lumps
//
//==========================================================================
void FTextureManager : : LoadTextureDefs ( int wadnum , const char * lumpname , FMultipatchTextureBuilder & build )
{
int remapLump , lastLump ;
lastLump = 0 ;
while ( ( remapLump = fileSystem . FindLump ( lumpname , & lastLump ) ) ! = - 1 )
{
if ( fileSystem . GetFileContainer ( remapLump ) = = wadnum )
{
ParseTextureDef ( remapLump , build ) ;
}
}
}
void FTextureManager : : ParseTextureDef ( int lump , FMultipatchTextureBuilder & build )
{
TArray < FTextureID > tlist ;
FScanner sc ( lump ) ;
while ( sc . GetString ( ) )
{
if ( sc . Compare ( " remap " ) ) // remap an existing texture
{
sc . MustGetString ( ) ;
// allow selection by type
int mode ;
ETextureType type ;
if ( sc . Compare ( " wall " ) ) type = ETextureType : : Wall , mode = FTextureManager : : TEXMAN_Overridable ;
else if ( sc . Compare ( " flat " ) ) type = ETextureType : : Flat , mode = FTextureManager : : TEXMAN_Overridable ;
else if ( sc . Compare ( " sprite " ) ) type = ETextureType : : Sprite , mode = 0 ;
else type = ETextureType : : Any , mode = 0 ;
if ( type ! = ETextureType : : Any ) sc . MustGetString ( ) ;
sc . String [ 8 ] = 0 ;
tlist . Clear ( ) ;
2021-11-14 14:03:50 +00:00
ListTextures ( sc . String , tlist ) ;
2020-05-24 14:11:10 +00:00
FName texname = sc . String ;
sc . MustGetString ( ) ;
int lumpnum = fileSystem . CheckNumForFullName ( sc . String , true , ns_patches ) ;
if ( lumpnum = = - 1 ) lumpnum = fileSystem . CheckNumForFullName ( sc . String , true , ns_graphics ) ;
if ( tlist . Size ( ) = = 0 )
{
Printf ( " Attempting to remap non-existent texture %s to %s \n " ,
texname . GetChars ( ) , sc . String ) ;
}
else if ( lumpnum = = - 1 )
{
Printf ( " Attempting to remap texture %s to non-existent lump %s \n " ,
texname . GetChars ( ) , sc . String ) ;
}
else
{
for ( unsigned int i = 0 ; i < tlist . Size ( ) ; i + + )
{
2020-05-25 21:59:07 +00:00
auto oldtex = Textures [ tlist [ i ] . GetIndex ( ) ] . Texture ;
2020-05-24 14:11:10 +00:00
int sl ;
// only replace matching types. For sprites also replace any MiscPatches
// based on the same lump. These can be created for icons.
2020-05-25 21:59:07 +00:00
if ( oldtex - > GetUseType ( ) = = type | | type = = ETextureType : : Any | |
( mode = = TEXMAN_Overridable & & oldtex - > GetUseType ( ) = = ETextureType : : Override ) | |
( type = = ETextureType : : Sprite & & oldtex - > GetUseType ( ) = = ETextureType : : MiscPatch & &
2020-05-24 14:11:10 +00:00
( sl = oldtex - > GetSourceLump ( ) ) > = 0 & & fileSystem . GetFileNamespace ( sl ) = = ns_sprites )
)
{
2020-05-25 21:59:07 +00:00
FTexture * newtex = CreateTextureFromLump ( lumpnum ) ;
2020-05-24 14:11:10 +00:00
if ( newtex ! = NULL )
{
// Replace the entire texture and adjust the scaling and offset factors.
2020-05-25 21:59:07 +00:00
auto gtex = MakeGameTexture ( newtex , nullptr , ETextureType : : Override ) ;
gtex - > SetWorldPanning ( true ) ;
gtex - > SetDisplaySize ( oldtex - > GetDisplayWidth ( ) , oldtex - > GetDisplayHeight ( ) ) ;
2020-06-07 13:02:54 +00:00
double xscale1 = oldtex - > GetTexelLeftOffset ( 0 ) * gtex - > GetScaleX ( ) / oldtex - > GetScaleX ( ) ;
double xscale2 = oldtex - > GetTexelLeftOffset ( 1 ) * gtex - > GetScaleX ( ) / oldtex - > GetScaleX ( ) ;
double yscale1 = oldtex - > GetTexelTopOffset ( 0 ) * gtex - > GetScaleY ( ) / oldtex - > GetScaleY ( ) ;
double yscale2 = oldtex - > GetTexelTopOffset ( 1 ) * gtex - > GetScaleY ( ) / oldtex - > GetScaleY ( ) ;
gtex - > SetOffsets ( 0 , xs_RoundToInt ( xscale1 ) , xs_RoundToInt ( yscale1 ) ) ;
gtex - > SetOffsets ( 1 , xs_RoundToInt ( xscale2 ) , xs_RoundToInt ( yscale2 ) ) ;
2020-05-25 21:59:07 +00:00
ReplaceTexture ( tlist [ i ] , gtex , true ) ;
2020-05-24 14:11:10 +00:00
}
}
}
}
}
else if ( sc . Compare ( " define " ) ) // define a new "fake" texture
{
sc . GetString ( ) ;
2021-12-30 09:30:21 +00:00
2020-05-24 14:11:10 +00:00
FString base = ExtractFileBase ( sc . String , false ) ;
if ( ! base . IsEmpty ( ) )
{
FString src = base . Left ( 8 ) ;
int lumpnum = fileSystem . CheckNumForFullName ( sc . String , true , ns_patches ) ;
if ( lumpnum = = - 1 ) lumpnum = fileSystem . CheckNumForFullName ( sc . String , true , ns_graphics ) ;
sc . GetString ( ) ;
bool is32bit = ! ! sc . Compare ( " force32bit " ) ;
if ( ! is32bit ) sc . UnGet ( ) ;
sc . MustGetNumber ( ) ;
int width = sc . Number ;
sc . MustGetNumber ( ) ;
int height = sc . Number ;
if ( lumpnum > = 0 )
{
2020-05-25 21:59:07 +00:00
auto newtex = MakeGameTexture ( CreateTextureFromLump ( lumpnum ) , src , ETextureType : : Override ) ;
2020-05-24 14:11:10 +00:00
if ( newtex ! = NULL )
{
// Replace the entire texture and adjust the scaling and offset factors.
2020-05-25 21:59:07 +00:00
newtex - > SetWorldPanning ( true ) ;
newtex - > SetDisplaySize ( ( float ) width , ( float ) height ) ;
2020-05-24 14:11:10 +00:00
FTextureID oldtex = TexMan . CheckForTexture ( src , ETextureType : : MiscPatch ) ;
if ( oldtex . isValid ( ) )
{
ReplaceTexture ( oldtex , newtex , true ) ;
2020-05-25 21:59:07 +00:00
newtex - > SetUseType ( ETextureType : : Override ) ;
2020-05-24 14:11:10 +00:00
}
2020-05-25 21:59:07 +00:00
else AddGameTexture ( newtex ) ;
2020-05-24 14:11:10 +00:00
}
}
}
//else Printf("Unable to define hires texture '%s'\n", tex->Name);
}
2021-12-29 09:29:22 +00:00
else if ( sc . Compare ( " notrim " ) )
{
sc . MustGetString ( ) ;
FTextureID id = TexMan . CheckForTexture ( sc . String , ETextureType : : Sprite ) ;
if ( id . isValid ( ) )
{
FGameTexture * tex = TexMan . GetGameTexture ( id ) ;
if ( tex ) tex - > SetNoTrimming ( true ) ;
else sc . ScriptError ( " NoTrim: %s not found " , sc . String ) ;
}
else
sc . ScriptError ( " NoTrim: %s is not a sprite " , sc . String ) ;
}
2020-05-24 14:11:10 +00:00
else if ( sc . Compare ( " texture " ) )
{
2020-06-11 07:15:44 +00:00
build . ParseTexture ( sc , ETextureType : : Override , lump ) ;
2020-05-24 14:11:10 +00:00
}
else if ( sc . Compare ( " sprite " ) )
{
2020-06-11 07:15:44 +00:00
build . ParseTexture ( sc , ETextureType : : Sprite , lump ) ;
2020-05-24 14:11:10 +00:00
}
else if ( sc . Compare ( " walltexture " ) )
{
2020-06-11 07:15:44 +00:00
build . ParseTexture ( sc , ETextureType : : Wall , lump ) ;
2020-05-24 14:11:10 +00:00
}
else if ( sc . Compare ( " flat " ) )
{
2020-06-11 07:15:44 +00:00
build . ParseTexture ( sc , ETextureType : : Flat , lump ) ;
2020-05-24 14:11:10 +00:00
}
else if ( sc . Compare ( " graphic " ) )
{
2020-06-11 07:15:44 +00:00
build . ParseTexture ( sc , ETextureType : : MiscPatch , lump ) ;
2020-05-24 14:11:10 +00:00
}
else if ( sc . Compare ( " #include " ) )
{
sc . MustGetString ( ) ;
// This is not using sc.Open because it can print a more useful error message when done here
int includelump = fileSystem . CheckNumForFullName ( sc . String , true ) ;
if ( includelump = = - 1 )
{
sc . ScriptError ( " Lump '%s' not found " , sc . String ) ;
}
else
{
ParseTextureDef ( includelump , build ) ;
}
}
else
{
sc . ScriptError ( " Texture definition expected, found '%s' " , sc . String ) ;
}
}
}
//==========================================================================
//
// FTextureManager :: AddPatches
//
//==========================================================================
void FTextureManager : : AddPatches ( int lumpnum )
{
auto file = fileSystem . ReopenFileReader ( lumpnum , true ) ;
uint32_t numpatches , i ;
char name [ 9 ] ;
numpatches = file . ReadUInt32 ( ) ;
name [ 8 ] = ' \0 ' ;
for ( i = 0 ; i < numpatches ; + + i )
{
file . Read ( name , 8 ) ;
if ( CheckForTexture ( name , ETextureType : : WallPatch , 0 ) = = - 1 )
{
CreateTexture ( fileSystem . CheckNumForName ( name , ns_patches ) , ETextureType : : WallPatch ) ;
}
progressFunc ( ) ;
}
}
//==========================================================================
//
// FTextureManager :: LoadTexturesX
//
// Initializes the texture list with the textures from the world map.
//
//==========================================================================
void FTextureManager : : LoadTextureX ( int wadnum , FMultipatchTextureBuilder & build )
{
// Use the most recent PNAMES for this WAD.
// Multiple PNAMES in a WAD will be ignored.
int pnames = fileSystem . CheckNumForName ( " PNAMES " , ns_global , wadnum , false ) ;
if ( pnames < 0 )
{
// should never happen except for zdoom.pk3
return ;
}
// Only add the patches if the PNAMES come from the current file
// Otherwise they have already been processed.
if ( fileSystem . GetFileContainer ( pnames ) = = wadnum ) TexMan . AddPatches ( pnames ) ;
int texlump1 = fileSystem . CheckNumForName ( " TEXTURE1 " , ns_global , wadnum ) ;
int texlump2 = fileSystem . CheckNumForName ( " TEXTURE2 " , ns_global , wadnum ) ;
build . AddTexturesLumps ( texlump1 , texlump2 , pnames ) ;
}
//==========================================================================
//
// FTextureManager :: AddTexturesForWad
//
//==========================================================================
void FTextureManager : : AddTexturesForWad ( int wadnum , FMultipatchTextureBuilder & build )
{
int firsttexture = Textures . Size ( ) ;
bool iwad = wadnum > = fileSystem . GetIwadNum ( ) & & wadnum < = fileSystem . GetMaxIwadNum ( ) ;
FirstTextureForFile . Push ( firsttexture ) ;
// First step: Load sprites
AddGroup ( wadnum , ns_sprites , ETextureType : : Sprite ) ;
// When loading a Zip, all graphics in the patches/ directory should be
// added as well.
AddGroup ( wadnum , ns_patches , ETextureType : : WallPatch ) ;
// Second step: TEXTUREx lumps
LoadTextureX ( wadnum , build ) ;
// Third step: Flats
AddGroup ( wadnum , ns_flats , ETextureType : : Flat ) ;
// Fourth step: Textures (TX_)
AddGroup ( wadnum , ns_newtextures , ETextureType : : Override ) ;
// Sixth step: Try to find any lump in the WAD that may be a texture and load as a TEX_MiscPatch
int firsttx = fileSystem . GetFirstEntry ( wadnum ) ;
int lasttx = fileSystem . GetLastEntry ( wadnum ) ;
for ( int i = firsttx ; i < = lasttx ; i + + )
{
bool skin = false ;
FString Name ;
fileSystem . GetFileShortName ( Name , i ) ;
// Ignore anything not in the global namespace
int ns = fileSystem . GetFileNamespace ( i ) ;
if ( ns = = ns_global )
{
// In Zips all graphics must be in a separate namespace.
if ( fileSystem . GetFileFlags ( i ) & LUMPF_FULLPATH ) continue ;
// Ignore lumps with empty names.
if ( fileSystem . CheckFileName ( i , " " ) ) continue ;
// Ignore anything belonging to a map
if ( fileSystem . CheckFileName ( i , " THINGS " ) ) continue ;
if ( fileSystem . CheckFileName ( i , " LINEDEFS " ) ) continue ;
if ( fileSystem . CheckFileName ( i , " SIDEDEFS " ) ) continue ;
if ( fileSystem . CheckFileName ( i , " VERTEXES " ) ) continue ;
if ( fileSystem . CheckFileName ( i , " SEGS " ) ) continue ;
if ( fileSystem . CheckFileName ( i , " SSECTORS " ) ) continue ;
if ( fileSystem . CheckFileName ( i , " NODES " ) ) continue ;
if ( fileSystem . CheckFileName ( i , " SECTORS " ) ) continue ;
if ( fileSystem . CheckFileName ( i , " REJECT " ) ) continue ;
if ( fileSystem . CheckFileName ( i , " BLOCKMAP " ) ) continue ;
if ( fileSystem . CheckFileName ( i , " BEHAVIOR " ) ) continue ;
bool force = false ;
// Don't bother looking at this lump if something later overrides it.
if ( fileSystem . CheckNumForName ( Name , ns_graphics ) ! = i )
{
if ( iwad )
{
// We need to make an exception for font characters of the SmallFont coming from the IWAD to be able to construct the original font.
if ( Name . IndexOf ( " STCFN " ) ! = 0 & & Name . IndexOf ( " FONTA " ) ! = 0 ) continue ;
force = true ;
}
else continue ;
}
// skip this if it has already been added as a wall patch.
if ( ! force & & CheckForTexture ( Name , ETextureType : : WallPatch , 0 ) . Exists ( ) ) continue ;
}
else if ( ns = = ns_graphics )
{
if ( fileSystem . CheckNumForName ( Name , ns_graphics ) ! = i )
{
if ( iwad )
{
// We need to make an exception for font characters of the SmallFont coming from the IWAD to be able to construct the original font.
if ( Name . IndexOf ( " STCFN " ) ! = 0 & & Name . IndexOf ( " FONTA " ) ! = 0 ) continue ;
}
else continue ;
}
}
else if ( ns > = ns_firstskin )
{
// Don't bother looking this lump if something later overrides it.
if ( fileSystem . CheckNumForName ( Name , ns ) ! = i ) continue ;
skin = true ;
}
else continue ;
// Try to create a texture from this lump and add it.
// Unfortunately we have to look at everything that comes through here...
2020-05-25 21:59:07 +00:00
auto out = MakeGameTexture ( CreateTextureFromLump ( i ) , Name , skin ? ETextureType : : SkinGraphic : ETextureType : : MiscPatch ) ;
2020-05-24 14:11:10 +00:00
if ( out ! = NULL )
{
2020-05-25 21:59:07 +00:00
AddGameTexture ( out ) ;
2020-05-24 14:11:10 +00:00
}
}
// Check for text based texture definitions
LoadTextureDefs ( wadnum , " TEXTURES " , build ) ;
LoadTextureDefs ( wadnum , " HIRESTEX " , build ) ;
// Seventh step: Check for hires replacements.
AddHiresTextures ( wadnum ) ;
SortTexturesByType ( firsttexture , Textures . Size ( ) ) ;
}
//==========================================================================
//
// FTextureManager :: SortTexturesByType
// sorts newly added textures by UseType so that anything defined
// in TEXTURES and HIRESTEX gets in its proper place.
//
//==========================================================================
void FTextureManager : : SortTexturesByType ( int start , int end )
{
2020-05-25 21:59:07 +00:00
TArray < FGameTexture * > newtextures ;
2020-05-24 14:11:10 +00:00
// First unlink all newly added textures from the hash chain
for ( int i = 0 ; i < HASH_SIZE ; i + + )
{
while ( HashFirst [ i ] > = start & & HashFirst [ i ] ! = HASH_END )
{
HashFirst [ i ] = Textures [ HashFirst [ i ] ] . HashNext ;
}
}
newtextures . Resize ( end - start ) ;
for ( int i = start ; i < end ; i + + )
{
newtextures [ i - start ] = Textures [ i ] . Texture ;
}
Textures . Resize ( start ) ;
Translation . Resize ( start ) ;
static ETextureType texturetypes [ ] = {
ETextureType : : Sprite , ETextureType : : Null , ETextureType : : FirstDefined ,
ETextureType : : WallPatch , ETextureType : : Wall , ETextureType : : Flat ,
ETextureType : : Override , ETextureType : : MiscPatch , ETextureType : : SkinGraphic
} ;
for ( unsigned int i = 0 ; i < countof ( texturetypes ) ; i + + )
{
for ( unsigned j = 0 ; j < newtextures . Size ( ) ; j + + )
{
2020-05-25 21:59:07 +00:00
if ( newtextures [ j ] ! = NULL & & newtextures [ j ] - > GetUseType ( ) = = texturetypes [ i ] )
2020-05-24 14:11:10 +00:00
{
2020-05-25 21:59:07 +00:00
AddGameTexture ( newtextures [ j ] ) ;
2020-05-24 14:11:10 +00:00
newtextures [ j ] = NULL ;
}
}
}
// This should never happen. All other UseTypes are only used outside
for ( unsigned j = 0 ; j < newtextures . Size ( ) ; j + + )
{
if ( newtextures [ j ] ! = NULL )
{
2020-05-25 21:59:07 +00:00
Printf ( " Texture %s has unknown type! \n " , newtextures [ j ] - > GetName ( ) . GetChars ( ) ) ;
AddGameTexture ( newtextures [ j ] ) ;
2020-05-24 14:11:10 +00:00
}
}
}
//==========================================================================
//
// FTextureManager :: AddLocalizedVariants
//
//==========================================================================
void FTextureManager : : AddLocalizedVariants ( )
{
TArray < FolderEntry > content ;
fileSystem . GetFilesInFolder ( " localized/textures/ " , content , false ) ;
for ( auto & entry : content )
{
FString name = entry . name ;
auto tokens = name . Split ( " . " , FString : : TOK_SKIPEMPTY ) ;
if ( tokens . Size ( ) = = 2 )
{
auto ext = tokens [ 1 ] ;
// Do not interpret common extensions for images as language IDs.
if ( ext . CompareNoCase ( " png " ) = = 0 | | ext . CompareNoCase ( " jpg " ) = = 0 | | ext . CompareNoCase ( " gfx " ) = = 0 | | ext . CompareNoCase ( " tga " ) = = 0 | | ext . CompareNoCase ( " lmp " ) = = 0 )
{
Printf ( " %s contains no language IDs and will be ignored \n " , entry . name ) ;
continue ;
}
}
if ( tokens . Size ( ) > = 2 )
{
FString base = ExtractFileBase ( tokens [ 0 ] ) ;
FTextureID origTex = CheckForTexture ( base , ETextureType : : MiscPatch ) ;
if ( origTex . isValid ( ) )
{
FTextureID tex = CheckForTexture ( entry . name , ETextureType : : MiscPatch ) ;
if ( tex . isValid ( ) )
{
2020-05-25 21:59:07 +00:00
auto otex = GetGameTexture ( origTex ) ;
auto ntex = GetGameTexture ( tex ) ;
2020-05-24 14:11:10 +00:00
if ( otex - > GetDisplayWidth ( ) ! = ntex - > GetDisplayWidth ( ) | | otex - > GetDisplayHeight ( ) ! = ntex - > GetDisplayHeight ( ) )
{
Printf ( " Localized texture %s must be the same size as the one it replaces \n " , entry . name ) ;
}
else
{
tokens [ 1 ] . ToLower ( ) ;
auto langids = tokens [ 1 ] . Split ( " - " , FString : : TOK_SKIPEMPTY ) ;
for ( auto & lang : langids )
{
if ( lang . Len ( ) = = 2 | | lang . Len ( ) = = 3 )
{
uint32_t langid = MAKE_ID ( lang [ 0 ] , lang [ 1 ] , lang [ 2 ] , 0 ) ;
uint64_t comboid = ( uint64_t ( langid ) < < 32 ) | origTex . GetIndex ( ) ;
LocalizedTextures . Insert ( comboid , tex . GetIndex ( ) ) ;
2022-11-25 09:03:04 +00:00
Textures [ origTex . GetIndex ( ) ] . Flags | = TEXFLAG_HASLOCALIZATION ;
2020-05-24 14:11:10 +00:00
}
else
{
Printf ( " Invalid language ID in texture %s \n " , entry . name ) ;
}
}
}
}
else
{
Printf ( " %s is not a texture \n " , entry . name ) ;
}
}
else
{
Printf ( " Unknown texture %s for localized variant %s \n " , tokens [ 0 ] . GetChars ( ) , entry . name ) ;
}
}
else
{
Printf ( " %s contains no language IDs and will be ignored \n " , entry . name ) ;
}
}
}
//==========================================================================
//
// FTextureManager :: Init
//
//==========================================================================
2020-05-25 21:59:07 +00:00
FGameTexture * CreateShaderTexture ( bool , bool ) ;
FImageSource * CreateEmptyTexture ( ) ;
2020-05-24 14:11:10 +00:00
2022-06-06 09:45:02 +00:00
void FTextureManager : : Init ( )
2020-05-24 14:11:10 +00:00
{
DeleteAll ( ) ;
2022-06-06 09:45:02 +00:00
// Add all the static content
2020-05-25 21:59:07 +00:00
auto nulltex = MakeGameTexture ( new FImageTexture ( CreateEmptyTexture ( ) ) , nullptr , ETextureType : : Null ) ;
AddGameTexture ( nulltex ) ;
// This is for binding to unused texture units, because accessing an unbound texture unit is undefined. It's a one pixel empty texture.
auto emptytex = MakeGameTexture ( new FImageTexture ( CreateEmptyTexture ( ) ) , nullptr , ETextureType : : Override ) ;
emptytex - > SetSize ( 1 , 1 ) ;
2022-06-06 09:45:02 +00:00
AddGameTexture ( emptytex ) ;
2020-05-24 14:11:10 +00:00
// some special textures used in the game.
2020-05-25 21:59:07 +00:00
AddGameTexture ( CreateShaderTexture ( false , false ) ) ;
AddGameTexture ( CreateShaderTexture ( false , true ) ) ;
AddGameTexture ( CreateShaderTexture ( true , false ) ) ;
AddGameTexture ( CreateShaderTexture ( true , true ) ) ;
2020-06-14 18:27:13 +00:00
// Add two animtexture entries so that movie playback can call functions using texture IDs.
2021-11-28 12:46:01 +00:00
auto mt = MakeGameTexture ( new AnimTexture ( ) , " AnimTextureFrame1 " , ETextureType : : Override ) ;
2021-11-28 16:38:09 +00:00
mt - > SetUpscaleFlag ( false , true ) ;
2021-11-28 12:46:01 +00:00
AddGameTexture ( mt ) ;
mt = MakeGameTexture ( new AnimTexture ( ) , " AnimTextureFrame2 " , ETextureType : : Override ) ;
2021-11-28 16:38:09 +00:00
mt - > SetUpscaleFlag ( false , true ) ;
2021-11-28 12:46:01 +00:00
AddGameTexture ( mt ) ;
2022-06-06 09:45:02 +00:00
}
2022-10-02 18:33:18 +00:00
void FTextureManager : : AddTextures ( void ( * progressFunc_ ) ( ) , void ( * checkForHacks ) ( BuildInfo & ) , void ( * customtexturehandler ) ( ) )
2022-06-06 09:45:02 +00:00
{
progressFunc = progressFunc_ ;
//if (BuildTileFiles.Size() == 0) CountBuildTiles ();
2020-05-24 14:11:10 +00:00
int wadcnt = fileSystem . GetNumWads ( ) ;
FMultipatchTextureBuilder build ( * this , progressFunc_ , checkForHacks ) ;
for ( int i = 0 ; i < wadcnt ; i + + )
{
AddTexturesForWad ( i , build ) ;
}
build . ResolveAllPatches ( ) ;
// Add one marker so that the last WAD is easier to handle and treat
2022-10-02 18:33:18 +00:00
// custom textures as a completely separate block.
2020-05-24 14:11:10 +00:00
FirstTextureForFile . Push ( Textures . Size ( ) ) ;
2022-10-02 18:33:18 +00:00
if ( customtexturehandler ) customtexturehandler ( ) ;
2020-05-24 14:11:10 +00:00
FirstTextureForFile . Push ( Textures . Size ( ) ) ;
DefaultTexture = CheckForTexture ( " -NOFLAT- " , ETextureType : : Override , 0 ) ;
InitPalettedVersions ( ) ;
AdjustSpriteOffsets ( ) ;
// Add auto materials to each texture after everything has been set up.
// Textures array can be reallocated in process, so ranged for loop is not suitable.
// There is no need to process discovered material textures here,
// CheckForTexture() did this already.
for ( unsigned int i = 0 , count = Textures . Size ( ) ; i < count ; + + i )
{
Textures [ i ] . Texture - > AddAutoMaterials ( ) ;
}
glPart2 = TexMan . CheckForTexture ( " glstuff/glpart2.png " , ETextureType : : MiscPatch ) ;
glPart = TexMan . CheckForTexture ( " glstuff/glpart.png " , ETextureType : : MiscPatch ) ;
mirrorTexture = TexMan . CheckForTexture ( " glstuff/mirror.png " , ETextureType : : MiscPatch ) ;
AddLocalizedVariants ( ) ;
2020-05-25 21:59:07 +00:00
// Make sure all ID's are correct by resetting them here to the proper index.
for ( unsigned int i = 0 , count = Textures . Size ( ) ; i < count ; + + i )
{
Textures [ i ] . Texture - > SetID ( i ) ;
}
2020-05-24 14:11:10 +00:00
}
//==========================================================================
//
// FTextureManager :: InitPalettedVersions
//
//==========================================================================
void FTextureManager : : InitPalettedVersions ( )
{
int lump , lastlump = 0 ;
while ( ( lump = fileSystem . FindLump ( " PALVERS " , & lastlump ) ) ! = - 1 )
{
FScanner sc ( lump ) ;
while ( sc . GetString ( ) )
{
FTextureID pic1 = CheckForTexture ( sc . String , ETextureType : : Any ) ;
if ( ! pic1 . isValid ( ) )
{
sc . ScriptMessage ( " Unknown texture %s to replace " , sc . String ) ;
}
sc . MustGetString ( ) ;
FTextureID pic2 = CheckForTexture ( sc . String , ETextureType : : Any ) ;
if ( ! pic2 . isValid ( ) )
{
2020-05-25 21:59:07 +00:00
sc . ScriptMessage ( " Unknown texture %s to use as paletted replacement " , sc . String ) ;
2020-05-24 14:11:10 +00:00
}
if ( pic1 . isValid ( ) & & pic2 . isValid ( ) )
{
2020-05-25 21:59:07 +00:00
Textures [ pic1 . GetIndex ( ) ] . Paletted = pic2 . GetIndex ( ) ;
2020-05-24 14:11:10 +00:00
}
}
}
}
2020-05-25 21:59:07 +00:00
//==========================================================================
//
//
//
//==========================================================================
2022-11-25 11:33:52 +00:00
FTextureID FTextureManager : : GetRawTexture ( FTextureID texid , bool dontlookup )
2020-05-25 21:59:07 +00:00
{
int texidx = texid . GetIndex ( ) ;
if ( ( unsigned ) texidx > = Textures . Size ( ) ) return texid ;
2022-11-25 11:33:52 +00:00
if ( Textures [ texidx ] . RawTexture ! = - 1 ) return FSetTextureID ( Textures [ texidx ] . RawTexture ) ;
if ( dontlookup ) return texid ;
2020-05-25 21:59:07 +00:00
// Reject anything that cannot have been a front layer for the sky in original Hexen, i.e. it needs to be an unscaled wall texture only using Doom patches.
auto tex = Textures [ texidx ] . Texture ;
auto ttex = tex - > GetTexture ( ) ;
auto image = ttex - > GetImage ( ) ;
// Reject anything that cannot have been a single-patch multipatch texture in vanilla.
if ( image = = nullptr | | image - > IsRawCompatible ( ) | | tex - > GetUseType ( ) ! = ETextureType : : Wall | | ttex - > GetWidth ( ) ! = tex - > GetDisplayWidth ( ) | |
ttex - > GetHeight ( ) ! = tex - > GetDisplayHeight ( ) )
{
Textures [ texidx ] . RawTexture = texidx ;
return texid ;
}
// Let the hackery begin
auto mptimage = static_cast < FMultiPatchTexture * > ( image ) ;
auto source = mptimage - > GetImageForPart ( 0 ) ;
// Size must match for this to work as intended
if ( source - > GetWidth ( ) ! = ttex - > GetWidth ( ) | | source - > GetHeight ( ) ! = ttex - > GetHeight ( ) )
{
Textures [ texidx ] . RawTexture = texidx ;
return texid ;
}
// Todo: later this can just link to the already existing texture for this source graphic, once it can be retrieved through the image's SourceLump index
auto RawTexture = MakeGameTexture ( new FImageTexture ( source ) , nullptr , ETextureType : : Wall ) ;
texid = TexMan . AddGameTexture ( RawTexture ) ;
Textures [ texidx ] . RawTexture = texid . GetIndex ( ) ;
Textures [ texid . GetIndex ( ) ] . RawTexture = texid . GetIndex ( ) ;
return texid ;
}
//==========================================================================
//
// Same shit for a different hack, this time Hexen's front sky layers.
//
//==========================================================================
FTextureID FTextureManager : : GetFrontSkyLayer ( FTextureID texid )
{
int texidx = texid . GetIndex ( ) ;
if ( ( unsigned ) texidx > = Textures . Size ( ) ) return texid ;
if ( Textures [ texidx ] . FrontSkyLayer ! = - 1 ) return FSetTextureID ( Textures [ texidx ] . FrontSkyLayer ) ;
// Reject anything that cannot have been a front layer for the sky in original Hexen, i.e. it needs to be an unscaled wall texture only using Doom patches.
auto tex = Textures [ texidx ] . Texture ;
auto ttex = tex - > GetTexture ( ) ;
auto image = ttex - > GetImage ( ) ;
if ( image = = nullptr | | ! image - > SupportRemap0 ( ) | | tex - > GetUseType ( ) ! = ETextureType : : Wall | | tex - > useWorldPanning ( ) | | tex - > GetTexelTopOffset ( ) ! = 0 | |
ttex - > GetWidth ( ) ! = tex - > GetDisplayWidth ( ) | | ttex - > GetHeight ( ) ! = tex - > GetDisplayHeight ( ) )
{
Textures [ texidx ] . FrontSkyLayer = texidx ;
return texid ;
}
// Set this up so that it serializes to the same info as the base texture - this is needed to restore it on load.
// But do not link the new texture into the hash chain!
auto itex = new FImageTexture ( image ) ;
itex - > SetNoRemap0 ( ) ;
auto FrontSkyLayer = MakeGameTexture ( itex , tex - > GetName ( ) , ETextureType : : Wall ) ;
FrontSkyLayer - > SetUseType ( tex - > GetUseType ( ) ) ;
texid = TexMan . AddGameTexture ( FrontSkyLayer , false ) ;
Textures [ texidx ] . FrontSkyLayer = texid . GetIndex ( ) ;
Textures [ texid . GetIndex ( ) ] . FrontSkyLayer = texid . GetIndex ( ) ; // also let it refer to itself as its front sky layer, in case for repeated InitSkyMap calls.
return texid ;
}
2020-05-24 14:11:10 +00:00
//==========================================================================
//
//
//
//==========================================================================
EXTERN_CVAR ( String , language )
int FTextureManager : : ResolveLocalizedTexture ( int tex )
{
size_t langlen = strlen ( language ) ;
int lang = ( langlen < 2 | | langlen > 3 ) ?
MAKE_ID ( ' e ' , ' n ' , ' u ' , ' \0 ' ) :
MAKE_ID ( language [ 0 ] , language [ 1 ] , language [ 2 ] , ' \0 ' ) ;
uint64_t index = ( uint64_t ( lang ) < < 32 ) + tex ;
if ( auto pTex = LocalizedTextures . CheckKey ( index ) ) return * pTex ;
index = ( uint64_t ( lang & MAKE_ID ( 255 , 255 , 0 , 0 ) ) < < 32 ) + tex ;
if ( auto pTex = LocalizedTextures . CheckKey ( index ) ) return * pTex ;
return tex ;
}
//===========================================================================
//
// R_GuesstimateNumTextures
//
// Returns an estimate of the number of textures R_InitData will have to
// process. Used by D_DoomMain() when it calls ST_Init().
//
//===========================================================================
int FTextureManager : : GuesstimateNumTextures ( )
{
int numtex = 0 ;
2021-12-30 09:30:21 +00:00
2020-05-24 14:11:10 +00:00
for ( int i = fileSystem . GetNumEntries ( ) - 1 ; i > = 0 ; i - - )
{
int space = fileSystem . GetFileNamespace ( i ) ;
switch ( space )
{
case ns_flats :
case ns_sprites :
case ns_newtextures :
case ns_hires :
case ns_patches :
case ns_graphics :
numtex + + ;
break ;
default :
if ( fileSystem . GetFileFlags ( i ) & LUMPF_MAYBEFLAT ) numtex + + ;
break ;
}
}
//numtex += CountBuildTiles (); // this cannot be done with a lot of overhead so just leave it out.
numtex + = CountTexturesX ( ) ;
return numtex ;
}
//===========================================================================
//
// R_CountTexturesX
//
// See R_InitTextures() for the logic in deciding what lumps to check.
//
//===========================================================================
int FTextureManager : : CountTexturesX ( )
{
int count = 0 ;
int wadcount = fileSystem . GetNumWads ( ) ;
for ( int wadnum = 0 ; wadnum < wadcount ; wadnum + + )
{
// Use the most recent PNAMES for this WAD.
// Multiple PNAMES in a WAD will be ignored.
int pnames = fileSystem . CheckNumForName ( " PNAMES " , ns_global , wadnum , false ) ;
// should never happen except for zdoom.pk3
if ( pnames < 0 ) continue ;
// Only count the patches if the PNAMES come from the current file
// Otherwise they have already been counted.
if ( fileSystem . GetFileContainer ( pnames ) = = wadnum )
{
count + = CountLumpTextures ( pnames ) ;
}
int texlump1 = fileSystem . CheckNumForName ( " TEXTURE1 " , ns_global , wadnum ) ;
int texlump2 = fileSystem . CheckNumForName ( " TEXTURE2 " , ns_global , wadnum ) ;
count + = CountLumpTextures ( texlump1 ) - 1 ;
count + = CountLumpTextures ( texlump2 ) - 1 ;
}
return count ;
}
//===========================================================================
//
// R_CountLumpTextures
//
// Returns the number of patches in a PNAMES/TEXTURE1/TEXTURE2 lump.
//
//===========================================================================
int FTextureManager : : CountLumpTextures ( int lumpnum )
{
if ( lumpnum > = 0 )
{
auto file = fileSystem . OpenFileReader ( lumpnum ) ;
2022-10-02 18:45:53 +00:00
uint32_t numtex = file . ReadUInt32 ( ) ;
2020-05-24 14:11:10 +00:00
return int ( numtex ) > = 0 ? numtex : 0 ;
}
return 0 ;
}
//-----------------------------------------------------------------------------
//
// Adjust sprite offsets for GL rendering (IWAD resources only)
//
//-----------------------------------------------------------------------------
void FTextureManager : : AdjustSpriteOffsets ( )
{
2020-05-24 19:19:33 +00:00
int lump , lastlump = 0 ;
2020-05-24 14:11:10 +00:00
int sprid ;
TMap < int , bool > donotprocess ;
int numtex = fileSystem . GetNumEntries ( ) ;
for ( int i = 0 ; i < numtex ; i + + )
{
if ( fileSystem . GetFileContainer ( i ) > fileSystem . GetMaxIwadNum ( ) ) break ; // we are past the IWAD
if ( fileSystem . GetFileNamespace ( i ) = = ns_sprites & & fileSystem . GetFileContainer ( i ) > = fileSystem . GetIwadNum ( ) & & fileSystem . GetFileContainer ( i ) < = fileSystem . GetMaxIwadNum ( ) )
{
char str [ 9 ] ;
fileSystem . GetFileShortName ( str , i ) ;
str [ 8 ] = 0 ;
FTextureID texid = TexMan . CheckForTexture ( str , ETextureType : : Sprite , 0 ) ;
2020-05-25 21:59:07 +00:00
if ( texid . isValid ( ) & & fileSystem . GetFileContainer ( GetGameTexture ( texid ) - > GetSourceLump ( ) ) > fileSystem . GetMaxIwadNum ( ) )
2020-05-24 14:11:10 +00:00
{
// This texture has been replaced by some PWAD.
memcpy ( & sprid , str , 4 ) ;
donotprocess [ sprid ] = true ;
}
}
}
while ( ( lump = fileSystem . FindLump ( " SPROFS " , & lastlump , false ) ) ! = - 1 )
{
FScanner sc ;
sc . OpenLumpNum ( lump ) ;
sc . SetCMode ( true ) ;
int ofslumpno = fileSystem . GetFileContainer ( lump ) ;
while ( sc . GetString ( ) )
{
int x , y ;
bool iwadonly = false ;
bool forced = false ;
FTextureID texno = TexMan . CheckForTexture ( sc . String , ETextureType : : Sprite ) ;
sc . MustGetStringName ( " , " ) ;
sc . MustGetNumber ( ) ;
x = sc . Number ;
sc . MustGetStringName ( " , " ) ;
sc . MustGetNumber ( ) ;
y = sc . Number ;
if ( sc . CheckString ( " , " ) )
{
sc . MustGetString ( ) ;
if ( sc . Compare ( " iwad " ) ) iwadonly = true ;
if ( sc . Compare ( " iwadforced " ) ) forced = iwadonly = true ;
}
if ( texno . isValid ( ) )
{
2020-05-25 21:59:07 +00:00
auto tex = GetGameTexture ( texno ) ;
2020-05-24 14:11:10 +00:00
int lumpnum = tex - > GetSourceLump ( ) ;
// We only want to change texture offsets for sprites in the IWAD or the file this lump originated from.
if ( lumpnum > = 0 & & lumpnum < fileSystem . GetNumEntries ( ) )
{
int wadno = fileSystem . GetFileContainer ( lumpnum ) ;
if ( ( iwadonly & & wadno > = fileSystem . GetIwadNum ( ) & & wadno < = fileSystem . GetMaxIwadNum ( ) ) | | ( ! iwadonly & & wadno = = ofslumpno ) )
{
if ( wadno > = fileSystem . GetIwadNum ( ) & & wadno < = fileSystem . GetMaxIwadNum ( ) & & ! forced & & iwadonly )
{
2020-05-25 21:59:07 +00:00
memcpy ( & sprid , tex - > GetName ( ) . GetChars ( ) , 4 ) ;
2020-05-24 14:11:10 +00:00
if ( donotprocess . CheckKey ( sprid ) ) continue ; // do not alter sprites that only get partially replaced.
}
2020-05-25 21:59:07 +00:00
tex - > SetOffsets ( 1 , x , y ) ;
2020-05-24 14:11:10 +00:00
}
}
}
}
}
}
//==========================================================================
//
// FTextureAnimator :: SetTranslation
//
// Sets animation translation for a texture
//
//==========================================================================
void FTextureManager : : SetTranslation ( FTextureID fromtexnum , FTextureID totexnum )
{
if ( ( size_t ) fromtexnum . texnum < Translation . Size ( ) )
{
if ( ( size_t ) totexnum . texnum > = Textures . Size ( ) )
{
totexnum . texnum = fromtexnum . texnum ;
}
Translation [ fromtexnum . texnum ] = totexnum . texnum ;
}
}
2020-10-06 17:26:22 +00:00
//-----------------------------------------------------------------------------
//
// Adds an alias name to the texture manager.
// Aliases are only checked if no real texture with the given name exists.
//
//-----------------------------------------------------------------------------
2022-12-07 16:10:27 +00:00
void FTextureManager : : AddAlias ( const char * name , int texindex )
2020-10-06 17:26:22 +00:00
{
2022-12-07 16:10:27 +00:00
if ( texindex < 0 | | texindex > = NumTextures ( ) ) return ; // Whatever got passed in here was not valid, so ignore the alias.
aliases . Insert ( name , texindex ) ;
2020-10-06 17:26:22 +00:00
}
2020-05-24 14:11:10 +00:00
2022-12-08 10:35:21 +00:00
void FTextureManager : : Listaliases ( )
{
decltype ( aliases ) : : Iterator it ( aliases ) ;
decltype ( aliases ) : : Pair * pair ;
TArray < FString > list ;
while ( it . NextPair ( pair ) )
{
auto tex = GetGameTexture ( pair - > Value ) ;
list . Push ( FStringf ( " %s -> %s%s " , pair - > Key . GetChars ( ) , tex ? tex - > GetName ( ) . GetChars ( ) : " (null) " , ( ( tex & & tex - > GetUseType ( ) = = ETextureType : : Null ) ? " , null " : " " ) ) ) ;
}
std : : sort ( list . begin ( ) , list . end ( ) , [ ] ( const FString & l , const FString & r ) { return l . CompareNoCase ( r ) < 0 ; } ) ;
for ( auto & s : list )
{
Printf ( " %s \n " , s . GetChars ( ) ) ;
}
}
2020-05-24 14:11:10 +00:00
//==========================================================================
//
// FTextureID::operator+
// Does not return invalid texture IDs
//
//==========================================================================
2022-07-20 12:47:18 +00:00
FTextureID FTextureID : : operator + ( int offset ) const noexcept ( true )
2020-05-24 14:11:10 +00:00
{
if ( ! isValid ( ) ) return * this ;
if ( texnum + offset > = TexMan . NumTextures ( ) ) return FTextureID ( - 1 ) ;
return FTextureID ( texnum + offset ) ;
}
2020-05-26 21:12:04 +00:00
CCMD ( flushtextures )
{
TexMan . FlushAll ( ) ;
}
2022-12-08 10:35:21 +00:00
CCMD ( listtexturealiases )
{
TexMan . Listaliases ( ) ;
}