From e8d844e7e55bdd4b58d2c1cabbf50ce56080d120 Mon Sep 17 00:00:00 2001 From: Diject Date: Tue, 30 Dec 2025 18:49:08 +0300 Subject: [PATCH] Refactors MapExtractor to use engine options --- apps/openmw/engine.cpp | 9 ++-- apps/openmw/engine.hpp | 4 +- apps/openmw/main.cpp | 17 ++++-- apps/openmw/mapextractor.cpp | 58 ++++++-------------- apps/openmw/mapextractor.hpp | 17 +++--- apps/openmw/mwbase/world.hpp | 8 +-- apps/openmw/mwlua/worldbindings.cpp | 14 +++-- apps/openmw/mwworld/worldimp.cpp | 83 +++++++---------------------- apps/openmw/mwworld/worldimp.hpp | 14 +++-- apps/openmw/options.cpp | 4 +- files/lua_api/openmw/world.lua | 19 ++++--- 11 files changed, 97 insertions(+), 150 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d0dc9915a5..2dec1d478e 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -375,9 +375,9 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mExportFonts(false) , mRandomSeed(0) , mNewGame(false) - , mExtractMaps(false) , mCfgMgr(configurationManager) , mGlMaxTextureImageUnits(0) + , mOverwriteMaps(false) { #if SDL_VERSION_ATLEAST(2, 24, 0) SDL_SetHint(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, "1"); @@ -836,7 +836,8 @@ void OMW::Engine::prepareEngine() // Create the world mWorld = std::make_unique( - mResourceSystem.get(), mActivationDistanceOverride, mCellName, mCfgMgr.getUserDataPath()); + mResourceSystem.get(), mActivationDistanceOverride, mCellName, mCfgMgr.getUserDataPath(), + mWorldMapOutput, mLocalMapOutput, mOverwriteMaps); mEnvironment.setWorld(*mWorld); mEnvironment.setWorldModel(mWorld->getWorldModel()); mEnvironment.setESMStore(mWorld->getStore()); @@ -1158,7 +1159,7 @@ void OMW::Engine::setLocalMapOutput(const std::string& path) mLocalMapOutput = path; } -void OMW::Engine::setExtractMaps(bool extract) +void OMW::Engine::setOverwriteMaps(bool overwrite) { - mExtractMaps = extract; + mOverwriteMaps = overwrite; } diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 11be9eac62..89ab520cba 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -185,7 +185,7 @@ namespace OMW std::string mWorldMapOutput; std::string mLocalMapOutput; - bool mExtractMaps; + bool mOverwriteMaps; Files::ConfigurationManager& mCfgMgr; int mGlMaxTextureImageUnits; @@ -272,6 +272,8 @@ namespace OMW void setLocalMapOutput(const std::string& path); + void setOverwriteMaps(bool overwrite); + void setExtractMaps(bool extract); void setRecastMaxLogLevel(Debug::Level value) { mMaxRecastLogLevel = value; } diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index a4e73672eb..1f0d7c2e56 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -167,17 +167,26 @@ bool parseOptions(int argc, char** argv, OMW::Engine& engine, Files::Configurati std::string worldMapOutput = variables["world-map-output"].as(); std::string localMapOutput = variables["local-map-output"].as(); - bool extractMaps = variables["extract-maps"].as(); - if (worldMapOutput.empty() && extractMaps) + auto removeQuotes = [](std::string& str) { + if (str.size() >= 2 && ((str.front() == '"' && str.back() == '"') || (str.front() == '\'' && str.back() == '\''))) + { + str = str.substr(1, str.size() - 2); + } + }; + + removeQuotes(worldMapOutput); + removeQuotes(localMapOutput); + + if (worldMapOutput.empty()) worldMapOutput = Files::pathToUnicodeString(std::filesystem::current_path() / "textures" / "advanced_world_map" / "custom"); - if (localMapOutput.empty() && extractMaps) + if (localMapOutput.empty()) localMapOutput = Files::pathToUnicodeString(std::filesystem::current_path() / "textures" / "advanced_world_map" / "local"); engine.setWorldMapOutput(worldMapOutput); engine.setLocalMapOutput(localMapOutput); - engine.setExtractMaps(extractMaps); + engine.setOverwriteMaps(variables["overwrite-maps"].as()); return true; } diff --git a/apps/openmw/mapextractor.cpp b/apps/openmw/mapextractor.cpp index 744e454492..86578ee1eb 100644 --- a/apps/openmw/mapextractor.cpp +++ b/apps/openmw/mapextractor.cpp @@ -36,14 +36,14 @@ namespace OMW { - MapExtractor::MapExtractor(MWWorld::World& world, osgViewer::Viewer* viewer, - MWBase::WindowManager* windowManager, const std::string& worldMapOutput, const std::string& localMapOutput) - : mWorld(world) - , mViewer(viewer) - , mWindowManager(windowManager) - , mWorldMapOutputDir(worldMapOutput) + MapExtractor::MapExtractor(const std::string& worldMapOutput, const std::string& localMapOutput, + bool forceOverwrite, MWRender::RenderingManager* renderingManager, const MWWorld::ESMStore* store) + : mWorldMapOutputDir(worldMapOutput) , mLocalMapOutputDir(localMapOutput) + , mRenderingManager(renderingManager) + , mStore(store) , mLocalMap(nullptr) + , mForceOverwrite(forceOverwrite) { // Only create directories if paths are not empty if (!mWorldMapOutputDir.empty()) @@ -52,14 +52,13 @@ namespace OMW std::filesystem::create_directories(mLocalMapOutputDir); // Create GlobalMap instance - MWRender::RenderingManager* renderingManager = mWorld.getRenderingManager(); - if (!renderingManager) + if (!mRenderingManager) { Log(Debug::Error) << "RenderingManager is null in MapExtractor constructor"; throw std::runtime_error("RenderingManager is null"); } - osg::Group* lightRoot = renderingManager->getLightRoot(); + osg::Group* lightRoot = mRenderingManager->getLightRoot(); if (!lightRoot) { Log(Debug::Error) << "LightRoot is null in MapExtractor constructor"; @@ -73,7 +72,7 @@ namespace OMW throw std::runtime_error("Root node is null"); } - SceneUtil::WorkQueue* workQueue = renderingManager->getWorkQueue(); + SceneUtil::WorkQueue* workQueue = mRenderingManager->getWorkQueue(); if (!workQueue) { Log(Debug::Error) << "WorkQueue is null in MapExtractor constructor"; @@ -83,8 +82,7 @@ namespace OMW try { mGlobalMap = std::make_unique(root, workQueue); - // Get LocalMap from WindowManager - it will be set after initUI is called - // For now just set to nullptr + // LocalMap will be set later via setLocalMap() method mLocalMap = nullptr; } catch (const std::exception& e) @@ -157,14 +155,13 @@ namespace OMW int width = mGlobalMap->getWidth(); int height = mGlobalMap->getHeight(); - const MWWorld::ESMStore& store = mWorld.getStore(); int minX = std::numeric_limits::max(); int maxX = std::numeric_limits::min(); int minY = std::numeric_limits::max(); int maxY = std::numeric_limits::min(); - MWWorld::Store::iterator it = store.get().extBegin(); - for (; it != store.get().extEnd(); ++it) + MWWorld::Store::iterator it = mStore->get().extBegin(); + for (; it != mStore->get().extEnd(); ++it) { if (it->getGridX() < minX) minX = it->getGridX(); @@ -201,7 +198,7 @@ namespace OMW Log(Debug::Info) << "Saved world map info: " << infoPath; } - void MapExtractor::extractLocalMaps(bool forceOverwrite) + void MapExtractor::extractLocalMaps(const std::vector& activeCells) { Log(Debug::Info) << "Extracting active local maps..."; @@ -214,28 +211,20 @@ namespace OMW // Create output directory if it doesn't exist std::filesystem::create_directories(mLocalMapOutputDir); - // Get LocalMap from WindowManager now that UI is initialized - if (mWindowManager) - { - mLocalMap = mWindowManager->getLocalMapRender(); - } - if (!mLocalMap) { Log(Debug::Error) << "Local map not initialized - cannot extract local maps"; - Log(Debug::Error) << "Make sure the game is fully loaded before calling extractLocalMaps"; return; } Log(Debug::Info) << "LocalMap instance is available, starting extraction"; - mForceOverwrite = forceOverwrite; mFramesToWait = 10; // Wait 10 frames before checking (increased from 3) - startExtraction(forceOverwrite); + startExtraction(activeCells); } - void MapExtractor::startExtraction(bool forceOverwrite) + void MapExtractor::startExtraction(const std::vector& activeCells) { // Enable extraction mode to prevent automatic camera cleanup if (mLocalMap) @@ -243,15 +232,6 @@ namespace OMW mLocalMap->setExtractionMode(true); } - // Get currently active cells - MWWorld::Scene* scene = &mWorld.getWorldScene(); - if (!scene) - { - Log(Debug::Error) << "Scene not available"; - throw std::runtime_error("Scene not available"); - } - - const auto& activeCells = scene->getActiveCells(); Log(Debug::Info) << "Processing " << activeCells.size() << " currently active cells..."; mPendingExtractions.clear(); @@ -267,7 +247,7 @@ namespace OMW filename << "(" << x << "," << y << ").png"; std::filesystem::path outputPath = mLocalMapOutputDir / filename.str(); - if (!forceOverwrite && std::filesystem::exists(outputPath)) + if (!mForceOverwrite && std::filesystem::exists(outputPath)) { Log(Debug::Info) << "Skipping cell (" << x << "," << y << ") - file already exists"; continue; @@ -302,7 +282,7 @@ namespace OMW std::filesystem::path texturePath = mLocalMapOutputDir / (lowerCaseId + ".png"); std::filesystem::path yamlPath = mLocalMapOutputDir / (lowerCaseId + ".yaml"); - if (!forceOverwrite && std::filesystem::exists(texturePath) && std::filesystem::exists(yamlPath)) + if (!mForceOverwrite && std::filesystem::exists(yamlPath)) { Log(Debug::Info) << "Skipping interior cell: " << cellName << " - files already exist"; continue; @@ -656,10 +636,6 @@ namespace OMW void MapExtractor::saveInteriorMapInfo(const ESM::RefId& cellId, const std::string& lowerCaseId, int segmentsX, int segmentsY) { - MWWorld::CellStore* cell = mWorld.getWorldModel().findCell(cellId); - if (!cell) - return; - // Get the bounds, center and angle that LocalMap actually used for rendering const osg::BoundingBox& bounds = mLocalMap->getInteriorBounds(); const osg::Vec2f& center = mLocalMap->getInteriorCenter(); diff --git a/apps/openmw/mapextractor.hpp b/apps/openmw/mapextractor.hpp index 2989aced90..9be1fcbfc4 100644 --- a/apps/openmw/mapextractor.hpp +++ b/apps/openmw/mapextractor.hpp @@ -28,12 +28,14 @@ namespace MWRender { class GlobalMap; class LocalMap; + class RenderingManager; } namespace MWWorld { class World; class CellStore; + class ESMStore; } namespace MWBase @@ -46,12 +48,14 @@ namespace OMW class MapExtractor { public: - MapExtractor(MWWorld::World& world, osgViewer::Viewer* viewer, MWBase::WindowManager* windowManager, - const std::string& worldMapOutput, const std::string& localMapOutput); + MapExtractor(const std::string& worldMapOutput, const std::string& localMapOutput, bool forceOverwrite, + MWRender::RenderingManager* renderingManager, const MWWorld::ESMStore* store); ~MapExtractor(); + void setLocalMap(MWRender::LocalMap* localMap) { mLocalMap = localMap; } + void extractWorldMap(); - void extractLocalMaps(bool forceOverwrite = false); + void extractLocalMaps(const std::vector& activeCells); // Called every frame to process pending extractions void update(); @@ -69,11 +73,10 @@ namespace OMW std::function completionCallback; }; - MWWorld::World& mWorld; - osgViewer::Viewer* mViewer; - MWBase::WindowManager* mWindowManager; std::filesystem::path mWorldMapOutputDir; std::filesystem::path mLocalMapOutputDir; + MWRender::RenderingManager* mRenderingManager; + const MWWorld::ESMStore* mStore; std::unique_ptr mGlobalMap; MWRender::LocalMap* mLocalMap; @@ -85,7 +88,7 @@ namespace OMW void saveWorldMapTexture(); void saveWorldMapInfo(); - void startExtraction(bool forceOverwrite); + void startExtraction(const std::vector& activeCells); void processNextCell(); bool savePendingExtraction(const PendingExtraction& extraction); diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 9a814fb47a..01be0f5ba9 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -613,11 +613,11 @@ namespace MWBase virtual std::string getLocalMapOutputPath() const = 0; ///< Get the local map output path from options or default - virtual void extractWorldMap(const std::string& worldMapOutput) = 0; - ///< Extract world map to the specified directory + virtual void extractWorldMap() = 0; + ///< Extract world map using path from options or default - virtual void extractLocalMaps(const std::string& localMapOutput) = 0; - ///< Extract local maps to the specified directory + virtual void extractLocalMaps() = 0; + ///< Extract local maps using path from options or default virtual bool isMapExtractionActive() const = 0; ///< Check if map extraction is currently in progress diff --git a/apps/openmw/mwlua/worldbindings.cpp b/apps/openmw/mwlua/worldbindings.cpp index dbd842b6f7..978842b4e0 100644 --- a/apps/openmw/mwlua/worldbindings.cpp +++ b/apps/openmw/mwlua/worldbindings.cpp @@ -247,22 +247,20 @@ namespace MWLua api["vfx"] = initWorldVfxBindings(context); - api["extractWorldMap"] = [context, lua = context.mLua](sol::optional worldMapOutput) { + api["extractWorldMap"] = [context, lua = context.mLua]() { checkGameInitialized(lua); context.mLuaManager->addAction( - [worldMapOutput] { - std::string path = worldMapOutput.value_or(""); - MWBase::Environment::get().getWorld()->extractWorldMap(path); + [] { + MWBase::Environment::get().getWorld()->extractWorldMap(); }, "extractWorldMapAction"); }; - api["extractLocalMaps"] = [context, lua = context.mLua](sol::optional localMapOutput) { + api["extractLocalMaps"] = [context, lua = context.mLua]() { checkGameInitialized(lua); context.mLuaManager->addAction( - [localMapOutput] { - std::string path = localMapOutput.value_or(""); - MWBase::Environment::get().getWorld()->extractLocalMaps(path); + [] { + MWBase::Environment::get().getWorld()->extractLocalMaps(); }, "extractLocalMapsAction"); }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 16a8067d90..9b92ae4e5b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -251,7 +251,7 @@ namespace MWWorld } World::World(Resource::ResourceSystem* resourceSystem, int activationDistanceOverride, const std::string& startCell, - const std::filesystem::path& userDataPath) + const std::filesystem::path& userDataPath, const std::string& worldMapOutputPath, const std::string& localMapOutputPath, bool overwriteMaps) : mResourceSystem(resourceSystem) , mLocalScripts(mStore) , mWorldModel(mStore, mReaders) @@ -263,6 +263,9 @@ namespace MWWorld , mUserDataPath(userDataPath) , mActivationDistanceOverride(activationDistanceOverride) , mStartCell(startCell) + , mWorldMapOutputPath(worldMapOutputPath) + , mLocalMapOutputPath(localMapOutputPath) + , mOverwriteMaps(overwriteMaps) , mSwimHeightScale(0.f) , mDistanceToFocusObject(-1.f) , mTeleportEnabled(true) @@ -3913,83 +3916,35 @@ namespace MWWorld actor->setActive(value); } - std::string World::getWorldMapOutputPath() const + void World::extractWorldMap() { - // Try to get from Engine via environment, fallback to default - // Since we can't directly access Engine from World, we'll use a default path - // The actual path from options will be passed through the Lua API - return "./textures/advanced_world_map/custom"; - } - - std::string World::getLocalMapOutputPath() const - { - // Try to get from Engine via environment, fallback to default - // Since we can't directly access Engine from World, we'll use a default path - // The actual path from options will be passed through the Lua API - return "./textures/advanced_world_map/local"; - } - - void World::extractWorldMap(const std::string& worldMapOutput) - { - if (!mRendering) + if (!mMapExtractor) { - throw std::runtime_error("Rendering manager is not initialized"); + mMapExtractor = std::make_unique( + mWorldMapOutputPath, mLocalMapOutputPath, mOverwriteMaps, mRendering.get(), &mStore); } - - osgViewer::Viewer* viewer = mRendering->getViewer(); - if (!viewer) - { - throw std::runtime_error("Viewer is not initialized"); - } - - // If extraction is already in progress, ignore the request - if (mMapExtractor) - { - Log(Debug::Warning) << "Map extraction is already in progress"; - return; - } - - std::string outputPath = worldMapOutput.empty() ? getWorldMapOutputPath() : worldMapOutput; - - MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager(); - mMapExtractor = std::make_unique(*this, viewer, windowManager, outputPath, ""); - - Log(Debug::Info) << "Starting world map extraction to: " << outputPath; mMapExtractor->extractWorldMap(); } - void World::extractLocalMaps(const std::string& localMapOutput) + void World::extractLocalMaps() { - if (!mRendering) + if (!mMapExtractor) { - throw std::runtime_error("Rendering manager is not initialized"); + mMapExtractor = std::make_unique( + mWorldMapOutputPath, mLocalMapOutputPath, mOverwriteMaps, mRendering.get(), &mStore); } - - osgViewer::Viewer* viewer = mRendering->getViewer(); - if (!viewer) + // Set LocalMap from WindowManager + if (auto* localMap = MWBase::Environment::get().getWindowManager()->getLocalMapRender()) { - throw std::runtime_error("Viewer is not initialized"); + mMapExtractor->setLocalMap(localMap); } - - // If extraction is already in progress, ignore the request - if (mMapExtractor) - { - Log(Debug::Warning) << "Map extraction is already in progress"; - return; - } - - std::string outputPath = localMapOutput.empty() ? getLocalMapOutputPath() : localMapOutput; - - MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager(); - mMapExtractor = std::make_unique(*this, viewer, windowManager, "", outputPath); - - Log(Debug::Info) << "Starting local maps extraction to: " << outputPath; - mMapExtractor->extractLocalMaps(false); - Log(Debug::Info) << "Local maps extraction started, will complete during gameplay..."; + const auto& activeCells = mWorldScene->getActiveCells(); + std::vector cells(activeCells.begin(), activeCells.end()); + mMapExtractor->extractLocalMaps(cells); } bool World::isMapExtractionActive() const { - return mMapExtractor != nullptr; + return mMapExtractor && !mMapExtractor->isExtractionComplete(); } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 44989f08bc..cf75443d9e 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -121,6 +121,9 @@ namespace MWWorld int mActivationDistanceOverride; std::string mStartCell; + std::string mWorldMapOutputPath; + std::string mLocalMapOutputPath; + bool mOverwriteMaps; float mSwimHeightScale; @@ -198,7 +201,7 @@ namespace MWWorld void removeContainerScripts(const Ptr& reference) override; World(Resource::ResourceSystem* resourceSystem, int activationDistanceOverride, const std::string& startCell, - const std::filesystem::path& userDataPath); + const std::filesystem::path& userDataPath, const std::string& worldMapOutputPath, const std::string& localMapOutputPath, bool overwriteMaps); void loadData(const Files::Collections& fileCollections, const std::vector& contentFiles, const std::vector& groundcoverFiles, ToUTF8::Utf8Encoder* encoder, @@ -684,11 +687,12 @@ namespace MWWorld void setActorActive(const MWWorld::Ptr& ptr, bool value) override; - std::string getWorldMapOutputPath() const override; - std::string getLocalMapOutputPath() const override; + std::string getWorldMapOutputPath() const override { return mWorldMapOutputPath; } + std::string getLocalMapOutputPath() const override { return mLocalMapOutputPath; } + bool getOverwriteMaps() const { return mOverwriteMaps; } - void extractWorldMap(const std::string& worldMapOutput) override; - void extractLocalMaps(const std::string& localMapOutput) override; + void extractWorldMap() override; + void extractLocalMaps() override; bool isMapExtractionActive() const override; }; } diff --git a/apps/openmw/options.cpp b/apps/openmw/options.cpp index d1f6f9357d..26720bc448 100644 --- a/apps/openmw/options.cpp +++ b/apps/openmw/options.cpp @@ -101,8 +101,8 @@ namespace OpenMW addOption("local-map-output", bpo::value()->default_value(""), "directory to save local map textures (default: textures/advanced_world_map/local)"); - addOption("extract-maps", bpo::value()->implicit_value(true)->default_value(true), - "extract world and local map textures and exit"); + addOption("overwrite-maps", bpo::value()->implicit_value(true)->default_value(false), + "overwrite existing map files during extraction"); return desc; } diff --git a/files/lua_api/openmw/world.lua b/files/lua_api/openmw/world.lua index bbf27a7ce4..84edc95874 100644 --- a/files/lua_api/openmw/world.lua +++ b/files/lua_api/openmw/world.lua @@ -222,22 +222,21 @@ -- @param #number hours Number of hours to advance time --- --- Extract world map to the specified directory. +-- Extract world map using path from --world-map-output option or default path. -- This function generates a world map image and saves it as a PNG file. --- If no path is provided (nil), uses the path from --world-map-output option or default "./textures/advanced_world_map/custom". +-- The output directory is determined by --world-map-output command line option, +-- or defaults to "./textures/advanced_world_map/custom" if not specified. -- @function [parent=#world] extractWorldMap --- @param #string worldMapOutput (optional) Directory path where world map will be saved --- @usage world.extractWorldMap("./maps/world") -- Custom path --- @usage world.extractWorldMap() -- Use default or option path +-- @usage world.extractWorldMap() -- Use path from option or default --- --- Extract local maps to the specified directory. +-- Extract local maps using path from --local-map-output option or default path. -- This function generates map images for all active cells and saves them as PNG files. --- If no path is provided (nil), uses the path from --local-map-output option or default "./textures/advanced_world_map/local". +-- The output directory is determined by --local-map-output command line option, +-- or defaults to "./textures/advanced_world_map/local" if not specified. +-- By default, existing maps are not overwritten. Use --overwrite-maps option to force overwriting. -- @function [parent=#world] extractLocalMaps --- @param #string localMapOutput (optional) Directory path where local maps will be saved --- @usage world.extractLocalMaps("./maps/local") -- Custom path --- @usage world.extractLocalMaps() -- Use default or option path +-- @usage world.extractLocalMaps() -- Use path from option or default --- -- Enable extraction mode for map generation.