From 20edd800f95dc4c919f7d19c0f8ff8123aee0882 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 7 Dec 2022 17:10:27 +0100 Subject: [PATCH] - big texture system refactor. Finally that quickly slapped together BuildTiles class is gone and replaced with something that better integrates with the underlying texture manager --- source/CMakeLists.txt | 6 +- source/build/include/build.h | 1 - source/build/src/clip.cpp | 1 + source/common/textures/texturemanager.cpp | 12 +- source/common/textures/texturemanager.h | 7 +- source/core/actorinfo.cpp | 2 + source/core/actorlist.cpp | 1 + source/core/defparser.cpp | 820 ++++---------------- source/core/g_mapinfo.cpp | 11 +- source/core/gamecontrol.cpp | 102 +-- source/core/gamecontrol.h | 3 - source/core/gamefuncs.cpp | 6 +- source/core/gamefuncs.h | 2 - source/core/gamehud.cpp | 8 +- source/core/gamestruct.h | 5 +- source/core/mainloop.cpp | 1 + source/core/maploader.cpp | 1 + source/core/models/modeldata.cpp | 1 + source/core/precache.cpp | 3 + source/core/psky.cpp | 3 +- source/core/rendering/hw_voxels.cpp | 1 + source/core/rendering/scene/hw_drawinfo.cpp | 17 +- source/core/rendering/scene/hw_flats.cpp | 1 + source/core/rendering/scene/hw_sky.cpp | 4 +- source/core/rendering/scene/hw_sprites.cpp | 4 +- source/core/rendering/scene/hw_walls.cpp | 4 +- source/core/savegamehelp.cpp | 4 + source/core/sectorgeometry.cpp | 1 + source/core/textures/buildtiles.cpp | 820 -------------------- source/core/textures/buildtiles.h | 444 ++--------- source/core/textures/hightile.cpp | 27 +- source/core/textures/skytexture.cpp | 1 + source/core/textures/texinfo.cpp | 100 +++ source/core/textures/texinfo.h | 122 +++ source/core/textures/tilesetbuilder.cpp | 315 ++++++++ source/core/textures/tilesetbuilder.h | 44 ++ source/core/textures/tiletexture.cpp | 525 +++++++++++++ source/core/textures/tiletexture.h | 18 + source/core/vmexports.cpp | 5 +- source/games/blood/src/blood.cpp | 11 +- source/games/blood/src/blood.h | 4 +- source/games/blood/src/hudsprites.cpp | 5 +- source/games/blood/src/misc.h | 3 + source/games/blood/src/tile.cpp | 34 +- source/games/duke/src/actors_lava.cpp | 2 + source/games/duke/src/animatesprites.cpp | 2 + source/games/duke/src/constants.h | 5 +- source/games/duke/src/duke3d.h | 3 +- source/games/duke/src/game.cpp | 39 +- source/games/duke/src/global.h | 1 + source/games/duke/src/hudweapon_d.cpp | 1 + source/games/duke/src/inlines.h | 7 +- source/games/duke/src/namelist_d.h | 2 +- source/games/duke/src/namelist_r.h | 2 +- source/games/duke/src/render.cpp | 6 +- source/games/exhumed/src/2d.cpp | 11 +- source/games/exhumed/src/exhumed.cpp | 32 +- source/games/exhumed/src/exhumed.h | 3 +- source/games/sw/src/cache.cpp | 1 + source/games/sw/src/draw.cpp | 2 + source/games/sw/src/game.cpp | 46 +- source/games/sw/src/game.h | 4 +- source/games/sw/src/jsector.cpp | 5 +- 63 files changed, 1583 insertions(+), 2101 deletions(-) delete mode 100644 source/core/textures/buildtiles.cpp create mode 100644 source/core/textures/texinfo.cpp create mode 100644 source/core/textures/texinfo.h create mode 100644 source/core/textures/tilesetbuilder.cpp create mode 100644 source/core/textures/tilesetbuilder.h create mode 100644 source/core/textures/tiletexture.cpp create mode 100644 source/core/textures/tiletexture.h diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index f2992e1c9..1e9623ac5 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1220,9 +1220,9 @@ set (PCH_SOURCES common/scripting/backend/vmbuilder.cpp common/scripting/backend/codegen.cpp - - - core/textures/buildtiles.cpp + core/textures/tiletexture.cpp + core/textures/texinfo.cpp + core/textures/tilesetbuilder.cpp core/textures/skytexture.cpp core/textures/hightile.cpp core/music/s_advsound.cpp diff --git a/source/build/include/build.h b/source/build/include/build.h index 4577e8e67..b47056601 100644 --- a/source/build/include/build.h +++ b/source/build/include/build.h @@ -17,7 +17,6 @@ static_assert('\xff' == 255, "Char must be unsigned!"); #include "printf.h" #include "palette.h" -#include "buildtiles.h" #include "c_cvars.h" #include "cmdlib.h" diff --git a/source/build/src/clip.cpp b/source/build/src/clip.cpp index f89e67081..8b6d04ff0 100644 --- a/source/build/src/clip.cpp +++ b/source/build/src/clip.cpp @@ -12,6 +12,7 @@ #include "gamefuncs.h" #include "coreactor.h" #include "texturemanager.h" +#include "buildtiles.h" enum { MAXCLIPDIST = 1024 }; diff --git a/source/common/textures/texturemanager.cpp b/source/common/textures/texturemanager.cpp index 5bb2b299d..94aa40de1 100644 --- a/source/common/textures/texturemanager.cpp +++ b/source/common/textures/texturemanager.cpp @@ -186,6 +186,7 @@ FTextureID FTextureManager::CheckForTexture (const char *name, ETextureType uset // The name matches, so check the texture type if (usetype == ETextureType::Any) { + if (flags & TEXMAN_ReturnAll) return FTextureID(i); // user asked to skip all checks, including null textures. // All NULL textures should actually return 0 if (texUseType == ETextureType::FirstDefined && !(flags & TEXMAN_ReturnFirst)) return 0; if (texUseType == ETextureType::SkinGraphic && !(flags & TEXMAN_AllowSkins)) return 0; @@ -229,6 +230,7 @@ FTextureID FTextureManager::CheckForTexture (const char *name, ETextureType uset // Never return the index of NULL textures. if (firstfound != -1) { + if (flags & TEXMAN_ReturnAll) return FTextureID(i); // user asked to skip all checks, including null textures. if (firsttype == ETextureType::Null) return FTextureID(0); if (firsttype == ETextureType::FirstDefined && !(flags & TEXMAN_ReturnFirst)) return FTextureID(0); return FTextureID(firstfound); @@ -1601,14 +1603,10 @@ void FTextureManager::SetTranslation(FTextureID fromtexnum, FTextureID totexnum) // //----------------------------------------------------------------------------- -void FTextureManager::AddAlias(const char* name, FGameTexture* tex) +void FTextureManager::AddAlias(const char* name, int texindex) { - FTextureID id = tex->GetID(); - if (tex != Textures[id.GetIndex()].Texture)// || !tex->isValid()) - { - return; // Whatever got passed in here was not valid, so ignore the alias. - } - aliases.Insert(name, id.GetIndex()); + if (texindex < 0 || texindex >= NumTextures()) return; // Whatever got passed in here was not valid, so ignore the alias. + aliases.Insert(name, texindex); } void FTextureManager::Listaliases() diff --git a/source/common/textures/texturemanager.h b/source/common/textures/texturemanager.h index 91ff461a2..575e838ea 100644 --- a/source/common/textures/texturemanager.h +++ b/source/common/textures/texturemanager.h @@ -100,6 +100,7 @@ public: TEXMAN_Localize = 64, TEXMAN_ForceLookup = 128, TEXMAN_NoAlias = 256, + TEXMAN_ReturnAll = 512, }; enum @@ -155,7 +156,11 @@ public: tmanips.Remove(cname); } - void AddAlias(const char* name, FGameTexture* tex); + void AddAlias(const char* name, int texindex); + void AddAlias(const char* name, FTextureID texindex) + { + AddAlias(name, texindex.GetIndex()); + } private: diff --git a/source/core/actorinfo.cpp b/source/core/actorinfo.cpp index 3f46a8ffb..2bc53f78b 100644 --- a/source/core/actorinfo.cpp +++ b/source/core/actorinfo.cpp @@ -45,6 +45,8 @@ #include "types.h" #include "filesystem.h" #include "texturemanager.h" +#include "texinfo.h" +#include "buildtiles.h" extern void LoadActors (); diff --git a/source/core/actorlist.cpp b/source/core/actorlist.cpp index 08c75fe60..fa36da6ef 100644 --- a/source/core/actorlist.cpp +++ b/source/core/actorlist.cpp @@ -38,6 +38,7 @@ #include "raze_sound.h" #include "vm.h" #include "texturemanager.h" +#include "buildtiles.h" // Doubly linked ring list of Actors diff --git a/source/core/defparser.cpp b/source/core/defparser.cpp index ff9588f7b..e4b9c8536 100644 --- a/source/core/defparser.cpp +++ b/source/core/defparser.cpp @@ -45,14 +45,20 @@ #include "hw_voxels.h" #include "psky.h" #include "gamefuncs.h" +#include "tilesetbuilder.h" #include "models/modeldata.h" +#include "texturemanager.h" + + +int tileSetHightileReplacement(int picnum, int palnum, FTextureID texid, float alphacut, float xscale, float yscale, float specpower, float specfactor, bool indexed); -int tileSetHightileReplacement(int picnum, int palnum, const char* filename, float alphacut, float xscale, float yscale, float specpower, float specfactor, bool indexed = false); int tileSetSkybox(int picnum, int palnum, FString* facenames, bool indexed = false); void tileRemoveReplacement(int num); void AddUserMapHack(const FString& title, const FString& mhkfile, uint8_t* md4); +static TilesetBuildInfo* tbuild; static void defsparser(FScanner& sc); +const char* G_DefaultDefFile(void); static void performInclude(FScanner* sc, const char* fn, FScriptPosition* pos) { @@ -117,6 +123,62 @@ bool ValidateTilenum(const char* cmd, int tile, FScriptPosition pos) return true; } +//=========================================================================== +// +// +// +//=========================================================================== + +static int tileSetHightileReplacement(FScanner& sc, int picnum, int palnum, const char* filename, float alphacut, float xscale, float yscale, float specpower, float specfactor, bool indexed = false) +{ + if ((uint32_t)picnum >= (uint32_t)MAXTILES) return -1; + if ((uint32_t)palnum >= (uint32_t)MAXPALOOKUPS) return -1; + + auto tex = tbuild->tile[picnum].tileimage; + + if (tex == nullptr || tex->GetWidth() <= 0 || tex->GetHeight() <= 0) + { + sc.ScriptMessage("Warning: defined hightile replacement for empty tile %d.", picnum); + return -1; // cannot add replacements to empty tiles, must create one beforehand + } + + FTextureID texid = TexMan.CheckForTexture(filename, ETextureType::Any, FTextureManager::TEXMAN_ForceLookup); + if (!texid.isValid()) + { + sc.ScriptMessage("%s: Replacement for tile %d does not exist or is invalid\n", filename, picnum); + return -1; + } + tileSetHightileReplacement(picnum, palnum, texid, alphacut, xscale, yscale, specpower, specfactor, indexed); + return 0; +} + +//========================================================================== +// +// Import a tile from an external image. +// This has been signifcantly altered so it may not cover everything yet. +// +//========================================================================== + +int tileImportFromTexture(FScanner& sc, const char* fn, int tilenum, int alphacut, int istexture) +{ + FTextureID texid = TexMan.CheckForTexture(fn, ETextureType::Any, FTextureManager::TEXMAN_ForceLookup); + if (!texid.isValid()) return -1; + auto tex = TexMan.GetGameTexture(texid); + + int32_t xsiz = tex->GetTexelWidth(), ysiz = tex->GetTexelHeight(); + + if (xsiz <= 0 || ysiz <= 0) + return -2; + + tbuild->tile[tilenum].imported = TexMan.GetGameTexture(texid); + tbuild->tile[tilenum].tileimage = tbuild->tile[tilenum].imported->GetTexture()->GetImage(); + + if (istexture) + tileSetHightileReplacement(sc, tilenum, 0, fn, (float)(255 - alphacut) * (1.f / 255.f), 1.0f, 1.0f, 1.0, 1.0); + return 0; + +} + //=========================================================================== // // Internal worker for tileImportTexture @@ -139,44 +201,47 @@ struct TileImport }; -void processTileImport(const char* cmd, FScriptPosition& pos, TileImport& imp) +void processTileImport(FScanner& sc, 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)) + auto tex = tbuild->tile[imp.tile].tileimage; + if (imp.crc32 != INT64_MAX && int(imp.crc32) != tileGetCRC32(tex)) return; - auto tex = tileGetTexture(imp.tile); - if (imp.sizex != INT_MAX && tex->GetTexelWidth() != imp.sizex && tex->GetTexelHeight() != imp.sizey) - return; + if (imp.sizex != INT_MAX) + { + // the size must match the previous tile + if (!tex) + { + if (imp.sizex != 0 || imp.sizey != 0) return; + } + else + { + if (tex->GetWidth() != imp.sizex || tex->GetHeight() != imp.sizey) return; + } + } imp.alphacut = clamp(imp.alphacut, 0, 255); gi->SetTileProps(imp.tile, imp.surface, imp.vox, imp.shade); - if (imp.fn.IsNotEmpty() && tileImportFromTexture(imp.fn, imp.tile, imp.alphacut, imp.istexture) < 0) return; + if (imp.fn.IsNotEmpty() && tileImportFromTexture(sc, imp.fn, imp.tile, imp.alphacut, imp.istexture) < 0) return; - TileFiles.tiledata[imp.tile].picanm.sf |= imp.flags; + tbuild->tile[imp.tile].extinfo.picanm.sf |= imp.flags; // 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. - tex = tileGetTexture(imp.tile); // this is not the same texture as above anymore! + auto itex = tbuild->tile[imp.tile].imported; - if (imp.xoffset == INT_MAX) imp.xoffset = tex->GetTexelLeftOffset(); + if (imp.xoffset == INT_MAX) imp.xoffset = itex->GetTexelLeftOffset(); else imp.xoffset = clamp(imp.xoffset, -128, 127); - if (imp.yoffset == INT_MAX) imp.yoffset = tex->GetTexelTopOffset(); + if (imp.yoffset == INT_MAX) imp.yoffset = itex->GetTexelTopOffset(); else imp.yoffset = clamp(imp.yoffset, -128, 127); - - if (tex) - { - tex->SetOffsets(imp.xoffset, imp.yoffset); - if (imp.flags & PICANM_NOFULLBRIGHT_BIT) - { - tex->SetDisableBrightmap(); - } - } - if (imp.extra != INT_MAX) TileFiles.tiledata[imp.tile].picanm.extra = imp.extra; + tbuild->tile[imp.tile].leftOffset = imp.xoffset; + tbuild->tile[imp.tile].topOffset = imp.yoffset; + if (imp.extra != INT_MAX) tbuild->tile[imp.tile].extinfo.picanm.extra = imp.extra; } //=========================================================================== @@ -190,6 +255,14 @@ struct SetAnim int tile1, tile2, speed, type; }; +void setAnim(int tile, int type, int speed, int frames) +{ + auto& anm = tbuild->tile[tile].extinfo.picanm; + anm.sf &= ~(PICANM_ANIMTYPE_MASK | PICANM_ANIMSPEED_MASK); + anm.sf |= clamp(speed, 0, 15) | (type << PICANM_ANIMTYPE_SHIFT); + anm.num = frames; +} + void processSetAnim(const char* cmd, FScriptPosition& pos, SetAnim& imp) { if (!ValidateTilenum(cmd, imp.tile1, pos) || @@ -206,7 +279,35 @@ void processSetAnim(const char* cmd, FScriptPosition& pos, SetAnim& imp) if (imp.type == (PICANM_ANIMTYPE_BACK >> PICANM_ANIMTYPE_SHIFT) && imp.tile1 > imp.tile2) count = -count; - TileFiles.setAnim(imp.tile1, imp.type, imp.speed, count); + setAnim(imp.tile1, imp.type, imp.speed, count); +} + +//========================================================================== +// +// Copies a tile into another and optionally translates its palette. +// +//========================================================================== + +static void tileCopy(int tile, int source, int pal, int xoffset, int yoffset, int flags) +{ + picanm_t* picanm = nullptr; + picanm_t* sourceanm = nullptr; + + if (source == -1) source = tile; + auto& tiledesc = tbuild->tile[tile]; + auto& srcdesc = tbuild->tile[source]; + tiledesc.extinfo.picanm = {}; + tiledesc.extinfo.picanm.sf = (srcdesc.extinfo.picanm.sf & PICANM_MISC_MASK) | flags; + tiledesc.tileimage = srcdesc.tileimage; + tiledesc.imported = srcdesc.imported; + tiledesc.leftOffset = srcdesc.leftOffset; + tiledesc.topOffset = srcdesc.topOffset; + /* todo: create translating image source class. + if (pal != -1) + { + tiledesc.tileimage = createTranslatedImage(tiledesc.tileimage, pal); + } + */ } //=========================================================================== @@ -247,7 +348,7 @@ void parseDefineTexture(FScanner& sc, FScriptPosition& pos) if (!sc.GetNumber(true)) return; //formerly y-size, unused if (!sc.GetString()) return; - tileSetHightileReplacement(tile, palette, sc.String, -1.0, 1.0, 1.0, 1.0, 1.0); + tileSetHightileReplacement(sc, tile, palette, sc.String, -1.0, 1.0, 1.0, 1.0, 1.0); } //=========================================================================== @@ -292,12 +393,12 @@ static void parseTexturePaletteBlock(FScanner& sc, int tile) { if (xsiz > 0 && ysiz > 0) { - tileSetDummy(tile, xsiz, ysiz); + tbuild->tile[tile].tileimage = createDummyTile(xsiz, ysiz); } xscale = 1.0f / xscale; yscale = 1.0f / yscale; - tileSetHightileReplacement(tile, pal, fn, alphacut, xscale, yscale, specpower, specfactor, indexed); + tileSetHightileReplacement(sc, tile, pal, fn, alphacut, xscale, yscale, specpower, specfactor, indexed); } } } @@ -333,7 +434,7 @@ static void parseTextureSpecialBlock(FScanner& sc, int tile, int pal) yscale = 1.0f / yscale; } - tileSetHightileReplacement(tile, pal, fn, -1.f, xscale, yscale, specpower, specfactor); + tileSetHightileReplacement(sc, tile, pal, fn, -1.f, xscale, yscale, specpower, specfactor); } } } @@ -431,7 +532,7 @@ void parseTileFromTexture(FScanner& sc, FScriptPosition& pos) } } } - processTileImport("tilefromtexture", pos, imp); + processTileImport(sc, "tilefromtexture", pos, imp); } //=========================================================================== @@ -509,8 +610,8 @@ void parseImportTile(FScanner& sc, FScriptPosition& pos) if (!sc.GetString()) return; if (!ValidateTilenum("importtile", tile, pos)) return; - int texstatus = tileImportFromTexture(sc.String, tile, 255, 0); - if (texstatus >= 0) TileFiles.tiledata[tile].picanm = {}; + int texstatus = tileImportFromTexture(sc, sc.String, tile, 255, 0); + if (texstatus >= 0) tbuild->tile[tile].extinfo.picanm = {}; } //=========================================================================== @@ -527,7 +628,9 @@ void parseDummyTile(FScanner& sc, FScriptPosition& pos) if (!sc.GetNumber(xsiz, true)) return; if (!sc.GetNumber(ysiz, true)) return; if (!ValidateTilenum("dummytile", tile, pos)) return; - tileSetDummy(tile, xsiz, ysiz); + auto& tiled = tbuild->tile[tile]; + tiled.tileimage = createDummyTile(xsiz, ysiz); + tiled.imported = nullptr; } //=========================================================================== @@ -547,7 +650,12 @@ void parseDummyTileRange(FScanner& sc, FScriptPosition& pos) if (!ValidateTileRange("dummytilerange", tile1, tile2, pos)) return; if (xsiz < 0 || ysiz < 0) return; - for (int i = tile1; i <= tile2; i++) tileSetDummy(i, xsiz, ysiz); + for (int i = tile1; i <= tile2; i++) + { + auto& tiled = tbuild->tile[i]; + tiled.tileimage = createDummyTile(xsiz, ysiz); + tiled.imported = nullptr; + } } //=========================================================================== @@ -561,8 +669,12 @@ void parseUndefineTile(FScanner& sc, FScriptPosition& pos) int tile; if (!sc.GetNumber(tile, true)) return; - if (ValidateTilenum("undefinetile", tile, pos)) - tileDelete(tile); + if (ValidateTilenum("undefinetile", tile, pos)) + { + auto& tiled = tbuild->tile[tile]; + tiled.tileimage = nullptr; + tiled.imported = nullptr; + } } //=========================================================================== @@ -579,7 +691,12 @@ void parseUndefineTileRange(FScanner& sc, FScriptPosition& pos) if (!sc.GetNumber(tile2, true)) return; if (!ValidateTileRange("undefinetilerange", tile1, tile2, pos)) return; - for (int i = tile1; i <= tile2; i++) tileDelete(i); + for (int i = tile1; i <= tile2; i++) + { + auto& tiled = tbuild->tile[i]; + tiled.tileimage = nullptr; + tiled.imported = nullptr; + } } //=========================================================================== @@ -647,11 +764,11 @@ void parseSetupTile(FScanner& sc, FScriptPosition& pos) int tile; if (!sc.GetNumber(tile, true)) return; if (!ValidateTilenum("setuptile", tile, pos)) return; - auto& tiled = TileFiles.tiledata[tile]; - if (!sc.GetNumber(tiled.hiofs.xsize, true)) return; - if (!sc.GetNumber(tiled.hiofs.ysize, true)) return; - if (!sc.GetNumber(tiled.hiofs.xoffs, true)) return; - if (!sc.GetNumber(tiled.hiofs.yoffs, true)) return; + auto& tiled = tbuild->tile[tile]; + if (!sc.GetNumber(tiled.extinfo.hiofs.xsize, true)) return; + if (!sc.GetNumber(tiled.extinfo.hiofs.ysize, true)) return; + if (!sc.GetNumber(tiled.extinfo.hiofs.xoffs, true)) return; + if (!sc.GetNumber(tiled.extinfo.hiofs.yoffs, true)) return; } //=========================================================================== @@ -673,7 +790,8 @@ void parseSetupTileRange(FScanner& sc, FScriptPosition& pos) if (!sc.GetNumber(hiofs.xoffs, true)) return; if (!sc.GetNumber(hiofs.yoffs, true)) return; - for (int i = tilestart; i <= tileend; i++) TileFiles.tiledata[i].hiofs = hiofs; + for (int i = tilestart; i <= tileend; i++) + tbuild->tile[i].extinfo.hiofs = hiofs; } //=========================================================================== @@ -704,7 +822,7 @@ void parseAlphahack(FScanner& sc, FScriptPosition& pos) if (!sc.GetNumber(tile, true)) return; if (!sc.GetFloat(true)) return; - if ((unsigned)tile < MAXTILES) TileFiles.tiledata[tile].texture->alphaThreshold = (float)sc.Float; + if ((unsigned)tile < MAXTILES) tbuild->tile[tile].alphathreshold = (float)sc.Float; } //=========================================================================== @@ -723,7 +841,7 @@ void parseAlphahackRange(FScanner& sc, FScriptPosition& pos) if (!ValidateTileRange("alphahackrange", tilestart, tileend, pos)) return; for (int i = tilestart; i <= tileend; i++) - TileFiles.tiledata[i].texture->alphaThreshold = (float)sc.Number; + tbuild->tile[i].alphathreshold = (float)sc.Float; } //=========================================================================== @@ -1123,7 +1241,7 @@ void parseTexHitscanRange(FScanner& sc, FScriptPosition& pos) if (start < 0) start = 0; if (end >= MAXUSERTILES) end = MAXUSERTILES - 1; for (int i = start; i <= end; i++) - TileFiles.tiledata[i].picanm.sf |= PICANM_TEXHITSCAN_BIT; + tbuild->tile[i].extinfo.picanm.sf |= PICANM_TEXHITSCAN_BIT; } //=========================================================================== @@ -1143,581 +1261,7 @@ void parseNoFullbrightRange(FScanner& sc, FScriptPosition& pos) if (end >= MAXUSERTILES) end = MAXUSERTILES - 1; for (int i = start; i <= end; i++) { - auto tex = tileGetTexture(i); - if (tex->isValid()) tex->SetDisableBrightmap(); - } -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void parseArtFile(FScanner& sc, FScriptPosition& pos) -{ - FScanner::SavedPos blockend; - FString file; - int tile = -1; - - if (sc.StartBraces(&blockend)) return; - while (!sc.FoundEndBrace(blockend)) - { - sc.MustGetString(); - if (sc.Compare("file")) sc.GetString(file); - else if (sc.Compare("tile")) sc.GetNumber(tile, true); - } - - if (file.IsEmpty()) - { - pos.Message(MSG_ERROR, "artfile: missing file name"); - } - else if (tile >= 0 && ValidateTilenum("artfile", tile, pos)) - TileFiles.LoadArtFile(file, nullptr, tile); -} - -//=========================================================================== -// -// this is only left in for compatibility purposes. -// There's way better methods to handle translucency. -// -//=========================================================================== - -static void parseBlendTableGlBlend(FScanner& sc, int id) -{ - FScanner::SavedPos blockend; - FScriptPosition pos = sc; - FString file; - - if (sc.StartBraces(&blockend)) return; - - glblend_t* const glb = glblend + id; - *glb = nullglblend; - - while (!sc.FoundEndBrace(blockend)) - { - int which = 0; - sc.MustGetString(); - if (sc.Compare("forward")) which = 1; - else if (sc.Compare("reverse")) which = 2; - else if (sc.Compare("both")) which = 3; - else continue; - - FScanner::SavedPos blockend2; - - if (sc.StartBraces(&blockend2)) return; - glblenddef_t bdef{}; - while (!sc.FoundEndBrace(blockend2)) - { - int whichb = 0; - sc.MustGetString(); - if (sc.Compare("}")) break; - if (sc.Compare({ "src", "sfactor", "top" })) whichb = 0; - else if (sc.Compare({ "dst", "dfactor", "bottom" })) whichb = 1; - else if (sc.Compare("alpha")) - { - sc.GetFloat(true); - bdef.alpha = (float)sc.Float; - continue; - } - uint8_t* const factor = whichb == 0 ? &bdef.src : &bdef.dst; - sc.MustGetString(); - if (sc.Compare("ZERO")) *factor = STYLEALPHA_Zero; - else if (sc.Compare("ONE")) *factor = STYLEALPHA_One; - else if (sc.Compare("SRC_COLOR")) *factor = STYLEALPHA_SrcCol; - else if (sc.Compare("ONE_MINUS_SRC_COLOR")) *factor = STYLEALPHA_InvSrcCol; - else if (sc.Compare("SRC_ALPHA")) *factor = STYLEALPHA_Src; - else if (sc.Compare("ONE_MINUS_SRC_ALPHA")) *factor = STYLEALPHA_InvSrc; - else if (sc.Compare("DST_ALPHA")) *factor = STYLEALPHA_Dst; - else if (sc.Compare("ONE_MINUS_DST_ALPHA")) *factor = STYLEALPHA_InvDst; - else if (sc.Compare("DST_COLOR")) *factor = STYLEALPHA_DstCol; - else if (sc.Compare("ONE_MINUS_DST_COLOR")) *factor = STYLEALPHA_InvDstCol; - else sc.ScriptMessage("Unknown blend operation %s", sc.String); - } - if (which & 1) glb->def[0] = bdef; - if (which & 2) glb->def[1] = bdef; - } -} - -void parseBlendTable(FScanner& sc, FScriptPosition& pos) -{ - FScanner::SavedPos blockend; - int id; - - if (!sc.GetNumber(id, true)) return; - - if (sc.StartBraces(&blockend)) return; - - if ((unsigned)id >= MAXBLENDTABS) - { - pos.Message(MSG_ERROR, "blendtable: Invalid blendtable number %d", id); - sc.RestorePos(blockend); - return; - } - - while (!sc.FoundEndBrace(blockend)) - { - sc.MustGetString(); - if (sc.Compare("raw")) parseEmptyBlock(sc, pos); // Raw translucency map for the software renderer. We have no use for this. - else if (sc.Compare("glblend")) parseBlendTableGlBlend(sc, id); - else if (sc.Compare("undef")) glblend[id] = defaultglblend; - else if (sc.Compare("copy")) - { - sc.GetNumber(true); - - if ((unsigned)sc.Number >= MAXBLENDTABS || sc.Number == id) - { - pos.Message(MSG_ERROR, "blendtable: Invalid source blendtable number %d in copy", sc.Number); - } - else glblend[id] = glblend[sc.Number]; - } - } -} - -//=========================================================================== -// -// thw same note as for blendtable applies here. -// -//=========================================================================== - -void parseNumAlphaTabs(FScanner& sc, FScriptPosition& pos) -{ - int value; - if (!sc.GetNumber(value)) return; - - for (int a = 1, value2 = value * 2 + (value & 1); a <= value; ++a) - { - float finv2value = 1.f / (float)value2; - - glblend_t* const glb = glblend + a; - *glb = defaultglblend; - glb->def[0].alpha = (float)(value2 - a) * finv2value; - glb->def[1].alpha = (float)a * finv2value; - } -} - -//=========================================================================== -// -// -// -//=========================================================================== - -static bool parseBasePaletteRaw(FScanner& sc, FScriptPosition& pos, int id) -{ - FScanner::SavedPos blockend; - FString fn; - int32_t offset = 0; - int32_t shiftleft = 0; - - if (sc.StartBraces(&blockend)) return false; - - while (!sc.FoundEndBrace(blockend)) - { - sc.MustGetString(); - if (sc.Compare("file")) sc.GetString(fn); - else if (sc.Compare("offset")) sc.GetNumber(offset, true); - else if (sc.Compare("shiftleft")) sc.GetNumber(shiftleft, true); - } - - if (fn.IsEmpty()) - { - pos.Message(MSG_ERROR, "basepalette: filename missing"); - } - else if (offset < 0) - { - pos.Message(MSG_ERROR, "basepalette: Invalid file offset"); - } - else if ((unsigned)shiftleft >= 8) - { - pos.Message(MSG_ERROR, "basepalette: Invalid left shift %d provided", shiftleft); - } - else - { - FileReader fil = fileSystem.OpenFileReader(fn); - if (!fil.isOpen()) - { - pos.Message(MSG_ERROR, "basepalette: Failed opening \"%s\"", fn.GetChars()); - } - else - { - fil.Seek(offset, FileReader::SeekSet); - auto palbuf = fil.Read(); - if (palbuf.Size() < 768) - { - pos.Message(MSG_ERROR, "basepalette: Read failed"); - } - else - { - if (shiftleft != 0) - { - for (auto& pe : palbuf) - pe <<= shiftleft; - } - - paletteSetColorTable(id, palbuf.Data(), false, false); - return true; - } - } - } - return false; -} - -void parseBasePalette(FScanner& sc, FScriptPosition& pos) -{ - FScanner::SavedPos blockend; - int id; - bool didLoadPal = false; - - if (!sc.GetNumber(id)) return; - - if (sc.StartBraces(&blockend)) return; - - if ((unsigned)id >= MAXBASEPALS) - { - pos.Message(MSG_ERROR, "basepalette: Invalid basepal number %d", id); - sc.RestorePos(blockend); - sc.CheckString("{"); - return; - } - - while (!sc.FoundEndBrace(blockend)) - { - sc.MustGetString(); - if (sc.Compare("raw")) didLoadPal |= parseBasePaletteRaw(sc, pos, id); - else if (sc.Compare("copy")) - { - int source = -1; - sc.GetNumber(source); - - if ((unsigned)source >= MAXBASEPALS || source == id) - { - pos.Message(MSG_ERROR, "basepalette: Invalid source basepal number %d", source); - } - else - { - auto sourcepal = GPalette.GetTranslation(Translation_BasePalettes, source); - if (sourcepal == nullptr) - { - pos.Message(MSG_ERROR, "basepalette: Source basepal %d does not exist", source); - } - else - { - GPalette.CopyTranslation(TRANSLATION(Translation_BasePalettes, id), TRANSLATION(Translation_BasePalettes, source)); - didLoadPal = true; - } - } - } - else if (sc.Compare("undef")) - { - GPalette.ClearTranslationSlot(TRANSLATION(Translation_BasePalettes, id)); - didLoadPal = 0; - if (id == 0) paletteloaded &= ~PALETTE_MAIN; - } - } - - if (didLoadPal && id == 0) - { - paletteloaded |= PALETTE_MAIN; - } -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void parseUndefBasePaletteRange(FScanner& sc, FScriptPosition& pos) -{ - int start, end; - - if (!sc.GetNumber(start)) return; - if (!sc.GetNumber(end)) return; - - if (start > end || (unsigned)start >= MAXBASEPALS || (unsigned)end >= MAXBASEPALS) - { - pos.Message(MSG_ERROR, "undefbasepaletterange: Invalid range [%d, %d]", start, end); - return; - } - for (int i = start; i <= end; i++) GPalette.ClearTranslationSlot(TRANSLATION(Translation_BasePalettes, i)); - if (start == 0) paletteloaded &= ~PALETTE_MAIN; -} - -//=========================================================================== -// -// sadly this looks broken by design with its hard coded 32 shades... -// -//=========================================================================== - -static void parsePalookupRaw(FScanner& sc, int id, int& didLoadShade) -{ - FScanner::SavedPos blockend; - FScriptPosition pos = sc; - - if (sc.StartBraces(&blockend)) return; - - FString fn; - int32_t offset = 0; - int32_t length = 256 * 32; // hardcoding 32 instead of numshades - - while (!sc.FoundEndBrace(blockend)) - { - sc.MustGetString(); - if (sc.Compare("file")) sc.GetString(fn); - else if (sc.Compare("offset")) sc.GetNumber(offset); - else if (sc.Compare("noshades")) length = 256; - } - - if (fn.IsEmpty()) - { - pos.Message(MSG_ERROR, "palookup: filename missing"); - } - else if (offset < 0) - { - pos.Message(MSG_ERROR, "palookup: Invalid file offset %d", offset); - } - else - { - FileReader fil = fileSystem.OpenFileReader(fn); - if (!fil.isOpen()) - { - pos.Message(MSG_ERROR, "palookup: Failed opening \"%s\"", fn.GetChars()); - } - else - { - fil.Seek(offset, FileReader::SeekSet); - auto palookupbuf = fil.Read(); - if (palookupbuf.Size() < 256) - { - pos.Message(MSG_ERROR, "palookup: Read failed"); - } - else if (palookupbuf.Size() >= 256 * 32) - { - didLoadShade = 1; - numshades = 32; - lookups.setTable(id, palookupbuf.Data()); - } - else - { - if (!(paletteloaded & PALETTE_SHADE)) - { - pos.Message(MSG_ERROR, "palookup: Shade tables not loaded"); - } - else - lookups.makeTable(id, palookupbuf.Data(), 0, 0, 0, lookups.tables[id].noFloorPal); - } - } - } -} - -static void parsePalookupFogpal(FScanner& sc, int id) -{ - FScanner::SavedPos blockend; - FScriptPosition pos = sc; - - if (sc.StartBraces(&blockend)) return; - - int red = 0, green = 0, blue = 0; - - while (!sc.FoundEndBrace(blockend)) - { - sc.MustGetString(); - if (sc.Compare({ "r", "red" })) sc.GetNumber(red); - else if (sc.Compare({ "g", "green" })) sc.GetNumber(green); - else if (sc.Compare({ "b", "blue" })) sc.GetNumber(blue); - } - red = clamp(red, 0, 255); - green = clamp(green, 0, 255); - blue = clamp(blue, 0, 255); - - if (!(paletteloaded & PALETTE_SHADE)) - { - pos.Message(MSG_ERROR, "palookup: Shade tables not loaded"); - } - else - lookups.makeTable(id, nullptr, red, green, blue, 1); -} - -static void parsePalookupMakePalookup(FScanner& sc, FScriptPosition& pos, int id, int& didLoadShade) -{ - FScanner::SavedPos blockend; - - if (sc.StartBraces(&blockend)) return; - - int red = 0, green = 0, blue = 0; - int remappal = -1; - - while (!sc.FoundEndBrace(blockend)) - { - sc.MustGetString(); - if (sc.Compare({ "r", "red" })) sc.GetNumber(red); - else if (sc.Compare({ "g", "green" })) sc.GetNumber(green); - else if (sc.Compare({ "b", "blue" })) sc.GetNumber(blue); - else if (sc.Compare("remappal")) sc.GetNumber(remappal, true); - else if (sc.Compare("remapself")) remappal = id; - } - red = clamp(red, 0, 255); - green = clamp(green, 0, 255); - blue = clamp(blue, 0, 255); - - if ((unsigned)remappal >= MAXPALOOKUPS) - { - pos.Message(MSG_ERROR, "palookup: Invalid remappal %d", remappal); - } - else if (!(paletteloaded & PALETTE_SHADE)) - { - pos.Message(MSG_ERROR, "palookup: Shade tables not loaded"); - } - else - lookups.makeTable(id, nullptr, red, green, blue, lookups.tables[id].noFloorPal); -} - -void parsePalookup(FScanner& sc, FScriptPosition& pos) -{ - FScanner::SavedPos blockend; - int id; - int didLoadShade = 0; - - if (!sc.GetNumber(id, true)) return; - if (sc.StartBraces(&blockend)) return; - - if ((unsigned)id >= MAXPALOOKUPS) - { - pos.Message(MSG_ERROR, "palookup: Invalid palette number %d", id); - sc.RestorePos(blockend); - return; - } - - while (!sc.FoundEndBrace(blockend)) - { - sc.MustGetString(); - if (sc.Compare("raw")) parsePalookupRaw(sc, id, didLoadShade); - else if (sc.Compare("fogpal")) parsePalookupFogpal(sc, id); - else if (sc.Compare("makepalookup")) parsePalookupMakePalookup(sc, pos, id, didLoadShade); - else if (sc.Compare("floorpal")) lookups.tables[id].noFloorPal = 0; - else if (sc.Compare("nofloorpal")) lookups.tables[id].noFloorPal = 1; - else if (sc.Compare("copy")) - { - int source = 0; - sc.GetNumber(source, true); - - if ((unsigned)source >= MAXPALOOKUPS || source == id) - { - pos.Message(MSG_ERROR, "palookup: Invalid source pal number %d", source); - } - else if (source == 0 && !(paletteloaded & PALETTE_SHADE)) - { - pos.Message(MSG_ERROR, "palookup: Shade tables not loaded"); - } - else - { - // do not overwrite the base with an empty table. - if (lookups.checkTable(source) || id > 0) lookups.copyTable(id, source); - didLoadShade = 1; - } - } - else if (sc.Compare("undef")) - { - lookups.clearTable(id); - didLoadShade = 0; - if (id == 0) paletteloaded &= ~PALETTE_SHADE; - } - - } - if (didLoadShade && id == 0) - { - paletteloaded |= PALETTE_SHADE; - } -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void parseMakePalookup(FScanner& sc, FScriptPosition& pos) -{ - FScanner::SavedPos blockend; - int red = 0, green = 0, blue = 0, pal = -1; - int remappal = 0; - int nofloorpal = -1; - bool havepal = false, haveremappal = false, haveremapself = false; - - if (sc.StartBraces(&blockend)) return; - - while (!sc.FoundEndBrace(blockend)) - { - sc.MustGetString(); - if (sc.Compare({ "r", "red" })) sc.GetNumber(red); - else if (sc.Compare({ "g", "green" })) sc.GetNumber(green); - else if (sc.Compare({ "b", "blue" })) sc.GetNumber(blue); - else if (sc.Compare("remappal")) - { - sc.GetNumber(remappal, true); - haveremappal = true; - } - else if (sc.Compare("remapself")) - { - haveremapself = true; - } - else if (sc.Compare("nofloorpal")) sc.GetNumber(nofloorpal, true); - else if (sc.Compare("pal")) - { - havepal = true; - sc.GetNumber(pal, true); - } - } - red = clamp(red, 0, 63); - green = clamp(green, 0, 63); - blue = clamp(blue, 0, 63); - - if (!havepal) - { - pos.Message(MSG_ERROR, "makepalookup: missing palette number"); - } - else if (pal == 0 || (unsigned)pal >= MAXREALPAL) - { - pos.Message(MSG_ERROR, "makepalookup: palette number %d out of range (1 .. %d)", pal, MAXREALPAL - 1); - } - else if (haveremappal && haveremapself) - { - // will also disallow multiple remappals or remapselfs - pos.Message(MSG_ERROR, "makepalookup: must have either 'remappal' or 'remapself' but not both"); - } - else if ((haveremappal && (unsigned)remappal >= MAXREALPAL)) - { - pos.Message(MSG_ERROR, "makepalookup: remap palette number %d out of range (0 .. %d)", pal, MAXREALPAL - 1); - } - else - { - if (haveremapself) remappal = pal; - lookups.makeTable(pal, lookups.getTable(remappal), red << 2, green << 2, blue << 2, - remappal == 0 ? 1 : (nofloorpal == -1 ? lookups.tables[remappal].noFloorPal : nofloorpal)); - } -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void parseUndefPalookupRange(FScanner& sc, FScriptPosition& pos) -{ - int id0, id1; - - if (!sc.GetNumber(id0, true)) return; - if (!sc.GetNumber(id1, true)) return; - - if (id0 > id1 || (unsigned)id0 >= MAXPALOOKUPS || (unsigned)id1 >= MAXPALOOKUPS) - { - pos.Message(MSG_ERROR, "undefpalookuprange: Invalid range"); - } - else - { - for (int i = id0; i <= id1; i++) lookups.clearTable(i); - if (id0 == 0) paletteloaded &= ~PALETTE_SHADE; + tbuild->tile[i].extinfo.picanm.sf |= PICANM_NOFULLBRIGHT_BIT; } } @@ -2355,34 +1899,6 @@ static void parseDefineQAV(FScanner& sc, FScriptPosition& pos) fileSystem.CreatePathlessCopy(fn, res_id, 0); } -static void parseTileFlags(FScanner& sc, FScriptPosition& pos) -{ - int num = -1; - - sc.SetCMode(true); - sc.GetNumber(num, true); - if (!sc.CheckString("{")) - { - pos.Message(MSG_ERROR, "tileflags:'{' expected, unable to continue"); - sc.SetCMode(false); - return; - } - while (!sc.CheckString("}")) - { - sc.MustGetString(); - int tile = tileForName(sc.String); - if (tile == -1) - { - pos.Message(MSG_ERROR, "tileflags:Unknown tile name '%s'", sc.String); - } - else - { - TileFiles.tiledata[tile].tileflags |= num; - } - } - sc.SetCMode(false); -} - //=========================================================================== // // @@ -2423,7 +1939,6 @@ static const dispatch basetokens[] = { "skybox", parseSkybox }, { "highpalookup", parseHighpalookup }, { "tint", parseTint }, - { "makepalookup", parseMakePalookup }, { "texture", parseTexture }, { "tile", parseTexture }, { "music", parseMusic }, @@ -2456,26 +1971,18 @@ static const dispatch basetokens[] = { "cachesize", parseSkip<1> }, { "dummytilefrompic",parseImportTile }, { "tilefromtexture", parseTileFromTexture }, - { "artfile", parseArtFile }, { "mapinfo", parseMapinfo }, { "echo", parseEcho }, { "globalflags", parseSkip<1> }, { "copytile", parseCopyTile }, { "globalgameflags", parseSkip<1> }, { "multipsky", parseMultiPsky }, - { "basepalette", parseBasePalette }, - { "palookup", parsePalookup }, - { "blendtable", parseBlendTable }, - { "numalphatables", parseNumAlphaTabs }, - { "undefbasepaletterange", parseUndefBasePaletteRange }, - { "undefpalookuprange", parseUndefPalookupRange }, { "undefblendtablerange", parseSkip<2> }, { "shadefactor", parseSkip<1> }, { "newgamechoices", parseEmptyBlock }, { "rffdefineid", parseRffDefineId }, { "defineqav", parseDefineQAV }, - { "tileflag", parseTileFlags }, { nullptr, nullptr }, }; @@ -2499,8 +2006,9 @@ static void defsparser(FScanner& sc) } } -void loaddefinitionsfile(const char* fn, bool cumulative, bool maingame) +void loaddefinitionsfile(TilesetBuildInfo& info, const char* fn, bool cumulative, bool maingame) { + tbuild = &info; bool done = false; auto parseit = [&](int lump) { diff --git a/source/core/g_mapinfo.cpp b/source/core/g_mapinfo.cpp index 93c731a5f..b43381f69 100644 --- a/source/core/g_mapinfo.cpp +++ b/source/core/g_mapinfo.cpp @@ -46,6 +46,9 @@ #include "i_system.h" #include "gamecontrol.h" #include "coreactor.h" +#include "texinfo.h" + +#include "buildtiles.h" extern TArray clusters; extern TArray volumes; @@ -500,18 +503,18 @@ void FMapInfoParser::ParseTextureFlags() do { sc.MustGetString(); - int tile = tileForName(sc.String); + // this must also get null textures and ones not yet loaded. + auto tex = TexMan.CheckForTexture(sc.String, ETextureType::Any, FTextureManager::TEXMAN_ReturnAll | FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ForceLookup); - if (tile == -1) + if (!tex.isValid()) { sc.ScriptMessage("textureflags:Unknown texture name '%s'", sc.String); } else { - TileFiles.tiledata[tile].tileflags |= num; + texExtInfo[tex.GetIndex()].flags |= num; } - // tileflags |= sc.Number; } while (sc.CheckString(",")); } } diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp index 9b978c047..a854e4fd0 100644 --- a/source/core/gamecontrol.cpp +++ b/source/core/gamecontrol.cpp @@ -82,6 +82,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "findfile.h" #include "version.h" #include "hw_material.h" +#include "tiletexture.h" +#include "tilesetbuilder.h" + +#include "buildtiles.h" void LoadHexFont(const char* filename); @@ -629,7 +633,6 @@ int GameMain() voxClear(); ClearPalManager(); TexMan.DeleteAll(); - TileFiles.CloseAll(); // delete the texture data before shutting down graphics. I_ShutdownGraphics(); if (gi) { @@ -950,22 +953,32 @@ void GetGames() // //========================================================================== -static void InitTextures() +static void InitTextures(TArray& usedgroups) { + voxInit(); TexMan.usefullnames = true; TexMan.Init(); TexMan.AddTextures([]() {}, [](BuildInfo&) {}); StartWindow->Progress(); - TileFiles.Init(); - TileFiles.LoadArtSet("tiles%03d.art"); // it's the same for all games. - voxInit(); - gi->LoadGameTextures(); // loads game-side data that must be present before processing the .def files. - LoadDefinitions(); + TArray addArt; + for (auto& grp : usedgroups) + { + for (auto& art : grp.FileInfo.loadart) + { + addArt.Push(art); + } + } + if (userConfig.AddArt) for (auto& art : *userConfig.AddArt) + { + addArt.Push(art); + } + InitArtFiles(addArt); + + ConstructTileset(); InitFont(); // InitFonts may only be called once all texture data has been initialized. - gi->SetupSpecialTextures(); // For installing dynamic or deleted textures in the texture manager. This must be done after parsing .def and before setting the aliases. - TileFiles.SetAliases(); + //TileFiles.SetAliases(); lookups.postLoadTables(); highTileSetup(); @@ -1074,20 +1087,6 @@ int RunGame() StartWindow = FStartupScreen::CreateInstance(8, true); StartWindow->Progress(); - TArray addArt; - for (auto& grp : usedgroups) - { - for (auto& art : grp.FileInfo.loadart) - { - addArt.Push(art); - } - } - if (userConfig.AddArt) for (auto& art : *userConfig.AddArt) - { - addArt.Push(art); - } - TileFiles.AddArt(addArt); - inputState.ClearAllInput(); if (!GameConfig->IsInitialized()) @@ -1108,7 +1107,7 @@ int RunGame() gi->loadPalette(); BuildFogTable(); StartWindow->Progress(); - InitTextures(); + InitTextures(usedgroups); StartWindow->Progress(); I_InitSound(); @@ -1419,61 +1418,6 @@ void DrawCrosshair(int deftile, int health, double xdelta, double ydelta, double ST_DrawCrosshair(health, xpos, ypos, 1, angle); } } -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void LoadDefinitions() -{ - const char* defsfile = G_DefFile(); - FString razedefsfile = defsfile; - razedefsfile.Substitute(".def", "-raze.def"); - - loaddefinitionsfile("engine/engine.def", true, true); // Internal stuff that is required. - - // check what we have. - // user .defs override the default ones and are not cumulative. - // if we fine even one Raze-specific file, all of those will be loaded cumulatively. - // otherwise the default rules inherited from older ports apply. - if (userConfig.UserDef.IsNotEmpty()) - { - loaddefinitionsfile(userConfig.UserDef, false); - } - else - { - if (fileSystem.FileExists(razedefsfile)) - { - loaddefinitionsfile(razedefsfile, true); - } - else if (fileSystem.FileExists(defsfile)) - { - loaddefinitionsfile(defsfile, false); - } - } - - if (userConfig.AddDefs) - { - for (auto& m : *userConfig.AddDefs) - { - loaddefinitionsfile(m, false); - } - userConfig.AddDefs.reset(); - } - - if (GameStartupInfo.def.IsNotEmpty()) - { - loaddefinitionsfile(GameStartupInfo.def); // Stuff from gameinfo. - } - - // load the widescreen replacements last. This ensures that mods still get the correct CRCs for their own tile replacements. - if (fileSystem.FindFile("engine/widescreen.def") >= 0 && !Args->CheckParm("-nowidescreen")) - { - loaddefinitionsfile("engine/widescreen.def"); - } - fileSystem.InitHashChains(); // make sure that any resources that got added can be found again. -} bool M_Active() { diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h index e8670206f..a950583b9 100644 --- a/source/core/gamecontrol.h +++ b/source/core/gamecontrol.h @@ -146,9 +146,6 @@ struct GrpEntry GrpInfo FileInfo; }; extern int g_gameType; -const char* G_DefaultDefFile(void); -const char* G_DefFile(void); -void LoadDefinitions(); // game check shortcuts inline bool isNam() diff --git a/source/core/gamefuncs.cpp b/source/core/gamefuncs.cpp index b46e41777..0369e0f9d 100644 --- a/source/core/gamefuncs.cpp +++ b/source/core/gamefuncs.cpp @@ -27,6 +27,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "interpolate.h" #include "texturemanager.h" #include "hw_voxels.h" +#include "texinfo.h" +#include "buildtiles.h" IntRect viewport3d; constexpr double MAXCLIPDISTF = 64; @@ -205,7 +207,7 @@ void GetWallSpritePosition(const spritetypebase* spr, const DVector2& pos, DVect auto tex = TexMan.GetGameTexture(spr->spritetexture()); double width, xoffset; - TileOffs* tofs; + const TileOffs* tofs; if (render && hw_hightile && (tofs = GetHiresOffset(spr->spritetexture()))) { width = tofs->xsize; @@ -245,7 +247,7 @@ void TGetFlatSpritePosition(const spritetypebase* spr, const DVector2& pos, DVec int xo = heinum ? 0 : spr->xoffset; int yo = heinum ? 0 : spr->yoffset; - TileOffs* tofs; + const TileOffs* tofs; if (render && hw_hightile && (tofs = GetHiresOffset(spr->spritetexture()))) { width = tofs->xsize * xrepeat; diff --git a/source/core/gamefuncs.h b/source/core/gamefuncs.h index 5d065a9e3..19bd4e24f 100644 --- a/source/core/gamefuncs.h +++ b/source/core/gamefuncs.h @@ -205,8 +205,6 @@ inline void dragpoint(walltype* startwall, const DVector2& pos) extern double cameradist, cameraclock; -void loaddefinitionsfile(const char* fn, bool cumulative = false, bool maingrp = false); - bool calcChaseCamPos(DVector3& ppos, DCoreActor* pspr, sectortype** psectnum, const DRotator& angles, double const interpfrac, double const backamp); int getslopeval(sectortype* sect, const DVector3& pos, double bazez); bool cansee(const DVector3& start, sectortype* sect1, const DVector3& end, sectortype* sect2); diff --git a/source/core/gamehud.cpp b/source/core/gamehud.cpp index d56967cc0..54068e76d 100644 --- a/source/core/gamehud.cpp +++ b/source/core/gamehud.cpp @@ -46,6 +46,10 @@ #include "v_font.h" #include "gamestruct.h" #include "gamefuncs.h" +#include "texinfo.h" + +#include "buildtiles.h" + F2DDrawer twodpsp; @@ -56,9 +60,7 @@ void hud_drawsprite(double sx, double sy, double sz, double a, int picnum, int d alpha *= (dastat & RS_TRANS1)? glblend[0].def[!!(dastat & RS_TRANS2)].alpha : 1.; int palid = TRANSLATION(Translation_Remap + curbasepal, dapalnum); - tileUpdatePicnum(&picnum); - - auto tex = tileGetTexture(picnum); + auto tex = tileGetTexture(picnum, true); DrawTexture(&twodpsp, tex, sx, sy, DTA_ScaleX, sz, DTA_ScaleY, sz, diff --git a/source/core/gamestruct.h b/source/core/gamestruct.h index 76786550f..d70db7c0e 100644 --- a/source/core/gamestruct.h +++ b/source/core/gamestruct.h @@ -49,6 +49,7 @@ struct ReservedSpace enum EMenuSounds : int; struct MapRecord; +struct TilesetBuildInfo; extern cycle_t drawtime, actortime, thinktime, gameupdatetime; @@ -71,8 +72,8 @@ struct GameInterface virtual ~GameInterface() {} virtual bool GenerateSavePic() { return false; } virtual void app_init() = 0; - virtual void LoadGameTextures() {} - virtual void SetupSpecialTextures() {} + virtual void LoadTextureInfo(TilesetBuildInfo& info) {} + virtual void SetupSpecialTextures(TilesetBuildInfo&) {} virtual void loadPalette(); virtual void clearlocalinputstate() {} virtual void UpdateScreenSize() {} diff --git a/source/core/mainloop.cpp b/source/core/mainloop.cpp index 0a9ae35e0..d1eef7f4c 100644 --- a/source/core/mainloop.cpp +++ b/source/core/mainloop.cpp @@ -89,6 +89,7 @@ #include "gamehud.h" #include "wipe.h" #include "i_interface.h" +#include "texinfo.h" #include "texturemanager.h" CVAR(Bool, vid_activeinbackground, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) diff --git a/source/core/maploader.cpp b/source/core/maploader.cpp index 04f8f2cb6..bf97c7ea5 100644 --- a/source/core/maploader.cpp +++ b/source/core/maploader.cpp @@ -47,6 +47,7 @@ #include "render.h" #include "hw_sections.h" #include "interpolate.h" +#include "tiletexture.h" #include "games/blood/src/mapstructs.h" extern BitArray clipsectormap; diff --git a/source/core/models/modeldata.cpp b/source/core/models/modeldata.cpp index 6739860ed..e6b6c7b16 100644 --- a/source/core/models/modeldata.cpp +++ b/source/core/models/modeldata.cpp @@ -4,6 +4,7 @@ #include "texturemanager.h" #include "hw_voxels.h" #include "gamefuncs.h" +#include "tiletexture.h" #include "build.h" diff --git a/source/core/precache.cpp b/source/core/precache.cpp index ebdb1e87e..7b58f15bc 100644 --- a/source/core/precache.cpp +++ b/source/core/precache.cpp @@ -45,6 +45,9 @@ #include "mapinfo.h" #include "models/modeldata.h" #include "gamefuncs.h" +#include "texinfo.h" + +#include "buildtiles.h" BEGIN_BLD_NS extern short voxelIndex[MAXTILES]; diff --git a/source/core/psky.cpp b/source/core/psky.cpp index ab9935d91..d709a5e89 100644 --- a/source/core/psky.cpp +++ b/source/core/psky.cpp @@ -34,9 +34,10 @@ */ #include "psky.h" -#include "buildtiles.h" #include "gamefuncs.h" #include "tarray.h" +#include "texinfo.h" +#include "buildtiles.h" static TArray skies; diff --git a/source/core/rendering/hw_voxels.cpp b/source/core/rendering/hw_voxels.cpp index 738d6622e..a4232b5bb 100644 --- a/source/core/rendering/hw_voxels.cpp +++ b/source/core/rendering/hw_voxels.cpp @@ -29,6 +29,7 @@ #include "build.h" #include "voxels.h" #include "hw_voxels.h" +#include "tiletexture.h" #include "gamecontrol.h" int16_t tiletovox[MAXTILES]; diff --git a/source/core/rendering/scene/hw_drawinfo.cpp b/source/core/rendering/scene/hw_drawinfo.cpp index ea10c3e7d..adda3e177 100644 --- a/source/core/rendering/scene/hw_drawinfo.cpp +++ b/source/core/rendering/scene/hw_drawinfo.cpp @@ -43,6 +43,9 @@ #include "automap.h" #include "hw_voxels.h" #include "coreactor.h" +#include "tiletexture.h" + +#include "buildtiles.h" EXTERN_CVAR(Float, r_visibility) CVAR(Bool, gl_no_skyclear, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) @@ -275,23 +278,26 @@ void HWDrawInfo::DispatchSprites() for (unsigned i = 0; i < tsprites.Size(); i++) { auto tspr = tsprites.get(i); - int tilenum = tspr->picnum; auto actor = tspr->ownerActor; + auto texid = tspr->spritetexture(); - if (actor == nullptr || tspr->scale.X == 0 || tspr->scale.Y == 0 || (unsigned)tilenum >= MAXTILES) + if (actor == nullptr || tspr->scale.X == 0 || tspr->scale.Y == 0) continue; actor->spr.cstat2 |= CSTAT2_SPRITE_MAPPED; if ((tspr->cstat & CSTAT_SPRITE_ALIGNMENT_MASK) != CSTAT_SPRITE_ALIGNMENT_SLAB && !(tspr->cstat2 & CSTAT2_SPRITE_NOANIMATE)) - tileUpdatePicnum(&tilenum, false, (actor->GetIndex() & 16383)); + { + tileUpdatePicnum(texid, (actor->GetIndex() & 16383)); + } if (tspr->cstat2 & CSTAT2_SPRITE_FULLBRIGHT) tspr->shade = -127; - tspr->picnum = tilenum; + tspr->picnum = legacyTileNum(texid); + int tilenum = tspr->picnum; if (!(actor->sprext.renderflags & SPREXT_NOTMD)) { - auto pt = modelManager.GetModel(tilenum, tspr->pal); + auto pt = modelManager.GetModel(tspr->picnum, tspr->pal); if (hw_models && pt && pt->modelid >= 0 && pt->framenum >= 0) { //HWSprite hwsprite; @@ -315,6 +321,7 @@ void HWDrawInfo::DispatchSprites() } } } + if (!texid.isValid()) return; // due to CSTAT_SPRITE_ALIGNMENT_SLAB this can only be checked here if (actor->sprext.renderflags & SPREXT_AWAY1) { diff --git a/source/core/rendering/scene/hw_flats.cpp b/source/core/rendering/scene/hw_flats.cpp index 6b76ab170..30e79d1a4 100644 --- a/source/core/rendering/scene/hw_flats.cpp +++ b/source/core/rendering/scene/hw_flats.cpp @@ -38,6 +38,7 @@ #include "sectorgeometry.h" #include "hw_sections.h" #include "texturemanager.h" +#include "buildtiles.h" #ifdef _DEBUG CVAR(Int, gl_breaksec, -1, 0) diff --git a/source/core/rendering/scene/hw_sky.cpp b/source/core/rendering/scene/hw_sky.cpp index b3ce6f5eb..f03895a82 100644 --- a/source/core/rendering/scene/hw_sky.cpp +++ b/source/core/rendering/scene/hw_sky.cpp @@ -31,6 +31,8 @@ #include "cmdlib.h" #include "psky.h" +#include "buildtiles.h" + CVAR(Bool,gl_noskyboxes, false, 0) FGameTexture* GetSkyTexture(int basetile, int lognumtiles, const int16_t* tilemap, int remap); FGameTexture* SkyboxReplacement(FTextureID picnum, int palnum); @@ -45,7 +47,7 @@ void initSkyInfo(HWDrawInfo *di, HWSkyInfo* sky, sectortype* sector, int plane) { int picnum = plane == plane_ceiling ? sector->ceilingpicnum : sector->floorpicnum; auto tex = tileGetTexture(picnum); - tileUpdatePicnum(&picnum); + //tileUpdatePicnum(&picnum); // for now we can make do without this. int palette = plane == plane_ceiling ? sector->ceilingpal : sector->floorpal; FGameTexture* skytex = SkyboxReplacement(tex->GetID(), palette); diff --git a/source/core/rendering/scene/hw_sprites.cpp b/source/core/rendering/scene/hw_sprites.cpp index 3d5b86c35..d45a2e3a5 100644 --- a/source/core/rendering/scene/hw_sprites.cpp +++ b/source/core/rendering/scene/hw_sprites.cpp @@ -30,6 +30,7 @@ #include "vectors.h" #include "texturemanager.h" #include "basics.h" +#include "texinfo.h" //#include "hw_models.h" #include "hw_drawstructs.h" @@ -45,6 +46,7 @@ #include "hw_models.h" #include "hw_viewpointbuffer.h" #include "hw_voxels.h" +#include "buildtiles.h" //========================================================================== // @@ -345,7 +347,7 @@ void HWSprite::Process(HWDrawInfo* di, tspritetype* spr, sectortype* sector, int int flags = spr->cstat; int xsize, ysize, tilexoff, tileyoff; - TileOffs* tofs; + const TileOffs* tofs; if (hw_hightile && (tofs = GetHiresOffset(spr->spritetexture()))) { diff --git a/source/core/rendering/scene/hw_walls.cpp b/source/core/rendering/scene/hw_walls.cpp index 6ce704d1b..d843a53ff 100644 --- a/source/core/rendering/scene/hw_walls.cpp +++ b/source/core/rendering/scene/hw_walls.cpp @@ -33,8 +33,10 @@ #include "hw_skydome.h" #include "hw_drawstructs.h" #include "hw_vertexmap.h" +#include "buildtiles.h" #include "gamefuncs.h" #include "cmdlib.h" +#include "texinfo.h" #include "v_video.h" #include "flatvertices.h" @@ -1163,7 +1165,7 @@ void HWWall::ProcessWallSprite(HWDrawInfo* di, tspritetype* spr, sectortype* sec SetSpriteTranslucency(Sprite, alpha, RenderStyle); - TileOffs* tofs; + const TileOffs* tofs; int height, topofs; if (hw_hightile && (tofs = GetHiresOffset(spr->spritetexture()))) { diff --git a/source/core/savegamehelp.cpp b/source/core/savegamehelp.cpp index ec2dc9294..2e1a8d972 100644 --- a/source/core/savegamehelp.cpp +++ b/source/core/savegamehelp.cpp @@ -63,8 +63,12 @@ #include "ns.h" #include "serialize_obj.h" #include "games/blood/src/mapstructs.h" +#include "texinfo.h" #include +#include "buildtiles.h" + + void WriteSavePic(FileWriter* file, int width, int height); bool WriteZip(const char* filename, TArray& filenames, TArray& content); diff --git a/source/core/sectorgeometry.cpp b/source/core/sectorgeometry.cpp index 7bfd692d6..c2c6deba9 100644 --- a/source/core/sectorgeometry.cpp +++ b/source/core/sectorgeometry.cpp @@ -36,6 +36,7 @@ #include "sectorgeometry.h" #include "build.h" +#include "buildtiles.h" #include "gamefuncs.h" #include "texturemanager.h" #include "earcut.hpp" diff --git a/source/core/textures/buildtiles.cpp b/source/core/textures/buildtiles.cpp deleted file mode 100644 index 622317b07..000000000 --- a/source/core/textures/buildtiles.cpp +++ /dev/null @@ -1,820 +0,0 @@ -/* -** buildtexture.cpp -** Handling Build textures -** -**--------------------------------------------------------------------------- -** Copyright 2019 Christoph Oelckers -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -** -*/ - -#include "files.h" -#include "zstring.h" -#include "buildtiles.h" -#include "image.h" - -#include "palette.h" -#include "m_crc32.h" -#include "build.h" -#include "gamecontrol.h" -#include "gamefuncs.h" -#include "palettecontainer.h" -#include "texturemanager.h" -#include "c_dispatch.h" -#include "sc_man.h" -#include "gamestruct.h" -#include "hw_voxels.h" -#include "models/modeldata.h" - -#include "hw_renderstate.h" - -enum -{ - MAXARTFILES_BASE = 200, - MAXARTFILES_TOTAL = 220 -}; - - -BuildTiles TileFiles; - -int tileSetHightileReplacement(int picnum, int palnum, const char* filename, float alphacut, float xscale, float yscale, float specpower, float specfactor, bool indexed = false); - -//========================================================================== -// -// -// -//========================================================================== - -picanm_t tileConvertAnimFormat(int32_t const picanimraw, int* lo, int* to) -{ - // Unpack a 4 byte packed anim descriptor into something more accessible - picanm_t anm; - anm.num = picanimraw & 63; - *lo = (int8_t)((picanimraw >> 8) & 255); - *to = (int8_t)((picanimraw >> 16) & 255); - anm.sf = ((picanimraw >> 24) & 15) | (picanimraw & 192); - anm.extra = (picanimraw >> 28) & 15; - return anm; -} - - -//========================================================================== -// -// Base class for Build tile textures -// This needs a few subclasses for different use cases. -// -//========================================================================== - -int FTileTexture::CopyPixels(FBitmap* bmp, int conversion) -{ - TArray buffer; - auto ppix = GetRawData(); - if (ppix) - { - bmp->CopyPixelData(0, 0, ppix, Width, Height, Height, 1, 0, GPalette.BaseColors); - } - return 0; -} - -TArray FTileTexture::CreatePalettedPixels(int conversion) -{ - TArray buffer(Width * Height, true); - auto p = GetRawData(); - if (p) memcpy(buffer.Data(), p, buffer.Size()); - else memset(buffer.Data(), 0, buffer.Size()); - return buffer; -} - -//========================================================================== -// -// raw pixel cache. This is for accessing pixel data in the game code, -// not for rendering. -// -//========================================================================== - -struct RawCacheNode -{ - TArray data; - uint64_t lastUseTime; - - RawCacheNode() = default; - RawCacheNode(RawCacheNode& other) = default; - RawCacheNode& operator=(RawCacheNode& other) = default; - - RawCacheNode(RawCacheNode&& other) noexcept - { - data = std::move(other.data); - lastUseTime = other.lastUseTime; - } - - RawCacheNode& operator=(RawCacheNode&& other) noexcept - { - data = std::move(other.data); - lastUseTime = other.lastUseTime; - return *this; - } -}; - -//========================================================================== -// -// -// -//========================================================================== -static TMap CacheNodes; - -const uint8_t* GetRawPixels(FTextureID texid) -{ - if (!texid.isValid()) return nullptr; - auto gtex = TexMan.GetGameTexture(texid); - auto tex = dynamic_cast(gtex->GetTexture()); - - if (!tex || !tex->GetImage()) return nullptr; - auto img = tex->GetImage(); - auto timg = dynamic_cast(img); - if (!timg || !timg->GetRawData()) - { - auto cache = CacheNodes.CheckKey(texid.GetIndex()); - if (cache) - { - cache->lastUseTime = I_nsTime(); - return cache->data.Data(); - } - RawCacheNode newnode; - newnode.data = img->GetPalettedPixels(0); - newnode.lastUseTime = I_nsTime(); - auto retval =newnode.data.Data(); - CacheNodes.Insert(texid.GetIndex(), std::move(newnode)); - return retval; - } - else - { - return timg->GetRawData(); - } -} - -//========================================================================== -// -//To use this the texture must have been made writable during texture init. -// -//========================================================================== - -uint8_t* GetWritablePixels(FTextureID texid) -{ - if (!texid.isValid()) return nullptr; - auto gtex = TexMan.GetGameTexture(texid); - auto tex = dynamic_cast(gtex->GetTexture()); - - if (!tex || !tex->GetImage()) return nullptr; - auto timg = dynamic_cast(tex->GetImage()); - if (!timg) return nullptr; - gtex->CleanHardwareData(); // we can safely assume that this only gets called when the texture is about to be changed. - return timg->GetRawData(); -} - -//========================================================================== -// -// -// -//========================================================================== - -static FGameTexture* GetTileTexture(const char* name, const TArray& backingstore, uint32_t offset, int width, int height) -{ - auto tex = new FArtTile(backingstore, offset, width, height); - 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; - } - - if (tex) - { - return MakeGameTexture(new FImageTexture(tex), name, ETextureType::Any); - } - return nullptr; -} - - -void BuildTiles::Init() -{ - Placeholder = TexMan.GameByIndex(0); - tiledata.Resize(MAXTILES); - memset(tiledata.Data(), 0, sizeof(tiledata[0]) * MAXTILES); - for (auto& tile : tiledata) - { - tile.texture = Placeholder; - tile.alphaThreshold = 0.5; - } - -} -//========================================================================== -// -// -// -//========================================================================== - -void BuildTiles::AddTile(int tilenum, FGameTexture* tex) -{ - if (!tex->GetID().isValid()) - TexMan.AddGameTexture(tex); - tiledata[tilenum].texture = tex; -} - -//=========================================================================== -// -// AddTiles -// -// Adds all the tiles in an artfile to the texture manager. -// -//=========================================================================== - -void BuildTiles::AddTiles (int firsttile, TArray& RawData, const char *mapname) -{ - - const uint8_t *tiles = RawData.Data(); -// int numtiles = LittleLong(((uint32_t *)tiles)[1]); // This value is not reliable - int tilestart = LittleLong(((int *)tiles)[2]); - int tileend = LittleLong(((int *)tiles)[3]); - 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]; - - if (firsttile != -1) - { - tileend = tileend - tilestart + firsttile; - tilestart = firsttile; - } - - 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; - - 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); - AddTile(i, tex); - int leftoffset, topoffset; - this->tiledata[i].picanm = tileConvertAnimFormat(anm, &leftoffset, &topoffset); - tex->SetOffsets(leftoffset, topoffset); - - tiledata += size; - } -} - -//=========================================================================== -// -// CountTiles -// -// Returns the number of tiles provided by an artfile -// -//=========================================================================== - -int CountTiles (const char *fn, const uint8_t *RawData) -{ - int version = LittleLong(*(uint32_t *)RawData); - if (version != 1) - { - Printf("%s: Invalid art file version. Must be 1, got %d\n", fn, version); - return 0; - } - - int tilestart = LittleLong(((uint32_t *)RawData)[2]); - int tileend = LittleLong(((uint32_t *)RawData)[3]); - - if ((unsigned)tilestart >= MAXUSERTILES || (unsigned)tileend >= MAXUSERTILES) - { - Printf("%s: Invalid tilestart or tileend\n", fn); - return 0; - } - if (tileend < tilestart) - { - Printf("%s: tileend < tilestart\n", fn); - return 0; - } - - return tileend >= tilestart ? tileend - tilestart + 1 : 0; -} - -//=========================================================================== -// -// MakeCanvas -// -// Turns texture into a canvas (i.e. camera texture) -// -//=========================================================================== - -void BuildTiles::MakeCanvas(int tilenum, int width, int height) -{ - auto canvas = ValidateCustomTile(tilenum, ReplacementType::Canvas); - canvas->SetSize(width*4, height*4); - canvas->SetDisplaySize((float)width, (float)height); - canvas->GetTexture()->SetSize(width * 4, height * 4); - static_cast(canvas->GetTexture())->aspectRatio = (float)width / height; -} - -//=========================================================================== -// -// LoadArtFile -// -// Returns the number of tiles found. -// -// let's load everything into memory on startup. -// -//=========================================================================== - -int BuildTiles::LoadArtFile(const char *fn, const char *mapname, int firsttile) -{ - unsigned old = FindFile(fn); - if (old >= ArtFiles.Size()) // Do not process if already loaded. - { - FileReader fr = fileSystem.OpenFileReader(fn); - if (fr.isOpen()) - { - auto artdata = fr.Read(); - const uint8_t *artptr = artdata.Data(); - if (artdata.Size() > 16) - { - if (memcmp(artptr, "BUILDART", 8) == 0) - { - artdata.Delete(0, 8); - } - // Only load the data if the header is present - if (CountTiles(fn, artptr) > 0) - { - auto file = new BuildArtFile; - ArtFiles.Push(file); - file->filename = fn; - file->RawData = std::move(artdata); - AddTiles(firsttile, file->RawData, mapname); - } - } - } - else - { - //Printf("%s: file not found\n", fn); - return -1; - } - } - return 0; -} - -//========================================================================== -// -// -// -//========================================================================== - -void BuildTiles::LoadArtSet(const char* filename) -{ - for (int index = 0; index < MAXARTFILES_BASE; index++) - { - FStringf fn(filename, index); - LoadArtFile(fn, nullptr); - } - for (auto& addart : addedArt) - { - LoadArtFile(addart, nullptr); - } -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void BuildTiles::SetAliases() -{ - TMap::Iterator it(nametoindex); - TMap::Pair* pair; - while (it.NextPair(pair)) - TexMan.AddAlias(pair->Key.GetChars(), tileGetTexture(pair->Value)); -} - -//========================================================================== -// -// 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! -// -// All these uses will need some review further down the line so that the texture manager's content is immutable. -// -//========================================================================== - -FGameTexture* BuildTiles::ValidateCustomTile(int tilenum, ReplacementType type) -{ - if (locked) I_FatalError("Modifying tiles after startup is not allowed."); - if (tilenum < 0 || tilenum >= MAXTILES) return nullptr; - auto &td = tiledata[tilenum]; - 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. - FTexture* replacement = nullptr; - td.replacement = type; - if (type == ReplacementType::Writable) - { - // Creates an empty writable tile. - // Current use cases are: - // 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. - replacement = new FImageTexture(new FWritableTile); - } - else if (type == ReplacementType::Restorable) - { - // This is for modifying an existing tile. - // It only gets used for a few specific effects: - // A) the fire in Blood. - // B) the pin display in Redneck Rampage's bowling lanes. - // C) Exhumed's menu plus one special effect tile. - if (tile->GetTexelWidth() == 0 || tile->GetTexelHeight() == 0) return nullptr; // The base must have a size for this to work. - replacement = new FImageTexture(new FRestorableTile(tile->GetTexture()->GetImage())); - } - else if (type == ReplacementType::Canvas) - { - replacement = new FCanvasTexture(1, 1); - } - else return nullptr; - auto rep = MakeGameTexture(replacement, tile->GetName(), ETextureType::Override); - AddTile(tilenum, rep); - return rep; -} - -//========================================================================== -// -// Creates a tile for displaying custom content -// -//========================================================================== - -uint8_t* BuildTiles::tileCreate(int tilenum, int width, int height) -{ - if (width <= 0 || height <= 0) return nullptr; - auto tex = ValidateCustomTile(tilenum, ReplacementType::Writable); - if (tex == nullptr) return nullptr; - auto wtex = static_cast(tex->GetTexture()->GetImage()); - if (!wtex->ResizeImage(width, height)) return nullptr; - tex->SetSize(width, height); - return wtex->GetRawData(); -} - -//========================================================================== -// -// Makes a tile writable - only used for a handful of special cases -// (todo: Investigate how to get rid of this) -// -//========================================================================== - -uint8_t* BuildTiles::tileMakeWritable(int num) -{ - auto tex = ValidateCustomTile(num, ReplacementType::Restorable); - auto wtex = static_cast(tex->GetTexture()->GetImage()); - return wtex ? wtex->GetRawData() : nullptr; -} - -//========================================================================== -// -// Returns checksum for a given tile -// -//========================================================================== - -int32_t tileGetCRC32(int tileNum) -{ - if ((unsigned)tileNum >= (unsigned)MAXTILES) return 0; - auto tile = dynamic_cast(TileFiles.tiledata[tileNum].texture->GetTexture()->GetImage()); // only consider original ART tiles. - if (!tile) return 0; - auto pixels = tile->GetRawData(); - if (!pixels) return 0; - - auto size = tile->GetWidth() * tile->GetHeight(); - if (size == 0) return 0; - - // 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; -} - -CCMD(tilecrc) -{ - if (argv.argc() > 1) - { - int tile = atoi(argv[1]); - Printf("%d\n", tileGetCRC32(tile)); - } -} - -//========================================================================== -// -// 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) -{ - FTextureID texid = TexMan.CheckForTexture(fn, ETextureType::Any, FTextureManager::TEXMAN_ForceLookup); - if (!texid.isValid()) return -1; - auto tex = TexMan.GetGameTexture(texid); - - int32_t xsiz = tex->GetTexelWidth(), ysiz = tex->GetTexelHeight(); - - if (xsiz <= 0 || ysiz <= 0) - return -2; - - // create a new game texture here - we want to give it a different name! - tex = MakeGameTexture(tex->GetTexture(), FStringf("#%05d", tilenum), ETextureType::Override); - TileFiles.AddTile(tilenum, tex); - if (istexture) - tileSetHightileReplacement(tilenum, 0, fn, (float)(255 - alphacut) * (1.f / 255.f), 1.0f, 1.0f, 1.0, 1.0); - 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; - int srcxo, srcyo; - FGameTexture* tex; - - if (pal == -1 && tile == source) - { - // Only modify the picanm info. - tex = tileGetTexture(tile); - if (!tex) return; - picanm = &TileFiles.tiledata[tile].picanm; - sourceanm = picanm; - srcxo = tex->GetTexelLeftOffset(0); - srcyo = tex->GetTexelTopOffset(0); - } - else - { - if (source == -1) source = tile; - tex = tileGetTexture(source); - if (!tex) return; - sourceanm = &TileFiles.tiledata[source].picanm; - srcxo = tex->GetTexelLeftOffset(0); - srcyo = tex->GetTexelTopOffset(0); - - TArray buffer = tex->GetTexture()->Get8BitPixels(false); - - if (pal != -1) - { - auto remap = lookups.getTable(pal); - for (auto& pixel : buffer) - { - pixel = remap[pixel]; - } - } - tex = MakeGameTexture(new FImageTexture(new FLooseTile(buffer, tex->GetTexelWidth(), tex->GetTexelHeight())), FStringf("#%05d", tile), ETextureType::Any); - picanm = &TileFiles.tiledata[tile].picanm; - TileFiles.AddTile(tile, tex); - } - - if (xoffset != -1024) srcxo = clamp(xoffset, -128, 127); - if (yoffset != -1024) srcyo = clamp(yoffset, -128, 127); - tex->SetOffsets(srcxo, srcyo); - picanm->sf = (picanm->sf & ~PICANM_MISC_MASK) | (sourceanm->sf & PICANM_MISC_MASK) | flags; -} - -//========================================================================== -// -// -// -//========================================================================== -FImageSource* CreateEmptyTexture(); - -void tileDelete(int tile) -{ - if (TileFiles.locked) - I_FatalError("Modifying tiles after startup is not allowed."); - - // explicitly deleted textures must be unique null textures - auto nulltex = MakeGameTexture(new FImageTexture(CreateEmptyTexture()), FStringf("#%05d", tile), ETextureType::Null); - TileFiles.AddTile(tile, nulltex); - TileFiles.tiledata[tile].replacement = ReplacementType::Art; // whatever this was, now it isn't anymore. - tiletovox[tile] = -1; // clear the link but don't clear the voxel. It may be in use for another tile. - modelManager.UndefineTile(tile); -} - -//========================================================================== -// -// -// -//========================================================================== - -void tileSetDummy(int tile, int width, int height) -{ - if (width == 0 || height == 0) - { - tileDelete(tile); - } - else if (width > 0 && height > 0) - { - FStringf texname("#%05d", tile); - auto dtile = MakeGameTexture(new FImageTexture(new FDummyTile(width, height)), texname, ETextureType::Any); - TileFiles.AddTile(tile, dtile); - } -} - -//========================================================================== -// -// -// -//========================================================================== - -void BuildTiles::CloseAll() -{ - ArtFiles.DeleteAndClear(); -} - -//========================================================================== -// -// Retrieve animation offset -// -//========================================================================== - -int tileAnimateOfs(int tilenum, int randomize) -{ - int framecount = picanm[tilenum].num; - if (framecount > 0) - { - int frametime = !isBlood() ? I_GetBuildTime() : PlayClock; - - if (isBlood() && randomize) - { - frametime += Bcrc32(&randomize, 2, 0); - } - - int curframe = (frametime & 0x7fffffff) >> (picanm[tilenum].speed()); - - switch (picanm[tilenum].type()) - { - case PICANM_ANIMTYPE_FWD: - return curframe % (framecount + 1); - case PICANM_ANIMTYPE_BACK: - return -(curframe % (framecount + 1)); - case PICANM_ANIMTYPE_OSC: - curframe = curframe % (framecount << 1); - if (curframe >= framecount) return (framecount << 1) - curframe; - else return curframe; - } - } - return 0; -} - -//=========================================================================== -// -// Picks a texture for rendering for a given tilenum/palette combination -// -//=========================================================================== - -void tileUpdateAnimations() -{ - for (int i = 0; i < MAXTILES; i++) - { - if (TileFiles.tiledata[i].picanm.type()) - { - int j = i + tileAnimateOfs(i); - - auto id1 = TileFiles.tiledata[i].texture->GetID(); - auto id2 = TileFiles.tiledata[j].texture->GetID(); - TexMan.SetTranslation(id1, id2); - } - } -} - -//=========================================================================== -// -// validates the texture for rendering to a given tilenum -// -//=========================================================================== - -FCanvasTexture* tileGetCanvas(int tilenum) -{ - auto tex = tileGetTexture(tilenum); - if (!tex || !tex->isHardwareCanvas()) return nullptr; - auto canvas = static_cast(tex->GetTexture()); - if (!canvas) return nullptr; - int xsiz = tex->GetTexelWidth(), ysiz = tex->GetTexelHeight(); - if (xsiz <= 0 || ysiz <= 0) - return nullptr; - return canvas; -} - -PicAnm picanm; - - -// wrappers that allow partial migration to a textureID-based setup. -const FTextureID walltype::walltexture() const -{ - return tileGetTextureID(wallpicnum); -} -const FTextureID walltype::overtexture() const -{ - return tileGetTextureID(overpicnum); -} - -const FTextureID sectortype::ceilingtexture() const -{ - return tileGetTextureID(ceilingpicnum); -} - -const FTextureID sectortype::floortexture() const -{ - return tileGetTextureID(floorpicnum); -} - -const FTextureID spritetypebase::spritetexture() const -{ - return tileGetTextureID(picnum); -} - -void sectortype::setfloortexture(FTextureID tex) -{ - auto p = TileFiles.textotile.CheckKey(tex.GetIndex()); - if (p) floorpicnum = *p; -} - -void sectortype::setceilingtexture(FTextureID tex) -{ - auto p = TileFiles.textotile.CheckKey(tex.GetIndex()); - if (p) ceilingpicnum = *p; -} - -void walltype::setwalltexture(FTextureID tex) -{ - auto p = TileFiles.textotile.CheckKey(tex.GetIndex()); - if (p) wallpicnum = *p; -} - -void walltype::setovertexture(FTextureID tex) -{ - auto p = TileFiles.textotile.CheckKey(tex.GetIndex()); - if (p) overpicnum = *p; - else overpicnum = -1; // unlike the others this one can be invalid. -} - -TileOffs* GetHiresOffset(FTextureID tex) -{ - // fixme: This must return nullptr if the tile has no replacement. - auto p = TileFiles.textotile.CheckKey(tex.GetIndex()); - if (p && TileFiles.tiledata[*p].hiofs.xsize != 0) return &TileFiles.tiledata[*p].hiofs; - else return nullptr; -} diff --git a/source/core/textures/buildtiles.h b/source/core/textures/buildtiles.h index 6fb14d490..38872ab1f 100644 --- a/source/core/textures/buildtiles.h +++ b/source/core/textures/buildtiles.h @@ -6,432 +6,102 @@ #include "i_time.h" #include "intvec.h" #include "name.h" +#include "tiletexture.h" +#include "maptypes.h" +#include "texinfo.h" +#include "texturemanager.h" -enum AnimFlags +// all that's left here is the wrappers that need to go away. + + +// wrappers that allow partial migration to a textureID-based setup. +inline const FTextureID walltype::walltexture() const { - 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 + return tileGetTextureID(wallpicnum); +} +inline const FTextureID walltype::overtexture() const { - MAXTILES = 30720, - MAXUSERTILES = (MAXTILES-16) // reserve 16 tiles at the end + return tileGetTextureID(overpicnum); +} -}; - -enum ETexType +inline const FTextureID sectortype::ceilingtexture() const { - TT_INDEXED, - TT_TRUECOLOR, -}; + return tileGetTextureID(ceilingpicnum); +} - -enum class ReplacementType : int +inline const FTextureID sectortype::floortexture() const { - Art, - Writable, - Restorable, - Canvas -}; + return tileGetTextureID(floorpicnum); +} - -// NOTE: If the layout of this struct is changed, loadpics() must be modified -// accordingly. -struct picanm_t +inline const FTextureID spritetypebase::spritetexture() const { - uint16_t num; // animate number - uint8_t sf; // anim. speed and flags - uint8_t extra; + return tileGetTextureID(picnum); +} - void Clear() - { - extra = sf = 0; - num = 0; - } - - int speed() - { - return sf & PICANM_ANIMSPEED_MASK; - } - - int type() - { - return sf & PICANM_ANIMTYPE_MASK; - } - -}; -picanm_t tileConvertAnimFormat(int32_t const picanmdisk, int* lo, int* to); - -class FTileTexture : public FImageSource +inline void sectortype::setfloortexture(FTextureID tex) { -public: - FTileTexture() - { - bUseGamePalette = true; - bTranslucent = false; - } - virtual uint8_t* GetRawData() = 0; - virtual TArray CreatePalettedPixels(int conversion); - virtual int CopyPixels(FBitmap* bmp, int conversion); // This will always ignore 'luminance'. -}; + floorpicnum = legacyTileNum(tex); +} -//========================================================================== -// -// A tile coming from an ART file. -// -//========================================================================== - -class FArtTile : public FTileTexture +inline void sectortype::setceilingtexture(FTextureID tex) { - const TArray& RawPixels; - const uint32_t Offset; -public: - FArtTile(const TArray& backingstore, uint32_t offset, int width, int height) - : RawPixels(backingstore), Offset(offset) - { - Width = width; - Height = height; - } - uint8_t* GetRawData() override final - { - return &RawPixels[Offset]; - } -}; + ceilingpicnum = legacyTileNum(tex); +} -//========================================================================== -// -// A tile with its own pixel buffer -// -//========================================================================== - -class FLooseTile : public FTileTexture +inline void walltype::setwalltexture(FTextureID tex) { - TArray RawPixels; -public: - FLooseTile(TArray& store, int width, int height) - { - RawPixels = std::move(store); - Width = width; - Height = height; - } + wallpicnum = legacyTileNum(tex); +} - uint8_t* GetRawData() override - { - return RawPixels.Data(); - } - -}; - -//========================================================================== -// -// A non-existent tile -// -//========================================================================== - -class FDummyTile : public FTileTexture +inline void walltype::setovertexture(FTextureID tex) { -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 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 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 TileOffs -{ - int xsize, ysize, xoffs, yoffs; -}; - -struct TileDesc -{ - FGameTexture* texture; // the currently active tile - picanm_t picanm; // animation descriptor - ReplacementType replacement; - float alphaThreshold; - int tileflags; - - // Sprite offset hackery for hires replacements. This only gets used for sprites in the 3D view, nothing else. - TileOffs hiofs; - -}; - -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 ArtFiles; - TArray tiledata; - TArray addedArt; - TMap nametoindex; - TMap textotile; - bool locked; // if this is true, no more tile modifications are allowed. - - void addName(const char* name, int index) - { - nametoindex.Insert(name, index); - } - - void lock() - { - locked = true; - // 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); - } - } - } - - void SetAliases(); + if (tex.isValid()) overpicnum = legacyTileNum(tex); + else overpicnum = -1; // unlike the others this one can be invalid. +} - void Init(); // This cannot be a constructor because it needs the texture manager running. - ~BuildTiles() - { - CloseAll(); - } - - void CloseAll(); - - void AddTile(int tilenum, FGameTexture* tex); - - void AddTiles(int firsttile, TArray& store, const char* mapname); - - void AddFile(BuildArtFile* bfd) - { - 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& art) - { - addedArt = std::move(art); - } - - 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); - 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; - +//[[deprecated]] inline int tileForName(const char* name) { - FName nm(name, true); - if (nm == NAME_None) return -1; - auto nmm = TileFiles.nametoindex.CheckKey(nm); - if (!nmm) return -1; - return *nmm; + auto texid = TexMan.CheckForTexture(name, ETextureType::Any, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ReturnAll); + if (!texid.isValid()) return -1; + return legacyTileNum(texid); } // 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) + //[[deprecated]] + const picanm_t& operator[](int index) const { - assert(index < MAXTILES); - return TileFiles.tiledata[index].picanm; + assert((unsigned)index < MAXTILES); + return GetExtInfo(tileGetTextureID(index)).picanm; } }; -extern PicAnm picanm; +inline PicAnm picanm; +//[[deprecated]] inline int tileWidth(int num) { - assert((unsigned)num < MAXTILES); - if ((unsigned)num >= MAXTILES) return 1; - return (int)TileFiles.tiledata[num].texture->GetDisplayWidth(); + auto texid = tileGetTextureID(num); + if (!texid.isValid()) return 1; + else return (int)TexMan.GetGameTexture(texid)->GetDisplayWidth(); } +//[[deprecated]] inline int tileHeight(int num) { - assert((unsigned)num < MAXTILES); - if ((unsigned)num >= MAXTILES) return 1; - return (int)TileFiles.tiledata[num].texture->GetDisplayHeight(); -} - -int tileAnimateOfs(int tilenum, int randomize = -1); - -inline void tileUpdatePicnum(int* const tileptr, bool mayrotate = false, int randomize = -1) -{ - auto& tile = *tileptr; - - if (picanm[tile].type()) - tile += tileAnimateOfs(tile, randomize); + auto texid = tileGetTextureID(num); + if (!texid.isValid()) return 1; + else return (int)TexMan.GetGameTexture(texid)->GetDisplayHeight(); } +//[[deprecated]] inline FGameTexture* tileGetTexture(int tile, bool animate = false) { - assert((unsigned)tile < MAXTILES && tile != -1); // -1 is valid for overpicnum as 'no texture'. - if (tile < 0 || tile >= MAXTILES) return nullptr; - if (animate) tileUpdatePicnum(&tile); - return TileFiles.tiledata[tile].texture; + auto texid = tileGetTextureID(tile); + if (animate) tileUpdatePicnum(texid); + return TexMan.GetGameTexture(texid); } - -inline FTextureID tileGetTextureID(int tile) -{ - if (tile < 0 || tile >= MAXTILES) return FNullTextureID(); - return TileFiles.tiledata[tile].texture->GetID(); -} - -inline int legacyTileNum(FTextureID tex) -{ - auto p = TileFiles.textotile.CheckKey(tex.GetIndex()); - return p ? *p : -1; -} - -void tileUpdateAnimations(); - -const uint8_t* GetRawPixels(FTextureID texid); -uint8_t* GetWritablePixels(FTextureID texid); -void InvalidateTexture(FTextureID num); -TileOffs* GetHiresOffset(FTextureID num); - -class FGameTexture; -bool PickTexture(FGameTexture* tex, int paletteid, TexturePick& pick, bool wantindexed = false); -FCanvasTexture* tileGetCanvas(int tilenum); diff --git a/source/core/textures/hightile.cpp b/source/core/textures/hightile.cpp index e385b0a26..a4de25d19 100644 --- a/source/core/textures/hightile.cpp +++ b/source/core/textures/hightile.cpp @@ -51,6 +51,13 @@ #include "skyboxtexture.h" #include "gamefuncs.h" +enum ETexType +{ + TT_INDEXED, + TT_TRUECOLOR, +}; + + CVARD(Bool, hw_shadeinterpolate, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enable/disable shade interpolation") struct HightileReplacement @@ -244,26 +251,10 @@ void highTileSetup() // //========================================================================== -int tileSetHightileReplacement(int picnum, int palnum, const char* filename, float alphacut, float xscale, float yscale, float specpower, float specfactor, bool indexed) +int tileSetHightileReplacement(int picnum, int palnum, FTextureID texid, float alphacut, float xscale, float yscale, float specpower, float specfactor, bool indexed) { - if ((uint32_t)picnum >= (uint32_t)MAXTILES) return -1; - if ((uint32_t)palnum >= (uint32_t)MAXPALOOKUPS) return -1; - - auto tex = tileGetTexture(picnum); - if (tex->GetTexelWidth() <= 0 || tex->GetTexelHeight() <= 0) - { - Printf("Warning: defined hightile replacement for empty tile %d.", picnum); - return -1; // cannot add replacements to empty tiles, must create one beforehand - } + // assumes the texture was already validated. HightileReplacement replace = {}; - - FTextureID texid = TexMan.CheckForTexture(filename, ETextureType::Any, FTextureManager::TEXMAN_ForceLookup); - if (!texid.isValid()) - { - Printf("%s: Replacement for tile %d does not exist or is invalid\n", filename, picnum); - return -1; - } - replace.image = TexMan.GetGameTexture(texid); replace.alphacut = min(alphacut,1.f); replace.scale = { xscale, yscale }; diff --git a/source/core/textures/skytexture.cpp b/source/core/textures/skytexture.cpp index fd6fe69da..ab2446f96 100644 --- a/source/core/textures/skytexture.cpp +++ b/source/core/textures/skytexture.cpp @@ -40,6 +40,7 @@ #include "printf.h" #include "texturemanager.h" #include "buildtiles.h" +#include "texinfo.h" FGameTexture* GetSkyTexture(int basetile, int lognumtiles, const int16_t *tilemap, int remap) { diff --git a/source/core/textures/texinfo.cpp b/source/core/textures/texinfo.cpp new file mode 100644 index 000000000..a2b7a88b2 --- /dev/null +++ b/source/core/textures/texinfo.cpp @@ -0,0 +1,100 @@ +/* +** texinfo.cpp +** Extended texture information / handling +** +**--------------------------------------------------------------------------- +** Copyright 2019-2022 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 "texturemanager.h" +#include "texinfo.h" +#include "m_crc32.h" + + +//========================================================================== +// +// Retrieve animation offset +// +//========================================================================== + +static int tileAnimateOfs(FTextureID texid, int randomize) +{ + auto ext = GetExtInfo(texid); + int framecount = ext.picanm.num; + if (framecount > 0) + { + int frametime = !isBlood() ? I_GetBuildTime() : PlayClock; + + if (isBlood() && randomize) + { + frametime += Bcrc32(&randomize, 2, 0); + } + + int curframe = (frametime & 0x7fffffff) >> (ext.picanm.speed()); + + switch (ext.picanm.type()) + { + case PICANM_ANIMTYPE_FWD: + return curframe % (framecount + 1); + case PICANM_ANIMTYPE_BACK: + return -(curframe % (framecount + 1)); + case PICANM_ANIMTYPE_OSC: + curframe = curframe % (framecount << 1); + if (curframe >= framecount) return (framecount << 1) - curframe; + else return curframe; + } + } + return 0; +} + +void tileUpdatePicnum(FTextureID& tileptr, int randomize) +{ + tileptr = FSetTextureID(tileptr.GetIndex() + tileAnimateOfs(tileptr, randomize)); +} + +//=========================================================================== +// +// update the animation info inside the texture manager. +// +//=========================================================================== + +void tileUpdateAnimations() +{ + for (unsigned i = 0; i < texExtInfo.Size(); i++) + { + auto& x = texExtInfo[i]; + if (x.picanm.type()) + { + int j = i + tileAnimateOfs(FSetTextureID(i), false); + TexMan.SetTranslation(FSetTextureID(i), FSetTextureID(j)); + } + } +} + + diff --git a/source/core/textures/texinfo.h b/source/core/textures/texinfo.h new file mode 100644 index 000000000..ec30bfa1f --- /dev/null +++ b/source/core/textures/texinfo.h @@ -0,0 +1,122 @@ +#pragma once + +#include +#include "gamefuncs.h" +#include "tiletexture.h" + +// extended texture info for which there is no room in the texture manager. + +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 +}; + + + +// 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; + } + + int speed() const + { + return sf & PICANM_ANIMSPEED_MASK; + } + + int type() const + { + return sf & PICANM_ANIMTYPE_MASK; + } + +}; + + +struct TileOffs +{ + int16_t xsize, ysize, xoffs, yoffs; +}; + +struct TexExtInfo +{ + // TexAnim *texanim // todo: extended texture animation like ZDoom's ANIMDEFS. + uint8_t terrain; // Contents depend on the game, e.g. this holds Blood's surfType. + uint8_t shadeinfo; // Blood's shade.dat + uint16_t voxindex; + picanm_t picanm; + uint32_t flags; // contents are game dependent. + TileOffs hiofs; +}; + +inline TArray texExtInfo; +inline int firstarttile, maxarttile; // we need this for conversion between tile numbers and texture IDs + +//========================================================================== +// +// THe tile container +// +//========================================================================== + +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? +}; +bool PickTexture(FGameTexture* tex, int paletteid, TexturePick& pick, bool wantindexed = false); + + + +void tileUpdatePicnum(FTextureID& tileptr, int randomize = -1); +void tileUpdateAnimations(); + + +inline const TexExtInfo& GetExtInfo(FTextureID tex) // this is only for reading, not for modifying! +{ + unsigned index = tex.GetIndex(); + if (index >= texExtInfo.Size()) index = 0; // index 0 (the null texture) serves as backup if textures get added at runtime. + return texExtInfo[index]; +} + +inline FTextureID tileGetTextureID(int tilenum) +{ + if ((unsigned)tilenum >= MAXTILES) return FNullTextureID(); + return FSetTextureID(firstarttile + min(tilenum, maxarttile)); +} + +// Use this only for places where some legacy feature needs a tile index. The only such places are CON and nnext. +inline int legacyTileNum(FTextureID id) +{ + int index = id.GetIndex() - firstarttile; + if (index < 0 || index > maxarttile) return maxarttile; + return index; +} + +inline const TileOffs* GetHiresOffset(FTextureID tex) +{ + // fixme: This must return nullptr if the tile has no replacement. + auto& x = GetExtInfo(tex); + if (x.hiofs.xsize != 0) return &x.hiofs; + else return nullptr; +} + diff --git a/source/core/textures/tilesetbuilder.cpp b/source/core/textures/tilesetbuilder.cpp new file mode 100644 index 000000000..06e3137fc --- /dev/null +++ b/source/core/textures/tilesetbuilder.cpp @@ -0,0 +1,315 @@ +/* +** tilesetbuilder.cpp +** Constructs the full tile set and adds it to the texture manager. +** +**--------------------------------------------------------------------------- +** Copyright 2019-2022 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 "image.h" +#include "texturemanager.h" +#include "m_crc32.h" +#include "c_dispatch.h" +#include "tiletexture.h" +#include "tilesetbuilder.h" +#include "gamecontrol.h" +#include "startupinfo.h" +#include "printf.h" +#include "m_argv.h" +#include "gamestruct.h" + +const char* G_DefFile(void); +void loaddefinitionsfile(TilesetBuildInfo& info, const char* fn, bool cumulative = false, bool maingrp = false); + + +//========================================================================== +// +// Returns checksum for a given tile or texture +// +//========================================================================== + +int32_t tileGetCRC32(FImageSource* image) +{ + if (image == nullptr) return 0; + auto pixels = image->GetPalettedPixels(0); + if (pixels.Size() == 0) return 0; + + // To get proper CRCs as the calling code expects we need to put the translucent index back to 255. + for (auto& p : pixels) + { + if (p == 0) p = 255; + else if (p == 255) p = 0; + } + + return crc32(0, (const Bytef*)pixels.Data(), pixels.Size()); +} + +CCMD(tilecrc) +{ + if (argv.argc() > 1) + { + char* p; + int tile = strtol(argv[1], &p, 10); + FGameTexture* tex; + if (tile >= 0 && tile < MAXTILES && !*p) + { + // tex = tileGetTexture(tile); + } + else + { + tex = TexMan.FindGameTexture(argv[1], ETextureType::Any); + } + auto img = tex? tex->GetTexture()->GetImage() : nullptr; + if (!img) Printf("%s: not a valid texture", argv[1]); + else Printf("%d\n", tileGetCRC32(img)); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +static void LoadDefinitions(TilesetBuildInfo& info) +{ + const char* defsfile = G_DefFile(); + FString razedefsfile = defsfile; + razedefsfile.Substitute(".def", "-raze.def"); + + loaddefinitionsfile(info, "engine/engine.def", true, true); // Internal stuff that is required. + + // check what we have. + // user .defs override the default ones and are not cumulative. + // if we fine even one Raze-specific file, all of those will be loaded cumulatively. + // otherwise the default rules inherited from older ports apply. + if (userConfig.UserDef.IsNotEmpty()) + { + loaddefinitionsfile(info, userConfig.UserDef, false); + } + else + { + if (fileSystem.FileExists(razedefsfile)) + { + loaddefinitionsfile(info, razedefsfile, true); + } + else if (fileSystem.FileExists(defsfile)) + { + loaddefinitionsfile(info, defsfile, false); + } + } + + if (userConfig.AddDefs) + { + for (auto& m : *userConfig.AddDefs) + { + loaddefinitionsfile(info, m, false); + } + userConfig.AddDefs.reset(); + } + + if (GameStartupInfo.def.IsNotEmpty()) + { + loaddefinitionsfile(info, GameStartupInfo.def); // Stuff from gameinfo. + } + + // load the widescreen replacements last. This ensures that mods still get the correct CRCs for their own tile replacements. + if (fileSystem.FindFile("engine/widescreen.def") >= 0 && !Args->CheckParm("-nowidescreen")) + { + loaddefinitionsfile(info, "engine/widescreen.def"); + } + fileSystem.InitHashChains(); // make sure that any resources that got added can be found again. +} + +//========================================================================== +// +// +// +//========================================================================== + +picanm_t tileConvertAnimFormat(int32_t const picanimraw) +{ + // Unpack a 4 byte packed anim descriptor into something more accessible + picanm_t anm; + anm.num = picanimraw & 63; + anm.sf = ((picanimraw >> 24) & 15) | (picanimraw & 192); + anm.extra = (picanimraw >> 28) & 15; + return anm; +} + +//========================================================================== +// +// +// +//========================================================================== +FImageSource* createWritableTile(int width, int height); +FImageSource* makeTileWritable(FImageSource* img); + +void TilesetBuildInfo::MakeWritable(int tileno) +{ + if (tile[tileno].tileimage != nullptr) + { + auto newtex = makeTileWritable(tile[tileno].tileimage); + tile[tileno].tileimage = newtex; + tile[tileno].imported = nullptr; + } +} + +void TilesetBuildInfo::CreateWritable(int tileno, int w, int h) +{ + auto newtex = createWritableTile(w, h); + tile[tileno].tileimage = newtex; + tile[tileno].imported = nullptr; +} + +//=========================================================================== +// +// MakeCanvas +// +// Turns texture into a canvas (i.e. camera texture) +// +//=========================================================================== + +void TilesetBuildInfo::MakeCanvas(int tilenum, int width, int height) +{ + auto ftex = new FCanvasTexture(width * 4, height * 4); + ftex->aspectRatio = (float)width / height; + auto canvas = MakeGameTexture(ftex, FStringf("#%05d", tilenum), ETextureType::Any); + canvas->SetSize(width * 4, height * 4); + canvas->SetDisplaySize((float)width, (float)height); + canvas->GetTexture()->SetSize(width * 4, height * 4); + tile[tilenum].imported = canvas; + tile[tilenum].tileimage = nullptr; +} + +//========================================================================== +// +// +// +//========================================================================== + +void ConstructTileset() +{ + TilesetBuildInfo info; + TArray images; + TArray rawpicanm; + GetArtImages(images, rawpicanm); + info.tile.Resize(MAXTILES); + memset(info.tile.Data(), 0, info.tile.Size() * sizeof(info.tile[0])); + // fill up the arrays to the maximum allowed but remember the highest original number. + for (unsigned i = 0; i < images.Size(); i++) + { + info.tile[i].orgimage = info.tile[i].tileimage = images[i]; + if (images[i]) + { + auto s = images[i]->GetOffsets(); + info.tile[i].leftOffset = s.first; + info.tile[i].topOffset = s.second; + } + info.tile[i].extinfo.picanm = tileConvertAnimFormat(rawpicanm[i]); + } + images.Reset(); + rawpicanm.Reset(); + for (auto& a : info.tile) a.alphathreshold = 0.5f; + gi->LoadTextureInfo(info); // initialize game data that must be done before loading .DEF + LoadDefinitions(info); + gi->SetupSpecialTextures(info); // initialize game data that needs .DEF being processed. + + // now that everything has been set up, we can add the textures to the texture manager. + // To keep things simple everything from .ART files and its replacements will remain in order and + // converting between a Build tilenum and a texture ID can done with a single addition. + // Even though this requires adding quite a few empty textures to the texture manager, it makes things a lot easier, + // because it ensures an unambiguous mapping and allows communicating with features that only work with tile numbers. + // as long as no named textures are used. + + auto nulltex = TexMan.GameByIndex(0); // Use the null texture's backing data for all empty placeholders. + // Only the outward facing FGameTexture needs to be different + + firstarttile = TexMan.NumTextures(); + maxarttile = MAXTILES - 1; + while (maxarttile >= 0 && info.tile[maxarttile].tileimage == nullptr) maxarttile--; + if (maxarttile < 0) return; // should never happen, but who knows - maybe someone will make a game without ART files later... :D + maxarttile++; // create a placeholder in the first unused spot. This will later get used for all out of range tile numbers. + + int lastid = firstarttile - 1; + for (int i = 0; i <= maxarttile; i++) + { + FTexture* ftex = nullptr; + FGameTexture* gtex; + FStringf tname("#%05d", i); + if (info.tile[i].tileimage == nullptr) + { + if (info.tile[i].imported == nullptr) + { + ftex = nulltex->GetTexture(); + gtex = MakeGameTexture(ftex, tname, ETextureType::Null); + } + else + { + // Canvas textures can be used directly without wrapping them again. + gtex = info.tile[i].imported; + } + } + else + { + if (info.tile[i].imported) ftex = info.tile[i].imported->GetTexture(); + else ftex = new FImageTexture(info.tile[i].tileimage); + gtex = MakeGameTexture(ftex, tname, ETextureType::Any); + gtex->SetOffsets(info.tile[i].leftOffset, info.tile[i].topOffset); + } + if (info.tile[i].extinfo.picanm.sf & PICANM_NOFULLBRIGHT_BIT) + { + gtex->SetDisableFullbright(true); + } + + auto id = TexMan.AddGameTexture(gtex, true); + if (id.GetIndex() != lastid + 1) + { + // this should never happen unless the texture manager gets redone in an incompatible fashion. + I_FatalError("Unable to assign consecutive texture IDs to tile set."); + } + lastid = id.GetIndex(); + } + // Now create the extended info. This will leave room for all regular textures as well so that later code can assign info to these, too. + // Textures being added afterward will always see the default extinfo, even if they are not covered by this array. + texExtInfo.Resize(TexMan.NumTextures()); + memset(texExtInfo.Data(), 0, sizeof(texExtInfo[0]) * texExtInfo.Size()); + for (auto& x : texExtInfo) x.voxindex = -1; + // now copy all extinfo stuff that got parsed by .DEF or some game specific setup. + for (int i = 0; i <= maxarttile; i++) + { + texExtInfo[i + firstarttile] = info.tile[i].extinfo; + } + + for (auto& a : info.aliases) + { + TexMan.AddAlias(a.first.GetChars(), a.second + firstarttile); + } +} diff --git a/source/core/textures/tilesetbuilder.h b/source/core/textures/tilesetbuilder.h new file mode 100644 index 000000000..596b8c452 --- /dev/null +++ b/source/core/textures/tilesetbuilder.h @@ -0,0 +1,44 @@ +#pragma once + +#include "texinfo.h" + +struct TileBuildDesc +{ + // persistent data that will be present for the entire run of the game. + TexExtInfo extinfo; + + // work data + FImageSource* orgimage; // this is the original tile image, not connected to a FTexture yet. + FImageSource* tileimage; // this is the current tile image, it may be connected to 'imported'. Used for convenient access. + FGameTexture* imported; // imported replacement from the texture manager; + + // info that will be copied into the final texture object + float alphathreshold; + int leftOffset, topOffset; // overrides for imported textures. +}; + +struct TilesetBuildInfo +{ + TArray tile; + unsigned maxtileofart; + TArray > aliases; + void addName(const char* name, int index) + { + aliases.Push(std::make_pair(name, index)); + } + + void Delete(int tileno) + { + tile[tileno].tileimage = nullptr; + tile[tileno].imported = nullptr; + } + + void MakeWritable(int tile); + void CreateWritable(int tile, int w, int h); + void MakeCanvas(int tilenum, int width, int height); + +}; + +void ConstructTileset(); +int32_t tileGetCRC32(FImageSource* image); + diff --git a/source/core/textures/tiletexture.cpp b/source/core/textures/tiletexture.cpp new file mode 100644 index 000000000..bed8df7b9 --- /dev/null +++ b/source/core/textures/tiletexture.cpp @@ -0,0 +1,525 @@ +/* +** tiletexture.cpp +** Image sources and file management for Build tile textures +** +**--------------------------------------------------------------------------- +** Copyright 2019-2022 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 "i_time.h" +#include "zstring.h" +#include "tiletexture.h" +#include "texturemanager.h" +#include "filesystem.h" +#include "printf.h" + + +class FTileTexture : public FImageSource +{ +public: + FTileTexture() noexcept + { + bUseGamePalette = true; + bTranslucent = false; + } + virtual uint8_t* GetRawData() = 0; + virtual TArray 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& RawPixels; + const uint32_t Offset; +public: + FArtTile(const TArray& backingstore, uint32_t offset, int width, int height) noexcept + : RawPixels(backingstore), Offset(offset) + { + Width = width; + Height = height; + } + uint8_t* GetRawData() override final + { + return &RawPixels[Offset]; + } +}; + +//========================================================================== +// +// 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 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; + } + } + + virtual void Reload() {} + +}; + +//========================================================================== +// +// 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() override + { + buffer = Base->GetPalettedPixels(0); + } +}; + + +//========================================================================== +// +// Base class for Build tile textures +// This needs a few subclasses for different use cases. +// +//========================================================================== + +int FTileTexture::CopyPixels(FBitmap* bmp, int conversion) +{ + TArray buffer; + auto ppix = GetRawData(); + if (ppix) + { + bmp->CopyPixelData(0, 0, ppix, Width, Height, Height, 1, 0, GPalette.BaseColors); + } + return 0; +} + +TArray FTileTexture::CreatePalettedPixels(int conversion) +{ + TArray buffer(Width * Height, true); + auto p = GetRawData(); + if (p) memcpy(buffer.Data(), p, buffer.Size()); + else memset(buffer.Data(), 0, buffer.Size()); + return buffer; +} + +//========================================================================== +// +// raw pixel cache. This is for accessing pixel data in the game code, +// not for rendering. +// +//========================================================================== + +struct RawCacheNode +{ + TArray data; + uint64_t lastUseTime; + + RawCacheNode() = default; + RawCacheNode(const RawCacheNode& other) = default; + RawCacheNode& operator=(const RawCacheNode& other) = default; + + RawCacheNode(RawCacheNode&& other) noexcept + { + data = std::move(other.data); + lastUseTime = other.lastUseTime; + } + + RawCacheNode& operator=(RawCacheNode&& other) noexcept + { + data = std::move(other.data); + lastUseTime = other.lastUseTime; + return *this; + } +}; + +//========================================================================== +// +// access raw pixel data for in-game checking. Works for all texture types. +// +//========================================================================== +static TMap CacheNodes; + +const uint8_t* GetRawPixels(FTextureID texid) +{ + if (!texid.isValid()) return nullptr; + auto gtex = TexMan.GetGameTexture(texid); + auto tex = dynamic_cast(gtex->GetTexture()); + + if (!tex || !tex->GetImage()) return nullptr; + auto img = tex->GetImage(); + auto timg = dynamic_cast(img); + if (!timg || !timg->GetRawData()) + { + auto cache = CacheNodes.CheckKey(texid.GetIndex()); + if (cache) + { + cache->lastUseTime = I_nsTime(); + return cache->data.Data(); + } + RawCacheNode newnode; + newnode.data = img->GetPalettedPixels(0); + newnode.lastUseTime = I_nsTime(); + auto retval =newnode.data.Data(); + CacheNodes.Insert(texid.GetIndex(), std::move(newnode)); + return retval; + } + else + { + return timg->GetRawData(); + } +} + +//========================================================================== +// +//To use this the texture must have been made writable during texture init. +// +//========================================================================== + +uint8_t* GetWritablePixels(FTextureID texid, bool reload) +{ + if (!texid.isValid()) return nullptr; + auto gtex = TexMan.GetGameTexture(texid); + auto tex = dynamic_cast(gtex->GetTexture()); + + if (!tex || !tex->GetImage()) return nullptr; + auto timg = dynamic_cast(tex->GetImage()); + if (!timg) return nullptr; + + if (reload) + timg->Reload(); + + gtex->CleanHardwareData(); // we can safely assume that this only gets called when the texture is about to be changed. + return timg->GetRawData(); +} + + +//========================================================================== +// +// +// +//========================================================================== + +static FImageSource* GetTileImage(const TArray& backingstore, uint32_t offset, int width, int height, TArray freelist) +{ + FImageSource* tex; + + void* mem = nullptr; + if (freelist.Size() > 0) freelist.Pop(mem); + // recycle discarded image sources if available. They are all the same type so this is safe. + if (mem) tex = new(mem) FArtTile(backingstore, offset, width, height); + else tex = new FArtTile(backingstore, offset, width, height); + 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; + } + return tex; +} + +//=========================================================================== +// +// Creates image sources for all valid tiles in one ART file. +// In case this overwrites older tiles, these get added to a freelist +// and reused later +// +//=========================================================================== + +static void GetImagesFromFile(TArray& array, TArray& picanmarray, TArray freelist, TArray& RawData) +{ + const uint8_t* tiles = RawData.Data(); + // int numtiles = LittleLong(((uint32_t *)tiles)[1]); // This value is not reliable + unsigned tilestart = LittleLong(((unsigned*)tiles)[2]); + unsigned tileend = LittleLong(((unsigned*)tiles)[3]); + 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]; + + unsigned oldsize = array.Size(); + if (array.Size() < tileend + 1) + array.Resize(tileend + 1); + + // picanm info needs to be stashed aside for later use, except for the offsets which are written into the image object + if (picanmarray.Size() < tileend + 1) + picanmarray.Resize(tileend + 1); + + // If there's a gap, initialize it. + for (unsigned i = oldsize; i < tilestart; i++) + { + array[i] = nullptr; + picanmarray[i] = {}; + } + + for (unsigned 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; + + picanmarray[i] = picanm[pic]; // this must also be retained for invalid tiles. + if (width <= 0 || height <= 0) + { + // If an existing tile is discarded, add it to the free list + if (array[i] && i < oldsize) + { + array[i]->~FImageSource(); // really a no-op but let's stay on the safe side. + freelist.Push(array[i]); + } + array[i] = nullptr; + continue; + } + + FString texname; + auto tex = GetTileImage(RawData, uint32_t(tiledata - tiles), width, height, freelist); + int leftoffset = (int8_t)((anm >> 8) & 255); + int topoffset = (int8_t)((anm >> 16) & 255); + tex->SetOffsets(leftoffset, topoffset); + tiledata += size; + array[i] = tex; + } +} + +//=========================================================================== +// +// CountTiles +// +// Returns the number of tiles provided by an artfile +// +//=========================================================================== + +int CountTiles(const char* fn, const uint8_t* RawData) +{ + int version = LittleLong(*(uint32_t*)RawData); + if (version != 1) + { + Printf("%s: Invalid art file version. Must be 1, got %d\n", fn, version); + return 0; + } + + int tilestart = LittleLong(((uint32_t*)RawData)[2]); + int tileend = LittleLong(((uint32_t*)RawData)[3]); + + if ((unsigned)tilestart >= MAXUSERTILES || (unsigned)tileend >= MAXUSERTILES) + { + Printf("%s: Invalid tilestart or tileend\n", fn); + return 0; + } + if (tileend < tilestart) + { + Printf("%s: tileend < tilestart\n", fn); + return 0; + } + + return tileend >= tilestart ? tileend - tilestart + 1 : 0; +} + +//=========================================================================== +// +// LoadArtFile +// +// Returns the number of tiles found. +// +// let's load everything into memory on startup. +// +//=========================================================================== + +static void AddArtFile(const FString& filename) +{ + FileReader fr = fileSystem.OpenFileReader(filename); + if (fr.isOpen()) + { + auto artdata = fr.Read(); + if (artdata.Size() > 16) + { + if (memcmp(artdata.Data(), "BUILDART", 8) == 0) + { + artdata.Delete(0, 8); + } + // Only load the data if the header is present + if (CountTiles(filename, artdata.Data()) > 0) + { + // The texture manager already has a store for Build ART files, so let's use it. :) + auto& store = TexMan.GetNewBuildTileData(); + store = std::move(artdata); + } + } + } +} + +//=========================================================================== +// +// Loads an art set but does not do anything with the tiles yet. +// +//=========================================================================== + +void InitArtFiles(TArray& addedArt) +{ + const int MAXARTFILES_BASE = 200; + + for (int index = 0; index < MAXARTFILES_BASE; index++) + { + FStringf fn("tiles%03d.art", index); + AddArtFile(fn); + } + for (auto& addart : addedArt) + { + AddArtFile(addart); + } +} + +//=========================================================================== +// +// Creates image sources for all valid tiles in the ART set. +// +//=========================================================================== + +void GetArtImages(TArray& array, TArray& picanm) +{ + TArray freelist; + for (auto& f : TexMan.GetBuildTileDataStore()) + { + GetImagesFromFile(array, picanm, freelist, f); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +FImageSource* createDummyTile(int width, int height) +{ + if (width > 0 && height > 0) + return new FDummyTile(width, height); + else + return nullptr; +} + +//========================================================================== +// +// +// +//========================================================================== + +FImageSource* createWritableTile(int width, int height) +{ + if (width > 0 && height > 0) + { + auto tile = new FWritableTile; + tile->ResizeImage(width, height); + return tile; + } + else + return nullptr; +} + +//========================================================================== +// +// +// +//========================================================================== + +FImageSource* makeTileWritable(FImageSource* img) +{ + return new FRestorableTile(img); +} + diff --git a/source/core/textures/tiletexture.h b/source/core/textures/tiletexture.h new file mode 100644 index 000000000..957f43eca --- /dev/null +++ b/source/core/textures/tiletexture.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "image.h" +#include "textureid.h" +#include "tarray.h" + +const uint8_t* GetRawPixels(FTextureID texid); +uint8_t* GetWritablePixels(FTextureID texid, bool reload = false); +FImageSource* createDummyTile(int width, int height); +void InitArtFiles(TArray& addart); +void GetArtImages(TArray& array, TArray& picanm); + +enum +{ + MAXTILES = 30720, + MAXUSERTILES = (MAXTILES - 16) // reserve 16 tiles at the end +}; diff --git a/source/core/vmexports.cpp b/source/core/vmexports.cpp index a2781333c..4edfcbd75 100644 --- a/source/core/vmexports.cpp +++ b/source/core/vmexports.cpp @@ -38,6 +38,9 @@ #include "gamefuncs.h" #include "raze_sound.h" #include "texturemanager.h" +#include "texinfo.h" + +#include "buildtiles.h" sectortype* Raze_updatesector(double x, double y, sectortype* sec, double dist) { @@ -478,7 +481,7 @@ int sector_checktexture(sectortype* sec, int place, int intname) { if (!sec) ThrowAbortException(X_READ_NIL, nullptr); - auto tex = TexMan.CheckForTexture(FName(ENamedName(intname)).GetChars(), ETextureType::Any); + auto tex = TexMan.CheckForTexture(FName(ENamedName(intname)).GetChars(), ETextureType::Any, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ReturnAll); return tex == (place == 0 ? sec->ceilingtexture() : sec->floortexture()); } diff --git a/source/games/blood/src/blood.cpp b/source/games/blood/src/blood.cpp index 53342ef72..f0c195df2 100644 --- a/source/games/blood/src/blood.cpp +++ b/source/games/blood/src/blood.cpp @@ -48,6 +48,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "texturemanager.h" #include "statusbar.h" #include "vm.h" +#include "tilesetbuilder.h" BEGIN_BLD_NS @@ -74,6 +75,7 @@ IMPLEMENT_POINTERS_END // //--------------------------------------------------------------------------- void MarkSprInSect(); +void tileInitProps(); size_t DBloodActor::PropagateMark() { @@ -585,6 +587,9 @@ void GameInterface::loadPalette(void) void GameInterface::app_init() { + mirrortile = tileGetTextureID(504); + tileInitProps(); + GC::AddMarkerFunc(markgcroots); InitCheats(); @@ -717,7 +722,11 @@ enum DEFINE_ACTION_FUNCTION(_Blood, OriginalLoadScreen) { static int bLoadScreenCrcMatch = -1; - if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC; + if (bLoadScreenCrcMatch == -1) + { + auto tex = tileGetTexture(kLoadScreen)->GetTexture()->GetImage(); // if this is invalid we have a bigger problem on our hand than the inevitable crash. + bLoadScreenCrcMatch = tileGetCRC32(tex) == kLoadScreenCRC; + } ACTION_RETURN_INT(bLoadScreenCrcMatch); } diff --git a/source/games/blood/src/blood.h b/source/games/blood/src/blood.h index 23bf7a935..e2309f307 100644 --- a/source/games/blood/src/blood.h +++ b/source/games/blood/src/blood.h @@ -142,8 +142,8 @@ struct GameInterface : public ::GameInterface void processSprites(tspriteArray& tsprites, const DVector3& view, DAngle viewang, double interpfrac) override; void EnterPortal(DCoreActor* viewer, int type) override; void LeavePortal(DCoreActor* viewer, int type) override; - void LoadGameTextures() override; - void SetupSpecialTextures() override; + void LoadTextureInfo(TilesetBuildInfo& info) override; + void SetupSpecialTextures(TilesetBuildInfo&) override; int GetCurrentSkill() override; bool IsQAVInterpTypeValid(const FString& type) override; void AddQAVInterpProps(const int res_id, const FString& interptype, const bool loopable, const TMap>&& ignoredata) override; diff --git a/source/games/blood/src/hudsprites.cpp b/source/games/blood/src/hudsprites.cpp index ea6aa0179..0e3f41f75 100644 --- a/source/games/blood/src/hudsprites.cpp +++ b/source/games/blood/src/hudsprites.cpp @@ -89,7 +89,10 @@ static void viewBurnTime(int gScale) for (int i = 0; i < 9; i++) { int nTile = burnTable[i].nTile; - tileUpdatePicnum(&nTile); + // This is needed because hud_drawsprite still works with tilenums. + FTextureID nID = tileGetTextureID(nTile); + tileUpdatePicnum(nID); + nTile = legacyTileNum(nID); int nScale = burnTable[i].nScale; if (gScale < 600) { diff --git a/source/games/blood/src/misc.h b/source/games/blood/src/misc.h index c01860965..e463f4b92 100644 --- a/source/games/blood/src/misc.h +++ b/source/games/blood/src/misc.h @@ -24,6 +24,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "m_fixed.h" #include "filesystem.h" +#include "texinfo.h" + +#include "buildtiles.h" BEGIN_BLD_NS diff --git a/source/games/blood/src/tile.cpp b/source/games/blood/src/tile.cpp index a661022ad..d445c6938 100644 --- a/source/games/blood/src/tile.cpp +++ b/source/games/blood/src/tile.cpp @@ -28,6 +28,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "build.h" #include "blood.h" +#include "hw_voxels.h" +#include "tilesetbuilder.h" BEGIN_BLD_NS @@ -40,16 +42,20 @@ static int8_t tileShade[kMaxTiles]; short voxelIndex[kMaxTiles]; #define x(a, b) registerName(#a, b); -static void SetTileNames() +static void SetTileNames(TilesetBuildInfo& info) { - auto registerName = [](const char* name, int index) + auto registerName = [&](const char* name, int index) { - TileFiles.addName(name, index); + info.addName(name, index); }; #include "namelist.h" - // Oh Joy! Plasma Pak changes the tile number of the title screen, but we preferably want mods that use the original one to display it. + // Oh Joy! Plasma Pak changes the tile number of the title screen, but we preferably want mods that use the original one to display it, e.g. Cryptic Passage // So let's make this remapping depend on the CRC. - if (tileGetCRC32(2518) == 1170870757 && (tileGetCRC32(2046) != 290208654 || tileWidth(2518) == 0)) registerName("titlescreen", 2046); + const int OTITLE = 2046, PTITLE = 2518; + auto& orgtitle = info.tile[OTITLE]; + auto& pptile = info.tile[PTITLE]; + + if (tileGetCRC32(pptile.tileimage) == 1170870757 && (tileGetCRC32(orgtitle.tileimage) != 290208654 || pptile.tileimage->GetWidth() == 0)) registerName("titlescreen", 2046); else registerName("titlescreen", 2518); } #undef x @@ -60,7 +66,7 @@ static void SetTileNames() // //--------------------------------------------------------------------------- -void GameInterface::LoadGameTextures() +void GameInterface::LoadTextureInfo(TilesetBuildInfo& info) { auto hFile = fileSystem.OpenFileReader("SURFACE.DAT"); if (hFile.isOpen()) @@ -86,16 +92,19 @@ void GameInterface::LoadGameTextures() if (voxelIndex[i] >= 0 && voxelIndex[i] < MAXVOXELS) voxreserve.Set(voxelIndex[i]); } - SetTileNames(); } -void GameInterface::SetupSpecialTextures() +void GameInterface::SetupSpecialTextures(TilesetBuildInfo& info) { + SetTileNames(info); // set up all special tiles here, before we fully hook up with the texture manager. - tileDelete(504); - TileFiles.tileMakeWritable(2342); - TileFiles.lock(); // from this point on the tile<->texture associations may not change anymore. - mirrortile = tileGetTextureID(504); + info.Delete(504); + info.MakeWritable(2342); + +} + +void tileInitProps() +{ for (int i = 0; i < MAXTILES; i++) { auto tex = tileGetTexture(i); @@ -106,7 +115,6 @@ void GameInterface::SetupSpecialTextures() } } } - //--------------------------------------------------------------------------- // // diff --git a/source/games/duke/src/actors_lava.cpp b/source/games/duke/src/actors_lava.cpp index b604360e2..c9ef08a81 100644 --- a/source/games/duke/src/actors_lava.cpp +++ b/source/games/duke/src/actors_lava.cpp @@ -33,6 +33,8 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms #include "dukeactor.h" #include "interpolate.h" +#include "buildtiles.h" + BEGIN_DUKE_NS static int torchcnt; diff --git a/source/games/duke/src/animatesprites.cpp b/source/games/duke/src/animatesprites.cpp index 9ab7d670b..d216557bd 100644 --- a/source/games/duke/src/animatesprites.cpp +++ b/source/games/duke/src/animatesprites.cpp @@ -32,6 +32,8 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms #include "gamefuncs.h" #include "models/modeldata.h" +#include "buildtiles.h" + BEGIN_DUKE_NS void drawshadows(tspriteArray& tsprites, tspritetype* t, DDukeActor* h) diff --git a/source/games/duke/src/constants.h b/source/games/duke/src/constants.h index 2b1ac4917..fd54c226b 100644 --- a/source/games/duke/src/constants.h +++ b/source/games/duke/src/constants.h @@ -26,7 +26,10 @@ enum GPSPEED = 10, FOF = 13, - TILE_VIEWSCR = (MAXTILES-5) + // hack alert! CYCLER is free for use here as all items of this type get destroyed right on map spawn and this value never gets checked anywhere else. + // This avoids overallocation of empty tile slots as a high value slightly below the tile limit would do. + // Once we can do texture management without tile numbers this can be done as a regular texture without a valid tile index. + TILE_VIEWSCR = CYCLER }; diff --git a/source/games/duke/src/duke3d.h b/source/games/duke/src/duke3d.h index 828765c8b..43f5b70dc 100644 --- a/source/games/duke/src/duke3d.h +++ b/source/games/duke/src/duke3d.h @@ -24,8 +24,7 @@ struct GameInterface : public ::GameInterface const char* Name() override { return "Duke"; } void app_init() override; void loadPalette() override; - void LoadGameTextures() override; - void SetupSpecialTextures() override; + void SetupSpecialTextures(TilesetBuildInfo& info) override; void clearlocalinputstate() override; bool GenerateSavePic() override; void PlayHudSound() override; diff --git a/source/games/duke/src/game.cpp b/source/games/duke/src/game.cpp index c109e0797..d8e8564c0 100644 --- a/source/games/duke/src/game.cpp +++ b/source/games/duke/src/game.cpp @@ -43,6 +43,7 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au) #include "psky.h" #include "vm.h" #include "thingdef.h" +#include "tilesetbuilder.h" BEGIN_DUKE_NS @@ -281,11 +282,11 @@ static void setupbackdrop() #define x(a, b) registerName(#a, b); #define y(a, b) registerName(#a, b); -static void SetTileNames() +static void SetTileNames(TilesetBuildInfo& info) { - auto registerName = [](const char* name, int index) + auto registerName = [&](const char* name, int index) { - TileFiles.addName(name, index); + info.addName(name, index); }; if (!isRR()) { @@ -299,31 +300,29 @@ static void SetTileNames() #undef x #undef y -void GameInterface::LoadGameTextures() -{ - SetTileNames(); -} - -void GameInterface::SetupSpecialTextures() +void GameInterface::SetupSpecialTextures(TilesetBuildInfo& info) { + SetTileNames(info); // set up all special tiles here, before we fully hook up with the texture manager. - tileDelete(FOF); // portal marker + info.Delete(FOF); // portal marker + + FImageSource* viewscreen; if (!isRR()) { - tileDelete(560); // the mirror tile. - TileFiles.MakeCanvas(TILE_VIEWSCR, tileWidth(502), tileHeight(502)); + info.Delete(560); // the mirror tile. + viewscreen = info.tile[502].tileimage; } else { - tileDelete(1089); // the mirror tile. - tileDelete(0); // RR uses this as an empty texture - TileFiles.tileMakeWritable(2025); // bowling lane pin displays - TileFiles.tileMakeWritable(2026); - TileFiles.tileMakeWritable(2027); - TileFiles.tileMakeWritable(2028); - TileFiles.MakeCanvas(TILE_VIEWSCR, tileWidth(1055), tileHeight(1055)); // not used by the game but all the support code is present, meaning maps are free to use it. + info.Delete(1089); // the mirror tile. + info.Delete(0); // RR uses this as an empty texture + info.MakeWritable(2025); // bowling lane pin displays + info.MakeWritable(2026); + info.MakeWritable(2027); + info.MakeWritable(2028); + viewscreen = info.tile[1055].tileimage; } - TileFiles.lock(); // from this point on the tile<->texture associations may not change anymore. + info.MakeCanvas(TILE_VIEWSCR, viewscreen? viewscreen->GetWidth() : 128, viewscreen? viewscreen->GetHeight() : 128); } diff --git a/source/games/duke/src/global.h b/source/games/duke/src/global.h index bbbf662ac..7c6744976 100644 --- a/source/games/duke/src/global.h +++ b/source/games/duke/src/global.h @@ -8,6 +8,7 @@ #include "types.h" #include "d_net.h" #include "serialize_obj.h" +#include "tiletexture.h" BEGIN_DUKE_NS diff --git a/source/games/duke/src/hudweapon_d.cpp b/source/games/duke/src/hudweapon_d.cpp index 5b91e8aca..ff374c813 100644 --- a/source/games/duke/src/hudweapon_d.cpp +++ b/source/games/duke/src/hudweapon_d.cpp @@ -36,6 +36,7 @@ source as it is released. #include "global.h" #include "names_d.h" #include "dukeactor.h" +#include "buildtiles.h" BEGIN_DUKE_NS diff --git a/source/games/duke/src/inlines.h b/source/games/duke/src/inlines.h index e4df73fea..8b3f4978a 100644 --- a/source/games/duke/src/inlines.h +++ b/source/games/duke/src/inlines.h @@ -3,6 +3,7 @@ #include "gamehud.h" #include "global.h" #include "models/modeldata.h" +#include "texinfo.h" // all inline functions. BEGIN_DUKE_NS @@ -119,11 +120,9 @@ inline bool inventory(DDukeActor* S) return actorflag(S, SFLAG_INVENTORY); } -inline int& tileflags(unsigned int tilenum) +inline const unsigned& tileflags(unsigned int tilenum) { - static int sink = 0; - if (tilenum >= MAXTILES) return sink; - return TileFiles.tiledata[tilenum].tileflags; + return GetExtInfo(tileGetTextureID(tilenum)).flags; } inline bool wallswitchcheck(DDukeActor* s) diff --git a/source/games/duke/src/namelist_d.h b/source/games/duke/src/namelist_d.h index c590d08a1..c7e20c9bd 100644 --- a/source/games/duke/src/namelist_d.h +++ b/source/games/duke/src/namelist_d.h @@ -933,5 +933,5 @@ x(DEVELOPERCOMMENTARYON, 5295) x(BOSS5, 5310) x(BOSS5STAYPUT, 5311) x(SERIOUSSAM, 5846) -x(VIEWSCR, 30715) // MAXTILES-5 +x(VIEWSCR, 7) // hijacks CYCLER diff --git a/source/games/duke/src/namelist_r.h b/source/games/duke/src/namelist_r.h index 7535d608b..98e04251c 100644 --- a/source/games/duke/src/namelist_r.h +++ b/source/games/duke/src/namelist_r.h @@ -1553,4 +1553,4 @@ x(MAMACLOUD, 8663) x(MAMA, 8705) x(MAMAJIBA, 8890) x(MAMAJIBB, 8895) -x(VIEWSCR, 30715) // MAXTILES-5 +x(VIEWSCR, 7) // hijacks CYCLER diff --git a/source/games/duke/src/render.cpp b/source/games/duke/src/render.cpp index c536a627e..d27992e9b 100644 --- a/source/games/duke/src/render.cpp +++ b/source/games/duke/src/render.cpp @@ -77,10 +77,10 @@ void GameInterface::UpdateCameras(double smoothratio) if (camsprite->GetOwner() && (p->GetActor()->spr.pos - camsprite->spr.pos).Length() < VIEWSCREEN_ACTIVE_DISTANCE) { - auto tex = tileGetTexture(camsprite->spr.picnum); + auto tex = TexMan.FindGameTexture("VIEWSCR", ETextureType::Any); + if (!tex || !tex->GetTexture()->isCanvas()) return; - auto canvas = tileGetCanvas(TILE_VIEWSCR); - if (!canvas) return; + auto canvas = static_cast(tex->GetTexture()); screen->RenderTextureView(canvas, [=](IntRect& rect) { diff --git a/source/games/exhumed/src/2d.cpp b/source/games/exhumed/src/2d.cpp index 6522fff96..6beb9fd6c 100644 --- a/source/games/exhumed/src/2d.cpp +++ b/source/games/exhumed/src/2d.cpp @@ -42,7 +42,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "vm.h" #include "razefont.h" -#include +#include "buildtiles.h" + #include @@ -436,9 +437,7 @@ void uploadCinemaPalettes() static int DoStatic(int a, int b) { - auto tex = dynamic_cast(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage()); - if (tex) tex->Reload(); - auto pixels = GetWritablePixels(tileGetTextureID(kTileLoboLaptop)); + auto pixels = GetWritablePixels(tileGetTextureID(kTileLoboLaptop), true); int y = 160 - a / 2; int left = 81 - b / 2; @@ -463,10 +462,8 @@ static int DoStatic(int a, int b) static int UndoStatic() { - auto tex = dynamic_cast(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage()); - if (tex) tex->Reload(); auto texid = tileGetTextureID(kTileLoboLaptop); - GetWritablePixels(texid); + GetWritablePixels(texid, true); return texid.GetIndex(); } diff --git a/source/games/exhumed/src/exhumed.cpp b/source/games/exhumed/src/exhumed.cpp index a5de0c1ae..a68c9c327 100644 --- a/source/games/exhumed/src/exhumed.cpp +++ b/source/games/exhumed/src/exhumed.cpp @@ -49,6 +49,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "razemenu.h" #include "v_draw.h" #include "interpolate.h" +#include "tilesetbuilder.h" #include "psky.h" BEGIN_PS_NS @@ -534,34 +535,29 @@ void LevelFinished() //--------------------------------------------------------------------------- #define x(a, b) registerName(#a, b); -static void SetTileNames() +static void SetTileNames(TilesetBuildInfo& info) { - auto registerName = [](const char* name, int index) + auto registerName = [&](const char* name, int index) { - TileFiles.addName(name, index); + info.addName(name, index); }; #include "namelist.h" } #undef x -void GameInterface::LoadGameTextures() +void GameInterface::SetupSpecialTextures(TilesetBuildInfo& info) { - SetTileNames(); -} - -void GameInterface::SetupSpecialTextures() -{ - TileFiles.tileCreate(kTile4092, kPlasmaWidth, kPlasmaHeight); - TileFiles.tileCreate(kTile4093, kPlasmaWidth, kPlasmaHeight); - TileFiles.tileCreate(kTileRamsesWorkTile, kSpiritY * 2, kSpiritX * 2); - TileFiles.tileMakeWritable(kTileLoboLaptop); + SetTileNames(info); + info.CreateWritable(kTile4092, kPlasmaWidth, kPlasmaHeight); + info.CreateWritable(kTile4093, kPlasmaWidth, kPlasmaHeight); + info.CreateWritable(kTileRamsesWorkTile, kSpiritY * 2, kSpiritX * 2); + info.MakeWritable(kTileLoboLaptop); for(int i = kTile3603; i < kClockSymbol1 + 145; i++) - TileFiles.tileMakeWritable(kTile3603); - TileFiles.tileMakeWritable(kEnergy1); - TileFiles.tileMakeWritable(kEnergy2); + info.MakeWritable(kTile3603); + info.MakeWritable(kEnergy1); + info.MakeWritable(kEnergy2); for (int i = 0; i < 16; i++) - TileFiles.tileMakeWritable(kClockSymbol1); - TileFiles.lock(); + info.MakeWritable(kClockSymbol1); } //--------------------------------------------------------------------------- diff --git a/source/games/exhumed/src/exhumed.h b/source/games/exhumed/src/exhumed.h index b04562ab4..a7495f851 100644 --- a/source/games/exhumed/src/exhumed.h +++ b/source/games/exhumed/src/exhumed.h @@ -209,8 +209,7 @@ struct GameInterface : public ::GameInterface { const char* Name() override { return "Exhumed"; } void app_init() override; - void LoadGameTextures() override; - void SetupSpecialTextures() override; + void SetupSpecialTextures(TilesetBuildInfo& info) override; void clearlocalinputstate() override; void loadPalette() override; bool GenerateSavePic() override; diff --git a/source/games/sw/src/cache.cpp b/source/games/sw/src/cache.cpp index 489de671b..ee3915be1 100644 --- a/source/games/sw/src/cache.cpp +++ b/source/games/sw/src/cache.cpp @@ -41,6 +41,7 @@ not load" error messages. #include "sounds.h" #include "network.h" #include "precache.h" +#include "texinfo.h" BEGIN_SW_NS diff --git a/source/games/sw/src/draw.cpp b/source/games/sw/src/draw.cpp index aa47086ff..a970933d0 100644 --- a/source/games/sw/src/draw.cpp +++ b/source/games/sw/src/draw.cpp @@ -58,6 +58,8 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms #include "razefont.h" #include "models/modeldata.h" +#include "buildtiles.h" + extern DCoreActor* wall_to_sprite_actors[8]; BEGIN_SW_NS diff --git a/source/games/sw/src/game.cpp b/source/games/sw/src/game.cpp index 7dd2f8631..11b9d1f33 100644 --- a/source/games/sw/src/game.cpp +++ b/source/games/sw/src/game.cpp @@ -49,6 +49,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms #include "i_interface.h" #include "psky.h" #include "startscreen.h" +#include "tilesetbuilder.h" @@ -209,46 +210,39 @@ int ThemeTrack[6]; ///////////////////////////////////////////////////////////////////////////////////////////// #define x(a, b) registerName(#a, b); -static void SetTileNames() +static void SetTileNames(TilesetBuildInfo& info) { - auto registerName = [](const char* name, int index) + auto registerName = [&](const char* name, int index) { - TileFiles.addName(name, index); + info.addName(name, index); }; #include "namelist.h" } #undef x -void GameInterface::LoadGameTextures() +void GameInterface::LoadTextureInfo(TilesetBuildInfo& info) { LoadKVXFromScript("swvoxfil.txt"); // Load voxels from script file - SetTileNames(); } -void GameInterface::SetupSpecialTextures() +enum { - enum - { - FAF_PLACE_MIRROR_PIC = 341, - FAF_MIRROR_PIC = 2356 - }; + FAF_PLACE_MIRROR_PIC = 341, + FAF_MIRROR_PIC = 2356 +}; - tileDelete(MIRROR); // mirror +void GameInterface::SetupSpecialTextures(TilesetBuildInfo& info) +{ + info.Delete(MIRROR); // mirror for (int i = 0; i < MAXMIRRORS; i++) { - tileDelete(i + MIRRORLABEL); - TileFiles.MakeCanvas(CAMSPRITE + i, 128, 114); + info.Delete(i + MIRRORLABEL); + info.MakeCanvas(CAMSPRITE + i, 128, 114); } // make these two unique, they are empty by default. - tileDelete(FAF_MIRROR_PIC); - tileDelete(FAF_MIRROR_PIC + 1); - TileFiles.lock(); - - // these are frequently checked markers. - FAFPlaceMirrorPic[0] = tileGetTextureID(FAF_PLACE_MIRROR_PIC); - FAFPlaceMirrorPic[1] = tileGetTextureID(FAF_PLACE_MIRROR_PIC + 1); - FAFMirrorPic[0] = tileGetTextureID(FAF_MIRROR_PIC); - FAFMirrorPic[1] = tileGetTextureID(FAF_MIRROR_PIC + 1); + info.Delete(FAF_MIRROR_PIC); + info.Delete(FAF_MIRROR_PIC + 1); + SetTileNames(info); } //--------------------------------------------------------------------------- // @@ -258,6 +252,12 @@ void GameInterface::SetupSpecialTextures() void GameInterface::app_init() { + // these are frequently checked markers. + FAFPlaceMirrorPic[0] = tileGetTextureID(FAF_PLACE_MIRROR_PIC); + FAFPlaceMirrorPic[1] = tileGetTextureID(FAF_PLACE_MIRROR_PIC + 1); + FAFMirrorPic[0] = tileGetTextureID(FAF_MIRROR_PIC); + FAFMirrorPic[1] = tileGetTextureID(FAF_MIRROR_PIC + 1); + GC::AddMarkerFunc(markgcroots); GameTicRate = TICS_PER_SEC / synctics; diff --git a/source/games/sw/src/game.h b/source/games/sw/src/game.h index e262fa995..34d9dc5e4 100644 --- a/source/games/sw/src/game.h +++ b/source/games/sw/src/game.h @@ -1654,8 +1654,8 @@ struct GameInterface : public ::GameInterface { const char* Name() override { return "ShadowWarrior"; } void app_init() override; - void LoadGameTextures() override; - void SetupSpecialTextures() override; + void LoadTextureInfo(TilesetBuildInfo& info) override; + void SetupSpecialTextures(TilesetBuildInfo& info) override; void loadPalette() override; void clearlocalinputstate() override; void FreeLevelData() override; diff --git a/source/games/sw/src/jsector.cpp b/source/games/sw/src/jsector.cpp index 49a57cadc..be652b9f2 100644 --- a/source/games/sw/src/jsector.cpp +++ b/source/games/sw/src/jsector.cpp @@ -397,12 +397,13 @@ void JS_InitMirrors(void) ///////////////////////////////////////////////////// void drawroomstotile(const DVector3& pos, DAngle ang, DAngle horiz, sectortype* dacursect, short tilenume, double smoothratio) { - auto canvas = tileGetCanvas(tilenume); + auto tex = tileGetTexture(tilenume); + auto canvas = dynamic_cast(tex->GetTexture()); if (!canvas) return; screen->RenderTextureView(canvas, [=](IntRect& rect) { - render_camtex(nullptr, pos, dacursect, DRotator(horiz, ang, nullAngle), tileGetTexture(tilenume), rect, smoothratio); + render_camtex(nullptr, pos, dacursect, DRotator(horiz, ang, nullAngle), tex, rect, smoothratio); }); }