Merge remote-tracking branch 'scrawl/resource'

coverity_scan
Marc Zinnschlag 9 years ago
commit da6dcfc49e

@ -224,8 +224,6 @@ void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::Land newLand (land); 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; newLand.mPlugin = 0;
if (land.mDataTypes & ESM::Land::DATA_VTEX) if (land.mDataTypes & ESM::Land::DATA_VTEX)

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

@ -67,6 +67,7 @@ add_openmw_dir (mwworld
actionequip timestamp actionalchemy cellstore actionapply actioneat actionequip timestamp actionalchemy cellstore actionapply actioneat
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager
cellpreloader
) )
add_openmw_dir (mwphysics add_openmw_dir (mwphysics

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

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

@ -182,6 +182,29 @@ namespace MWClass
return ""; 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 std::string Creature::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>(); 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 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 virtual bool
isActor() const { isActor() const {
return true; return true;

@ -6,6 +6,7 @@
#include "../mwmechanics/levelledlist.hpp" #include "../mwmechanics/levelledlist.hpp"
#include "../mwworld/customdata.hpp" #include "../mwworld/customdata.hpp"
#include "../mwmechanics/creaturestats.hpp"
namespace MWClass namespace MWClass
{ {
@ -53,6 +54,22 @@ namespace MWClass
registerClass (typeid (ESM::CreatureLevList).name(), instance); 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 void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, const std::string& model, MWRender::RenderingInterface &renderingInterface) const
{ {
ensureCustomData(ptr); ensureCustomData(ptr);

@ -17,6 +17,9 @@ namespace MWClass
static void registerSelf(); 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; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

@ -37,6 +37,7 @@
#include "../mwrender/objects.hpp" #include "../mwrender/objects.hpp"
#include "../mwrender/renderinginterface.hpp" #include "../mwrender/renderinginterface.hpp"
#include "../mwrender/npcanimation.hpp"
#include "../mwgui/tooltips.hpp" #include "../mwgui/tooltips.hpp"
@ -429,6 +430,91 @@ namespace MWClass
return model; 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 std::string Npc::getName (const MWWorld::ConstPtr& ptr) const
{ {
if(ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf()) 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 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, virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Generate action for activation ///< Generate action for activation

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

@ -164,7 +164,7 @@ namespace MWGui
mBackgroundImage->setBackgroundImage(""); mBackgroundImage->setBackgroundImage("");
mBackgroundImage->setRenderItemTexture(mGuiTexture.get()); 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); setVisible(true);

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

@ -142,7 +142,7 @@ namespace MWGui
mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture())); mPreviewTexture.reset(new osgMyGUI::OSGTexture(mPreview->getTexture()));
mPreviewImage->setRenderItemTexture(mPreviewTexture.get()); 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(); const ESM::NPC& proto = mPreview->getPrototype();
setRaceId(proto.mRace); setRaceId(proto.mRace);

@ -422,6 +422,6 @@ namespace MWGui
mScreenshotTexture.reset(new osgMyGUI::OSGTexture(texture)); mScreenshotTexture.reset(new osgMyGUI::OSGTexture(texture));
mScreenshot->setRenderItemTexture(mScreenshotTexture.get()); 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)); mTexture.reset(new osgMyGUI::OSGTexture(texture));
setRenderItemTexture(mTexture.get()); 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() int VideoWidget::getVideoWidth()

@ -28,7 +28,7 @@
#include <components/fontloader/fontloader.hpp> #include <components/fontloader/fontloader.hpp>
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
#include <components/resource/texturemanager.hpp> #include <components/resource/imagemanager.hpp>
#include <components/translation/translation.hpp> #include <components/translation/translation.hpp>
@ -194,7 +194,7 @@ namespace MWGui
, mVersionDescription(versionDescription) , mVersionDescription(versionDescription)
{ {
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); 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); mGuiPlatform->initialise(resourcePath, logpath);
mGui = new MyGUI::Gui; mGui = new MyGUI::Gui;
@ -2015,9 +2015,9 @@ namespace MWGui
continue; continue;
std::string tex_name = imgSetPointer->getImageSet()->getIndexInfo(0,0).texture; 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 //everything looks good, send it to the cursor manager
Uint8 size_x = imgSetPointer->getSize().width; Uint8 size_x = imgSetPointer->getSize().width;
@ -2026,7 +2026,7 @@ namespace MWGui
Uint8 hotspot_y = imgSetPointer->getHotSpot().top; Uint8 hotspot_y = imgSetPointer->getHotSpot().top;
int rotation = imgSetPointer->getRotation(); 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) : mCanWaterWalk(false), mWalkingOnWater(false)
, mCollisionObject(0), mForce(0.f, 0.f, 0.f), mOnGround(false) , mCollisionObject(0), mForce(0.f, 0.f, 0.f), mOnGround(false)
, mInternalCollisionMode(true) , mInternalCollisionMode(true)

@ -15,7 +15,7 @@ class btCollisionObject;
namespace Resource namespace Resource
{ {
class BulletShapeInstance; class BulletShape;
} }
namespace MWPhysics namespace MWPhysics
@ -48,7 +48,7 @@ namespace MWPhysics
class Actor : public PtrHolder class Actor : public PtrHolder
{ {
public: 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(); ~Actor();
/** /**

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

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

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

@ -55,6 +55,9 @@ public:
~PartHolder(); ~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() osg::ref_ptr<osg::Node> getNode()
{ {
return mNode; return mNode;
@ -229,7 +232,8 @@ protected:
// Stored in all lowercase for a case-insensitive lookup // Stored in all lowercase for a case-insensitive lookup
typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > NodeMap; typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > NodeMap;
NodeMap mNodeMap; mutable NodeMap mNodeMap;
mutable bool mNodeMapCreated;
MWWorld::Ptr mPtr; MWWorld::Ptr mPtr;
@ -260,6 +264,8 @@ protected:
float mAlpha; float mAlpha;
const NodeMap& getNodeMap() const;
/* Sets the appropriate animations on the bone groups based on priority. /* Sets the appropriate animations on the bone groups based on priority.
*/ */
void resetActiveGroups(); void resetActiveGroups();

@ -194,7 +194,7 @@ namespace MWRender
sizeX = std::max(sizeX, 0); sizeX = std::max(sizeX, 0);
sizeY = std::max(sizeY, 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(); redraw();
} }

@ -105,7 +105,7 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot)
else else
bonename = "Shield Bone"; 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); osg::ref_ptr<osg::Node> attached = SceneUtil::attach(node, mObjectRoot, bonename, bonename);
mResourceSystem->getSceneManager()->notifyAttached(attached); 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) 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); node->setNodeMask(Mask_Effect);

@ -273,6 +273,13 @@ NpcAnimation::~NpcAnimation()
// all from within this destructor. ouch! // all from within this destructor. ouch!
&& mPtr.getRefData().getCustomData() && mPtr.getClass().getInventoryStore(mPtr).getListener() == this) && mPtr.getRefData().getCustomData() && mPtr.getClass().getInventoryStore(mPtr).getListener() == this)
mPtr.getClass().getInventoryStore(mPtr).setListener(NULL, mPtr); 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, NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem,
@ -619,116 +626,10 @@ void NpcAnimation::updateParts()
showWeapons(mShowWeapons); showWeapons(mShowWeapons);
showCarriedLeft(mShowCarriedLeft); 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); 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::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) for(int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part)
{ {
if(mPartPriorities[part] < 1) 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) 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); osg::ref_ptr<osg::Node> attached = SceneUtil::attach(instance, mObjectRoot, bonefilter, bonename);
mResourceSystem->getSceneManager()->notifyAttached(attached); mResourceSystem->getSceneManager()->notifyAttached(attached);
if (enchantedGlow) if (enchantedGlow)
@ -946,9 +847,9 @@ void NpcAnimation::addControllers()
if (mViewMode == VM_FirstPerson) if (mViewMode == VM_FirstPerson)
{ {
NodeMap::iterator found = mNodeMap.find("bip01 neck"); 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()); mFirstPersonNeckController = new NeckController(mObjectRoot.get());
node->addUpdateCallback(mFirstPersonNeckController); node->addUpdateCallback(mFirstPersonNeckController);
mActiveControllers.insert(std::make_pair(node, mFirstPersonNeckController)); mActiveControllers.insert(std::make_pair(node, mFirstPersonNeckController));
@ -1116,6 +1017,118 @@ void NpcAnimation::updatePtr(const MWWorld::Ptr &updated)
mHeadAnimationTime->updatePtr(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) void NpcAnimation::setAccurateAiming(bool enabled)
{ {
mAccurateAiming = enabled; mAccurateAiming = enabled;

@ -10,6 +10,7 @@
namespace ESM namespace ESM
{ {
struct NPC; struct NPC;
struct BodyPart;
} }
namespace MWRender namespace MWRender
@ -150,6 +151,10 @@ public:
void setFirstPersonOffset(const osg::Vec3f& offset); void setFirstPersonOffset(const osg::Vec3f& offset);
virtual void updatePtr(const MWWorld::Ptr& updated); 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/visitor.hpp>
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/unrefqueue.hpp>
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -85,9 +86,10 @@ namespace
namespace MWRender 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) : mRootNode(rootNode)
, mResourceSystem(resourceSystem) , mResourceSystem(resourceSystem)
, mUnrefQueue(unrefQueue)
{ {
} }
@ -186,7 +188,11 @@ bool Objects::removeObject (const MWWorld::Ptr& ptr)
delete iter->second; delete iter->second;
mObjects.erase(iter); mObjects.erase(iter);
if (mUnrefQueue.get())
mUnrefQueue->push(ptr.getRefData().getBaseNode());
ptr.getRefData().getBaseNode()->getParent(0)->removeChild(ptr.getRefData().getBaseNode()); ptr.getRefData().getBaseNode()->getParent(0)->removeChild(ptr.getRefData().getBaseNode());
ptr.getRefData().setBaseNode(NULL); ptr.getRefData().setBaseNode(NULL);
return true; return true;
} }
@ -200,6 +206,8 @@ void Objects::removeCell(const MWWorld::CellStore* store)
{ {
if(iter->first.getCell() == store) if(iter->first.getCell() == store)
{ {
if (mUnrefQueue.get())
mUnrefQueue->push(iter->second->getObjectRoot());
delete iter->second; delete iter->second;
mObjects.erase(iter++); mObjects.erase(iter++);
} }
@ -211,6 +219,8 @@ void Objects::removeCell(const MWWorld::CellStore* store)
if(cell != mCellSceneNodes.end()) if(cell != mCellSceneNodes.end())
{ {
cell->second->getParent(0)->removeChild(cell->second); cell->second->getParent(0)->removeChild(cell->second);
if (mUnrefQueue.get())
mUnrefQueue->push(cell->second);
mCellSceneNodes.erase(cell); mCellSceneNodes.erase(cell);
} }
} }

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

