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:
parent
68e7a4083e
commit
a62b22cd31
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user