diff --git a/game/game.cpp b/game/game.cpp index 8cc067c..54b6793 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -71,6 +71,7 @@ int gcSupportGobsLimit; bool gfMultiplayer; bool gfIgnoreBluetoothWarning; SpriteManager *gpsprm; +TexAtlasMgr *gptam; #ifdef STRESS bool gfStress = false; @@ -325,6 +326,10 @@ bool Game::Init(int imm) return false; } + if (!InitTexAtlasMgr()) { + HostOutputDebugString("Failed to initilize texture atlas manager"); + } + // Init memory manager *after* setting screen mode so we know how much dyn ram is left after // allocating back buffers (potentially from main memory). @@ -635,6 +640,19 @@ bool Game::LoadGameData() return true; } +bool Game::InitTexAtlasMgr() +{ + gptam = new TexAtlasMgr(); + Assert(gptam != NULL, "out of memory!"); + if (gptam == NULL) + return false; + if (!gptam->Init()) { + delete gptam; + return false; + } + return true; +} + bool Game::InitCoordMappingTables() { // Initialize world -> pixel coord mapping table @@ -2037,6 +2055,9 @@ void Game::Exit() delete gpstrtbl; gpstrtbl = NULL; + delete gptam; + gptam = NULL; + // Pop off all data files while (gpakr.Pop()) diff --git a/game/ht.h b/game/ht.h index 27f854a..0a20187 100644 --- a/game/ht.h +++ b/game/ht.h @@ -265,6 +265,7 @@ typedef word CacheHandle; // hc #define secBuilderGob secCode12 #define secDrm secCode16 #define secBtTransport secCode17 +#define secTexAtlasMgr secCode13 // Performance options @@ -1162,6 +1163,24 @@ private: }; TBitmap *CreateTBitmap(char *pszName) secTBitmap; +// Texture Atlas Manager + +class TexAtlasMgr +{ +public: + TexAtlasMgr() secTexAtlasMgr; + ~TexAtlasMgr() secTexAtlasMgr; + + bool Init() secTexAtlasMgr; + TBitmap *CreateTBitmap(char *pszName) secTexAtlasMgr; + void BltTo(TBitmap *ptbmSrc, DibBitmap *pbmDst, int xDst, int yDst, Side side, Rect *prcSrc) secTexAtlasMgr; + +private: + int m_natlases; + json::JsonMap *m_json; + DibBitmap **m_pbmAtlases; +}; + struct FontHeader // fnth { word cy; @@ -2354,6 +2373,7 @@ private: int FindBestModeMatch(int nSizeDataAbove) secGame; void AddModeMatches(int nDepthData, int nSizeData, int nDepthOrGreater, int cxWidthOrGreater) secGame; bool LoadGameData() secGame; + bool InitTexAtlasMgr() secGame; bool InitCoordMappingTables() secGame; bool InitSimulation(Stream *pstm, char *pszLevel, word wfRole, dword gameid, Chatter *chatter); @@ -8541,6 +8561,7 @@ extern int gnMPPos; #endif extern char *gpszDataDir; extern bool gfIgnoreBluetoothWarning; +extern TexAtlasMgr *gptam; inline Color GetColor(int iclr) { return gaclrFixed[iclr]; diff --git a/game/texatlasmgr.cpp b/game/texatlasmgr.cpp new file mode 100644 index 0000000..7e40fee --- /dev/null +++ b/game/texatlasmgr.cpp @@ -0,0 +1,194 @@ +#include "ht.h" +#include "yajl/wrapper/jsonbuilder.h" +#include "base/thread.h" + +namespace wi { + +TexAtlasMgr::TexAtlasMgr() +{ + m_json = NULL; + m_pbmAtlases = NULL; + m_natlases = 0; +} +TexAtlasMgr::~TexAtlasMgr() +{ + delete m_json; + m_json = NULL; + for (int n = 0; n < m_natlases; n++) { + if (m_pbmAtlases[n] != NULL) + delete m_pbmAtlases[n]; + } + delete[] m_pbmAtlases; + m_pbmAtlases = NULL; +} + +char *apszatlases[] = { + "animations.png", + "fonts.png", + "bitmaps.png", + "bitmaps0.png", + "bitmaps1.png", + "bitmaps2.png", + "bitmaps3.png", + "bitmaps4.png", + "units0.png", + "units1.png", + "units2.png", + "units3.png", + "units4.png" +}; + +bool TexAtlasMgr::Init() +{ + m_natlases = ARRAYSIZE(apszatlases); + m_pbmAtlases = new DibBitmap*[m_natlases]; + + // Load atlases + + for (int i = 0; i < m_natlases; i++) { + m_pbmAtlases[i] = LoadDibBitmap(apszatlases[i]); + if (!m_pbmAtlases[i]) + return false; + } + + // Read json + + FileMap fmap; + char *pszJson = (char *)gpakr.MapFile("atlasmap.json", &fmap); + if (!pszJson) { + return false; + } + + // Parse json + + json::JsonBuilder builder; + builder.Start(); + if (!builder.Update(pszJson, (int)strlen(pszJson))) { + gpakr.UnmapFile(&fmap); + return false; + } + json::JsonObject *obj = builder.End(); + if (obj == NULL) { + gpakr.UnmapFile(&fmap); + return false; + } + + m_json = (json::JsonMap *)obj; + gpakr.UnmapFile(&fmap); + return true; +} + +void TexAtlasMgr::BltTo(TBitmap *ptbmSrc, DibBitmap *pbmDst, int xDst, int yDst, Side side, Rect *prcSrc) +{ + Size siz; + ptbmSrc->GetTextureSize(&siz); + + Point pt; + ptbmSrc->GetPosition(&pt); + + // Where do we want to blt from? + + Rect rc; + if (prcSrc != NULL) { + + // NOTE: the clipping rect calculated in this block has not been + // tested with an image that was was cropped by the atlas packer + + // prcSrc data needs to be converted to work within the texture atlas + + rc.left = pt.x + prcSrc->left - ptbmSrc->ClippedLeft(); + rc.top = pt.y + prcSrc->top - ptbmSrc->ClippedTop(); + rc.right = rc.left + prcSrc->Width(); + rc.bottom = rc.top + prcSrc->Height(); + + // Set to image edge if anything is past image edge + // to prevent other images in the texture from being + // in the blt + + if (rc.left < pt.x) + rc.left = pt.x; + + if (rc.top < pt.y) + rc.top = pt.y; + + if (rc.right > pt.x + siz.cx) + rc.right = pt.x + siz.cx; + + if (rc.bottom > pt.y + siz.cy) + rc.bottom = pt.y + siz.cy; + + } else { + // No prcSrc, the blt rect is just the location and size of the image + + rc.Set(pt.x, pt.y, pt.x + siz.cx , pt.y + siz.cy); + } + + if (m_pbmAtlases[ptbmSrc->GetAtlas(side)]) { + pbmDst->Blt(m_pbmAtlases[ptbmSrc->GetAtlas(side)], + &rc, xDst + ptbmSrc->ClippedLeft(), yDst + ptbmSrc->ClippedTop()); + } +} + +TBitmap *TexAtlasMgr::CreateTBitmap(char *pszName) +{ + json::JsonMap *entry = (json::JsonMap *)m_json->GetObject(pszName); + if (entry == NULL) { + + // Scenery gobs and cut scene images are "hardcoded" into the levels + // files as .tbm and .rbm. If pszName is a .tbm or .rbm, load as .png + + char *pszSuffix = strrchr(pszName, '.'); + if (pszSuffix) { + if ((strcmp(pszSuffix, ".tbm") == 0) || (strcmp(pszSuffix, ".rbm") == 0)) { + + // Dump the filename, remove the suffix, then append the .png suffix + + char *psz = strdup(pszName); + psz[strlen(pszName) - strlen(pszSuffix)] = '\0'; + char sz[MAX_PATH]; + sprintf(sz, "%s.png", psz); + free(psz); + + return CreateTBitmap(sz); + } + } + + // Wasnt a .tbm or .rbm, loading failed + + LOG() << "couldn't find TBitmap in atlas json: " << pszName; + return NULL; + } + + int *anSideMap = new int[kcSides]; + if (anSideMap == NULL) { + return NULL; + } + + json::JsonArray *atlases = (json::JsonArray *)entry->GetObject("atlases"); + for (int i = 0; i < kcSides; i++) { + anSideMap[i] = ((json::JsonNumber *)atlases->GetObject(i))->GetInteger(); + } + + TBitmap *ptbm = new TBitmap(); + Assert(ptbm != NULL, "out of memory!"); + if (ptbm == NULL) + return NULL; + if (!ptbm->Init( + pszName, + entry->GetInteger("x"), + entry->GetInteger("y"), + entry->GetInteger("cx"), + entry->GetInteger("cy"), + entry->GetInteger("cx_orig"), + entry->GetInteger("cy_orig"), + entry->GetInteger("cc_left"), + entry->GetInteger("cc_top"), + anSideMap)) { + + delete ptbm; + return NULL; + } + return ptbm; +} + +} // namespace wi