@ -17,7 +17,7 @@
#include <osgViewer/Viewer> #include <osgViewer/Viewer>
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
#include <components/resource/texturemanager.hpp> #include <components/resource/imagemanager.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -26,6 +26,8 @@
#include <components/sceneutil/lightmanager.hpp> #include <components/sceneutil/lightmanager.hpp>
#include <components/sceneutil/statesetupdater.hpp> #include <components/sceneutil/statesetupdater.hpp>
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/workqueue.hpp>
#include <components/sceneutil/unrefqueue.hpp>
#include <components/terrain/terraingrid.hpp> #include <components/terrain/terraingrid.hpp>
@ -126,11 +128,36 @@ namespace MWRender
bool mWireframe; 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, RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, Resource::ResourceSystem* resourceSystem,
const Fallback::Map* fallback, const std::string& resourcePath) const Fallback::Map* fallback, const std::string& resourcePath)
: mViewer(viewer) : mViewer(viewer)
, mRootNode(rootNode) , mRootNode(rootNode)
, mResourceSystem(resourceSystem) , mResourceSystem(resourceSystem)
, mWorkQueue(new SceneUtil::WorkQueue)
, mUnrefQueue(new SceneUtil::UnrefQueue)
, mFogDepth(0.f) , mFogDepth(0.f)
, mUnderwaterColor(fallback->getFallbackColour("Water_UnderwaterColor")) , mUnderwaterColor(fallback->getFallbackColour("Water_UnderwaterColor"))
, mUnderwaterWeight(fallback->getFallbackFloat("Water_UnderwaterColorWeight")) , mUnderwaterWeight(fallback->getFallbackFloat("Water_UnderwaterColorWeight"))
@ -151,7 +178,7 @@ namespace MWRender
mPathgrid.reset(new Pathgrid(mRootNode)); mPathgrid.reset(new Pathgrid(mRootNode));
mObjects.reset(new Objects(mResourceSystem, lightRoot)); mObjects.reset(new Objects(mResourceSystem, lightRoot, mUnrefQueue.get()));
mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation);
@ -162,7 +189,7 @@ namespace MWRender
mWater.reset(new Water(mRootNode, lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath)); mWater.reset(new Water(mRootNode, lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath));
mTerrain.reset(new Terrain::TerrainGrid(lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), 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())); mCamera.reset(new Camera(mViewer->getCamera()));
@ -231,6 +258,37 @@ namespace MWRender
return mResourceSystem; 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() osg::Group* RenderingManager::getLightRoot()
{ {
return mLightRoot.get(); return mLightRoot.get();
@ -385,6 +443,8 @@ namespace MWRender
void RenderingManager::update(float dt, bool paused) void RenderingManager::update(float dt, bool paused)
{ {
mUnrefQueue->flush(mWorkQueue.get());
if (!paused) if (!paused)
{ {
mEffectManager->update(dt); mEffectManager->update(dt);
@ -776,7 +836,10 @@ namespace MWRender
void RenderingManager::updateTextureFiltering() 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 mag filter", "General"),
Settings::Manager::getString("texture min filter", "General"), Settings::Manager::getString("texture min filter", "General"),
Settings::Manager::getString("texture mipmap", "General"), Settings::Manager::getString("texture mipmap", "General"),

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

@ -13,8 +13,9 @@
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/nifosg/controller.hpp> #include <components/nifosg/controller.hpp>
#include <components/resource/texturemanager.hpp> #include <components/resource/imagemanager.hpp>
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/fallback/fallback.hpp> #include <components/fallback/fallback.hpp>
#include "vismask.hpp" #include "vismask.hpp"
@ -39,7 +40,11 @@ namespace
{ {
std::ostringstream texname; std::ostringstream texname;
texname << "textures/water/" << tex << std::setw(2) << std::setfill('0') << i << ".dds"; 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)); 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/misc/resourcehelpers.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/resource/texturemanager.hpp> #include <components/resource/imagemanager.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include <components/fallback/fallback.hpp> #include <components/fallback/fallback.hpp>
@ -134,10 +134,10 @@ private:
class AtmosphereNightUpdater : public SceneUtil::StateSetUpdater class AtmosphereNightUpdater : public SceneUtil::StateSetUpdater
{ {
public: public:
AtmosphereNightUpdater(Resource::TextureManager* textureManager) AtmosphereNightUpdater(Resource::ImageManager* imageManager)
{ {
// we just need a texture, its contents don't really matter // 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) void setFade(const float fade)
@ -469,16 +469,16 @@ const float CelestialBody::mDistance = 1000.0f;
class Sun : public CelestialBody class Sun : public CelestialBody
{ {
public: public:
Sun(osg::Group* parentNode, Resource::TextureManager& textureManager) Sun(osg::Group* parentNode, Resource::ImageManager& imageManager)
: CelestialBody(parentNode, 1.0f, 1) : CelestialBody(parentNode, 1.0f, 1)
, mUpdater(new Updater) , mUpdater(new Updater)
{ {
mTransform->addUpdateCallback(mUpdater); mTransform->addUpdateCallback(mUpdater);
mTransform->setNodeMask(Mask_Sun); mTransform->setNodeMask(Mask_Sun);
osg::ref_ptr<osg::Texture2D> sunTex = textureManager.getTexture2D("textures/tx_sun_05.dds", osg::ref_ptr<osg::Texture2D> sunTex (new osg::Texture2D(imageManager.getImage("textures/tx_sun_05.dds")));
osg::Texture::CLAMP, sunTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
osg::Texture::CLAMP); sunTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
mGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON); mGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, sunTex, osg::StateAttribute::ON);
@ -498,7 +498,7 @@ public:
mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true); mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true);
mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false); mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false);
createSunFlash(textureManager); createSunFlash(imageManager);
createSunGlare(); createSunGlare();
} }
@ -602,11 +602,11 @@ private:
return oqn; 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::ref_ptr<osg::Texture2D> tex (new osg::Texture2D(imageManager.getImage("textures/tx_sun_flash_grey_05.dds")));
osg::Texture::CLAMP, tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
osg::Texture::CLAMP); tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
osg::ref_ptr<osg::PositionAttitudeTransform> transform (new osg::PositionAttitudeTransform); osg::ref_ptr<osg::PositionAttitudeTransform> transform (new osg::PositionAttitudeTransform);
const float scale = 2.6f; const float scale = 2.6f;
@ -932,11 +932,11 @@ public:
Type_Secunda 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) : CelestialBody(parentNode, scaleFactor, 2)
, mType(type) , mType(type)
, mPhase(MoonState::Phase_Unspecified) , mPhase(MoonState::Phase_Unspecified)
, mUpdater(new Updater(textureManager)) , mUpdater(new Updater(imageManager))
{ {
setPhase(MoonState::Phase_Full); setPhase(MoonState::Phase_Full);
setVisible(true); setVisible(true);
@ -1001,7 +1001,7 @@ public:
private: private:
struct Updater : public SceneUtil::StateSetUpdater struct Updater : public SceneUtil::StateSetUpdater
{ {
Resource::TextureManager& mTextureManager; Resource::ImageManager& mImageManager;
osg::ref_ptr<osg::Texture2D> mPhaseTex; osg::ref_ptr<osg::Texture2D> mPhaseTex;
osg::ref_ptr<osg::Texture2D> mCircleTex; osg::ref_ptr<osg::Texture2D> mCircleTex;
float mTransparency; float mTransparency;
@ -1009,8 +1009,8 @@ private:
osg::Vec4f mAtmosphereColor; osg::Vec4f mAtmosphereColor;
osg::Vec4f mMoonColor; osg::Vec4f mMoonColor;
Updater(Resource::TextureManager& textureManager) Updater(Resource::ImageManager& imageManager)
: mTextureManager(textureManager) : mImageManager(imageManager)
, mPhaseTex() , mPhaseTex()
, mCircleTex() , mCircleTex()
, mTransparency(1.0f) , mTransparency(1.0f)
@ -1055,8 +1055,12 @@ private:
void setTextures(const std::string& phaseTex, const std::string& circleTex) void setTextures(const std::string& phaseTex, const std::string& circleTex)
{ {
mPhaseTex = mTextureManager.getTexture2D(phaseTex, osg::Texture::CLAMP, osg::Texture::CLAMP); mPhaseTex = new osg::Texture2D(mImageManager.getImage(phaseTex));
mCircleTex = mTextureManager.getTexture2D(circleTex, osg::Texture::CLAMP, osg::Texture::CLAMP); 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(); reset();
} }
@ -1142,7 +1146,7 @@ void SkyManager::create()
{ {
assert(!mCreated); assert(!mCreated);
mAtmosphereDay = mSceneManager->createInstance("meshes/sky_atmosphere.nif", mEarlyRenderBinRoot); mAtmosphereDay = mSceneManager->getInstance("meshes/sky_atmosphere.nif", mEarlyRenderBinRoot);
ModVertexAlphaVisitor modAtmosphere(0); ModVertexAlphaVisitor modAtmosphere(0);
mAtmosphereDay->accept(modAtmosphere); mAtmosphereDay->accept(modAtmosphere);
@ -1155,31 +1159,31 @@ void SkyManager::create()
osg::ref_ptr<osg::Node> atmosphereNight; osg::ref_ptr<osg::Node> atmosphereNight;
if (mSceneManager->getVFS()->exists("meshes/sky_night_02.nif")) 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 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); atmosphereNight->getOrCreateStateSet()->setAttributeAndModes(createAlphaTrackingUnlitMaterial(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
ModVertexAlphaVisitor modStars(2); ModVertexAlphaVisitor modStars(2);
atmosphereNight->accept(modStars); atmosphereNight->accept(modStars);
mAtmosphereNightUpdater = new AtmosphereNightUpdater(mSceneManager->getTextureManager()); mAtmosphereNightUpdater = new AtmosphereNightUpdater(mSceneManager->getImageManager());
atmosphereNight->addUpdateCallback(mAtmosphereNightUpdater); 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(); 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)); mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), 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)); mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), fallback->getFallbackFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda));
mCloudNode = new osg::PositionAttitudeTransform; mCloudNode = new osg::PositionAttitudeTransform;
mEarlyRenderBinRoot->addChild(mCloudNode); mEarlyRenderBinRoot->addChild(mCloudNode);
mCloudMesh = mSceneManager->createInstance("meshes/sky_clouds_01.nif", mCloudNode); mCloudMesh = mSceneManager->getInstance("meshes/sky_clouds_01.nif", mCloudNode);
ModVertexAlphaVisitor modClouds(1); ModVertexAlphaVisitor modClouds(1);
mCloudMesh->accept(modClouds); mCloudMesh->accept(modClouds);
mCloudUpdater = new CloudUpdater; mCloudUpdater = new CloudUpdater;
mCloudUpdater->setOpacity(1.f); mCloudUpdater->setOpacity(1.f);
mCloudMesh->addUpdateCallback(mCloudUpdater); mCloudMesh->addUpdateCallback(mCloudUpdater);
mCloudMesh2 = mSceneManager->createInstance("meshes/sky_clouds_01.nif", mCloudNode); mCloudMesh2 = mSceneManager->getInstance("meshes/sky_clouds_01.nif", mCloudNode);
mCloudMesh2->accept(modClouds); mCloudMesh2->accept(modClouds);
mCloudUpdater2 = new CloudUpdater; mCloudUpdater2 = new CloudUpdater;
mCloudUpdater2->setOpacity(0.f); mCloudUpdater2->setOpacity(0.f);
@ -1342,8 +1346,12 @@ void SkyManager::createRain()
mRainParticleSystem->setAlignVectorY(osg::Vec3f(0,0,-1)); mRainParticleSystem->setAlignVectorY(osg::Vec3f(0,0,-1));
osg::ref_ptr<osg::StateSet> stateset (mRainParticleSystem->getOrCreateStateSet()); 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->setNestRenderBins(false);
stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
@ -1529,7 +1537,7 @@ void SkyManager::setWeather(const WeatherResult& weather)
mParticleNode->setNodeMask(Mask_WeatherParticles); mParticleNode->setNodeMask(Mask_WeatherParticles);
mRootNode->addChild(mParticleNode); mRootNode->addChild(mParticleNode);
} }
mParticleEffect = mSceneManager->createInstance(mCurrentParticleEffect, mParticleNode); mParticleEffect = mSceneManager->getInstance(mCurrentParticleEffect, mParticleNode);
SceneUtil::AssignControllerSourcesVisitor assignVisitor(boost::shared_ptr<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource)); SceneUtil::AssignControllerSourcesVisitor assignVisitor(boost::shared_ptr<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource));
mParticleEffect->accept(assignVisitor); mParticleEffect->accept(assignVisitor);
@ -1549,8 +1557,11 @@ void SkyManager::setWeather(const WeatherResult& weather)
std::string texture = Misc::ResourceHelpers::correctTexturePath(mClouds, mSceneManager->getVFS()); std::string texture = Misc::ResourceHelpers::correctTexturePath(mClouds, mSceneManager->getVFS());
mCloudUpdater->setTexture(mSceneManager->getTextureManager()->getTexture2D(texture, osg::ref_ptr<osg::Texture2D> cloudTex (new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture)));
osg::Texture::REPEAT, osg::Texture::REPEAT)); 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) if (mNextClouds != weather.mNextCloudTexture)
@ -1561,8 +1572,11 @@ void SkyManager::setWeather(const WeatherResult& weather)
{ {
std::string texture = Misc::ResourceHelpers::correctTexturePath(mNextClouds, mSceneManager->getVFS()); std::string texture = Misc::ResourceHelpers::correctTexturePath(mNextClouds, mSceneManager->getVFS());
mCloudUpdater2->setTexture(mSceneManager->getTextureManager()->getTexture2D(texture, osg::ref_ptr<osg::Texture2D> cloudTex (new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture)));
osg::Texture::REPEAT, osg::Texture::REPEAT)); 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); 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) void SkyManager::setWaterEnabled(bool enabled)
{ {
mUnderwaterSwitch->setEnabled(enabled); mUnderwaterSwitch->setEnabled(enabled);

@ -153,6 +153,8 @@ namespace MWRender
/// Set height of water plane (used to remove underwater weather particles) /// Set height of water plane (used to remove underwater weather particles)
void setWaterHeight(float height); void setWaterHeight(float height);
void listAssetsToPreload(std::vector<std::string>& models, std::vector<std::string>& textures);
private: private:
void create(); void create();
///< no need to call this, automatically done on first enable() ///< 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; const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX;
if (!land->isDataLoaded(flags)) if (!land->isDataLoaded(flags))
land->loadData(flags); land->loadData(flags);
// TODO: unload land data when it's no longer needed
return land; return land;
} }

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

