1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-04-17 02:42:45 +00:00

isolates groundcover content files (#3208)

Specifications developed in PR #3206 require that groundcover content files must not be allowed to corrupt normal content files. With this PR we simply isolate our existing loading logic by instantiating a separate `ESMStore` for `Groundcover`. In addition, we remove some outdated workarounds.
This commit is contained in:
Bo Svensson 2021-11-13 22:37:53 +00:00 committed by GitHub
parent 68e7a4083e
commit a62b22cd31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 62 additions and 86 deletions

View File

@ -5,6 +5,7 @@
#include <osg/BlendFunc>
#include <osg/Geometry>
#include <osg/VertexAttribDivisor>
#include <osg/Program>
#include <components/esm/esmreader.hpp>
#include <components/sceneutil/lightmanager.hpp>
@ -20,15 +21,12 @@
namespace MWRender
{
std::string getGroundcoverModel(int type, const std::string& id, const MWWorld::ESMStore& store)
std::string getGroundcoverModel(const std::string& id, const MWWorld::ESMStore& groundcoverStore, const MWWorld::ESMStore& store)
{
switch (type)
{
case ESM::REC_STAT:
return store.get<ESM::Static>().searchStatic(id)->mModel;
default:
return std::string();
}
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
@ -155,11 +153,12 @@ namespace MWRender
}
}
Groundcover::Groundcover(Resource::SceneManager* sceneManager, float density, float viewDistance)
Groundcover::Groundcover(Resource::SceneManager* sceneManager, float density, float viewDistance, const MWWorld::ESMStore& store)
: GenericResourceManager<GroundcoverChunkId>(nullptr)
, mSceneManager(sceneManager)
, mDensity(density)
, mStateset(new osg::StateSet)
, mGroundcoverStore(store)
{
setViewDistance(viewDistance);
// MGE uses default alpha settings for groundcover, so we can not rely on alpha properties
@ -176,9 +175,13 @@ namespace MWRender
mProgramTemplate->addBindAttribLocation("aRotation", 7);
}
Groundcover::~Groundcover()
{
}
void Groundcover::collectInstances(InstanceMap& instances, float size, const osg::Vec2f& center)
{
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
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);
@ -188,7 +191,7 @@ namespace MWRender
{
for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY)
{
const ESM::Cell* cell = store.get<ESM::Cell>().searchStatic(cellX, cellY);
const ESM::Cell* cell = mGroundcoverStore.get<ESM::Cell>().searchStatic(cellX, cellY);
if (!cell) continue;
calculator.reset();
@ -204,14 +207,11 @@ namespace MWRender
while(cell->getNextRef(esm[index], ref, deleted))
{
if (deleted) continue;
if (!ref.mRefNum.fromGroundcoverFile()) continue;
if (!calculator.isInstanceEnabled()) continue;
if (!isInChunkBorders(ref, minBound, maxBound)) continue;
Misc::StringUtils::lowerCaseInPlace(ref.mRefID);
int type = store.findStatic(ref.mRefID);
std::string model = getGroundcoverModel(type, ref.mRefID, store);
std::string model = getGroundcoverModel(ref.mRefID, mGroundcoverStore, worldStore);
if (model.empty()) continue;
model = "meshes/" + model;

View File

@ -4,7 +4,15 @@
#include <components/terrain/quadtreeworld.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/esm/loadcell.hpp>
#include <osg/Program>
namespace MWWorld
{
class ESMStore;
}
namespace osg
{
class Program;
}
namespace MWRender
{
@ -12,8 +20,8 @@ namespace MWRender
class Groundcover : public Resource::GenericResourceManager<GroundcoverChunkId>, public Terrain::QuadTreeWorld::ChunkManager
{
public:
Groundcover(Resource::SceneManager* sceneManager, float density, float viewDistance);
~Groundcover() = default;
Groundcover(Resource::SceneManager* sceneManager, float density, float viewDistance, const MWWorld::ESMStore& groundcoverStore);
~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;
@ -35,6 +43,8 @@ namespace MWRender
float mDensity;
osg::ref_ptr<osg::StateSet> mStateset;
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::ESMStore& mGroundcoverStore;
typedef std::map<std::string, std::vector<GroundcoverEntry>> InstanceMap;
osg::ref_ptr<osg::Node> createChunk(InstanceMap& instances, const osg::Vec2f& center);

View File

@ -432,7 +432,6 @@ namespace MWRender
int type = store.findStatic(ref.mRefID);
if (!typeFilter(type,size>=2)) continue;
if (deleted) { refs.erase(ref.mRefNum); continue; }
if (ref.mRefNum.fromGroundcoverFile()) continue;
refs[ref.mRefNum] = std::move(ref);
}
}

View File

@ -294,7 +294,7 @@ namespace MWRender
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const std::string& resourcePath, DetourNavigator::Navigator& navigator)
const std::string& resourcePath, DetourNavigator::Navigator& navigator, const MWWorld::ESMStore& groundcoverStore)
: mViewer(viewer)
, mRootNode(rootNode)
, mResourceSystem(resourceSystem)
@ -450,7 +450,7 @@ namespace MWRender
float density = Settings::Manager::getFloat("density", "Groundcover");
density = std::clamp(density, 0.f, 1.f);
mGroundcover.reset(new Groundcover(mResourceSystem->getSceneManager(), density, groundcoverDistance));
mGroundcover.reset(new Groundcover(mResourceSystem->getSceneManager(), density, groundcoverDistance, groundcoverStore));
static_cast<Terrain::QuadTreeWorld*>(mTerrain.get())->addChunkManager(mGroundcover.get());
mResourceSystem->addResourceManager(mGroundcover.get());
}

