Merge remote-tracking branch 'scrawl/resource'

coverity_scan
Marc Zinnschlag 8 years ago
commit da6dcfc49e

@ -224,8 +224,6 @@ void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::Land newLand (land);
newLand.mEsm = 0; // avoid potential dangling pointer (ESMReader isn't needed anyway,
// because record is already fully loaded)
newLand.mPlugin = 0;
if (land.mDataTypes & ESM::Land::DATA_VTEX)

@ -100,7 +100,7 @@ void CSVRender::Object::update()
{
std::string path = "meshes\\" + model;
mResourceSystem->getSceneManager()->createInstance(path, mBaseNode);
mResourceSystem->getSceneManager()->getInstance(path, mBaseNode);
}
catch (std::exception& e)
{

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

@ -20,7 +20,7 @@
#include <components/sdlutil/imagetosurface.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/texturemanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/compiler/extensions0.hpp>
@ -448,8 +448,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get()));
mResourceSystem->getTextureManager()->setUnRefImageDataAfterApply(true);
mResourceSystem->getTextureManager()->setFilterSettings(
mResourceSystem->getSceneManager()->setUnRefImageDataAfterApply(false); // keep to Off for now to allow better state sharing
mResourceSystem->getSceneManager()->setFilterSettings(
Settings::Manager::getString("texture mag filter", "General"),
Settings::Manager::getString("texture min filter", "General"),
Settings::Manager::getString("texture mipmap", "General"),
@ -651,6 +651,8 @@ void OMW::Engine::go()
}
else if (!mSkipMenu)
{
mEnvironment.getWorld()->preloadCommonAssets();
// start in main menu
mEnvironment.getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
try

@ -95,6 +95,8 @@ namespace MWBase
virtual ~World() {}
virtual void preloadCommonAssets() = 0;
virtual void startNewGame (bool bypass) = 0;
///< \param bypass Bypass regular game start.

@ -182,6 +182,29 @@ namespace MWClass
return "";
}
void Creature::getModelsToPreload(const MWWorld::Ptr &ptr, std::vector<std::string> &models) const
{
std::string model = getModel(ptr);
if (!model.empty())
models.push_back(model);
// FIXME: use const version of InventoryStore functions once they are available
if (ptr.getClass().hasInventoryStore(ptr))
{
MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr);
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
{
MWWorld::ContainerStoreIterator equipped = invStore.getSlot(slot);
if (equipped != invStore.end())
{
model = equipped->getClass().getModel(*equipped);
if (!model.empty())
models.push_back(model);
}
}
}
}
std::string Creature::getName (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();

@ -101,6 +101,9 @@ namespace MWClass
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const;
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel().
virtual bool
isActor() const {
return true;

@ -6,6 +6,7 @@
#include "../mwmechanics/levelledlist.hpp"
#include "../mwworld/customdata.hpp"
#include "../mwmechanics/creaturestats.hpp"
namespace MWClass
{
@ -53,6 +54,22 @@ namespace MWClass
registerClass (typeid (ESM::CreatureLevList).name(), instance);
}
void CreatureLevList::getModelsToPreload(const MWWorld::Ptr &ptr, std::vector<std::string> &models) const
{
const MWWorld::LiveCellRef<ESM::CreatureLevList> *ref = ptr.get<ESM::CreatureLevList>();
for (std::vector<ESM::LevelledListBase::LevelItem>::const_iterator it = ref->mBase->mList.begin(); it != ref->mBase->mList.end(); ++it)
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if (it->mLevel > player.getClass().getCreatureStats(player).getLevel())
continue;
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
MWWorld::ManualRef ref(store, it->mId);
ref.getPtr().getClass().getModelsToPreload(ref.getPtr(), models);
}
}
void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, const std::string& model, MWRender::RenderingInterface &renderingInterface) const
{
ensureCustomData(ptr);

@ -17,6 +17,9 @@ namespace MWClass
static void registerSelf();
virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const;
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel().
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering

@ -37,6 +37,7 @@
#include "../mwrender/objects.hpp"
#include "../mwrender/renderinginterface.hpp"
#include "../mwrender/npcanimation.hpp"
#include "../mwgui/tooltips.hpp"
@ -429,6 +430,91 @@ namespace MWClass
return model;
}
void Npc::getModelsToPreload(const MWWorld::Ptr &ptr, std::vector<std::string> &models) const
{
const MWWorld::LiveCellRef<ESM::NPC> *npc = ptr.get<ESM::NPC>();
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().search(npc->mBase->mRace);
if(race && race->mData.mFlags & ESM::Race::Beast)
models.push_back("meshes\\base_animkna.nif");
// keep these always loaded just in case
models.push_back("meshes/xargonian_swimkna.nif");
models.push_back("meshes/xbase_anim_female.nif");
models.push_back("meshes/xbase_anim.nif");
if (!npc->mBase->mModel.empty())
models.push_back("meshes/"+npc->mBase->mModel);
if (!npc->mBase->mHead.empty())
{
const ESM::BodyPart* head = MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>().search(npc->mBase->mHead);
if (head)
models.push_back("meshes/"+head->mModel);
}
if (!npc->mBase->mHair.empty())
{
const ESM::BodyPart* hair = MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>().search(npc->mBase->mHair);
if (hair)
models.push_back("meshes/"+hair->mModel);
}
bool female = (npc->mBase->mFlags & ESM::NPC::Female);
// FIXME: use const version of InventoryStore functions once they are available
// preload equipped items
if (ptr.getClass().hasInventoryStore(ptr))
{
MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr);
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
{
MWWorld::ContainerStoreIterator equipped = invStore.getSlot(slot);
if (equipped != invStore.end())
{
std::vector<ESM::PartReference> parts;
if(equipped->getTypeName() == typeid(ESM::Clothing).name())
{
const ESM::Clothing *clothes = equipped->get<ESM::Clothing>()->mBase;
parts = clothes->mParts.mParts;
}
else if(equipped->getTypeName() == typeid(ESM::Armor).name())
{
const ESM::Armor *armor = equipped->get<ESM::Armor>()->mBase;
parts = armor->mParts.mParts;
}
else
{
std::string model = equipped->getClass().getModel(*equipped);
if (!model.empty())
models.push_back(model);
}
for (std::vector<ESM::PartReference>::const_iterator it = parts.begin(); it != parts.end(); ++it)
{
std::string partname = female ? it->mFemale : it->mMale;
if (partname.empty())
partname = female ? it->mMale : it->mFemale;
const ESM::BodyPart* part = MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>().search(partname);
if (part && !part->mModel.empty())
models.push_back("meshes/"+part->mModel);
}
}
}
}
// preload body parts
if (race)
{
const std::vector<const ESM::BodyPart*>& parts = MWRender::NpcAnimation::getBodyParts(Misc::StringUtils::lowerCase(race->mId), female, false, false);
for (std::vector<const ESM::BodyPart*>::const_iterator it = parts.begin(); it != parts.end(); ++it)
{
const ESM::BodyPart* part = *it;
if (part && !part->mModel.empty())
models.push_back("meshes/"+part->mModel);
}
}
}
std::string Npc::getName (const MWWorld::ConstPtr& ptr) const
{
if(ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf())

@ -75,6 +75,9 @@ namespace MWClass
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const;
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel().
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
///< Generate action for activation

@ -87,7 +87,7 @@ namespace MWGui
mAvatarImage->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked);
mAvatarImage->setRenderItemTexture(mPreviewTexture.get());
mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f));
mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
getWidget(mItemView, "ItemView");
mItemView->eventItemClicked += MyGUI::newDelegate(this, &InventoryWindow::onItemSelected);
@ -403,8 +403,8 @@ namespace MWGui
int height = std::min(mPreview->getTextureHeight(), size.height);
mPreview->setViewport(width, height);
mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, height/float(mPreview->getTextureHeight()),
width/float(mPreview->getTextureWidth()), 0.f));
mAvatarImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f,
width/float(mPreview->getTextureWidth()), height/float(mPreview->getTextureHeight())));
}
void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender)

@ -164,7 +164,7 @@ namespace MWGui
mBackgroundImage->setBackgroundImage("");
mBackgroundImage->setRenderItemTexture(mGuiTexture.get());
mBackgroundImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f));
mBackgroundImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
}
setVisible(true);

@ -254,7 +254,7 @@ namespace MWGui
{
boost::shared_ptr<MyGUI::ITexture> myguitex (new osgMyGUI::OSGTexture(tex));
fog->setRenderItemTexture(myguitex.get());
fog->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
fog->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f));
fogTextures.push_back(myguitex);
}
else
@ -391,7 +391,7 @@ namespace MWGui
boost::shared_ptr<MyGUI::ITexture> guiTex (new osgMyGUI::OSGTexture(texture));
textures.push_back(guiTex);
box->setRenderItemTexture(guiTex.get());
box->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f));
box->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
}
else
box->setRenderItemTexture(NULL);
@ -770,11 +770,11 @@ namespace MWGui
mGlobalMapTexture.reset(new osgMyGUI::OSGTexture(mGlobalMapRender->getBaseTexture()));
mGlobalMapImage->setRenderItemTexture(mGlobalMapTexture.get());
mGlobalMapImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f));
mGlobalMapImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
mGlobalMapOverlayTexture.reset(new osgMyGUI::OSGTexture(mGlobalMapRender->getOverlayTexture()));
mGlobalMapOverlay->setRenderItemTexture(mGlobalMapOverlayTexture.get());
mGlobalMapOverlay->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f));
mGlobalMapOverlay->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
}
MapWindow::~MapWindow()

@ -142,7 +142,7 @@ namespace MWGui
mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture()));
mPreviewImage->setRenderItemTexture(mPreviewTexture.get());
mPreviewImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f));
mPreviewImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
const ESM::NPC& proto = mPreview->getPrototype();
setRaceId(proto.mRace);

@ -422,6 +422,6 @@ namespace MWGui
mScreenshotTexture.reset(new osgMyGUI::OSGTexture(texture));
mScreenshot->setRenderItemTexture(mScreenshotTexture.get());
mScreenshot->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f));
mScreenshot->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
}
}

@ -50,7 +50,7 @@ void VideoWidget::playVideo(const std::string &video)
mTexture.reset(new osgMyGUI::OSGTexture(texture));
setRenderItemTexture(mTexture.get());
getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f));
getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 1.f, 1.f, 0.f));
}
int VideoWidget::getVideoWidth()

@ -28,7 +28,7 @@
#include <components/fontloader/fontloader.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/texturemanager.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/translation/translation.hpp>
@ -194,7 +194,7 @@ namespace MWGui
, mVersionDescription(versionDescription)
{
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getTextureManager(), uiScale);
mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), uiScale);
mGuiPlatform->initialise(resourcePath, logpath);
mGui = new MyGUI::Gui;
@ -2015,9 +2015,9 @@ namespace MWGui
continue;
std::string tex_name = imgSetPointer->getImageSet()->getIndexInfo(0,0).texture;
osg::ref_ptr<osg::Texture2D> tex = mResourceSystem->getTextureManager()->getTexture2D(tex_name, osg::Texture::CLAMP, osg::Texture::CLAMP);
osg::ref_ptr<osg::Image> image = mResourceSystem->getImageManager()->getImage(tex_name);
if(tex.valid())
if(image.valid())
{
//everything looks good, send it to the cursor manager
Uint8 size_x = imgSetPointer->getSize().width;
@ -2026,7 +2026,7 @@ namespace MWGui
Uint8 hotspot_y = imgSetPointer->getHotSpot().top;
int rotation = imgSetPointer->getRotation();
mCursorManager->createCursor(imgSetPointer->getResourceName(), rotation, tex->getImage(), size_x, size_y, hotspot_x, hotspot_y);
mCursorManager->createCursor(imgSetPointer->getResourceName(), rotation, image, size_x, size_y, hotspot_x, hotspot_y);
}
}
}

@ -16,7 +16,7 @@ namespace MWPhysics
{
Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<Resource::BulletShapeInstance> shape, btCollisionWorld* world)
Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<const Resource::BulletShape> shape, btCollisionWorld* world)
: mCanWaterWalk(false), mWalkingOnWater(false)
, mCollisionObject(0), mForce(0.f, 0.f, 0.f), mOnGround(false)
, mInternalCollisionMode(true)

@ -15,7 +15,7 @@ class btCollisionObject;
namespace Resource
{
class BulletShapeInstance;
class BulletShape;
}
namespace MWPhysics
@ -48,7 +48,7 @@ namespace MWPhysics
class Actor : public PtrHolder
{
public:
Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<Resource::BulletShapeInstance> shape, btCollisionWorld* world);
Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<const Resource::BulletShape> shape, btCollisionWorld* world);
~Actor();
/**

@ -22,6 +22,7 @@
#include <components/esm/loadgmst.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/unrefqueue.hpp>
#include <components/nifosg/particle.hpp> // FindRecIndexVisitor
@ -533,6 +534,11 @@ namespace MWPhysics
setOrigin(btVector3(pos[0], pos[1], pos[2]));
}
const Resource::BulletShapeInstance* getShapeInstance() const
{
return mShapeInstance.get();
}
void setScale(float scale)
{
mShapeInstance->getCollisionShape()->setLocalScaling(btVector3(scale,scale,scale));
@ -640,12 +646,15 @@ namespace MWPhysics
PhysicsSystem::PhysicsSystem(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode)
: mShapeManager(new Resource::BulletShapeManager(resourceSystem->getVFS(), resourceSystem->getSceneManager(), resourceSystem->getNifFileManager()))
, mResourceSystem(resourceSystem)
, mDebugDrawEnabled(false)
, mTimeAccum(0.0f)
, mWaterHeight(0)
, mWaterEnabled(false)
, mParentNode(parentNode)
{
mResourceSystem->addResourceManager(mShapeManager.get());
mCollisionConfiguration = new btDefaultCollisionConfiguration();
mDispatcher = new btCollisionDispatcher(mCollisionConfiguration);
mBroadphase = new btDbvtBroadphase();
@ -659,6 +668,8 @@ namespace MWPhysics
PhysicsSystem::~PhysicsSystem()
{
mResourceSystem->removeResourceManager(mShapeManager.get());
if (mWaterCollisionObject.get())
mCollisionWorld->removeCollisionObject(mWaterCollisionObject.get());
@ -685,6 +696,16 @@ namespace MWPhysics
delete mBroadphase;
}
void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue)
{
mUnrefQueue = unrefQueue;
}
Resource::BulletShapeManager *PhysicsSystem::getShapeManager()
{
return mShapeManager.get();
}
bool PhysicsSystem::toggleDebugRendering()
{
mDebugDrawEnabled = !mDebugDrawEnabled;
@ -1078,7 +1099,7 @@ namespace MWPhysics
void PhysicsSystem::addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType)
{
osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->createInstance(mesh);
osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->getInstance(mesh);
if (!shapeInstance || !shapeInstance->getCollisionShape())
return;
@ -1099,6 +1120,9 @@ namespace MWPhysics
{
mCollisionWorld->removeCollisionObject(found->second->getCollisionObject());
if (mUnrefQueue.get())
mUnrefQueue->push(found->second->getShapeInstance());
mAnimatedObjects.erase(found->second);
delete found->second;
@ -1224,11 +1248,11 @@ namespace MWPhysics
}
void PhysicsSystem::addActor (const MWWorld::Ptr& ptr, const std::string& mesh) {
osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->createInstance(mesh);
if (!shapeInstance)
osg::ref_ptr<const Resource::BulletShape> shape = mShapeManager->getShape(mesh);
if (!shape)
return;
Actor* actor = new Actor(ptr, shapeInstance, mCollisionWorld);
Actor* actor = new Actor(ptr, shape, mCollisionWorld);
mActors.insert(std::make_pair(ptr, actor));
}

@ -25,11 +25,12 @@ namespace MWRender
namespace Resource
{
class BulletShapeManager;
class ResourceSystem;
}
namespace Resource
namespace SceneUtil
{
class ResourceSystem;
class UnrefQueue;
}
class btCollisionWorld;
@ -53,6 +54,10 @@ namespace MWPhysics
PhysicsSystem (Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode);
~PhysicsSystem ();
void setUnrefQueue(SceneUtil::UnrefQueue* unrefQueue);
Resource::BulletShapeManager* getShapeManager();
void enableWater(float height);
void setWaterHeight(float height);
void disableWater();
@ -163,12 +168,15 @@ namespace MWPhysics
void updateWater();
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
btBroadphaseInterface* mBroadphase;
btDefaultCollisionConfiguration* mCollisionConfiguration;
btCollisionDispatcher* mDispatcher;
btCollisionWorld* mCollisionWorld;
std::auto_ptr<Resource::BulletShapeManager> mShapeManager;
Resource::ResourceSystem* mResourceSystem;
typedef std::map<MWWorld::ConstPtr, Object*> ObjectMap;
ObjectMap mObjects;

@ -19,7 +19,7 @@
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/resource/keyframemanager.hpp>
#include <components/resource/texturemanager.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/nifosg/nifloader.hpp> // KeyframeHolder
#include <components/nifosg/controller.hpp>
@ -95,7 +95,12 @@ namespace
class NodeMapVisitor : public osg::NodeVisitor
{
public:
NodeMapVisitor() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > NodeMap;
NodeMapVisitor(NodeMap& map)
: osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
, mMap(map)
{}
void apply(osg::MatrixTransform& trans)
{
@ -103,15 +108,8 @@ namespace
traverse(trans);
}
typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > NodeMap;
const NodeMap& getNodeMap() const
{
return mMap;
}
private:
NodeMap mMap;
NodeMap& mMap;
};
NifOsg::TextKeyMap::const_iterator findGroupStart(const NifOsg::TextKeyMap &keys, const std::string &groupname)
@ -312,6 +310,7 @@ namespace MWRender
Animation::Animation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem)
: mInsert(parentNode)
, mSkeleton(NULL)
, mNodeMapCreated(false)
, mPtr(ptr)
, mResourceSystem(resourceSystem)
, mAccumulate(1.f, 1.f, 0.f)
@ -407,12 +406,14 @@ namespace MWRender
if (!animsrc->mKeyframes || animsrc->mKeyframes->mTextKeys.empty() || animsrc->mKeyframes->mKeyframeControllers.empty())
return;
const NodeMap& nodeMap = getNodeMap();
for (NifOsg::KeyframeHolder::KeyframeControllerMap::const_iterator it = animsrc->mKeyframes->mKeyframeControllers.begin();
it != animsrc->mKeyframes->mKeyframeControllers.end(); ++it)
{
std::string bonename = Misc::StringUtils::lowerCase(it->first);
NodeMap::const_iterator found = mNodeMap.find(bonename);
if (found == mNodeMap.end())
NodeMap::const_iterator found = nodeMap.find(bonename);
if (found == nodeMap.end())
{
std::cerr << "addAnimSource: can't find bone '" + bonename << "' in " << model << " (referenced by " << kfname << ")" << std::endl;
continue;
@ -436,11 +437,11 @@ namespace MWRender
if (!mAccumRoot)
{
NodeMap::const_iterator found = mNodeMap.find("root bone");
if (found == mNodeMap.end())
found = mNodeMap.find("bip01");
NodeMap::const_iterator found = nodeMap.find("root bone");
if (found == nodeMap.end())
found = nodeMap.find("bip01");
if (found != mNodeMap.end())
if (found != nodeMap.end())
mAccumRoot = found->second;
}
}
@ -690,6 +691,17 @@ namespace MWRender
mTextKeyListener = listener;
}
const Animation::NodeMap &Animation::getNodeMap() const
{
if (!mNodeMapCreated && mObjectRoot)
{
NodeMapVisitor visitor(mNodeMap);
mObjectRoot->accept(visitor);
mNodeMapCreated = true;
}
return mNodeMap;
}
void Animation::resetActiveGroups()
{
// remove all previous external controllers from the scene graph
@ -729,7 +741,7 @@ namespace MWRender
for (AnimSource::ControllerMap::iterator it = animsrc->mControllerMap[blendMask].begin(); it != animsrc->mControllerMap[blendMask].end(); ++it)
{
osg::ref_ptr<osg::Node> node = mNodeMap.at(it->first); // this should not throw, we already checked for the node existing in addAnimSource
osg::ref_ptr<osg::Node> node = getNodeMap().at(it->first); // this should not throw, we already checked for the node existing in addAnimSource
node->addUpdateCallback(it->second);
mActiveControllers.insert(std::make_pair(node, it->second));
@ -978,13 +990,14 @@ namespace MWRender
mSkeleton = NULL;
mNodeMap.clear();
mNodeMapCreated = false;
mActiveControllers.clear();
mAccumRoot = NULL;
mAccumCtrl = NULL;
if (!forceskeleton)
{
osg::ref_ptr<osg::Node> created = mResourceSystem->getSceneManager()->createInstance(model, mInsert);
osg::ref_ptr<osg::Node> created = mResourceSystem->getSceneManager()->getInstance(model, mInsert);
mObjectRoot = created->asGroup();
if (!mObjectRoot)
{
@ -996,7 +1009,7 @@ namespace MWRender
}
else
{
osg::ref_ptr<osg::Node> created = mResourceSystem->getSceneManager()->createInstance(model);
osg::ref_ptr<osg::Node> created = mResourceSystem->getSceneManager()->getInstance(model);
osg::ref_ptr<SceneUtil::Skeleton> skel = dynamic_cast<SceneUtil::Skeleton*>(created.get());
if (!skel)
{
@ -1025,10 +1038,6 @@ namespace MWRender
removeTriBipVisitor.remove();
}
NodeMapVisitor visitor;
mObjectRoot->accept(visitor);
mNodeMap = visitor.getNodeMap();
mObjectRoot->addCullCallback(new SceneUtil::LightListCallback);
}
@ -1059,7 +1068,12 @@ namespace MWRender
stream << i;
stream << ".dds";
textures.push_back(mResourceSystem->getTextureManager()->getTexture2D(stream.str(), osg::Texture2D::REPEAT, osg::Texture2D::REPEAT));
osg::ref_ptr<osg::Image> image = mResourceSystem->getImageManager()->getImage(stream.str());
osg::ref_ptr<osg::Texture2D> tex (new osg::Texture2D(image));
tex->setWrap(osg::Texture::WRAP_S, osg::Texture2D::REPEAT);
tex->setWrap(osg::Texture::WRAP_T, osg::Texture2D::REPEAT);
mResourceSystem->getSceneManager()->applyFilterSettings(tex);
textures.push_back(tex);
}
osg::ref_ptr<GlowUpdater> glowupdater (new GlowUpdater(glowColor, textures));
@ -1116,13 +1130,13 @@ namespace MWRender
parentNode = mInsert;
else
{
NodeMap::iterator found = mNodeMap.find(Misc::StringUtils::lowerCase(bonename));
if (found == mNodeMap.end())
NodeMap::const_iterator found = getNodeMap().find(Misc::StringUtils::lowerCase(bonename));
if (found == getNodeMap().end())
throw std::runtime_error("Can't find bone " + bonename);
parentNode = found->second;
}
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->createInstance(model, parentNode);
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model, parentNode);
node->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
@ -1217,8 +1231,8 @@ namespace MWRender
const osg::Node* Animation::getNode(const std::string &name) const
{
std::string lowerName = Misc::StringUtils::lowerCase(name);
NodeMap::const_iterator found = mNodeMap.find(lowerName);
if (found == mNodeMap.end())
NodeMap::const_iterator found = getNodeMap().find(lowerName);
if (found == getNodeMap().end())
return NULL;
else
return found->second;
@ -1312,10 +1326,10 @@ namespace MWRender
if (mPtr.getClass().isBipedal(mPtr))
{
NodeMap::iterator found = mNodeMap.find("bip01 head");
if (found != mNodeMap.end() && dynamic_cast<osg::MatrixTransform*>(found->second.get()))
NodeMap::const_iterator found = getNodeMap().find("bip01 head");
if (found != getNodeMap().end())
{
osg::Node* node = found->second;
osg::MatrixTransform* node = found->second;
mHeadController = new RotateController(mObjectRoot.get());
node->addUpdateCallback(mHeadController);
mActiveControllers.insert(std::make_pair(node, mHeadController));
@ -1404,8 +1418,13 @@ namespace MWRender
PartHolder::~PartHolder()
{
if (mNode->getNumParents())
if (mNode.get() && mNode->getNumParents())
mNode->getParent(0)->removeChild(mNode);
}
void PartHolder::unlink()
{
mNode = NULL;
}
}

@ -55,6 +55,9 @@ public:
~PartHolder();
/// Unreferences mNode *without* detaching it from the graph. Only use if you know what you are doing.
void unlink();
osg::ref_ptr<osg::Node> getNode()
{
return mNode;
@ -229,7 +232,8 @@ protected:
// Stored in all lowercase for a case-insensitive lookup
typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > NodeMap;
NodeMap mNodeMap;
mutable NodeMap mNodeMap;
mutable bool mNodeMapCreated;
MWWorld::Ptr mPtr;
@ -260,6 +264,8 @@ protected:
float mAlpha;
const NodeMap& getNodeMap() const;
/* Sets the appropriate animations on the bone groups based on priority.
*/
void resetActiveGroups();