@ -15,6 +15,7 @@
#include <osg/FrontFace> #include <osg/FrontFace>
#include <osg/Shader> #include <osg/Shader>
#include <osg/GLExtensions> #include <osg/GLExtensions>
#include <osg/UserDataContainer>
#include <osgDB/ReadFile> #include <osgDB/ReadFile>
@ -25,7 +26,7 @@
#include <osgUtil/CullVisitor> #include <osgUtil/CullVisitor>
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
#include <components/resource/texturemanager.hpp> #include <components/resource/imagemanager.hpp>
#include <components/nifosg/controller.hpp> #include <components/nifosg/controller.hpp>
#include <components/sceneutil/controller.hpp> #include <components/sceneutil/controller.hpp>
@ -317,6 +318,7 @@ public:
mRefractionTexture->setInternalFormat(GL_RGB); mRefractionTexture->setInternalFormat(GL_RGB);
mRefractionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mRefractionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
mRefractionTexture->setFilter(osg::Texture::MAG_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); attach(osg::Camera::COLOR_BUFFER, mRefractionTexture);
@ -328,6 +330,7 @@ public:
mRefractionDepthTexture->setSourceType(GL_UNSIGNED_INT); mRefractionDepthTexture->setSourceType(GL_UNSIGNED_INT);
mRefractionDepthTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); mRefractionDepthTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
mRefractionDepthTexture->setFilter(osg::Texture::MAG_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); attach(osg::Camera::DEPTH_BUFFER, mRefractionDepthTexture);
} }
@ -388,6 +391,7 @@ public:
mReflectionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); 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_S, osg::Texture::CLAMP_TO_EDGE);
mReflectionTexture->setWrap(osg::Texture::WRAP_T, 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); attach(osg::Camera::COLOR_BUFFER, mReflectionTexture);
@ -553,7 +557,10 @@ void Water::createSimpleWaterStateSet(osg::Node* node, float alpha)
{ {
std::ostringstream texname; std::ostringstream texname;
texname << "textures/water/" << texture << std::setw(2) << std::setfill('0') << i << ".dds"; 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()) 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) void Water::setEnabled(bool enabled)
{ {
mEnabled = enabled; mEnabled = enabled;

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

@ -84,7 +84,7 @@ void WeaponAnimation::attachArrow(MWWorld::Ptr actor)
return; return;
std::string model = ammo->getClass().getModel(*ammo); 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)); 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; return mState;
} }
const std::vector<std::string> &CellStore::getPreloadedIds() const
{
return mIds;
}
bool CellStore::hasState() const bool CellStore::hasState() const
{ {
return mHasState; return mHasState;

@ -204,6 +204,9 @@ namespace MWWorld
State getState() const; 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; bool hasState() const;
///< Does this cell have state that needs to be stored in a saved game file? ///< Does this cell have state that needs to be stored in a saved game file?

@ -291,6 +291,13 @@ namespace MWWorld
return ""; 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 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"); 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 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; 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. ///< 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) for (std::list<std::pair<std::string, Ptr> >::iterator iter = mScripts.begin(); iter!=mScripts.end(); ++iter)
if (iter->second==ptr) 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); remove(ptr);
break; break;
} }

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

