#include "stdafx.h" #include "SpriteFontLoader.h" SpriteFont* SpriteFontLoader::LoadContent(const ContentLoadInfo& loadInfo) { const auto pReader = new BinaryReader(); pReader->Open(loadInfo.assetFullPath); if (!pReader->Exists()) { Logger::LogError(L"Failed to read the assetFile!\nPath: \'{}\'", loadInfo.assetSubPath); return nullptr; } //See BMFont Documentation for Binary Layout //Parse the Identification bytes (B,M,F) if (pReader->Read() != 'B' || pReader->Read() != 'M' || pReader->Read() != 'F') { Logger::LogError(L"SpriteFontLoader::LoadContent > Not a valid .fnt font"); return nullptr; } //Check is version 3 if (pReader->Read() < 3) { Logger::LogError(L"SpriteFontLoader::LoadContent > Only .fnt version 3 is supported"); return nullptr; } //Valid .fnt file >> Start Parsing! //use this SpriteFontDesc to store all relevant information (used to initialize a SpriteFont object) SpriteFontDesc fontDesc{}; //********** // BLOCK 0 * //********** //Retrieve the blockId and blockSize int(pReader->Read()); pReader->Read(); //Retrieve the FontSize [fontDesc.fontSize] fontDesc.fontSize = pReader->Read(); //Move the binreader to the start of the FontName [BinaryReader::MoveBufferPosition(...) or you can set its position using BinaryReader::SetBufferPosition(...)) pReader->MoveBufferPosition(12); // Move to FontName //Retrieve the FontName [fontDesc.fontName] fontDesc.fontName = pReader->ReadNullString(); //... //********** // BLOCK 1 * //********** //Retrieve the blockId and blockSize int(pReader->Read()); pReader->Read(); //Retrieve Texture Width & Height [fontDesc.textureWidth/textureHeight] pReader->MoveBufferPosition(4); fontDesc.textureWidth = pReader->Read(); fontDesc.textureHeight = pReader->Read(); //Retrieve PageCount short pageCount = pReader->Read(); //> if pagecount > 1 if (pageCount > 1) { // > Log Error (Only one texture per font is allowed!) Logger::LogError(L"Only one texture per font is allowed!"); } //Advance to Block2 (Move Reader) pReader->MoveBufferPosition(5); //********** // BLOCK 2 * //********** //Retrieve the blockId and blockSize int(pReader->Read()); pReader->Read(); //Retrieve the PageName (BinaryReader::ReadNullString) std::wstring pageName = pReader->ReadNullString(); //Construct the full path to the page texture file // >> page texture should be stored next to the .fnt file, pageName contains the name of the texture file // >> full texture path = asset parent_path of .fnt file (see loadInfo.assetFullPath > get parent_path) + pageName (filesystem::path::append) std::filesystem::path fullPath = std::filesystem::path(loadInfo.assetFullPath).parent_path(); fullPath.append(pageName.begin(), pageName.end()); // >> Load the texture (ContentManager::Load) & Store [fontDesc.pTexture] fontDesc.pTexture = ContentManager::Load(fullPath.wstring()); //********** // BLOCK 3 * //********** //Retrieve the blockId and blockSize int(pReader->Read()); int blockSize = pReader->Read(); blockSize; //Retrieve Character Count (see documentation) int charCount = blockSize / 20; //Create loop for Character Count, and: for (int i = 0; i < charCount; ++i) { // > Create instance of FontMetric (struct) FontMetric metric{}; // > Retrieve CharacterId (store Local) and cast to a 'wchar_t' wchar_t charId = wchar_t(pReader->Read()); // > Set Character (CharacterId) [FontMetric::character] metric.character = charId; // > Retrieve Xposition (store Local) // > Retrieve Yposition (store Local) unsigned short xPos = pReader->Read(); unsigned short yPos = pReader->Read(); // > Retrieve & Set Width [FontMetric::width] metric.width = pReader->Read(); // > Retrieve & Set Height [FontMetric::height] metric.height = pReader->Read(); // > Retrieve & Set OffsetX [FontMetric::offsetX] // > Retrieve & Set OffsetY [FontMetric::offsetY] metric.offsetX = pReader->Read(); metric.offsetY = pReader->Read(); // > Retrieve & Set AdvanceX [FontMetric::advanceX] metric.advanceX = pReader->Read(); // > Retrieve & Set Page [FontMetric::page] metric.page = pReader->Read(); // > Retrieve Channel (BITFIELD!!!) // > See documentation for BitField meaning [FontMetric::channel] unsigned char channel{ pReader->Read() }; switch (channel) { default: metric.channel = 0; break; case 0x1: metric.channel = 2; break; case 0x2: metric.channel = 1; break; case 0x4: metric.channel = 0; break; case 0x8: metric.channel = 4; break; case 0xF: metric.channel = 0; } // > Calculate Texture Coordinates using Xposition, Yposition, fontDesc.TextureWidth & fontDesc.TextureHeight [FontMetric::texCoord] metric.texCoord = { (float)xPos / (float)fontDesc.textureWidth, (float)yPos / (float)fontDesc.textureHeight }; //> Insert new FontMetric to the metrics [font.metrics] map // > key = (wchar_t) charId // > value = new FontMetric //(loop restarts till all metrics are parsed) fontDesc.metrics.insert(std::make_pair(charId, metric)); } //Done! delete pReader; return new SpriteFont(fontDesc); } void SpriteFontLoader::Destroy(SpriteFont* objToDestroy) { SafeDelete(objToDestroy); }