@ -194,7 +194,7 @@ namespace MWRender
sizeX = std::max(sizeX, 0);
sizeY = std::max(sizeY, 0);
mCamera->setViewport(0, 0, std::min(mSizeX, sizeX), std::min(mSizeY, sizeY));
mCamera->setViewport(0, mSizeY-sizeY, std::min(mSizeX, sizeX), std::min(mSizeY, sizeY));
redraw();
}

@ -105,7 +105,7 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot)
else
bonename = "Shield Bone";
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->createInstance(item.getClass().getModel(item));
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(item.getClass().getModel(item));
osg::ref_ptr<osg::Node> attached = SceneUtil::attach(node, mObjectRoot, bonename, bonename);
mResourceSystem->getSceneManager()->notifyAttached(attached);

@ -27,7 +27,7 @@ EffectManager::~EffectManager()
void EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, float scale)
{
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->createInstance(model);
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model);
node->setNodeMask(Mask_Effect);

@ -273,6 +273,13 @@ NpcAnimation::~NpcAnimation()
// all from within this destructor. ouch!
&& mPtr.getRefData().getCustomData() && mPtr.getClass().getInventoryStore(mPtr).getListener() == this)
mPtr.getClass().getInventoryStore(mPtr).setListener(NULL, mPtr);
// do not detach (delete) parts yet, this is done so the background thread can handle the deletion
for(size_t i = 0;i < ESM::PRT_Count;i++)
{
if (mObjectParts[i].get())
mObjectParts[i]->unlink();
}
}
NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
@ -619,116 +626,10 @@ void NpcAnimation::updateParts()
showWeapons(mShowWeapons);
showCarriedLeft(mShowCarriedLeft);
// Remember body parts so we only have to search through the store once for each race/gender/viewmode combination
static std::map< std::pair<std::string,int>,std::vector<const ESM::BodyPart*> > sRaceMapping;
bool isWerewolf = (mNpcType == Type_Werewolf);
int flags = (isWerewolf ? -1 : 0);
if(!mNpc->isMale())
{
static const int Flag_Female = 1<<0;
flags |= Flag_Female;
}
if(mViewMode == VM_FirstPerson)
{
static const int Flag_FirstPerson = 1<<1;
flags |= Flag_FirstPerson;
}
std::string race = (isWerewolf ? "werewolf" : Misc::StringUtils::lowerCase(mNpc->mRace));
std::pair<std::string, int> thisCombination = std::make_pair(race, flags);
if (sRaceMapping.find(thisCombination) == sRaceMapping.end())
{
typedef std::multimap<ESM::BodyPart::MeshPart,ESM::PartReferenceType> BodyPartMapType;
static BodyPartMapType sBodyPartMap;
if(sBodyPartMap.empty())
{
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Neck, ESM::PRT_Neck));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Groin, ESM::PRT_Groin));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_RHand));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_LHand));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_RFoot));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_LFoot));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_RKnee));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_LKnee));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Tail, ESM::PRT_Tail));
}
std::vector<const ESM::BodyPart*> &parts = sRaceMapping[thisCombination];
parts.resize(ESM::PRT_Count, NULL);
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
for(MWWorld::Store<ESM::BodyPart>::iterator it = partStore.begin(); it != partStore.end(); ++it)
{
if(isWerewolf)
break;
const ESM::BodyPart& bodypart = *it;
if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)
continue;
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
continue;
if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace))
continue;
bool firstPerson = (bodypart.mId.size() >= 3)
&& bodypart.mId[bodypart.mId.size()-3] == '1'
&& bodypart.mId[bodypart.mId.size()-2] == 's'
&& bodypart.mId[bodypart.mId.size()-1] == 't';
if(firstPerson != (mViewMode == VM_FirstPerson))
{
if(mViewMode == VM_FirstPerson && (bodypart.mData.mPart == ESM::BodyPart::MP_Hand ||
bodypart.mData.mPart == ESM::BodyPart::MP_Wrist ||
bodypart.mData.mPart == ESM::BodyPart::MP_Forearm ||
bodypart.mData.mPart == ESM::BodyPart::MP_Upperarm))
{
/* Allow 3rd person skins as a fallback for the arms if 1st person is missing. */
BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));
while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)
{
if(!parts[bIt->second])
parts[bIt->second] = &*it;
++bIt;
}
}
continue;
}
if ((!mNpc->isMale()) != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))
{
// Allow opposite gender's parts as fallback if parts for our gender are missing
BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));
while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)
{
if(!parts[bIt->second])
parts[bIt->second] = &*it;
++bIt;
}
continue;
}
BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));
while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)
{
parts[bIt->second] = &*it;
++bIt;
}
}
}
const std::vector<const ESM::BodyPart*> &parts = sRaceMapping[thisCombination];
const std::vector<const ESM::BodyPart*> &parts = getBodyParts(race, !mNpc->isMale(), mViewMode == VM_FirstPerson, isWerewolf);
for(int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part)
{
if(mPartPriorities[part] < 1)
@ -746,7 +647,7 @@ void NpcAnimation::updateParts()
PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const std::string& bonename, const std::string& bonefilter, bool enchantedGlow, osg::Vec4f* glowColor)
{
osg::ref_ptr<osg::Node> instance = mResourceSystem->getSceneManager()->createInstance(model);
osg::ref_ptr<osg::Node> instance = mResourceSystem->getSceneManager()->getInstance(model);
osg::ref_ptr<osg::Node> attached = SceneUtil::attach(instance, mObjectRoot, bonefilter, bonename);
mResourceSystem->getSceneManager()->notifyAttached(attached);
if (enchantedGlow)
@ -946,9 +847,9 @@ void NpcAnimation::addControllers()
if (mViewMode == VM_FirstPerson)
{
NodeMap::iterator found = mNodeMap.find("bip01 neck");
if (found != mNodeMap.end() && dynamic_cast<osg::MatrixTransform*>(found->second.get()))
if (found != mNodeMap.end())
{
osg::Node* node = found->second;
osg::MatrixTransform* node = found->second.get();
mFirstPersonNeckController = new NeckController(mObjectRoot.get());
node->addUpdateCallback(mFirstPersonNeckController);
mActiveControllers.insert(std::make_pair(node, mFirstPersonNeckController));
@ -1116,6 +1017,118 @@ void NpcAnimation::updatePtr(const MWWorld::Ptr &updated)
mHeadAnimationTime->updatePtr(updated);
}
// Remember body parts so we only have to search through the store once for each race/gender/viewmode combination
typedef std::map< std::pair<std::string,int>,std::vector<const ESM::BodyPart*> > RaceMapping;
static RaceMapping sRaceMapping;
const std::vector<const ESM::BodyPart *>& NpcAnimation::getBodyParts(const std::string &race, bool female, bool firstPerson, bool werewolf)
{
static const int Flag_FirstPerson = 1<<1;
static const int Flag_Female = 1<<0;
int flags = (werewolf ? -1 : 0);
if(female)
flags |= Flag_Female;
if(firstPerson)
flags |= Flag_FirstPerson;
RaceMapping::iterator found = sRaceMapping.find(std::make_pair(race, flags));
if (found != sRaceMapping.end())
return found->second;
else
{
std::vector<const ESM::BodyPart*>& parts = sRaceMapping[std::make_pair(race, flags)];
typedef std::multimap<ESM::BodyPart::MeshPart,ESM::PartReferenceType> BodyPartMapType;
static BodyPartMapType sBodyPartMap;
if(sBodyPartMap.empty())
{
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Neck, ESM::PRT_Neck));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Groin, ESM::PRT_Groin));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_RHand));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_LHand));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_RFoot));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_LFoot));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_RKnee));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_LKnee));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg));
sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Tail, ESM::PRT_Tail));
}
parts.resize(ESM::PRT_Count, NULL);
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
for(MWWorld::Store<ESM::BodyPart>::iterator it = partStore.begin(); it != partStore.end(); ++it)
{
if(werewolf)
break;
const ESM::BodyPart& bodypart = *it;
if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)
continue;
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
continue;
if (!Misc::StringUtils::ciEqual(bodypart.mRace, race))
continue;
bool partFirstPerson = (bodypart.mId.size() >= 3)
&& bodypart.mId[bodypart.mId.size()-3] == '1'
&& bodypart.mId[bodypart.mId.size()-2] == 's'
&& bodypart.mId[bodypart.mId.size()-1] == 't';
if(partFirstPerson != (firstPerson))
{
if(firstPerson && (bodypart.mData.mPart == ESM::BodyPart::MP_Hand ||
bodypart.mData.mPart == ESM::BodyPart::MP_Wrist ||
bodypart.mData.mPart == ESM::BodyPart::MP_Forearm ||
bodypart.mData.mPart == ESM::BodyPart::MP_Upperarm))
{
/* Allow 3rd person skins as a fallback for the arms if 1st person is missing. */
BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));
while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)
{
if(!parts[bIt->second])
parts[bIt->second] = &*it;
++bIt;
}
}
continue;
}
if ((female) != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))
{
// Allow opposite gender's parts as fallback if parts for our gender are missing
BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));
while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)
{
if(!parts[bIt->second])
parts[bIt->second] = &*it;
++bIt;
}
continue;
}
BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));
while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)
{
parts[bIt->second] = &*it;
++bIt;
}
}
return parts;
}
}
void NpcAnimation::setAccurateAiming(bool enabled)
{
mAccurateAiming = enabled;

@ -10,6 +10,7 @@
namespace ESM
{
struct NPC;
struct BodyPart;
}
namespace MWRender
@ -150,6 +151,10 @@ public:
void setFirstPersonOffset(const osg::Vec3f& offset);
virtual void updatePtr(const MWWorld::Ptr& updated);
/// Get a list of body parts that may be used by an NPC of given race and gender.
/// @note This is a fixed size list, one list item for each ESM::PartReferenceType, may contain NULL body parts.
static const std::vector<const ESM::BodyPart*>& getBodyParts(const std::string& raceId, bool female, bool firstperson, bool werewolf);
};
}

@ -14,6 +14,7 @@
#include <components/sceneutil/visitor.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/unrefqueue.hpp>
#include "../mwworld/ptr.hpp"
#include "../mwworld/class.hpp"
@ -85,9 +86,10 @@ namespace
namespace MWRender
{
Objects::Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> rootNode)
Objects::Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> rootNode, SceneUtil::UnrefQueue* unrefQueue)
: mRootNode(rootNode)
, mResourceSystem(resourceSystem)
, mUnrefQueue(unrefQueue)
{
}
@ -186,7 +188,11 @@ bool Objects::removeObject (const MWWorld::Ptr& ptr)
delete iter->second;
mObjects.erase(iter);
if (mUnrefQueue.get())
mUnrefQueue->push(ptr.getRefData().getBaseNode());
ptr.getRefData().getBaseNode()->getParent(0)->removeChild(ptr.getRefData().getBaseNode());
ptr.getRefData().setBaseNode(NULL);
return true;
}
@ -200,6 +206,8 @@ void Objects::removeCell(const MWWorld::CellStore* store)
{
if(iter->first.getCell() == store)
{
if (mUnrefQueue.get())
mUnrefQueue->push(iter->second->getObjectRoot());
delete iter->second;
mObjects.erase(iter++);
}
@ -211,6 +219,8 @@ void Objects::removeCell(const MWWorld::CellStore* store)
if(cell != mCellSceneNodes.end())
{
cell->second->getParent(0)->removeChild(cell->second);
if (mUnrefQueue.get())
mUnrefQueue->push(cell->second);
mCellSceneNodes.erase(cell);
}
}