@ -24,6 +24,7 @@
#include "class.hpp" #include "class.hpp"
#include "cellvisitors.hpp" #include "cellvisitors.hpp"
#include "cellstore.hpp" #include "cellstore.hpp"
#include "cellpreloader.hpp"
namespace namespace
{ {
@ -192,6 +193,16 @@ namespace MWWorld
void Scene::update (float duration, bool paused) void Scene::update (float duration, bool paused)
{ {
if (mPreloadEnabled)
{
mPreloadTimer += duration;
if (mPreloadTimer > 0.25f)
{
preloadCells();
mPreloadTimer = 0.f;
}
}
mRendering.update (duration, paused); mRendering.update (duration, paused);
} }
@ -284,6 +295,8 @@ namespace MWWorld
if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) if (!cell->isExterior() && !(cell->getCell()->mData.mFlags & ESM::Cell::QuasiEx))
mRendering.configureAmbient(cell->getCell()); mRendering.configureAmbient(cell->getCell());
} }
mPreloader->notifyLoaded(cell);
} }
void Scene::changeToVoid() void Scene::changeToVoid()
@ -305,7 +318,7 @@ namespace MWWorld
getGridCenter(cellX, cellY); getGridCenter(cellX, cellY);
float centerX, centerY; float centerX, centerY;
MWBase::Environment::get().getWorld()->indexToPosition(cellX, cellY, centerX, centerY, true); 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())); float distance = std::max(std::abs(centerX-pos.x()), std::abs(centerY-pos.y()));
if (distance > maxDistance) if (distance > maxDistance)
{ {
@ -326,15 +339,13 @@ namespace MWWorld
std::string loadingExteriorText = "#{sLoadingMessage3}"; std::string loadingExteriorText = "#{sLoadingMessage3}";
loadingListener->setLabel(loadingExteriorText); loadingListener->setLabel(loadingExteriorText);
const int halfGridSize = Settings::Manager::getInt("exterior cell load distance", "Cells");
CellStoreCollection::iterator active = mActiveCells.begin(); CellStoreCollection::iterator active = mActiveCells.begin();
while (active!=mActiveCells.end()) while (active!=mActiveCells.end())
{ {
if ((*active)->getCell()->isExterior()) if ((*active)->getCell()->isExterior())
{ {
if (std::abs (X-(*active)->getCell()->getGridX())<=halfGridSize && if (std::abs (X-(*active)->getCell()->getGridX())<=mHalfGridSize &&
std::abs (Y-(*active)->getCell()->getGridY())<=halfGridSize) std::abs (Y-(*active)->getCell()->getGridY())<=mHalfGridSize)
{ {
// keep cells within the new grid // keep cells within the new grid
++active; ++active;
@ -346,9 +357,9 @@ namespace MWWorld
int refsToLoad = 0; int refsToLoad = 0;
// get the number of refs to load // 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(); CellStoreCollection::iterator iter = mActiveCells.begin();
@ -371,9 +382,9 @@ namespace MWWorld
loadingListener->setProgressRange(refsToLoad); loadingListener->setProgressRange(refsToLoad);
// Load cells // 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(); CellStoreCollection::iterator iter = mActiveCells.begin();
@ -402,7 +413,7 @@ namespace MWWorld
mCellChanged = true; mCellChanged = true;
mRendering.getResourceSystem()->clearCache(); mPreloader->updateCache(mRendering.getReferenceTime());
} }
void Scene::changePlayerCell(CellStore *cell, const ESM::Position &pos, bool adjustPlayerPos) 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) Scene::Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics)
: mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering) : 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() Scene::~Scene()
@ -515,7 +542,7 @@ namespace MWWorld
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
mRendering.getResourceSystem()->clearCache(); mPreloader->updateCache(mRendering.getReferenceTime());
} }
void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos) void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos)
@ -599,4 +626,167 @@ namespace MWWorld
return Ptr(); 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 #ifndef GAME_MWWORLD_SCENE_H
#define GAME_MWWORLD_SCENE_H #define GAME_MWWORLD_SCENE_H
//#include "../mwrender/renderingmanager.hpp"
#include "ptr.hpp" #include "ptr.hpp"
#include "globals.hpp" #include "globals.hpp"
#include <set> #include <set>
#include <memory>
namespace osg namespace osg
{ {
@ -43,6 +42,7 @@ namespace MWWorld
{ {
class Player; class Player;
class CellStore; class CellStore;
class CellPreloader;
class Scene class Scene
{ {
@ -57,6 +57,16 @@ namespace MWWorld
bool mCellChanged; bool mCellChanged;
MWPhysics::PhysicsSystem *mPhysics; MWPhysics::PhysicsSystem *mPhysics;
MWRender::RenderingManager& mRendering; 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); void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener);
@ -65,6 +75,13 @@ namespace MWWorld
void getGridCenter(int& cellX, int& cellY); void getGridCenter(int& cellX, int& cellY);
void preloadCells();
void preloadTeleportDoorDestinations();
void preloadExteriorGrid();
void preloadFastTravelDestinations();
void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false);
public: public:
Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics); Scene (MWRender::RenderingManager& rendering, MWPhysics::PhysicsSystem *physics);

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

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

@ -41,14 +41,12 @@ add_component_dir (vfs
) )
add_component_dir (resource 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 add_component_dir (sceneutil
clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller
lightmanager lightutil positionattitudetransform lightmanager lightutil positionattitudetransform workqueue unrefqueue
# not used yet
#workqueue
) )
add_component_dir (nif add_component_dir (nif

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

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

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

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

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

@ -7,14 +7,14 @@
namespace osgMyGUI 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) : mRenderManager(nullptr)
, mDataManager(nullptr) , mDataManager(nullptr)
, mLogManager(nullptr) , mLogManager(nullptr)
, mLogFacility(nullptr) , mLogFacility(nullptr)
{ {
mLogManager = new MyGUI::LogManager(); mLogManager = new MyGUI::LogManager();
mRenderManager = new RenderManager(viewer, guiRoot, textureManager, uiScalingFactor); mRenderManager = new RenderManager(viewer, guiRoot, imageManager, uiScalingFactor);
mDataManager = new DataManager(); mDataManager = new DataManager();
} }

@ -13,7 +13,7 @@ namespace osg
} }
namespace Resource namespace Resource
{ {
class TextureManager; class ImageManager;
} }
namespace MyGUI namespace MyGUI
{ {
@ -30,7 +30,7 @@ namespace osgMyGUI
class Platform class Platform
{ {
public: 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(); ~Platform();

@ -9,12 +9,13 @@
#include <osg/Geode> #include <osg/Geode>
#include <osg/BlendFunc> #include <osg/BlendFunc>
#include <osg/Texture2D> #include <osg/Texture2D>
#include <osg/TexMat>
#include <osgViewer/Viewer> #include <osgViewer/Viewer>
#include <osgGA/GUIEventHandler> #include <osgGA/GUIEventHandler>
#include <components/resource/texturemanager.hpp> #include <components/resource/imagemanager.hpp>
#include "myguitexture.hpp" #include "myguitexture.hpp"
@ -189,6 +190,12 @@ public:
mStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON); mStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
mStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); mStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
mStateSet->setMode(GL_BLEND, osg::StateAttribute::ON); 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) Drawable(const Drawable &copy, const osg::CopyOp &copyop=osg::CopyOp::SHALLOW_COPY)
: osg::Drawable(copy, copyop) : 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) : mViewer(viewer)
, mSceneRoot(sceneroot) , mSceneRoot(sceneroot)
, mTextureManager(textureManager) , mImageManager(imageManager)
, mUpdate(false) , mUpdate(false)
, mIsInitialise(false) , mIsInitialise(false)
, mInvScalingFactor(1.f) , mInvScalingFactor(1.f)
@ -516,7 +523,7 @@ MyGUI::ITexture* RenderManager::createTexture(const std::string &name)
mTextures.erase(item); mTextures.erase(item);
} }
OSGTexture* texture = new OSGTexture(name, mTextureManager); OSGTexture* texture = new OSGTexture(name, mImageManager);
mTextures.insert(std::make_pair(name, texture)); mTextures.insert(std::make_pair(name, texture));
return texture; return texture;
} }

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

