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"
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"
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
}
//==========================================================================
//
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 ) ;
2020-05-24 21:26:47 +00:00
for ( auto & tile : tiledata )
{
tile . texture = Placeholder ;
tile . backup = Placeholder ;
tile . picanm = { } ;
tile . RotTile = { - 1 , - 1 } ;
tile . replacement = ReplacementType : : Art ;
2020-05-29 21:33:26 +00:00
tile . alphaThreshold = 0.5 ;
2021-04-06 13:55:33 +00:00
tile . hiofs = { } ;
2020-05-24 21:26:47 +00:00
}
}
2019-10-14 21:11:01 +00:00
//==========================================================================
//
//
//
//==========================================================================
2020-05-25 21:59:07 +00:00
void BuildTiles : : AddTile ( int tilenum , FGameTexture * tex , bool permap )
2019-10-14 21:11:01 +00:00
{
2020-05-24 21:26:47 +00:00
assert ( ! tex - > GetID ( ) . isValid ( ) ) ; // must not be added yet.
2020-05-25 21:59:07 +00:00
TexMan . AddGameTexture ( tex ) ;
2020-05-24 13:02:20 +00:00
tiledata [ tilenum ] . texture = tex ;
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.
//
//===========================================================================
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 ;
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 ;
}
}
//===========================================================================
//
// 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
//===========================================================================
//
// 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 ;
2021-04-05 17:18:57 +00:00
tex - > CleanHardwareData ( ) ;
2020-05-24 19:19:33 +00:00
tiledata [ num ] . rawCache . data . Clear ( ) ;
2019-10-18 09:37:07 +00:00
}
}
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
}
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
{
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 ] ;
if ( td . texture ! = td . backup ) return nullptr ; // no mucking around with map tiles.
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:
// 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.
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.
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.
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-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 ;
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 ) ;
TexMan . AddGameTexture ( tex ) ;
2020-09-09 22:05:10 +00:00
TileFiles . tiledata [ tilenum ] . backup = TileFiles . tiledata [ tilenum ] . texture = tex ;
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
}
}
2020-05-25 21:59:07 +00:00
tex = MakeGameTexture ( new FImageTexture ( new FLooseTile ( buffer , tex - > GetTexelWidth ( ) , tex - > GetTexelHeight ( ) ) ) , " " , 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
//==========================================================================
//
// Clear map specific ART
//
//==========================================================================
2020-09-01 22:36:49 +00:00
static FString currentMapArt ;
2019-10-15 21:56:29 +00:00
void artClearMapArt ( void )
{
2020-05-24 13:02:20 +00:00
for ( auto & td : TileFiles . tiledata )
{
td . texture = td . backup ;
td . picanm = td . picanmbackup ;
}
2020-09-01 22:36:49 +00:00
currentMapArt = " " ;
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
//
//==========================================================================
void artSetupMapArt ( const char * filename )
{
2021-04-14 12:16:09 +00:00
FString lcfilename = StripExtension ( filename ) ;
lcfilename . MakeLower ( ) ;
if ( currentMapArt . CompareNoCase ( lcfilename ) = = 0 ) return ;
2019-10-15 21:56:29 +00:00
artClearMapArt ( ) ;
2021-04-14 12:16:09 +00:00
currentMapArt = lcfilename ;
2019-10-15 21:56:29 +00:00
2020-05-24 21:26:47 +00:00
// Re-get from the texture manager if this map's tiles have already been created.
if ( TileFiles . maptilesadded . Find ( lcfilename ) < TileFiles . maptilesadded . Size ( ) )
{
for ( int i = 0 ; i < MAXTILES ; i + + )
{
FStringf name ( " maptile_%s_%05d " , lcfilename . GetChars ( ) , i ) ;
auto texid = TexMan . CheckForTexture ( name , ETextureType : : Any ) ;
if ( texid . isValid ( ) )
{
2020-05-25 21:59:07 +00:00
TileFiles . tiledata [ i ] . texture = TexMan . GetGameTexture ( texid ) ;
2020-05-24 21:26:47 +00:00
}
}
return ;
}
TileFiles . maptilesadded . Push ( lcfilename ) ;
FStringf firstname ( " %s_00.art " , lcfilename . GetChars ( ) ) ;
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
2020-05-24 21:26:47 +00:00
2021-03-24 20:13:36 +00:00
for ( int i = 0 ; i < MAXARTFILES_TOTAL - MAXARTFILES_BASE ; i + + )
2019-10-15 21:56:29 +00:00
{
2021-04-14 12:16:09 +00:00
FStringf fullname ( " %s_%02d.art " , lcfilename . GetChars ( ) , i ) ;
2020-05-24 20:37:50 +00:00
TileFiles . LoadArtFile ( fullname , filename ) ;
2019-10-15 21:56:29 +00:00
}
}
//==========================================================================
//
2019-10-15 21:29:47 +00:00
//
//
//==========================================================================
void tileDelete ( int tile )
{
2020-05-25 21:59:07 +00:00
TileFiles . tiledata [ tile ] . texture = TileFiles . tiledata [ tile ] . backup = TexMan . GameByIndex ( 0 ) ;
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.
2019-10-15 21:29:47 +00:00
md_undefinetile ( tile ) ;
}
//==========================================================================
//
//
//
//==========================================================================
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
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-29 13:55:08 +00:00
if ( ! tex | | ! tex - > isValid ( ) ) return lastUnusedTile ;
2019-10-15 21:56:29 +00:00
}
return - 1 ;
}
2020-04-10 16:17:26 +00:00
//==========================================================================
//
// fixme: This *really* needs to be done by rotating the texture coordinates,
// not by creating an entirely new texture.
// Unfortunately it's in all the wrong place in the rendering code so it
// has to wait for later.
//
//==========================================================================
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 ;
2020-05-25 21:59:07 +00:00
TArray < uint8_t > buffer = tex - > GetTexture ( ) - > Get8BitPixels ( false ) ;
2020-05-24 17:12:22 +00:00
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-30 18:55:29 +00:00
auto dtex = MakeGameTexture ( new FImageTexture ( new FLooseTile ( dbuffer , tex - > GetTexelHeight ( ) , tex - > GetTexelWidth ( ) ) ) , " " , ETextureType : : Override ) ;
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 ;
}
2019-10-16 19:16:40 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-10-21 07:22:55 +00:00
void BuildTiles : : CloseAll ( )
2019-10-16 19:16:40 +00:00
{
ArtFiles . DeleteAndClear ( ) ;
}
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 ) ;
}
2020-09-29 19:47:32 +00:00
//==========================================================================
//
// Check if two tiles are the same
//
//==========================================================================
bool tileEqualTo ( int me , int other )
{
auto tile = tileGetTexture ( me ) ;
auto tile2 = tileGetTexture ( other ) ;
int tilew1 = tile - > GetTexelWidth ( ) ;
int tileh1 = tile - > GetTexelHeight ( ) ;
int tilew2 = tile2 - > GetTexelWidth ( ) ;
int tileh2 = tile2 - > GetTexelHeight ( ) ;
if ( tilew1 = = tilew2 & & tileh1 = = tileh2 )
{
auto p1 = tileRawData ( me ) ;
auto p2 = tileRawData ( other ) ;
if ( p1 & & p2 & & ! memcmp ( p1 , p2 , tilew1 * tileh2 ) )
return true ;
}
return false ;
}
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 + + )
{
2021-04-17 10:40:23 +00:00
if ( TileFiles . tiledata [ i ] . picanm . sf & PICANM_ANIMTYPE_MASK )
2020-10-07 21:22:29 +00:00
{
int j = i + animateoffs ( i , 0 ) ;
auto id1 = TileFiles . tiledata [ i ] . texture - > GetID ( ) ;
auto id2 = TileFiles . tiledata [ j ] . texture - > GetID ( ) ;
TexMan . SetTranslation ( id1 , id2 ) ;
}
}
}
2020-09-14 23:21:17 +00:00
//===========================================================================
//
2020-09-15 22:10:12 +00:00
// Parsing stuff for tile data comes below.
2020-09-14 23:21:17 +00:00
//
//===========================================================================
2020-09-15 22:10:12 +00:00
//===========================================================================
//
// Helpers for tile parsing
//
//===========================================================================
2020-09-15 22:21:17 +00:00
bool ValidateTileRange ( const char * cmd , int & begin , int & end , FScriptPosition pos , bool allowswap )
2020-09-14 23:21:17 +00:00
{
2020-09-15 22:10:12 +00:00
if ( end < begin )
2020-09-14 23:21:17 +00:00
{
2020-09-15 22:10:12 +00:00
pos . Message ( MSG_WARNING , " %s: tile range [%d..%d] is backwards. Indices were swapped. " , cmd , begin , end ) ;
std : : swap ( begin , end ) ;
2020-09-14 23:21:17 +00:00
}
2020-09-15 22:10:12 +00:00
if ( ( unsigned ) begin > = MAXUSERTILES | | ( unsigned ) end > = MAXUSERTILES )
2020-09-14 23:21:17 +00:00
{
2020-09-15 22:10:12 +00:00
pos . Message ( MSG_ERROR , " %s: Invalid tile range [%d..%d] " , cmd , begin , end ) ;
return false ;
2020-09-14 23:21:17 +00:00
}
2020-09-15 22:10:12 +00:00
return true ;
}
bool ValidateTilenum ( const char * cmd , int tile , FScriptPosition pos )
{
if ( ( unsigned ) tile > = MAXUSERTILES )
2020-09-14 23:21:17 +00:00
{
2020-09-15 22:10:12 +00:00
pos . Message ( MSG_ERROR , " %s: Invalid tile number %d " , cmd , tile ) ;
return false ;
2020-09-14 23:21:17 +00:00
}
2019-10-17 10:45:25 +00:00
2020-09-15 22:10:12 +00:00
return true ;
}
//===========================================================================
//
// Internal worker for tileImportTexture
//
//===========================================================================
void processTileImport ( const char * cmd , FScriptPosition & pos , TileImport & imp )
{
if ( ! ValidateTilenum ( cmd , imp . tile , pos ) )
return ;
if ( imp . crc32 ! = INT64_MAX & & int ( imp . crc32 ) ! = tileGetCRC32 ( imp . tile ) )
return ;
2020-09-14 23:21:17 +00:00
if ( imp . sizex ! = INT_MAX & & tileWidth ( imp . tile ) ! = imp . sizex & & tileHeight ( imp . tile ) ! = imp . sizey )
return ;
2020-09-15 22:10:12 +00:00
2021-04-13 16:17:28 +00:00
imp . alphacut = clamp ( imp . alphacut , 0 , 255 ) ;
2020-09-14 23:27:24 +00:00
gi - > SetTileProps ( imp . tile , imp . surface , imp . vox , imp . shade ) ;
2020-09-14 23:21:17 +00:00
if ( imp . fn . IsNotEmpty ( ) & & tileImportFromTexture ( imp . fn , imp . tile , imp . alphacut , imp . istexture ) < 0 ) return ;
2020-09-15 19:17:08 +00:00
TileFiles . tiledata [ imp . tile ] . picanm . sf | = imp . flags ;
2020-09-14 23:21:17 +00:00
// This is not quite the same as originally, for two reasons:
// 1: Since these are texture properties now, there's no need to clear them.
// 2: The original code assumed that an imported texture cannot have an offset. But this can import Doom patches and PNGs with grAb, so the situation is very different.
if ( imp . xoffset = = INT_MAX ) imp . xoffset = tileLeftOffset ( imp . tile ) ;
2021-04-13 16:17:28 +00:00
else imp . xoffset = clamp ( imp . xoffset , - 128 , 127 ) ;
2020-09-14 23:21:17 +00:00
if ( imp . yoffset = = INT_MAX ) imp . yoffset = tileTopOffset ( imp . tile ) ;
2021-04-13 16:17:28 +00:00
else imp . yoffset = clamp ( imp . yoffset , - 128 , 127 ) ;
2020-09-14 23:21:17 +00:00
auto tex = tileGetTexture ( imp . tile ) ;
2021-04-13 16:17:28 +00:00
if ( tex )
{
tex - > SetOffsets ( imp . xoffset , imp . yoffset ) ;
if ( imp . flags & PICANM_NOFULLBRIGHT_BIT )
{
tex - > SetDisableBrightmap ( ) ;
}
}
2020-09-15 19:17:08 +00:00
if ( imp . extra ! = INT_MAX ) TileFiles . tiledata [ imp . tile ] . picanm . extra = imp . extra ;
2020-09-14 23:21:17 +00:00
}
2019-10-16 19:16:40 +00:00
2020-09-15 22:21:17 +00:00
//===========================================================================
//
// Internal worker for tileSetAnim
//
//===========================================================================
void processSetAnim ( const char * cmd , FScriptPosition & pos , SetAnim & imp )
{
if ( ! ValidateTilenum ( cmd , imp . tile1 , pos ) | |
! ValidateTilenum ( cmd , imp . tile2 , pos ) )
return ;
if ( imp . type < 0 | | imp . type > 3 )
{
pos . Message ( MSG_ERROR , " %s: animation type must be 0-3, got %d " , cmd , imp . type ) ;
return ;
}
int count = imp . tile2 - imp . tile1 ;
if ( imp . type = = ( PICANM_ANIMTYPE_BACK > > PICANM_ANIMTYPE_SHIFT ) & & imp . tile1 > imp . tile2 )
count = - count ;
TileFiles . setAnim ( imp . tile1 , imp . type , imp . speed , count ) ;
}
2019-10-16 18:39:59 +00:00
PicAnm picanm ;
2020-10-05 19:47:53 +00:00
2020-10-06 18:05:51 +00:00
#if 0 // this only gets in if unavoidable. It'd be preferable if the script side can solely operate on texture names.
2020-10-05 19:47:53 +00:00
# include "vm.h"
static int GetTexture ( int tile , int anim )
{
if ( tile < 0 | | tile > = MAXTILES ) return 0 ;
auto tex = tileGetTexture ( tile , anim ) ;
return tex ? tex - > GetID ( ) . GetIndex ( ) : 0 ;
}
DEFINE_ACTION_FUNCTION_NATIVE ( _TileFiles , GetTexture , GetTexture )
{
PARAM_PROLOGUE ;
PARAM_INT ( tile ) ;
PARAM_BOOL ( animate ) ;
ACTION_RETURN_INT ( GetTexture ( tile , animate ) ) ;
}
2020-10-06 18:05:51 +00:00
# endif
2020-11-10 15:22:02 +00:00