diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index effdcf547..01520a331 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -113,7 +113,8 @@ namespace MWClass bool hasKey = false; std::string keyName; - if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr() && // assuming player is using telekinesis + // make door glow if player activates it with telekinesis + if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr() && MWBase::Environment::get().getWorld()->getDistanceToFacedObject() > MWBase::Environment::get().getWorld()->getMaxActivationDistance()) { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index e7a211c60..e65195531 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1531,11 +1531,10 @@ void CharacterController::update(float duration) osg::Vec3f movement(0.f, 0.f, 0.f); float speed = 0.f; + updateMagicEffects(); + if(!cls.isActor()) { - updateMagicEffects(); - MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mPtr); - animation->updateSpellGlow(duration); if(mAnimQueue.size() > 1) { if(mAnimation->isPlaying(mAnimQueue.front().mGroup) == false) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index d038fb946..bef720764 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -837,7 +837,7 @@ namespace MWMechanics mCaster.getClass().skillUsageSucceeded(mCaster, spellSchoolToSkill(school), 0); - // A non-actor doesn't have casting animation so it plays its spell casting effects here + // A non-actor doesn't play its effects from a character controller, so play spell casting effects here if (!mCaster.getClass().isActor()) playSpellCastingEffects(mId); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 736ba8956..71b569285 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -38,7 +38,6 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" -#include "../mwbase/mechanicsmanager.hpp" #include "../mwmechanics/character.hpp" // FIXME: for MWMechanics::Priority @@ -90,11 +89,18 @@ namespace class GlowUpdater : public SceneUtil::StateSetUpdater { public: - GlowUpdater(int texUnit, osg::Vec4f color, const std::vector >& textures, bool hasDuration) + GlowUpdater(int texUnit, osg::Vec4f color, const std::vector >& textures, + osg::ref_ptr node, float maxduration, Resource::ResourceSystem* resourcesystem, osg::Uniform* uniform) : mTexUnit(texUnit) , mColor(color) , mTextures(textures) - , mHasDuration(hasDuration) + , mNode(node) + , mMaxDuration(maxduration) + , mStartingTime(0) + , mResourceSystem(resourcesystem) + , mDone(false) + , mUniform(uniform) + , mWatchedSpellGlow(NULL) { } @@ -115,35 +121,99 @@ namespace texEnv->setOperand2_RGB(osg::TexEnvCombine::SRC_COLOR); stateset->setTextureAttributeAndModes(mTexUnit, texEnv, osg::StateAttribute::ON); + + // Reduce the texture list back down by one when a temporary glow finishes + // to allow FindLowestUnusedTexUnitVisitor to choose the same texunit again. + if (mDone) + stateset->getTextureAttributeList().resize(stateset->getTextureAttributeList().size() - 1); } virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv) { + if (mDone) + return; + + // If there is a permanent enchantment glow already on this object, give the + // permanent glow a pointer to a temporary glow added in a nested update callback + // The permanent glow will remove the temporary glow when it finishes, allowing + // the permanent glow to display its glow texture cycling properly. + if (mWatchedSpellGlow != NULL && mWatchedSpellGlow->isDone()) + { + mNode->removeUpdateCallback(mWatchedSpellGlow); + mWatchedSpellGlow = NULL; + } + + // Set the starting time to measure glow duration from if this is a temporary glow + if ((mMaxDuration >= 0) && mStartingTime == 0) + mStartingTime = nv->getFrameStamp()->getSimulationTime(); + float time = nv->getFrameStamp()->getSimulationTime(); int index = (int)(time*16) % mTextures.size(); stateset->setTextureAttribute(mTexUnit, mTextures[index], osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); + + if ((mMaxDuration >= 0) && (time - mStartingTime > mMaxDuration)) // If this is a temporary glow and it has finished its duration + { + osg::ref_ptr writableStateSet = NULL; + if (!mNode->getStateSet()) + writableStateSet = mNode->getOrCreateStateSet(); + else + writableStateSet = osg::clone(mNode->getStateSet(), osg::CopyOp::SHALLOW_COPY); + + for (size_t index = 0; index < mTextures.size(); index++) + { + writableStateSet->removeTextureAttribute(mTexUnit, mTextures[index]); + } + + writableStateSet->removeUniform(mUniform); // remove the uniform given to the temporary glow in addglow() + mNode->setStateSet(writableStateSet); + + this->reset(); // without this a texture from the glow will continue to show on the object + mResourceSystem->getSceneManager()->recreateShaders(mNode); + mDone = true; + } } - bool const getHasDuration() + bool isSpellGlowUpdater() { - return mHasDuration; + return (mMaxDuration >= 0); } - std::vector > const getTextures() + bool isEnchantmentGlowUpdater() { - return mTextures; + return (mMaxDuration < 0); } - int const getTexUnit() + bool isDone() + { + return mDone; + } + + int getTexUnit() { return mTexUnit; } + void setWatchedSpellGlow(osg::ref_ptr watched) + { + mWatchedSpellGlow = watched; + } + + osg::ref_ptr getWatchedSpellGlow() + { + return mWatchedSpellGlow; + } + private: int mTexUnit; osg::Vec4f mColor; std::vector > mTextures; - bool mHasDuration; + osg::ref_ptr mNode; + float mMaxDuration; + float mStartingTime; + Resource::ResourceSystem* mResourceSystem; + bool mDone; + osg::Uniform* mUniform; + osg::ref_ptr mWatchedSpellGlow; }; class NodeMapVisitor : public osg::NodeVisitor @@ -358,7 +428,6 @@ namespace MWRender , mHeadYawRadians(0.f) , mHeadPitchRadians(0.f) , mAlpha(1.f) - , mSpellGlowDuration(0.f) { for(size_t i = 0;i < sNumBlendMasks;i++) mAnimationTimePtr[i].reset(new AnimationTime); @@ -1126,47 +1195,18 @@ namespace MWRender }; void Animation::addSpellCastGlow(osg::Vec4f glowColor){ - if (mSpellGlowUpdater == NULL) // only start a new spell glow if there isn't one already - addGlow(mObjectRoot, glowColor, true); - MWBase::Environment::get().getMechanicsManager()->add(mPtr); - } - - void Animation::updateSpellGlow(float duration){ - if (mSpellGlowUpdater != NULL) - mSpellGlowDuration += duration; - if (mSpellGlowDuration >= 1.5f) // length of spell glow effect was measured from original game as around 1.5 seconds - removeSpellGlow(); - } - - void Animation::removeSpellGlow() - { - osg::ref_ptr writableStateSet = NULL; - if (!mObjectRoot->getStateSet()) - writableStateSet = mObjectRoot->getOrCreateStateSet(); - else - writableStateSet = osg::clone(mObjectRoot->getStateSet(), osg::CopyOp::SHALLOW_COPY); - - std::vector > Textures = mSpellGlowUpdater->getTextures(); - int TexUnit = mSpellGlowUpdater->getTexUnit(); - mObjectRoot->removeUpdateCallback(mSpellGlowUpdater); - mSpellGlowUpdater = NULL; + if (!mObjectRoot->getUpdateCallback()) // If there is no glow on object + addGlow(mObjectRoot, glowColor, 1.5); // Glow length measured from in-game as about 1.5 seconds - for (size_t index = 0; index < Textures.size(); index++) - { - writableStateSet->setTextureAttribute(TexUnit, Textures[index], osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE); - writableStateSet->removeTextureAttribute(TexUnit, Textures[index]); - } + else if (dynamic_cast (mObjectRoot->getUpdateCallback())->isDone() == true) // If there was a temporary glow on object and it finished + addGlow(mObjectRoot, glowColor, 1.5); - mObjectRoot->setStateSet(writableStateSet); - if (writableStateSet->getUniform("envMapColor2")) // if we added a second uniform, remove that one instead of the original - writableStateSet->removeUniform("envMapColor2"); - else - writableStateSet->removeUniform("envMapColor"); - mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot); - mSpellGlowDuration = 0.f; + else if (dynamic_cast (mObjectRoot->getUpdateCallback())->isEnchantmentGlowUpdater() == true + && dynamic_cast (mObjectRoot->getUpdateCallback())->getWatchedSpellGlow() == NULL) // If there is a permanent enchantment glow on object and no temporary glow is running + addGlow(mObjectRoot, glowColor, 1.5); } - void Animation::addGlow(osg::ref_ptr node, osg::Vec4f glowColor, bool hasDuration) + void Animation::addGlow(osg::ref_ptr node, osg::Vec4f glowColor, float glowDuration) { std::vector > textures; for (int i=0; i<32; ++i) @@ -1187,16 +1227,29 @@ namespace MWRender textures.push_back(tex); } + int texUnit; + + // If we have a spell glow updater left over from this object prevously casting a spell, + // and there was no permanent glow updater on the object to watch it and remove it, we + // remove it here. + if (node->getUpdateCallback() && + dynamic_cast (node->getUpdateCallback())->isSpellGlowUpdater() == true) + node->removeUpdateCallback(node->getUpdateCallback()); + FindLowestUnusedTexUnitVisitor findLowestUnusedTexUnitVisitor; node->accept(findLowestUnusedTexUnitVisitor); - int texUnit = findLowestUnusedTexUnitVisitor.mLowestUnusedTexUnit; + texUnit = findLowestUnusedTexUnitVisitor.mLowestUnusedTexUnit; + + osg::Uniform* uniform = new osg::Uniform("envMapColor", glowColor); - osg::ref_ptr glowUpdater = new GlowUpdater(texUnit, glowColor, textures, hasDuration); - if (hasDuration) // store the glowUpdater for later removal and checking if this is a spell glow - mSpellGlowUpdater = glowUpdater; + osg::ref_ptr glowUpdater = new GlowUpdater(texUnit, glowColor, textures, node, glowDuration, mResourceSystem, uniform); node->addUpdateCallback(glowUpdater); + if (node->getUpdateCallback() && + dynamic_cast (node->getUpdateCallback())->isEnchantmentGlowUpdater() == true) + if (glowDuration >= 0) + dynamic_cast (node->getUpdateCallback())->setWatchedSpellGlow(glowUpdater); // set a texture now so that the ShaderVisitor can find it osg::ref_ptr writableStateSet = NULL; @@ -1208,11 +1261,7 @@ namespace MWRender node->setStateSet(writableStateSet); } writableStateSet->setTextureAttributeAndModes(texUnit, textures.front(), osg::StateAttribute::ON); - - if (hasDuration && writableStateSet->getUniform("envMapColor")) // if we are adding a spell glow to something with an enchantment glow - writableStateSet->addUniform(new osg::Uniform("envMapColor2", glowColor)); - else - writableStateSet->addUniform(new osg::Uniform("envMapColor", glowColor)); + writableStateSet->addUniform(uniform); mResourceSystem->getSceneManager()->recreateShaders(node); } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 30e2de601..96bd0c03e 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -5,11 +5,6 @@ #include -namespace -{ - class GlowUpdater; -} - namespace ESM { struct Light; @@ -268,9 +263,6 @@ protected: osg::ref_ptr mGlowLight; float mAlpha; - float mSpellGlowDuration; - - osg::ref_ptr mSpellGlowUpdater; const NodeMap& getNodeMap() const; @@ -325,7 +317,7 @@ protected: osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const; - void addGlow(osg::ref_ptr node, osg::Vec4f glowColor, bool hasDuration = false); + void addGlow(osg::ref_ptr node, osg::Vec4f glowColor, float glowDuration = -1); /// Set the render bin for this animation's object root. May be customized by subclasses. virtual void setRenderBin(); @@ -360,8 +352,6 @@ public: void getLoopingEffects (std::vector& out) const; void addSpellCastGlow(osg::Vec4f glowColor); - void updateSpellGlow(float duration); - void removeSpellGlow(); virtual void updatePtr(const MWWorld::Ptr &ptr);