View File

@ -95,7 +95,7 @@ namespace MWRender
public:
RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const std::string& resourcePath, DetourNavigator::Navigator& navigator);
const std::string& resourcePath, DetourNavigator::Navigator& navigator, const MWWorld::ESMStore& groundcoverStore);
~RenderingManager();
osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation();

View File

@ -741,11 +741,7 @@ namespace MWWorld
case ESM::REC_NPC_: mNpcs.load(ref, deleted, store); break;
case ESM::REC_PROB: mProbes.load(ref, deleted, store); break;
case ESM::REC_REPA: mRepairs.load(ref, deleted, store); break;
case ESM::REC_STAT:
{
if (ref.mRefNum.fromGroundcoverFile()) return;
mStatics.load(ref, deleted, store); break;
}
case ESM::REC_STAT: mStatics.load(ref, deleted, store); break;
case ESM::REC_WEAP: mWeapons.load(ref, deleted, store); break;
case ESM::REC_BODY: mBodyParts.load(ref, deleted, store); break;

View File

@ -152,23 +152,16 @@ namespace MWWorld
mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0),
mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f)
{
mEsm.resize(contentFiles.size() + groundcoverFiles.size());
mEsm.resize(contentFiles.size());
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
listener->loadingOn();
GameContentLoader gameContentLoader(*listener);
EsmLoader esmLoader(mStore, mEsm, encoder, *listener);
gameContentLoader.addLoader(".esm", &esmLoader);
gameContentLoader.addLoader(".esp", &esmLoader);
gameContentLoader.addLoader(".omwgame", &esmLoader);
gameContentLoader.addLoader(".omwaddon", &esmLoader);
gameContentLoader.addLoader(".project", &esmLoader);
OMWScriptsLoader omwScriptsLoader(*listener, mStore);
gameContentLoader.addLoader(".omwscripts", &omwScriptsLoader);
loadContentFiles(fileCollections, contentFiles, groundcoverFiles, gameContentLoader);
loadContentFiles(fileCollections, contentFiles, mStore, mEsm, encoder, listener);
if (!groundcoverFiles.empty())
{
std::vector<ESM::ESMReader> tempReaders (groundcoverFiles.size());
loadContentFiles(fileCollections, groundcoverFiles, mGroundcoverStore, tempReaders, encoder, listener, false);
}
listener->loadingOff();
@ -176,10 +169,6 @@ namespace MWWorld
if (mEsm[0].getFormat() == 0)
ensureNeededRecords();
// TODO: We can and should validate before we call loadContentFiles().
// Currently we validate here to prevent merge conflicts with groundcover ESMStore fixes.
validateMasterFiles(mEsm);
mCurrentDate.reset(new DateTimeManager());
fillGlobalVariables();
@ -202,7 +191,7 @@ namespace MWWorld
mNavigator = DetourNavigator::makeNavigatorStub();
}
mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, resourcePath, *mNavigator));
mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, resourcePath, *mNavigator, mGroundcoverStore));
mProjectileManager.reset(new ProjectileManager(mRendering->getLightRoot(), resourceSystem, mRendering.get(), mPhysics.get()));
mRendering->preloadCommonAssets();
@ -2959,9 +2948,22 @@ namespace MWWorld
return mScriptsEnabled;
}
void World::loadContentFiles(const Files::Collections& fileCollections,
const std::vector<std::string>& content, const std::vector<std::string>& groundcover, ContentLoader& contentLoader)
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)
{
GameContentLoader gameContentLoader(*listener);
EsmLoader esmLoader(store, readers, encoder, *listener);
if (validate)
validateMasterFiles(readers);
gameContentLoader.addLoader(".esm", &esmLoader);
gameContentLoader.addLoader(".esp", &esmLoader);
gameContentLoader.addLoader(".omwgame", &esmLoader);
gameContentLoader.addLoader(".omwaddon", &esmLoader);
gameContentLoader.addLoader(".project", &esmLoader);
OMWScriptsLoader omwScriptsLoader(*listener, store);
gameContentLoader.addLoader(".omwscripts", &omwScriptsLoader);
int idx = 0;
for (const std::string &file : content)
{
@ -2969,7 +2971,7 @@ namespace MWWorld
const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string());
if (col.doesExist(file))
{
contentLoader.load(col.getPath(file), idx);
gameContentLoader.load(col.getPath(file), idx);
}
else
{
@ -2978,24 +2980,6 @@ namespace MWWorld
}
idx++;
}
ESM::GroundcoverIndex = idx;
for (const std::string &file : groundcover)
{
boost::filesystem::path filename(file);
const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string());
if (col.doesExist(file))
{
contentLoader.load(col.getPath(file), idx);
}
else
{
std::string message = "Failed loading " + file + ": the groundcover file does not exist";
throw std::runtime_error(message);
}
idx++;
}
}
bool World::startSpellCast(const Ptr &actor)

