diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index d88da9865..c87e24e77 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -16,6 +16,8 @@ #include +#include + #include "model/settings/usersettings.hpp" #include "model/doc/documentmanager.hpp" @@ -37,6 +39,7 @@ namespace CS { Q_OBJECT + Nif::Cache mNifCache; Files::ConfigurationManager mCfgMgr; CSMSettings::UserSettings mUserSettings; CSMDoc::DocumentManager mDocumentManager; diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2e5689fa8..21d0a2511 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -315,8 +314,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mEnvironment.setStateManager ( new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0))); - Nif::NIFFile::CacheLock cachelock; - std::string renderSystem = settings.getString("render system", "Video"); if (renderSystem == "") { diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 10c1f6ff2..e5f3e5f99 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -7,6 +7,8 @@ #include #include #include +#include + #include "mwbase/environment.hpp" @@ -94,6 +96,8 @@ namespace OMW std::vector mScriptBlacklist; bool mScriptBlacklistUse; + Nif::Cache mNifCache; + // not implemented Engine (const Engine&); Engine& operator= (const Engine&); diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index a35415e75..84b5ca6ea 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -37,10 +37,11 @@ namespace MWGui mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked); mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteButtonClicked); - mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); + mCharacterSelection->eventComboAccept += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick); mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated); + mSaveList->eventKeyButtonPressed += MyGUI::newDelegate(this, &SaveGameDialog::onKeyButtonPressed); mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept); mSaveNameEdit->eventEditTextChange += MyGUI::newDelegate(this, &SaveGameDialog::onSaveNameChanged); } @@ -247,6 +248,12 @@ namespace MWGui } } + void SaveGameDialog::onKeyButtonPressed(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char character) + { + if (key == MyGUI::KeyCode::Delete && mCurrentSlot) + confirmDeleteSave(); + } + void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender) { accept(); diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index 80cfad279..11470a20f 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -26,6 +26,7 @@ namespace MWGui private: void confirmDeleteSave(); + void onKeyButtonPressed(MyGUI::Widget* _sender, MyGUI::KeyCode key, MyGUI::Char character); void onCancelButtonClicked (MyGUI::Widget* sender); void onOkButtonClicked (MyGUI::Widget* sender); void onDeleteButtonClicked (MyGUI::Widget* sender); diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index fb24946a5..f830d09d5 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -32,6 +32,15 @@ MWMechanics::AiFollow::AiFollow(const std::string &actorId, bool commanded) { } +MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) + : mAlwaysFollow(follow->mAlwaysFollow), mRemainingDuration(follow->mRemainingDuration) + , mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ) + , mActorRefId(follow->mTargetId), mActorId(-1), mCellId(follow->mCellId) + , mCommanded(follow->mCommanded) +{ + +} + bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { MWWorld::Ptr target = getTarget(); @@ -131,15 +140,6 @@ void MWMechanics::AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) co sequence.mPackages.push_back(package); } -MWMechanics::AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) - : mAlwaysFollow(follow->mAlwaysFollow), mRemainingDuration(follow->mRemainingDuration) - , mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ) - , mActorRefId(follow->mTargetId), mCellId(follow->mCellId) - , mCommanded(follow->mCommanded) -{ - -} - MWWorld::Ptr MWMechanics::AiFollow::getTarget() { if (mActorId == -2) diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 2995a8c36..c41c639ea 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -32,12 +32,18 @@ AiPursue *MWMechanics::AiPursue::clone() const } bool AiPursue::execute (const MWWorld::Ptr& actor, float duration) { + if(actor.getClass().getCreatureStats(actor).isDead()) + return true; + ESM::Position pos = actor.getRefData().getPosition(); //position of the actor const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); //The target to follow if(target == MWWorld::Ptr()) return true; //Target doesn't exist + if(target.getClass().getCreatureStats(target).isDead()) + return true; + actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); //Set the target desition from the actor diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 641bd3f6b..a43b73d91 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1663,6 +1663,9 @@ void CharacterController::updateVisibility() } mAnimation->setAlpha(alpha); + + float light = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Light).getMagnitude(); + mAnimation->setLightEffect(light); } void CharacterController::determineAttackType() @@ -1671,9 +1674,9 @@ void CharacterController::determineAttackType() if(mPtr.getClass().hasInventoryStore(mPtr)) { - if (move[1]) // forward-backward + if (move[1] && !move[0]) // forward-backward mAttackType = "thrust"; - else if (move[0]) //sideway + else if (move[0] && !move[1]) //sideway mAttackType = "slash"; else mAttackType = "chop"; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 0547051a7..ab181ca77 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -71,6 +71,7 @@ Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node) , mNonAccumCtrl(NULL) , mAccumulate(0.0f) , mNullAnimationTimePtr(OGRE_NEW NullAnimationTime) + , mGlowLight(NULL) { for(size_t i = 0;i < sNumGroups;i++) mAnimationTimePtr[i].bind(OGRE_NEW AnimationTime(this)); @@ -78,6 +79,8 @@ Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node) Animation::~Animation() { + setLightEffect(0); + mEffects.clear(); mAnimSources.clear(); @@ -110,6 +113,11 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) : NifOgre::Loader::createObjectBase(mInsert, mdlname)); + + // Fast forward auto-play particles, which will have been set up as Emitting by the loader. + for (unsigned int i=0; imParticles.size(); ++i) + mObjectRoot->mParticles[i]->fastForward(1, 0.1); + if(mObjectRoot->mSkelBase) { mSkelBase = mObjectRoot->mSkelBase; @@ -1194,8 +1202,9 @@ bool Animation::allowSwitchViewMode() const { for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter) { - if(stateiter->second.mPriority > MWMechanics::Priority_Movement + if((stateiter->second.mPriority > MWMechanics::Priority_Movement && stateiter->second.mPriority < MWMechanics::Priority_Torch) + || stateiter->second.mPriority == MWMechanics::Priority_Death) return false; } return true; @@ -1391,6 +1400,37 @@ Ogre::Vector3 Animation::getEnchantmentColor(MWWorld::Ptr item) return result; } +void Animation::setLightEffect(float effect) +{ + if (effect == 0) + { + if (mGlowLight) + { + mInsert->getCreator()->destroySceneNode(mGlowLight->getParentSceneNode()); + mInsert->getCreator()->destroyLight(mGlowLight); + mGlowLight = NULL; + } + } + else + { + if (!mGlowLight) + { + mGlowLight = mInsert->getCreator()->createLight(); + + Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; + for(size_t i = 0;i < mObjectRoot->mEntities.size();i++) + { + Ogre::Entity *ent = mObjectRoot->mEntities[i]; + bounds.merge(ent->getBoundingBox()); + } + mInsert->createChildSceneNode(bounds.getCenter())->attachObject(mGlowLight); + } + mGlowLight->setType(Ogre::Light::LT_POINT); + effect += 3; + mGlowLight->setAttenuation(1.0f / (0.03 * (0.5/effect)), 0, 0.5/effect, 0); + } +} + ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model) : Animation(ptr, ptr.getRefData().getBaseNode()) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index dc4244c39..b85e74549 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -126,6 +126,8 @@ protected: MWWorld::Ptr mPtr; + Ogre::Light* mGlowLight; + Ogre::SceneNode *mInsert; Ogre::Entity *mSkelBase; NifOgre::ObjectScenePtr mObjectRoot; @@ -301,6 +303,11 @@ public: /// This is typically called as part of runAnimation, but may be called manually if needed. void updateEffects(float duration); + // TODO: move outside of this class + /// Makes this object glow, by placing a Light in its center. + /// @param effect Controls the radius and intensity of the light. + void setLightEffect(float effect); + virtual void showWeapons(bool showWeapon); virtual void showCarriedLeft(bool show) {} virtual void attachArrow() {} diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 7467bd776..303917c95 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -552,6 +552,10 @@ NifOgre::ObjectScenePtr NpcAnimation::insertBoundedPart(const std::string &model std::for_each(objects->mEntities.begin(), objects->mEntities.end(), SetObjectGroup(group)); std::for_each(objects->mParticles.begin(), objects->mParticles.end(), SetObjectGroup(group)); + // Fast forward auto-play particles, which will have been set up as Emitting by the loader. + for (unsigned int i=0; imParticles.size(); ++i) + objects->mParticles[i]->fastForward(1, 0.1); + if(objects->mSkelBase) { Ogre::AnimationStateSet *aset = objects->mSkelBase->getAllAnimationStates(); diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index ff1743c64..b04f67b00 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -20,6 +20,8 @@ #include "../mwmechanics/spellcasting.hpp" #include "../mwrender/effectmanager.hpp" +#include "../mwrender/animation.hpp" +#include "../mwrender/renderconst.hpp" #include "../mwsound/sound.hpp" @@ -41,6 +43,9 @@ namespace MWWorld if(state.mObject->mControllers[i].getSource().isNull()) state.mObject->mControllers[i].setSource(Ogre::SharedPtr (new MWRender::EffectAnimationTime())); } + + MWRender::Animation::setRenderProperties(state.mObject, MWRender::RV_Misc, + MWRender::RQG_Main, MWRender::RQG_Alpha, 0.f, false, NULL); } void ProjectileManager::update(NifOgre::ObjectScenePtr object, float duration) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index ee107e1f2..ee5f97d72 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -259,8 +259,6 @@ namespace MWWorld void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos) { - Nif::NIFFile::CacheLock cachelock; - Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); @@ -408,7 +406,6 @@ namespace MWWorld if(!loadcell) loadcell = *mCurrentCell != *cell; - Nif::NIFFile::CacheLock lock; MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 96ccc281a..897e87735 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -22,6 +22,10 @@ add_component_dir (nif controlled effect niftypes record controller extra node record_ptr data niffile property ) +add_component_dir (nifcache + nifcache + ) + add_component_dir (nifogre ogrenifloader skeleton material mesh particles controller ) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 84f4aacee..6c38f73dc 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -35,143 +35,11 @@ #include -//TODO: when threading is needed, enable these -//#include -#include - namespace Nif { -class NIFFile::LoadedCache -{ - //TODO: enable this to make cache thread safe... - //typedef boost::mutex mutex; - - struct mutex - { - void lock () {}; - void unlock () {} - }; - - typedef boost::lock_guard lock_guard; - typedef std::map < std::string, boost::weak_ptr > loaded_map; - typedef std::vector < boost::shared_ptr > locked_files; - - static int sLockLevel; - static mutex sProtector; - static loaded_map sLoadedMap; - static locked_files sLockedFiles; - -public: - - static ptr create (const std::string &name) - { - lock_guard _ (sProtector); - - ptr result; - - // lookup the resource - loaded_map::iterator i = sLoadedMap.find (name); - - if (i == sLoadedMap.end ()) // it doesn't existing currently, - { // or hasn't in the very near past - - // create it now, for smoother threading if needed, the - // loading should be performed outside of the sLoaderMap - // lock and an alternate mechanism should be used to - // synchronize threads competing to load the same resource - result = boost::make_shared (name, psudo_private_modifier()); - - // if we are locking the cache add an extra reference - // to keep the file in memory - if (sLockLevel > 0) - sLockedFiles.push_back (result); - - // stash a reference to the resource so that future - // calls can benefit - sLoadedMap [name] = boost::weak_ptr (result); - } - else // it may (probably) still exists - { - // attempt to get the reference - result = i->second.lock (); - - if (!result) // resource is in the process of being destroyed - { - // create a new instance, to replace the one that has - // begun the irreversible process of being destroyed - result = boost::make_shared (name, psudo_private_modifier()); - - // respect the cache lock... - if (sLockLevel > 0) - sLockedFiles.push_back (result); - - // we potentially overwrite an expired pointer here - // but the other thread performing the delete on - // the previous copy of this resource will detect it - // and make sure not to erase the new reference - sLoadedMap [name] = boost::weak_ptr (result); - } - } - - // we made it! - return result; - } - - static void release (NIFFile * file) - { - lock_guard _ (sProtector); - - loaded_map::iterator i = sLoadedMap.find (file->filename); - - // its got to be in here, it just might not be us... - assert (i != sLoadedMap.end ()); - - // if weak_ptr is still expired, this resource hasn't been recreated - // between the initiation of the final release due to destruction - // of the last shared pointer and this thread acquiring the lock on - // the loader map - if (i->second.expired ()) - sLoadedMap.erase (i); - } - - static void lockCache () - { - lock_guard _ (sProtector); - - sLockLevel++; - } - - static void unlockCache () - { - locked_files resetList; - - { - lock_guard _ (sProtector); - - if (--sLockLevel) - sLockedFiles.swap(resetList); - } - - // this not necessary, but makes it clear that the - // deletion of the locked cache entries is being done - // outside the protection of sProtector - resetList.clear (); - } -}; - -int NIFFile::LoadedCache::sLockLevel = 0; -NIFFile::LoadedCache::mutex NIFFile::LoadedCache::sProtector; -NIFFile::LoadedCache::loaded_map NIFFile::LoadedCache::sLoadedMap; -NIFFile::LoadedCache::locked_files NIFFile::LoadedCache::sLockedFiles; - -// these three calls are forwarded to the cache implementation... -void NIFFile::lockCache () { LoadedCache::lockCache (); } -void NIFFile::unlockCache () { LoadedCache::unlockCache (); } -NIFFile::ptr NIFFile::create (const std::string &name) { return LoadedCache::create (name); } - /// Open a NIF stream. The name is used for error messages. -NIFFile::NIFFile(const std::string &name, psudo_private_modifier) +NIFFile::NIFFile(const std::string &name) : ver(0) , filename(name) { @@ -180,8 +48,6 @@ NIFFile::NIFFile(const std::string &name, psudo_private_modifier) NIFFile::~NIFFile() { - LoadedCache::release (this); - for(std::size_t i=0; i ptr; - /// Open a NIF stream. The name is used for error messages. - NIFFile(const std::string &name, psudo_private_modifier); + NIFFile(const std::string &name); ~NIFFile(); - static ptr create (const std::string &name); - static void lockCache (); - static void unlockCache (); - - struct CacheLock - { - CacheLock () { lockCache (); } - ~CacheLock () { unlockCache (); } - }; - /// Get a given record Record *getRecord(size_t index) const { diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 31d4e10d6..799ae32df 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -28,6 +28,8 @@ http://www.gnu.org/licenses/ . #include +#include + #include "../nif/niffile.hpp" #include "../nif/node.hpp" #include "../nif/data.hpp" @@ -74,7 +76,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) // of the early stages of development. Right now we WANT to catch // every error as early and intrusively as possible, as it's most // likely a sign of incomplete code rather than faulty input. - Nif::NIFFile::ptr pnif (Nif::NIFFile::create (mResourceName.substr(0, mResourceName.length()-7))); + Nif::NIFFilePtr pnif (Nif::Cache::getInstance().load(mResourceName.substr(0, mResourceName.length()-7))); Nif::NIFFile & nif = *pnif.get (); if (nif.numRoots() < 1) { @@ -388,7 +390,7 @@ bool findBoundingBox (const Nif::Node* node, Ogre::Vector3& halfExtents, Ogre::V bool getBoundingBox(const std::string& nifFile, Ogre::Vector3& halfExtents, Ogre::Vector3& translation, Ogre::Quaternion& orientation) { - Nif::NIFFile::ptr pnif (Nif::NIFFile::create (nifFile)); + Nif::NIFFilePtr pnif (Nif::Cache::getInstance().load(nifFile)); Nif::NIFFile & nif = *pnif.get (); if (nif.numRoots() < 1) diff --git a/components/nifcache/nifcache.cpp b/components/nifcache/nifcache.cpp new file mode 100644 index 000000000..342251dbc --- /dev/null +++ b/components/nifcache/nifcache.cpp @@ -0,0 +1,40 @@ +#include "nifcache.hpp" + +namespace Nif +{ + +Cache* Cache::sThis = 0; + +Cache& Cache::getInstance() +{ + assert (sThis); + return *sThis; +} + +Cache* Cache::getInstancePtr() +{ + return sThis; +} + +Cache::Cache() +{ + assert (!sThis); + sThis = this; +} + +NIFFilePtr Cache::load(const std::string &filename) +{ + // TODO: normalize file path to make sure we're not loading the same file twice + + LoadedMap::iterator it = mLoadedMap.find(filename); + if (it != mLoadedMap.end()) + return it->second; + else + { + NIFFilePtr file(new Nif::NIFFile(filename)); + mLoadedMap[filename] = file; + return file; + } +} + +} diff --git a/components/nifcache/nifcache.hpp b/components/nifcache/nifcache.hpp new file mode 100644 index 000000000..dc4155cad --- /dev/null +++ b/components/nifcache/nifcache.hpp @@ -0,0 +1,48 @@ +#ifndef OPENMW_COMPONENTS_NIFCACHE_H +#define OPENMW_COMPONENTS_NIFCACHE_H + +#include + +#include + +namespace Nif +{ + + typedef boost::shared_ptr NIFFilePtr; + + /// @brief A basic resource manager for NIF files + class Cache + { + public: + Cache(); + + /// Queue this file for background loading. A worker thread will start loading the file. + /// To get the loaded NIFFilePtr, use the load method, which will wait until the worker thread is finished + /// and then return the loaded file. + //void loadInBackground (const std::string& file); + + /// Read and parse the given file. May retrieve from cache if this file has been used previously. + /// @note If the file is currently loading in the background, this function will block until + /// the background loading finishes, then return the background loaded file. + /// @note Returns a SharedPtr to the file and the file will stay loaded as long as the user holds on to this pointer. + /// When all external SharedPtrs to a file are released, the cache may decide to unload the file. + NIFFilePtr load (const std::string& filename); + + /// Return instance of this class. + static Cache& getInstance(); + static Cache* getInstancePtr(); + + private: + static Cache* sThis; + + Cache(const Cache&); + Cache& operator =(const Cache&); + + typedef std::map LoadedMap; + + LoadedMap mLoadedMap; + }; + +} + +#endif diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index 8bebe0589..af73df637 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include "material.hpp" @@ -383,7 +384,7 @@ void NIFMeshLoader::loadResource(Ogre::Resource *resource) Ogre::Mesh *mesh = dynamic_cast(resource); OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!"); - Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName); + Nif::NIFFilePtr nif = Nif::Cache::getInstance().load(mName); if(mShapeIndex >= nif->numRecords()) { Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 495dd1f16..86d713aab 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -45,6 +45,7 @@ #include #include +#include #include #include @@ -390,9 +391,10 @@ public: class Value : public NodeTargetValue, public ValueInterpolator { private: - Nif::QuaternionKeyList mRotations; - Nif::Vector3KeyList mTranslations; - Nif::FloatKeyList mScales; + const Nif::QuaternionKeyList* mRotations; + const Nif::Vector3KeyList* mTranslations; + const Nif::FloatKeyList* mScales; + Nif::NIFFilePtr mNif; // Hold a SharedPtr to make sure key lists stay valid using ValueInterpolator::interpKey; @@ -420,31 +422,33 @@ public: } public: - Value(Ogre::Node *target, const Nif::NiKeyframeData *data) + /// @note The NiKeyFrameData must be valid as long as this KeyframeController exists. + Value(Ogre::Node *target, const Nif::NIFFilePtr& nif, const Nif::NiKeyframeData *data) : NodeTargetValue(target) - , mRotations(data->mRotations) - , mTranslations(data->mTranslations) - , mScales(data->mScales) + , mRotations(&data->mRotations) + , mTranslations(&data->mTranslations) + , mScales(&data->mScales) + , mNif(nif) { } virtual Ogre::Quaternion getRotation(float time) const { - if(mRotations.mKeys.size() > 0) - return interpKey(mRotations.mKeys, time); + if(mRotations->mKeys.size() > 0) + return interpKey(mRotations->mKeys, time); return mNode->getOrientation(); } virtual Ogre::Vector3 getTranslation(float time) const { - if(mTranslations.mKeys.size() > 0) - return interpKey(mTranslations.mKeys, time); + if(mTranslations->mKeys.size() > 0) + return interpKey(mTranslations->mKeys, time); return mNode->getPosition(); } virtual Ogre::Vector3 getScale(float time) const { - if(mScales.mKeys.size() > 0) - return Ogre::Vector3(interpKey(mScales.mKeys, time)); + if(mScales->mKeys.size() > 0) + return Ogre::Vector3(interpKey(mScales->mKeys, time)); return mNode->getScale(); } @@ -456,12 +460,12 @@ public: virtual void setValue(Ogre::Real time) { - if(mRotations.mKeys.size() > 0) - mNode->setOrientation(interpKey(mRotations.mKeys, time)); - if(mTranslations.mKeys.size() > 0) - mNode->setPosition(interpKey(mTranslations.mKeys, time)); - if(mScales.mKeys.size() > 0) - mNode->setScale(Ogre::Vector3(interpKey(mScales.mKeys, time))); + if(mRotations->mKeys.size() > 0) + mNode->setOrientation(interpKey(mRotations->mKeys, time)); + if(mTranslations->mKeys.size() > 0) + mNode->setPosition(interpKey(mTranslations->mKeys, time)); + if(mScales->mKeys.size() > 0) + mNode->setScale(Ogre::Vector3(interpKey(mScales->mKeys, time))); } }; @@ -916,8 +920,10 @@ class NIFObjectLoader scene->mControllers.push_back(Ogre::Controller(srcval, dstval, func)); - if (partflags&Nif::NiNode::ParticleFlag_AutoPlay) - partsys->fastForward(1, 0.1); + // Emitting state will be overwritten on frame update by the ParticleSystemController, + // but set up an initial value anyway so the user can fast-forward particle systems + // immediately after creation if desired. + partsys->setEmitting(partflags&Nif::NiNode::ParticleFlag_AutoPlay); } ctrl = ctrl->next; } @@ -929,7 +935,7 @@ class NIFObjectLoader } - static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectScenePtr scene, int animflags) + static void createNodeControllers(const Nif::NIFFilePtr& nif, const std::string &name, Nif::ControllerPtr ctrl, ObjectScenePtr scene, int animflags) { do { if (ctrl->flags & Nif::NiNode::ControllerFlag_Active) @@ -963,7 +969,7 @@ class NIFObjectLoader Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); - Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, nif, key->data.getPtr())); KeyframeController::Function* function = OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); Ogre::ControllerFunctionRealPtr func(function); @@ -1016,7 +1022,7 @@ class NIFObjectLoader } - static void createObjects(const std::string &name, const std::string &group, + static void createObjects(const Nif::NIFFilePtr& nif, const std::string &name, const std::string &group, Ogre::SceneNode *sceneNode, const Nif::Node *node, ObjectScenePtr scene, int flags, int animflags, int partflags) { @@ -1073,7 +1079,7 @@ class NIFObjectLoader } if(!node->controller.empty()) - createNodeControllers(name, node->controller, scene, animflags); + createNodeControllers(nif, name, node->controller, scene, animflags); if(node->recType == Nif::RC_NiCamera) { @@ -1098,7 +1104,7 @@ class NIFObjectLoader for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) - createObjects(name, group, sceneNode, children[i].getPtr(), scene, flags, animflags, partflags); + createObjects(nif, name, group, sceneNode, children[i].getPtr(), scene, flags, animflags, partflags); } } } @@ -1121,7 +1127,7 @@ class NIFObjectLoader public: static void load(Ogre::SceneNode *sceneNode, ObjectScenePtr scene, const std::string &name, const std::string &group, int flags=0) { - Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); + Nif::NIFFilePtr nif = Nif::Cache::getInstance().load(name); if(nif->numRoots() < 1) { nif->warn("Found no root nodes in "+name+"."); @@ -1145,13 +1151,13 @@ public: // Create a base skeleton entity if this NIF needs one createSkelBase(name, group, sceneNode->getCreator(), node, scene); } - createObjects(name, group, sceneNode, node, scene, flags, 0, 0); + createObjects(nif, name, group, sceneNode, node, scene, flags, 0, 0); } static void loadKf(Ogre::Skeleton *skel, const std::string &name, TextKeyMap &textKeys, std::vector > &ctrls) { - Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); + Nif::NIFFilePtr nif = Nif::Cache::getInstance().load(name); if(nif->numRoots() < 1) { nif->warn("Found no root nodes in "+name+"."); @@ -1202,7 +1208,7 @@ public: Ogre::Bone *trgtbone = skel->getBone(strdata->string); Ogre::ControllerValueRealPtr srcval; - Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); + Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, nif, key->data.getPtr())); Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false)); ctrls.push_back(Ogre::Controller(srcval, dstval, func)); diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index c96f03950..9e12eec90 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -6,6 +6,7 @@ #include #include +#include #include namespace NifOgre @@ -83,7 +84,7 @@ void NIFSkeletonLoader::loadResource(Ogre::Resource *resource) Ogre::Skeleton *skel = dynamic_cast(resource); OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!"); - Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName())); + Nif::NIFFilePtr nif(Nif::Cache::getInstance().load(skel->getName())); const Nif::Node *node = static_cast(nif->getRoot(0)); try { diff --git a/extern/shiny/Main/Factory.cpp b/extern/shiny/Main/Factory.cpp index 48caa225a..d7c4234cb 100644 --- a/extern/shiny/Main/Factory.cpp +++ b/extern/shiny/Main/Factory.cpp @@ -803,7 +803,7 @@ namespace sh for (MaterialMap::iterator it = mMaterials.begin(); it != mMaterials.end(); ++it) { if (it->second.getMaterial()->isUnreferenced()) - it->second.destroyAll(); + it->second.getMaterial()->unreferenceTextures(); } } diff --git a/extern/shiny/Main/Platform.hpp b/extern/shiny/Main/Platform.hpp index 24afea03b..d3156e680 100644 --- a/extern/shiny/Main/Platform.hpp +++ b/extern/shiny/Main/Platform.hpp @@ -69,6 +69,7 @@ namespace sh virtual void removeAll () = 0; ///< remove all configurations virtual bool isUnreferenced() = 0; + virtual void unreferenceTextures() = 0; virtual void ensureLoaded() = 0; virtual void setLodLevels (const std::string& lodLevels) = 0; diff --git a/extern/shiny/Platforms/Ogre/OgreMaterial.cpp b/extern/shiny/Platforms/Ogre/OgreMaterial.cpp index 77091fe03..04560e1f9 100644 --- a/extern/shiny/Platforms/Ogre/OgreMaterial.cpp +++ b/extern/shiny/Platforms/Ogre/OgreMaterial.cpp @@ -35,6 +35,11 @@ namespace sh return (!mMaterial.isNull() && mMaterial.useCount() <= Ogre::ResourceGroupManager::RESOURCE_SYSTEM_NUM_REFERENCE_COUNTS+1); } + void OgreMaterial::unreferenceTextures() + { + mMaterial->unload(); + } + OgreMaterial::~OgreMaterial() { if (!mMaterial.isNull()) diff --git a/extern/shiny/Platforms/Ogre/OgreMaterial.hpp b/extern/shiny/Platforms/Ogre/OgreMaterial.hpp index bfa6e2c6f..67a2c26e8 100644 --- a/extern/shiny/Platforms/Ogre/OgreMaterial.hpp +++ b/extern/shiny/Platforms/Ogre/OgreMaterial.hpp @@ -19,6 +19,7 @@ namespace sh virtual bool createConfiguration (const std::string& name, unsigned short lodIndex); virtual bool isUnreferenced(); + virtual void unreferenceTextures(); virtual void ensureLoaded(); virtual void removeAll (); diff --git a/files/mygui/openmw_list.skin.xml b/files/mygui/openmw_list.skin.xml index b2f9c1bef..291e3a8cc 100644 --- a/files/mygui/openmw_list.skin.xml +++ b/files/mygui/openmw_list.skin.xml @@ -113,7 +113,7 @@ - + diff --git a/libs/openengine/ogre/selectionbuffer.cpp b/libs/openengine/ogre/selectionbuffer.cpp index af95750d5..0ca24676e 100644 --- a/libs/openengine/ogre/selectionbuffer.cpp +++ b/libs/openengine/ogre/selectionbuffer.cpp @@ -32,6 +32,7 @@ namespace Render void SelectionBuffer::setupRenderTarget() { mRenderTarget = mTexture->getBuffer()->getRenderTarget(); + mRenderTarget->removeAllViewports(); Ogre::Viewport* vp = mRenderTarget->addViewport(mCamera); vp->setOverlaysEnabled(false); vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0)); @@ -65,6 +66,7 @@ namespace Render { Ogre::MaterialManager::getSingleton ().addListener (this); + mTexture->load(); if (mRenderTarget == NULL) setupRenderTarget();