@ -5,14 +5,14 @@
#include <osg/Texture2D> #include <osg/Texture2D>
#include <components/resource/texturemanager.hpp> #include <components/resource/imagemanager.hpp>
namespace osgMyGUI namespace osgMyGUI
{ {
OSGTexture::OSGTexture(const std::string &name, Resource::TextureManager* textureManager) OSGTexture::OSGTexture(const std::string &name, Resource::ImageManager* imageManager)
: mName(name) : mName(name)
, mTextureManager(textureManager) , mImageManager(imageManager)
, mFormat(MyGUI::PixelFormat::Unknow) , mFormat(MyGUI::PixelFormat::Unknow)
, mUsage(MyGUI::TextureUsage::Default) , mUsage(MyGUI::TextureUsage::Default)
, mNumElemBytes(0) , mNumElemBytes(0)
@ -20,7 +20,7 @@ namespace osgMyGUI
} }
OSGTexture::OSGTexture(osg::Texture2D *texture) OSGTexture::OSGTexture(osg::Texture2D *texture)
: mTextureManager(NULL) : mImageManager(NULL)
, mTexture(texture) , mTexture(texture)
, mFormat(MyGUI::PixelFormat::Unknow) , mFormat(MyGUI::PixelFormat::Unknow)
, mUsage(MyGUI::TextureUsage::Default) , mUsage(MyGUI::TextureUsage::Default)
@ -83,10 +83,12 @@ namespace osgMyGUI
void OSGTexture::loadFromFile(const std::string &fname) void OSGTexture::loadFromFile(const std::string &fname)
{ {
if (!mTextureManager) if (!mImageManager)
throw std::runtime_error("No texturemanager set"); 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 // disable mip-maps
mTexture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); mTexture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
@ -143,6 +145,8 @@ namespace osgMyGUI
if (!mLockedImage.valid()) if (!mLockedImage.valid())
throw std::runtime_error("Texture not locked"); 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. // 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; osg::ref_ptr<osg::Texture2D> newTexture = new osg::Texture2D;
newTexture->setTextureSize(getWidth(), getHeight()); newTexture->setTextureSize(getWidth(), getHeight());

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

@ -65,8 +65,14 @@ void ShapeData::read(NIFStream *nif)
uvlist.resize(uvs); uvlist.resize(uvs);
for(int i = 0;i < uvs;i++) for(int i = 0;i < uvs;i++)
{ {
uvlist[i] = new osg::Vec2Array(osg::Array::BIND_PER_VERTEX); osg::Vec2Array* list = uvlist[i] = new osg::Vec2Array(osg::Array::BIND_PER_VERTEX);
nif->getVector2s(uvlist[i], verts); 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/Texture2D>
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <osg/Timer>
#include <osg/StateSet> #include <osg/StateSet>
#include <osg/NodeCallback> #include <osg/NodeCallback>
#include <osg/Drawable> #include <osg/Drawable>
@ -305,6 +304,8 @@ namespace NifOsg
META_Object(NifOsg, FlipController) META_Object(NifOsg, FlipController)
std::vector<osg::ref_ptr<osg::Texture2D> >& getTextures() { return mTextures; }
virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv); virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv);
}; };

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

@ -15,7 +15,7 @@ namespace osg
namespace Resource namespace Resource
{ {
class TextureManager; class ImageManager;
} }
namespace NifOsg namespace NifOsg
@ -62,7 +62,7 @@ namespace NifOsg
{ {
public: public:
/// Create a scene graph for the given NIF. Auto-detects when skinning is used and wraps the graph in a Skeleton if so. /// 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. /// Load keyframe controllers from the given kf file.
static void loadKf(Nif::NIFFilePtr kf, KeyframeHolder& target); 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() BulletShape::~BulletShape()
{ {
deleteShape(mCollisionShape); 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()) if(shape->isCompound())
{ {
btCompoundShape *comp = static_cast<btCompoundShape*>(shape); const btCompoundShape *comp = static_cast<const btCompoundShape*>(shape);
btCompoundShape *newShape = new btCompoundShape; btCompoundShape *newShape = new btCompoundShape;
int numShapes = comp->getNumChildShapes(); int numShapes = comp->getNumChildShapes();
@ -55,29 +63,27 @@ btCollisionShape* BulletShape::duplicateCollisionShape(btCollisionShape *shape)
return newShape; return newShape;
} }
if(btBvhTriangleMeshShape* trishape = dynamic_cast<btBvhTriangleMeshShape*>(shape)) if(const btBvhTriangleMeshShape* trishape = dynamic_cast<const btBvhTriangleMeshShape*>(shape))
{ {
#if BT_BULLET_VERSION >= 283 #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 #else
// work around btScaledBvhTriangleMeshShape bug ( https://code.google.com/p/bullet/issues/detail?id=371 ) in older bullet versions // 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); btTriangleMesh* newMesh = new btTriangleMesh(*oldMesh);
// Do not build a new bvh (not needed, since it's the same as the original shape's bvh) // Do not build a new bvh (not needed, since it's the same as the original shape's bvh)
bool buildBvh = true; btOptimizedBvh* bvh = const_cast<btBvhTriangleMeshShape*>(trishape)->getOptimizedBvh();
if (trishape->getOptimizedBvh()) TriangleMeshShape* newShape = new TriangleMeshShape(newMesh, true, bvh == NULL);
buildBvh = false;
TriangleMeshShape* newShape = new TriangleMeshShape(newMesh, true, buildBvh);
// Set original shape's bvh via pointer // Set original shape's bvh via pointer
// The pointer is safe because the BulletShapeInstance keeps a ref_ptr to the original BulletShape // The pointer is safe because the BulletShapeInstance keeps a ref_ptr to the original BulletShape
if (!buildBvh) if (bvh)
newShape->setOptimizedBvh(trishape->getOptimizedBvh()); newShape->setOptimizedBvh(bvh);
#endif #endif
return newShape; return newShape;
} }
if (btBoxShape* boxshape = dynamic_cast<btBoxShape*>(shape)) if (const btBoxShape* boxshape = dynamic_cast<const btBoxShape*>(shape))
{ {
return new btBoxShape(*boxshape); return new btBoxShape(*boxshape);
} }
@ -90,13 +96,13 @@ btCollisionShape *BulletShape::getCollisionShape()
return mCollisionShape; return mCollisionShape;
} }
osg::ref_ptr<BulletShapeInstance> BulletShape::makeInstance() osg::ref_ptr<BulletShapeInstance> BulletShape::makeInstance() const
{ {
osg::ref_ptr<BulletShapeInstance> instance (new BulletShapeInstance(this)); osg::ref_ptr<BulletShapeInstance> instance (new BulletShapeInstance(this));
return instance; return instance;
} }
BulletShapeInstance::BulletShapeInstance(osg::ref_ptr<BulletShape> source) BulletShapeInstance::BulletShapeInstance(osg::ref_ptr<const BulletShape> source)
: BulletShape() : BulletShape()
, mSource(source) , mSource(source)
{ {

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

@ -13,7 +13,8 @@
#include "bulletshape.hpp" #include "bulletshape.hpp"
#include "scenemanager.hpp" #include "scenemanager.hpp"
#include "niffilemanager.hpp" #include "niffilemanager.hpp"
#include "objectcache.hpp"
#include "multiobjectcache.hpp"
namespace Resource namespace Resource
{ {
@ -96,7 +97,8 @@ private:
}; };
BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager) BulletShapeManager::BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager)
: mVFS(vfs) : ResourceManager(vfs)
, mInstanceCache(new MultiObjectCache)
, mSceneManager(sceneMgr) , mSceneManager(sceneMgr)
, mNifFileManager(nifFileManager) , 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; std::string normalized = name;
mVFS->normalizeFilename(normalized); mVFS->normalizeFilename(normalized);
osg::ref_ptr<BulletShape> shape; osg::ref_ptr<BulletShape> shape;
Index::iterator it = mIndex.find(normalized); osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);
if (it == mIndex.end()) if (obj)
shape = osg::ref_ptr<BulletShape>(static_cast<BulletShape*>(obj.get()));
else
{ {
size_t extPos = normalized.find_last_of('.'); size_t extPos = normalized.find_last_of('.');
std::string ext; std::string ext;
@ -137,16 +141,53 @@ osg::ref_ptr<BulletShapeInstance> BulletShapeManager::createInstance(const std::
node->accept(visitor); node->accept(visitor);
shape = visitor.getShape(); shape = visitor.getShape();
if (!shape) if (!shape)
return osg::ref_ptr<BulletShapeInstance>(); {
mCache->addEntryToObjectCache(normalized, NULL);
return osg::ref_ptr<BulletShape>();
}
} }
mIndex[normalized] = shape; mCache->addEntryToObjectCache(normalized, shape);
} }
else return shape;
shape = it->second; }
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; 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 <osg/ref_ptr>
#include "bulletshape.hpp" #include "bulletshape.hpp"
#include "resourcemanager.hpp"
namespace VFS
{
class Manager;
}
namespace Resource namespace Resource
{ {
@ -21,21 +17,37 @@ namespace Resource
class BulletShape; class BulletShape;
class BulletShapeInstance; 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: public:
BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager); BulletShapeManager(const VFS::Manager* vfs, SceneManager* sceneMgr, NifFileManager* nifFileManager);
~BulletShapeManager(); ~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: private:
const VFS::Manager* mVFS; osg::ref_ptr<BulletShapeInstance> createInstance(const std::string& name);
osg::ref_ptr<MultiObjectCache> mInstanceCache;
SceneManager* mSceneManager; SceneManager* mSceneManager;
NifFileManager* mNifFileManager; 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) KeyframeManager::KeyframeManager(const VFS::Manager* vfs)
: mCache(new osgDB::ObjectCache) : ResourceManager(vfs)
, mVFS(vfs)
{ {
} }

