diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index bfdf58ce06..16b888d945 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -373,6 +373,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mScriptConsoleMode(false) , mActivationDistanceOverride(-1) , mGrab(true) + , mExportFonts(false) , mRandomSeed(0) , mNewGame(false) , mCfgMgr(configurationManager) @@ -807,7 +808,7 @@ void OMW::Engine::prepareEngine() rootNode->addChild(guiRoot); mWindowManager = std::make_unique(mWindow, mViewer, guiRoot, mResourceSystem.get(), - mWorkQueue.get(), mCfgMgr.getLogPath(), mScriptConsoleMode, mTranslationDataStorage, mEncoding, + mWorkQueue.get(), mCfgMgr.getLogPath(), mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, Version::getOpenmwVersionDescription(), shadersSupported, mCfgMgr); mEnvironment.setWindowManager(*mWindowManager); @@ -1109,6 +1110,11 @@ void OMW::Engine::setWarningsMode(int mode) mWarningsMode = mode; } +void OMW::Engine::enableFontExport(bool exportFonts) +{ + mExportFonts = exportFonts; +} + void OMW::Engine::setSaveGameFile(const std::filesystem::path& savegame) { mSaveGameFile = savegame; diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 69fdd3869c..38e95ea7c8 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -172,6 +172,7 @@ namespace OMW // Grab mouse? bool mGrab; + bool mExportFonts; unsigned int mRandomSeed; Debug::Level mMaxRecastLogLevel = Debug::Error; @@ -256,6 +257,8 @@ namespace OMW void setWarningsMode(int mode); + void enableFontExport(bool exportFonts); + /// Set the save game file to load after initialising the engine. void setSaveGameFile(const std::filesystem::path& savegame); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 7ed3292b55..ac2baad8b1 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -155,6 +155,7 @@ bool parseOptions(int argc, char** argv, OMW::Engine& engine, Files::Configurati Fallback::Map::init(variables["fallback"].as().mMap); engine.setSoundUsage(!variables["no-sound"].as()); engine.setActivationDistanceOverride(variables["activate-dist"].as()); + engine.enableFontExport(variables["export-fonts"].as()); engine.setRandomSeed(variables["random-seed"].as()); return true; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 2d14c25cfc..322b38b7e6 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -146,7 +146,7 @@ namespace MWGui WindowManager::WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::filesystem::path& logpath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, - const std::string& versionDescription, bool useShaders, Files::ConfigurationManager& cfgMgr) + bool exportFonts, const std::string& versionDescription, bool useShaders, Files::ConfigurationManager& cfgMgr) : mOldUpdateMask(0) , mOldCullMask(0) , mStore(nullptr) @@ -215,7 +215,8 @@ namespace MWGui MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); // Load fonts - mFontLoader = std::make_unique(encoding, resourceSystem->getVFS(), mScalingFactor); + mFontLoader + = std::make_unique(encoding, resourceSystem->getVFS(), mScalingFactor, exportFonts); // Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 052a269188..409a31a514 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -128,7 +128,7 @@ namespace MWGui WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::filesystem::path& logpath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, - ToUTF8::FromType encoding, const std::string& versionDescription, bool useShaders, + ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, bool useShaders, Files::ConfigurationManager& cfgMgr); virtual ~WindowManager(); diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index 637967e510..c9003f3aa8 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -227,9 +227,10 @@ namespace namespace Gui { - FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, float scalingFactor) + FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, float scalingFactor, bool exportFonts) : mVFS(vfs) , mScalingFactor(scalingFactor) + , mExportFonts(exportFonts) { if (encoding == ToUTF8::WINDOWS_1252) mEncoding = ToUTF8::CP437; @@ -407,7 +408,8 @@ namespace Gui file.reset(); // Create the font texture - std::string bitmapFilename = "fonts/" + std::string(name_) + ".tex"; + const std::string name(name_); + const std::string bitmapFilename = "fonts/" + name + ".tex"; Files::IStreamPtr bitmapFile = mVFS->get(bitmapFilename); @@ -429,6 +431,19 @@ namespace Gui << bitmapFile->gcount() << "/" << (width * height * 4) << " bytes)"; bitmapFile.reset(); + if (mExportFonts) + { + osg::ref_ptr image = new osg::Image; + image->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE); + assert(image->isDataContiguous()); + memcpy(image->data(), textureData.data(), textureData.size()); + // Convert to OpenGL origin for sensible output + image->flipVertical(); + + Log(Debug::Info) << "Writing " << name + ".png"; + osgDB::writeImageFile(*image, name + ".png"); + } + MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture(bitmapFilename); tex->createManual(width, height, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8A8); unsigned char* texData = reinterpret_cast(tex->lock(MyGUI::TextureUsage::Write)); @@ -626,6 +641,13 @@ namespace Gui code->addAttribute("size", "0 0"); } + if (mExportFonts) + { + Log(Debug::Info) << "Writing " << name + ".xml"; + xmlDocument.createDeclaration(); + xmlDocument.save(name + ".xml"); + } + // Register the font with MyGUI MyGUI::ResourceManualFont* font = static_cast( MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont")); diff --git a/components/fontloader/fontloader.hpp b/components/fontloader/fontloader.hpp index 7e9220d58d..a7269f1a56 100644 --- a/components/fontloader/fontloader.hpp +++ b/components/fontloader/fontloader.hpp @@ -25,7 +25,8 @@ namespace Gui class FontLoader { public: - FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, float scalingFactor); + /// @param exportFonts export the converted fonts (Images and XML with glyph metrics) to files? + FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, float scalingFactor, bool exportFonts); void overrideLineHeight(MyGUI::xml::ElementPtr _node, std::string_view _file, MyGUI::Version _version); @@ -35,6 +36,7 @@ namespace Gui ToUTF8::FromType mEncoding; const VFS::Manager* mVFS; float mScalingFactor; + bool mExportFonts; void loadFonts(); void loadFont(const std::string& fontName, const std::string& fontId); diff --git a/docs/source/reference/modding/font.rst b/docs/source/reference/modding/font.rst index ae2a457e44..2c67a940d1 100644 --- a/docs/source/reference/modding/font.rst +++ b/docs/source/reference/modding/font.rst @@ -15,6 +15,8 @@ Morrowind .fnt fonts Morrowind uses a custom ``.fnt`` file format. It is not compatible with the Windows Font File ``.fnt`` format. To our knowledge, the format is undocumented. OpenMW can load this format and convert it on the fly into something usable (see font loader `source code `_). +You can use --export-fonts command line option to write the converted font +(a PNG image and an XML file describing the position of each glyph in the image) to the current directory. They can be used instead of TrueType fonts if needed by specifying their ``.fnt`` files names in the ``openmw.cfg``. For example: