From 3275440f0de2eb09cf045b54769777740ffc5f81 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 29 Oct 2021 16:47:17 +0400 Subject: [PATCH] Use a separate storage for groundcover data --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwrender/groundcover.cpp | 32 +++++--------- apps/openmw/mwrender/groundcover.hpp | 6 +-- apps/openmw/mwrender/renderingmanager.cpp | 3 +- apps/openmw/mwrender/renderingmanager.hpp | 7 ++- apps/openmw/mwworld/groundcoverstore.cpp | 54 +++++++++++++++++++++++ apps/openmw/mwworld/groundcoverstore.hpp | 29 ++++++++++++ apps/openmw/mwworld/worldimp.cpp | 22 +++++---- apps/openmw/mwworld/worldimp.hpp | 8 ++-- components/esmloader/load.cpp | 2 + 10 files changed, 125 insertions(+), 40 deletions(-) create mode 100644 apps/openmw/mwworld/groundcoverstore.cpp create mode 100644 apps/openmw/mwworld/groundcoverstore.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index f22204eb8c..7aa2928da5 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,7 +74,7 @@ add_openmw_dir (mwworld actionequip timestamp actionalchemy cellstore actionapply actioneat store esmstore fallback actionrepair actionsoulgem livecellref actiondoor contentloader esmloader actiontrap cellreflist cellref weather projectilemanager - cellpreloader datetimemanager + cellpreloader datetimemanager groundcoverstore ) add_openmw_dir (mwphysics diff --git a/apps/openmw/mwrender/groundcover.cpp b/apps/openmw/mwrender/groundcover.cpp index 1cabf564e2..a39e31bb4d 100644 --- a/apps/openmw/mwrender/groundcover.cpp +++ b/apps/openmw/mwrender/groundcover.cpp @@ -13,22 +13,12 @@ #include #include -#include "apps/openmw/mwworld/esmstore.hpp" -#include "apps/openmw/mwbase/environment.hpp" -#include "apps/openmw/mwbase/world.hpp" +#include "../mwworld/groundcoverstore.hpp" #include "vismask.hpp" namespace MWRender { - std::string getGroundcoverModel(const std::string& id, const MWWorld::ESMStore& groundcoverStore, const MWWorld::ESMStore& store) - { - const ESM::Static* stat = groundcoverStore.get().searchStatic(id); - if (!stat) - stat = store.get().searchStatic(id); - return stat ? stat->mModel : std::string(); - } - class InstancingVisitor : public osg::NodeVisitor { public: @@ -153,7 +143,7 @@ namespace MWRender } } - Groundcover::Groundcover(Resource::SceneManager* sceneManager, float density, float viewDistance, const MWWorld::ESMStore& store) + Groundcover::Groundcover(Resource::SceneManager* sceneManager, float density, float viewDistance, const MWWorld::GroundcoverStore& store) : GenericResourceManager(nullptr) , mSceneManager(sceneManager) , mDensity(density) @@ -183,7 +173,6 @@ namespace MWRender { if (mDensity <=0.f) return; - const MWWorld::ESMStore& worldStore = MWBase::Environment::get().getWorld()->getStore(); osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f)); osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f)); DensityCalculator calculator(mDensity); @@ -193,21 +182,22 @@ namespace MWRender { for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY) { - const ESM::Cell* cell = mGroundcoverStore.get().searchStatic(cellX, cellY); - if (!cell) continue; + ESM::Cell cell; + mGroundcoverStore.initCell(cell, cellX, cellY); + if (cell.mContextList.empty()) continue; calculator.reset(); std::map refs; - for (size_t i=0; imContextList.size(); ++i) + for (size_t i=0; imContextList[i].index; + unsigned int index = cell.mContextList[i].index; if (esm.size() <= index) esm.resize(index+1); - cell->restore(esm[index], i); + cell.restore(esm[index], i); ESM::CellRef ref; ref.mRefNum.unset(); bool deleted = false; - while(cell->getNextRef(esm[index], ref, deleted)) + while(cell.getNextRef(esm[index], ref, deleted)) { if (!deleted && refs.find(ref.mRefNum) == refs.end() && !calculator.isInstanceEnabled()) deleted = true; if (!deleted && !isInChunkBorders(ref, minBound, maxBound)) deleted = true; @@ -220,9 +210,9 @@ namespace MWRender for (auto& pair : refs) { ESM::CellRef& ref = pair.second; - const std::string& model = getGroundcoverModel(ref.mRefID, mGroundcoverStore, worldStore); + const std::string& model = mGroundcoverStore.getGroundcoverModel(ref.mRefID); if (!model.empty()) - instances["meshes\\" + model].emplace_back(std::move(ref)); + instances[model].emplace_back(std::move(ref)); } } } diff --git a/apps/openmw/mwrender/groundcover.hpp b/apps/openmw/mwrender/groundcover.hpp index ea0cfa9b62..26ed8530aa 100644 --- a/apps/openmw/mwrender/groundcover.hpp +++ b/apps/openmw/mwrender/groundcover.hpp @@ -8,6 +8,7 @@ namespace MWWorld { class ESMStore; + class GroundcoverStore; } namespace osg { @@ -20,7 +21,7 @@ namespace MWRender class Groundcover : public Resource::GenericResourceManager, public Terrain::QuadTreeWorld::ChunkManager { public: - Groundcover(Resource::SceneManager* sceneManager, float density, float viewDistance, const MWWorld::ESMStore& groundcoverStore); + Groundcover(Resource::SceneManager* sceneManager, float density, float viewDistance, const MWWorld::GroundcoverStore& store); ~Groundcover(); osg::ref_ptr getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override; @@ -43,8 +44,7 @@ namespace MWRender float mDensity; osg::ref_ptr mStateset; osg::ref_ptr mProgramTemplate; - /// @note mGroundcoverStore is separated from World's store because groundcover files must not be allowed to corrupt normal content files. - const MWWorld::ESMStore& mGroundcoverStore; + const MWWorld::GroundcoverStore& mGroundcoverStore; typedef std::map> InstanceMap; osg::ref_ptr createChunk(InstanceMap& instances, const osg::Vec2f& center); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 2ba18378f9..583df469dc 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -49,6 +49,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/groundcoverstore.hpp" #include "../mwgui/loadingscreen.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwmechanics/actorutil.hpp" @@ -294,7 +295,7 @@ namespace MWRender RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr rootNode, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, - const std::string& resourcePath, DetourNavigator::Navigator& navigator, const MWWorld::ESMStore& groundcoverStore) + const std::string& resourcePath, DetourNavigator::Navigator& navigator, const MWWorld::GroundcoverStore& groundcoverStore) : mViewer(viewer) , mRootNode(rootNode) , mResourceSystem(resourceSystem) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 99d0bb5f5e..bcfa64ec14 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -67,6 +67,11 @@ namespace DetourNavigator struct Settings; } +namespace MWWorld +{ + class GroundcoverStore; +} + namespace MWRender { class StateUpdater; @@ -95,7 +100,7 @@ namespace MWRender public: RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr rootNode, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, - const std::string& resourcePath, DetourNavigator::Navigator& navigator, const MWWorld::ESMStore& groundcoverStore); + const std::string& resourcePath, DetourNavigator::Navigator& navigator, const MWWorld::GroundcoverStore& groundcoverStore); ~RenderingManager(); osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation(); diff --git a/apps/openmw/mwworld/groundcoverstore.cpp b/apps/openmw/mwworld/groundcoverstore.cpp new file mode 100644 index 0000000000..543cd3e0d8 --- /dev/null +++ b/apps/openmw/mwworld/groundcoverstore.cpp @@ -0,0 +1,54 @@ +#include "groundcoverstore.hpp" + +#include +#include + +namespace MWWorld +{ + void GroundcoverStore::init(const Store& statics, const Files::Collections& fileCollections, const std::vector& groundcoverFiles, ToUTF8::Utf8Encoder* encoder) + { + EsmLoader::Query query; + query.mLoadStatics = true; + query.mLoadCells = true; + + std::vector readers(groundcoverFiles.size()); + const EsmLoader::EsmData content = EsmLoader::loadEsmData(query, groundcoverFiles, fileCollections, readers, encoder); + + for (const ESM::Static& stat : statics) + { + std::string id = Misc::StringUtils::lowerCase(stat.mId); + mMeshCache[id] = "meshes\\" + Misc::StringUtils::lowerCase(stat.mModel); + } + + for (const ESM::Static& stat : content.mStatics) + { + std::string id = Misc::StringUtils::lowerCase(stat.mId); + mMeshCache[id] = "meshes\\" + Misc::StringUtils::lowerCase(stat.mModel); + } + + for (const ESM::Cell& cell : content.mCells) + { + if (!cell.isExterior()) continue; + auto cellIndex = std::make_pair(cell.getCellId().mIndex.mX, cell.getCellId().mIndex.mY); + mCellContexts[cellIndex] = std::move(cell.mContextList); + } + } + + std::string GroundcoverStore::getGroundcoverModel(const std::string& id) const + { + std::string idLower = Misc::StringUtils::lowerCase(id); + auto search = mMeshCache.find(idLower); + if (search == mMeshCache.end()) return std::string(); + + return search->second; + } + + void GroundcoverStore::initCell(ESM::Cell& cell, int cellX, int cellY) const + { + cell.blank(); + + auto searchCell = mCellContexts.find(std::make_pair(cellX, cellY)); + if (searchCell != mCellContexts.end()) + cell.mContextList = searchCell->second; + } +} diff --git a/apps/openmw/mwworld/groundcoverstore.hpp b/apps/openmw/mwworld/groundcoverstore.hpp new file mode 100644 index 0000000000..197be2a998 --- /dev/null +++ b/apps/openmw/mwworld/groundcoverstore.hpp @@ -0,0 +1,29 @@ +#ifndef GAME_MWWORLD_GROUNDCOVER_STORE_H +#define GAME_MWWORLD_GROUNDCOVER_STORE_H + +#include +#include +#include + +#include +#include +#include + +#include "esmstore.hpp" + +namespace MWWorld +{ + class GroundcoverStore + { + private: + std::map mMeshCache; + std::map, std::vector> mCellContexts; + + public: + void init(const Store& statics, const Files::Collections& fileCollections, const std::vector& groundcoverFiles, ToUTF8::Utf8Encoder* encoder); + std::string getGroundcoverModel(const std::string& id) const; + void initCell(ESM::Cell& cell, int cellX, int cellY) const; + }; +} + +#endif diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f434d059da..bb97f23407 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -155,13 +155,9 @@ namespace MWWorld mEsm.resize(contentFiles.size()); Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); listener->loadingOn(); - + loadContentFiles(fileCollections, contentFiles, mStore, mEsm, encoder, listener); - if (!groundcoverFiles.empty()) - { - std::vector tempReaders (groundcoverFiles.size()); - loadContentFiles(fileCollections, groundcoverFiles, mGroundcoverStore, tempReaders, encoder, listener, false); - } + loadGroundcoverFiles(fileCollections, groundcoverFiles, encoder); listener->loadingOff(); @@ -2948,12 +2944,11 @@ namespace MWWorld return mScriptsEnabled; } - void World::loadContentFiles(const Files::Collections& fileCollections, const std::vector& content, ESMStore& store, std::vector& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener, bool validate) + void World::loadContentFiles(const Files::Collections& fileCollections, const std::vector& content, ESMStore& store, std::vector& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener) { GameContentLoader gameContentLoader(*listener); EsmLoader esmLoader(store, readers, encoder, *listener); - if (validate) - validateMasterFiles(readers); + validateMasterFiles(readers); gameContentLoader.addLoader(".esm", &esmLoader); gameContentLoader.addLoader(".esp", &esmLoader); @@ -2982,6 +2977,15 @@ namespace MWWorld } } + void World::loadGroundcoverFiles(const Files::Collections& fileCollections, const std::vector& groundcoverFiles, ToUTF8::Utf8Encoder* encoder) + { + if (!Settings::Manager::getBool("enabled", "Groundcover")) return; + + Log(Debug::Info) << "Loading groundcover:"; + + mGroundcoverStore.init(mStore.get(), fileCollections, groundcoverFiles, encoder); + } + bool World::startSpellCast(const Ptr &actor) { MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index c8eebd9a8b..753169107e 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -15,6 +15,7 @@ #include "timestamp.hpp" #include "globals.hpp" #include "contentloader.hpp" +#include "groundcoverstore.hpp" namespace osg { @@ -80,7 +81,7 @@ namespace MWWorld std::vector mEsm; MWWorld::ESMStore mStore; - MWWorld::ESMStore mGroundcoverStore; + GroundcoverStore mGroundcoverStore; LocalScripts mLocalScripts; MWWorld::Globals mGlobalVariables; @@ -164,10 +165,9 @@ namespace MWWorld void updateSkyDate(); - // A helper method called automatically during World construction. - void loadContentFiles(const Files::Collections& fileCollections, const std::vector& content, - ESMStore& store, std::vector& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener, bool validateMasterFiles = true); + void loadContentFiles(const Files::Collections& fileCollections, const std::vector& content, ESMStore& store, std::vector& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener); + void loadGroundcoverFiles(const Files::Collections& fileCollections, const std::vector& groundcoverFiles, ToUTF8::Utf8Encoder* encoder); float feetToGameUnits(float feet); float getActivationDistancePlusTelekinesis(); diff --git a/components/esmloader/load.cpp b/components/esmloader/load.cpp index 9879f33274..673c272e59 100644 --- a/components/esmloader/load.cpp +++ b/components/esmloader/load.cpp @@ -215,6 +215,8 @@ namespace EsmLoader reader.setEncoder(encoder); reader.setIndex(static_cast(i)); reader.open(collection.getPath(file).string()); + if (query.mLoadCells) + reader.resolveParentFileIndices(readers); loadEsm(query, readers[i], result); }