@ -30,6 +30,11 @@ namespace MWWorld
class CellStore;
}
namespace SceneUtil
{
class UnrefQueue;
}
namespace MWRender{
class Animation;
@ -65,12 +70,14 @@ class Objects{
osg::ref_ptr<osg::Group> mRootNode;
void insertBegin(const MWWorld::Ptr& ptr);
Resource::ResourceSystem* mResourceSystem;
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
void insertBegin(const MWWorld::Ptr& ptr);
public:
Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> rootNode);
Objects(Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> rootNode, SceneUtil::UnrefQueue* unrefQueue);
~Objects();
/// @param animated Attempt to load separate keyframes from a .kf file matching the model file?

@ -17,7 +17,7 @@
#include <osgViewer/Viewer>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/texturemanager.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/settings/settings.hpp>
@ -26,6 +26,8 @@
#include <components/sceneutil/lightmanager.hpp>
#include <components/sceneutil/statesetupdater.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/workqueue.hpp>
#include <components/sceneutil/unrefqueue.hpp>
#include <components/terrain/terraingrid.hpp>
@ -126,11 +128,36 @@ namespace MWRender
bool mWireframe;
};
class PreloadCommonAssetsWorkItem : public SceneUtil::WorkItem
{
public:
PreloadCommonAssetsWorkItem(Resource::ResourceSystem* resourceSystem)
: mResourceSystem(resourceSystem)
{
}
virtual void doWork()
{
for (std::vector<std::string>::const_iterator it = mModels.begin(); it != mModels.end(); ++it)
mResourceSystem->getSceneManager()->getTemplate(*it);
for (std::vector<std::string>::const_iterator it = mTextures.begin(); it != mTextures.end(); ++it)
mResourceSystem->getImageManager()->getImage(*it);
}
std::vector<std::string> mModels;
std::vector<std::string> mTextures;
private:
Resource::ResourceSystem* mResourceSystem;
};
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, Resource::ResourceSystem* resourceSystem,
const Fallback::Map* fallback, const std::string& resourcePath)
: mViewer(viewer)
, mRootNode(rootNode)
, mResourceSystem(resourceSystem)
, mWorkQueue(new SceneUtil::WorkQueue)
, mUnrefQueue(new SceneUtil::UnrefQueue)
, mFogDepth(0.f)
, mUnderwaterColor(fallback->getFallbackColour("Water_UnderwaterColor"))
, mUnderwaterWeight(fallback->getFallbackFloat("Water_UnderwaterColorWeight"))
@ -151,7 +178,7 @@ namespace MWRender
mPathgrid.reset(new Pathgrid(mRootNode));
mObjects.reset(new Objects(mResourceSystem, lightRoot));
mObjects.reset(new Objects(mResourceSystem, lightRoot, mUnrefQueue.get()));
mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation);
@ -162,7 +189,7 @@ namespace MWRender
mWater.reset(new Water(mRootNode, lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath));
mTerrain.reset(new Terrain::TerrainGrid(lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(),
new TerrainStorage(mResourceSystem->getVFS(), false), Mask_Terrain));
new TerrainStorage(mResourceSystem->getVFS(), false), Mask_Terrain, mUnrefQueue.get()));
mCamera.reset(new Camera(mViewer->getCamera()));
@ -231,6 +258,37 @@ namespace MWRender
return mResourceSystem;
}
SceneUtil::WorkQueue* RenderingManager::getWorkQueue()
{
return mWorkQueue.get();
}
SceneUtil::UnrefQueue* RenderingManager::getUnrefQueue()
{
return mUnrefQueue.get();
}
Terrain::World* RenderingManager::getTerrain()
{
return mTerrain.get();
}
void RenderingManager::preloadCommonAssets()
{
osg::ref_ptr<PreloadCommonAssetsWorkItem> workItem (new PreloadCommonAssetsWorkItem(mResourceSystem));
mSky->listAssetsToPreload(workItem->mModels, workItem->mTextures);
mWater->listAssetsToPreload(workItem->mTextures);
workItem->mTextures.push_back("textures/_land_default.dds");
mWorkQueue->addWorkItem(workItem);
}
double RenderingManager::getReferenceTime() const
{
return mViewer->getFrameStamp()->getReferenceTime();
}
osg::Group* RenderingManager::getLightRoot()
{
return mLightRoot.get();
@ -385,6 +443,8 @@ namespace MWRender
void RenderingManager::update(float dt, bool paused)
{
mUnrefQueue->flush(mWorkQueue.get());
if (!paused)
{
mEffectManager->update(dt);
@ -776,7 +836,10 @@ namespace MWRender
void RenderingManager::updateTextureFiltering()
{
mResourceSystem->getTextureManager()->setFilterSettings(
if (mTerrain.get())
mTerrain->updateCache();
mResourceSystem->getSceneManager()->setFilterSettings(
Settings::Manager::getString("texture mag filter", "General"),
Settings::Manager::getString("texture min filter", "General"),
Settings::Manager::getString("texture mipmap", "General"),

@ -42,6 +42,12 @@ namespace Fallback
class Map;
}
namespace SceneUtil
{
class WorkQueue;
class UnrefQueue;
}
namespace MWRender
{
@ -65,6 +71,14 @@ namespace MWRender
Resource::ResourceSystem* getResourceSystem();
SceneUtil::WorkQueue* getWorkQueue();
SceneUtil::UnrefQueue* getUnrefQueue();
Terrain::World* getTerrain();
void preloadCommonAssets();
double getReferenceTime() const;
osg::Group* getLightRoot();
void setNightEyeFactor(float factor);
@ -186,6 +200,9 @@ namespace MWRender
osg::ref_ptr<osg::Group> mLightRoot;
Resource::ResourceSystem* mResourceSystem;
osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
osg::ref_ptr<osg::Light> mSunLight;
std::auto_ptr<Pathgrid> mPathgrid;

@ -13,8 +13,9 @@
#include <components/misc/rng.hpp>
#include <components/nifosg/controller.hpp>
#include <components/resource/texturemanager.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/fallback/fallback.hpp>
#include "vismask.hpp"
@ -39,7 +40,11 @@ namespace
{
std::ostringstream texname;
texname << "textures/water/" << tex << std::setw(2) << std::setfill('0') << i << ".dds";
textures.push_back(resourceSystem->getTextureManager()->getTexture2D(texname.str(), osg::Texture::REPEAT, osg::Texture::REPEAT));
osg::ref_ptr<osg::Texture2D> tex (new osg::Texture2D(resourceSystem->getImageManager()->getImage(texname.str())));
tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
resourceSystem->getSceneManager()->applyFilterSettings(tex);
textures.push_back(tex);
}
osg::ref_ptr<NifOsg::FlipController> controller (new NifOsg::FlipController(0, 0.3f/rippleFrameCount, textures));

@ -31,7 +31,7 @@
#include <components/misc/resourcehelpers.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/resource/texturemanager.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/vfs/manager.hpp>
#include <components/fallback/fallback.hpp>
@ -134,10 +134,10 @@ private:
class AtmosphereNightUpdater : public SceneUtil::StateSetUpdater
{
public:
AtmosphereNightUpdater(Resource::TextureManager* textureManager)
AtmosphereNightUpdater(Resource::ImageManager* imageManager)
{
// we just need a texture, its contents don't really matter
mTexture = textureManager->getWarningTexture();
mTexture = new osg::Texture2D(imageManager->getWarningImage());
}
void setFade(const float fade)
@ -469,16 +469,16 @@ const float CelestialBody::mDistance = 1000.0f;
class Sun : public CelestialBody
{
public:
Sun(osg::Group* parentNode, Resource::TextureManager& textureManager)
Sun(osg::Group* parentNode, Resource::ImageManager& imageManager)
: CelestialBody(parentNode, 1.0f, 1)
, mUpdater(new Updater)
{
mTransform->addUpdateCallback(mUpdater);
mTransform->setNodeMask(Mask_Sun);
osg::ref_ptr<osg::Texture2D> sunTex = textureManager.getTexture2D("textures/tx_sun_05.dds",
osg::Texture::CLAMP,
osg::Texture::CLAMP);
osg::ref_ptr<osg::Texture2D> sunTex (new osg::Texture2D(imageManager.getImage("textures/tx_sun_05.dds")));
sunTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
sunTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
mGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON);
@ -498,7 +498,7 @@ public:
mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true);
mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false);
createSunFlash(textureManager);
createSunFlash(imageManager);
createSunGlare();
}
@ -602,11 +602,11 @@ private:
return oqn;
}
void createSunFlash(Resource::TextureManager& textureManager)
void createSunFlash(Resource::ImageManager& imageManager)
{
osg::ref_ptr<osg::Texture2D> tex = textureManager.getTexture2D("textures/tx_sun_flash_grey_05.dds",
osg::Texture::CLAMP,
osg::Texture::CLAMP);
osg::ref_ptr<osg::Texture2D> tex (new osg::Texture2D(imageManager.getImage("textures/tx_sun_flash_grey_05.dds")));
tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
osg::ref_ptr<osg::PositionAttitudeTransform> transform (new osg::PositionAttitudeTransform);
const float scale = 2.6f;
@ -932,11 +932,11 @@ public:
Type_Secunda
};
Moon(osg::Group* parentNode, Resource::TextureManager& textureManager, float scaleFactor, Type type)
Moon(osg::Group* parentNode, Resource::ImageManager& imageManager, float scaleFactor, Type type)
: CelestialBody(parentNode, scaleFactor, 2)
, mType(type)
, mPhase(MoonState::Phase_Unspecified)
, mUpdater(new Updater(textureManager))
, mUpdater(new Updater(imageManager))
{
setPhase(MoonState::Phase_Full);
setVisible(true);
@ -1001,7 +1001,7 @@ public:
private:
struct Updater : public SceneUtil::StateSetUpdater
{
Resource::TextureManager& mTextureManager;
Resource::ImageManager& mImageManager;
osg::ref_ptr<osg::Texture2D> mPhaseTex;
osg::ref_ptr<osg::Texture2D> mCircleTex;
float mTransparency;
@ -1009,8 +1009,8 @@ private:
osg::Vec4f mAtmosphereColor;
osg::Vec4f mMoonColor;
Updater(Resource::TextureManager& textureManager)
: mTextureManager(textureManager)
Updater(Resource::ImageManager& imageManager)
: mImageManager(imageManager)
, mPhaseTex()
, mCircleTex()
, mTransparency(1.0f)
@ -1055,8 +1055,12 @@ private:
void setTextures(const std::string& phaseTex, const std::string& circleTex)
{
mPhaseTex = mTextureManager.getTexture2D(phaseTex, osg::Texture::CLAMP, osg::Texture::CLAMP);
mCircleTex = mTextureManager.getTexture2D(circleTex, osg::Texture::CLAMP, osg::Texture::CLAMP);
mPhaseTex = new osg::Texture2D(mImageManager.getImage(phaseTex));
mPhaseTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
mPhaseTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
mCircleTex = new osg::Texture2D(mImageManager.getImage(circleTex));
mCircleTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
mCircleTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
reset();
}
@ -1142,7 +1146,7 @@ void SkyManager::create()
{
assert(!mCreated);
mAtmosphereDay = mSceneManager->createInstance("meshes/sky_atmosphere.nif", mEarlyRenderBinRoot);
mAtmosphereDay = mSceneManager->getInstance("meshes/sky_atmosphere.nif", mEarlyRenderBinRoot);
ModVertexAlphaVisitor modAtmosphere(0);
mAtmosphereDay->accept(modAtmosphere);
@ -1155,31 +1159,31 @@ void SkyManager::create()
osg::ref_ptr<osg::Node> atmosphereNight;
if (mSceneManager->getVFS()->exists("meshes/sky_night_02.nif"))
atmosphereNight = mSceneManager->createInstance("meshes/sky_night_02.nif", mAtmosphereNightNode);
atmosphereNight = mSceneManager->getInstance("meshes/sky_night_02.nif", mAtmosphereNightNode);
else
atmosphereNight = mSceneManager->createInstance("meshes/sky_night_01.nif", mAtmosphereNightNode);
atmosphereNight = mSceneManager->getInstance("meshes/sky_night_01.nif", mAtmosphereNightNode);
atmosphereNight->getOrCreateStateSet()->setAttributeAndModes(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
ModVertexAlphaVisitor modStars(2);
atmosphereNight->accept(modStars);
mAtmosphereNightUpdater = new AtmosphereNightUpdater(mSceneManager->getTextureManager());
mAtmosphereNightUpdater = new AtmosphereNightUpdater(mSceneManager->getImageManager());
atmosphereNight->addUpdateCallback(mAtmosphereNightUpdater);
mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getTextureManager()));
mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getImageManager()));
const Fallback::Map* fallback=MWBase::Environment::get().getWorld()->getFallback();
mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getTextureManager(), fallback->getFallbackFloat("Moons_Masser_Size")/125, Moon::Type_Masser));
mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getTextureManager(), fallback->getFallbackFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda));
mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), fallback->getFallbackFloat("Moons_Masser_Size")/125, Moon::Type_Masser));
mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), fallback->getFallbackFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda));
mCloudNode = new osg::PositionAttitudeTransform;
mEarlyRenderBinRoot->addChild(mCloudNode);
mCloudMesh = mSceneManager->createInstance("meshes/sky_clouds_01.nif", mCloudNode);
mCloudMesh = mSceneManager->getInstance("meshes/sky_clouds_01.nif", mCloudNode);
ModVertexAlphaVisitor modClouds(1);
mCloudMesh->accept(modClouds);
mCloudUpdater = new CloudUpdater;
mCloudUpdater->setOpacity(1.f);
mCloudMesh->addUpdateCallback(mCloudUpdater);
mCloudMesh2 = mSceneManager->createInstance("meshes/sky_clouds_01.nif", mCloudNode);
mCloudMesh2 = mSceneManager->getInstance("meshes/sky_clouds_01.nif", mCloudNode);
mCloudMesh2->accept(modClouds);
mCloudUpdater2 = new CloudUpdater;
mCloudUpdater2->setOpacity(0.f);
@ -1342,8 +1346,12 @@ void SkyManager::createRain()
mRainParticleSystem->setAlignVectorY(osg::Vec3f(0,0,-1));
osg::ref_ptr<osg::StateSet> stateset (mRainParticleSystem->getOrCreateStateSet());
stateset->setTextureAttributeAndModes(0, mSceneManager->getTextureManager()->getTexture2D("textures/tx_raindrop_01.dds",
osg::Texture::CLAMP, osg::Texture::CLAMP), osg::StateAttribute::ON);
osg::ref_ptr<osg::Texture2D> raindropTex (new osg::Texture2D(mSceneManager->getImageManager()->getImage("textures/tx_raindrop_01.dds")));
raindropTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
raindropTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
stateset->setTextureAttributeAndModes(0, raindropTex, osg::StateAttribute::ON);
stateset->setNestRenderBins(false);
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
@ -1529,7 +1537,7 @@ void SkyManager::setWeather(const WeatherResult& weather)
mParticleNode->setNodeMask(Mask_WeatherParticles);
mRootNode->addChild(mParticleNode);
}
mParticleEffect = mSceneManager->createInstance(mCurrentParticleEffect, mParticleNode);
mParticleEffect = mSceneManager->getInstance(mCurrentParticleEffect, mParticleNode);
SceneUtil::AssignControllerSourcesVisitor assignVisitor(boost::shared_ptr<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource));
mParticleEffect->accept(assignVisitor);
@ -1549,8 +1557,11 @@ void SkyManager::setWeather(const WeatherResult& weather)
std::string texture = Misc::ResourceHelpers::correctTexturePath(mClouds, mSceneManager->getVFS());
mCloudUpdater->setTexture(mSceneManager->getTextureManager()->getTexture2D(texture,
osg::Texture::REPEAT, osg::Texture::REPEAT));
osg::ref_ptr<osg::Texture2D> cloudTex (new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture)));
cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
mCloudUpdater->setTexture(cloudTex);
}
if (mNextClouds != weather.mNextCloudTexture)
@ -1561,8 +1572,11 @@ void SkyManager::setWeather(const WeatherResult& weather)
{
std::string texture = Misc::ResourceHelpers::correctTexturePath(mNextClouds, mSceneManager->getVFS());
mCloudUpdater2->setTexture(mSceneManager->getTextureManager()->getTexture2D(texture,
osg::Texture::REPEAT, osg::Texture::REPEAT));
osg::ref_ptr<osg::Texture2D> cloudTex (new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture)));
cloudTex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
cloudTex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
mCloudUpdater2->setTexture(cloudTex);
}
}
@ -1680,6 +1694,46 @@ void SkyManager::setWaterHeight(float height)
mUnderwaterSwitch->setWaterLevel(height);
}
void SkyManager::listAssetsToPreload(std::vector<std::string>& models, std::vector<std::string>& textures)
{
models.push_back("meshes/sky_atmosphere.nif");
if (mSceneManager->getVFS()->exists("meshes/sky_night_02.nif"))
models.push_back("meshes/sky_night_02.nif");
models.push_back("meshes/sky_night_01.nif");
models.push_back("meshes/sky_clouds_01.nif");
models.push_back("meshes\\ashcloud.nif");
models.push_back("meshes\\blightcloud.nif");
models.push_back("meshes\\snow.nif");
models.push_back("meshes\\blizzard.nif");
textures.push_back("textures/tx_mooncircle_full_s.dds");
textures.push_back("textures/tx_mooncircle_full_m.dds");
textures.push_back("textures/tx_masser_new.dds");
textures.push_back("textures/tx_masser_one_wax.dds");
textures.push_back("textures/tx_masser_half_wax.dds");
textures.push_back("textures/tx_masser_three_wax.dds");
textures.push_back("textures/tx_masser_one_wan.dds");
textures.push_back("textures/tx_masser_half_wan.dds");
textures.push_back("textures/tx_masser_three_wan.dds");
textures.push_back("textures/tx_masser_full.dds");
textures.push_back("textures/tx_secunda_new.dds");
textures.push_back("textures/tx_secunda_one_wax.dds");
textures.push_back("textures/tx_secunda_half_wax.dds");
textures.push_back("textures/tx_secunda_three_wax.dds");
textures.push_back("textures/tx_secunda_one_wan.dds");
textures.push_back("textures/tx_secunda_half_wan.dds");
textures.push_back("textures/tx_secunda_three_wan.dds");
textures.push_back("textures/tx_secunda_full.dds");
textures.push_back("textures/tx_sun_05.dds");
textures.push_back("textures/tx_sun_flash_grey_05.dds");
textures.push_back("textures/tx_raindrop_01.dds");
}
void SkyManager::setWaterEnabled(bool enabled)
{
mUnderwaterSwitch->setEnabled(enabled);

@ -153,6 +153,8 @@ namespace MWRender
/// Set height of water plane (used to remove underwater weather particles)
void setWaterHeight(float height);
void listAssetsToPreload(std::vector<std::string>& models, std::vector<std::string>& textures);
private:
void create();
///< no need to call this, automatically done on first enable()

@ -62,6 +62,9 @@ namespace MWRender
const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX;
if (!land->isDataLoaded(flags))
land->loadData(flags);
// TODO: unload land data when it's no longer needed
return land;
}

@ -3,7 +3,7 @@
#include <osg/Node>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/texturemanager.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/misc/resourcehelpers.hpp>
namespace MWRender
@ -15,8 +15,10 @@ void overrideTexture(const std::string &texture, Resource::ResourceSystem *resou
return;
std::string correctedTexture = Misc::ResourceHelpers::correctTexturePath(texture, resourceSystem->getVFS());
// Not sure if wrap settings should be pulled from the overridden texture?
osg::ref_ptr<osg::Texture2D> tex = resourceSystem->getTextureManager()->getTexture2D(correctedTexture, osg::Texture2D::CLAMP,
osg::Texture2D::CLAMP);
osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D(resourceSystem->getImageManager()->getImage(correctedTexture));
tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
osg::ref_ptr<osg::StateSet> stateset;
if (node->getStateSet())
stateset = static_cast<osg::StateSet*>(node->getStateSet()->clone(osg::CopyOp::SHALLOW_COPY));

@ -15,6 +15,7 @@
#include <osg/FrontFace>
#include <osg/Shader>
#include <osg/GLExtensions>
#include <osg/UserDataContainer>
#include <osgDB/ReadFile>
@ -25,7 +26,7 @@
#include <osgUtil/CullVisitor>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/texturemanager.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/nifosg/controller.hpp>
#include <components/sceneutil/controller.hpp>
@ -317,6 +318,7 @@ public:
mRefractionTexture->setInternalFormat(GL_RGB);
mRefractionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
mRefractionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
mRefractionTexture->getOrCreateUserDataContainer()->addDescription("dont_override_filter");
attach(osg::Camera::COLOR_BUFFER, mRefractionTexture);
@ -328,6 +330,7 @@ public:
mRefractionDepthTexture->setSourceType(GL_UNSIGNED_INT);
mRefractionDepthTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
mRefractionDepthTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
mRefractionDepthTexture->getOrCreateUserDataContainer()->addDescription("dont_override_filter");
attach(osg::Camera::DEPTH_BUFFER, mRefractionDepthTexture);
}
@ -388,6 +391,7 @@ public:
mReflectionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
mReflectionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
mReflectionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
mReflectionTexture->getOrCreateUserDataContainer()->addDescription("dont_override_filter");
attach(osg::Camera::COLOR_BUFFER, mReflectionTexture);
@ -553,7 +557,10 @@ void Water::createSimpleWaterStateSet(osg::Node* node, float alpha)
{
std::ostringstream texname;
texname << "textures/water/" << texture << std::setw(2) << std::setfill('0') << i << ".dds";
textures.push_back(mResourceSystem->getTextureManager()->getTexture2D(texname.str(), osg::Texture::REPEAT, osg::Texture::REPEAT));
osg::ref_ptr<osg::Texture2D> tex (new osg::Texture2D(mResourceSystem->getImageManager()->getImage(texname.str())));
tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
textures.push_back(tex);
}
if (!textures.size())
@ -643,6 +650,18 @@ Water::~Water()
}
}
void Water::listAssetsToPreload(std::vector<std::string> &textures)
{
int frameCount = mFallback->getFallbackInt("Water_SurfaceFrameCount");
std::string texture = mFallback->getFallbackString("Water_SurfaceTexture");
for (int i=0; i<frameCount; ++i)
{
std::ostringstream texname;
texname << "textures/water/" << texture << std::setw(2) << std::setfill('0') << i << ".dds";
textures.push_back(texname.str());
}
}
void Water::setEnabled(bool enabled)
{
mEnabled = enabled;

@ -2,6 +2,7 @@
#define OPENMW_MWRENDER_WATER_H
#include <memory>
#include <vector>
#include <osg/ref_ptr>
#include <osg/Vec3f>
@ -85,6 +86,8 @@ namespace MWRender
const std::string& resourcePath);
~Water();
void listAssetsToPreload(std::vector<std::string>& textures);
void setEnabled(bool enabled);
bool toggle();

@ -84,7 +84,7 @@ void WeaponAnimation::attachArrow(MWWorld::Ptr actor)
return;
std::string model = ammo->getClass().getModel(*ammo);
osg::ref_ptr<osg::Node> arrow = getResourceSystem()->getSceneManager()->createInstance(model, parent);
osg::ref_ptr<osg::Node> arrow = getResourceSystem()->getSceneManager()->getInstance(model, parent);
mAmmunition = PartHolderPtr(new PartHolder(arrow));
}

@ -0,0 +1,230 @@
#include "cellpreloader.hpp"
#include <iostream>
#include <components/resource/scenemanager.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/bulletshapemanager.hpp>
#include <components/resource/keyframemanager.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/nifosg/nifloader.hpp>
#include <components/terrain/world.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/esmstore.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::Ptr& ptr)
{
ptr.getClass().getModelsToPreload(ptr, mOut);
return true;
}
std::vector<std::string>& mOut;
};
/// Worker thread item: preload models in a cell.
class PreloadItem : public SceneUtil::WorkItem
{
public:
/// Constructor to be called from the main thread.
PreloadItem(MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager, Resource::KeyframeManager* keyframeManager, Terrain::World* terrain)
: mIsExterior(cell->getCell()->isExterior())
, mX(cell->getCell()->getGridX())
, mY(cell->getCell()->getGridY())
, mSceneManager(sceneManager)
, mBulletShapeManager(bulletShapeManager)
, mKeyframeManager(keyframeManager)
, mTerrain(terrain)
{
ListModelsVisitor visitor (mMeshes);
if (cell->getState() == MWWorld::CellStore::State_Loaded)
{
cell->forEach(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
{
std::string mesh = *it;
mesh = Misc::ResourceHelpers::correctActorModelPath(mesh, mSceneManager->getVFS());
mPreloadedObjects.push_back(mSceneManager->cacheInstance(mesh));
mPreloadedObjects.push_back(mBulletShapeManager->cacheInstance(mesh));
size_t slashpos = mesh.find_last_of("/\\");
if (slashpos != std::string::npos && slashpos != mesh.size()-1)
{
Misc::StringUtils::lowerCaseInPlace(mesh);
if (mesh[slashpos+1] == 'x')
{
std::string kfname = mesh;
if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0)
{
kfname.replace(kfname.size()-4, 4, ".kf");
mPreloadedObjects.push_back(mKeyframeManager->get(kfname));
}
}
}
}
catch (std::exception& e)
{
// ignore error for now, would spam the log too much
// error will be shown when visiting the cell
}
}
if (mIsExterior)
{
try
{
mPreloadedObjects.push_back(mTerrain->cacheCell(mX, mY));
}
catch(std::exception& e)
{
}
}
}
private:
typedef std::vector<std::string> MeshList;
bool mIsExterior;
int mX;
int mY;
MeshList mMeshes;
Resource::SceneManager* mSceneManager;
Resource::BulletShapeManager* mBulletShapeManager;
Resource::KeyframeManager* mKeyframeManager;
Terrain::World* mTerrain;
// keep a ref to the loaded objects to make sure it stays loaded as long as this cell is in the preloaded state
std::vector<osg::ref_ptr<const osg::Object> > mPreloadedObjects;
};
/// Worker thread item: update the resource system's cache, effectively deleting unused entries.
class UpdateCacheItem : public SceneUtil::WorkItem
{
public:
UpdateCacheItem(Resource::ResourceSystem* resourceSystem, Terrain::World* terrain, double referenceTime)
: mReferenceTime(referenceTime)
, mResourceSystem(resourceSystem)
, mTerrain(terrain)
{
}
virtual void doWork()
{
mResourceSystem->updateCache(mReferenceTime);
mTerrain->updateCache();
}
private:
double mReferenceTime;
Resource::ResourceSystem* mResourceSystem;
Terrain::World* mTerrain;
};
CellPreloader::CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain)
: mResourceSystem(resourceSystem)
, mBulletShapeManager(bulletShapeManager)
, mTerrain(terrain)
, mExpiryDelay(0.0)
{
}
void CellPreloader::preload(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, mResourceSystem->getSceneManager(), mBulletShapeManager, mResourceSystem->getKeyframeManager(), mTerrain));
mWorkQueue->addWorkItem(item);
mPreloadCells[cell] = PreloadEntry(timestamp, item);
}
void CellPreloader::notifyLoaded(CellStore *cell)
{
mPreloadCells.erase(cell);
}
void CellPreloader::updateCache(double timestamp)
{
// TODO: add settings for a minimum/maximum size of the cache
for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();)
{
if (it->second.mTimeStamp < timestamp - mExpiryDelay)
mPreloadCells.erase(it++);
else
++it;
}
// the resource cache is cleared from the worker thread so that we're not holding up the main thread with delete operations
mWorkQueue->addWorkItem(new UpdateCacheItem(mResourceSystem, mTerrain, timestamp));
}
void CellPreloader::setExpiryDelay(double expiryDelay)
{
mExpiryDelay = expiryDelay;
}
void CellPreloader::setWorkQueue(osg::ref_ptr<SceneUtil::WorkQueue> workQueue)
{
mWorkQueue = workQueue;
}
}

@ -0,0 +1,72 @@
#ifndef OPENMW_MWWORLD_CELLPRELOADER_H
#define OPENMW_MWWORLD_CELLPRELOADER_H
#include <map>
#include <osg/ref_ptr>
#include <components/sceneutil/workqueue.hpp>
namespace Resource
{
class ResourceSystem;
class BulletShapeManager;
}
namespace Terrain
{
class World;
}
namespace MWWorld
{
class CellStore;
class CellPreloader
{
public:
CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain);
/// 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(MWWorld::CellStore* cell, double timestamp);
void notifyLoaded(MWWorld::CellStore* cell);
/// Removes preloaded cells that have not had a preload request for a while.
void updateCache(double timestamp);
/// How long to keep a preloaded cell in cache after it's no longer requested.
void setExpiryDelay(double expiryDelay);
void setWorkQueue(osg::ref_ptr<SceneUtil::WorkQueue> workQueue);
private:
Resource::ResourceSystem* mResourceSystem;
Resource::BulletShapeManager* mBulletShapeManager;
Terrain::World* mTerrain;
osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;
double mExpiryDelay;
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?

@ -291,6 +291,13 @@ namespace MWWorld
return "";
}
void Class::getModelsToPreload(const Ptr &ptr, std::vector<std::string> &models) const
{
std::string model = getModel(ptr);
if (!model.empty())
models.push_back(model);
}
std::string Class::applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const
{
throw std::runtime_error ("class can't be enchanted");

@ -262,6 +262,9 @@ namespace MWWorld
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const;
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel().
virtual std::string applyEnchantment(const MWWorld::ConstPtr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const;
///< Creates a new record using \a ptr as template, with the given name and the given enchantment applied to it.

@ -101,7 +101,7 @@ void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr)
for (std::list<std::pair<std::string, Ptr> >::iterator iter = mScripts.begin(); iter!=mScripts.end(); ++iter)
if (iter->second==ptr)
{
std::cout << "warning, tried to add local script twice for " << ptr.getCellRef().getRefId() << std::endl;
std::cerr << "warning, tried to add local script twice for " << ptr.getCellRef().getRefId() << std::endl;
remove(ptr);
break;
}

@ -92,7 +92,7 @@ namespace MWWorld
attachTo = rotateNode;
}
mResourceSystem->getSceneManager()->createInstance(model, attachTo);
mResourceSystem->getSceneManager()->getInstance(model, attachTo);
SceneUtil::DisableFreezeOnCullVisitor disableFreezeOnCullVisitor;
state.mNode->accept(disableFreezeOnCullVisitor);

