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"
# include "textures.h"
# include "image.h"
# include "cache1d.h"
2019-10-14 21:11:01 +00:00
# include "baselayer.h"
# include "palette.h"
# include "m_crc32.h"
# include "build.h"
2019-10-12 06:54:06 +00:00
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
extern char * palookup [ ] ;
2019-10-21 07:22:55 +00:00
BuildTiles TileFiles ;
2019-10-14 21:11:01 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-10-17 07:42:11 +00:00
picanm_t tileConvertAnimFormat ( int32_t const picanimraw )
2019-10-12 06:54:06 +00:00
{
2019-10-12 20:49:46 +00:00
// Unpack a 4 byte packed anim descriptor into the internal 5 byte format.
picanm_t anm ;
2019-10-17 07:42:11 +00:00
anm . num = picanimraw & 63 ;
anm . xofs = ( picanimraw > > 8 ) & 255 ;
anm . yofs = ( picanimraw > > 16 ) & 255 ;
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.
//
//==========================================================================
2019-10-18 17:06:57 +00:00
FBitmap FTileTexture : : GetBgraBitmap ( const PalEntry * remap , int * ptrans )
2019-10-12 06:54:06 +00:00
{
2019-10-12 20:49:46 +00:00
FBitmap bmp ;
2019-10-14 21:11:01 +00:00
TArray < uint8_t > buffer ;
2019-10-12 20:49:46 +00:00
bmp . Create ( Size . x , Size . y ) ;
2019-10-14 21:11:01 +00:00
const uint8_t * ppix = Get8BitPixels ( ) ;
if ( ! ppix )
{
// This is needed for tiles with a palette remap.
buffer . Resize ( Size . x * Size . y ) ;
Create8BitPixels ( buffer . Data ( ) ) ;
ppix = buffer . Data ( ) ;
}
2019-10-12 20:49:46 +00:00
if ( ppix ) bmp . CopyPixelData ( 0 , 0 , ppix , Size . x , Size . y , Size . y , 1 , 0 , remap ) ;
return bmp ;
}
void FTileTexture : : Create8BitPixels ( uint8_t * buffer )
{
auto pix = Get8BitPixels ( ) ;
if ( pix ) memcpy ( buffer , pix , Size . x * Size . y ) ;
}
//==========================================================================
//
// Tile textures are owned by their containing file object.
//
//==========================================================================
2019-10-14 21:11:01 +00:00
FArtTile * GetTileTexture ( const char * name , const TArray < uint8_t > & backingstore , uint32_t offset , int width , int height , int picanm )
2019-10-12 20:49:46 +00:00
{
auto tex = new FArtTile ( backingstore , offset , width , height , picanm ) ;
if ( tex )
2019-10-12 06:54:06 +00:00
{
2019-10-12 20:49:46 +00:00
tex - > SetName ( name ) ;
2019-10-12 06:54:06 +00:00
}
2019-10-12 20:49:46 +00:00
return tex ;
}
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 : : AddTile ( int tilenum , FTexture * tex , bool permap )
2019-10-14 21:11:01 +00:00
{
2019-10-16 19:16:40 +00:00
assert ( AllTiles . Find ( tex ) = = AllTiles . Size ( ) & & AllMapTiles . Find ( tex ) = = AllMapTiles . Size ( ) ) ;
2019-10-14 21:11:01 +00:00
auto & array = permap ? AllMapTiles : AllTiles ;
array . Push ( tex ) ;
tiles [ tilenum ] = tex ;
if ( ! permap ) tilesbak [ tilenum ] = tex ;
}
2019-10-12 06:54:06 +00:00
//===========================================================================
//
// AddTiles
//
// Adds all the tiles in an artfile to the texture manager.
//
//===========================================================================
2019-10-21 07:22:55 +00:00
void BuildTiles : : AddTiles ( int firsttile , TArray < uint8_t > & RawData , bool permap )
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 ;
2019-10-14 21:11:01 +00:00
auto tex = GetTileTexture ( " " , RawData , uint32_t ( tiledata - tiles ) , width , height , anm ) ;
AddTile ( i , tex ) ;
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 )
{
2019-10-14 21:11:01 +00:00
initprintf ( " %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 )
{
initprintf ( " %s: Invalid tilestart or tileend \n " , fn ) ;
return 0 ;
}
if ( tileend < tilestart )
{
initprintf ( " %s: tileend < tilestart \n " , fn ) ;
return 0 ;
}
2019-10-12 06:54:06 +00:00
return tileend > = tilestart ? tileend - tilestart + 1 : 0 ;
}
//===========================================================================
//
// CloseAllMapArt
//
// Closes all per-map ART files
//
//===========================================================================
2019-10-21 07:22:55 +00:00
void BuildTiles : : CloseAllMapArt ( )
2019-10-12 06:54:06 +00:00
{
2019-10-14 21:11:01 +00:00
AllMapTiles . DeleteAndClear ( ) ;
PerMapArtFiles . DeleteAndClear ( ) ;
2019-10-12 06:54:06 +00:00
}
2019-10-18 09:37:07 +00:00
//===========================================================================
//
// ClearTextureCache
//
// Deletes all hardware textures
//
//===========================================================================
2019-10-21 07:22:55 +00:00
void BuildTiles : : ClearTextureCache ( bool artonly )
2019-10-18 09:37:07 +00:00
{
for ( auto tex : AllTiles )
{
tex - > DeleteHardwareTextures ( ) ;
}
for ( auto tex : AllMapTiles )
{
tex - > DeleteHardwareTextures ( ) ;
}
if ( ! artonly )
{
decltype ( textures ) : : Iterator it ( textures ) ;
decltype ( textures ) : : Pair * pair ;
while ( it . NextPair ( pair ) )
{
pair - > Value - > DeleteHardwareTextures ( ) ;
}
}
}
2019-10-21 07:22:55 +00:00
void BuildTiles : : InvalidateTile ( int num )
2019-10-18 09:37:07 +00:00
{
if ( ( unsigned ) num < MAXTILES )
{
auto tex = tiles [ num ] ;
tex - > DeleteHardwareTextures ( ) ;
for ( auto & rep : tex - > Hightiles )
{
2019-10-18 17:06:57 +00:00
for ( auto & reptex : rep . faces )
2019-10-18 09:37:07 +00:00
{
if ( reptex ) reptex - > DeleteHardwareTextures ( ) ;
}
}
}
}
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.
2019-10-17 07:42:11 +00:00
// Even for Ion Fury this will merely add 80 MB, because the engine already needs to cache the data, albeit in a compressed-per-lump form,
2019-10-12 06:54:06 +00:00
// so its 100MB art file will only have a partial impact on memory.
//
//===========================================================================
2019-10-21 07:22:55 +00:00
int BuildTiles : : LoadArtFile ( const char * fn , bool mapart , int firsttile )
2019-10-12 06:54:06 +00:00
{
auto 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
{
FileReader fr = kopenFileReader ( fn , 0 ) ;
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-12 20:49:46 +00:00
auto & descs = mapart ? PerMapArtFiles : ArtFiles ;
2019-10-14 21:11:01 +00:00
auto file = new BuildArtFile ;
descs . Push ( file ) ;
file - > filename = fn ;
file - > RawData = std : : move ( artdata ) ;
AddTiles ( firsttile , file - > RawData , mapart ) ;
2019-10-12 06:54:06 +00:00
}
}
}
2019-10-14 21:11:01 +00:00
else
{
//initprintf("%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-12 20:49:46 +00:00
else
{
2019-10-14 21:11:01 +00:00
// Reuse the old one but move it to the top. (better not.)
//auto fd = std::move(ArtFiles[old]);
//ArtFiles.Delete(old);
//ArtFiles.Push(std::move(fd));
2019-10-12 20:49:46 +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 ) ;
LoadArtFile ( fn , false ) ;
2019-10-12 20:49:46 +00:00
}
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
//==========================================================================
2019-10-21 07:22:55 +00:00
FTexture * BuildTiles : : ValidateCustomTile ( int tilenum , int type )
2019-10-12 20:49:46 +00:00
{
2019-10-14 21:11:01 +00:00
if ( tilenum < 0 | | tilenum > = MAXTILES ) return nullptr ;
if ( tiles [ tilenum ] ! = tilesbak [ tilenum ] ) return nullptr ; // no mucking around with map tiles.
auto tile = tiles [ tilenum ] ;
if ( tile & & tile - > GetUseType ( ) = = type ) return tile ; // already created
if ( tile - > GetUseType ( ) > FTexture : : Art ) return nullptr ; // different custom type - cannot replace again.
FTexture * replacement = nullptr ;
if ( type = = FTexture : : 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:
// Camera textures (should be made to be creatable by the hardware renderer instead of falling back on the software renderer.)
// thumbnails for savegame and loadgame (should bypass the texture manager entirely.)
// view tilting in the software renderer (this should just use a local buffer instead of relying on the texture manager.)
// Movie playback (like thumbnails this should bypass the texture manager entirely.)
// 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.
2019-10-14 21:11:01 +00:00
replacement = new FWritableTile ;
2019-10-12 20:49:46 +00:00
}
2019-10-14 21:11:01 +00:00
else if ( type = = FTexture : : Restorable )
{
// This is for modifying an existing tile.
// It only gets used for the crosshair and two specific effects:
// A) the fire in Blood.
// B) the pin display in Redneck Rampage's bowling lanes.
2019-10-17 07:42:11 +00:00
// All of these effects should probably be redone without actual texture hacking...
2019-10-14 21:11:01 +00:00
if ( tile - > GetWidth ( ) = = 0 | | tile - > GetHeight ( ) = = 0 ) return nullptr ; // The base must have a size for this to work.
// todo: invalidate hardware textures for tile.
replacement = new FRestorableTile ( tile ) ;
}
else return nullptr ;
AddTile ( tilenum , replacement ) ;
return replacement ;
2019-10-12 06:54:06 +00:00
}
2019-10-15 18:02:37 +00:00
//==========================================================================
//
// global interface
//
//==========================================================================
2019-10-21 07:22:55 +00:00
int32_t BuildTiles : : artLoadFiles ( const char * filename )
2019-10-15 18:02:37 +00:00
{
TileFiles . LoadArtSet ( filename ) ;
2019-10-16 18:39:59 +00:00
memset ( gotpic , 0 , sizeof ( gotpic ) ) ;
2019-10-15 18:02:37 +00:00
cacheInitBuffer ( MAXCACHE1DSIZE ) ;
return 0 ;
}
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 ;
auto tex = ValidateCustomTile ( tilenum , FTexture : : Writable ) ;
if ( tex = = nullptr ) return nullptr ;
auto wtex = static_cast < FWritableTile * > ( tex ) ;
if ( ! wtex - > Resize ( width , height ) ) return nullptr ;
return tex - > GetWritableBuffer ( ) ;
}
//==========================================================================
//
// Makes a tile writable - only used for a handful of special cases
// (todo: Investigate how to get rid of this)
//
//==========================================================================
2019-10-21 07:22:55 +00:00
uint8_t * BuildTiles : : tileMakeWritable ( int num )
2019-10-14 22:19:31 +00:00
{
auto tex = ValidateCustomTile ( num , FTexture : : Restorable ) ;
return tex ? tex - > GetWritableBuffer ( ) : nullptr ;
}
//==========================================================================
//
// Sets user content for a tile.
// This must copy the buffer to make sure that the renderer has the data,
// even if processing is deferred.
//
// Only used by the movie players.
//
//==========================================================================
2019-10-21 07:22:55 +00:00
void BuildTiles : : tileSetExternal ( int tilenum , int width , int height , uint8_t * data )
2019-10-14 22:19:31 +00:00
{
uint8_t * buffer = tileCreate ( tilenum , width , height ) ;
if ( buffer ) memcpy ( buffer , data , width * height ) ;
}
2019-10-15 21:29:47 +00:00
//==========================================================================
//
2019-10-15 21:38:01 +00:00
// Returns checksum for a given tile
//
//==========================================================================
int32_t tileCRC ( int tileNum )
{
if ( ( unsigned ) tileNum > = ( unsigned ) MAXTILES ) return 0 ;
auto tile = TileFiles . tiles [ tileNum ] ;
if ( ! tile | | tile - > GetUseType ( ) ! = FTexture : : Art ) return 0 ; // only consider original ART tiles.
auto pixels = tile - > Get8BitPixels ( ) ;
if ( ! pixels ) return 0 ;
int size = tile - > GetWidth ( ) * tile - > GetHeight ( ) ;
if ( size = = 0 ) return 0 ;
return crc32 ( 0 , ( const Bytef * ) pixels , size ) ;
}
//==========================================================================
//
// 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 )
{
2019-10-16 19:16:40 +00:00
FTexture * tex = TileFiles . GetTexture ( fn ) ;
2019-10-15 21:38:01 +00:00
if ( tex = = nullptr ) return - 1 ;
tex - > alphaThreshold = 255 - alphacut ;
int32_t xsiz = tex - > GetWidth ( ) , ysiz = tex - > GetHeight ( ) ;
if ( xsiz < = 0 | | ysiz < = 0 )
return - 2 ;
2019-10-16 20:02:27 +00:00
TileFiles . tiles [ tilenum ] = tex ;
# pragma message("tileImportFromTexture needs rework!") // Reminder so that this place isn't forgotten.
//#if 0
2019-10-15 21:38:01 +00:00
// Does this make any difference when the texture gets *properly* inserted into the tile array?
2019-10-16 20:02:27 +00:00
//if (istexture)
2019-10-17 10:45:25 +00:00
tileSetHightileReplacement ( tilenum , 0 , fn , ( float ) ( 255 - alphacut ) * ( 1.f / 255.f ) , 1.0f , 1.0f , 1.0 , 1.0 , 0 ) ; // At the moment this is the only way to load the texture. The texture creation code is not ready yet for downconverting an image.
2019-10-16 20:02:27 +00:00
//#endif
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 )
{
// Todo. Since I do not know if some mod needs this it's of low priority now.
// Let's get things working first.
picanm_t * picanm = nullptr ;
picanm_t * sourceanm = nullptr ;
if ( pal = = - 1 & & tile = = source )
{
// Only modify the picanm info.
FTexture * tex = TileFiles . tiles [ tile ] ;
if ( ! tex ) return ;
picanm = & tex - > PicAnim ;
sourceanm = picanm ;
}
else
{
if ( source = = - 1 ) source = tile ;
FTexture * tex = TileFiles . tiles [ source ] ;
if ( ! tex ) return ;
sourceanm = & tex - > PicAnim ;
TArray < uint8_t > buffer ( tex - > GetWidth ( ) * tex - > GetHeight ( ) , true ) ;
tex - > Create8BitPixels ( buffer . Data ( ) ) ;
if ( pal ! = - 1 )
{
auto remap = palookup [ pal ] ;
for ( auto & pixel : buffer )
{
pixel = palookup [ pal ] [ pixel ] ;
}
}
tex = new FLooseTile ( buffer , tex - > GetWidth ( ) , tex - > GetHeight ( ) ) ;
picanm = & tex - > PicAnim ;
TileFiles . AddTile ( tile , tex ) ;
}
picanm - > xofs = xoffset ! = - 1024 ? clamp ( xoffset , - 128 , 127 ) : sourceanm - > xofs ;
picanm - > yofs = yoffset ! = - 1024 ? clamp ( yoffset , - 128 , 127 ) : sourceanm - > yofs ;
picanm - > sf = ( picanm - > sf & ~ PICANM_MISC_MASK ) | ( sourceanm - > sf & PICANM_MISC_MASK ) | flags ;
}
2019-10-15 21:56:29 +00:00
//==========================================================================
//
// Clear map specific ART
//
//==========================================================================
void artClearMapArt ( void )
{
TileFiles . CloseAllMapArt ( ) ;
memcpy ( TileFiles . tiles , TileFiles . tilesbak , sizeof ( TileFiles . tiles ) ) ;
}
//==========================================================================
//
// Load map specfici ART
//
//==========================================================================
void artSetupMapArt ( const char * filename )
{
artClearMapArt ( ) ;
FStringf firstname ( " %s_00.art " , filename ) ;
auto fr = kopenFileReader ( firstname , 0 ) ;
if ( ! fr . isOpen ( ) ) return ;
for ( bssize_t i = 0 ; i < MAXARTFILES_TOTAL - MAXARTFILES_BASE ; i + + )
{
FStringf fullname ( " %s_%02d.art " , filename , i ) ;
TileFiles . LoadArtFile ( fullname , true ) ;
}
}
//==========================================================================
//
2019-10-15 21:29:47 +00:00
//
//
//==========================================================================
void tileDelete ( int tile )
{
TileFiles . tiles [ tile ] = TileFiles . tilesbak [ tile ] = TileFiles . Placeholder ;
vox_undefine ( tile ) ;
md_undefinetile ( tile ) ;
2019-10-17 10:45:25 +00:00
tileRemoveReplacement ( tile ) ;
}
//==========================================================================
//
//
//
//==========================================================================
2019-10-15 21:29:47 +00:00
2019-10-17 10:45:25 +00:00
void tileRemoveReplacement ( int tile )
{
2019-10-17 18:29:58 +00:00
if ( ( unsigned ) tile > = MAXTILES ) return ;
2019-10-17 10:45:25 +00:00
FTexture * tex = TileFiles . tiles [ tile ] ;
tex - > DeleteReplacements ( ) ;
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 )
{
auto dtile = new FDummyTile ( width , height ) ;
TileFiles . AddTile ( tile , dtile ) ;
}
}
2019-10-15 21:56:29 +00:00
//==========================================================================
//
//
//
//==========================================================================
bool tileLoad ( int tileNum )
{
if ( ( unsigned ) tileNum > = MAXTILES ) return false ;
auto tex = TileFiles . tiles [ tileNum ] ;
if ( ! tex | | tex - > GetWidth ( ) < = 0 | | tex - > GetHeight ( ) < = 0 ) return false ;
if ( tex - > Get8BitPixels ( ) ) return true ;
2019-10-20 23:01:38 +00:00
if ( ! tex - > CachedPixels . Size ( ) )
2019-10-15 21:56:29 +00:00
{
// Allocate storage if necessary.
2019-10-20 23:01:38 +00:00
tex - > CachedPixels . Resize ( tex - > GetWidth ( ) * tex - > GetHeight ( ) ) ;
tex - > Create8BitPixels ( tex - > CachedPixels . Data ( ) ) ;
2019-10-15 21:56:29 +00:00
}
return true ;
2019-10-20 23:01:38 +00:00
}
2019-10-15 21:56:29 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-10-21 07:22:55 +00:00
int BuildTiles : : findUnusedTile ( void )
2019-10-15 21:56:29 +00:00
{
static int lastUnusedTile = MAXUSERTILES - 1 ;
for ( ; lastUnusedTile > = 0 ; - - lastUnusedTile )
{
auto tex = TileFiles . tiles [ lastUnusedTile ] ;
if ( ! tex | | tex - > GetWidth ( ) < = 0 | | tex - > GetHeight ( ) < = 0 ) return lastUnusedTile ;
}
return - 1 ;
}
2019-10-21 07:22:55 +00:00
int BuildTiles : : tileCreateRotated ( int tileNum )
2019-10-15 21:56:29 +00:00
{
if ( ( unsigned ) tileNum > = MAXTILES ) return tileNum ;
auto tex = TileFiles . tiles [ tileNum ] ;
if ( ! tex | | tex - > GetWidth ( ) < = 0 | | tex - > GetHeight ( ) < = 0 ) return tileNum ;
TArray < uint8_t > buffer ( tex - > GetWidth ( ) * tex - > GetHeight ( ) , true ) ;
tex - > Create8BitPixels ( buffer . Data ( ) ) ;
TArray < uint8_t > dbuffer ( tex - > GetWidth ( ) * tex - > GetHeight ( ) , true ) ;
auto src = buffer . Data ( ) ;
auto dst = dbuffer . Data ( ) ;
// the engine has a squarerotatetile() we could call, but it mirrors at the same time
auto siz = tex - > GetSize ( ) ;
for ( int x = 0 ; x < siz . x ; + + x )
{
int xofs = siz . x - x - 1 ;
int yofs = siz . y * x ;
for ( int y = 0 ; y < siz . y ; + + y )
* ( dst + y * siz . x + xofs ) = * ( src + y + yofs ) ;
}
auto dtex = new FLooseTile ( dbuffer , tex - > GetHeight ( ) , tex - > GetWidth ( ) ) ;
int index = findUnusedTile ( ) ;
bool mapart = TileFiles . tiles [ tileNum ] ! = TileFiles . tilesbak [ tileNum ] ;
TileFiles . AddTile ( index , dtex , mapart ) ;
return index ;
}
void tileSetAnim ( int tile , const picanm_t & anm )
{
}
2019-10-16 18:39:59 +00:00
2019-10-16 19:16:40 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-10-21 07:22:55 +00:00
FTexture * BuildTiles : : GetTexture ( const char * path )
2019-10-16 19:16:40 +00:00
{
auto res = textures . CheckKey ( path ) ;
if ( res ) return * res ;
auto tex = FTexture : : CreateTexture ( path ) ;
if ( tex ) textures . Insert ( path , tex ) ;
return tex ;
}
//==========================================================================
//
//
//
//==========================================================================
2019-10-21 07:22:55 +00:00
void BuildTiles : : CloseAll ( )
2019-10-16 19:16:40 +00:00
{
decltype ( textures ) : : Iterator it ( textures ) ;
decltype ( textures ) : : Pair * pair ;
while ( it . NextPair ( pair ) ) delete pair - > Value ;
textures . Clear ( ) ;
CloseAllMapArt ( ) ;
ArtFiles . DeleteAndClear ( ) ;
AllTiles . DeleteAndClear ( ) ;
if ( Placeholder ) delete Placeholder ;
Placeholder = nullptr ;
}
2019-10-17 10:45:25 +00:00
//==========================================================================
//
// Specifies a replacement texture for an ART tile.
//
//==========================================================================
int tileSetHightileReplacement ( int picnum , int palnum , const char * filename , float alphacut , float xscale , float yscale , float specpower , float specfactor , uint8_t flags )
{
if ( ( uint32_t ) picnum > = ( uint32_t ) MAXTILES ) return - 1 ;
if ( ( uint32_t ) palnum > = ( uint32_t ) MAXPALOOKUPS ) return - 1 ;
auto tex = TileFiles . tiles [ picnum ] ;
if ( tex - > GetWidth ( ) < = 0 | | tex - > GetHeight ( ) < = 0 )
{
initprintf ( " Warning: defined hightile replacement for empty tile %d. " , picnum ) ;
return - 1 ; // cannot add replacements to empty tiles, must create one beforehand
}
HightileReplacement replace = { } ;
replace . faces [ 0 ] = TileFiles . GetTexture ( filename ) ;
if ( replace . faces [ 0 ] = = nullptr )
{
initprintf ( " %s: Replacement for tile %d does not exist or is invalid \n " , filename , picnum ) ;
return - 1 ;
}
replace . alphacut = min ( alphacut , 1.f ) ;
replace . scale = { xscale , yscale } ;
replace . specpower = specpower ; // currently unused
replace . specfactor = specfactor ; // currently unused
replace . flags = flags ;
2019-10-17 18:29:58 +00:00
replace . palnum = ( uint16_t ) palnum ;
2019-10-17 10:45:25 +00:00
tex - > AddReplacement ( replace ) ;
return 0 ;
}
//==========================================================================
//
// Define the faces of a skybox
//
//==========================================================================
int tileSetSkybox ( int picnum , int palnum , const char * * facenames , int flags )
{
if ( ( uint32_t ) picnum > = ( uint32_t ) MAXTILES ) return - 1 ;
if ( ( uint32_t ) palnum > = ( uint32_t ) MAXPALOOKUPS ) return - 1 ;
2019-10-17 18:29:58 +00:00
auto tex = TileFiles . tiles [ picnum ] ;
2019-10-17 10:45:25 +00:00
if ( tex - > GetWidth ( ) < = 0 | | tex - > GetHeight ( ) < = 0 )
{
initprintf ( " Warning: defined skybox replacement for empty tile %d. " , picnum ) ;
return - 1 ; // cannot add replacements to empty tiles, must create one beforehand
}
HightileReplacement replace = { } ;
for ( auto & face : replace . faces )
{
face = TileFiles . GetTexture ( * facenames ) ;
if ( face = = nullptr )
{
initprintf ( " %s: Skybox image for tile %d does not exist or is invalid \n " , * facenames , picnum ) ;
return - 1 ;
}
}
replace . flags = flags ;
2019-10-17 18:29:58 +00:00
replace . palnum = ( uint16_t ) palnum ;
2019-10-17 10:45:25 +00:00
tex - > AddReplacement ( replace ) ;
return 0 ;
}
//==========================================================================
//
// Remove a replacement
//
//==========================================================================
int tileDeleteReplacement ( int picnum , int palnum )
{
if ( ( uint32_t ) picnum > = ( uint32_t ) MAXTILES ) return - 1 ;
if ( ( uint32_t ) palnum > = ( uint32_t ) MAXPALOOKUPS ) return - 1 ;
auto tex = TileFiles . tiles [ picnum ] ;
tex - > DeleteReplacement ( palnum ) ;
return 0 ;
}
2019-10-16 19:16:40 +00:00
2019-10-16 18:39:59 +00:00
TileSiz tilesiz ;
PicAnm picanm ;
PicSiz picsiz ;