2019-10-12 06:54:06 +00:00
/*
* * buildtexture . cpp
* * Handling Build textures
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright 2019 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 "files.h"
# include "zstring.h"
2020-05-24 05:58:56 +00:00
# include "buildtiles.h"
2019-10-12 06:54:06 +00:00
# include "image.h"
2019-12-17 22:25:07 +00:00
2019-10-14 21:11:01 +00:00
# include "palette.h"
# include "m_crc32.h"
# include "build.h"
2019-12-11 22:41:05 +00:00
# include "gamecontrol.h"
2022-08-23 10:33:55 +00:00
# include "gamefuncs.h"
2020-05-24 05:58:56 +00:00
# include "palettecontainer.h"
2020-05-24 17:12:22 +00:00
# include "texturemanager.h"
2020-09-09 22:05:10 +00:00
# include "c_dispatch.h"
2020-09-14 23:21:17 +00:00
# include "sc_man.h"
2020-09-14 23:27:24 +00:00
# include "gamestruct.h"
2021-04-05 11:55:36 +00:00
# include "hw_voxels.h"
2022-09-30 14:25:21 +00:00
# include "models/modeldata.h"
2019-10-12 06:54:06 +00:00
2020-11-10 15:22:02 +00:00
# include "hw_renderstate.h"
2019-10-12 20:49:46 +00:00
enum
2019-10-12 06:54:06 +00:00
{
2019-10-12 20:49:46 +00:00
MAXARTFILES_BASE = 200 ,
MAXARTFILES_TOTAL = 220
2019-10-12 06:54:06 +00:00
} ;
2019-10-14 21:11:01 +00:00
2019-10-21 07:22:55 +00:00
BuildTiles TileFiles ;
2019-10-14 21:11:01 +00:00
2021-07-13 07:48:14 +00:00
int tileSetHightileReplacement ( int picnum , int palnum , const char * filename , float alphacut , float xscale , float yscale , float specpower , float specfactor , bool indexed = false ) ;
2020-11-10 19:12:46 +00:00
2019-10-14 21:11:01 +00:00
//==========================================================================
//
//
//
//==========================================================================
2020-05-24 10:31:38 +00:00
picanm_t tileConvertAnimFormat ( int32_t const picanimraw , int * lo , int * to )
2019-10-12 06:54:06 +00:00
{
2020-05-24 19:19:33 +00:00
// Unpack a 4 byte packed anim descriptor into something more accessible
2019-10-12 20:49:46 +00:00
picanm_t anm ;
2019-10-17 07:42:11 +00:00
anm . num = picanimraw & 63 ;
2020-05-24 10:31:38 +00:00
* lo = ( int8_t ) ( ( picanimraw > > 8 ) & 255 ) ;
* to = ( int8_t ) ( ( picanimraw > > 16 ) & 255 ) ;
2019-10-17 07:42:11 +00:00
anm . sf = ( ( picanimraw > > 24 ) & 15 ) | ( picanimraw & 192 ) ;
anm . extra = ( picanimraw > > 28 ) & 15 ;
2019-10-12 20:49:46 +00:00
return anm ;
}
2019-10-12 06:54:06 +00:00
2019-10-12 20:49:46 +00:00
//==========================================================================
//
// Base class for Build tile textures
// This needs a few subclasses for different use cases.
//
//==========================================================================
2020-04-10 16:17:26 +00:00
int FTileTexture : : CopyPixels ( FBitmap * bmp , int conversion )
2019-10-12 06:54:06 +00:00
{
2019-10-14 21:11:01 +00:00
TArray < uint8_t > buffer ;
2020-05-24 17:12:22 +00:00
auto ppix = GetRawData ( ) ;
if ( ppix )
{
2020-04-10 16:17:26 +00:00
bmp - > CopyPixelData ( 0 , 0 , ppix , Width , Height , Height , 1 , 0 , GPalette . BaseColors ) ;
2020-05-24 17:12:22 +00:00
}
2020-04-10 16:17:26 +00:00
return 0 ;
2019-10-12 20:49:46 +00:00
}
2020-04-10 16:17:26 +00:00
TArray < uint8_t > FTileTexture : : CreatePalettedPixels ( int conversion )
2019-10-12 20:49:46 +00:00
{
2020-05-24 17:12:22 +00:00
TArray < uint8_t > buffer ( Width * Height , true ) ;
auto p = GetRawData ( ) ;
if ( p ) memcpy ( buffer . Data ( ) , p , buffer . Size ( ) ) ;
else memset ( buffer . Data ( ) , 0 , buffer . Size ( ) ) ;
return buffer ;
2019-10-12 20:49:46 +00:00
}
2022-12-06 17:30:57 +00:00
//==========================================================================
//
// raw pixel cache. This is for accessing pixel data in the game code,
// not for rendering.
//
//==========================================================================
struct RawCacheNode
{
TArray < uint8_t > data ;
uint64_t lastUseTime ;
RawCacheNode ( ) = default ;
RawCacheNode ( RawCacheNode & other ) = default ;
RawCacheNode & operator = ( RawCacheNode & other ) = default ;
RawCacheNode ( RawCacheNode & & other ) noexcept
{
data = std : : move ( other . data ) ;
lastUseTime = other . lastUseTime ;
}
RawCacheNode & operator = ( RawCacheNode & & other ) noexcept
{
data = std : : move ( other . data ) ;
lastUseTime = other . lastUseTime ;
return * this ;
}
} ;
//==========================================================================
//
//
//
//==========================================================================
static TMap < int , RawCacheNode > CacheNodes ;
const uint8_t * GetRawPixels ( FTextureID texid )
{
if ( ! texid . isValid ( ) ) return nullptr ;
auto gtex = TexMan . GetGameTexture ( texid ) ;
auto tex = dynamic_cast < FImageTexture * > ( gtex - > GetTexture ( ) ) ;
if ( ! tex | | ! tex - > GetImage ( ) ) return nullptr ;
auto img = tex - > GetImage ( ) ;
auto timg = dynamic_cast < FTileTexture * > ( img ) ;
if ( ! timg | | ! timg - > GetRawData ( ) )
{
auto cache = CacheNodes . CheckKey ( texid . GetIndex ( ) ) ;
if ( cache )
{
cache - > lastUseTime = I_nsTime ( ) ;
return cache - > data . Data ( ) ;
}
RawCacheNode newnode ;
newnode . data = img - > GetPalettedPixels ( 0 ) ;
newnode . lastUseTime = I_nsTime ( ) ;
auto retval = newnode . data . Data ( ) ;
CacheNodes . Insert ( texid . GetIndex ( ) , std : : move ( newnode ) ) ;
return retval ;
}
else
{
return timg - > GetRawData ( ) ;
}
}
//==========================================================================
//
//To use this the texture must have been made writable during texture init.
//
//==========================================================================
uint8_t * GetWritablePixels ( FTextureID texid )
{
if ( ! texid . isValid ( ) ) return nullptr ;
auto gtex = TexMan . GetGameTexture ( texid ) ;
auto tex = dynamic_cast < FImageTexture * > ( gtex - > GetTexture ( ) ) ;
if ( ! tex | | ! tex - > GetImage ( ) ) return nullptr ;
auto timg = dynamic_cast < FWritableTile * > ( tex - > GetImage ( ) ) ;
if ( ! timg ) return nullptr ;
gtex - > CleanHardwareData ( ) ; // we can safely assume that this only gets called when the texture is about to be changed.
return timg - > GetRawData ( ) ;
}
2019-10-12 20:49:46 +00:00
//==========================================================================
//
2020-04-10 16:17:26 +00:00
//
2019-10-12 20:49:46 +00:00
//
//==========================================================================
2020-05-25 21:59:07 +00:00
static FGameTexture * GetTileTexture ( const char * name , const TArray < uint8_t > & backingstore , uint32_t offset , int width , int height )
2019-10-12 20:49:46 +00:00
{
2020-05-24 17:12:22 +00:00
auto tex = new FArtTile ( backingstore , offset , width , height ) ;
2020-05-24 19:19:33 +00:00
auto p = & backingstore [ offset ] ;
auto siz = width * height ;
for ( int i = 0 ; i < siz ; i + + , p + + )
{
// move transparent color to index 0 to get in line with the rest of the texture management.
if ( * p = = 0 ) * p = 255 ;
else if ( * p = = 255 ) * p = 0 ;
}
2020-05-24 11:53:27 +00:00
2019-10-12 20:49:46 +00:00
if ( tex )
2019-10-12 06:54:06 +00:00
{
2020-05-25 21:59:07 +00:00
return MakeGameTexture ( new FImageTexture ( tex ) , name , ETextureType : : Any ) ;
2019-10-12 06:54:06 +00:00
}
2020-04-10 16:17:26 +00:00
return nullptr ;
2019-10-12 20:49:46 +00:00
}
2019-10-12 06:54:06 +00:00
2020-05-24 21:26:47 +00:00
void BuildTiles : : Init ( )
{
2020-05-25 21:59:07 +00:00
Placeholder = TexMan . GameByIndex ( 0 ) ;
2022-12-06 21:30:52 +00:00
tiledata . Resize ( MAXTILES ) ;
memset ( tiledata . Data ( ) , 0 , sizeof ( tiledata [ 0 ] ) * MAXTILES ) ;
2020-05-24 21:26:47 +00:00
for ( auto & tile : tiledata )
{
tile . texture = Placeholder ;
2020-05-29 21:33:26 +00:00
tile . alphaThreshold = 0.5 ;
2020-05-24 21:26:47 +00:00
}
}
2019-10-14 21:11:01 +00:00
//==========================================================================
//
//
//
//==========================================================================
2022-12-05 16:14:01 +00:00
void BuildTiles : : AddTile ( int tilenum , FGameTexture * tex )
2019-10-14 21:11:01 +00:00
{
2021-12-02 20:40:43 +00:00
if ( ! tex - > GetID ( ) . isValid ( ) )
TexMan . AddGameTexture ( tex ) ;
2020-05-24 13:02:20 +00:00
tiledata [ tilenum ] . texture = tex ;
2019-10-14 21:11:01 +00:00
}
2019-10-12 06:54:06 +00:00
//===========================================================================
//
// AddTiles
//
// Adds all the tiles in an artfile to the texture manager.
//
//===========================================================================
2020-05-24 20:37:50 +00:00
void BuildTiles : : AddTiles ( int firsttile , TArray < uint8_t > & RawData , const char * mapname )
2019-10-12 06:54:06 +00:00
{
const uint8_t * tiles = RawData . Data ( ) ;
// int numtiles = LittleLong(((uint32_t *)tiles)[1]); // This value is not reliable
2019-10-14 21:11:01 +00:00
int tilestart = LittleLong ( ( ( int * ) tiles ) [ 2 ] ) ;
int tileend = LittleLong ( ( ( int * ) tiles ) [ 3 ] ) ;
2019-10-12 06:54:06 +00:00
const uint16_t * tilesizx = & ( ( const uint16_t * ) tiles ) [ 8 ] ;
const uint16_t * tilesizy = & tilesizx [ tileend - tilestart + 1 ] ;
const uint32_t * picanm = ( const uint32_t * ) & tilesizy [ tileend - tilestart + 1 ] ;
const uint8_t * tiledata = ( const uint8_t * ) & picanm [ tileend - tilestart + 1 ] ;
2019-10-14 21:11:01 +00:00
if ( firsttile ! = - 1 )
{
tileend = tileend - tilestart + firsttile ;
tilestart = firsttile ;
}
2019-10-12 06:54:06 +00:00
for ( int i = tilestart ; i < = tileend ; + + i )
{
int pic = i - tilestart ;
int width = LittleShort ( tilesizx [ pic ] ) ;
int height = LittleShort ( tilesizy [ pic ] ) ;
uint32_t anm = LittleLong ( picanm [ pic ] ) ;
int size = width * height ;
if ( width < = 0 | | height < = 0 ) continue ;
2020-04-08 19:22:30 +00:00
FString texname ;
if ( mapname ) texname . Format ( " maptile_%s_%05d " , mapname , i ) ;
else texname . Format ( " #%05d " , i ) ;
auto tex = GetTileTexture ( texname , RawData , uint32_t ( tiledata - tiles ) , width , height ) ;
2019-10-14 21:11:01 +00:00
AddTile ( i , tex ) ;
2020-05-24 11:53:27 +00:00
int leftoffset , topoffset ;
2022-12-05 16:14:01 +00:00
this - > tiledata [ i ] . picanm = tileConvertAnimFormat ( anm , & leftoffset , & topoffset ) ;
2020-05-24 11:53:27 +00:00
tex - > SetOffsets ( leftoffset , topoffset ) ;
2019-10-12 06:54:06 +00:00
tiledata + = size ;
}
}
//===========================================================================
//
// CountTiles
//
// Returns the number of tiles provided by an artfile
//
//===========================================================================
2019-10-14 21:11:01 +00:00
int CountTiles ( const char * fn , const uint8_t * RawData )
2019-10-12 06:54:06 +00:00
{
int version = LittleLong ( * ( uint32_t * ) RawData ) ;
if ( version ! = 1 )
{
2020-04-11 21:45:45 +00:00
Printf ( " %s: Invalid art file version. Must be 1, got %d \n " , fn , version ) ;
2019-10-12 06:54:06 +00:00
return 0 ;
}
int tilestart = LittleLong ( ( ( uint32_t * ) RawData ) [ 2 ] ) ;
int tileend = LittleLong ( ( ( uint32_t * ) RawData ) [ 3 ] ) ;
2019-10-14 21:11:01 +00:00
if ( ( unsigned ) tilestart > = MAXUSERTILES | | ( unsigned ) tileend > = MAXUSERTILES )
{
2020-04-11 21:45:45 +00:00
Printf ( " %s: Invalid tilestart or tileend \n " , fn ) ;
2019-10-14 21:11:01 +00:00
return 0 ;
}
if ( tileend < tilestart )
{
2020-04-11 21:45:45 +00:00
Printf ( " %s: tileend < tilestart \n " , fn ) ;
2019-10-14 21:11:01 +00:00
return 0 ;
}
2019-10-12 06:54:06 +00:00
return tileend > = tilestart ? tileend - tilestart + 1 : 0 ;
}
2020-03-29 12:01:46 +00:00
//===========================================================================
//
// MakeCanvas
//
// Turns texture into a canvas (i.e. camera texture)
//
//===========================================================================
void BuildTiles : : MakeCanvas ( int tilenum , int width , int height )
{
2020-05-24 13:02:20 +00:00
auto canvas = ValidateCustomTile ( tilenum , ReplacementType : : Canvas ) ;
2020-05-31 19:43:32 +00:00
canvas - > SetSize ( width * 4 , height * 4 ) ;
2021-05-12 15:57:36 +00:00
canvas - > SetDisplaySize ( ( float ) width , ( float ) height ) ;
2020-05-31 19:43:32 +00:00
canvas - > GetTexture ( ) - > SetSize ( width * 4 , height * 4 ) ;
2020-05-31 08:53:11 +00:00
static_cast < FCanvasTexture * > ( canvas - > GetTexture ( ) ) - > aspectRatio = ( float ) width / height ;
2020-03-29 12:01:46 +00:00
}
2019-10-12 06:54:06 +00:00
//===========================================================================
//
// LoadArtFile
//
2019-10-17 07:42:11 +00:00
// Returns the number of tiles found.
2019-10-12 06:54:06 +00:00
//
// let's load everything into memory on startup.
//
//===========================================================================
2020-05-24 20:37:50 +00:00
int BuildTiles : : LoadArtFile ( const char * fn , const char * mapname , int firsttile )
2019-10-12 06:54:06 +00:00
{
2021-05-12 15:57:36 +00:00
unsigned old = FindFile ( fn ) ;
2019-10-12 20:49:46 +00:00
if ( old > = ArtFiles . Size ( ) ) // Do not process if already loaded.
2019-10-12 06:54:06 +00:00
{
2020-04-11 21:54:33 +00:00
FileReader fr = fileSystem . OpenFileReader ( fn ) ;
2019-10-12 06:54:06 +00:00
if ( fr . isOpen ( ) )
{
auto artdata = fr . Read ( ) ;
const uint8_t * artptr = artdata . Data ( ) ;
if ( artdata . Size ( ) > 16 )
{
2019-10-18 22:26:43 +00:00
if ( memcmp ( artptr , " BUILDART " , 8 ) = = 0 )
{
artdata . Delete ( 0 , 8 ) ;
}
2019-10-12 06:54:06 +00:00
// Only load the data if the header is present
2019-10-14 21:11:01 +00:00
if ( CountTiles ( fn , artptr ) > 0 )
2019-10-12 06:54:06 +00:00
{
2019-10-14 21:11:01 +00:00
auto file = new BuildArtFile ;
2020-05-24 21:26:47 +00:00
ArtFiles . Push ( file ) ;
2019-10-14 21:11:01 +00:00
file - > filename = fn ;
file - > RawData = std : : move ( artdata ) ;
2020-05-24 20:37:50 +00:00
AddTiles ( firsttile , file - > RawData , mapname ) ;
2019-10-12 06:54:06 +00:00
}
}
}
2019-10-14 21:11:01 +00:00
else
{
2020-04-11 21:45:45 +00:00
//Printf("%s: file not found\n", fn);
2019-10-15 18:02:37 +00:00
return - 1 ;
2019-10-14 21:11:01 +00:00
}
2019-10-12 06:54:06 +00:00
}
2019-10-15 18:02:37 +00:00
return 0 ;
2019-10-12 06:54:06 +00:00
}
2019-10-14 21:11:01 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-10-21 07:22:55 +00:00
void BuildTiles : : LoadArtSet ( const char * filename )
2019-10-12 06:54:06 +00:00
{
2019-10-14 21:11:01 +00:00
for ( int index = 0 ; index < MAXARTFILES_BASE ; index + + )
2019-10-12 20:49:46 +00:00
{
2019-10-14 21:11:01 +00:00
FStringf fn ( filename , index ) ;
2020-05-24 20:37:50 +00:00
LoadArtFile ( fn , nullptr ) ;
2019-10-12 20:49:46 +00:00
}
2019-12-11 22:41:05 +00:00
for ( auto & addart : addedArt )
{
2020-05-24 20:37:50 +00:00
LoadArtFile ( addart , nullptr ) ;
2019-12-11 22:41:05 +00:00
}
2019-10-12 06:54:06 +00:00
}
2022-11-25 15:52:08 +00:00
//===========================================================================
//
//
//
//===========================================================================
void BuildTiles : : SetAliases ( )
{
TMap < FName , int > : : Iterator it ( nametoindex ) ;
TMap < FName , int > : : Pair * pair ;
while ( it . NextPair ( pair ) )
TexMan . AddAlias ( pair - > Key . GetChars ( ) , tileGetTexture ( pair - > Value ) ) ;
}
2019-10-12 06:54:06 +00:00
2019-10-14 21:11:01 +00:00
//==========================================================================
//
// Checks if a custom tile has alredy been added to the list.
// For each tile index there may only be one replacement and its
// type may never change!
//
2019-10-17 07:42:11 +00:00
// All these uses will need some review further down the line so that the texture manager's content is immutable.
//
2019-10-14 21:11:01 +00:00
//==========================================================================
2020-05-25 21:59:07 +00:00
FGameTexture * BuildTiles : : ValidateCustomTile ( int tilenum , ReplacementType type )
2019-10-12 20:49:46 +00:00
{
2022-12-05 16:14:01 +00:00
if ( locked ) I_FatalError ( " Modifying tiles after startup is not allowed. " ) ;
2019-10-14 21:11:01 +00:00
if ( tilenum < 0 | | tilenum > = MAXTILES ) return nullptr ;
2020-05-24 21:26:47 +00:00
auto & td = tiledata [ tilenum ] ;
auto tile = td . texture ;
auto reptype = td . replacement ;
if ( reptype = = type ) return tile ; // already created
if ( reptype > ReplacementType : : Art ) return nullptr ; // different custom type - cannot replace again.
2019-10-14 21:11:01 +00:00
FTexture * replacement = nullptr ;
2020-05-30 09:46:49 +00:00
td . replacement = type ;
2020-05-24 13:02:20 +00:00
if ( type = = ReplacementType : : Writable )
2019-10-12 20:49:46 +00:00
{
2019-10-17 07:42:11 +00:00
// Creates an empty writable tile.
// Current use cases are:
// Blood's 'lens' effect (apparently MP only) - combination of a camera texture with a distortion map - should be made a shader effect to be applied to the camera texture.
2020-04-10 16:17:26 +00:00
replacement = new FImageTexture ( new FWritableTile ) ;
2019-10-12 20:49:46 +00:00
}
2020-05-24 13:02:20 +00:00
else if ( type = = ReplacementType : : Restorable )
2019-10-14 21:11:01 +00:00
{
// This is for modifying an existing tile.
2022-12-05 16:14:01 +00:00
// It only gets used for a few specific effects:
2019-10-14 21:11:01 +00:00
// A) the fire in Blood.
// B) the pin display in Redneck Rampage's bowling lanes.
2019-11-24 12:59:36 +00:00
// C) Exhumed's menu plus one special effect tile.
2020-05-24 17:12:22 +00:00
if ( tile - > GetTexelWidth ( ) = = 0 | | tile - > GetTexelHeight ( ) = = 0 ) return nullptr ; // The base must have a size for this to work.
2020-05-25 21:59:07 +00:00
replacement = new FImageTexture ( new FRestorableTile ( tile - > GetTexture ( ) - > GetImage ( ) ) ) ;
2019-10-14 21:11:01 +00:00
}
2020-05-24 13:02:20 +00:00
else if ( type = = ReplacementType : : Canvas )
2020-03-29 12:01:46 +00:00
{
2020-05-31 08:53:11 +00:00
replacement = new FCanvasTexture ( 1 , 1 ) ;
2020-03-29 12:01:46 +00:00
}
2019-10-14 21:11:01 +00:00
else return nullptr ;
2020-05-30 18:55:29 +00:00
auto rep = MakeGameTexture ( replacement , tile - > GetName ( ) , ETextureType : : Override ) ;
AddTile ( tilenum , rep ) ;
return rep ;
2019-10-12 06:54:06 +00:00
}
2019-10-14 22:19:31 +00:00
//==========================================================================
//
// Creates a tile for displaying custom content
//
//==========================================================================
2019-10-21 07:22:55 +00:00
uint8_t * BuildTiles : : tileCreate ( int tilenum , int width , int height )
2019-10-14 22:19:31 +00:00
{
if ( width < = 0 | | height < = 0 ) return nullptr ;
2020-05-24 13:02:20 +00:00
auto tex = ValidateCustomTile ( tilenum , ReplacementType : : Writable ) ;
2019-10-14 22:19:31 +00:00
if ( tex = = nullptr ) return nullptr ;
2020-05-25 21:59:07 +00:00
auto wtex = static_cast < FWritableTile * > ( tex - > GetTexture ( ) - > GetImage ( ) ) ;
2020-04-10 16:17:26 +00:00
if ( ! wtex - > ResizeImage ( width , height ) ) return nullptr ;
tex - > SetSize ( width , height ) ;
2020-05-24 17:12:22 +00:00
return wtex - > GetRawData ( ) ;
2019-10-14 22:19:31 +00:00
}
//==========================================================================
//
// Makes a tile writable - only used for a handful of special cases
// (todo: Investigate how to get rid of this)
//
//==========================================================================
2020-05-24 17:12:22 +00:00
uint8_t * BuildTiles : : tileMakeWritable ( int num )
2019-10-14 22:19:31 +00:00
{
2020-05-24 13:02:20 +00:00
auto tex = ValidateCustomTile ( num , ReplacementType : : Restorable ) ;
2020-05-25 21:59:07 +00:00
auto wtex = static_cast < FWritableTile * > ( tex - > GetTexture ( ) - > GetImage ( ) ) ;
2020-05-24 17:12:22 +00:00
return wtex ? wtex - > GetRawData ( ) : nullptr ;
2019-10-14 22:19:31 +00:00
}
2019-10-15 21:29:47 +00:00
//==========================================================================
//
2019-10-15 21:38:01 +00:00
// Returns checksum for a given tile
//
//==========================================================================
2019-12-29 21:47:40 +00:00
int32_t tileGetCRC32 ( int tileNum )
2019-10-15 21:38:01 +00:00
{
if ( ( unsigned ) tileNum > = ( unsigned ) MAXTILES ) return 0 ;
2020-05-25 21:59:07 +00:00
auto tile = dynamic_cast < FArtTile * > ( TileFiles . tiledata [ tileNum ] . texture - > GetTexture ( ) - > GetImage ( ) ) ; // only consider original ART tiles.
2020-04-10 16:17:26 +00:00
if ( ! tile ) return 0 ;
auto pixels = tile - > GetRawData ( ) ;
if ( ! pixels ) return 0 ;
auto size = tile - > GetWidth ( ) * tile - > GetHeight ( ) ;
2019-10-15 21:38:01 +00:00
if ( size = = 0 ) return 0 ;
2020-05-24 19:19:33 +00:00
// Temporarily revert the data to its original form with 255 being transparent. Otherwise the CRC won't match.
auto p = pixels ;
for ( int i = 0 ; i < size ; i + + , p + + )
{
// move transparent color to index 0 to get in line with the rest of the texture management.
if ( * p = = 0 ) * p = 255 ;
else if ( * p = = 255 ) * p = 0 ;
}
auto crc = crc32 ( 0 , ( const Bytef * ) pixels , size ) ;
// ... and back again.
p = pixels ;
for ( int i = 0 ; i < size ; i + + , p + + )
{
// move transparent color to index 0 to get in line with the rest of the texture management.
if ( * p = = 0 ) * p = 255 ;
else if ( * p = = 255 ) * p = 0 ;
}
return crc ;
2019-10-15 21:38:01 +00:00
}
2020-09-09 22:05:10 +00:00
CCMD ( tilecrc )
{
if ( argv . argc ( ) > 1 )
{
int tile = atoi ( argv [ 1 ] ) ;
Printf ( " %d \n " , tileGetCRC32 ( tile ) ) ;
}
}
2019-10-15 21:38:01 +00:00
//==========================================================================
//
// Import a tile from an external image.
// This has been signifcantly altered so it may not cover everything yet.
//
//==========================================================================
int tileImportFromTexture ( const char * fn , int tilenum , int alphacut , int istexture )
{
2021-04-04 19:00:41 +00:00
FTextureID texid = TexMan . CheckForTexture ( fn , ETextureType : : Any , FTextureManager : : TEXMAN_ForceLookup ) ;
2020-05-24 17:12:22 +00:00
if ( ! texid . isValid ( ) ) return - 1 ;
2020-05-25 21:59:07 +00:00
auto tex = TexMan . GetGameTexture ( texid ) ;
2019-10-15 21:38:01 +00:00
2020-05-24 17:12:22 +00:00
int32_t xsiz = tex - > GetTexelWidth ( ) , ysiz = tex - > GetTexelHeight ( ) ;
2019-10-15 21:38:01 +00:00
if ( xsiz < = 0 | | ysiz < = 0 )
return - 2 ;
2021-04-04 19:00:41 +00:00
// create a new game texture here - we want to give it a different name!
2020-10-10 14:52:04 +00:00
tex = MakeGameTexture ( tex - > GetTexture ( ) , FStringf ( " #%05d " , tilenum ) , ETextureType : : Override ) ;
2022-12-06 07:30:53 +00:00
TileFiles . AddTile ( tilenum , tex ) ;
2020-09-09 22:05:10 +00:00
if ( istexture )
2021-04-07 22:47:07 +00:00
tileSetHightileReplacement ( tilenum , 0 , fn , ( float ) ( 255 - alphacut ) * ( 1.f / 255.f ) , 1.0f , 1.0f , 1.0 , 1.0 ) ;
2019-10-15 21:38:01 +00:00
return 0 ;
}
//==========================================================================
//
// Copies a tile into another and optionally translates its palette.
//
//==========================================================================
void tileCopy ( int tile , int source , int pal , int xoffset , int yoffset , int flags )
{
picanm_t * picanm = nullptr ;
picanm_t * sourceanm = nullptr ;
2020-05-24 10:31:38 +00:00
int srcxo , srcyo ;
2020-05-25 21:59:07 +00:00
FGameTexture * tex ;
2019-10-15 21:38:01 +00:00
if ( pal = = - 1 & & tile = = source )
{
// Only modify the picanm info.
2020-05-24 13:02:20 +00:00
tex = tileGetTexture ( tile ) ;
2019-10-15 21:38:01 +00:00
if ( ! tex ) return ;
2020-05-24 11:53:27 +00:00
picanm = & TileFiles . tiledata [ tile ] . picanm ;
2019-10-15 21:38:01 +00:00
sourceanm = picanm ;
2020-05-24 17:12:22 +00:00
srcxo = tex - > GetTexelLeftOffset ( 0 ) ;
srcyo = tex - > GetTexelTopOffset ( 0 ) ;
2019-10-15 21:38:01 +00:00
}
else
{
if ( source = = - 1 ) source = tile ;
2020-05-24 13:02:20 +00:00
tex = tileGetTexture ( source ) ;
2019-10-15 21:38:01 +00:00
if ( ! tex ) return ;
2020-05-24 11:53:27 +00:00
sourceanm = & TileFiles . tiledata [ source ] . picanm ;
2020-05-24 17:12:22 +00:00
srcxo = tex - > GetTexelLeftOffset ( 0 ) ;
srcyo = tex - > GetTexelTopOffset ( 0 ) ;
2019-10-15 21:38:01 +00:00
2020-05-25 21:59:07 +00:00
TArray < uint8_t > buffer = tex - > GetTexture ( ) - > Get8BitPixels ( false ) ;
2019-10-15 21:38:01 +00:00
if ( pal ! = - 1 )
{
2020-05-27 20:19:02 +00:00
auto remap = lookups . getTable ( pal ) ;
2019-10-15 21:38:01 +00:00
for ( auto & pixel : buffer )
{
2020-05-23 12:40:54 +00:00
pixel = remap [ pixel ] ;
2019-10-15 21:38:01 +00:00
}
}
2022-12-05 17:32:43 +00:00
tex = MakeGameTexture ( new FImageTexture ( new FLooseTile ( buffer , tex - > GetTexelWidth ( ) , tex - > GetTexelHeight ( ) ) ) , FStringf ( " #%05d " , tile ) , ETextureType : : Any ) ;
2020-05-24 11:53:27 +00:00
picanm = & TileFiles . tiledata [ tile ] . picanm ;
2019-10-15 21:38:01 +00:00
TileFiles . AddTile ( tile , tex ) ;
}
2020-05-24 10:31:38 +00:00
if ( xoffset ! = - 1024 ) srcxo = clamp ( xoffset , - 128 , 127 ) ;
if ( yoffset ! = - 1024 ) srcyo = clamp ( yoffset , - 128 , 127 ) ;
tex - > SetOffsets ( srcxo , srcyo ) ;
2019-10-15 21:38:01 +00:00
picanm - > sf = ( picanm - > sf & ~ PICANM_MISC_MASK ) | ( sourceanm - > sf & PICANM_MISC_MASK ) | flags ;
}
2019-10-15 21:56:29 +00:00
//==========================================================================
//
2019-10-15 21:29:47 +00:00
//
//
//==========================================================================
2022-12-06 07:30:53 +00:00
FImageSource * CreateEmptyTexture ( ) ;
2019-10-15 21:29:47 +00:00
void tileDelete ( int tile )
{
2022-12-05 19:04:31 +00:00
if ( TileFiles . locked )
I_FatalError ( " Modifying tiles after startup is not allowed. " ) ;
2022-12-06 07:30:53 +00:00
// explicitly deleted textures must be unique null textures
auto nulltex = MakeGameTexture ( new FImageTexture ( CreateEmptyTexture ( ) ) , FStringf ( " #%05d " , tile ) , ETextureType : : Null ) ;
TileFiles . AddTile ( tile , nulltex ) ;
TileFiles . tiledata [ tile ] . replacement = ReplacementType : : Art ; // whatever this was, now it isn't anymore.
2021-04-05 11:55:36 +00:00
tiletovox [ tile ] = - 1 ; // clear the link but don't clear the voxel. It may be in use for another tile.
2022-09-30 14:25:21 +00:00
modelManager . UndefineTile ( tile ) ;
2019-10-15 21:29:47 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
void tileSetDummy ( int tile , int width , int height )
{
if ( width = = 0 | | height = = 0 )
{
tileDelete ( tile ) ;
}
else if ( width > 0 & & height > 0 )
{
2020-05-30 18:55:29 +00:00
FStringf texname ( " #%05d " , tile ) ;
auto dtile = MakeGameTexture ( new FImageTexture ( new FDummyTile ( width , height ) ) , texname , ETextureType : : Any ) ;
2019-10-15 21:29:47 +00:00
TileFiles . AddTile ( tile , dtile ) ;
}
}
2019-10-15 21:56:29 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-10-21 07:22:55 +00:00
void BuildTiles : : CloseAll ( )
2019-10-16 19:16:40 +00:00
{
ArtFiles . DeleteAndClear ( ) ;
}
2022-08-05 16:47:35 +00:00
//==========================================================================
//
// Retrieve animation offset
//
//==========================================================================
int tileAnimateOfs ( int tilenum , int randomize )
{
int framecount = picanm [ tilenum ] . num ;
if ( framecount > 0 )
{
2022-08-23 10:33:55 +00:00
int frametime = ! isBlood ( ) ? I_GetBuildTime ( ) : PlayClock ;
2022-08-05 16:47:35 +00:00
if ( isBlood ( ) & & randomize )
{
frametime + = Bcrc32 ( & randomize , 2 , 0 ) ;
}
2022-08-05 21:48:43 +00:00
int curframe = ( frametime & 0x7fffffff ) > > ( picanm [ tilenum ] . speed ( ) ) ;
2022-08-05 16:47:35 +00:00
2022-08-05 21:48:43 +00:00
switch ( picanm [ tilenum ] . type ( ) )
2022-08-05 16:47:35 +00:00
{
case PICANM_ANIMTYPE_FWD :
return curframe % ( framecount + 1 ) ;
case PICANM_ANIMTYPE_BACK :
return - ( curframe % ( framecount + 1 ) ) ;
case PICANM_ANIMTYPE_OSC :
curframe = curframe % ( framecount < < 1 ) ;
if ( curframe > = framecount ) return ( framecount < < 1 ) - curframe ;
else return curframe ;
}
}
return 0 ;
}
2020-05-29 21:33:26 +00:00
//===========================================================================
//
// Picks a texture for rendering for a given tilenum/palette combination
//
//===========================================================================
2020-10-07 21:22:29 +00:00
void tileUpdateAnimations ( )
{
for ( int i = 0 ; i < MAXTILES ; i + + )
{
2022-08-05 21:48:43 +00:00
if ( TileFiles . tiledata [ i ] . picanm . type ( ) )
2020-10-07 21:22:29 +00:00
{
2022-08-05 16:47:35 +00:00
int j = i + tileAnimateOfs ( i ) ;
2020-10-07 21:22:29 +00:00
auto id1 = TileFiles . tiledata [ i ] . texture - > GetID ( ) ;
auto id2 = TileFiles . tiledata [ j ] . texture - > GetID ( ) ;
TexMan . SetTranslation ( id1 , id2 ) ;
}
}
}
2022-08-04 21:47:01 +00:00
//===========================================================================
//
// validates the texture for rendering to a given tilenum
//
//===========================================================================
FCanvasTexture * tileGetCanvas ( int tilenum )
{
auto tex = tileGetTexture ( tilenum ) ;
if ( ! tex | | ! tex - > isHardwareCanvas ( ) ) return nullptr ;
auto canvas = static_cast < FCanvasTexture * > ( tex - > GetTexture ( ) ) ;
if ( ! canvas ) return nullptr ;
int xsiz = tex - > GetTexelWidth ( ) , ysiz = tex - > GetTexelHeight ( ) ;
if ( xsiz < = 0 | | ysiz < = 0 )
return nullptr ;
return canvas ;
}
2019-10-16 18:39:59 +00:00
PicAnm picanm ;
2020-10-05 19:47:53 +00:00
2022-12-06 08:28:00 +00:00
// wrappers that allow partial migration to a textureID-based setup.
2022-12-06 10:22:41 +00:00
const FTextureID walltype : : walltexture ( ) const
2022-12-06 08:28:00 +00:00
{
2022-12-06 10:46:48 +00:00
return tileGetTextureID ( wallpicnum ) ;
2022-12-06 08:28:00 +00:00
}
2022-12-06 10:22:41 +00:00
const FTextureID walltype : : overtexture ( ) const
2022-12-06 08:28:00 +00:00
{
2022-12-06 10:46:48 +00:00
return tileGetTextureID ( overpicnum ) ;
2022-12-06 08:28:00 +00:00
}
2022-12-06 10:11:50 +00:00
const FTextureID sectortype : : ceilingtexture ( ) const
2022-12-06 08:28:00 +00:00
{
2022-12-06 10:46:48 +00:00
return tileGetTextureID ( ceilingpicnum ) ;
2022-12-06 08:28:00 +00:00
}
2022-12-06 10:11:50 +00:00
const FTextureID sectortype : : floortexture ( ) const
2022-12-06 08:28:00 +00:00
{
2022-12-06 10:46:48 +00:00
return tileGetTextureID ( floorpicnum ) ;
2022-12-06 08:28:00 +00:00
}
2022-12-06 10:22:41 +00:00
const FTextureID spritetypebase : : spritetexture ( ) const
2022-12-06 08:28:00 +00:00
{
2022-12-06 10:46:48 +00:00
return tileGetTextureID ( picnum ) ;
2022-12-06 08:28:00 +00:00
}
2022-12-06 10:11:50 +00:00
void sectortype : : setfloortexture ( FTextureID tex )
{
auto p = TileFiles . textotile . CheckKey ( tex . GetIndex ( ) ) ;
if ( p ) floorpicnum = * p ;
}
void sectortype : : setceilingtexture ( FTextureID tex )
{
auto p = TileFiles . textotile . CheckKey ( tex . GetIndex ( ) ) ;
if ( p ) ceilingpicnum = * p ;
}
2022-12-06 10:46:48 +00:00
void walltype : : setwalltexture ( FTextureID tex )
{
auto p = TileFiles . textotile . CheckKey ( tex . GetIndex ( ) ) ;
if ( p ) wallpicnum = * p ;
}
void walltype : : setovertexture ( FTextureID tex )
{
auto p = TileFiles . textotile . CheckKey ( tex . GetIndex ( ) ) ;
if ( p ) overpicnum = * p ;
else overpicnum = - 1 ; // unlike the others this one can be invalid.
}
2022-12-07 14:57:28 +00:00
TileOffs * GetHiresOffset ( FTextureID tex )
{
// fixme: This must return nullptr if the tile has no replacement.
auto p = TileFiles . textotile . CheckKey ( tex . GetIndex ( ) ) ;
if ( p & & TileFiles . tiledata [ * p ] . hiofs . xsize ! = 0 ) return & TileFiles . tiledata [ * p ] . hiofs ;
else return nullptr ;
}