@ -24,6 +24,7 @@
#include "class.hpp"
#include "cellvisitors.hpp"
#include "cellstore.hpp"
#include "cellpreloader.hpp"
namespace
{
@ -192,6 +193,16 @@ namespace MWWorld
void Scene::update (float duration, bool paused)
{
if (mPreloadEnabled)
{
mPreloadTimer += duration;
if (mPreloadTimer > 0.25f)
{
preloadCells();
mPreloadTimer = 0.f;
}
}
mRendering.update (duration, paused);
}
@ -284,6 +295,8 @@ namespace MWWorld
if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))
mRendering.configureAmbient(cell->getCell());
}
mPreloader->notifyLoaded(cell);
}
void Scene::changeToVoid()
@ -305,7 +318,7 @@ namespace MWWorld
getGridCenter(cellX, cellY);
float centerX, centerY;
MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true);
const float maxDistance = 8192/2 + 1024; // 1/2 cell size + threshold
const float maxDistance = 8192/2 + mCellLoadingThreshold; // 1/2 cell size + threshold
float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y()));
if (distance > maxDistance)
{
@ -326,15 +339,13 @@ namespace MWWorld
std::string loadingExteriorText = "#{sLoadingMessage3}";
loadingListener->setLabel(loadingExteriorText);
const int halfGridSize = Settings::Manager::getInt("exterior cell load distance", "Cells");
CellStoreCollection::iterator active = mActiveCells.begin();
while (active!=mActiveCells.end())
{
if ((*active)->getCell()->isExterior())
{
if (std::abs (X-(*active)->getCell()->getGridX())<=halfGridSize &&
std::abs (Y-(*active)->getCell()->getGridY())<=halfGridSize)
if (std::abs (X-(*active)->getCell()->getGridX())<=mHalfGridSize &&
std::abs (Y-(*active)->getCell()->getGridY())<=mHalfGridSize)
{
// keep cells within the new grid
++active;
@ -346,9 +357,9 @@ namespace MWWorld
int refsToLoad = 0;
// get the number of refs to load
for (int x=X-halfGridSize; x<=X+halfGridSize; ++x)
for (int x=X-mHalfGridSize; x<=X+mHalfGridSize; ++x)
{
for (int y=Y-halfGridSize; y<=Y+halfGridSize; ++y)
for (int y=Y-mHalfGridSize; y<=Y+mHalfGridSize; ++y)
{
CellStoreCollection::iterator iter = mActiveCells.begin();
@ -371,9 +382,9 @@ namespace MWWorld
loadingListener->setProgressRange(refsToLoad);
// Load cells
for (int x=X-halfGridSize; x<=X+halfGridSize; ++x)
for (int x=X-mHalfGridSize; x<=X+mHalfGridSize; ++x)
{
for (int y=Y-halfGridSize; y<=Y+halfGridSize; ++y)
for (int y=Y-mHalfGridSize; y<=Y+mHalfGridSize; ++y)
{
CellStoreCollection::iterator iter = mActiveCells.begin();
@ -402,7 +413,7 @@ namespace MWWorld
mCellChanged = true;
mRendering.getResourceSystem()->clearCache();
mPreloader->updateCache(mRendering.getReferenceTime());
}
void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos)
@ -438,7 +449,23 @@ namespace MWWorld
Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics)
: mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering)
, mPreloadTimer(0.f)
, mHalfGridSize(Settings::Manager::getInt("exterior cell load distance", "Cells"))
, mCellLoadingThreshold(1024.f)
, mPreloadDistance(Settings::Manager::getInt("preload distance", "Cells"))
, mPreloadEnabled(Settings::Manager::getBool("preload enabled", "Cells"))
, mPreloadExteriorGrid(Settings::Manager::getBool("preload exterior grid", "Cells"))
, mPreloadDoors(Settings::Manager::getBool("preload doors", "Cells"))
, mPreloadFastTravel(Settings::Manager::getBool("preload fast travel", "Cells"))
{
mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager(), rendering.getTerrain()));
mPreloader->setWorkQueue(mRendering.getWorkQueue());
mPhysics->setUnrefQueue(rendering.getUnrefQueue());
float cacheExpiryDelay = Settings::Manager::getFloat("cache expiry delay", "Cells");
rendering.getResourceSystem()->setExpiryDelay(cacheExpiryDelay);
mPreloader->setExpiryDelay(cacheExpiryDelay);
}
Scene::~Scene()
@ -515,7 +542,7 @@ namespace MWWorld
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
mRendering.getResourceSystem()->clearCache();
mPreloader->updateCache(mRendering.getReferenceTime());
}
void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos)
@ -599,4 +626,167 @@ namespace MWWorld
return Ptr();
}
void Scene::preloadCells()
{
if (mPreloadDoors)
preloadTeleportDoorDestinations();
if (mPreloadExteriorGrid)
preloadExteriorGrid();
if (mPreloadFastTravel)
preloadFastTravelDestinations();
}
void Scene::preloadTeleportDoorDestinations()
{
std::vector<MWWorld::ConstPtr> teleportDoors;
for (CellStoreCollection::const_iterator iter (mActiveCells.begin());
iter!=mActiveCells.end(); ++iter)
{
const MWWorld::CellStore* cellStore = *iter;
typedef MWWorld::CellRefList<ESM::Door>::List DoorList;
const DoorList &doors = cellStore->getReadOnlyDoors().mList;
for (DoorList::const_iterator doorIt = doors.begin(); doorIt != doors.end(); ++doorIt)
{
if (!doorIt->mRef.getTeleport()) {
continue;
}
teleportDoors.push_back(MWWorld::ConstPtr(&*doorIt, cellStore));
}
}
const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
for (std::vector<MWWorld::ConstPtr>::iterator it = teleportDoors.begin(); it != teleportDoors.end(); ++it)
{
const MWWorld::ConstPtr& door = *it;
float sqrDistToPlayer = (player.getRefData().getPosition().asVec3() - door.getRefData().getPosition().asVec3()).length2();
if (sqrDistToPlayer < mPreloadDistance*mPreloadDistance)
{
try
{
if (!door.getCellRef().getDestCell().empty())
preloadCell(MWBase::Environment::get().getWorld()->getInterior(door.getCellRef().getDestCell()));
else
{
int x,y;
MWBase::Environment::get().getWorld()->positionToIndex (door.getCellRef().getDoorDest().pos[0], door.getCellRef().getDoorDest().pos[1], x, y);
preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true);
}
}
catch (std::exception& e)
{
// ignore error for now, would spam the log too much
}
}
}
}
void Scene::preloadExteriorGrid()
{
if (!MWBase::Environment::get().getWorld()->isCellExterior())
return;
int halfGridSizePlusOne = mHalfGridSize + 1;
const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
int cellX,cellY;
getGridCenter(cellX,cellY);
float centerX, centerY;
MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true);
for (int dx = -halfGridSizePlusOne; dx <= halfGridSizePlusOne; ++dx)
{
for (int dy = -halfGridSizePlusOne; dy <= halfGridSizePlusOne; ++dy)
{
if (dy != halfGridSizePlusOne && dy != -halfGridSizePlusOne && dx != halfGridSizePlusOne && dx != -halfGridSizePlusOne)
continue; // only care about the outer (not yet loaded) part of the grid
float thisCellCenterX, thisCellCenterY;
MWBase::Environment::get().getWorld()->indexToPosition(cellX+dx, cellY+dy, thisCellCenterX, thisCellCenterY, true);
float dist = std::max(std::abs(thisCellCenterX - playerPos.x()), std::abs(thisCellCenterY - playerPos.y()));
float loadDist = 8192/2 + 8192 - mCellLoadingThreshold + mPreloadDistance;
if (dist < loadDist)
preloadCell(MWBase::Environment::get().getWorld()->getExterior(cellX+dx, cellY+dy));
}
}
}
void Scene::preloadCell(CellStore *cell, bool preloadSurrounding)
{
if (preloadSurrounding && cell->isExterior())
{
int x = cell->getCell()->getGridX();
int y = cell->getCell()->getGridY();
for (int dx = -mHalfGridSize; dx <= mHalfGridSize; ++dx)
{
for (int dy = -mHalfGridSize; dy <= mHalfGridSize; ++dy)
{
mPreloader->preload(MWBase::Environment::get().getWorld()->getExterior(x+dx, y+dy), mRendering.getReferenceTime());
}
}
}
else
mPreloader->preload(cell, mRendering.getReferenceTime());
}
struct ListFastTravelDestinationsVisitor
{
ListFastTravelDestinationsVisitor(float preloadDist, const osg::Vec3f& playerPos)
: mPreloadDist(preloadDist)
, mPlayerPos(playerPos)
{
}
bool operator()(const MWWorld::Ptr& ptr)
{
if ((ptr.getRefData().getPosition().asVec3() - mPlayerPos).length2() > mPreloadDist * mPreloadDist)
return true;
if (ptr.getClass().isNpc())
{
const std::vector<ESM::Transport::Dest>& transport = ptr.get<ESM::NPC>()->mBase->mTransport.mList;
mList.insert(mList.begin(), transport.begin(), transport.end());
}
else
{
const std::vector<ESM::Transport::Dest>& transport = ptr.get<ESM::Creature>()->mBase->mTransport.mList;
mList.insert(mList.begin(), transport.begin(), transport.end());
}
return true;
}
float mPreloadDist;
osg::Vec3f mPlayerPos;
std::vector<ESM::Transport::Dest> mList;
};
void Scene::preloadFastTravelDestinations()
{
const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
ListFastTravelDestinationsVisitor listVisitor(mPreloadDistance, player.getRefData().getPosition().asVec3());
for (CellStoreCollection::const_iterator iter (mActiveCells.begin()); iter!=mActiveCells.end(); ++iter)
{
MWWorld::CellStore* cellStore = *iter;
cellStore->forEachType<ESM::NPC>(listVisitor);
cellStore->forEachType<ESM::Creature>(listVisitor);
}
for (std::vector<ESM::Transport::Dest>::const_iterator it = listVisitor.mList.begin(); it != listVisitor.mList.end(); ++it)
{
if (!it->mCellName.empty())
preloadCell(MWBase::Environment::get().getWorld()->getInterior(it->mCellName));
else
{
int x,y;
MWBase::Environment::get().getWorld()->positionToIndex( it->mPos.pos[0], it->mPos.pos[1], x, y);
preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true);
}
}
}
}

@ -1,12 +1,11 @@
#ifndef GAME_MWWORLD_SCENE_H
#define GAME_MWWORLD_SCENE_H
//#include "../mwrender/renderingmanager.hpp"
#include "ptr.hpp"
#include "globals.hpp"
#include <set>
#include <memory>
namespace osg
{
@ -43,6 +42,7 @@ namespace MWWorld
{
class Player;
class CellStore;
class CellPreloader;
class Scene
{
@ -57,6 +57,16 @@ namespace MWWorld
bool mCellChanged;
MWPhysics::PhysicsSystem *mPhysics;
MWRender::RenderingManager& mRendering;
std::auto_ptr<CellPreloader> mPreloader;
float mPreloadTimer;
int mHalfGridSize;
float mCellLoadingThreshold;
float mPreloadDistance;
bool mPreloadEnabled;
bool mPreloadExteriorGrid;
bool mPreloadDoors;
bool mPreloadFastTravel;
void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener);
@ -65,6 +75,13 @@ namespace MWWorld
void getGridCenter(int& cellX, int& cellY);
void preloadCells();
void preloadTeleportDoorDestinations();
void preloadExteriorGrid();
void preloadFastTravelDestinations();
void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false);
public:
Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics);

@ -3216,4 +3216,9 @@ namespace MWWorld
return mPhysics->getHitDistance(weaponPos, target);
}
void World::preloadCommonAssets()
{
mRendering->preloadCommonAssets();
}
}

@ -183,6 +183,8 @@ namespace MWWorld
virtual void startNewGame (bool bypass);
///< \param bypass Bypass regular game start.
virtual void preloadCommonAssets();
virtual void clear();
virtual int countSavedGameRecords() const;

@ -41,14 +41,12 @@ add_component_dir (vfs
)
add_component_dir (resource
scenemanager keyframemanager texturemanager resourcesystem bulletshapemanager bulletshape niffilemanager objectcache
scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem resourcemanager
)
add_component_dir (sceneutil
clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller
lightmanager lightutil positionattitudetransform
# not used yet
#workqueue
lightmanager lightutil positionattitudetransform workqueue unrefqueue
)
add_component_dir (nif

@ -90,6 +90,7 @@ private:
void readHeader();
/// Get the index of a given file name, or -1 if not found
/// @note Thread safe.
int getIndex(const char *str) const;
public:
@ -116,12 +117,17 @@ public:
/** Open a file contained in the archive. Throws an exception if the
file doesn't exist.
* @note Thread safe.
*/
Files::IStreamPtr getFile(const char *file);
/** Open a file contained in the archive.
* @note Thread safe.
*/
Files::IStreamPtr getFile(const FileStruct* file);
/// Get a list of all files
/// @note Thread safe.
const FileList &getList() const
{ return files; }
};

@ -2,6 +2,8 @@
#include <utility>
#include <OpenThreads/ScopedLock>
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "defs.hpp"
@ -60,7 +62,6 @@ namespace ESM
, mX(0)
, mY(0)
, mPlugin(0)
, mEsm(NULL)
, mDataTypes(0)
, mDataLoaded(false)
, mLandData(NULL)
@ -86,8 +87,7 @@ namespace ESM
{
isDeleted = false;
mEsm = &esm;
mPlugin = mEsm->getIndex();
mPlugin = esm.getIndex();
bool hasLocation = false;
bool isLoaded = false;
@ -180,6 +180,8 @@ namespace ESM
void Land::loadData(int flags) const
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
// Try to load only available data
flags = flags & mDataTypes;
// Return if all required data is loaded
@ -191,15 +193,17 @@ namespace ESM
mLandData = new LandData;
mLandData->mDataTypes = mDataTypes;
}
mEsm->restoreContext(mContext);
if (mEsm->isNextSub("VNML")) {
condLoad(flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals));
ESM::ESMReader reader;
reader.restoreContext(mContext);
if (reader.isNextSub("VNML")) {
condLoad(reader, flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals));
}
if (mEsm->isNextSub("VHGT")) {
if (reader.isNextSub("VHGT")) {
static VHGT vhgt;
if (condLoad(flags, DATA_VHGT, &vhgt, sizeof(vhgt))) {
if (condLoad(reader, flags, DATA_VHGT, &vhgt, sizeof(vhgt))) {
float rowOffset = vhgt.mHeightOffset;
for (int y = 0; y < LAND_SIZE; y++) {
rowOffset += vhgt.mHeightData[y * LAND_SIZE];
@ -217,14 +221,14 @@ namespace ESM
}
}
if (mEsm->isNextSub("WNAM")) {
condLoad(flags, DATA_WNAM, mLandData->mWnam, 81);
if (reader.isNextSub("WNAM")) {
condLoad(reader, flags, DATA_WNAM, mLandData->mWnam, 81);
}
if (mEsm->isNextSub("VCLR"))
condLoad(flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS);
if (mEsm->isNextSub("VTEX")) {
if (reader.isNextSub("VCLR"))
condLoad(reader, flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS);
if (reader.isNextSub("VTEX")) {
static uint16_t vtex[LAND_NUM_TEXTURES];
if (condLoad(flags, DATA_VTEX, vtex, sizeof(vtex))) {
if (condLoad(reader, flags, DATA_VTEX, vtex, sizeof(vtex))) {
LandData::transposeTextureData(vtex, mLandData->mTextures);
}
}
@ -240,25 +244,26 @@ namespace ESM
}
}
bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size) const
bool Land::condLoad(ESM::ESMReader& reader, int flags, int dataFlag, void *ptr, unsigned int size) const
{
if ((mDataLoaded & dataFlag) == 0 && (flags & dataFlag) != 0) {
mEsm->getHExact(ptr, size);
reader.getHExact(ptr, size);
mDataLoaded |= dataFlag;
return true;
}
mEsm->skipHSubSize(size);
reader.skipHSubSize(size);
return false;
}
bool Land::isDataLoaded(int flags) const
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
return (mDataLoaded & flags) == (flags & mDataTypes);
}
Land::Land (const Land& land)
: mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin),
mEsm (land.mEsm), mContext (land.mContext), mDataTypes (land.mDataTypes),
mContext (land.mContext), mDataTypes (land.mDataTypes),
mDataLoaded (land.mDataLoaded),
mLandData (land.mLandData ? new LandData (*land.mLandData) : 0)
{}
@ -275,7 +280,6 @@ namespace ESM
std::swap (mX, land.mX);
std::swap (mY, land.mY);
std::swap (mPlugin, land.mPlugin);
std::swap (mEsm, land.mEsm);
std::swap (mContext, land.mContext);
std::swap (mDataTypes, land.mDataTypes);
std::swap (mDataLoaded, land.mDataLoaded);

@ -3,6 +3,8 @@
#include <stdint.h>
#include <OpenThreads/Mutex>
#include "esmcommon.hpp"
namespace ESM
@ -31,7 +33,6 @@ struct Land
// File context. This allows the ESM reader to be 'reset' to this
// location later when we are ready to load the full data set.
ESMReader* mEsm;
ESM_Context mContext;
int mDataTypes;
@ -155,7 +156,9 @@ struct Land
/// Loads data and marks it as loaded
/// \return true if data is actually loaded from file, false otherwise
/// including the case when data is already loaded
bool condLoad(int flags, int dataFlag, void *ptr, unsigned int size) const;
bool condLoad(ESM::ESMReader& reader, int flags, int dataFlag, void *ptr, unsigned int size) const;
mutable OpenThreads::Mutex mMutex;
mutable int mDataLoaded;

@ -3,6 +3,8 @@
#include <set>
#include <iostream>
#include <OpenThreads/ScopedLock>
#include <osg/Image>
#include <osg/Plane>
@ -399,9 +401,9 @@ namespace ESMTerrain
int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0;
if (blendIndex == i)
pData[y*blendmapSize*channels + x*channels + channel] = 255;
pData[(blendmapSize - y - 1)*blendmapSize*channels + x*channels + channel] = 255;
else
pData[y*blendmapSize*channels + x*channels + channel] = 0;
pData[(blendmapSize - y - 1)*blendmapSize*channels + x*channels + channel] = 0;
}
}
@ -498,6 +500,8 @@ namespace ESMTerrain
Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mLayerInfoMutex);
// Already have this cached?
std::map<std::string, Terrain::LayerInfo>::iterator found = mLayerInfoMap.find(texture);
if (found != mLayerInfoMap.end())

@ -1,6 +1,8 @@
#ifndef COMPONENTS_ESM_TERRAIN_STORAGE_H
#define COMPONENTS_ESM_TERRAIN_STORAGE_H
#include <OpenThreads/Mutex>
#include <components/terrain/storage.hpp>
#include <components/esm/loadland.hpp>
@ -105,6 +107,7 @@ namespace ESMTerrain
std::string getTextureName (UniqueTextureId id);
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;
OpenThreads::Mutex mLayerInfoMutex;
Terrain::LayerInfo getLayerInfo(const std::string& texture);
};

@ -7,14 +7,14 @@
namespace osgMyGUI
{
Platform::Platform(osgViewer::Viewer *viewer, osg::Group *guiRoot, Resource::TextureManager *textureManager, float uiScalingFactor)
Platform::Platform(osgViewer::Viewer *viewer, osg::Group *guiRoot, Resource::ImageManager *imageManager, float uiScalingFactor)
: mRenderManager(nullptr)
, mDataManager(nullptr)
, mLogManager(nullptr)
, mLogFacility(nullptr)
{
mLogManager = new MyGUI::LogManager();
mRenderManager = new RenderManager(viewer, guiRoot, textureManager, uiScalingFactor);
mRenderManager = new RenderManager(viewer, guiRoot, imageManager, uiScalingFactor);
mDataManager = new DataManager();
}

@ -13,7 +13,7 @@ namespace osg
}
namespace Resource
{
class TextureManager;
class ImageManager;
}
namespace MyGUI
{
@ -30,7 +30,7 @@ namespace osgMyGUI
class Platform
{
public:
Platform(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::TextureManager* textureManager, float uiScalingFactor);
Platform(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ImageManager* imageManager, float uiScalingFactor);
~Platform();

@ -9,12 +9,13 @@
#include <osg/Geode>
#include <osg/BlendFunc>
#include <osg/Texture2D>
#include <osg/TexMat>
#include <osgViewer/Viewer>
#include <osgGA/GUIEventHandler>
#include <components/resource/texturemanager.hpp>
#include <components/resource/imagemanager.hpp>
#include "myguitexture.hpp"
@ -189,6 +190,12 @@ public:
mStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
mStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
mStateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
// need to flip tex coords since MyGUI uses DirectX convention of top left image origin
osg::Matrix flipMat;
flipMat.preMultTranslate(osg::Vec3f(0,1,0));
flipMat.preMultScale(osg::Vec3f(1,-1,1));
mStateSet->setTextureAttribute(0, new osg::TexMat(flipMat), osg::StateAttribute::ON);
}
Drawable(const Drawable &copy, const osg::CopyOp &copyop=osg::CopyOp::SHALLOW_COPY)
: osg::Drawable(copy, copyop)
@ -346,10 +353,10 @@ void OSGVertexBuffer::create()
// ---------------------------------------------------------------------------
RenderManager::RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::TextureManager* textureManager, float scalingFactor)
RenderManager::RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::ImageManager* imageManager, float scalingFactor)
: mViewer(viewer)
, mSceneRoot(sceneroot)
, mTextureManager(textureManager)
, mImageManager(imageManager)
, mUpdate(false)
, mIsInitialise(false)
, mInvScalingFactor(1.f)
@ -516,7 +523,7 @@ MyGUI::ITexture* RenderManager::createTexture(const std::string &name)
mTextures.erase(item);
}
OSGTexture* texture = new OSGTexture(name, mTextureManager);
OSGTexture* texture = new OSGTexture(name, mImageManager);
mTextures.insert(std::make_pair(name, texture));
return texture;
}

@ -7,7 +7,7 @@
namespace Resource
{
class TextureManager;
class ImageManager;
}
namespace osgViewer
@ -33,7 +33,7 @@ class RenderManager : public MyGUI::RenderManager, public MyGUI::IRenderTarget
osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osg::Group> mSceneRoot;
osg::ref_ptr<Drawable> mDrawable;
Resource::TextureManager* mTextureManager;
Resource::ImageManager* mImageManager;
MyGUI::IntSize mViewSize;
bool mUpdate;
@ -54,7 +54,7 @@ class RenderManager : public MyGUI::RenderManager, public MyGUI::IRenderTarget
void destroyAllResources();
public:
RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::TextureManager* textureManager, float scalingFactor);
RenderManager(osgViewer::Viewer *viewer, osg::Group *sceneroot, Resource::ImageManager* imageManager, float scalingFactor);
virtual ~RenderManager();
void initialise();

