From ffdb91bb21be8953a60013fa6bf8d4428d0eff17 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 11 Nov 2013 23:43:28 +0100 Subject: [PATCH] Add particles for Cast + Hit. Not looking quite right yet. --- apps/openmw/mwmechanics/activespells.cpp | 30 ++++++++++++------ apps/openmw/mwmechanics/character.cpp | 3 ++ apps/openmw/mwrender/animation.cpp | 39 ++++++++++++++++++++++++ apps/openmw/mwrender/animation.hpp | 19 ++++++++++++ apps/openmw/mwworld/worldimp.cpp | 6 +++- components/esm/loadmgef.hpp | 4 +-- components/nifogre/ogrenifloader.cpp | 26 +++++++++++++--- components/nifogre/ogrenifloader.hpp | 5 ++- 8 files changed, 113 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index c433ac5e7..666a14775 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -17,6 +17,8 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwrender/animation.hpp" + #include "../mwworld/class.hpp" #include "creaturestats.hpp" @@ -217,7 +219,8 @@ namespace MWMechanics else iter->second = params; - // Play sounds + // Play sounds & particles + bool first=true; for (std::vector::const_iterator iter (effects.first.mList.begin()); iter!=effects.first.mList.end(); ++iter) { @@ -228,17 +231,24 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getStore().get().find ( iter->mEffectID); - static const std::string schools[] = { - "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" - }; + // Only the sound of the first effect plays + if (first) + { + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(!magicEffect->mHitSound.empty()) - sndMgr->playSound3D(actor, magicEffect->mHitSound, 1.0f, 1.0f); - else - sndMgr->playSound3D(actor, schools[magicEffect->mData.mSchool]+" hit", 1.0f, 1.0f); + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!magicEffect->mHitSound.empty()) + sndMgr->playSound3D(actor, magicEffect->mHitSound, 1.0f, 1.0f); + else + sndMgr->playSound3D(actor, schools[magicEffect->mData.mSchool]+" hit", 1.0f, 1.0f); + } - break; + const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get().find (magicEffect->mHit); + MWBase::Environment::get().getWorld()->getAnimation(actor)->addEffect("meshes\\" + castStatic->mModel, ""); + + first = false; } mSpellsChanged = true; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 8c88b3ad0..fe1254b43 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -512,6 +512,9 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun const ESM::MagicEffect *effect; effect = store.get().find(effectentry.mEffectID); + const ESM::Static* castStatic = store.get().find (effect->mCasting); + mAnimation->addEffect("meshes\\" + castStatic->mModel, ""); + switch(effectentry.mRange) { case 0: mAttackType = "self"; break; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 53d697fdd..d494304d1 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -39,6 +39,15 @@ void Animation::AnimationValue::setValue(Ogre::Real) { } +Ogre::Real Animation::EffectAnimationValue::getValue() const +{ + return mTime; +} + +void Animation::EffectAnimationValue::setValue(Ogre::Real) +{ +} + void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects) { @@ -916,6 +925,25 @@ Ogre::Vector3 Animation::runAnimation(float duration) mSkelBase->getAllAnimationStates()->_notifyDirty(); } + + for (std::vector::iterator it = mEffects.begin(); it != mEffects.end(); ) + { + for(size_t i = 0; i < it->mControllers.size() ;i++) + { + static_cast (it->mControllers[i].getSource().get())->addTime(duration); + + it->mControllers[i].update(); + } + + if (it->mControllers[0].getSource()->getValue() >= it->mMaxControllerLength) + { + destroyObjectList(mInsert->getCreator(), *it); + it = mEffects.erase(it); + } + else + ++it; + } + return movement; } @@ -980,6 +1008,17 @@ void Animation::detachObjectFromBone(Ogre::MovableObject *obj) mSkelBase->detachObjectFromBone(obj); } +void Animation::addEffect(const std::string &model, const std::string &bonename) +{ + NifOgre::ObjectList list = NifOgre::Loader::createObjects(mInsert, model); + for(size_t i = 0;i < list.mControllers.size();i++) + { + if(list.mControllers[i].getSource().isNull()) + list.mControllers[i].setSource(Ogre::SharedPtr (new EffectAnimationValue())); + } + mEffects.push_back(list); +} + 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 2215fc582..efc7a6a59 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -52,6 +53,19 @@ protected: virtual void setValue(Ogre::Real value); }; + class EffectAnimationValue : public Ogre::ControllerValue + { + private: + float mTime; + public: + EffectAnimationValue() : mTime(0) { } + void addTime(float time) { mTime += time; } + + virtual Ogre::Real getValue() const; + virtual void setValue(Ogre::Real value); + }; + + class NullAnimationValue : public Ogre::ControllerValue { @@ -95,6 +109,8 @@ protected: typedef std::map ObjectAttachMap; + std::vector mEffects; + MWWorld::Ptr mPtr; Camera *mCamera; @@ -114,6 +130,7 @@ protected: ObjectAttachMap mAttachedObjects; + /* Sets the appropriate animations on the bone groups based on priority. */ void resetActiveGroups(); @@ -173,6 +190,8 @@ public: Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node); virtual ~Animation(); + void addEffect (const std::string& model, const std::string& bonename = ""); + void updatePtr(const MWWorld::Ptr &ptr); bool hasAnimation(const std::string &anim); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5747c636f..254c7e1be 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2029,6 +2029,8 @@ namespace MWWorld MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); stats.setAttackingOrSpell(false); + ESM::EffectList effects; + std::string selectedSpell = stats.getSpells().getSelectedSpell(); std::string sourceName; if (!selectedSpell.empty()) @@ -2094,6 +2096,7 @@ namespace MWWorld actor.getClass().skillUsageSucceeded(actor, MWMechanics::spellSchoolToSkill(MWMechanics::getSpellSchool(selectedSpell, actor)), 0); + effects = spell->mEffects; } InventoryStore& inv = actor.getClass().getInventoryStore(actor); if (selectedSpell.empty() && inv.getSelectedEnchantItem() != inv.end()) @@ -2137,6 +2140,8 @@ namespace MWWorld } else MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); // Set again to show the modified charge + + effects = enchantment->mEffects; } // Now apply the spell! @@ -2158,7 +2163,6 @@ namespace MWWorld } } - // TODO: Launch projectile if there's a Target portion } void World::updateAnimParts(const Ptr& actor) diff --git a/components/esm/loadmgef.hpp b/components/esm/loadmgef.hpp index 9d7397a34..d18507b10 100644 --- a/components/esm/loadmgef.hpp +++ b/components/esm/loadmgef.hpp @@ -64,8 +64,8 @@ struct MagicEffect MEDTstruct mData; std::string mIcon, mParticle; // Textures - std::string mCasting, mHit, mArea; // Statics - std::string mBolt; // Weapon + std::string mCasting, mHit, mArea; // ESM::Static + std::string mBolt; // ESM::Weapon std::string mCastSound, mBoltSound, mHitSound, mAreaSound; // Sounds std::string mDescription; diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 3bb9ea230..4b9cb5ad5 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -54,6 +54,7 @@ private: float mFrequency; float mPhase; float mStartTime; +public: float mStopTime; public: @@ -468,7 +469,10 @@ class NIFObjectLoader Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr())); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); + + UVController::Function* function = OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + objectlist.mMaxControllerLength = std::max(function->mStopTime, objectlist.mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } @@ -481,7 +485,10 @@ class NIFObjectLoader Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(subent, geom->data.getPtr())); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); + + GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + objectlist.mMaxControllerLength = std::max(function->mStopTime, objectlist.mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } @@ -616,7 +623,11 @@ class NIFObjectLoader Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW ParticleSystemController::Value(partsys, partctrl)); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&Nif::NiNode::ParticleFlag_AutoPlay))); + + ParticleSystemController::Function* function = + OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&Nif::NiNode::ParticleFlag_AutoPlay)); + objectlist.mMaxControllerLength = std::max(function->mStopTime, objectlist.mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } @@ -648,7 +659,10 @@ class NIFObjectLoader Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr())); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); + + VisController::Function* function = OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + objectlist.mMaxControllerLength = std::max(function->mStopTime, objectlist.mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } @@ -663,7 +677,9 @@ class NIFObjectLoader Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); - Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay))); + KeyframeController::Function* function = OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); + objectlist.mMaxControllerLength = std::max(function->mStopTime, objectlist.mMaxControllerLength); + Ogre::ControllerFunctionRealPtr func(function); objectlist.mControllers.push_back(Ogre::Controller(srcval, dstval, func)); } diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index edad13a9a..de06dd3d5 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -45,11 +45,14 @@ struct ObjectList { std::vector mParticles; std::vector mLights; + // The maximum length on any of the controllers. For animations with controllers, but no text keys, consider this the animation length. + float mMaxControllerLength; + std::map mTextKeys; std::vector > mControllers; - ObjectList() : mSkelBase(0) + ObjectList() : mSkelBase(0), mMaxControllerLength(0) { } };