Use a separate storage for groundcover data

pull/3206/head
Andrei Kortunov 3 years ago
parent ca64d7bc18
commit 3275440f0d

@ -74,7 +74,7 @@ add_openmw_dir (mwworld
actionequip timestamp actionalchemy cellstore actionapply actioneat actionequip timestamp actionalchemy cellstore actionapply actioneat
store esmstore fallback actionrepair actionsoulgem livecellref actiondoor store esmstore fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader actiontrap cellreflist cellref weather projectilemanager contentloader esmloader actiontrap cellreflist cellref weather projectilemanager
cellpreloader datetimemanager cellpreloader datetimemanager groundcoverstore
) )
add_openmw_dir (mwphysics add_openmw_dir (mwphysics

@ -13,22 +13,12 @@
#include <components/terrain/quadtreenode.hpp> #include <components/terrain/quadtreenode.hpp>
#include <components/shader/shadermanager.hpp> #include <components/shader/shadermanager.hpp>
#include "apps/openmw/mwworld/esmstore.hpp" #include "../mwworld/groundcoverstore.hpp"
#include "apps/openmw/mwbase/environment.hpp"
#include "apps/openmw/mwbase/world.hpp"
#include "vismask.hpp" #include "vismask.hpp"
namespace MWRender namespace MWRender
{ {
std::string getGroundcoverModel(const std::string& id, const MWWorld::ESMStore& groundcoverStore, const MWWorld::ESMStore& store)
{
const ESM::Static* stat = groundcoverStore.get<ESM::Static>().searchStatic(id);
if (!stat)
stat = store.get<ESM::Static>().searchStatic(id);
return stat ? stat->mModel : std::string();
}
class InstancingVisitor : public osg::NodeVisitor class InstancingVisitor : public osg::NodeVisitor
{ {
public: 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<GroundcoverChunkId>(nullptr) : GenericResourceManager<GroundcoverChunkId>(nullptr)
, mSceneManager(sceneManager) , mSceneManager(sceneManager)
, mDensity(density) , mDensity(density)
@ -183,7 +173,6 @@ namespace MWRender
{ {
if (mDensity <=0.f) return; 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 minBound = (center - osg::Vec2f(size/2.f, size/2.f));
osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f)); osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f));
DensityCalculator calculator(mDensity); DensityCalculator calculator(mDensity);
@ -193,21 +182,22 @@ namespace MWRender
{ {
for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY) for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY)
{ {
const ESM::Cell* cell = mGroundcoverStore.get<ESM::Cell>().searchStatic(cellX, cellY); ESM::Cell cell;
if (!cell) continue; mGroundcoverStore.initCell(cell, cellX, cellY);
if (cell.mContextList.empty()) continue;
calculator.reset(); calculator.reset();
std::map<ESM::RefNum, ESM::CellRef> refs; std::map<ESM::RefNum, ESM::CellRef> refs;
for (size_t i=0; i<cell->mContextList.size(); ++i) for (size_t i=0; i<cell.mContextList.size(); ++i)
{ {
unsigned int index = cell->mContextList[i].index; unsigned int index = cell.mContextList[i].index;
if (esm.size() <= index) if (esm.size() <= index)
esm.resize(index+1); esm.resize(index+1);
cell->restore(esm[index], i); cell.restore(esm[index], i);
ESM::CellRef ref; ESM::CellRef ref;
ref.mRefNum.unset(); ref.mRefNum.unset();
bool deleted = false; 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 && refs.find(ref.mRefNum) == refs.end() && !calculator.isInstanceEnabled()) deleted = true;
if (!deleted && !isInChunkBorders(ref, minBound, maxBound)) deleted = true; if (!deleted && !isInChunkBorders(ref, minBound, maxBound)) deleted = true;
@ -220,9 +210,9 @@ namespace MWRender
for (auto& pair : refs) for (auto& pair : refs)
{ {
ESM::CellRef& ref = pair.second; 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()) if (!model.empty())
instances["meshes\\" + model].emplace_back(std::move(ref)); instances[model].emplace_back(std::move(ref));
} }
} }
} }

@ -8,6 +8,7 @@
namespace MWWorld namespace MWWorld
{ {
class ESMStore; class ESMStore;
class GroundcoverStore;
} }
namespace osg namespace osg
{ {
@ -20,7 +21,7 @@ namespace MWRender
class Groundcover : public Resource::GenericResourceManager<GroundcoverChunkId>, public Terrain::QuadTreeWorld::ChunkManager class Groundcover : public Resource::GenericResourceManager<GroundcoverChunkId>, public Terrain::QuadTreeWorld::ChunkManager
{ {
public: 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(); ~Groundcover();
osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override; osg::ref_ptr<osg::Node> 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; float mDensity;
osg::ref_ptr<osg::StateSet> mStateset; osg::ref_ptr<osg::StateSet> mStateset;
osg::ref_ptr<osg::Program> mProgramTemplate; osg::ref_ptr<osg::Program> mProgramTemplate;
/// @note mGroundcoverStore is separated from World's store because groundcover files must not be allowed to corrupt normal content files. const MWWorld::GroundcoverStore& mGroundcoverStore;
const MWWorld::ESMStore& mGroundcoverStore;
typedef std::map<std::string, std::vector<GroundcoverEntry>> InstanceMap; typedef std::map<std::string, std::vector<GroundcoverEntry>> InstanceMap;
osg::ref_ptr<osg::Node> createChunk(InstanceMap& instances, const osg::Vec2f& center); osg::ref_ptr<osg::Node> createChunk(InstanceMap& instances, const osg::Vec2f& center);

@ -49,6 +49,7 @@
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/groundcoverstore.hpp"
#include "../mwgui/loadingscreen.hpp" #include "../mwgui/loadingscreen.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/actorutil.hpp"
@ -294,7 +295,7 @@ namespace MWRender
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, 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) : mViewer(viewer)
, mRootNode(rootNode) , mRootNode(rootNode)
, mResourceSystem(resourceSystem) , mResourceSystem(resourceSystem)

@ -67,6 +67,11 @@ namespace DetourNavigator
struct Settings; struct Settings;
} }
namespace MWWorld
{
class GroundcoverStore;
}
namespace MWRender namespace MWRender
{ {
class StateUpdater; class StateUpdater;
@ -95,7 +100,7 @@ namespace MWRender
public: public:
RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, 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(); ~RenderingManager();
osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation(); osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation();

@ -0,0 +1,54 @@
#include "groundcoverstore.hpp"
#include <components/esmloader/load.hpp>
#include <components/misc/stringops.hpp>
namespace MWWorld
{
void GroundcoverStore::init(const Store<ESM::Static>& statics, const Files::Collections& fileCollections, const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder)
{
EsmLoader::Query query;
query.mLoadStatics = true;
query.mLoadCells = true;
std::vector<ESM::ESMReader> 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;
}
}

@ -0,0 +1,29 @@
#ifndef GAME_MWWORLD_GROUNDCOVER_STORE_H
#define GAME_MWWORLD_GROUNDCOVER_STORE_H
#include <vector>
#include <string>
#include <map>
#include <components/esm/esmreader.hpp>
#include <components/esmloader/esmdata.hpp>
#include <components/files/collections.hpp>
#include "esmstore.hpp"
namespace MWWorld
{
class GroundcoverStore
{
private:
std::map<std::string, std::string> mMeshCache;
std::map<std::pair<int, int>, std::vector<ESM::ESM_Context>> mCellContexts;
public:
void init(const Store<ESM::Static>& statics, const Files::Collections& fileCollections, const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder);
std::string getGroundcoverModel(const std::string& id) const;
void initCell(ESM::Cell& cell, int cellX, int cellY) const;
};
}
#endif

@ -155,13 +155,9 @@ namespace MWWorld
mEsm.resize(contentFiles.size()); mEsm.resize(contentFiles.size());
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
listener->loadingOn(); listener->loadingOn();
loadContentFiles(fileCollections, contentFiles, mStore, mEsm, encoder, listener); loadContentFiles(fileCollections, contentFiles, mStore, mEsm, encoder, listener);
if (!groundcoverFiles.empty()) loadGroundcoverFiles(fileCollections, groundcoverFiles, encoder);
{
std::vector<ESM::ESMReader> tempReaders (groundcoverFiles.size());
loadContentFiles(fileCollections, groundcoverFiles, mGroundcoverStore, tempReaders, encoder, listener, false);
}
listener->loadingOff(); listener->loadingOff();
@ -2948,12 +2944,11 @@ namespace MWWorld
return mScriptsEnabled; return mScriptsEnabled;
} }
void World::loadContentFiles(const Files::Collections& fileCollections, const std::vector<std::string>& content, ESMStore& store, std::vector<ESM::ESMReader>& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener, bool validate) void World::loadContentFiles(const Files::Collections& fileCollections, const std::vector<std::string>& content, ESMStore& store, std::vector<ESM::ESMReader>& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener)
{ {
GameContentLoader gameContentLoader(*listener); GameContentLoader gameContentLoader(*listener);
EsmLoader esmLoader(store, readers, encoder, *listener); EsmLoader esmLoader(store, readers, encoder, *listener);
if (validate) validateMasterFiles(readers);
validateMasterFiles(readers);
gameContentLoader.addLoader(".esm", &esmLoader); gameContentLoader.addLoader(".esm", &esmLoader);
gameContentLoader.addLoader(".esp", &esmLoader); gameContentLoader.addLoader(".esp", &esmLoader);
@ -2982,6 +2977,15 @@ namespace MWWorld
} }
} }
void World::loadGroundcoverFiles(const Files::Collections& fileCollections, const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder)
{
if (!Settings::Manager::getBool("enabled", "Groundcover")) return;
Log(Debug::Info) << "Loading groundcover:";
mGroundcoverStore.init(mStore.get<ESM::Static>(), fileCollections, groundcoverFiles, encoder);
}
bool World::startSpellCast(const Ptr &actor) bool World::startSpellCast(const Ptr &actor)
{ {
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);

@ -15,6 +15,7 @@
#include "timestamp.hpp" #include "timestamp.hpp"
#include "globals.hpp" #include "globals.hpp"
#include "contentloader.hpp" #include "contentloader.hpp"
#include "groundcoverstore.hpp"
namespace osg namespace osg
{ {
@ -80,7 +81,7 @@ namespace MWWorld
std::vector<ESM::ESMReader> mEsm; std::vector<ESM::ESMReader> mEsm;
MWWorld::ESMStore mStore; MWWorld::ESMStore mStore;
MWWorld::ESMStore mGroundcoverStore; GroundcoverStore mGroundcoverStore;
LocalScripts mLocalScripts; LocalScripts mLocalScripts;
MWWorld::Globals mGlobalVariables; MWWorld::Globals mGlobalVariables;
@ -164,10 +165,9 @@ namespace MWWorld
void updateSkyDate(); void updateSkyDate();
// A helper method called automatically during World construction. void loadContentFiles(const Files::Collections& fileCollections, const std::vector<std::string>& content, ESMStore& store, std::vector<ESM::ESMReader>& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener);
void loadContentFiles(const Files::Collections& fileCollections, const std::vector<std::string>& content,
ESMStore& store, std::vector<ESM::ESMReader>& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener, bool validateMasterFiles = true);
void loadGroundcoverFiles(const Files::Collections& fileCollections, const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder);
float feetToGameUnits(float feet); float feetToGameUnits(float feet);
float getActivationDistancePlusTelekinesis(); float getActivationDistancePlusTelekinesis();

@ -215,6 +215,8 @@ namespace EsmLoader
reader.setEncoder(encoder); reader.setEncoder(encoder);
reader.setIndex(static_cast<int>(i)); reader.setIndex(static_cast<int>(i));
reader.open(collection.getPath(file).string()); reader.open(collection.getPath(file).string());
if (query.mLoadCells)
reader.resolveParentFileIndices(readers);
loadEsm(query, readers[i], result); loadEsm(query, readers[i], result);
} }

Loading…
Cancel
Save