@ -5,14 +5,14 @@
#include <osg/Texture2D>
#include <components/resource/texturemanager.hpp>
#include <components/resource/imagemanager.hpp>
namespace osgMyGUI
{
OSGTexture::OSGTexture(const std::string &name, Resource::TextureManager* textureManager)
OSGTexture::OSGTexture(const std::string &name, Resource::ImageManager* imageManager)
: mName(name)
, mTextureManager(textureManager)
, mImageManager(imageManager)
, mFormat(MyGUI::PixelFormat::Unknow)
, mUsage(MyGUI::TextureUsage::Default)
, mNumElemBytes(0)
@ -20,7 +20,7 @@ namespace osgMyGUI
}
OSGTexture::OSGTexture(osg::Texture2D *texture)
: mTextureManager(NULL)
: mImageManager(NULL)
, mTexture(texture)
, mFormat(MyGUI::PixelFormat::Unknow)
, mUsage(MyGUI::TextureUsage::Default)
@ -83,10 +83,12 @@ namespace osgMyGUI
void OSGTexture::loadFromFile(const std::string &fname)
{
if (!mTextureManager)
throw std::runtime_error("No texturemanager set");
if (!mImageManager)
throw std::runtime_error("No imagemanager set");
mTexture = mTextureManager->getTexture2D(fname, osg::Texture2D::CLAMP_TO_EDGE, osg::Texture2D::CLAMP_TO_EDGE);
mTexture = new osg::Texture2D(mImageManager->getImage(fname));
mTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
mTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
// disable mip-maps
mTexture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
@ -143,6 +145,8 @@ namespace osgMyGUI
if (!mLockedImage.valid())
throw std::runtime_error("Texture not locked");
mLockedImage->flipVertical();
// mTexture might be in use by the draw thread, so create a new texture instead and use that.
osg::ref_ptr<osg::Texture2D> newTexture = new osg::Texture2D;
newTexture->setTextureSize(getWidth(), getHeight());

@ -13,7 +13,7 @@ namespace osg
namespace Resource
{
class TextureManager;
class ImageManager;
}
namespace osgMyGUI
@ -21,7 +21,7 @@ namespace osgMyGUI
class OSGTexture : public MyGUI::ITexture {
std::string mName;
Resource::TextureManager* mTextureManager;
Resource::ImageManager* mImageManager;
osg::ref_ptr<osg::Image> mLockedImage;
osg::ref_ptr<osg::Texture2D> mTexture;
@ -30,7 +30,7 @@ namespace osgMyGUI
size_t mNumElemBytes;
public:
OSGTexture(const std::string &name, Resource::TextureManager* textureManager);
OSGTexture(const std::string &name, Resource::ImageManager* imageManager);
OSGTexture(osg::Texture2D* texture);
virtual ~OSGTexture();

@ -65,8 +65,14 @@ void ShapeData::read(NIFStream *nif)
uvlist.resize(uvs);
for(int i = 0;i < uvs;i++)
{
uvlist[i] = new osg::Vec2Array(osg::Array::BIND_PER_VERTEX);
nif->getVector2s(uvlist[i], verts);
osg::Vec2Array* list = uvlist[i] = new osg::Vec2Array(osg::Array::BIND_PER_VERTEX);
nif->getVector2s(list, verts);
// flip the texture coordinates to convert them to the OpenGL convention of bottom-left image origin
for (unsigned int uv=0; uv<list->size(); ++uv)
{
(*list)[uv] = osg::Vec2((*list)[uv].x(), 1.f - (*list)[uv].y());
}
}
}
}

@ -17,7 +17,6 @@
#include <osg/Texture2D>
#include <osg/ref_ptr>
#include <osg/Timer>
#include <osg/StateSet>
#include <osg/NodeCallback>
#include <osg/Drawable>
@ -305,6 +304,8 @@ namespace NifOsg
META_Object(NifOsg, FlipController)
std::vector<osg::ref_ptr<osg::Texture2D> >& getTextures() { return mTextures; }
virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv);
};

@ -11,7 +11,7 @@
// resource
#include <components/misc/stringops.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/resource/texturemanager.hpp>
#include <components/resource/imagemanager.hpp>
// skel
#include <osgAnimation/MorphGeometry>
@ -343,7 +343,7 @@ namespace NifOsg
}
}
osg::ref_ptr<osg::Node> load(Nif::NIFFilePtr nif, Resource::TextureManager* textureManager)
osg::ref_ptr<osg::Node> load(Nif::NIFFilePtr nif, Resource::ImageManager* imageManager)
{
if (nif->numRoots() < 1)
nif->fail("Found no root nodes");
@ -356,7 +356,7 @@ namespace NifOsg
osg::ref_ptr<TextKeyMapHolder> textkeys (new TextKeyMapHolder);
osg::ref_ptr<osg::Node> created = handleNode(nifNode, NULL, textureManager, std::vector<int>(), 0, 0, false, &textkeys->mTextKeys);
osg::ref_ptr<osg::Node> created = handleNode(nifNode, NULL, imageManager, std::vector<int>(), 0, 0, false, &textkeys->mTextKeys);
if (nif->getUseSkinning())
{
@ -369,13 +369,13 @@ namespace NifOsg
return created;
}
void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetUpdater* composite, Resource::TextureManager* textureManager, std::vector<int>& boundTextures, int animflags)
void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector<int>& boundTextures, int animflags)
{
const Nif::PropertyList& props = nifNode->props;
for (size_t i = 0; i <props.length();++i)
{
if (!props[i].empty())
handleProperty(props[i].getPtr(), applyTo, composite, textureManager, boundTextures, animflags);
handleProperty(props[i].getPtr(), applyTo, composite, imageManager, boundTextures, animflags);
}
}
@ -440,7 +440,7 @@ namespace NifOsg
return lod;
}
osg::ref_ptr<osg::Node> handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::TextureManager* textureManager,
osg::ref_ptr<osg::Node> handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager,
std::vector<int> boundTextures, int animflags, int particleflags, bool skipMeshes, TextKeyMap* textKeys, osg::Node* rootNode=NULL)
{
osg::ref_ptr<osg::Group> node = new osg::MatrixTransform(nifNode->trafo.toMatrix());
@ -542,7 +542,7 @@ namespace NifOsg
osg::ref_ptr<SceneUtil::CompositeStateSetUpdater> composite = new SceneUtil::CompositeStateSetUpdater;
applyNodeProperties(nifNode, node, composite, textureManager, boundTextures, animflags);
applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags);
if (nifNode->recType == Nif::RC_NiTriShape && !skipMeshes)
{
@ -588,7 +588,7 @@ namespace NifOsg
{
if(!children[i].empty())
{
handleNode(children[i].getPtr(), node, textureManager, boundTextures, animflags, particleflags, skipMeshes, textKeys, rootNode);
handleNode(children[i].getPtr(), node, imageManager, boundTextures, animflags, particleflags, skipMeshes, textKeys, rootNode);
}
}
}
@ -682,7 +682,7 @@ namespace NifOsg
}
}
void handleTextureControllers(const Nif::Property *texProperty, SceneUtil::CompositeStateSetUpdater* composite, Resource::TextureManager* textureManager, osg::StateSet *stateset, int animflags)
void handleTextureControllers(const Nif::Property *texProperty, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, osg::StateSet *stateset, int animflags)
{
for (Nif::ControllerPtr ctrl = texProperty->controller; !ctrl.empty(); ctrl = ctrl->next)
{
@ -708,8 +708,10 @@ namespace NifOsg
wrapT = inherit->getWrap(osg::Texture2D::WRAP_T);
}
std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, textureManager->getVFS());
osg::ref_ptr<osg::Texture2D> texture = textureManager->getTexture2D(filename, wrapS, wrapT);
std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, imageManager->getVFS());
osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D(imageManager->getImage(filename)));
texture->setWrap(osg::Texture::WRAP_S, wrapS);
texture->setWrap(osg::Texture::WRAP_T, wrapT);
textures.push_back(texture);
}
osg::ref_ptr<FlipController> callback(new FlipController(flipctrl, textures));
@ -1242,7 +1244,7 @@ namespace NifOsg
}
void handleProperty(const Nif::Property *property,
osg::Node *node, SceneUtil::CompositeStateSetUpdater* composite, Resource::TextureManager* textureManager, std::vector<int>& boundTextures, int animflags)
osg::Node *node, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector<int>& boundTextures, int animflags)
{
switch (property->recType)
{
@ -1370,15 +1372,16 @@ namespace NifOsg
continue;
}
std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, textureManager->getVFS());
std::string filename = Misc::ResourceHelpers::correctTexturePath(st->filename, imageManager->getVFS());
unsigned int clamp = static_cast<unsigned int>(tex.clamp);
int wrapT = (clamp) & 0x1;
int wrapS = (clamp >> 1) & 0x1;
osg::ref_ptr<osg::Texture2D> texture2d = textureManager->getTexture2D(filename,
wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP,
wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP);
// create a new texture, will later attempt to share using the SharedStateManager
osg::ref_ptr<osg::Texture2D> texture2d (new osg::Texture2D(imageManager->getImage(filename)));
texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP);
texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP);
int texUnit = boundTextures.size();
@ -1420,7 +1423,7 @@ namespace NifOsg
boundTextures.push_back(tex.uvSet);
}
handleTextureControllers(texprop, composite, textureManager, stateset, animflags);
handleTextureControllers(texprop, composite, imageManager, stateset, animflags);
}
break;
}
@ -1540,10 +1543,10 @@ namespace NifOsg
};
osg::ref_ptr<osg::Node> Loader::load(Nif::NIFFilePtr file, Resource::TextureManager* textureManager)
osg::ref_ptr<osg::Node> Loader::load(Nif::NIFFilePtr file, Resource::ImageManager* imageManager)
{
LoaderImpl impl(file->getFilename());
return impl.load(file, textureManager);
return impl.load(file, imageManager);
}
void Loader::loadKf(Nif::NIFFilePtr kf, KeyframeHolder& target)

@ -15,7 +15,7 @@ namespace osg
namespace Resource
{
class TextureManager;
class ImageManager;
}
namespace NifOsg
@ -62,7 +62,7 @@ namespace NifOsg
{
public:
/// Create a scene graph for the given NIF. Auto-detects when skinning is used and wraps the graph in a Skeleton if so.
static osg::ref_ptr<osg::Node> load(Nif::NIFFilePtr file, Resource::TextureManager* textureManager);
static osg::ref_ptr<osg::Node> load(Nif::NIFFilePtr file, Resource::ImageManager* imageManager);
/// Load keyframe controllers from the given kf file.
static void loadKf(Nif::NIFFilePtr kf, KeyframeHolder& target);

@ -17,6 +17,14 @@ BulletShape::BulletShape()
}
BulletShape::BulletShape(const BulletShape &copy, const osg::CopyOp &copyop)
: mCollisionShape(duplicateCollisionShape(copy.mCollisionShape))
, mCollisionBoxHalfExtents(copy.mCollisionBoxHalfExtents)
, mCollisionBoxTranslate(copy.mCollisionBoxTranslate)
, mAnimatedShapes(copy.mAnimatedShapes)
{
}
BulletShape::~BulletShape()
{
deleteShape(mCollisionShape);
@ -37,11 +45,11 @@ void BulletShape::deleteShape(btCollisionShape* shape)
}
}
btCollisionShape* BulletShape::duplicateCollisionShape(btCollisionShape *shape) const
btCollisionShape* BulletShape::duplicateCollisionShape(const btCollisionShape *shape) const
{
if(shape->isCompound())
{
btCompoundShape *comp = static_cast<btCompoundShape*>(shape);
const btCompoundShape *comp = static_cast<const btCompoundShape*>(shape);
btCompoundShape *newShape = new btCompoundShape;
int numShapes = comp->getNumChildShapes();
@ -55,29 +63,27 @@ btCollisionShape* BulletShape::duplicateCollisionShape(btCollisionShape *shape)
return newShape;
}
if(btBvhTriangleMeshShape* trishape = dynamic_cast<btBvhTriangleMeshShape*>(shape))
if(const btBvhTriangleMeshShape* trishape = dynamic_cast<const btBvhTriangleMeshShape*>(shape))
{
#if BT_BULLET_VERSION >= 283
btScaledBvhTriangleMeshShape* newShape = new btScaledBvhTriangleMeshShape(trishape, btVector3(1.f, 1.f, 1.f));
btScaledBvhTriangleMeshShape* newShape = new btScaledBvhTriangleMeshShape(const_cast<btBvhTriangleMeshShape*>(trishape), btVector3(1.f, 1.f, 1.f));
#else
// work around btScaledBvhTriangleMeshShape bug ( https://code.google.com/p/bullet/issues/detail?id=371 ) in older bullet versions
btTriangleMesh* oldMesh = static_cast<btTriangleMesh*>(trishape->getMeshInterface());
const btTriangleMesh* oldMesh = static_cast<const btTriangleMesh*>(trishape->getMeshInterface());
btTriangleMesh* newMesh = new btTriangleMesh(*oldMesh);
// Do not build a new bvh (not needed, since it's the same as the original shape's bvh)
bool buildBvh = true;
if (trishape->getOptimizedBvh())
buildBvh = false;
TriangleMeshShape* newShape = new TriangleMeshShape(newMesh, true, buildBvh);
btOptimizedBvh* bvh = const_cast<btBvhTriangleMeshShape*>(trishape)->getOptimizedBvh();
TriangleMeshShape* newShape = new TriangleMeshShape(newMesh, true, bvh == NULL);
// Set original shape's bvh via pointer
// The pointer is safe because the BulletShapeInstance keeps a ref_ptr to the original BulletShape
if (!buildBvh)
newShape->setOptimizedBvh(trishape->getOptimizedBvh());
if (bvh)
newShape->setOptimizedBvh(bvh);
#endif
return newShape;
}
if (btBoxShape* boxshape = dynamic_cast<btBoxShape*>(shape))
if (const btBoxShape* boxshape = dynamic_cast<const btBoxShape*>(shape))
{
return new btBoxShape(*boxshape);
}
@ -90,13 +96,13 @@ btCollisionShape *BulletShape::getCollisionShape()
return mCollisionShape;
}
osg::ref_ptr<BulletShapeInstance> BulletShape::makeInstance()
osg::ref_ptr<BulletShapeInstance> BulletShape::makeInstance() const
{
osg::ref_ptr<BulletShapeInstance> instance (new BulletShapeInstance(this));
return instance;
}
BulletShapeInstance::BulletShapeInstance(osg::ref_ptr<BulletShape> source)
BulletShapeInstance::BulletShapeInstance(osg::ref_ptr<const BulletShape> source)
: BulletShape()
, mSource(source)
{

@ -3,7 +3,7 @@
#include <map>
#include <osg/Referenced>
#include <osg/Object>
#include <osg/ref_ptr>
#include <osg/Vec3f>
@ -15,12 +15,15 @@ namespace Resource
{
class BulletShapeInstance;
class BulletShape : public osg::Referenced
class BulletShape : public osg::Object
{
public:
BulletShape();
BulletShape(const BulletShape& copy, const osg::CopyOp& copyop);
virtual ~BulletShape();
META_Object(Resource, BulletShape)
btCollisionShape* mCollisionShape;
// Used for actors. Note, ideally actors would use a separate loader - as it is
@ -35,13 +38,14 @@ namespace Resource
// we store the node's record index mapped to the child index of the shape in the btCompoundShape.
std::map<int, int> mAnimatedShapes;
osg::ref_ptr<BulletShapeInstance> makeInstance();
osg::ref_ptr<BulletShapeInstance> makeInstance() const;
btCollisionShape* duplicateCollisionShape(btCollisionShape* shape) const;
btCollisionShape* duplicateCollisionShape(const btCollisionShape* shape) const;
btCollisionShape* getCollisionShape();
private:
void deleteShape(btCollisionShape* shape);
};
@ -51,10 +55,10 @@ namespace Resource
class BulletShapeInstance : public BulletShape
{
public:
BulletShapeInstance(osg::ref_ptr<BulletShape> source);
BulletShapeInstance(osg::ref_ptr<const BulletShape> source);
private:
osg::ref_ptr<BulletShape> mSource;
osg::ref_ptr<const BulletShape> mSource;
};
// Subclass btBhvTriangleMeshShape to auto-delete the meshInterface

@ -13,7 +13,8 @@
#include "bulletshape.hpp"
#include "scenemanager.hpp"
#include "niffilemanager.hpp"
#include "objectcache.hpp"
#include "multiobjectcache.hpp"
namespace Resource
{
@ -96,7 +97,8 @@ private:
};
BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager)
: mVFS(vfs)
: ResourceManager(vfs)
, mInstanceCache(new MultiObjectCache)
, mSceneManager(sceneMgr)
, mNifFileManager(nifFileManager)
{
@ -108,14 +110,16 @@ BulletShapeManager::~BulletShapeManager()
}
osg::ref_ptr<BulletShapeInstance> BulletShapeManager::createInstance(const std::string &name)
osg::ref_ptr<const BulletShape> BulletShapeManager::getShape(const std::string &name)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
osg::ref_ptr<BulletShape> shape;
Index::iterator it = mIndex.find(normalized);
if (it == mIndex.end())
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);
if (obj)
shape = osg::ref_ptr<BulletShape>(static_cast<BulletShape*>(obj.get()));
else
{
size_t extPos = normalized.find_last_of('.');
std::string ext;
@ -137,16 +141,53 @@ osg::ref_ptr<BulletShapeInstance> BulletShapeManager::createInstance(const std::
node->accept(visitor);
shape = visitor.getShape();
if (!shape)
return osg::ref_ptr<BulletShapeInstance>();
{
mCache->addEntryToObjectCache(normalized, NULL);
return osg::ref_ptr<BulletShape>();
}
}
mIndex[normalized] = shape;
mCache->addEntryToObjectCache(normalized, shape);
}
else
shape = it->second;
return shape;
}
osg::ref_ptr<BulletShapeInstance> instance = shape->makeInstance();
osg::ref_ptr<BulletShapeInstance> BulletShapeManager::cacheInstance(const std::string &name)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
osg::ref_ptr<BulletShapeInstance> instance = createInstance(normalized);
mInstanceCache->addEntryToObjectCache(normalized, instance.get());
return instance;
}
osg::ref_ptr<BulletShapeInstance> BulletShapeManager::getInstance(const std::string &name)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
osg::ref_ptr<osg::Object> obj = mInstanceCache->takeFromObjectCache(normalized);
if (obj.get())
return static_cast<BulletShapeInstance*>(obj.get());
else
return createInstance(normalized);
}
osg::ref_ptr<BulletShapeInstance> BulletShapeManager::createInstance(const std::string &name)
{
osg::ref_ptr<const BulletShape> shape = getShape(name);
if (shape)
return shape->makeInstance();
else
return osg::ref_ptr<BulletShapeInstance>();
}
void BulletShapeManager::updateCache(double referenceTime)
{
ResourceManager::updateCache(referenceTime);
mInstanceCache->removeUnreferencedObjectsInCache();
}
}

@ -7,11 +7,7 @@
#include <osg/ref_ptr>
#include "bulletshape.hpp"
namespace VFS
{
class Manager;
}
#include "resourcemanager.hpp"
namespace Resource
{
@ -21,21 +17,37 @@ namespace Resource
class BulletShape;
class BulletShapeInstance;
class BulletShapeManager
class MultiObjectCache;
/// Handles loading, caching and "instancing" of bullet shapes.
/// A shape 'instance' is a clone of another shape, with the goal of setting a different scale on this instance.
/// @note May be used from any thread.
class BulletShapeManager : public ResourceManager
{
public:
BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager);
~BulletShapeManager();
osg::ref_ptr<BulletShapeInstance> createInstance(const std::string& name);
/// @note May return a null pointer if the object has no shape.
osg::ref_ptr<const BulletShape> getShape(const std::string& name);
/// Create an instance of the given shape and cache it for later use, so that future calls to getInstance() can simply return
/// the cached instance instead of having to create a new one.
/// @note The returned ref_ptr may be kept by the caller to ensure that the instance stays in cache for as long as needed.
osg::ref_ptr<BulletShapeInstance> cacheInstance(const std::string& name);
/// @note May return a null pointer if the object has no shape.
osg::ref_ptr<BulletShapeInstance> getInstance(const std::string& name);
/// @see ResourceManager::updateCache
virtual void updateCache(double referenceTime);
private:
const VFS::Manager* mVFS;
osg::ref_ptr<BulletShapeInstance> createInstance(const std::string& name);
osg::ref_ptr<MultiObjectCache> mInstanceCache;
SceneManager* mSceneManager;
NifFileManager* mNifFileManager;
typedef std::map<std::string, osg::ref_ptr<BulletShape> > Index;
Index mIndex;
};
}

@ -0,0 +1,148 @@
#include "imagemanager.hpp"
#include <osgDB/Registry>
#include <osg/GLExtensions>
#include <osg/Version>
#include <components/vfs/manager.hpp>
#include "objectcache.hpp"
#ifdef OSG_LIBRARY_STATIC
// This list of plugins should match with the list in the top-level CMakelists.txt.
USE_OSGPLUGIN(png)
USE_OSGPLUGIN(tga)
USE_OSGPLUGIN(dds)
USE_OSGPLUGIN(jpeg)
#endif
namespace
{
osg::ref_ptr<osg::Image> createWarningImage()
{
osg::ref_ptr<osg::Image> warningImage = new osg::Image;
int width = 8, height = 8;
warningImage->allocateImage(width, height, 1, GL_RGB, GL_UNSIGNED_BYTE);
assert (warningImage->isDataContiguous());
unsigned char* data = warningImage->data();
for (int i=0;i<width*height;++i)
{
data[3*i] = (255);
data[3*i+1] = (0);
data[3*i+2] = (255);
}
return warningImage;
}
}
namespace Resource
{
ImageManager::ImageManager(const VFS::Manager *vfs)
: ResourceManager(vfs)
, mWarningImage(createWarningImage())
, mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba"))
{
}
ImageManager::~ImageManager()
{
}
bool checkSupported(osg::Image* image, const std::string& filename)
{
switch(image->getPixelFormat())
{
case(GL_COMPRESSED_RGB_S3TC_DXT1_EXT):
case(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT):
case(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT):
case(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT):
{
#if OSG_VERSION_GREATER_OR_EQUAL(3,3,3)
osg::GLExtensions* exts = osg::GLExtensions::Get(0, false);
if (exts && !exts->isTextureCompressionS3TCSupported
// This one works too. Should it be included in isTextureCompressionS3TCSupported()? Submitted as a patch to OSG.
&& !osg::isGLExtensionSupported(0, "GL_S3_s3tc"))
#else
osg::Texture::Extensions* exts = osg::Texture::getExtensions(0, false);
if (exts && !exts->isTextureCompressionS3TCSupported()
// This one works too. Should it be included in isTextureCompressionS3TCSupported()? Submitted as a patch to OSG.
&& !osg::isGLExtensionSupported(0, "GL_S3_s3tc"))
#endif
{
std::cerr << "Error loading " << filename << ": no S3TC texture compression support installed" << std::endl;
return false;
}
break;
}
// not bothering with checks for other compression formats right now, we are unlikely to ever use those anyway
default:
return true;
}
return true;
}
osg::ref_ptr<osg::Image> ImageManager::getImage(const std::string &filename)
{
std::string normalized = filename;
mVFS->normalizeFilename(normalized);
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);
if (obj)
return osg::ref_ptr<osg::Image>(static_cast<osg::Image*>(obj.get()));
else
{
Files::IStreamPtr stream;
try
{
stream = mVFS->get(normalized.c_str());
}
catch (std::exception& e)
{
std::cerr << "Failed to open image: " << e.what() << std::endl;
mCache->addEntryToObjectCache(normalized, mWarningImage);
return mWarningImage;
}
size_t extPos = normalized.find_last_of('.');
std::string ext;
if (extPos != std::string::npos && extPos+1 < normalized.size())
ext = normalized.substr(extPos+1);
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext);
if (!reader)
{
std::cerr << "Error loading " << filename << ": no readerwriter for '" << ext << "' found" << std::endl;
mCache->addEntryToObjectCache(normalized, mWarningImage);
return mWarningImage;
}
osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, mOptions);
if (!result.success())
{
std::cerr << "Error loading " << filename << ": " << result.message() << " code " << result.status() << std::endl;
mCache->addEntryToObjectCache(normalized, mWarningImage);
return mWarningImage;
}
osg::Image* image = result.getImage();
if (!checkSupported(image, filename))
{
mCache->addEntryToObjectCache(normalized, mWarningImage);
return mWarningImage;
}
mCache->addEntryToObjectCache(normalized, image);
return image;
}
}
osg::Image *ImageManager::getWarningImage()
{
return mWarningImage;
}
}

