forked from mirror/openmw-tes3mp
Add basic cell preloader class
Not properly in use yet, but seems to be working.
This commit is contained in:
parent
e055ae094a
commit
6f9ca0f68f
9 changed files with 228 additions and 3 deletions
|
@ -67,6 +67,7 @@ add_openmw_dir (mwworld
|
|||
actionequip timestamp actionalchemy cellstore actionapply actioneat
|
||||
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
|
||||
contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager
|
||||
cellpreloader
|
||||
)
|
||||
|
||||
add_openmw_dir (mwphysics
|
||||
|
|
|
@ -690,6 +690,11 @@ namespace MWPhysics
|
|||
delete mBroadphase;
|
||||
}
|
||||
|
||||
Resource::BulletShapeManager *PhysicsSystem::getShapeManager()
|
||||
{
|
||||
return mShapeManager.get();
|
||||
}
|
||||
|
||||
bool PhysicsSystem::toggleDebugRendering()
|
||||
{
|
||||
mDebugDrawEnabled = !mDebugDrawEnabled;
|
||||
|
|
|
@ -53,6 +53,8 @@ namespace MWPhysics
|
|||
PhysicsSystem (Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode);
|
||||
~PhysicsSystem ();
|
||||
|
||||
Resource::BulletShapeManager* getShapeManager();
|
||||
|
||||
void enableWater(float height);
|
||||
void setWaterHeight(float height);
|
||||
void disableWater();
|
||||
|
|
148
apps/openmw/mwworld/cellpreloader.cpp
Normal file
148
apps/openmw/mwworld/cellpreloader.cpp
Normal file
|
@ -0,0 +1,148 @@
|
|||
#include "cellpreloader.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/resource/bulletshapemanager.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "cellstore.hpp"
|
||||
#include "manualref.hpp"
|
||||
#include "class.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
||||
struct ListModelsVisitor
|
||||
{
|
||||
ListModelsVisitor(std::vector<std::string>& out)
|
||||
: mOut(out)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool operator()(const MWWorld::ConstPtr& ptr)
|
||||
{
|
||||
std::string model = ptr.getClass().getModel(ptr);
|
||||
if (!model.empty())
|
||||
mOut.push_back(model);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string>& mOut;
|
||||
};
|
||||
|
||||
class PreloadItem : public SceneUtil::WorkItem
|
||||
{
|
||||
public:
|
||||
/// Constructor to be called from the main thread.
|
||||
PreloadItem(const MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager)
|
||||
: mSceneManager(sceneManager)
|
||||
, mBulletShapeManager(bulletShapeManager)
|
||||
{
|
||||
if (cell->getState() == MWWorld::CellStore::State_Loaded)
|
||||
{
|
||||
ListModelsVisitor visitor (mMeshes);
|
||||
cell->forEachConst(visitor);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::vector<std::string>& objectIds = cell->getPreloadedIds();
|
||||
|
||||
// could possibly build the model list in the worker thread if we manage to make the Store thread safe
|
||||
for (std::vector<std::string>::const_iterator it = objectIds.begin(); it != objectIds.end(); ++it)
|
||||
{
|
||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), *it);
|
||||
std::string model = ref.getPtr().getClass().getModel(ref.getPtr());
|
||||
if (!model.empty())
|
||||
mMeshes.push_back(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Preload work to be called from the worker thread.
|
||||
virtual void doWork()
|
||||
{
|
||||
for (MeshList::const_iterator it = mMeshes.begin(); it != mMeshes.end(); ++it)
|
||||
{
|
||||
try
|
||||
{
|
||||
mPreloadedNodes.push_back(mSceneManager->getTemplate(*it));
|
||||
mPreloadedShapes.push_back(mBulletShapeManager->getShape(*it));
|
||||
|
||||
// TODO: do a createInstance() and hold on to it since we can make use of it when the cell goes active
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
// ignore error for now, would spam the log too much
|
||||
// error will be shown when visiting the cell
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::vector<std::string> MeshList;
|
||||
MeshList mMeshes;
|
||||
Resource::SceneManager* mSceneManager;
|
||||
Resource::BulletShapeManager* mBulletShapeManager;
|
||||
|
||||
// keep a ref to the loaded object to make sure it stays loaded as long as this cell is in the preloaded state
|
||||
std::vector<osg::ref_ptr<const osg::Node> > mPreloadedNodes;
|
||||
std::vector<osg::ref_ptr<const Resource::BulletShape> > mPreloadedShapes;
|
||||
};
|
||||
|
||||
CellPreloader::CellPreloader(Resource::SceneManager *sceneManager, Resource::BulletShapeManager* bulletShapeManager)
|
||||
: mSceneManager(sceneManager)
|
||||
, mBulletShapeManager(bulletShapeManager)
|
||||
{
|
||||
mWorkQueue = new SceneUtil::WorkQueue;
|
||||
}
|
||||
|
||||
void CellPreloader::preload(const CellStore *cell, double timestamp)
|
||||
{
|
||||
if (!mWorkQueue)
|
||||
{
|
||||
std::cerr << "can't preload, no work queue set " << std::endl;
|
||||
return;
|
||||
}
|
||||
if (cell->getState() == CellStore::State_Unloaded)
|
||||
{
|
||||
std::cerr << "can't preload objects for unloaded cell" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
PreloadMap::iterator found = mPreloadCells.find(cell);
|
||||
if (found != mPreloadCells.end())
|
||||
{
|
||||
// already preloaded, nothing to do other than updating the timestamp
|
||||
found->second.mTimeStamp = timestamp;
|
||||
return;
|
||||
}
|
||||
|
||||
osg::ref_ptr<PreloadItem> item (new PreloadItem(cell, mSceneManager, mBulletShapeManager));
|
||||
mWorkQueue->addWorkItem(item);
|
||||
|
||||
mPreloadCells[cell] = PreloadEntry(timestamp, item);
|
||||
}
|
||||
|
||||
void CellPreloader::updateCache(double timestamp)
|
||||
{
|
||||
// time (in seconds) to keep a preloaded cell in cache after it's no longer needed
|
||||
const double expiryTime = 60.0;
|
||||
|
||||
for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();)
|
||||
{
|
||||
if (it->second.mTimeStamp < timestamp - expiryTime)
|
||||
mPreloadCells.erase(it++);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void CellPreloader::setWorkQueue(osg::ref_ptr<SceneUtil::WorkQueue> workQueue)
|
||||
{
|
||||
mWorkQueue = workQueue;
|
||||
}
|
||||
|
||||
}
|
60
apps/openmw/mwworld/cellpreloader.hpp
Normal file
60
apps/openmw/mwworld/cellpreloader.hpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
#ifndef OPENMW_MWWORLD_CELLPRELOADER_H
|
||||
#define OPENMW_MWWORLD_CELLPRELOADER_H
|
||||
|
||||
#include <map>
|
||||
#include <osg/ref_ptr>
|
||||
#include <components/sceneutil/workqueue.hpp>
|
||||
|
||||
namespace Resource
|
||||
{
|
||||
class SceneManager;
|
||||
class BulletShapeManager;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class CellStore;
|
||||
|
||||
class CellPreloader
|
||||
{
|
||||
public:
|
||||
CellPreloader(Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager);
|
||||
|
||||
/// Ask a background thread to preload rendering meshes and collision shapes for objects in this cell.
|
||||
/// @note The cell itself must be in State_Loaded or State_Preloaded.
|
||||
void preload(const MWWorld::CellStore* cell, double timestamp);
|
||||
|
||||
/// Removes preloaded cells that have not had a preload request for a while.
|
||||
void updateCache(double timestamp);
|
||||
|
||||
void setWorkQueue(osg::ref_ptr<SceneUtil::WorkQueue> workQueue);
|
||||
|
||||
private:
|
||||
Resource::SceneManager* mSceneManager;
|
||||
Resource::BulletShapeManager* mBulletShapeManager;
|
||||
osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;
|
||||
|
||||
struct PreloadEntry
|
||||
{
|
||||
PreloadEntry(double timestamp, osg::ref_ptr<SceneUtil::WorkItem> workItem)
|
||||
: mTimeStamp(timestamp)
|
||||
, mWorkItem(workItem)
|
||||
{
|
||||
}
|
||||
PreloadEntry()
|
||||
: mTimeStamp(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
double mTimeStamp;
|
||||
osg::ref_ptr<SceneUtil::WorkItem> mWorkItem;
|
||||
};
|
||||
typedef std::map<const MWWorld::CellStore*, PreloadEntry> PreloadMap;
|
||||
|
||||
// Cells that are currently being preloaded, or have already finished preloading
|
||||
PreloadMap mPreloadCells;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -333,6 +333,11 @@ namespace MWWorld
|
|||
return mState;
|
||||
}
|
||||
|
||||
const std::vector<std::string> &CellStore::getPreloadedIds() const
|
||||
{
|
||||
return mIds;
|
||||
}
|
||||
|
||||
bool CellStore::hasState() const
|
||||
{
|
||||
return mHasState;
|
||||
|
|
|
@ -204,6 +204,9 @@ namespace MWWorld
|
|||
|
||||
State getState() const;
|
||||
|
||||
const std::vector<std::string>& getPreloadedIds() const;
|
||||
///< Get Ids of objects in this cell, only valid in State_Preloaded
|
||||
|
||||
bool hasState() const;
|
||||
///< Does this cell have state that needs to be stored in a saved game file?
|
||||
|
||||
|
|
|
@ -46,9 +46,7 @@ add_component_dir (resource
|
|||
|
||||
add_component_dir (sceneutil
|
||||
clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller
|
||||
lightmanager lightutil positionattitudetransform
|
||||
# not used yet
|
||||
#workqueue
|
||||
lightmanager lightutil positionattitudetransform workqueue
|
||||
)
|
||||
|
||||
add_component_dir (nif
|
||||
|
|
|
@ -54,6 +54,9 @@ namespace Resource
|
|||
|
||||
void ResourceSystem::updateCache(double referenceTime)
|
||||
{
|
||||
// TODO: call updateCache from the worker thread so the main thread isn't held up by the delete operations
|
||||
// change ObjectCache to not hold lock while the unref happens
|
||||
|
||||
osg::Timer timer;
|
||||
for (std::vector<ResourceManager*>::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it)
|
||||
(*it)->updateCache(referenceTime);
|
||||
|
|
Loading…
Reference in a new issue