forked from mirror/openmw-tes3mp
Merge remote-tracking branch 'scrawl/resource'
This commit is contained in:
commit
da6dcfc49e
105 changed files with 2523 additions and 1045 deletions
|
@ -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];
|
const std::vector<const ESM::BodyPart*> &parts = getBodyParts(race, !mNpc->isMale(), mViewMode == VM_FirstPerson, isWerewolf);
|
||||||
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];
|
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
230
apps/openmw/mwworld/cellpreloader.cpp
Normal file
230
apps/openmw/mwworld/cellpreloader.cpp
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
72
apps/openmw/mwworld/cellpreloader.hpp
Normal file
72
apps/openmw/mwworld/cellpreloader.hpp
Normal file
|
@ -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 ©, const osg::CopyOp ©op=osg::CopyOp::SHALLOW_COPY)
|
Drawable(const Drawable ©, const osg::CopyOp ©op=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 ©, const osg::CopyOp ©op)
|
||||||
|
: 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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
148
components/resource/imagemanager.cpp
Normal file
148
components/resource/imagemanager.cpp
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
components/resource/imagemanager.hpp
Normal file
50
components/resource/imagemanager.hpp
Normal file
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
79
components/resource/multiobjectcache.cpp
Normal file
79
components/resource/multiobjectcache.cpp
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
47
components/resource/multiobjectcache.hpp
Normal file
47
components/resource/multiobjectcache.hpp
Normal file
|
@ -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,6 +68,9 @@ void ObjectCache::updateTimeStampOfObjectsInCacheWithExternalReferences(double r
|
||||||
|
|
||||||
void ObjectCache::removeExpiredObjectsInCache(double expiryTime)
|
void ObjectCache::removeExpiredObjectsInCache(double expiryTime)
|
||||||
{
|
{
|
||||||
|
std::vector<osg::ref_ptr<osg::Object> > objectsToRemove;
|
||||||
|
|
||||||
|
{
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
|
||||||
|
|
||||||
// Remove expired entries from object cache
|
// Remove expired entries from object cache
|
||||||
|
@ -103,6 +79,7 @@ void ObjectCache::removeExpiredObjectsInCache(double expiryTime)
|
||||||
{
|
{
|
||||||
if (oitr->second.second<=expiryTime)
|
if (oitr->second.second<=expiryTime)
|
||||||
{
|
{
|
||||||
|
objectsToRemove.push_back(oitr->second.first);
|
||||||
_objectCache.erase(oitr++);
|
_objectCache.erase(oitr++);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -110,6 +87,10 @@ void ObjectCache::removeExpiredObjectsInCache(double expiryTime)
|
||||||
++oitr;
|
++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
|
#include <osg/Referenced>
|
||||||
#define OSGDB_OBJECTCACHE_WRAPPER 1
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
#include <osg/Version>
|
|
||||||
|
|
||||||
#if OSG_VERSION_GREATER_OR_EQUAL(3,3,3)
|
|
||||||
#include <osgDB/ObjectCache>
|
|
||||||
#else
|
|
||||||
|
|
||||||
#include <osg/Node>
|
|
||||||
|
|
||||||
#include <osgDB/ReaderWriter>
|
|
||||||
#include <osgDB/DatabaseRevisions>
|
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
namespace osgDB {
|
namespace osg
|
||||||
|
{
|
||||||
|
class Object;
|
||||||
|
class State;
|
||||||
|
}
|
||||||
|
|
||||||
class /*OSGDB_EXPORT*/ ObjectCache : public osg::Referenced
|
namespace Resource {
|
||||||
|
|
||||||
|
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
|
|
||||||
|
|
32
components/resource/resourcemanager.cpp
Normal file
32
components/resource/resourcemanager.cpp
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
components/resource/resourcemanager.hpp
Normal file
38
components/resource/resourcemanager.hpp
Normal file
|
@ -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)
|
||||||
{
|
{
|
||||||
mNifFileManager->clearCache();
|
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)
|
||||||
|
{
|
||||||
|
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,14 +434,9 @@ 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()
|
|
||||||
{
|
|
||||||
return mTextureManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::setParticleSystemMask(unsigned int mask)
|
void SceneManager::setParticleSystemMask(unsigned int mask)
|
||||||
|
@ -285,4 +444,76 @@ namespace Resource
|
||||||
mParticleSystemMask = mask;
|
mParticleSystemMask = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SceneManager::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();
|
||||||
|
|
||||||
|
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
|
|
||||||
{
|
|
||||||
class Manager;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace osgUtil
|
namespace osgUtil
|
||||||
{
|
{
|
||||||
class IncrementalCompileOperation;
|
class IncrementalCompileOperation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace osgViewer
|
||||||
|
{
|
||||||
|
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
|
||||||
/// @see getTemplate
|
/// return this cached object instead of creating a new one.
|
||||||
osg::ref_ptr<osg::Node> createInstance(const std::string& name);
|
/// @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);
|
||||||
|
|
||||||
/// Create an instance of the given scene template and immediately attach it to a parent node
|
/// Get an instance of the given scene template
|
||||||
/// @see getTemplate
|
/// @see getTemplate
|
||||||
osg::ref_ptr<osg::Node> createInstance(const std::string& name, osg::Group* parentNode);
|
/// @note Thread safe.
|
||||||
|
osg::ref_ptr<osg::Node> getInstance(const std::string& name);
|
||||||
|
|
||||||
|
/// Get an instance of the given scene template and immediately attach it to a parent node
|
||||||
|
/// @see getTemplate
|
||||||
|
/// @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
|
|
48
components/sceneutil/unrefqueue.cpp
Normal file
48
components/sceneutil/unrefqueue.cpp
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
components/sceneutil/unrefqueue.hpp
Normal file
37
components/sceneutil/unrefqueue.hpp
Normal file
|
@ -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();
|
return (mDone > 0);
|
||||||
}
|
|
||||||
|
|
||||||
osg::ref_ptr<WorkTicket> WorkItem::getTicket()
|
|
||||||
{
|
|
||||||
return mTicket;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,37 +14,60 @@
|
||||||
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();
|
bool isDone() const;
|
||||||
|
|
||||||
|
/// Wait until the work is completed. Usually called from the main thread.
|
||||||
|
void waitTillDone();
|
||||||
|
|
||||||
|
/// Internal use by the WorkQueue.
|
||||||
|
void signalDone();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
osg::ref_ptr<WorkTicket> mTicket;
|
OpenThreads::Atomic mDone;
|
||||||
|
OpenThreads::Mutex mMutex;
|
||||||
|
OpenThreads::Condition mCondition;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WorkQueue;
|
class WorkThread;
|
||||||
|
|
||||||
|
/// @brief A work queue that users can push work items onto, to be completed by one or more background threads.
|
||||||
|
/// @note Work items will be processed in the order that they were given in, however
|
||||||
|
/// if multiple work threads are involved then it is possible for a later item to complete before earlier items.
|
||||||
|
class WorkQueue : public osg::Referenced
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WorkQueue(int numWorkerThreads=1);
|
||||||
|
~WorkQueue();
|
||||||
|
|
||||||
|
/// Add a new work item to the back of the queue.
|
||||||
|
/// @par The work item's waitTillDone() method may be used by the caller to wait until the work is complete.
|
||||||
|
void addWorkItem(osg::ref_ptr<WorkItem> item);
|
||||||
|
|
||||||
|
/// Get the next work item from the front of the queue. If the queue is empty, waits until a new item is added.
|
||||||
|
/// If the workqueue is in the process of being destroyed, may return NULL.
|
||||||
|
/// @par Used internally by the WorkThread.
|
||||||
|
osg::ref_ptr<WorkItem> removeWorkItem();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mIsReleased;
|
||||||
|
std::queue<osg::ref_ptr<WorkItem> > mQueue;
|
||||||
|
|
||||||
|
OpenThreads::Mutex mMutex;
|
||||||
|
OpenThreads::Condition mCondition;
|
||||||
|
|
||||||
|
std::vector<WorkThread*> mThreads;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Internally used by WorkQueue.
|
||||||
class WorkThread : public OpenThreads::Thread
|
class WorkThread : public OpenThreads::Thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -56,35 +79,6 @@ namespace SceneUtil
|
||||||
WorkQueue* mWorkQueue;
|
WorkQueue* mWorkQueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief A work queue that users can push work items onto, to be completed by one or more background threads.
|
|
||||||
class WorkQueue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
WorkQueue(int numWorkerThreads=1);
|
|
||||||
~WorkQueue();
|
|
||||||
|
|
||||||
/// Add a new work item to the back of the queue.
|
|
||||||
/// @par The returned WorkTicket may be used by the caller to wait until the work is complete.
|
|
||||||
osg::ref_ptr<WorkTicket> addWorkItem(WorkItem* item);
|
|
||||||
|
|
||||||
/// Get the next work item from the front of the queue. If the queue is empty, waits until a new item is added.
|
|
||||||
/// If the workqueue is in the process of being destroyed, may return NULL.
|
|
||||||
/// @note The caller must free the returned WorkItem
|
|
||||||
WorkItem* removeWorkItem();
|
|
||||||
|
|
||||||
void runThread();
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool mIsReleased;
|
|
||||||
std::queue<WorkItem*> mQueue;
|
|
||||||
|
|
||||||
OpenThreads::Mutex mMutex;
|
|
||||||
OpenThreads::Condition mCondition;
|
|
||||||
|
|
||||||
std::vector<WorkThread*> mThreads;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,12 +148,25 @@ 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;
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mTextureCacheMutex);
|
||||||
for (std::vector<LayerInfo>::const_iterator it = layerList.begin(); it != layerList.end(); ++it)
|
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));
|
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());
|
textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, layerTextures.back());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<osg::ref_ptr<osg::Texture2D> > blendmapTextures;
|
std::vector<osg::ref_ptr<osg::Texture2D> > blendmapTextures;
|
||||||
for (std::vector<osg::ref_ptr<osg::Image> >::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it)
|
for (std::vector<osg::ref_ptr<osg::Image> >::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it)
|
||||||
|
@ -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;
|
||||||
osg::ref_ptr<osg::Node> terrainNode = buildTerrain(NULL, 1.f, center);
|
{
|
||||||
|
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)
|
if (!terrainNode)
|
||||||
return; // no terrain defined
|
return; // no terrain defined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::auto_ptr<GridElement> element (new GridElement);
|
// didn't find in cache, build it
|
||||||
element->mNode = terrainNode;
|
if (!terrainNode)
|
||||||
mTerrainRoot->addChild(element->mNode);
|
{
|
||||||
|
osg::Vec2f center(x+0.5f, y+0.5f);
|
||||||
|
terrainNode = buildTerrain(NULL, 1.f, center);
|
||||||
|
if (!terrainNode)
|
||||||
|
return; // no terrain defined
|
||||||
|
}
|
||||||
|
|
||||||
// kdtree probably not needed with mNumSplits=4
|
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…
Reference in a new issue