@ -0,0 +1,50 @@
#ifndef OPENMW_COMPONENTS_RESOURCE_IMAGEMANAGER_H
#define OPENMW_COMPONENTS_RESOURCE_IMAGEMANAGER_H
#include <string>
#include <map>
#include <osg/ref_ptr>
#include <osg/Image>
#include <osg/Texture2D>
#include "resourcemanager.hpp"
namespace osgViewer
{
class Viewer;
}
namespace osgDB
{
class Options;
}
namespace Resource
{
/// @brief Handles loading/caching of Images.
/// @note May be used from any thread.
class ImageManager : public ResourceManager
{
public:
ImageManager(const VFS::Manager* vfs);
~ImageManager();
/// Create or retrieve an Image
/// Returns the dummy image if the given image is not found.
osg::ref_ptr<osg::Image> getImage(const std::string& filename);
osg::Image* getWarningImage();
private:
osg::ref_ptr<osg::Image> mWarningImage;
osg::ref_ptr<osgDB::Options> mOptions;
ImageManager(const ImageManager&);
void operator = (const ImageManager&);
};
}
#endif

@ -9,8 +9,7 @@ namespace Resource
{
KeyframeManager::KeyframeManager(const VFS::Manager* vfs)
: mCache(new osgDB::ObjectCache)
, mVFS(vfs)
: ResourceManager(vfs)
{
}

@ -4,15 +4,7 @@
#include <osg/ref_ptr>
#include <string>
namespace VFS
{
class Manager;
}
namespace osgDB
{
class ObjectCache;
}
#include "resourcemanager.hpp"
namespace NifOsg
{
@ -23,23 +15,16 @@ namespace Resource
{
/// @brief Managing of keyframe resources
class KeyframeManager
/// @note May be used from any thread.
class KeyframeManager : public ResourceManager
{
public:
KeyframeManager(const VFS::Manager* vfs);
~KeyframeManager();
void clearCache();
/// Retrieve a read-only keyframe resource by name (case-insensitive).
/// @note This method is safe to call from any thread.
/// @note Throws an exception if the resource is not found.
osg::ref_ptr<const NifOsg::KeyframeHolder> get(const std::string& name);
private:
osg::ref_ptr<osgDB::ObjectCache> mCache;
const VFS::Manager* mVFS;
};
}

@ -0,0 +1,79 @@
#include "multiobjectcache.hpp"
#include <vector>
#include <osg/Object>
namespace Resource
{
MultiObjectCache::MultiObjectCache()
{
}
MultiObjectCache::~MultiObjectCache()
{
}
void MultiObjectCache::removeUnreferencedObjectsInCache()
{
std::vector<osg::ref_ptr<osg::Object> > objectsToRemove;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
// Remove unreferenced entries from object cache
ObjectCacheMap::iterator oitr = _objectCache.begin();
while(oitr != _objectCache.end())
{
if (oitr->second->referenceCount() <= 1)
{
objectsToRemove.push_back(oitr->second);
_objectCache.erase(oitr++);
}
else
{
++oitr;
}
}
}
// note, actual unref happens outside of the lock
objectsToRemove.clear();
}
void MultiObjectCache::addEntryToObjectCache(const std::string &filename, osg::Object *object)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
_objectCache.insert(std::make_pair(filename, object));
}
osg::ref_ptr<osg::Object> MultiObjectCache::takeFromObjectCache(const std::string &fileName)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
ObjectCacheMap::iterator found = _objectCache.find(fileName);
if (found == _objectCache.end())
return osg::ref_ptr<osg::Object>();
else
{
osg::ref_ptr<osg::Object> object = found->second;
_objectCache.erase(found);
return object;
}
}
void MultiObjectCache::releaseGLObjects(osg::State *state)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
for(ObjectCacheMap::iterator itr = _objectCache.begin();
itr != _objectCache.end();
++itr)
{
osg::Object* object = itr->second.get();
object->releaseGLObjects(state);
}
}
}

@ -0,0 +1,47 @@
#ifndef OPENMW_COMPONENTS_MULTIOBJECTCACHE_H
#define OPENMW_COMPONENTS_MULTIOBJECTCACHE_H
#include <map>
#include <string>
#include <osg/ref_ptr>
#include <osg/Referenced>
namespace osg
{
class Object;
class State;
}
namespace Resource
{
/// @brief Cache for "non reusable" objects.
class MultiObjectCache : public osg::Referenced
{
public:
MultiObjectCache();
~MultiObjectCache();
void removeUnreferencedObjectsInCache();
void addEntryToObjectCache(const std::string& filename, osg::Object* object);
/** Take an Object from cache. Return NULL if no object found. */
osg::ref_ptr<osg::Object> takeFromObjectCache(const std::string& fileName);
/** call releaseGLObjects on all objects attached to the object cache.*/
void releaseGLObjects(osg::State* state);
protected:
typedef std::multimap<std::string, osg::ref_ptr<osg::Object> > ObjectCacheMap;
ObjectCacheMap _objectCache;
OpenThreads::Mutex _objectCacheMutex;
};
}
#endif

@ -1,5 +1,7 @@
#include "niffilemanager.hpp"
#include <osg/Object>
#include <components/vfs/manager.hpp>
#include "objectcache.hpp"
@ -29,9 +31,8 @@ namespace Resource
};
NifFileManager::NifFileManager(const VFS::Manager *vfs)
: mVFS(vfs)
: ResourceManager(vfs)
{
mCache = new osgDB::ObjectCache;
}
NifFileManager::~NifFileManager()
@ -39,12 +40,6 @@ namespace Resource
}
void NifFileManager::clearCache()
{
// NIF files aren't needed any more when the converted objects are cached in SceneManager / BulletShapeManager,
// so we'll simply drop all nif files here, unlikely to need them again
mCache->clear();
}
Nif::NIFFilePtr NifFileManager::get(const std::string &name)
{

@ -5,39 +5,23 @@
#include <components/nif/niffile.hpp>
namespace VFS
{
class Manager;
}
namespace osgDB
{
class ObjectCache;
}
#include "resourcemanager.hpp"
namespace Resource
{
/// @brief Handles caching of NIFFiles.
/// @note The NifFileManager is completely thread safe.
class NifFileManager
/// @note May be used from any thread.
class NifFileManager : public ResourceManager
{
public:
NifFileManager(const VFS::Manager* vfs);
~NifFileManager();
void clearCache();
/// Retrieve a NIF file from the cache, or load it from the VFS if not cached yet.
/// @note For performance reasons the NifFileManager does not handle case folding, needs
/// to be done in advance by other managers accessing the NifFileManager.
Nif::NIFFilePtr get(const std::string& name);
private:
// Use the osgDB::ObjectCache so objects are retrieved in thread safe way
osg::ref_ptr<osgDB::ObjectCache> mCache;
const VFS::Manager* mVFS;
};
}

@ -11,13 +11,12 @@
* OpenSceneGraph Public License for more details.
*/
#include <osg/Version>
#if OSG_VERSION_LESS_THAN(3,3,3)
#include "objectcache.hpp"
using namespace osgDB;
#include <osg/Object>
namespace Resource
{
////////////////////////////////////////////////////////////////////////////////////////////
//
@ -26,50 +25,24 @@ using namespace osgDB;
ObjectCache::ObjectCache():
osg::Referenced(true)
{
// OSG_NOTICE<<"Constructed ObjectCache"<<std::endl;
}
ObjectCache::~ObjectCache()
{
// OSG_NOTICE<<"Destructed ObjectCache"<<std::endl;
}
void ObjectCache::addObjectCache(ObjectCache* objectCache)
{
// don't allow a cache to be added to itself.
if (objectCache==this) return;
// lock both ObjectCache to prevent their contents from being modified by other threads while we merge.
OpenThreads::ScopedLock<OpenThreads::Mutex> lock1(_objectCacheMutex);
OpenThreads::ScopedLock<OpenThreads::Mutex> lock2(objectCache->_objectCacheMutex);
// OSG_NOTICE<<"Inserting objects to main ObjectCache "<<objectCache->_objectCache.size()<<std::endl;
_objectCache.insert(objectCache->_objectCache.begin(), objectCache->_objectCache.end());
}
void ObjectCache::addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
_objectCache[filename]=ObjectTimeStampPair(object,timestamp);
}
osg::Object* ObjectCache::getFromObjectCache(const std::string& fileName)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
ObjectCacheMap::iterator itr = _objectCache.find(fileName);
if (itr!=_objectCache.end()) return itr->second.first.get();
else return 0;
}
osg::ref_ptr<osg::Object> ObjectCache::getRefFromObjectCache(const std::string& fileName)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
ObjectCacheMap::iterator itr = _objectCache.find(fileName);
if (itr!=_objectCache.end())
{
// OSG_NOTICE<<"Found "<<fileName<<" in ObjectCache "<<this<<std::endl;
return itr->second.first;
}
else return 0;
@ -95,21 +68,29 @@ void ObjectCache::updateTimeStampOfObjectsInCacheWithExternalReferences(double r
void ObjectCache::removeExpiredObjectsInCache(double expiryTime)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
std::vector<osg::ref_ptr<osg::Object> > objectsToRemove;
// Remove expired entries from object cache
ObjectCacheMap::iterator oitr = _objectCache.begin();
while(oitr != _objectCache.end())
{
if (oitr->second.second<=expiryTime)
{
_objectCache.erase(oitr++);
}
else
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
// Remove expired entries from object cache
ObjectCacheMap::iterator oitr = _objectCache.begin();
while(oitr != _objectCache.end())
{
++oitr;
if (oitr->second.second<=expiryTime)
{
objectsToRemove.push_back(oitr->second.first);
_objectCache.erase(oitr++);
}
else
{
++oitr;
}
}
}
// note, actual unref happens outside of the lock
objectsToRemove.clear();
}
void ObjectCache::removeFromObjectCache(const std::string& fileName)
@ -138,4 +119,4 @@ void ObjectCache::releaseGLObjects(osg::State* state)
}
}
#endif
}

@ -1,3 +1,6 @@
// Resource ObjectCache for OpenMW, forked from osgDB ObjectCache by Robert Osfield, see copyright notice below.
// The main change from the upstream version is that removeExpiredObjectsInCache no longer keeps a lock while the unref happens.
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
@ -11,28 +14,24 @@
* OpenSceneGraph Public License for more details.
*/
// Wrapper for osgDB/ObjectCache. Works around ObjectCache not being available in old OSG 3.2.
// Use "#include objectcache.hpp" in place of "#include <osgDB/ObjectCache".
#ifndef OSGDB_OBJECTCACHE_WRAPPER
#define OSGDB_OBJECTCACHE_WRAPPER 1
#include <osg/Version>
#ifndef OPENMW_COMPONENTS_RESOURCE_OBJECTCACHE
#define OPENMW_COMPONENTS_RESOURCE_OBJECTCACHE
#if OSG_VERSION_GREATER_OR_EQUAL(3,3,3)
#include <osgDB/ObjectCache>
#else
#include <osg/Node>
#include <osgDB/ReaderWriter>
#include <osgDB/DatabaseRevisions>
#include <osg/Referenced>
#include <osg/ref_ptr>
#include <string>
#include <map>
namespace osgDB {
namespace osg
{
class Object;
class State;
}
namespace Resource {
class /*OSGDB_EXPORT*/ ObjectCache : public osg::Referenced
class ObjectCache : public osg::Referenced
{
public:
@ -55,22 +54,16 @@ class /*OSGDB_EXPORT*/ ObjectCache : public osg::Referenced
/** Remove all objects in the cache regardless of having external references or expiry times.*/
void clear();
/** Add contents of specified ObjectCache to this object cache.*/
void addObjectCache(ObjectCache* object);
/** Add a filename,object,timestamp triple to the Registry::ObjectCache.*/
void addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp = 0.0);
/** Remove Object from cache.*/
void removeFromObjectCache(const std::string& fileName);
/** Get an Object from the object cache*/
osg::Object* getFromObjectCache(const std::string& fileName);
/** Get an ref_ptr<Object> from the object cache*/
osg::ref_ptr<osg::Object> getRefFromObjectCache(const std::string& fileName);
/** call rleaseGLObjects on all objects attached to the object cache.*/
/** call releaseGLObjects on all objects attached to the object cache.*/
void releaseGLObjects(osg::State* state);
protected:
@ -88,5 +81,3 @@ class /*OSGDB_EXPORT*/ ObjectCache : public osg::Referenced
}
#endif
#endif

@ -0,0 +1,32 @@
#include "resourcemanager.hpp"
#include "objectcache.hpp"
namespace Resource
{
ResourceManager::ResourceManager(const VFS::Manager *vfs)
: mVFS(vfs)
, mCache(new Resource::ObjectCache)
, mExpiryDelay(0.0)
{
}
void ResourceManager::updateCache(double referenceTime)
{
mCache->updateTimeStampOfObjectsInCacheWithExternalReferences(referenceTime);
mCache->removeExpiredObjectsInCache(referenceTime - mExpiryDelay);
}
void ResourceManager::setExpiryDelay(double expiryDelay)
{
mExpiryDelay = expiryDelay;
}
const VFS::Manager* ResourceManager::getVFS() const
{
return mVFS;
}
}

@ -0,0 +1,38 @@
#ifndef OPENMW_COMPONENTS_RESOURCE_MANAGER_H
#define OPENMW_COMPONENTS_RESOURCE_MANAGER_H
#include <osg/ref_ptr>
namespace VFS
{
class Manager;
}
namespace Resource
{
class ObjectCache;
/// @brief Base class for managers that require a virtual file system and object cache.
/// @par This base class implements clearing of the cache, but populating it and what it's used for is up to the individual sub classes.
class ResourceManager
{
public:
ResourceManager(const VFS::Manager* vfs);
/// Clear cache entries that have not been referenced for longer than expiryDelay.
virtual void updateCache(double referenceTime);
/// How long to keep objects in cache after no longer being referenced.
void setExpiryDelay (double expiryDelay);
const VFS::Manager* getVFS() const;
protected:
const VFS::Manager* mVFS;
osg::ref_ptr<Resource::ObjectCache> mCache;
double mExpiryDelay;
};
}
#endif

@ -1,7 +1,7 @@
#include "resourcesystem.hpp"
#include "scenemanager.hpp"
#include "texturemanager.hpp"
#include "imagemanager.hpp"
#include "niffilemanager.hpp"
#include "keyframemanager.hpp"
@ -13,13 +13,23 @@ namespace Resource
{
mNifFileManager.reset(new NifFileManager(vfs));
mKeyframeManager.reset(new KeyframeManager(vfs));
mTextureManager.reset(new TextureManager(vfs));
mSceneManager.reset(new SceneManager(vfs, mTextureManager.get(), mNifFileManager.get()));
mImageManager.reset(new ImageManager(vfs));
mSceneManager.reset(new SceneManager(vfs, mImageManager.get(), mNifFileManager.get()));
addResourceManager(mNifFileManager.get());
addResourceManager(mKeyframeManager.get());
// note, scene references images so add images afterwards for correct implementation of updateCache()
addResourceManager(mSceneManager.get());
addResourceManager(mImageManager.get());
}
ResourceSystem::~ResourceSystem()
{
// this has to be defined in the .cpp file as we can't delete incomplete types
mResourceManagers.clear();
// no delete, all handled by auto_ptr
}
SceneManager* ResourceSystem::getSceneManager()
@ -27,9 +37,9 @@ namespace Resource
return mSceneManager.get();
}
TextureManager* ResourceSystem::getTextureManager()
ImageManager* ResourceSystem::getImageManager()
{
return mTextureManager.get();
return mImageManager.get();
}
NifFileManager* ResourceSystem::getNifFileManager()
@ -42,9 +52,32 @@ namespace Resource
return mKeyframeManager.get();
}
void ResourceSystem::clearCache()
void ResourceSystem::setExpiryDelay(double expiryDelay)
{
for (std::vector<ResourceManager*>::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it)
(*it)->setExpiryDelay(expiryDelay);
// NIF files aren't needed any more once the converted objects are cached in SceneManager / BulletShapeManager,
// so no point in using an expiry delay
mNifFileManager->setExpiryDelay(0.0);
}
void ResourceSystem::updateCache(double referenceTime)
{
for (std::vector<ResourceManager*>::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it)
(*it)->updateCache(referenceTime);
}
void ResourceSystem::addResourceManager(ResourceManager *resourceMgr)
{
mResourceManagers.push_back(resourceMgr);
}
void ResourceSystem::removeResourceManager(ResourceManager *resourceMgr)
{
mNifFileManager->clearCache();
std::vector<ResourceManager*>::iterator found = std::find(mResourceManagers.begin(), mResourceManagers.end(), resourceMgr);
if (found != mResourceManagers.end())
mResourceManagers.erase(found);
}
const VFS::Manager* ResourceSystem::getVFS() const

