feat: font loading and dumping (#866)

* feat: font loading and dumping

* chore: make font properties unsigned if possible

* chore: use merge_patch to dump json font

* chore: font material is required

* chore: move static methods from font loader and dumper to anonymous namespace

* chore: make sure loaded fonts have their glyphs sorted

* chore: add macros for json NLOHMANN_TO_JSON_METHOD and NLOHMANN_FROM_JSON_METHOD

* feat: encode printable letters as string in json

Co-authored-by: hindercanrun <[email protected]>

* chore: omit redundant glyph count from jsons

---------

Co-authored-by: Jan Laupetin <[email protected]>
Co-authored-by: hindercanrun <[email protected]>
Co-authored-by: MrIkso <[email protected]>
This commit is contained in:
mo
2026-07-03 19:26:12 +02:00
committed by GitHub
co-authored by hindercanrun Jan Laupetin MrIkso
parent fb4b00398c
commit 3fb8b2bb17
24 changed files with 659 additions and 35 deletions
+119
View File
@@ -0,0 +1,119 @@
#options GAME (IW3, IW4, IW5, T4, T5)
#filename "Game/" + GAME + "/Font/FontDumper" + GAME + ".cpp"
#if GAME == "IW3"
#define GAME_LOWER "iw3"
#elif GAME == "IW4"
#define GAME_LOWER "iw4"
#elif GAME == "IW5"
#define GAME_LOWER "iw5"
#elif GAME == "T4"
#define GAME_LOWER "t4"
#elif GAME == "T5"
#define GAME_LOWER "t5"
#endif
// This file was templated.
// See FontDumper.cpp.template.
// Do not modify, changes will be lost.
#set DUMPER_HEADER "\"FontDumper" + GAME + ".h\""
#include DUMPER_HEADER
#include "Font/FontCommon.h"
#set JSON_HEADER "\"Game/" + GAME + "/Font/JsonFont" + GAME + ".h\""
#include JSON_HEADER
#include <iomanip>
#include <nlohmann/json.hpp>
using namespace nlohmann;
using namespace GAME;
#set CLASS_NAME "JsonDumper" + GAME
namespace
{
const char* AssetName(const char* input)
{
if (input && input[0] == ',')
return &input[1];
return input;
}
void CreateJsonGlyph(JsonGlyph& jGlyph, const Glyph& glyph)
{
jGlyph.letter = glyph.letter;
jGlyph.x0 = glyph.x0;
jGlyph.y0 = glyph.y0;
jGlyph.dx = glyph.dx;
jGlyph.pixelWidth = glyph.pixelWidth;
jGlyph.pixelHeight = glyph.pixelHeight;
jGlyph.s0 = glyph.s0;
jGlyph.t0 = glyph.t0;
jGlyph.s1 = glyph.s1;
jGlyph.t1 = glyph.t1;
}
void CreateJsonFont(JsonFont& jFont, const Font_s& font)
{
jFont.pixelHeight = font.pixelHeight;
if (font.material && font.material->info.name)
jFont.material = AssetName(font.material->info.name);
if (font.glowMaterial && font.glowMaterial->info.name)
jFont.glowMaterial = AssetName(font.glowMaterial->info.name);
if (font.glyphs == nullptr || font.glyphCount <= 0)
return;
jFont.glyphs.resize(font.glyphCount);
for (auto i = 0; i < font.glyphCount; i++)
CreateJsonGlyph(jFont.glyphs[i], font.glyphs[i]);
}
class Dumper
{
public:
explicit Dumper(std::ostream& stream)
: m_stream(stream)
{
}
void Dump(const Font_s& font) const
{
JsonFont jFont;
CreateJsonFont(jFont, font);
ordered_json jRoot;
jRoot["$schema"] = "http://openassettools.dev/schema/font.v1.json";
jRoot["_type"] = "font";
jRoot["_version"] = 1;
jRoot["_game"] = GAME_LOWER;
jRoot.merge_patch(jFont);
m_stream << std::setw(4) << jRoot << "\n";
}
private:
std::ostream& m_stream;
};
} // namespace
namespace font
{
void CLASS_NAME::DumpAsset(AssetDumpingContext& context, const XAssetInfo<AssetFont::Type>& asset)
{
const auto assetFile = context.OpenAssetFile(GetJsonFileNameForAssetName(asset.m_name));
if (!assetFile)
return;
Dumper dumper(*assetFile);
dumper.Dump(*asset.Asset());
}
} // namespace font
+25
View File
@@ -0,0 +1,25 @@
#options GAME (IW3, IW4, IW5, T4, T5)
#filename "Game/" + GAME + "/Font/FontDumper" + GAME + ".h"
// This file was templated.
// See FontDumper.h.template.
// Do not modify, changes will be lost.
#pragma once
#include "Dumping/AbstractAssetDumper.h"
#include "Dumping/AssetDumpingContext.h"
#set GAME_HEADER "\"Game/" + GAME + "/" + GAME + ".h\""
#include GAME_HEADER
#set CLASS_NAME "JsonDumper" + GAME
namespace font
{
class CLASS_NAME final : public AbstractAssetDumper<GAME::AssetFont>
{
protected:
void DumpAsset(AssetDumpingContext& context, const XAssetInfo<GAME::AssetFont::Type>& asset) override;
};
} // namespace font
+2 -1
View File
@@ -1,5 +1,6 @@
#include "ObjWriterIW3.h"
#include "Game/IW3/Font/FontDumperIW3.h"
#include "Game/IW3/Image/ImageDumperIW3.h"
#include "Game/IW3/Material/MaterialJsonDumperIW3.h"
#include "Game/IW3/Techset/TechsetDumperIW3.h"
@@ -41,7 +42,7 @@ void ObjWriter::RegisterAssetDumpers(AssetDumpingContext& context)
RegisterAssetDumper(std::make_unique<map_ents::DumperIW3>());
// REGISTER_DUMPER(AssetDumperGfxWorld)
RegisterAssetDumper(std::make_unique<light_def::DumperIW3>());
// REGISTER_DUMPER(AssetDumperFont_s)
RegisterAssetDumper(std::make_unique<font::JsonDumperIW3>());
// REGISTER_DUMPER(AssetDumperMenuList)
// REGISTER_DUMPER(AssetDumpermenuDef_t)
RegisterAssetDumper(std::make_unique<localize::DumperIW3>());
+2 -1
View File
@@ -1,5 +1,6 @@
#include "ObjWriterIW4.h"
#include "Game/IW4/Font/FontDumperIW4.h"
#include "Game/IW4/Image/ImageDumperIW4.h"
#include "Game/IW4/Material/MaterialJsonDumperIW4.h"
#include "Game/IW4/Techset/PixelShaderDumperIW4.h"
@@ -58,7 +59,7 @@ void ObjWriter::RegisterAssetDumpers(AssetDumpingContext& context)
// REGISTER_DUMPER(AssetDumperFxWorld)
// REGISTER_DUMPER(AssetDumperGfxWorld)
RegisterAssetDumper(std::make_unique<light_def::DumperIW4>());
// REGISTER_DUMPER(AssetDumperFont_s)
RegisterAssetDumper(std::make_unique<font::JsonDumperIW4>());
RegisterAssetDumper(std::make_unique<menu::MenuListDumperIW4>());
RegisterAssetDumper(std::make_unique<menu::MenuDumperIW4>());
RegisterAssetDumper(std::make_unique<localize::DumperIW4>());
+2 -1
View File
@@ -1,6 +1,7 @@
#include "ObjWriterIW5.h"
#include "Game/IW4/XAnim/XAnimDumperIW4.h"
#include "Game/IW5/Font/FontDumperIW5.h"
#include "Game/IW5/Image/ImageDumperIW5.h"
#include "Game/IW5/Material/MaterialJsonDumperIW5.h"
#include "Game/IW5/Techset/PixelShaderDumperIW5.h"
@@ -53,7 +54,7 @@ void ObjWriter::RegisterAssetDumpers(AssetDumpingContext& context)
// REGISTER_DUMPER(AssetDumperFxWorld)
// REGISTER_DUMPER(AssetDumperGfxWorld)
RegisterAssetDumper(std::make_unique<light_def::DumperIW5>());
// REGISTER_DUMPER(AssetDumperFont_s)
RegisterAssetDumper(std::make_unique<font::JsonDumperIW5>());
RegisterAssetDumper(std::make_unique<menu::MenuListDumperIW5>());
RegisterAssetDumper(std::make_unique<menu::MenuDumperIW5>());
RegisterAssetDumper(std::make_unique<localize::DumperIW5>());
+2
View File
@@ -1,5 +1,6 @@
#include "ObjWriterT4.h"
#include "Game/T4/Font/FontDumperT4.h"
#include "Game/T4/Image/ImageDumperT4.h"
#include "Game/T4/XAnim/XAnimDumperT4.h"
#include "Game/T4/XModel/XModelDumperT4.h"
@@ -19,6 +20,7 @@ void ObjWriter::RegisterAssetDumpers(AssetDumpingContext& context)
RegisterAssetDumper(std::make_unique<image::DumperT4>());
RegisterAssetDumper(std::make_unique<sound::LoadedSoundDumperT4>());
RegisterAssetDumper(std::make_unique<map_ents::DumperT4>());
RegisterAssetDumper(std::make_unique<font::JsonDumperT4>());
RegisterAssetDumper(std::make_unique<localize::DumperT4>());
RegisterAssetDumper(std::make_unique<weapon::DumperT4>());
RegisterAssetDumper(std::make_unique<raw_file::DumperT4>());
+2 -1
View File
@@ -1,5 +1,6 @@
#include "ObjWriterT5.h"
#include "Game/T5/Font/FontDumperT5.h"
#include "Game/T5/Image/ImageDumperT5.h"
#include "Game/T5/Material/MaterialJsonDumperT5.h"
#include "Game/T5/Techset/TechsetDumperT5.h"
@@ -39,7 +40,7 @@ void ObjWriter::RegisterAssetDumpers(AssetDumpingContext& context)
// REGISTER_DUMPER(AssetDumperMapEnts, m_map_ents)
// REGISTER_DUMPER(AssetDumperGfxWorld, m_gfx_world)
RegisterAssetDumper(std::make_unique<light_def::DumperT5>());
// REGISTER_DUMPER(AssetDumperFont, m_font)
RegisterAssetDumper(std::make_unique<font::JsonDumperT5>());
// REGISTER_DUMPER(AssetDumperMenuList, m_menu_list)
// REGISTER_DUMPER(AssetDumperMenuDef, m_menu_def)
RegisterAssetDumper(std::make_unique<localize::DumperT5>());