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 "baselayer.h"
# include "palette.h"
# include "m_crc32.h"
# include "build.h"
2019-12-11 22:41:05 +00:00
# include "gamecontrol.h"
2020-05-24 05:58:56 +00:00
# include "palettecontainer.h"
2020-05-24 17:12:22 +00:00
# include "texturemanager.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
2019-10-21 07:22:55 +00:00
BuildTiles TileFiles ;
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
{
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 ;
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.
//
//==========================================================================
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 ;
2020-05-24 17:12:22 +00:00
bmp . Create ( Width , Height ) ;
auto ppix = GetRawData ( ) ;
if ( ppix )
{
if ( remap = = nullptr ) remap = GPalette . BaseColors ;
bmp . CopyPixelData ( 0 , 0 , ppix , Width , Height , Height , 1 , 0 , remap ) ;
}
2019-10-12 20:49:46 +00:00
return bmp ;
}
2020-05-24 17:12:22 +00:00
TArray < uint8_t > FTileTexture : : Get8BitPixels ( bool alphatex )
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
}
//==========================================================================
//
// Tile textures are owned by their containing file object.
//
//==========================================================================
2020-05-24 17:12:22 +00:00
FArtTile * 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 11:53:27 +00:00
2019-10-12 20:49:46 +00:00
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 ) ;
2020-05-24 13:02:20 +00:00
tiledata [ tilenum ] . texture = tex ;
2020-05-24 08:30:09 +00:00
2020-05-24 13:02:20 +00:00
if ( ! permap ) tiledata [ tilenum ] . backup = 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.
//
//===========================================================================
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 ;
2020-05-24 17:12:22 +00:00
auto tex = GetTileTexture ( " " , 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 ;
2020-05-24 18:30:02 +00:00
this - > tiledata [ i ] . picanmbackup = 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 ;
}
}
2020-05-24 06:47:45 +00:00
//===========================================================================
//
// Replacement textures
//
//===========================================================================
void BuildTiles : : AddReplacement ( int picnum , const HightileReplacement & replace )
{
auto & Hightiles = tiledata [ picnum ] . Hightiles ;
for ( auto & ht : Hightiles )
{
if ( replace . palnum = = ht . palnum & & ( replace . faces [ 1 ] = = nullptr ) = = ( ht . faces [ 1 ] = = nullptr ) )
{
ht = replace ;
return ;
}
}
Hightiles . Push ( replace ) ;
}
void BuildTiles : : DeleteReplacement ( int picnum , int palnum )
{
auto & Hightiles = tiledata [ picnum ] . Hightiles ;
for ( int i = Hightiles . Size ( ) - 1 ; i > = 0 ; i - - )
{
if ( Hightiles [ i ] . palnum = = palnum ) Hightiles . Delete ( i ) ;
}
}
//===========================================================================
//
//
//
//===========================================================================
HightileReplacement * BuildTiles : : FindReplacement ( int picnum , int palnum , bool skybox )
{
auto & Hightiles = tiledata [ picnum ] . Hightiles ;
for ( ; ; )
{
for ( auto & rep : Hightiles )
{
if ( rep . palnum = = palnum & & ( rep . faces [ 1 ] ! = nullptr ) = = skybox ) return & rep ;
}
if ( ! palnum | | palnum > = MAXPALOOKUPS - RESERVEDPALS ) break ;
palnum = 0 ;
}
return nullptr ; // no replacement found
}
2019-10-12 06:54:06 +00:00
//===========================================================================
//
// 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 ;
}
//===========================================================================
//
// 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 ( ) ;
}
}
}
2020-03-29 12:01:46 +00:00
//===========================================================================
//
// InvalidateTile
//
//===========================================================================
2019-10-18 09:37:07 +00:00
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 )
{
2020-05-24 13:02:20 +00:00
auto tex = tiledata [ num ] . texture ;
2019-10-18 09:37:07 +00:00
tex - > DeleteHardwareTextures ( ) ;
2020-05-24 06:47:45 +00:00
for ( auto & rep : tiledata [ num ] . Hightiles )
2019-10-18 09:37:07 +00:00
{
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 ( ) ;
}
}
}
}
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-24 17:12:22 +00:00
canvas - > SetSize ( 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.
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
{
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-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
{
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-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-12-11 22:41:05 +00:00
for ( auto & addart : addedArt )
{
LoadArtFile ( addart , false ) ;
}
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-24 13:02:20 +00:00
FTexture * BuildTiles : : ValidateCustomTile ( int tilenum , ReplacementType type )
2019-10-12 20:49:46 +00:00
{
2019-10-14 21:11:01 +00:00
if ( tilenum < 0 | | tilenum > = MAXTILES ) return nullptr ;
2020-05-24 13:02:20 +00:00
if ( tiledata [ tilenum ] . texture ! = tiledata [ tilenum ] . backup ) return nullptr ; // no mucking around with map tiles.
auto tile = tiledata [ tilenum ] . texture ;
if ( tiledata [ tilenum ] . replacement = = type ) return tile ; // already created
if ( tiledata [ tilenum ] . replacement > ReplacementType : : Art ) return nullptr ; // different custom type - cannot replace again.
2019-10-14 21:11:01 +00:00
FTexture * replacement = nullptr ;
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:
// 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
}
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.
2019-11-24 12:59:36 +00:00
// It only gets used for the crosshair and 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.
2019-10-17 07:42:11 +00:00
// All of these effects should probably be redone without actual texture hacking...
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.
2019-10-14 21:11:01 +00:00
// todo: invalidate hardware textures for tile.
replacement = new FRestorableTile ( tile ) ;
}
2020-05-24 13:02:20 +00:00
else if ( type = = ReplacementType : : Canvas )
2020-03-29 12:01:46 +00:00
{
replacement = new FCanvasTexture ( " camera " , 0 , 0 ) ;
}
2019-10-14 21:11:01 +00:00
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
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 ;
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 ;
auto wtex = static_cast < FWritableTile * > ( tex ) ;
if ( ! wtex - > Resize ( width , height ) ) return nullptr ;
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-24 17:12:22 +00:00
auto wtex = static_cast < FWritableTile * > ( tex ) ;
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-24 13:02:20 +00:00
auto tile = tileGetTexture ( tileNum ) ;
if ( ! tile | | TileFiles . tiledata [ tileNum ] . replacement ! = ReplacementType : : Art ) return 0 ; // only consider original ART tiles.
2020-05-24 17:12:22 +00:00
auto pixels = tile - > Get8BitPixels ( false ) ;
if ( ! pixels . Size ( ) ) return 0 ;
int size = tile - > GetTexelWidth ( ) * tile - > GetTexelHeight ( ) ;
2019-10-15 21:38:01 +00:00
if ( size = = 0 ) return 0 ;
2020-05-24 17:12:22 +00:00
return crc32 ( 0 , ( const Bytef * ) pixels . Data ( ) , size ) ;
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 )
{
2020-05-24 17:12:22 +00:00
FTextureID texid = TexMan . CheckForTexture ( fn , ETextureType : : Any ) ;
if ( ! texid . isValid ( ) ) return - 1 ;
auto tex = TexMan . GetTexture ( texid ) ;
//tex->alphaThreshold = 255 - alphacut;
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 ;
2020-05-24 13:02:20 +00:00
TileFiles . tiledata [ tilenum ] . texture = tex ;
2019-10-16 20:02:27 +00:00
# pragma message("tileImportFromTexture needs rework!") // Reminder so that this place isn't forgotten.
//#if 0
2020-05-24 13:02:20 +00:00
// Does this make any difference when the texture gets *properly* inserted into the tile array? Answer: Yes, it affects how translations affect it.
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 ;
2020-05-24 10:31:38 +00:00
int srcxo , srcyo ;
FTexture * 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-24 17:12:22 +00:00
TArray < uint8_t > buffer = tex - > Get8BitPixels ( false ) ;
2019-10-15 21:38:01 +00:00
if ( pal ! = - 1 )
{
2020-05-23 16:18:36 +00:00
auto remap = paletteGetLookupTable ( 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
}
}
2020-05-24 17:12:22 +00:00
tex = new FLooseTile ( buffer , tex - > GetTexelWidth ( ) , tex - > GetTexelHeight ( ) ) ;
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
//==========================================================================
//
// Clear map specific ART
//
//==========================================================================
void artClearMapArt ( void )
{
TileFiles . CloseAllMapArt ( ) ;
2020-05-24 13:02:20 +00:00
for ( auto & td : TileFiles . tiledata )
{
td . texture = td . backup ;
td . picanm = td . picanmbackup ;
}
2020-05-24 08:30:09 +00:00
TileFiles . SetupReverseTileMap ( ) ;
2019-10-15 21:56:29 +00:00
}
//==========================================================================
//
2019-12-09 23:31:55 +00:00
// Load map specficied ART
2019-10-15 21:56:29 +00:00
//
//==========================================================================
2019-12-09 23:31:55 +00:00
static FString currentMapArt ;
2019-10-15 21:56:29 +00:00
void artSetupMapArt ( const char * filename )
{
2019-12-09 23:31:55 +00:00
if ( currentMapArt . CompareNoCase ( filename ) ) return ;
currentMapArt = filename ;
2019-10-15 21:56:29 +00:00
artClearMapArt ( ) ;
FStringf firstname ( " %s_00.art " , filename ) ;
2020-04-11 21:54:33 +00:00
auto fr = fileSystem . OpenFileReader ( firstname ) ;
2019-10-15 21:56:29 +00:00
if ( ! fr . isOpen ( ) ) return ;
2020-05-24 13:02:20 +00:00
for ( auto & td : TileFiles . tiledata )
{
td . picanmbackup = td . picanm ;
}
2019-10-15 21:56:29 +00:00
for ( bssize_t i = 0 ; i < MAXARTFILES_TOTAL - MAXARTFILES_BASE ; i + + )
{
FStringf fullname ( " %s_%02d.art " , filename , i ) ;
TileFiles . LoadArtFile ( fullname , true ) ;
}
2020-05-24 08:30:09 +00:00
TileFiles . SetupReverseTileMap ( ) ;
2019-10-15 21:56:29 +00:00
}
//==========================================================================
//
2019-10-15 21:29:47 +00:00
//
//
//==========================================================================
void tileDelete ( int tile )
{
2020-05-24 13:02:20 +00:00
TileFiles . TextureToTile . Remove ( tileGetTexture ( tile ) ) ;
TileFiles . tiledata [ tile ] . texture = TileFiles . tiledata [ tile ] . backup = TileFiles . Placeholder ;
2019-10-15 21:29:47 +00:00
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 )
{
2020-05-24 06:47:45 +00:00
TileFiles . DeleteReplacements ( 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 )
{
auto dtile = new FDummyTile ( width , height ) ;
TileFiles . AddTile ( tile , dtile ) ;
}
}
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 )
{
2020-05-24 13:02:20 +00:00
auto tex = tileGetTexture ( lastUnusedTile ) ;
2020-05-24 17:12:22 +00:00
if ( ! tex | | tex - > GetTexelWidth ( ) < = 0 | | tex - > GetTexelHeight ( ) < = 0 ) return lastUnusedTile ;
2019-10-15 21:56:29 +00:00
}
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 ;
2020-05-24 13:02:20 +00:00
auto tex = tileGetTexture ( tileNum ) ;
2020-05-24 17:12:22 +00:00
if ( ! tex | | tex - > GetTexelWidth ( ) < = 0 | | tex - > GetTexelHeight ( ) < = 0 ) return tileNum ;
TArray < uint8_t > buffer = tex - > Get8BitPixels ( false ) ;
TArray < uint8_t > dbuffer ( tex - > GetTexelWidth ( ) * tex - > GetTexelHeight ( ) , true ) ;
2019-10-15 21:56:29 +00:00
auto src = buffer . Data ( ) ;
auto dst = dbuffer . Data ( ) ;
2020-05-24 17:12:22 +00:00
auto width = tex - > GetTexelWidth ( ) ;
auto height = tex - > GetTexelHeight ( ) ;
for ( int x = 0 ; x < width ; + + x )
2019-10-15 21:56:29 +00:00
{
2020-05-24 17:12:22 +00:00
int xofs = width - x - 1 ;
int yofs = height * x ;
2019-10-15 21:56:29 +00:00
2020-05-24 17:12:22 +00:00
for ( int y = 0 ; y < height ; + + y )
* ( dst + y * width + xofs ) = * ( src + y + yofs ) ;
2019-10-15 21:56:29 +00:00
}
2020-05-24 17:12:22 +00:00
auto dtex = new FLooseTile ( dbuffer , tex - > GetTexelHeight ( ) , tex - > GetTexelWidth ( ) ) ;
2019-10-15 21:56:29 +00:00
int index = findUnusedTile ( ) ;
2020-05-24 13:02:20 +00:00
bool mapart = TileFiles . tiledata [ tileNum ] . texture ! = TileFiles . tiledata [ tileNum ] . backup ;
2019-10-15 21:56:29 +00:00
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
{
2020-05-24 14:11:10 +00:00
// let this go away.
2019-10-16 19:16:40 +00:00
auto res = textures . CheckKey ( path ) ;
if ( res ) return * res ;
2020-04-08 16:04:21 +00:00
auto lump = fileSystem . FindFile ( path ) ;
auto tex = FTexture : : CreateTexture ( path , lump , ETextureType : : Override ) ;
2019-10-16 19:16:40 +00:00
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.
//
//==========================================================================
2020-05-24 17:12:22 +00:00
int tileSetHightileReplacement ( int picnum , int palnum , const char * filename , float alphacut , float xscale , float yscale , float specpower , float specfactor , uint8_t flags )
2019-10-17 10:45:25 +00:00
{
2020-05-24 17:12:22 +00:00
if ( ( uint32_t ) picnum > = ( uint32_t ) MAXTILES ) return - 1 ;
if ( ( uint32_t ) palnum > = ( uint32_t ) MAXPALOOKUPS ) return - 1 ;
2019-10-17 10:45:25 +00:00
2020-05-24 13:02:20 +00:00
auto tex = tileGetTexture ( picnum ) ;
2020-05-24 17:12:22 +00:00
if ( tex - > GetTexelWidth ( ) < = 0 | | tex - > GetTexelHeight ( ) < = 0 )
2019-10-17 10:45:25 +00:00
{
2020-04-11 21:45:45 +00:00
Printf ( " Warning: defined hightile replacement for empty tile %d. " , picnum ) ;
2019-10-17 10:45:25 +00:00
return - 1 ; // cannot add replacements to empty tiles, must create one beforehand
}
HightileReplacement replace = { } ;
2020-05-24 17:12:22 +00:00
FTextureID texid = TexMan . CheckForTexture ( filename , ETextureType : : Any ) ;
if ( ! texid . isValid ( ) )
2019-10-17 10:45:25 +00:00
{
2020-04-11 21:45:45 +00:00
Printf ( " %s: Replacement for tile %d does not exist or is invalid \n " , filename , picnum ) ;
2019-10-17 10:45:25 +00:00
return - 1 ;
}
2020-05-24 17:12:22 +00:00
replace . faces [ 0 ] = TexMan . GetTexture ( texid ) ;
if ( replace . faces [ 0 ] = = nullptr )
replace . alphacut = min ( alphacut , 1.f ) ;
2019-10-17 10:45:25 +00:00
replace . scale = { xscale , yscale } ;
2020-05-24 17:12:22 +00:00
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 ;
2020-05-24 06:47:45 +00:00
TileFiles . AddReplacement ( picnum , replace ) ;
2020-05-24 17:12:22 +00:00
return 0 ;
2019-10-17 10:45:25 +00:00
}
//==========================================================================
//
// 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 ;
2020-05-24 13:02:20 +00:00
auto tex = tileGetTexture ( picnum ) ;
2020-05-24 17:12:22 +00:00
if ( tex - > GetTexelWidth ( ) < = 0 | | tex - > GetTexelHeight ( ) < = 0 )
2019-10-17 10:45:25 +00:00
{
2020-04-11 21:45:45 +00:00
Printf ( " Warning: defined skybox replacement for empty tile %d. " , picnum ) ;
2019-10-17 10:45:25 +00:00
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 )
{
2020-04-11 21:45:45 +00:00
Printf ( " %s: Skybox image for tile %d does not exist or is invalid \n " , * facenames , picnum ) ;
2019-10-17 10:45:25 +00:00
return - 1 ;
}
}
replace . flags = flags ;
2019-10-17 18:29:58 +00:00
replace . palnum = ( uint16_t ) palnum ;
2020-05-24 06:47:45 +00:00
TileFiles . AddReplacement ( picnum , replace ) ;
2019-10-17 10:45:25 +00:00
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 ;
2020-05-24 06:47:45 +00:00
TileFiles . DeleteReplacement ( picnum , palnum ) ;
2019-10-17 10:45:25 +00:00
return 0 ;
}
2020-01-28 11:42:17 +00:00
//==========================================================================
//
// Copy a block of a tile.
// Only used by RR's bowling lane.
//
//==========================================================================
void tileCopySection ( int tilenum1 , int sx1 , int sy1 , int xsiz , int ysiz , int tilenum2 , int sx2 , int sy2 )
{
2020-04-05 21:30:37 +00:00
int xsiz1 = tileWidth ( tilenum1 ) ;
int ysiz1 = tileHeight ( tilenum1 ) ;
int xsiz2 = tileWidth ( tilenum2 ) ;
int ysiz2 = tileHeight ( tilenum2 ) ;
2020-01-28 11:42:17 +00:00
if ( xsiz1 > 0 & & ysiz1 > 0 & & xsiz2 > 0 & & ysiz2 > 0 )
{
auto p1 = tilePtr ( tilenum1 ) ;
auto p2 = tileData ( tilenum2 ) ;
if ( p2 = = nullptr ) return ; // Error: Destination is not writable.
int x1 = sx1 ;
int x2 = sx2 ;
for ( int i = 0 ; i < xsiz ; i + + )
{
int y1 = sy1 ;
int y2 = sy2 ;
for ( int j = 0 ; j < ysiz ; j + + )
{
if ( x2 > = 0 & & y2 > = 0 & & x2 < xsiz2 & & y2 < ysiz2 )
{
auto src = p1 [ x1 * ysiz1 + y1 ] ;
2020-04-11 22:04:02 +00:00
if ( src ! = TRANSPARENT_INDEX )
2020-01-28 11:42:17 +00:00
p2 [ x2 * ysiz2 + y2 ] = src ;
}
y1 + + ;
y2 + + ;
if ( y1 > = ysiz1 ) y1 = 0 ;
}
x1 + + ;
x2 + + ;
if ( x1 > = xsiz1 ) x1 = 0 ;
}
}
TileFiles . InvalidateTile ( tilenum2 ) ;
}
2019-10-17 10:45:25 +00:00
2019-10-16 19:16:40 +00:00
2019-10-16 18:39:59 +00:00
TileSiz tilesiz ;
PicAnm picanm ;