@ -2,6 +2,7 @@
#define OPENMW_COMPONENTS_RESOURCE_RESOURCESYSTEM_H
#include <memory>
#include <vector>
namespace VFS
{
@ -12,9 +13,10 @@ namespace Resource
{
class SceneManager;
class TextureManager;
class ImageManager;
class NifFileManager;
class KeyframeManager;
class ResourceManager;
/// @brief Wrapper class that constructs and provides access to the most commonly used resource subsystems.
/// @par Resource subsystems can be used with multiple OpenGL contexts, just like the OSG equivalents, but
@ -26,21 +28,37 @@ namespace Resource
~ResourceSystem();
SceneManager* getSceneManager();
TextureManager* getTextureManager();
ImageManager* getImageManager();
NifFileManager* getNifFileManager();
KeyframeManager* getKeyframeManager();
/// Indicates to each resource manager to clear the cache, i.e. to drop cached objects that are no longer referenced.
void clearCache();
/// @note May be called from any thread if you do not add or remove resource managers at that point.
void updateCache(double referenceTime);
/// Add this ResourceManager to be handled by the ResourceSystem.
/// @note Does not transfer ownership.
void addResourceManager(ResourceManager* resourceMgr);
/// @note Do nothing if resourceMgr does not exist.
/// @note Does not delete resourceMgr.
void removeResourceManager(ResourceManager* resourceMgr);
/// How long to keep objects in cache after no longer being referenced.
void setExpiryDelay(double expiryDelay);
/// @note May be called from any thread.
const VFS::Manager* getVFS() const;
private:
std::auto_ptr<SceneManager> mSceneManager;
std::auto_ptr<TextureManager> mTextureManager;
std::auto_ptr<ImageManager> mImageManager;
std::auto_ptr<NifFileManager> mNifFileManager;
std::auto_ptr<KeyframeManager> mKeyframeManager;
// Store the base classes separately to get convenient access to the common interface
// Here users can register their own resourcemanager as well
std::vector<ResourceManager*> mResourceManagers;
const VFS::Manager* mVFS;
ResourceSystem(const ResourceSystem&);

@ -1,14 +1,18 @@
#include "scenemanager.hpp"
#include <iostream>
#include <osg/Node>
#include <osg/Geode>
#include <osg/UserDataContainer>
#include <osg/Version>
#include <osgParticle/ParticleSystem>
#include <osgFX/Effect>
#include <osgUtil/IncrementalCompileOperation>
#include <osgViewer/Viewer>
#include <osgDB/SharedStateManager>
#include <osgDB/Registry>
@ -19,13 +23,17 @@
#include <components/sceneutil/clone.hpp>
#include <components/sceneutil/util.hpp>
#include <components/sceneutil/controller.hpp>
#include "texturemanager.hpp"
#include "imagemanager.hpp"
#include "niffilemanager.hpp"
#include "objectcache.hpp"
#include "multiobjectcache.hpp"
namespace
{
/// @todo Do this in updateCallback so that animations are accounted for.
class InitWorldSpaceParticlesVisitor : public osg::NodeVisitor
{
public:
@ -105,10 +113,134 @@ namespace
namespace Resource
{
SceneManager::SceneManager(const VFS::Manager *vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager)
: mVFS(vfs)
, mTextureManager(textureManager)
/// Set texture filtering settings on textures contained in a FlipController.
class SetFilterSettingsControllerVisitor : public SceneUtil::ControllerVisitor
{
public:
SetFilterSettingsControllerVisitor(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy)
: mMinFilter(minFilter)
, mMagFilter(magFilter)
, mMaxAnisotropy(maxAnisotropy)
{
}
virtual void visit(osg::Node& node, SceneUtil::Controller& ctrl)
{
if (NifOsg::FlipController* flipctrl = dynamic_cast<NifOsg::FlipController*>(&ctrl))
{
for (std::vector<osg::ref_ptr<osg::Texture2D> >::iterator it = flipctrl->getTextures().begin(); it != flipctrl->getTextures().end(); ++it)
{
osg::Texture* tex = *it;
tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter);
tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter);
tex->setMaxAnisotropy(mMaxAnisotropy);
}
}
}
private:
osg::Texture::FilterMode mMinFilter;
osg::Texture::FilterMode mMagFilter;
int mMaxAnisotropy;
};
/// Set texture filtering settings on textures contained in StateSets.
class SetFilterSettingsVisitor : public osg::NodeVisitor
{
public:
SetFilterSettingsVisitor(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mMinFilter(minFilter)
, mMagFilter(magFilter)
, mMaxAnisotropy(maxAnisotropy)
{
}
virtual void apply(osg::Node& node)
{
if (osgFX::Effect* effect = dynamic_cast<osgFX::Effect*>(&node))
apply(*effect);
osg::StateSet* stateset = node.getStateSet();
if (stateset)
apply(stateset);
traverse(node);
}
void apply(osgFX::Effect& effect)
{
for (int i =0; i<effect.getNumTechniques(); ++i)
{
osgFX::Technique* tech = effect.getTechnique(i);
for (int pass=0; pass<tech->getNumPasses(); ++pass)
{
if (tech->getPassStateSet(pass))
apply(tech->getPassStateSet(pass));
}
}
}
virtual void apply(osg::Geode& geode)
{
osg::StateSet* stateset = geode.getStateSet();
if (stateset)
apply(stateset);
for (unsigned int i=0; i<geode.getNumDrawables(); ++i)
{
osg::Drawable* drw = geode.getDrawable(i);
stateset = drw->getStateSet();
if (stateset)
apply(stateset);
}
}
void apply(osg::StateSet* stateset)
{
const osg::StateSet::TextureAttributeList& texAttributes = stateset->getTextureAttributeList();
for(unsigned int unit=0;unit<texAttributes.size();++unit)
{
osg::StateAttribute *texture = stateset->getTextureAttribute(unit, osg::StateAttribute::TEXTURE);
if (texture)
apply(texture);
}
}
void apply(osg::StateAttribute* attr)
{
osg::Texture* tex = attr->asTexture();
if (tex)
{
if (tex->getUserDataContainer())
{
const std::vector<std::string>& descriptions = tex->getUserDataContainer()->getDescriptions();
if (std::find(descriptions.begin(), descriptions.end(), "dont_override_filter") != descriptions.end())
return;
}
tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter);
tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter);
tex->setMaxAnisotropy(mMaxAnisotropy);
}
}
private:
osg::Texture::FilterMode mMinFilter;
osg::Texture::FilterMode mMagFilter;
int mMaxAnisotropy;
};
SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager)
: ResourceManager(vfs)
, mInstanceCache(new MultiObjectCache)
, mImageManager(imageManager)
, mNifFileManager(nifFileManager)
, mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR)
, mMagFilter(osg::Texture::LINEAR)
, mMaxAnisotropy(1)
, mUnRefImageDataAfterApply(false)
, mParticleSystemMask(~0u)
{
}
@ -116,15 +248,14 @@ namespace Resource
SceneManager::~SceneManager()
{
// this has to be defined in the .cpp file as we can't delete incomplete types
}
/// @brief Callback to read image files from the VFS.
class ImageReadCallback : public osgDB::ReadFileCallback
{
public:
ImageReadCallback(Resource::TextureManager* textureMgr)
: mTextureManager(textureMgr)
ImageReadCallback(Resource::ImageManager* imageMgr)
: mImageManager(imageMgr)
{
}
@ -132,7 +263,7 @@ namespace Resource
{
try
{
return osgDB::ReaderWriter::ReadResult(mTextureManager->getImage(filename), osgDB::ReaderWriter::ReadResult::FILE_LOADED);
return osgDB::ReaderWriter::ReadResult(mImageManager->getImage(filename), osgDB::ReaderWriter::ReadResult::FILE_LOADED);
}
catch (std::exception& e)
{
@ -141,7 +272,7 @@ namespace Resource
}
private:
Resource::TextureManager* mTextureManager;
Resource::ImageManager* mImageManager;
};
std::string getFileExtension(const std::string& file)
@ -152,11 +283,11 @@ namespace Resource
return std::string();
}
osg::ref_ptr<osg::Node> load (Files::IStreamPtr file, const std::string& normalizedFilename, Resource::TextureManager* textureMgr, Resource::NifFileManager* nifFileManager)
osg::ref_ptr<osg::Node> load (Files::IStreamPtr file, const std::string& normalizedFilename, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager)
{
std::string ext = getFileExtension(normalizedFilename);
if (ext == "nif")
return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), textureMgr);
return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), imageManager);
else
{
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext);
@ -171,7 +302,7 @@ namespace Resource
// Set a ReadFileCallback so that image files referenced in the model are read from our virtual file system instead of the osgDB.
// Note, for some formats (.obj/.mtl) that reference other (non-image) files a findFileCallback would be necessary.
// but findFileCallback does not support virtual files, so we can't implement it.
options->setReadFileCallback(new ImageReadCallback(textureMgr));
options->setReadFileCallback(new ImageReadCallback(imageManager));
osgDB::ReaderWriter::ReadResult result = reader->readNode(*file, options);
if (!result.success())
@ -189,15 +320,17 @@ namespace Resource
std::string normalized = name;
mVFS->normalizeFilename(normalized);
Index::iterator it = mIndex.find(normalized);
if (it == mIndex.end())
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);
if (obj)
return osg::ref_ptr<const osg::Node>(static_cast<osg::Node*>(obj.get()));
else
{
osg::ref_ptr<osg::Node> loaded;
try
{
Files::IStreamPtr file = mVFS->get(normalized);
loaded = load(file, normalized, mTextureManager, mNifFileManager);
loaded = load(file, normalized, mImageManager, mNifFileManager);
}
catch (std::exception& e)
{
@ -210,7 +343,7 @@ namespace Resource
{
std::cerr << "Failed to load '" << name << "': " << e.what() << ", using marker_error." << sMeshTypes[i] << " instead" << std::endl;
Files::IStreamPtr file = mVFS->get(normalized);
loaded = load(file, normalized, mTextureManager, mNifFileManager);
loaded = load(file, normalized, mImageManager, mNifFileManager);
break;
}
}
@ -219,28 +352,62 @@ namespace Resource
throw;
}
// set filtering settings
SetFilterSettingsVisitor setFilterSettingsVisitor(mMinFilter, mMagFilter, mMaxAnisotropy);
loaded->accept(setFilterSettingsVisitor);
SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor(mMinFilter, mMagFilter, mMaxAnisotropy);
loaded->accept(setFilterSettingsControllerVisitor);
// share state
mSharedStateMutex.lock();
osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get());
mSharedStateMutex.unlock();
if (mIncrementalCompileOperation)
mIncrementalCompileOperation->add(loaded);
mIndex[normalized] = loaded;
mCache->addEntryToObjectCache(normalized, loaded);
return loaded;
}
else
return it->second;
}
osg::ref_ptr<osg::Node> SceneManager::createInstance(const std::string &name)
osg::ref_ptr<osg::Node> SceneManager::cacheInstance(const std::string &name)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
osg::ref_ptr<osg::Node> node = createInstance(normalized);
mInstanceCache->addEntryToObjectCache(normalized, node.get());
return node;
}
osg::ref_ptr<osg::Node> SceneManager::createInstance(const std::string& name)
{
osg::ref_ptr<const osg::Node> scene = getTemplate(name);
osg::ref_ptr<osg::Node> cloned = osg::clone(scene.get(), SceneUtil::CopyOp());
// add a ref to the original template, to hint to the cache that it's still being used and should be kept in cache
cloned->getOrCreateUserDataContainer()->addUserObject(const_cast<osg::Node*>(scene.get()));
return cloned;
}
osg::ref_ptr<osg::Node> SceneManager::createInstance(const std::string &name, osg::Group* parentNode)
osg::ref_ptr<osg::Node> SceneManager::getInstance(const std::string &name)
{
osg::ref_ptr<osg::Node> cloned = createInstance(name);
std::string normalized = name;
mVFS->normalizeFilename(normalized);
osg::ref_ptr<osg::Object> obj = mInstanceCache->takeFromObjectCache(normalized);
if (obj.get())
return static_cast<osg::Node*>(obj.get());
return createInstance(normalized);
}
osg::ref_ptr<osg::Node> SceneManager::getInstance(const std::string &name, osg::Group* parentNode)
{
osg::ref_ptr<osg::Node> cloned = getInstance(name);
attachTo(cloned, parentNode);
return cloned;
}
@ -253,10 +420,7 @@ namespace Resource
void SceneManager::releaseGLObjects(osg::State *state)
{
for (Index::iterator it = mIndex.begin(); it != mIndex.end(); ++it)
{
it->second->releaseGLObjects(state);
}
mCache->releaseGLObjects(state);
}
void SceneManager::setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation *ico)
@ -270,19 +434,86 @@ namespace Resource
node->accept(visitor);
}
const VFS::Manager* SceneManager::getVFS() const
Resource::ImageManager* SceneManager::getImageManager()
{
return mVFS;
return mImageManager;
}
Resource::TextureManager* SceneManager::getTextureManager()
void SceneManager::setParticleSystemMask(unsigned int mask)
{
return mTextureManager;
mParticleSystemMask = mask;
}
void SceneManager::setParticleSystemMask(unsigned int mask)
void SceneManager::setFilterSettings(const std::string &magfilter, const std::string &minfilter,
const std::string &mipmap, int maxAnisotropy,
osgViewer::Viewer *viewer)
{
mParticleSystemMask = mask;
osg::Texture::FilterMode min = osg::Texture::LINEAR;
osg::Texture::FilterMode mag = osg::Texture::LINEAR;
if(magfilter == "nearest")
mag = osg::Texture::NEAREST;
else if(magfilter != "linear")
std::cerr<< "Invalid texture mag filter: "<<magfilter <<std::endl;
if(minfilter == "nearest")
min = osg::Texture::NEAREST;
else if(minfilter != "linear")
std::cerr<< "Invalid texture min filter: "<<minfilter <<std::endl;
if(mipmap == "nearest")
{
if(min == osg::Texture::NEAREST)
min = osg::Texture::NEAREST_MIPMAP_NEAREST;
else if(min == osg::Texture::LINEAR)
min = osg::Texture::LINEAR_MIPMAP_NEAREST;
}
else if(mipmap != "none")
{
if(mipmap != "linear")
std::cerr<< "Invalid texture mipmap: "<<mipmap <<std::endl;
if(min == osg::Texture::NEAREST)
min = osg::Texture::NEAREST_MIPMAP_LINEAR;
else if(min == osg::Texture::LINEAR)
min = osg::Texture::LINEAR_MIPMAP_LINEAR;
}
if(viewer) viewer->stopThreading();
mMinFilter = min;
mMagFilter = mag;
mMaxAnisotropy = std::max(1, maxAnisotropy);
mCache->clear();
SetFilterSettingsControllerVisitor setFilterSettingsControllerVisitor (mMinFilter, mMagFilter, mMaxAnisotropy);
SetFilterSettingsVisitor setFilterSettingsVisitor (mMinFilter, mMagFilter, mMaxAnisotropy);
if (viewer && viewer->getSceneData())
{
viewer->getSceneData()->accept(setFilterSettingsControllerVisitor);
viewer->getSceneData()->accept(setFilterSettingsVisitor);
}
if(viewer) viewer->startThreading();
}
void SceneManager::applyFilterSettings(osg::Texture *tex)
{
tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter);
tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter);
tex->setMaxAnisotropy(mMaxAnisotropy);
}
void SceneManager::setUnRefImageDataAfterApply(bool unref)
{
mUnRefImageDataAfterApply = unref;
}
void SceneManager::updateCache(double referenceTime)
{
ResourceManager::updateCache(referenceTime);
mInstanceCache->removeUnreferencedObjectsInCache();
}
}

@ -6,50 +6,66 @@
#include <osg/ref_ptr>
#include <osg/Node>
#include <osg/Texture>
#include "resourcemanager.hpp"
namespace Resource
{
class TextureManager;
class ImageManager;
class NifFileManager;
}
namespace VFS
namespace osgUtil
{
class Manager;
class IncrementalCompileOperation;
}
namespace osgUtil
namespace osgViewer
{
class IncrementalCompileOperation;
class Viewer;
}
namespace Resource
{
/// @brief Handles loading and caching of scenes, e.g. NIF files
class SceneManager
class MultiObjectCache;
/// @brief Handles loading and caching of scenes, e.g. .nif files or .osg files
/// @note Some methods of the scene manager can be used from any thread, see the methods documentation for more details.
class SceneManager : public ResourceManager
{
public:
SceneManager(const VFS::Manager* vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager);
SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager);
~SceneManager();
/// Get a read-only copy of this scene "template"
/// @note If the given filename does not exist or fails to load, an error marker mesh will be used instead.
/// If even the error marker mesh can not be found, an exception is thrown.
/// @note Thread safe.
osg::ref_ptr<const osg::Node> getTemplate(const std::string& name);
/// Create an instance of the given scene template
/// Create an instance of the given scene template and cache it for later use, so that future calls to getInstance() can simply
/// return this cached object instead of creating a new one.
/// @note The returned ref_ptr may be kept around by the caller to ensure that the object stays in cache for as long as needed.
/// @note Thread safe.
osg::ref_ptr<osg::Node> cacheInstance(const std::string& name);
/// Get an instance of the given scene template
/// @see getTemplate
osg::ref_ptr<osg::Node> createInstance(const std::string& name);
/// @note Thread safe.
osg::ref_ptr<osg::Node> getInstance(const std::string& name);
/// Create an instance of the given scene template and immediately attach it to a parent node
/// Get an instance of the given scene template and immediately attach it to a parent node
/// @see getTemplate
osg::ref_ptr<osg::Node> createInstance(const std::string& name, osg::Group* parentNode);
/// @note Not thread safe, unless parentNode is not part of the main scene graph yet.
osg::ref_ptr<osg::Node> getInstance(const std::string& name, osg::Group* parentNode);
/// Attach the given scene instance to the given parent node
/// @note You should have the parentNode in its intended position before calling this method,
/// so that world space particles of the \a instance get transformed correctly.
/// @note Assumes the given instance was not attached to any parents before.
/// @note Not thread safe, unless parentNode is not part of the main scene graph yet.
void attachTo(osg::Node* instance, osg::Group* parentNode) const;
/// Manually release created OpenGL objects for the given graphics context. This may be required
@ -62,26 +78,47 @@ namespace Resource
/// @note SceneManager::attachTo calls this method automatically, only needs to be called by users if manually attaching
void notifyAttached(osg::Node* node) const;
const VFS::Manager* getVFS() const;
Resource::TextureManager* getTextureManager();
Resource::ImageManager* getImageManager();
/// @param mask The node mask to apply to loaded particle system nodes.
void setParticleSystemMask(unsigned int mask);
/// @param viewer used to apply the new filter settings to the existing scene graph. If there is no scene yet, you can pass a NULL viewer.
void setFilterSettings(const std::string &magfilter, const std::string &minfilter,
const std::string &mipmap, int maxAnisotropy,
osgViewer::Viewer *viewer);
/// Apply filter settings to the given texture. Note, when loading an object through this scene manager (i.e. calling getTemplate or createInstance)
/// the filter settings are applied automatically. This method is provided for textures that were created outside of the SceneManager.
void applyFilterSettings (osg::Texture* tex);
/// Keep a copy of the texture data around in system memory? This is needed when using multiple graphics contexts,
/// otherwise should be disabled to reduce memory usage.
void setUnRefImageDataAfterApply(bool unref);
/// @see ResourceManager::updateCache
virtual void updateCache(double referenceTime);
private:
const VFS::Manager* mVFS;
Resource::TextureManager* mTextureManager;
osg::ref_ptr<osg::Node> createInstance(const std::string& name);
osg::ref_ptr<MultiObjectCache> mInstanceCache;
OpenThreads::Mutex mSharedStateMutex;
Resource::ImageManager* mImageManager;
Resource::NifFileManager* mNifFileManager;
osg::Texture::FilterMode mMinFilter;
osg::Texture::FilterMode mMagFilter;
int mMaxAnisotropy;
bool mUnRefImageDataAfterApply;
osg::ref_ptr<osgUtil::IncrementalCompileOperation> mIncrementalCompileOperation;
unsigned int mParticleSystemMask;
// observer_ptr?
typedef std::map<std::string, osg::ref_ptr<const osg::Node> > Index;
Index mIndex;
SceneManager(const SceneManager&);
void operator = (const SceneManager&);
};

@ -1,314 +0,0 @@
#include "texturemanager.hpp"
#include <osgDB/Registry>
#include <osg/GLExtensions>
#include <osg/Version>
#include <osgViewer/Viewer>
#include <stdexcept>
#include <components/vfs/manager.hpp>
#ifdef OSG_LIBRARY_STATIC
// This list of plugins should match with the list in the top-level CMakelists.txt.
USE_OSGPLUGIN(png)
USE_OSGPLUGIN(tga)
USE_OSGPLUGIN(dds)
USE_OSGPLUGIN(jpeg)
#endif
namespace
{
osg::ref_ptr<osg::Texture2D> createWarningTexture()
{
osg::ref_ptr<osg::Image> warningImage = new osg::Image;
int width = 8, height = 8;
warningImage->allocateImage(width, height, 1, GL_RGB, GL_UNSIGNED_BYTE);
assert (warningImage->isDataContiguous());
unsigned char* data = warningImage->data();
for (int i=0;i<width*height;++i)
{
data[3*i] = (255);
data[3*i+1] = (0);
data[3*i+2] = (255);
}
osg::ref_ptr<osg::Texture2D> warningTexture = new osg::Texture2D;
warningTexture->setImage(warningImage);
return warningTexture;
}
}
namespace Resource
{
TextureManager::TextureManager(const VFS::Manager *vfs)
: mVFS(vfs)
, mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR)
, mMagFilter(osg::Texture::LINEAR)
, mMaxAnisotropy(1)
, mWarningTexture(createWarningTexture())
, mUnRefImageDataAfterApply(false)
{
}
TextureManager::~TextureManager()
{
}
void TextureManager::setUnRefImageDataAfterApply(bool unref)
{
mUnRefImageDataAfterApply = unref;
}
void TextureManager::setFilterSettings(const std::string &magfilter, const std::string &minfilter,
const std::string &mipmap, int maxAnisotropy,
osgViewer::Viewer *viewer)
{
osg::Texture::FilterMode min = osg::Texture::LINEAR;
osg::Texture::FilterMode mag = osg::Texture::LINEAR;
if(magfilter == "nearest")
mag = osg::Texture::NEAREST;
else if(magfilter != "linear")
std::cerr<< "Invalid texture mag filter: "<<magfilter <<std::endl;
if(minfilter == "nearest")
min = osg::Texture::NEAREST;
else if(minfilter != "linear")
std::cerr<< "Invalid texture min filter: "<<minfilter <<std::endl;
if(mipmap == "nearest")
{
if(min == osg::Texture::NEAREST)
min = osg::Texture::NEAREST_MIPMAP_NEAREST;
else if(min == osg::Texture::LINEAR)
min = osg::Texture::LINEAR_MIPMAP_NEAREST;
}
else if(mipmap != "none")
{
if(mipmap != "linear")
std::cerr<< "Invalid texture mipmap: "<<mipmap <<std::endl;
if(min == osg::Texture::NEAREST)
min = osg::Texture::NEAREST_MIPMAP_LINEAR;
else if(min == osg::Texture::LINEAR)
min = osg::Texture::LINEAR_MIPMAP_LINEAR;
}
if(viewer) viewer->stopThreading();
setFilterSettings(min, mag, maxAnisotropy);
if(viewer) viewer->startThreading();
}
void TextureManager::setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode magFilter, int maxAnisotropy)
{
mMinFilter = minFilter;
mMagFilter = magFilter;
mMaxAnisotropy = std::max(1, maxAnisotropy);
for (std::map<MapKey, osg::ref_ptr<osg::Texture2D> >::iterator it = mTextures.begin(); it != mTextures.end(); ++it)
{
osg::ref_ptr<osg::Texture2D> tex = it->second;
// Keep mip-mapping disabled if the texture creator explicitely requested no mipmapping.
osg::Texture::FilterMode oldMin = tex->getFilter(osg::Texture::MIN_FILTER);
if (oldMin == osg::Texture::LINEAR || oldMin == osg::Texture::NEAREST)
{
osg::Texture::FilterMode newMin = osg::Texture::LINEAR;
switch (mMinFilter)
{
case osg::Texture::LINEAR:
case osg::Texture::LINEAR_MIPMAP_LINEAR:
case osg::Texture::LINEAR_MIPMAP_NEAREST:
newMin = osg::Texture::LINEAR;
break;
case osg::Texture::NEAREST:
case osg::Texture::NEAREST_MIPMAP_LINEAR:
case osg::Texture::NEAREST_MIPMAP_NEAREST:
newMin = osg::Texture::NEAREST;
break;
}
tex->setFilter(osg::Texture::MIN_FILTER, newMin);
}
else
tex->setFilter(osg::Texture::MIN_FILTER, mMinFilter);
tex->setFilter(osg::Texture::MAG_FILTER, mMagFilter);
tex->setMaxAnisotropy(static_cast<float>(mMaxAnisotropy));
}
}
/*
osg::ref_ptr<osg::Image> TextureManager::getImage(const std::string &filename)
{
}
*/
bool checkSupported(osg::Image* image, const std::string& filename)
{
switch(image->getPixelFormat())
{
case(GL_COMPRESSED_RGB_S3TC_DXT1_EXT):
case(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT):
case(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT):
case(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT):
{
#if OSG_VERSION_GREATER_OR_EQUAL(3,3,3)
osg::GLExtensions* exts = osg::GLExtensions::Get(0, false);
if (exts && !exts->isTextureCompressionS3TCSupported
// This one works too. Should it be included in isTextureCompressionS3TCSupported()? Submitted as a patch to OSG.
&& !osg::isGLExtensionSupported(0, "GL_S3_s3tc"))
#else
osg::Texture::Extensions* exts = osg::Texture::getExtensions(0, false);
if (exts && !exts->isTextureCompressionS3TCSupported()
// This one works too. Should it be included in isTextureCompressionS3TCSupported()? Submitted as a patch to OSG.
&& !osg::isGLExtensionSupported(0, "GL_S3_s3tc"))
#endif
{
std::cerr << "Error loading " << filename << ": no S3TC texture compression support installed" << std::endl;
return false;
}
break;
}
// not bothering with checks for other compression formats right now, we are unlikely to ever use those anyway
default:
return true;
}
return true;
}
osg::ref_ptr<osg::Image> TextureManager::getImage(const std::string &filename)
{
std::string normalized = filename;
mVFS->normalizeFilename(normalized);
std::map<std::string, osg::ref_ptr<osg::Image> >::iterator found = mImages.find(normalized);
if (found != mImages.end())
return found->second;
else
{
Files::IStreamPtr stream;
try
{
stream = mVFS->get(normalized.c_str());
}
catch (std::exception& e)
{
std::cerr << "Failed to open image: " << e.what() << std::endl;
return NULL;
}
osg::ref_ptr<osgDB::Options> opts (new osgDB::Options);
opts->setOptionString("dds_dxt1_detect_rgba"); // tx_creature_werewolf.dds isn't loading in the correct format without this option
size_t extPos = normalized.find_last_of('.');
std::string ext;
if (extPos != std::string::npos && extPos+1 < normalized.size())
ext = normalized.substr(extPos+1);
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext);
if (!reader)
{
std::cerr << "Error loading " << filename << ": no readerwriter for '" << ext << "' found" << std::endl;
return NULL;
}
osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, opts);
if (!result.success())
{
std::cerr << "Error loading " << filename << ": " << result.message() << " code " << result.status() << std::endl;
return NULL;
}
osg::Image* image = result.getImage();
if (!checkSupported(image, filename))
{
return NULL;
}
mImages.insert(std::make_pair(normalized, image));
return image;
}
}
osg::ref_ptr<osg::Texture2D> TextureManager::getTexture2D(const std::string &filename, osg::Texture::WrapMode wrapS, osg::Texture::WrapMode wrapT)
{
std::string normalized = filename;
mVFS->normalizeFilename(normalized);
MapKey key = std::make_pair(std::make_pair(wrapS, wrapT), normalized);
std::map<MapKey, osg::ref_ptr<osg::Texture2D> >::iterator found = mTextures.find(key);
if (found != mTextures.end())
{
return found->second;
}
else
{
Files::IStreamPtr stream;
try
{
stream = mVFS->get(normalized.c_str());
}
catch (std::exception& e)
{
std::cerr << "Failed to open texture: " << e.what() << std::endl;
return mWarningTexture;
}
osg::ref_ptr<osgDB::Options> opts (new osgDB::Options);
opts->setOptionString("dds_dxt1_detect_rgba"); // tx_creature_werewolf.dds isn't loading in the correct format without this option
size_t extPos = normalized.find_last_of('.');
std::string ext;
if (extPos != std::string::npos && extPos+1 < normalized.size())
ext = normalized.substr(extPos+1);
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext);
if (!reader)
{
std::cerr << "Error loading " << filename << ": no readerwriter for '" << ext << "' found" << std::endl;
return mWarningTexture;
}
osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, opts);
if (!result.success())
{
std::cerr << "Error loading " << filename << ": " << result.message() << " code " << result.status() << std::endl;
return mWarningTexture;
}
osg::Image* image = result.getImage();
if (!checkSupported(image, filename))
{
return mWarningTexture;
}
// We need to flip images, because the Morrowind texture coordinates use the DirectX convention (top-left image origin),
// but OpenGL uses bottom left as the image origin.
// For some reason this doesn't concern DDS textures, which are already flipped when loaded.
if (ext != "dds")
{
image->flipVertical();
}
osg::ref_ptr<osg::Texture2D> texture(new osg::Texture2D);
texture->setImage(image);
texture->setWrap(osg::Texture::WRAP_S, wrapS);
texture->setWrap(osg::Texture::WRAP_T, wrapT);
texture->setFilter(osg::Texture::MIN_FILTER, mMinFilter);
texture->setFilter(osg::Texture::MAG_FILTER, mMagFilter);
texture->setMaxAnisotropy(mMaxAnisotropy);
texture->setUnRefImageDataAfterApply(mUnRefImageDataAfterApply);
mTextures.insert(std::make_pair(key, texture));
return texture;
}
}
osg::Texture2D* TextureManager::getWarningTexture()
{
return mWarningTexture.get();
}
}

