1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-31 19:20:26 +00:00

Merge pull request #3206 from akortunov/groundcover_loading

Use a separate storage for groundcover data
This commit is contained in:
Bret Curtis 2021-11-29 20:09:21 +01:00 committed by GitHub
commit 246912f73a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 125 additions and 40 deletions

View File

@ -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

View File

@ -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));
} }
} }
} }

View File

@ -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);

View File

@ -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"
@ -293,7 +294,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)

View File

@ -67,6 +67,11 @@ namespace DetourNavigator
struct Settings; struct Settings;
} }
namespace MWWorld
{
class GroundcoverStore;
}
namespace MWRender namespace MWRender
{ {
class StateUpdater; class StateUpdater;
@ -94,7 +99,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();

View File

@ -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;
}
}

View File

@ -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

View File

@ -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();
@ -2938,12 +2934,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);
@ -2972,6 +2967,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);

View File

@ -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();

View File

@ -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);
} }