2020-05-24 05:58:56 +00:00
|
|
|
#pragma once
|
|
|
|
|
2020-09-14 23:21:17 +00:00
|
|
|
#include <limits.h>
|
2020-05-24 05:58:56 +00:00
|
|
|
#include "textures.h"
|
2020-04-10 16:17:26 +00:00
|
|
|
#include "image.h"
|
2020-05-24 17:12:22 +00:00
|
|
|
#include "i_time.h"
|
2020-06-20 16:17:49 +00:00
|
|
|
#include "intvec.h"
|
2022-01-23 20:30:00 +00:00
|
|
|
#include "name.h"
|
2020-05-24 11:53:27 +00:00
|
|
|
|
|
|
|
enum AnimFlags
|
|
|
|
{
|
|
|
|
PICANM_ANIMTYPE_NONE = 0,
|
|
|
|
PICANM_ANIMTYPE_OSC = (1 << 6),
|
|
|
|
PICANM_ANIMTYPE_FWD = (2 << 6),
|
|
|
|
PICANM_ANIMTYPE_BACK = (3 << 6),
|
|
|
|
|
|
|
|
PICANM_ANIMTYPE_SHIFT = 6,
|
|
|
|
PICANM_ANIMTYPE_MASK = (3 << 6), // must be 192
|
|
|
|
PICANM_MISC_MASK = (3 << 4),
|
|
|
|
PICANM_TEXHITSCAN_BIT = (2 << 4),
|
|
|
|
PICANM_NOFULLBRIGHT_BIT = (1 << 4),
|
|
|
|
PICANM_ANIMSPEED_MASK = 15, // must be 15
|
|
|
|
};
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
MAXTILES = 30720,
|
|
|
|
MAXUSERTILES = (MAXTILES-16) // reserve 16 tiles at the end
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2020-05-29 21:33:26 +00:00
|
|
|
enum ETexType
|
|
|
|
{
|
|
|
|
TT_INDEXED,
|
|
|
|
TT_TRUECOLOR,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2020-05-24 06:47:45 +00:00
|
|
|
enum class ReplacementType : int
|
|
|
|
{
|
|
|
|
Art,
|
|
|
|
Writable,
|
|
|
|
Restorable,
|
|
|
|
Canvas
|
|
|
|
};
|
|
|
|
|
2020-05-24 11:53:27 +00:00
|
|
|
|
|
|
|
// NOTE: If the layout of this struct is changed, loadpics() must be modified
|
|
|
|
// accordingly.
|
|
|
|
struct picanm_t
|
|
|
|
{
|
2020-09-15 22:21:17 +00:00
|
|
|
uint16_t num; // animate number
|
2020-05-24 11:53:27 +00:00
|
|
|
uint8_t sf; // anim. speed and flags
|
|
|
|
uint8_t extra;
|
|
|
|
|
|
|
|
void Clear()
|
|
|
|
{
|
2020-09-26 09:59:24 +00:00
|
|
|
extra = sf = 0;
|
|
|
|
num = 0;
|
2020-05-24 11:53:27 +00:00
|
|
|
}
|
2022-08-05 21:48:43 +00:00
|
|
|
|
|
|
|
int speed()
|
|
|
|
{
|
|
|
|
return sf & PICANM_ANIMSPEED_MASK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int type()
|
|
|
|
{
|
|
|
|
return sf & PICANM_ANIMTYPE_MASK;
|
|
|
|
}
|
|
|
|
|
2020-05-24 11:53:27 +00:00
|
|
|
};
|
|
|
|
picanm_t tileConvertAnimFormat(int32_t const picanmdisk, int* lo, int* to);
|
|
|
|
|
2020-04-10 16:17:26 +00:00
|
|
|
class FTileTexture : public FImageSource
|
2020-05-24 05:58:56 +00:00
|
|
|
{
|
|
|
|
public:
|
2020-05-24 17:12:22 +00:00
|
|
|
FTileTexture()
|
2020-04-10 16:17:26 +00:00
|
|
|
{
|
|
|
|
bUseGamePalette = true;
|
|
|
|
bTranslucent = false;
|
|
|
|
}
|
2020-05-24 17:12:22 +00:00
|
|
|
virtual uint8_t* GetRawData() = 0;
|
2020-04-10 16:17:26 +00:00
|
|
|
virtual TArray<uint8_t> CreatePalettedPixels(int conversion);
|
|
|
|
virtual int CopyPixels(FBitmap* bmp, int conversion); // This will always ignore 'luminance'.
|
2020-05-24 05:58:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// A tile coming from an ART file.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
class FArtTile : public FTileTexture
|
|
|
|
{
|
|
|
|
const TArray<uint8_t>& RawPixels;
|
|
|
|
const uint32_t Offset;
|
|
|
|
public:
|
2020-05-24 17:12:22 +00:00
|
|
|
FArtTile(const TArray<uint8_t>& backingstore, uint32_t offset, int width, int height)
|
2020-05-24 05:58:56 +00:00
|
|
|
: RawPixels(backingstore), Offset(offset)
|
|
|
|
{
|
2020-04-10 16:17:26 +00:00
|
|
|
Width = width;
|
|
|
|
Height = height;
|
2020-05-24 05:58:56 +00:00
|
|
|
}
|
2020-05-24 17:12:22 +00:00
|
|
|
uint8_t* GetRawData() override final
|
2020-05-24 05:58:56 +00:00
|
|
|
{
|
2020-05-24 17:12:22 +00:00
|
|
|
return &RawPixels[Offset];
|
2020-05-24 05:58:56 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// A tile with its own pixel buffer
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
class FLooseTile : public FTileTexture
|
|
|
|
{
|
|
|
|
TArray<uint8_t> RawPixels;
|
|
|
|
public:
|
2020-05-24 17:12:22 +00:00
|
|
|
FLooseTile(TArray<uint8_t>& store, int width, int height)
|
2020-05-24 05:58:56 +00:00
|
|
|
{
|
|
|
|
RawPixels = std::move(store);
|
2020-04-10 16:17:26 +00:00
|
|
|
Width = width;
|
|
|
|
Height = height;
|
2020-05-24 05:58:56 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 17:12:22 +00:00
|
|
|
uint8_t* GetRawData() override
|
2020-05-24 05:58:56 +00:00
|
|
|
{
|
|
|
|
return RawPixels.Data();
|
|
|
|
}
|
2020-05-24 17:12:22 +00:00
|
|
|
|
2020-05-24 05:58:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// A non-existent tile
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
class FDummyTile : public FTileTexture
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
FDummyTile(int width, int height)
|
|
|
|
{
|
2020-04-10 16:17:26 +00:00
|
|
|
Width = width;
|
|
|
|
Height = height;
|
2020-05-24 05:58:56 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 17:12:22 +00:00
|
|
|
uint8_t* GetRawData() override
|
2020-05-24 05:58:56 +00:00
|
|
|
{
|
2020-05-24 17:12:22 +00:00
|
|
|
return nullptr;
|
2020-05-24 05:58:56 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// A tile with a writable surface
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
class FWritableTile : public FTileTexture
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
TArray<uint8_t> buffer;
|
|
|
|
|
|
|
|
public:
|
2020-05-24 17:12:22 +00:00
|
|
|
FWritableTile()
|
2020-05-24 05:58:56 +00:00
|
|
|
{
|
2020-05-24 17:12:22 +00:00
|
|
|
//useType = Writable;
|
2020-05-24 05:58:56 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 17:12:22 +00:00
|
|
|
uint8_t* GetRawData() override
|
2020-05-24 05:58:56 +00:00
|
|
|
{
|
|
|
|
return buffer.Data();
|
|
|
|
}
|
|
|
|
|
2020-04-10 16:17:26 +00:00
|
|
|
bool ResizeImage(int w, int h)
|
2020-05-24 05:58:56 +00:00
|
|
|
{
|
2020-05-24 17:12:22 +00:00
|
|
|
if (w <= 0 || h <= 0)
|
2020-05-24 05:58:56 +00:00
|
|
|
{
|
|
|
|
buffer.Reset();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-10 16:17:26 +00:00
|
|
|
Width = w;
|
|
|
|
Height = h;
|
2020-05-24 05:58:56 +00:00
|
|
|
buffer.Resize(w * h);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// A tile with a writable surface
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
class FRestorableTile : public FWritableTile
|
|
|
|
{
|
2020-04-10 16:17:26 +00:00
|
|
|
FImageSource* Base;
|
2020-05-24 05:58:56 +00:00
|
|
|
|
|
|
|
public:
|
2020-04-10 16:17:26 +00:00
|
|
|
FRestorableTile(FImageSource* base)
|
2020-05-24 05:58:56 +00:00
|
|
|
{
|
|
|
|
Base = base;
|
2021-12-30 09:30:21 +00:00
|
|
|
|
2020-04-10 16:17:26 +00:00
|
|
|
CopySize(*base);
|
|
|
|
ResizeImage(Width, Height);
|
2020-05-24 05:58:56 +00:00
|
|
|
Reload();
|
|
|
|
}
|
|
|
|
|
2020-05-24 17:12:22 +00:00
|
|
|
void Reload()
|
2020-05-24 05:58:56 +00:00
|
|
|
{
|
2020-07-23 14:31:26 +00:00
|
|
|
buffer = Base->GetPalettedPixels(0);
|
2020-05-24 05:58:56 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// One ART file.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
struct BuildArtFile
|
|
|
|
{
|
|
|
|
FString filename;
|
|
|
|
TArray<uint8_t> RawData;
|
|
|
|
|
|
|
|
BuildArtFile() = default;
|
|
|
|
BuildArtFile(const BuildArtFile&) = delete;
|
|
|
|
BuildArtFile& operator=(const BuildArtFile&) = delete;
|
|
|
|
BuildArtFile(const BuildArtFile&& other)
|
|
|
|
{
|
|
|
|
filename = std::move(other.filename);
|
|
|
|
RawData = std::move(other.RawData);
|
|
|
|
}
|
|
|
|
|
|
|
|
BuildArtFile& operator=(const BuildArtFile&& other)
|
|
|
|
{
|
|
|
|
filename = std::move(other.filename);
|
|
|
|
RawData = std::move(other.RawData);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// THe tile container
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2021-04-06 13:55:33 +00:00
|
|
|
struct TileOffs
|
|
|
|
{
|
|
|
|
int xsize, ysize, xoffs, yoffs;
|
|
|
|
};
|
|
|
|
|
2020-05-24 06:47:45 +00:00
|
|
|
struct TileDesc
|
|
|
|
{
|
2020-05-25 21:59:07 +00:00
|
|
|
FGameTexture* texture; // the currently active tile
|
2020-05-24 06:47:45 +00:00
|
|
|
picanm_t picanm; // animation descriptor
|
|
|
|
ReplacementType replacement;
|
2020-05-29 21:33:26 +00:00
|
|
|
float alphaThreshold;
|
2022-11-25 12:45:18 +00:00
|
|
|
int tileflags;
|
2020-09-23 14:54:52 +00:00
|
|
|
|
|
|
|
// Sprite offset hackery for hires replacements. This only gets used for sprites in the 3D view, nothing else.
|
2021-04-06 13:55:33 +00:00
|
|
|
TileOffs hiofs;
|
2020-09-23 14:54:52 +00:00
|
|
|
|
2020-05-29 21:33:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct TexturePick
|
|
|
|
{
|
|
|
|
FGameTexture* texture; // which texture to use
|
|
|
|
int translation; // which translation table to use
|
|
|
|
int tintFlags; // which shader tinting options to use
|
|
|
|
PalEntry tintColor; // Tint color
|
|
|
|
PalEntry basepalTint; // can the base palette be done with a global tint effect?
|
2020-05-24 06:47:45 +00:00
|
|
|
};
|
|
|
|
|
2020-05-24 05:58:56 +00:00
|
|
|
struct BuildTiles
|
|
|
|
{
|
2020-05-25 21:59:07 +00:00
|
|
|
FGameTexture* Placeholder;
|
2020-05-24 05:58:56 +00:00
|
|
|
TDeletingArray<BuildArtFile*> ArtFiles;
|
2022-12-06 21:30:52 +00:00
|
|
|
TArray<TileDesc> tiledata;
|
2020-05-24 05:58:56 +00:00
|
|
|
TArray<FString> addedArt;
|
2022-01-23 20:30:00 +00:00
|
|
|
TMap<FName, int> nametoindex;
|
2022-12-06 07:52:41 +00:00
|
|
|
TMap<int, int> textotile;
|
2022-12-05 16:14:01 +00:00
|
|
|
bool locked; // if this is true, no more tile modifications are allowed.
|
2022-01-23 20:30:00 +00:00
|
|
|
|
|
|
|
void addName(const char* name, int index)
|
|
|
|
{
|
|
|
|
nametoindex.Insert(name, index);
|
|
|
|
}
|
|
|
|
|
2022-12-05 16:14:01 +00:00
|
|
|
void lock()
|
|
|
|
{
|
|
|
|
locked = true;
|
2022-12-06 07:52:41 +00:00
|
|
|
// Now we can set up the reverse map.
|
|
|
|
for (unsigned i = 0; i < MAXTILES; i++)
|
|
|
|
{
|
|
|
|
if (tiledata[i].texture != Placeholder)
|
|
|
|
{
|
|
|
|
textotile.Insert(tiledata[i].texture->GetID().GetIndex(), i);
|
|
|
|
}
|
|
|
|
}
|
2022-12-05 16:14:01 +00:00
|
|
|
}
|
|
|
|
|
2022-01-23 20:30:00 +00:00
|
|
|
int tileForName(const char* name)
|
|
|
|
{
|
|
|
|
FName nm(name, true);
|
|
|
|
if (nm == NAME_None) return -1;
|
|
|
|
auto nmm = nametoindex.CheckKey(nm);
|
|
|
|
if (!nmm) return -1;
|
|
|
|
return *nmm;
|
|
|
|
}
|
2020-05-24 05:58:56 +00:00
|
|
|
|
2022-11-25 15:52:08 +00:00
|
|
|
void SetAliases();
|
|
|
|
|
|
|
|
|
2020-05-24 21:26:47 +00:00
|
|
|
void Init(); // This cannot be a constructor because it needs the texture manager running.
|
2020-05-24 05:58:56 +00:00
|
|
|
~BuildTiles()
|
|
|
|
{
|
|
|
|
CloseAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CloseAll();
|
|
|
|
|
2022-12-05 16:14:01 +00:00
|
|
|
void AddTile(int tilenum, FGameTexture* tex);
|
2020-05-24 05:58:56 +00:00
|
|
|
|
2020-05-24 21:26:47 +00:00
|
|
|
void AddTiles(int firsttile, TArray<uint8_t>& store, const char* mapname);
|
2020-05-24 05:58:56 +00:00
|
|
|
|
2022-12-05 16:14:01 +00:00
|
|
|
void AddFile(BuildArtFile* bfd)
|
2020-05-24 05:58:56 +00:00
|
|
|
{
|
2020-05-25 14:53:35 +00:00
|
|
|
ArtFiles.Push(bfd);
|
2020-05-24 05:58:56 +00:00
|
|
|
}
|
|
|
|
int FindFile(const FString& filename)
|
|
|
|
{
|
|
|
|
return ArtFiles.FindEx([filename](const BuildArtFile* element) { return filename.CompareNoCase(element->filename) == 0; });
|
|
|
|
}
|
2020-05-24 21:26:47 +00:00
|
|
|
int LoadArtFile(const char* file, const char* mapname = nullptr, int firsttile = -1);
|
2020-05-24 05:58:56 +00:00
|
|
|
void LoadArtSet(const char* filename);
|
|
|
|
void AddArt(TArray<FString>& art)
|
|
|
|
{
|
|
|
|
addedArt = std::move(art);
|
|
|
|
}
|
2020-09-15 22:21:17 +00:00
|
|
|
|
|
|
|
void setAnim(int tile, int type, int speed, int frames)
|
|
|
|
{
|
|
|
|
auto& anm = tiledata[tile].picanm;
|
|
|
|
anm.sf &= ~(PICANM_ANIMTYPE_MASK | PICANM_ANIMSPEED_MASK);
|
|
|
|
anm.sf |= clamp(speed, 0, 15) | (type << PICANM_ANIMTYPE_SHIFT);
|
|
|
|
anm.num = frames;
|
|
|
|
}
|
|
|
|
|
2020-05-25 21:59:07 +00:00
|
|
|
FGameTexture* ValidateCustomTile(int tilenum, ReplacementType type);
|
2020-05-24 05:58:56 +00:00
|
|
|
uint8_t* tileMakeWritable(int num);
|
|
|
|
uint8_t* tileCreate(int tilenum, int width, int height);
|
|
|
|
void MakeCanvas(int tilenum, int width, int height);
|
|
|
|
};
|
|
|
|
|
|
|
|
int tileGetCRC32(int tileNum);
|
|
|
|
int tileImportFromTexture(const char* fn, int tilenum, int alphacut, int istexture);
|
|
|
|
void tileCopy(int tile, int tempsource, int temppal, int xoffset, int yoffset, int flags);
|
|
|
|
void tileSetDummy(int tile, int width, int height);
|
|
|
|
void tileDelete(int tile);
|
|
|
|
|
|
|
|
extern BuildTiles TileFiles;
|
|
|
|
|
|
|
|
// Some hacks to allow accessing the no longer existing arrays as if they still were arrays to avoid changing hundreds of lines of code.
|
|
|
|
struct PicAnm
|
|
|
|
{
|
|
|
|
picanm_t& operator[](size_t index)
|
|
|
|
{
|
|
|
|
assert(index < MAXTILES);
|
2020-05-24 11:53:27 +00:00
|
|
|
return TileFiles.tiledata[index].picanm;
|
2020-05-24 05:58:56 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
extern PicAnm picanm;
|
|
|
|
|
|
|
|
inline int tileWidth(int num)
|
|
|
|
{
|
2021-04-17 10:40:23 +00:00
|
|
|
assert((unsigned)num < MAXTILES);
|
|
|
|
if ((unsigned)num >= MAXTILES) return 1;
|
2020-09-26 09:59:24 +00:00
|
|
|
return (int)TileFiles.tiledata[num].texture->GetDisplayWidth();
|
2020-05-24 05:58:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline int tileHeight(int num)
|
|
|
|
{
|
2021-04-17 10:40:23 +00:00
|
|
|
assert((unsigned)num < MAXTILES);
|
|
|
|
if ((unsigned)num >= MAXTILES) return 1;
|
2020-09-26 09:59:24 +00:00
|
|
|
return (int)TileFiles.tiledata[num].texture->GetDisplayHeight();
|
2020-05-24 10:31:38 +00:00
|
|
|
}
|
|
|
|
|
2022-08-05 16:47:35 +00:00
|
|
|
int tileAnimateOfs(int tilenum, int randomize = -1);
|
|
|
|
|
|
|
|
inline void tileUpdatePicnum(int* const tileptr, bool mayrotate = false, int randomize = -1)
|
|
|
|
{
|
|
|
|
auto& tile = *tileptr;
|
|
|
|
|
2022-08-05 21:48:43 +00:00
|
|
|
if (picanm[tile].type())
|
2022-08-05 16:47:35 +00:00
|
|
|
tile += tileAnimateOfs(tile, randomize);
|
|
|
|
}
|
|
|
|
|
2020-06-28 19:38:25 +00:00
|
|
|
|
|
|
|
inline FGameTexture* tileGetTexture(int tile, bool animate = false)
|
2020-05-24 10:31:38 +00:00
|
|
|
{
|
2022-12-06 10:46:48 +00:00
|
|
|
assert((unsigned)tile < MAXTILES && tile != -1); // -1 is valid for overpicnum as 'no texture'.
|
2020-11-21 15:34:32 +00:00
|
|
|
if (tile < 0 || tile >= MAXTILES) return nullptr;
|
2022-08-05 16:47:35 +00:00
|
|
|
if (animate) tileUpdatePicnum(&tile);
|
2020-05-24 13:02:20 +00:00
|
|
|
return TileFiles.tiledata[tile].texture;
|
2020-05-29 21:33:26 +00:00
|
|
|
}
|
|
|
|
|
2022-12-06 07:52:41 +00:00
|
|
|
inline FTextureID tileGetTextureID(int tile)
|
|
|
|
{
|
|
|
|
if (tile < 0 || tile >= MAXTILES) return FNullTextureID();
|
|
|
|
return TileFiles.tiledata[tile].texture->GetID();
|
|
|
|
}
|
|
|
|
|
2022-12-06 10:46:48 +00:00
|
|
|
inline int legacyTileNum(FTextureID tex)
|
|
|
|
{
|
|
|
|
auto p = TileFiles.textotile.CheckKey(tex.GetIndex());
|
|
|
|
return p ? *p : -1;
|
|
|
|
}
|
|
|
|
|
2020-10-07 21:22:29 +00:00
|
|
|
void tileUpdateAnimations();
|
2020-09-29 19:47:32 +00:00
|
|
|
|
2022-12-06 17:30:57 +00:00
|
|
|
const uint8_t* GetRawPixels(FTextureID texid);
|
|
|
|
uint8_t* GetWritablePixels(FTextureID texid);
|
|
|
|
void InvalidateTexture(FTextureID num);
|
2020-09-15 22:21:17 +00:00
|
|
|
|
2021-06-01 09:05:26 +00:00
|
|
|
class FGameTexture;
|
2021-06-01 09:29:39 +00:00
|
|
|
bool PickTexture(FGameTexture* tex, int paletteid, TexturePick& pick, bool wantindexed = false);
|
2022-08-04 21:47:01 +00:00
|
|
|
FCanvasTexture* tileGetCanvas(int tilenum);
|