@ -1,76 +0,0 @@
#ifndef OPENMW_COMPONENTS_RESOURCE_TEXTUREMANAGER_H
#define OPENMW_COMPONENTS_RESOURCE_TEXTUREMANAGER_H
#include <string>
#include <map>
#include <osg/ref_ptr>
#include <osg/Image>
#include <osg/Texture2D>
namespace osgViewer
{
class Viewer;
}
namespace VFS
{
class Manager;
}
namespace Resource
{
/// @brief Handles loading/caching of Images and Texture StateAttributes.
class TextureManager
{
public:
TextureManager(const VFS::Manager* vfs);
~TextureManager();
void setFilterSettings(const std::string &magfilter, const std::string &minfilter,
const std::string &mipmap, int maxAnisotropy,
osgViewer::Viewer *view);
/// Keep a copy of the texture data around in system memory? This is needed when using multiple graphics contexts,
/// otherwise should be disabled to reduce memory usage.
void setUnRefImageDataAfterApply(bool unref);
/// Create or retrieve a Texture2D using the specified image filename, and wrap parameters.
/// Returns the dummy texture if the given texture is not found.
osg::ref_ptr<osg::Texture2D> getTexture2D(const std::string& filename, osg::Texture::WrapMode wrapS, osg::Texture::WrapMode wrapT);
/// Create or retrieve an Image
osg::ref_ptr<osg::Image> getImage(const std::string& filename);
const VFS::Manager* getVFS() { return mVFS; }
osg::Texture2D* getWarningTexture();
private:
const VFS::Manager* mVFS;
osg::Texture::FilterMode mMinFilter;
osg::Texture::FilterMode mMagFilter;
int mMaxAnisotropy;
typedef std::pair<std::pair<int, int>, std::string> MapKey;
std::map<std::string, osg::ref_ptr<osg::Image> > mImages;
std::map<MapKey, osg::ref_ptr<osg::Texture2D> > mTextures;
osg::ref_ptr<osg::Texture2D> mWarningTexture;
bool mUnRefImageDataAfterApply;
/// @warning It is unsafe to call this function when a draw thread is using the textures. Call stopThreading() first!
void setFilterSettings(osg::Texture::FilterMode minFilter, osg::Texture::FilterMode maxFilter, int maxAnisotropy);
TextureManager(const TextureManager&);
void operator = (const TextureManager&);
};
}
#endif

@ -0,0 +1,48 @@
#include "unrefqueue.hpp"
#include <deque>
#include <osg/Object>
//#include <osg/Timer>
//#include <iostream>
#include <components/sceneutil/workqueue.hpp>
namespace SceneUtil
{
class UnrefWorkItem : public SceneUtil::WorkItem
{
public:
std::deque<osg::ref_ptr<const osg::Object> > mObjects;
virtual void doWork()
{
//osg::Timer timer;
//size_t objcount = mObjects.size();
mObjects.clear();
//std::cout << "cleared " << objcount << " objects in " << timer.time_m() << std::endl;
}
};
UnrefQueue::UnrefQueue()
{
mWorkItem = new UnrefWorkItem;
}
void UnrefQueue::push(const osg::Object *obj)
{
mWorkItem->mObjects.push_back(obj);
}
void UnrefQueue::flush(SceneUtil::WorkQueue *workQueue)
{
if (mWorkItem->mObjects.empty())
return;
workQueue->addWorkItem(mWorkItem);
mWorkItem = new UnrefWorkItem;
}
}

@ -0,0 +1,37 @@
#ifndef OPENMW_COMPONENTS_UNREFQUEUE_H
#define OPENMW_COMPONENTS_UNREFQUEUE_H
#include <osg/ref_ptr>
#include <osg/Referenced>
namespace osg
{
class Object;
}
namespace SceneUtil
{
class WorkQueue;
class UnrefWorkItem;
/// @brief Handles unreferencing of objects through the WorkQueue. Typical use scenario
/// would be the main thread pushing objects that are no longer needed, and the background thread deleting them.
class UnrefQueue : public osg::Referenced
{
public:
UnrefQueue();
/// Adds an object to the list of objects to be unreferenced. Call from the main thread.
void push(const osg::Object* obj);
/// Adds a WorkItem to the given WorkQueue that will clear the list of objects in a worker thread, thus unreferencing them.
/// Call from the main thread.
void flush(SceneUtil::WorkQueue* workQueue);
private:
osg::ref_ptr<UnrefWorkItem> mWorkItem;
};
}
#endif

@ -1,9 +1,11 @@
#include "workqueue.hpp"
#include <iostream>
namespace SceneUtil
{
void WorkTicket::waitTillDone()
void WorkItem::waitTillDone()
{
if (mDone > 0)
return;
@ -15,7 +17,7 @@ void WorkTicket::waitTillDone()
}
}
void WorkTicket::signalDone()
void WorkItem::signalDone()
{
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
@ -25,23 +27,16 @@ void WorkTicket::signalDone()
}
WorkItem::WorkItem()
: mTicket(new WorkTicket)
{
mTicket->setThreadSafeRefUnref(true);
}
WorkItem::~WorkItem()
{
}
void WorkItem::doWork()
{
mTicket->signalDone();
}
osg::ref_ptr<WorkTicket> WorkItem::getTicket()
bool WorkItem::isDone() const
{
return mTicket;
return (mDone > 0);
}
WorkQueue::WorkQueue(int workerThreads)
@ -60,11 +55,7 @@ WorkQueue::~WorkQueue()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
while (!mQueue.empty())
{
WorkItem* item = mQueue.front();
delete item;
mQueue.pop();
}
mIsReleased = true;
mCondition.broadcast();
}
@ -76,16 +67,20 @@ WorkQueue::~WorkQueue()
}
}
osg::ref_ptr<WorkTicket> WorkQueue::addWorkItem(WorkItem *item)
void WorkQueue::addWorkItem(osg::ref_ptr<WorkItem> item)
{
osg::ref_ptr<WorkTicket> ticket = item->getTicket();
if (item->isDone())
{
std::cerr << "warning, trying to add a work item that is already completed" << std::endl;
return;
}
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
mQueue.push(item);
mCondition.signal();
return ticket;
}
WorkItem *WorkQueue::removeWorkItem()
osg::ref_ptr<WorkItem> WorkQueue::removeWorkItem()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
while (mQueue.empty() && !mIsReleased)
@ -94,7 +89,7 @@ WorkItem *WorkQueue::removeWorkItem()
}
if (mQueue.size())
{
WorkItem* item = mQueue.front();
osg::ref_ptr<WorkItem> item = mQueue.front();
mQueue.pop();
return item;
}
@ -111,11 +106,11 @@ void WorkThread::run()
{
while (true)
{
WorkItem* item = mWorkQueue->removeWorkItem();
osg::ref_ptr<WorkItem> item = mWorkQueue->removeWorkItem();
if (!item)
return;
item->doWork();
delete item;
item->signalDone();
}
}

@ -14,69 +14,52 @@
namespace SceneUtil
{
class WorkTicket : public osg::Referenced
{
public:
void waitTillDone();
void signalDone();
private:
OpenThreads::Atomic mDone;
OpenThreads::Mutex mMutex;
OpenThreads::Condition mCondition;
};
class WorkItem
class WorkItem : public osg::Referenced
{
public:
WorkItem();
virtual ~WorkItem();
/// Override in a derived WorkItem to perform actual work.
/// By default, just signals the ticket that the work is done.
virtual void doWork();
osg::ref_ptr<WorkTicket> getTicket();
protected:
osg::ref_ptr<WorkTicket> mTicket;
};
virtual void doWork() {}
class WorkQueue;
bool isDone() const;
class WorkThread : public OpenThreads::Thread
{
public:
WorkThread(WorkQueue* workQueue);
/// Wait until the work is completed. Usually called from the main thread.
void waitTillDone();
virtual void run();
/// Internal use by the WorkQueue.
void signalDone();
private:
WorkQueue* mWorkQueue;
protected:
OpenThreads::Atomic mDone;
OpenThreads::Mutex mMutex;
OpenThreads::Condition mCondition;
};
class WorkThread;
/// @brief A work queue that users can push work items onto, to be completed by one or more background threads.
class WorkQueue
/// @note Work items will be processed in the order that they were given in, however
/// if multiple work threads are involved then it is possible for a later item to complete before earlier items.
class WorkQueue : public osg::Referenced
{
public:
WorkQueue(int numWorkerThreads=1);
~WorkQueue();
/// Add a new work item to the back of the queue.
/// @par The returned WorkTicket may be used by the caller to wait until the work is complete.
osg::ref_ptr<WorkTicket> addWorkItem(WorkItem* item);
/// @par The work item's waitTillDone() method may be used by the caller to wait until the work is complete.
void addWorkItem(osg::ref_ptr<WorkItem> item);
/// Get the next work item from the front of the queue. If the queue is empty, waits until a new item is added.
/// If the workqueue is in the process of being destroyed, may return NULL.
/// @note The caller must free the returned WorkItem
WorkItem* removeWorkItem();
void runThread();
/// @par Used internally by the WorkThread.
osg::ref_ptr<WorkItem> removeWorkItem();
private:
bool mIsReleased;
std::queue<WorkItem*> mQueue;
std::queue<osg::ref_ptr<WorkItem> > mQueue;
OpenThreads::Mutex mMutex;
OpenThreads::Condition mCondition;
@ -84,6 +67,17 @@ namespace SceneUtil
std::vector<WorkThread*> mThreads;
};
/// Internally used by WorkQueue.
class WorkThread : public OpenThreads::Thread
{
public:
WorkThread(WorkQueue* workQueue);
virtual void run();
private:
WorkQueue* mWorkQueue;
};
}

@ -231,7 +231,7 @@ namespace SDLUtil
return;
}
SDL_Surface* surf = SDLUtil::imageToSurface(decompressed, false);
SDL_Surface* surf = SDLUtil::imageToSurface(decompressed, true);
//set the cursor and store it for later
SDL_Cursor* curs = SDL_CreateColorCursor(surf, hotspot_x, hotspot_y);

@ -2,6 +2,8 @@
#include <cassert>
#include <OpenThreads/ScopedLock>
#include <osg/PrimitiveSet>
#include "defs.hpp"
@ -178,6 +180,7 @@ namespace Terrain
osg::ref_ptr<osg::Vec2Array> BufferCache::getUVBuffer()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mUvBufferMutex);
if (mUvBufferMap.find(mNumVerts) != mUvBufferMap.end())
{
return mUvBufferMap[mNumVerts];
@ -193,7 +196,7 @@ namespace Terrain
for (unsigned int row = 0; row < mNumVerts; ++row)
{
uvs->push_back(osg::Vec2f(col / static_cast<float>(mNumVerts-1),
row / static_cast<float>(mNumVerts-1)));
((mNumVerts-1) - row) / static_cast<float>(mNumVerts-1)));
}
}
@ -206,6 +209,7 @@ namespace Terrain
osg::ref_ptr<osg::DrawElements> BufferCache::getIndexBuffer(unsigned int flags)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mIndexBufferMutex);
unsigned int verts = mNumVerts;
if (mIndexBufferMap.find(flags) != mIndexBufferMap.end())

@ -17,8 +17,10 @@ namespace Terrain
/// @param flags first 4*4 bits are LOD deltas on each edge, respectively (4 bits each)
/// next 4 bits are LOD level of the index buffer (LOD 0 = don't omit any vertices)
/// @note Thread safe.
osg::ref_ptr<osg::DrawElements> getIndexBuffer (unsigned int flags);
/// @note Thread safe.
osg::ref_ptr<osg::Vec2Array> getUVBuffer();
// TODO: add releaseGLObjects() for our vertex/element buffer objects
@ -27,8 +29,10 @@ namespace Terrain
// Index buffers are shared across terrain batches where possible. There is one index buffer for each
// combination of LOD deltas and index buffer LOD we may need.
std::map<int, osg::ref_ptr<osg::DrawElements> > mIndexBufferMap;
OpenThreads::Mutex mIndexBufferMutex;
std::map<int, osg::ref_ptr<osg::Vec2Array> > mUvBufferMap;
OpenThreads::Mutex mUvBufferMutex;
unsigned int mNumVerts;
};

@ -2,11 +2,17 @@
#include <memory>
#include <osg/UserDataContainer>
#include <OpenThreads/ScopedLock>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/texturemanager.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/unrefqueue.hpp>
#include <components/esm/loadland.hpp>
@ -45,13 +51,12 @@ namespace
namespace Terrain
{
TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico,
Storage* storage, int nodeMask)
TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, SceneUtil::UnrefQueue* unrefQueue)
: Terrain::World(parent, resourceSystem, ico, storage, nodeMask)
, mNumSplits(4)
, mKdTreeBuilder(new osg::KdTreeBuilder)
, mCache((storage->getCellVertices()-1)/static_cast<float>(mNumSplits) + 1)
, mUnrefQueue(unrefQueue)
{
mCache = BufferCache((storage->getCellVertices()-1)/static_cast<float>(mNumSplits) + 1);
}
TerrainGrid::~TerrainGrid()
@ -62,11 +67,20 @@ TerrainGrid::~TerrainGrid()
}
}
class GridElement
osg::ref_ptr<osg::Node> TerrainGrid::cacheCell(int x, int y)
{
public:
osg::ref_ptr<osg::Node> mNode;
};
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mGridCacheMutex);
Grid::iterator found = mGridCache.find(std::make_pair(x,y));
if (found != mGridCache.end())
return found->second;
}
osg::ref_ptr<osg::Node> node = buildTerrain(NULL, 1.f, osg::Vec2f(x+0.5, y+0.5));
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mGridCacheMutex);
mGridCache.insert(std::make_pair(std::make_pair(x,y), node));
return node;
}
osg::ref_ptr<osg::Node> TerrainGrid::buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter)
{
@ -134,11 +148,24 @@ osg::ref_ptr<osg::Node> TerrainGrid::buildTerrain (osg::Group* parent, float chu
// For compiling textures, I don't think the osgFX::Effect does it correctly
osg::ref_ptr<osg::Node> textureCompileDummy (new osg::Node);
unsigned int dummyTextureCounter = 0;
std::vector<osg::ref_ptr<osg::Texture2D> > layerTextures;
for (std::vector<LayerInfo>::const_iterator it = layerList.begin(); it != layerList.end(); ++it)
{
layerTextures.push_back(mResourceSystem->getTextureManager()->getTexture2D(it->mDiffuseMap, osg::Texture::REPEAT, osg::Texture::REPEAT));
textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, layerTextures.back());
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mTextureCacheMutex);
for (std::vector<LayerInfo>::const_iterator it = layerList.begin(); it != layerList.end(); ++it)
{
osg::ref_ptr<osg::Texture2D> texture = mTextureCache[it->mDiffuseMap];
if (!texture)
{
texture = new osg::Texture2D(mResourceSystem->getImageManager()->getImage(it->mDiffuseMap));
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
mResourceSystem->getSceneManager()->applyFilterSettings(texture);
mTextureCache[it->mDiffuseMap] = texture;
}
layerTextures.push_back(texture);
textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, layerTextures.back());
}
}
std::vector<osg::ref_ptr<osg::Texture2D> > blendmapTextures;
@ -148,9 +175,8 @@ osg::ref_ptr<osg::Node> TerrainGrid::buildTerrain (osg::Group* parent, float chu
texture->setImage(*it);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
texture->setResizeNonPowerOfTwoHint(false);
texture->getOrCreateUserDataContainer()->addDescription("dont_override_filter");
blendmapTextures.push_back(texture);
textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, blendmapTextures.back());
@ -192,24 +218,31 @@ void TerrainGrid::loadCell(int x, int y)
if (mGrid.find(std::make_pair(x, y)) != mGrid.end())
return; // already loaded
osg::Vec2f center(x+0.5f, y+0.5f);
// try to get it from the cache
osg::ref_ptr<osg::Node> terrainNode;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mGridCacheMutex);
Grid::const_iterator found = mGridCache.find(std::make_pair(x,y));
if (found != mGridCache.end())
{
terrainNode = found->second;
if (!terrainNode)
return; // no terrain defined
}
}
osg::ref_ptr<osg::Node> terrainNode = buildTerrain(NULL, 1.f, center);
// didn't find in cache, build it
if (!terrainNode)
return; // no terrain defined
std::auto_ptr<GridElement> element (new GridElement);
element->mNode = terrainNode;
mTerrainRoot->addChild(element->mNode);
{
osg::Vec2f center(x+0.5f, y+0.5f);
terrainNode = buildTerrain(NULL, 1.f, center);
if (!terrainNode)
return; // no terrain defined
}
// kdtree probably not needed with mNumSplits=4
/*
// build a kdtree to speed up intersection tests with the terrain
// Note, the build could be optimized using a custom kdtree builder, since we know that the terrain can be represented by a quadtree
geode->accept(*mKdTreeBuilder);
*/
mTerrainRoot->addChild(terrainNode);
mGrid[std::make_pair(x,y)] = element.release();
mGrid[std::make_pair(x,y)] = terrainNode;
}
void TerrainGrid::unloadCell(int x, int y)
@ -218,11 +251,38 @@ void TerrainGrid::unloadCell(int x, int y)
if (it == mGrid.end())
return;
GridElement* element = it->second;
mTerrainRoot->removeChild(element->mNode);
delete element;
osg::ref_ptr<osg::Node> terrainNode = it->second;
mTerrainRoot->removeChild(terrainNode);
if (mUnrefQueue.get())
mUnrefQueue->push(terrainNode);
mGrid.erase(it);
}
void TerrainGrid::updateCache()
{
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mGridCacheMutex);
for (Grid::iterator it = mGridCache.begin(); it != mGridCache.end();)
{
if (it->second->referenceCount() <= 1)
mGridCache.erase(it++);
else
++it;
}
}
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mTextureCacheMutex);
for (TextureCache::iterator it = mTextureCache.begin(); it != mTextureCache.end();)
{
if (it->second->referenceCount() <= 1)
mTextureCache.erase(it++);
else
++it;
}
}
}
}

@ -6,37 +6,55 @@
#include "world.hpp"
#include "material.hpp"
namespace osg
namespace SceneUtil
{
class KdTreeBuilder;
class UnrefQueue;
}
namespace Terrain
{
class GridElement;
/// @brief Simple terrain implementation that loads cells in a grid, with no LOD
class TerrainGrid : public Terrain::World
{
public:
TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico,
Storage* storage, int nodeMask);
TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, SceneUtil::UnrefQueue* unrefQueue = NULL);
~TerrainGrid();
/// Load a terrain cell and store it in cache for later use.
/// @note The returned ref_ptr should be kept by the caller to ensure that the terrain stays in cache for as long as needed.
/// @note Thread safe.
virtual osg::ref_ptr<osg::Node> cacheCell(int x, int y);
/// @note Not thread safe.
virtual void loadCell(int x, int y);
/// @note Not thread safe.
virtual void unloadCell(int x, int y);
/// Clear cached objects that are no longer referenced
/// @note Thread safe.
void updateCache();
private:
osg::ref_ptr<osg::Node> buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter);
// split each ESM::Cell into mNumSplits*mNumSplits terrain chunks
unsigned int mNumSplits;
typedef std::map<std::pair<int, int>, GridElement*> Grid;
typedef std::map<std::string, osg::ref_ptr<osg::Texture2D> > TextureCache;
TextureCache mTextureCache;
OpenThreads::Mutex mTextureCacheMutex;
typedef std::map<std::pair<int, int>, osg::ref_ptr<osg::Node> > Grid;
Grid mGrid;
osg::ref_ptr<osg::KdTreeBuilder> mKdTreeBuilder;
Grid mGridCache;
OpenThreads::Mutex mGridCacheMutex;
BufferCache mCache;
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
};
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save