View File

@ -80,6 +80,7 @@ namespace MWWorld
std::vector<ESM::ESMReader> mEsm;
MWWorld::ESMStore mStore;
MWWorld::ESMStore mGroundcoverStore;
LocalScripts mLocalScripts;
MWWorld::Globals mGlobalVariables;
@ -163,14 +164,10 @@ namespace MWWorld
void updateSkyDate();
/**
* @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon)
* @param fileCollections- Container which holds content file names and their paths
* @param content - Container which holds content file names
* @param contentLoader -
*/
void loadContentFiles(const Files::Collections& fileCollections,
const std::vector<std::string>& content, const std::vector<std::string>& groundcover, ContentLoader& contentLoader);
// 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, bool validateMasterFiles = true);
float feetToGameUnits(float feet);
float getActivationDistancePlusTelekinesis();

View File

@ -5,11 +5,6 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
namespace ESM
{
int GroundcoverIndex = std::numeric_limits<int>::max();
}
void ESM::RefNum::load (ESMReader& esm, bool wide, const std::string& tag)
{
if (wide)

View File

@ -12,7 +12,6 @@ namespace ESM
class ESMReader;
const int UnbreakableLock = std::numeric_limits<int>::max();
extern int GroundcoverIndex;
struct RefNum
{
@ -27,10 +26,6 @@ namespace ESM
inline bool isSet() const { return mIndex != 0 || mContentFile != -1; }
inline void unset() { *this = {0, -1}; }
// Note: this method should not be used for objects with invalid RefNum
// (for example, for objects from disabled plugins in savegames).
inline bool fromGroundcoverFile() const { return mContentFile >= GroundcoverIndex; }
};
/* Cell reference. This represents ONE object (of many) inside the