@ -4,15 +4,7 @@
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <string> #include <string>
namespace VFS #include "resourcemanager.hpp"
{
class Manager;
}
namespace osgDB
{
class ObjectCache;
}
namespace NifOsg namespace NifOsg
{ {
@ -23,23 +15,16 @@ namespace Resource
{ {
/// @brief Managing of keyframe resources /// @brief Managing of keyframe resources
class KeyframeManager /// @note May be used from any thread.
class KeyframeManager : public ResourceManager
{ {
public: public:
KeyframeManager(const VFS::Manager* vfs); KeyframeManager(const VFS::Manager* vfs);
~KeyframeManager(); ~KeyframeManager();
void clearCache();
/// Retrieve a read-only keyframe resource by name (case-insensitive). /// 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. /// @note Throws an exception if the resource is not found.
osg::ref_ptr<const NifOsg::KeyframeHolder> get(const std::string& name); 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 "niffilemanager.hpp"
#include <osg/Object>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include "objectcache.hpp" #include "objectcache.hpp"
@ -29,9 +31,8 @@ namespace Resource
}; };
NifFileManager::NifFileManager(const VFS::Manager *vfs) NifFileManager::NifFileManager(const VFS::Manager *vfs)
: mVFS(vfs) : ResourceManager(vfs)
{ {
mCache = new osgDB::ObjectCache;
} }
NifFileManager::~NifFileManager() 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) Nif::NIFFilePtr NifFileManager::get(const std::string &name)
{ {

@ -5,39 +5,23 @@
#include <components/nif/niffile.hpp> #include <components/nif/niffile.hpp>
namespace VFS #include "resourcemanager.hpp"
{
class Manager;
}
namespace osgDB
{
class ObjectCache;
}
namespace Resource namespace Resource
{ {
/// @brief Handles caching of NIFFiles. /// @brief Handles caching of NIFFiles.
/// @note The NifFileManager is completely thread safe. /// @note May be used from any thread.
class NifFileManager class NifFileManager : public ResourceManager
{ {
public: public:
NifFileManager(const VFS::Manager* vfs); NifFileManager(const VFS::Manager* vfs);
~NifFileManager(); ~NifFileManager();
void clearCache();
/// Retrieve a NIF file from the cache, or load it from the VFS if not cached yet. /// 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 /// @note For performance reasons the NifFileManager does not handle case folding, needs
/// to be done in advance by other managers accessing the NifFileManager. /// to be done in advance by other managers accessing the NifFileManager.
Nif::NIFFilePtr get(const std::string& name); 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. * OpenSceneGraph Public License for more details.
*/ */
#include <osg/Version>
#if OSG_VERSION_LESS_THAN(3,3,3)
#include "objectcache.hpp" #include "objectcache.hpp"
using namespace osgDB; #include <osg/Object>
namespace Resource
{
//////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////
// //
@ -26,50 +25,24 @@ using namespace osgDB;
ObjectCache::ObjectCache(): ObjectCache::ObjectCache():
osg::Referenced(true) osg::Referenced(true)
{ {
// OSG_NOTICE<<"Constructed ObjectCache"<<std::endl;
} }
ObjectCache::~ObjectCache() 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) void ObjectCache::addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp)
{ {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex); OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
_objectCache[filename]=ObjectTimeStampPair(object,timestamp); _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) osg::ref_ptr<osg::Object> ObjectCache::getRefFromObjectCache(const std::string& fileName)
{ {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex); OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
ObjectCacheMap::iterator itr = _objectCache.find(fileName); ObjectCacheMap::iterator itr = _objectCache.find(fileName);
if (itr!=_objectCache.end()) if (itr!=_objectCache.end())
{ {
// OSG_NOTICE<<"Found "<<fileName<<" in ObjectCache "<<this<<std::endl;
return itr->second.first; return itr->second.first;
} }
else return 0; else return 0;
@ -95,21 +68,29 @@ void ObjectCache::updateTimeStampOfObjectsInCacheWithExternalReferences(double r
void ObjectCache::removeExpiredObjectsInCache(double expiryTime) 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) OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
{
_objectCache.erase(oitr++); // Remove expired entries from object cache
} ObjectCacheMap::iterator oitr = _objectCache.begin();
else 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) 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 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
* *
* This library is open source and may be redistributed and/or modified under * This library is open source and may be redistributed and/or modified under
@ -11,28 +14,24 @@
* OpenSceneGraph Public License for more details. * OpenSceneGraph Public License for more details.
*/ */
// Wrapper for osgDB/ObjectCache. Works around ObjectCache not being available in old OSG 3.2. #ifndef OPENMW_COMPONENTS_RESOURCE_OBJECTCACHE
// Use "#include objectcache.hpp" in place of "#include <osgDB/ObjectCache". #define OPENMW_COMPONENTS_RESOURCE_OBJECTCACHE
#ifndef OSGDB_OBJECTCACHE_WRAPPER
#define OSGDB_OBJECTCACHE_WRAPPER 1
#include <osg/Version>
#if OSG_VERSION_GREATER_OR_EQUAL(3,3,3) #include <osg/Referenced>
#include <osgDB/ObjectCache> #include <osg/ref_ptr>
#else
#include <osg/Node>
#include <osgDB/ReaderWriter>
#include <osgDB/DatabaseRevisions>
#include <string>
#include <map> #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: 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.*/ /** Remove all objects in the cache regardless of having external references or expiry times.*/
void clear(); 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.*/ /** Add a filename,object,timestamp triple to the Registry::ObjectCache.*/
void addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp = 0.0); void addEntryToObjectCache(const std::string& filename, osg::Object* object, double timestamp = 0.0);
/** Remove Object from cache.*/ /** Remove Object from cache.*/
void removeFromObjectCache(const std::string& fileName); 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*/ /** Get an ref_ptr<Object> from the object cache*/
osg::ref_ptr<osg::Object> getRefFromObjectCache(const std::string& fileName); 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); void releaseGLObjects(osg::State* state);
protected: protected:
@ -88,5 +81,3 @@ class /*OSGDB_EXPORT*/ ObjectCache : public osg::Referenced
} }
#endif #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 "resourcesystem.hpp"
#include "scenemanager.hpp" #include "scenemanager.hpp"
#include "texturemanager.hpp" #include "imagemanager.hpp"
#include "niffilemanager.hpp" #include "niffilemanager.hpp"
#include "keyframemanager.hpp" #include "keyframemanager.hpp"
@ -13,13 +13,23 @@ namespace Resource
{ {
mNifFileManager.reset(new NifFileManager(vfs)); mNifFileManager.reset(new NifFileManager(vfs));
mKeyframeManager.reset(new KeyframeManager(vfs)); mKeyframeManager.reset(new KeyframeManager(vfs));
mTextureManager.reset(new TextureManager(vfs)); mImageManager.reset(new ImageManager(vfs));
mSceneManager.reset(new SceneManager(vfs, mTextureManager.get(), mNifFileManager.get())); 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() ResourceSystem::~ResourceSystem()
{ {
// this has to be defined in the .cpp file as we can't delete incomplete types // 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() SceneManager* ResourceSystem::getSceneManager()
@ -27,9 +37,9 @@ namespace Resource
return mSceneManager.get(); return mSceneManager.get();
} }
TextureManager* ResourceSystem::getTextureManager() ImageManager* ResourceSystem::getImageManager()
{ {
return mTextureManager.get(); return mImageManager.get();
} }
NifFileManager* ResourceSystem::getNifFileManager() NifFileManager* ResourceSystem::getNifFileManager()
@ -42,9 +52,32 @@ namespace Resource
return mKeyframeManager.get(); 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 const VFS::Manager* ResourceSystem::getVFS() const

@ -2,6 +2,7 @@
#define OPENMW_COMPONENTS_RESOURCE_RESOURCESYSTEM_H #define OPENMW_COMPONENTS_RESOURCE_RESOURCESYSTEM_H
#include <memory> #include <memory>
#include <vector>
namespace VFS namespace VFS
{ {
@ -12,9 +13,10 @@ namespace Resource
{ {
class SceneManager; class SceneManager;
class TextureManager; class ImageManager;
class NifFileManager; class NifFileManager;
class KeyframeManager; class KeyframeManager;
class ResourceManager;
/// @brief Wrapper class that constructs and provides access to the most commonly used resource subsystems. /// @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 /// @par Resource subsystems can be used with multiple OpenGL contexts, just like the OSG equivalents, but
@ -26,21 +28,37 @@ namespace Resource
~ResourceSystem(); ~ResourceSystem();
SceneManager* getSceneManager(); SceneManager* getSceneManager();
TextureManager* getTextureManager(); ImageManager* getImageManager();
NifFileManager* getNifFileManager(); NifFileManager* getNifFileManager();
KeyframeManager* getKeyframeManager(); KeyframeManager* getKeyframeManager();
/// Indicates to each resource manager to clear the cache, i.e. to drop cached objects that are no longer referenced. /// 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; const VFS::Manager* getVFS() const;
private: private:
std::auto_ptr<SceneManager> mSceneManager; std::auto_ptr<SceneManager> mSceneManager;
std::auto_ptr<TextureManager> mTextureManager; std::auto_ptr<ImageManager> mImageManager;
std::auto_ptr<NifFileManager> mNifFileManager; std::auto_ptr<NifFileManager> mNifFileManager;
std::auto_ptr<KeyframeManager> mKeyframeManager; 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; const VFS::Manager* mVFS;
ResourceSystem(const ResourceSystem&); ResourceSystem(const ResourceSystem&);

@ -1,14 +1,18 @@
#include "scenemanager.hpp" #include "scenemanager.hpp"
#include <iostream>
#include <osg/Node> #include <osg/Node>
#include <osg/Geode> #include <osg/Geode>
#include <osg/UserDataContainer> #include <osg/UserDataContainer>
#include <osg/Version> #include <osg/Version>
#include <osgParticle/ParticleSystem> #include <osgParticle/ParticleSystem>
#include <osgFX/Effect>
#include <osgUtil/IncrementalCompileOperation> #include <osgUtil/IncrementalCompileOperation>
#include <osgViewer/Viewer>
#include <osgDB/SharedStateManager> #include <osgDB/SharedStateManager>
#include <osgDB/Registry> #include <osgDB/Registry>
@ -19,13 +23,17 @@
#include <components/sceneutil/clone.hpp> #include <components/sceneutil/clone.hpp>
#include <components/sceneutil/util.hpp> #include <components/sceneutil/util.hpp>
#include <components/sceneutil/controller.hpp>
#include "texturemanager.hpp" #include "imagemanager.hpp"
#include "niffilemanager.hpp" #include "niffilemanager.hpp"
#include "objectcache.hpp"
#include "multiobjectcache.hpp"
namespace namespace
{ {
/// @todo Do this in updateCallback so that animations are accounted for.
class InitWorldSpaceParticlesVisitor : public osg::NodeVisitor class InitWorldSpaceParticlesVisitor : public osg::NodeVisitor
{ {
public: public:
@ -105,10 +113,134 @@ namespace
namespace Resource namespace Resource
{ {
SceneManager::SceneManager(const VFS::Manager *vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager) /// Set texture filtering settings on textures contained in a FlipController.
: mVFS(vfs) class SetFilterSettingsControllerVisitor : public SceneUtil::ControllerVisitor
, mTextureManager(textureManager) {
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) , mNifFileManager(nifFileManager)
, mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR)
, mMagFilter(osg::Texture::LINEAR)
, mMaxAnisotropy(1)
, mUnRefImageDataAfterApply(false)
, mParticleSystemMask(~0u) , mParticleSystemMask(~0u)
{ {
} }
@ -116,15 +248,14 @@ namespace Resource
SceneManager::~SceneManager() SceneManager::~SceneManager()
{ {
// this has to be defined in the .cpp file as we can't delete incomplete types // 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. /// @brief Callback to read image files from the VFS.
class ImageReadCallback : public osgDB::ReadFileCallback class ImageReadCallback : public osgDB::ReadFileCallback
{ {
public: public:
ImageReadCallback(Resource::TextureManager* textureMgr) ImageReadCallback(Resource::ImageManager* imageMgr)
: mTextureManager(textureMgr) : mImageManager(imageMgr)
{ {
} }
@ -132,7 +263,7 @@ namespace Resource
{ {
try 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) catch (std::exception& e)
{ {
@ -141,7 +272,7 @@ namespace Resource
} }
private: private:
Resource::TextureManager* mTextureManager; Resource::ImageManager* mImageManager;
}; };
std::string getFileExtension(const std::string& file) std::string getFileExtension(const std::string& file)
@ -152,11 +283,11 @@ namespace Resource
return std::string(); 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); std::string ext = getFileExtension(normalizedFilename);
if (ext == "nif") if (ext == "nif")
return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), textureMgr); return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), imageManager);
else else
{ {
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); 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. // 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. // 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. // 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); osgDB::ReaderWriter::ReadResult result = reader->readNode(*file, options);
if (!result.success()) if (!result.success())
@ -189,15 +320,17 @@ namespace Resource
std::string normalized = name; std::string normalized = name;
mVFS->normalizeFilename(normalized); mVFS->normalizeFilename(normalized);
Index::iterator it = mIndex.find(normalized); osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);
if (it == mIndex.end()) if (obj)
return osg::ref_ptr<const osg::Node>(static_cast<osg::Node*>(obj.get()));
else
{ {
osg::ref_ptr<osg::Node> loaded; osg::ref_ptr<osg::Node> loaded;
try try
{ {
Files::IStreamPtr file = mVFS->get(normalized); Files::IStreamPtr file = mVFS->get(normalized);
loaded = load(file, normalized, mTextureManager, mNifFileManager); loaded = load(file, normalized, mImageManager, mNifFileManager);
} }
catch (std::exception& e) 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; std::cerr << "Failed to load '" << name << "': " << e.what() << ", using marker_error." << sMeshTypes[i] << " instead" << std::endl;
Files::IStreamPtr file = mVFS->get(normalized); Files::IStreamPtr file = mVFS->get(normalized);
loaded = load(file, normalized, mTextureManager, mNifFileManager); loaded = load(file, normalized, mImageManager, mNifFileManager);
break; break;
} }
} }
@ -219,28 +352,62 @@ namespace Resource
throw; 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()); osgDB::Registry::instance()->getOrCreateSharedStateManager()->share(loaded.get());
mSharedStateMutex.unlock();
if (mIncrementalCompileOperation) if (mIncrementalCompileOperation)
mIncrementalCompileOperation->add(loaded); mIncrementalCompileOperation->add(loaded);
mIndex[normalized] = loaded; mCache->addEntryToObjectCache(normalized, loaded);
return 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<const osg::Node> scene = getTemplate(name);
osg::ref_ptr<osg::Node> cloned = osg::clone(scene.get(), SceneUtil::CopyOp()); 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; 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); attachTo(cloned, parentNode);
return cloned; return cloned;
} }
@ -253,10 +420,7 @@ namespace Resource
void SceneManager::releaseGLObjects(osg::State *state) void SceneManager::releaseGLObjects(osg::State *state)
{ {
for (Index::iterator it = mIndex.begin(); it != mIndex.end(); ++it) mCache->releaseGLObjects(state);
{
it->second->releaseGLObjects(state);
}
} }
void SceneManager::setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation *ico) void SceneManager::setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation *ico)
@ -270,19 +434,86 @@ namespace Resource
node->accept(visitor); 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/ref_ptr>
#include <osg/Node> #include <osg/Node>
#include <osg/Texture>
#include "resourcemanager.hpp"
namespace Resource namespace Resource
{ {
class TextureManager; class ImageManager;
class NifFileManager; class NifFileManager;
} }
namespace VFS namespace osgUtil
{ {
class Manager; class IncrementalCompileOperation;
} }
namespace osgUtil namespace osgViewer
{ {
class IncrementalCompileOperation; class Viewer;
} }
namespace Resource namespace Resource
{ {
/// @brief Handles loading and caching of scenes, e.g. NIF files class MultiObjectCache;
class SceneManager
/// @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: public:
SceneManager(const VFS::Manager* vfs, Resource::TextureManager* textureManager, Resource::NifFileManager* nifFileManager); SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager);
~SceneManager(); ~SceneManager();
/// Get a read-only copy of this scene "template" /// 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. /// @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. /// 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); 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 /// @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 /// @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 /// Attach the given scene instance to the given parent node
/// @note You should have the parentNode in its intended position before calling this method, /// @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. /// 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 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; void attachTo(osg::Node* instance, osg::Group* parentNode) const;
/// Manually release created OpenGL objects for the given graphics context. This may be required /// 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 /// @note SceneManager::attachTo calls this method automatically, only needs to be called by users if manually attaching
void notifyAttached(osg::Node* node) const; void notifyAttached(osg::Node* node) const;
const VFS::Manager* getVFS() const; Resource::ImageManager* getImageManager();
Resource::TextureManager* getTextureManager();
/// @param mask The node mask to apply to loaded particle system nodes. /// @param mask The node mask to apply to loaded particle system nodes.
void setParticleSystemMask(unsigned int mask); 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: 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; Resource::NifFileManager* mNifFileManager;
osg::Texture::FilterMode mMinFilter;
osg::Texture::FilterMode mMagFilter;
int mMaxAnisotropy;
bool mUnRefImageDataAfterApply;
osg::ref_ptr<osgUtil::IncrementalCompileOperation> mIncrementalCompileOperation; osg::ref_ptr<osgUtil::IncrementalCompileOperation> mIncrementalCompileOperation;
unsigned int mParticleSystemMask; unsigned int mParticleSystemMask;
// observer_ptr?
typedef std::map<std::string, osg::ref_ptr<const osg::Node> > Index;
Index mIndex;
SceneManager(const SceneManager&); SceneManager(const SceneManager&);
void operator = (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 "workqueue.hpp"
#include <iostream>
namespace SceneUtil namespace SceneUtil
{ {
void WorkTicket::waitTillDone() void WorkItem::waitTillDone()
{ {
if (mDone > 0) if (mDone > 0)
return; return;
@ -15,7 +17,7 @@ void WorkTicket::waitTillDone()
} }
} }
void WorkTicket::signalDone() void WorkItem::signalDone()
{ {
{ {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex); OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
@ -25,23 +27,16 @@ void WorkTicket::signalDone()
} }
WorkItem::WorkItem() WorkItem::WorkItem()
: mTicket(new WorkTicket)
{ {
mTicket->setThreadSafeRefUnref(true);
} }
WorkItem::~WorkItem() WorkItem::~WorkItem()
{ {
} }
void WorkItem::doWork() bool WorkItem::isDone() const
{
mTicket->signalDone();
}
osg::ref_ptr<WorkTicket> WorkItem::getTicket()
{ {
return mTicket; return (mDone > 0);
} }
WorkQueue::WorkQueue(int workerThreads) WorkQueue::WorkQueue(int workerThreads)
@ -60,11 +55,7 @@ WorkQueue::~WorkQueue()
{ {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex); OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
while (!mQueue.empty()) while (!mQueue.empty())
{
WorkItem* item = mQueue.front();
delete item;
mQueue.pop(); mQueue.pop();
}
mIsReleased = true; mIsReleased = true;
mCondition.broadcast(); 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); OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
mQueue.push(item); mQueue.push(item);
mCondition.signal(); mCondition.signal();
return ticket;
} }
WorkItem *WorkQueue::removeWorkItem() osg::ref_ptr<WorkItem> WorkQueue::removeWorkItem()
{ {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex); OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
while (mQueue.empty() && !mIsReleased) while (mQueue.empty() && !mIsReleased)
@ -94,7 +89,7 @@ WorkItem *WorkQueue::removeWorkItem()
} }
if (mQueue.size()) if (mQueue.size())
{ {
WorkItem* item = mQueue.front(); osg::ref_ptr<WorkItem> item = mQueue.front();
mQueue.pop(); mQueue.pop();
return item; return item;
} }
@ -111,11 +106,11 @@ void WorkThread::run()
{ {
while (true) while (true)
{ {
WorkItem* item = mWorkQueue->removeWorkItem(); osg::ref_ptr<WorkItem> item = mWorkQueue->removeWorkItem();
if (!item) if (!item)
return; return;
item->doWork(); item->doWork();
delete item; item->signalDone();
} }
} }

@ -14,69 +14,52 @@
namespace SceneUtil namespace SceneUtil
{ {
class WorkTicket : public osg::Referenced class WorkItem : public osg::Referenced
{
public:
void waitTillDone();
void signalDone();
private:
OpenThreads::Atomic mDone;
OpenThreads::Mutex mMutex;
OpenThreads::Condition mCondition;
};
class WorkItem
{ {
public: public:
WorkItem(); WorkItem();
virtual ~WorkItem(); virtual ~WorkItem();
/// Override in a derived WorkItem to perform actual work. /// Override in a derived WorkItem to perform actual work.
/// By default, just signals the ticket that the work is done. virtual void doWork() {}
virtual void doWork();
osg::ref_ptr<WorkTicket> getTicket();
protected:
osg::ref_ptr<WorkTicket> mTicket;
};
class WorkQueue; bool isDone() const;
class WorkThread : public OpenThreads::Thread /// Wait until the work is completed. Usually called from the main thread.
{ void waitTillDone();
public:
WorkThread(WorkQueue* workQueue);
virtual void run(); /// Internal use by the WorkQueue.
void signalDone();
private: protected:
WorkQueue* mWorkQueue; 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. /// @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: public:
WorkQueue(int numWorkerThreads=1); WorkQueue(int numWorkerThreads=1);
~WorkQueue(); ~WorkQueue();
/// Add a new work item to the back of the queue. /// 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. /// @par The work item's waitTillDone() method may be used by the caller to wait until the work is complete.
osg::ref_ptr<WorkTicket> addWorkItem(WorkItem* item); 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. /// 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. /// If the workqueue is in the process of being destroyed, may return NULL.
/// @note The caller must free the returned WorkItem /// @par Used internally by the WorkThread.
WorkItem* removeWorkItem(); osg::ref_ptr<WorkItem> removeWorkItem();
void runThread();
private: private:
bool mIsReleased; bool mIsReleased;
std::queue<WorkItem*> mQueue; std::queue<osg::ref_ptr<WorkItem> > mQueue;
OpenThreads::Mutex mMutex; OpenThreads::Mutex mMutex;
OpenThreads::Condition mCondition; OpenThreads::Condition mCondition;
@ -84,6 +67,17 @@ namespace SceneUtil
std::vector<WorkThread*> mThreads; 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; return;
} }
SDL_Surface* surf = SDLUtil::imageToSurface(decompressed, false); SDL_Surface* surf = SDLUtil::imageToSurface(decompressed, true);
//set the cursor and store it for later //set the cursor and store it for later
SDL_Cursor* curs = SDL_CreateColorCursor(surf, hotspot_x, hotspot_y); SDL_Cursor* curs = SDL_CreateColorCursor(surf, hotspot_x, hotspot_y);

@ -2,6 +2,8 @@
#include <cassert> #include <cassert>
#include <OpenThreads/ScopedLock>
#include <osg/PrimitiveSet> #include <osg/PrimitiveSet>
#include "defs.hpp" #include "defs.hpp"
@ -178,6 +180,7 @@ namespace Terrain
osg::ref_ptr<osg::Vec2Array> BufferCache::getUVBuffer() osg::ref_ptr<osg::Vec2Array> BufferCache::getUVBuffer()
{ {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mUvBufferMutex);
if (mUvBufferMap.find(mNumVerts) != mUvBufferMap.end()) if (mUvBufferMap.find(mNumVerts) != mUvBufferMap.end())
{ {
return mUvBufferMap[mNumVerts]; return mUvBufferMap[mNumVerts];
@ -193,7 +196,7 @@ namespace Terrain
for (unsigned int row = 0; row < mNumVerts; ++row) for (unsigned int row = 0; row < mNumVerts; ++row)
{ {
uvs->push_back(osg::Vec2f(col / static_cast<float>(mNumVerts-1), 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) osg::ref_ptr<osg::DrawElements> BufferCache::getIndexBuffer(unsigned int flags)
{ {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mIndexBufferMutex);
unsigned int verts = mNumVerts; unsigned int verts = mNumVerts;
if (mIndexBufferMap.find(flags) != mIndexBufferMap.end()) 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) /// @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) /// 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); osg::ref_ptr<osg::DrawElements> getIndexBuffer (unsigned int flags);
/// @note Thread safe.
osg::ref_ptr<osg::Vec2Array> getUVBuffer(); osg::ref_ptr<osg::Vec2Array> getUVBuffer();
// TODO: add releaseGLObjects() for our vertex/element buffer objects // 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 // 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. // combination of LOD deltas and index buffer LOD we may need.
std::map<int, osg::ref_ptr<osg::DrawElements> > mIndexBufferMap; std::map<int, osg::ref_ptr<osg::DrawElements> > mIndexBufferMap;
OpenThreads::Mutex mIndexBufferMutex;
std::map<int, osg::ref_ptr<osg::Vec2Array> > mUvBufferMap; std::map<int, osg::ref_ptr<osg::Vec2Array> > mUvBufferMap;
OpenThreads::Mutex mUvBufferMutex;
unsigned int mNumVerts; unsigned int mNumVerts;
}; };

@ -2,11 +2,17 @@
#include <memory> #include <memory>
#include <osg/UserDataContainer>
#include <OpenThreads/ScopedLock>
#include <components/resource/resourcesystem.hpp> #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/lightmanager.hpp>
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/unrefqueue.hpp>
#include <components/esm/loadland.hpp> #include <components/esm/loadland.hpp>
@ -45,13 +51,12 @@ namespace
namespace Terrain namespace Terrain
{ {
TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, SceneUtil::UnrefQueue* unrefQueue)
Storage* storage, int nodeMask)
: Terrain::World(parent, resourceSystem, ico, storage, nodeMask) : Terrain::World(parent, resourceSystem, ico, storage, nodeMask)
, mNumSplits(4) , 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() 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) 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 // For compiling textures, I don't think the osgFX::Effect does it correctly
osg::ref_ptr<osg::Node> textureCompileDummy (new osg::Node); osg::ref_ptr<osg::Node> textureCompileDummy (new osg::Node);
unsigned int dummyTextureCounter = 0; unsigned int dummyTextureCounter = 0;
std::vector<osg::ref_ptr<osg::Texture2D> > layerTextures; 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)); OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mTextureCacheMutex);
textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, layerTextures.back()); 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; 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->setImage(*it);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture::WRAP_T, 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->setResizeNonPowerOfTwoHint(false);
texture->getOrCreateUserDataContainer()->addDescription("dont_override_filter");
blendmapTextures.push_back(texture); blendmapTextures.push_back(texture);
textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, blendmapTextures.back()); 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()) if (mGrid.find(std::make_pair(x, y)) != mGrid.end())
return; // already loaded 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) if (!terrainNode)
return; // no terrain defined {
osg::Vec2f center(x+0.5f, y+0.5f);
std::auto_ptr<GridElement> element (new GridElement); terrainNode = buildTerrain(NULL, 1.f, center);
element->mNode = terrainNode; if (!terrainNode)
mTerrainRoot->addChild(element->mNode); return; // no terrain defined
}
// kdtree probably not needed with mNumSplits=4 mTerrainRoot->addChild(terrainNode);
/*
// 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);
*/
mGrid[std::make_pair(x,y)] = element.release(); mGrid[std::make_pair(x,y)] = terrainNode;
} }
void TerrainGrid::unloadCell(int x, int y) void TerrainGrid::unloadCell(int x, int y)
@ -218,11 +251,38 @@ void TerrainGrid::unloadCell(int x, int y)
if (it == mGrid.end()) if (it == mGrid.end())
return; return;
GridElement* element = it->second; osg::ref_ptr<osg::Node> terrainNode = it->second;
mTerrainRoot->removeChild(element->mNode); mTerrainRoot->removeChild(terrainNode);
delete element;
if (mUnrefQueue.get())
mUnrefQueue->push(terrainNode);
mGrid.erase(it); 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 "world.hpp"
#include "material.hpp" #include "material.hpp"
namespace osg namespace SceneUtil
{ {
class KdTreeBuilder; class UnrefQueue;
} }
namespace Terrain namespace Terrain
{ {
class GridElement;
/// @brief Simple terrain implementation that loads cells in a grid, with no LOD /// @brief Simple terrain implementation that loads cells in a grid, with no LOD
class TerrainGrid : public Terrain::World class TerrainGrid : public Terrain::World
{ {
public: public:
TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, SceneUtil::UnrefQueue* unrefQueue = NULL);
Storage* storage, int nodeMask);
~TerrainGrid(); ~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); virtual void loadCell(int x, int y);
/// @note Not thread safe.
virtual void unloadCell(int x, int y); virtual void unloadCell(int x, int y);
/// Clear cached objects that are no longer referenced
/// @note Thread safe.
void updateCache();
private: private:
osg::ref_ptr<osg::Node> buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter); osg::ref_ptr<osg::Node> buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter);
// split each ESM::Cell into mNumSplits*mNumSplits terrain chunks // split each ESM::Cell into mNumSplits*mNumSplits terrain chunks
unsigned int mNumSplits; 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; 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