raze/source/core/textures/buildtiles.h
Christoph Oelckers 4c2ff2950a - for Duke/RR, when not using English force use of the regular text font for drawing the map label on the automap.
This essentially removes the tiny font which is simply too small to ever be able to receive a proper international extension from the equation for localization because the level name display was the only remaining relevant content using this font.
The only other screen where this font is still being used to draw text requiring translated content is the multiplayer summary which will have to be redesigned anyway if multiplayer becomes functional again.
2020-09-29 21:47:32 +02:00

556 lines
13 KiB
C++

#pragma once
#include <limits.h>
#include "textures.h"
#include "image.h"
#include "i_time.h"
#include "intvec.h"
// picanm[].sf:
// |bit(1<<7)
// |animtype|animtype|texhitscan|nofullbright|speed|speed|speed|speed|
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
};
enum ETexType
{
TT_INDEXED,
TT_TRUECOLOR,
};
enum class ReplacementType : int
{
Art,
Writable,
Restorable,
Canvas
};
// NOTE: If the layout of this struct is changed, loadpics() must be modified
// accordingly.
struct picanm_t
{
uint16_t num; // animate number
uint8_t sf; // anim. speed and flags
uint8_t extra;
void Clear()
{
extra = sf = 0;
num = 0;
}
};
picanm_t tileConvertAnimFormat(int32_t const picanmdisk, int* lo, int* to);
struct rottile_t
{
int16_t newtile;
int16_t owner;
};
struct HightileReplacement
{
FGameTexture* faces[6]; // only one gets used by a texture, the other 5 are for skyboxes only
FVector2 scale;
float alphacut, specpower, specfactor;
uint16_t palnum, flags;
};
class FTileTexture : public FImageSource
{
public:
FTileTexture()
{
bUseGamePalette = true;
bTranslucent = false;
}
virtual uint8_t* GetRawData() = 0;
virtual TArray<uint8_t> CreatePalettedPixels(int conversion);
virtual int CopyPixels(FBitmap* bmp, int conversion); // This will always ignore 'luminance'.
};
//==========================================================================
//
// A tile coming from an ART file.
//
//==========================================================================
class FArtTile : public FTileTexture
{
const TArray<uint8_t>& RawPixels;
const uint32_t Offset;
public:
FArtTile(const TArray<uint8_t>& backingstore, uint32_t offset, int width, int height)
: RawPixels(backingstore), Offset(offset)
{
Width = width;
Height = height;
}
uint8_t* GetRawData() override final
{
return &RawPixels[Offset];
}
};
//==========================================================================
//
// A tile with its own pixel buffer
//
//==========================================================================
class FLooseTile : public FTileTexture
{
TArray<uint8_t> RawPixels;
public:
FLooseTile(TArray<uint8_t>& store, int width, int height)
{
RawPixels = std::move(store);
Width = width;
Height = height;
}
uint8_t* GetRawData() override
{
return RawPixels.Data();
}
};
//==========================================================================
//
// A non-existent tile
//
//==========================================================================
class FDummyTile : public FTileTexture
{
public:
FDummyTile(int width, int height)
{
Width = width;
Height = height;
}
uint8_t* GetRawData() override
{
return nullptr;
}
};
//==========================================================================
//
// A tile with a writable surface
//
//==========================================================================
class FWritableTile : public FTileTexture
{
protected:
TArray<uint8_t> buffer;
public:
FWritableTile()
{
//useType = Writable;
}
uint8_t* GetRawData() override
{
return buffer.Data();
}
bool ResizeImage(int w, int h)
{
if (w <= 0 || h <= 0)
{
buffer.Reset();
return false;
}
else
{
Width = w;
Height = h;
buffer.Resize(w * h);
return true;
}
}
};
//==========================================================================
//
// A tile with a writable surface
//
//==========================================================================
class FRestorableTile : public FWritableTile
{
FImageSource* Base;
public:
FRestorableTile(FImageSource* base)
{
Base = base;
CopySize(*base);
ResizeImage(Width, Height);
Reload();
}
void Reload()
{
buffer = Base->GetPalettedPixels(0);
}
};
//==========================================================================
//
// 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
//
//==========================================================================
struct RawCacheNode
{
TArray<uint8_t> data;
uint64_t lastUseTime;
};
struct TileDesc
{
FGameTexture* texture; // the currently active tile
FGameTexture* backup; // original backup for map tiles
RawCacheNode rawCache; // this is needed for hitscan testing to avoid reloading the texture each time.
picanm_t picanm; // animation descriptor
picanm_t picanmbackup; // animation descriptor backup when using map tiles
rottile_t RotTile;// = { -1,-1 };
TArray<HightileReplacement> Hightiles;
ReplacementType replacement;
float alphaThreshold;
// Sprite offset hackery for hires replacements. This only gets used for sprites in the 3D view, nothing else.
uint16_t h_xsize, h_ysize;
int8_t h_xoffs, h_yoffs;
};
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?
};
struct BuildTiles
{
FGameTexture* Placeholder;
TDeletingArray<BuildArtFile*> ArtFiles;
TileDesc tiledata[MAXTILES];
TArray<FString> addedArt;
TMap<FGameTexture*, int> TextureToTile;
TArray<FString> maptilesadded;
void Init(); // This cannot be a constructor because it needs the texture manager running.
~BuildTiles()
{
CloseAll();
}
void SetBackup()
{
for (auto& td : tiledata)
{
td.backup = td.texture;
td.picanmbackup = td.picanm;
}
}
void CloseAll();
void AddTile(int tilenum, FGameTexture* tex, bool permap = false);
void AddTiles(int firsttile, TArray<uint8_t>& store, const char* mapname);
void AddFile(BuildArtFile* bfd, bool permap)
{
ArtFiles.Push(bfd);
}
int FindFile(const FString& filename)
{
return ArtFiles.FindEx([filename](const BuildArtFile* element) { return filename.CompareNoCase(element->filename) == 0; });
}
int LoadArtFile(const char* file, const char* mapname = nullptr, int firsttile = -1);
void LoadArtSet(const char* filename);
void AddArt(TArray<FString>& art)
{
addedArt = std::move(art);
}
int GetTileIndex(FGameTexture* tex)
{
auto p = TextureToTile.CheckKey(tex);
return p ? *p : -1;
}
void SetupReverseTileMap()
{
TextureToTile.Clear();
for (int i = 0; i < MAXTILES; i++)
{
if (tiledata[i].texture != nullptr && tiledata[i].texture != Placeholder) TextureToTile.Insert(tiledata[i].texture, i);
}
}
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;
}
FGameTexture* ValidateCustomTile(int tilenum, ReplacementType type);
int32_t artLoadFiles(const char* filename);
uint8_t* tileMakeWritable(int num);
uint8_t* tileCreate(int tilenum, int width, int height);
int findUnusedTile(void);
int tileCreateRotated(int owner);
void InvalidateTile(int num);
void MakeCanvas(int tilenum, int width, int height);
HightileReplacement* FindReplacement(int picnum, int palnum, bool skybox = false);
void AddReplacement(int picnum, const HightileReplacement&);
void DeleteReplacement(int picnum, int palnum);
void DeleteReplacements(int picnum)
{
assert(picnum < MAXTILES);
tiledata[picnum].Hightiles.Clear();
}
void PostLoadSetup();
};
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);
void tileRemoveReplacement(int tile);
bool tileLoad(int tileNum);
void artClearMapArt(void);
void artSetupMapArt(const char* filename);
int tileSetHightileReplacement(int picnum, int palnum, const char *filen, float alphacut, float xscale, float yscale, float specpower, float specfactor, uint8_t flags);
int tileSetSkybox(int picnum, int palnum, const char **facenames, int flags );
int tileDeleteReplacement(int picnum, int palnum);
void tileCopySection(int tilenum1, int sx1, int sy1, int xsiz, int ysiz, int tilenum2, int sx2, int sy2);
extern BuildTiles TileFiles;
inline bool tileCheck(int num)
{
auto tex = TileFiles.tiledata[num].texture;
return tex && tex->GetTexelWidth() > 0 && tex->GetTexelHeight() > 0;
}
inline const uint8_t* tilePtr(int num)
{
if (TileFiles.tiledata[num].rawCache.data.Size() == 0)
{
auto tex = TileFiles.tiledata[num].texture;
if (!tex || tex->GetTexelWidth() <= 0 || tex->GetTexelHeight() <= 0) return nullptr;
TileFiles.tiledata[num].rawCache.data = tex->GetTexture()->Get8BitPixels(false);
}
TileFiles.tiledata[num].rawCache.lastUseTime = I_nsTime();
return TileFiles.tiledata[num].rawCache.data.Data();
}
inline bool tileLoad(int tileNum)
{
return !!tilePtr(tileNum);
}
inline uint8_t* tileData(int num)
{
auto tex = dynamic_cast<FImageTexture*>(TileFiles.tiledata[num].texture->GetTexture());
if (!tex) return nullptr;
auto p = dynamic_cast<FWritableTile*>(tex->GetImage());
return p ? p->GetRawData() : nullptr;
}
inline const uint8_t* tileRawData(int num)
{
auto tex = dynamic_cast<FImageTexture*>(TileFiles.tiledata[num].texture->GetTexture());
if (!tex) return nullptr;
auto p = dynamic_cast<FTileTexture*>(tex->GetImage());
return p ? p->GetRawData() : nullptr;
}
// 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 TileSiz
{
const vec2_16_t operator[](size_t index)
{
assert(index < MAXTILES);
vec2_16_t v = { (int16_t)TileFiles.tiledata[index].texture->GetDisplayWidth(), (int16_t)TileFiles.tiledata[index].texture->GetDisplayHeight() };
return v;
}
};
extern TileSiz tilesiz;
struct PicAnm
{
picanm_t& operator[](size_t index)
{
assert(index < MAXTILES);
return TileFiles.tiledata[index].picanm;
}
};
extern PicAnm picanm;
// Helpers to read the refactored tilesiz array.
inline int tileWidth(int num)
{
assert(num < MAXTILES);
return (int)TileFiles.tiledata[num].texture->GetDisplayWidth();
}
inline int tileHeight(int num)
{
assert(num < MAXTILES);
return (int)TileFiles.tiledata[num].texture->GetDisplayHeight();
}
inline int tileLeftOffset(int num)
{
assert(num < MAXTILES);
return (int)TileFiles.tiledata[num].texture->GetDisplayLeftOffset();
}
inline int tileTopOffset(int num)
{
assert(num < MAXTILES);
return (int)TileFiles.tiledata[num].texture->GetDisplayTopOffset();
}
inline int widthBits(int num)
{
int w = tileWidth(num);
int j = 15;
while ((j > 1) && ((1 << j) > w))
j--;
return j;
}
inline int heightBits(int num)
{
int w = tileHeight(num);
int j = 15;
while ((j > 1) && ((1 << j) > w))
j--;
return j;
}
inline rottile_t& RotTile(int tile)
{
assert(tile < MAXTILES);
return TileFiles.tiledata[tile].RotTile;
}
int32_t animateoffs(int const tilenum, int fakevar);
inline FGameTexture* tileGetTexture(int tile, bool animate = false)
{
assert(tile < MAXTILES);
if (animate)
{
if (picanm[tile].sf & PICANM_ANIMTYPE_MASK)
tile += animateoffs(tile, 0);
}
return TileFiles.tiledata[tile].texture;
}
bool tileEqualTo(int me, int other);
bool PickTexture(int picnum, FGameTexture* tex, int paletteid, TexturePick& pick);
bool ValidateTileRange(const char* cmd, int& begin, int& end, FScriptPosition pos, bool allowswap = true);
bool ValidateTilenum(const char* cmd, int tile, FScriptPosition pos);
struct TileImport
{
FString fn;
int tile = -1;
int alphacut = 128, flags = 0;
int haveextra = 0;
int xoffset = INT_MAX, yoffset = INT_MAX;
int istexture = 0, extra = INT_MAX;
int64_t crc32 = INT64_MAX;
int sizex = INT_MAX, sizey;
// Blood extensions
int surface = INT_MAX, vox = INT_MAX, shade = INT_MAX;
};
void processTileImport(const char* cmd, FScriptPosition& pos, TileImport& imp);
struct SetAnim
{
int tile1, tile2, speed, type;
};
void processSetAnim(const char* cmd, FScriptPosition& pos, SetAnim& imp);