mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 07:56:38 +00:00 
			
		
		
		
	Rewrite spell glow implementation
This commit is contained in:
		
							parent
							
								
									cad41599cf
								
							
						
					
					
						commit
						775162ccdf
					
				
					 3 changed files with 142 additions and 148 deletions
				
			
		|  | @ -572,6 +572,11 @@ namespace MWMechanics | ||||||
|         short effectId = effect.mId; |         short effectId = effect.mId; | ||||||
|         if (target.getClass().canLock(target)) |         if (target.getClass().canLock(target)) | ||||||
|         { |         { | ||||||
|  |             const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); | ||||||
|  |             const ESM::MagicEffect *magiceffect; | ||||||
|  |             magiceffect = store.get<ESM::MagicEffect>().find(effectId); | ||||||
|  |             MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target); | ||||||
|  |             animation->addSpellCastGlow(magiceffect); | ||||||
|             if (effectId == ESM::MagicEffect::Lock) |             if (effectId == ESM::MagicEffect::Lock) | ||||||
|             { |             { | ||||||
|                 if (target.getCellRef().getLockLevel() < magnitude) //If the door is not already locked to a higher value, lock it to spell magnitude
 |                 if (target.getCellRef().getLockLevel() < magnitude) //If the door is not already locked to a higher value, lock it to spell magnitude
 | ||||||
|  | @ -584,12 +589,6 @@ namespace MWMechanics | ||||||
|             } |             } | ||||||
|             else if (effectId == ESM::MagicEffect::Open) |             else if (effectId == ESM::MagicEffect::Open) | ||||||
|             { |             { | ||||||
|                 const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); |  | ||||||
|                 const ESM::MagicEffect *magiceffect; |  | ||||||
|                 magiceffect = store.get<ESM::MagicEffect>().find(effectId); |  | ||||||
|                 MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(target); |  | ||||||
|                 animation->addSpellCastGlow(magiceffect); |  | ||||||
| 
 |  | ||||||
|                 if (target.getCellRef().getLockLevel() <= magnitude) |                 if (target.getCellRef().getLockLevel() <= magnitude) | ||||||
|                 { |                 { | ||||||
|                     if (target.getCellRef().getLockLevel() > 0) |                     if (target.getCellRef().getLockLevel() > 0) | ||||||
|  |  | ||||||
|  | @ -86,125 +86,6 @@ namespace | ||||||
|         std::vector<osg::ref_ptr<osg::Node> > mToRemove; |         std::vector<osg::ref_ptr<osg::Node> > mToRemove; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     class GlowUpdater : public SceneUtil::StateSetUpdater |  | ||||||
|     { |  | ||||||
|     public: |  | ||||||
|         GlowUpdater(int texUnit, osg::Vec4f color, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures, |  | ||||||
|             osg::ref_ptr<osg::Node> node, float maxduration, Resource::ResourceSystem* resourcesystem) |  | ||||||
|             : mTexUnit(texUnit) |  | ||||||
|             , mColor(color) |  | ||||||
|             , mTextures(textures) |  | ||||||
|             , mNode(node) |  | ||||||
|             , mMaxDuration(maxduration) |  | ||||||
|             , mStartingTime(0) |  | ||||||
|             , mResourceSystem(resourcesystem) |  | ||||||
|             , mDone(false) |  | ||||||
|             , mWatchedSpellGlow(NULL) |  | ||||||
|         { |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         virtual void setDefaults(osg::StateSet *stateset) |  | ||||||
|         { |  | ||||||
|             stateset->setTextureMode(mTexUnit, GL_TEXTURE_2D, osg::StateAttribute::ON); |  | ||||||
| 
 |  | ||||||
|             osg::TexGen* texGen = new osg::TexGen; |  | ||||||
|             texGen->setMode(osg::TexGen::SPHERE_MAP); |  | ||||||
| 
 |  | ||||||
|             stateset->setTextureAttributeAndModes(mTexUnit, texGen, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); |  | ||||||
| 
 |  | ||||||
|             osg::TexEnvCombine* texEnv = new osg::TexEnvCombine; |  | ||||||
|             texEnv->setSource0_RGB(osg::TexEnvCombine::CONSTANT); |  | ||||||
|             texEnv->setConstantColor(mColor); |  | ||||||
|             texEnv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE); |  | ||||||
|             texEnv->setSource2_RGB(osg::TexEnvCombine::TEXTURE); |  | ||||||
|             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<osg::StateSet> writableStateSet = NULL; |  | ||||||
|                 if (!mNode->getStateSet()) |  | ||||||
|                     writableStateSet = mNode->getOrCreateStateSet(); |  | ||||||
|                 else |  | ||||||
|                     writableStateSet = osg::clone(mNode->getStateSet(), osg::CopyOp::SHALLOW_COPY); |  | ||||||
|                  |  | ||||||
|                 for (size_t i = 0; i < mTextures.size(); i++) |  | ||||||
|                     writableStateSet->removeTextureAttribute(mTexUnit, mTextures[i]); |  | ||||||
| 
 |  | ||||||
|                 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 isSpellGlowUpdater() |  | ||||||
|         { |  | ||||||
|             return (mMaxDuration >= 0); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool isEnchantmentGlowUpdater() |  | ||||||
|         { |  | ||||||
|             return (mMaxDuration < 0); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool isDone() |  | ||||||
|         { |  | ||||||
|             return mDone; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void setWatchedSpellGlow(osg::ref_ptr<GlowUpdater> watched) |  | ||||||
|         { |  | ||||||
|             mWatchedSpellGlow = watched; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         osg::ref_ptr<GlowUpdater> getWatchedSpellGlow() |  | ||||||
|         { |  | ||||||
|             return mWatchedSpellGlow; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     private: |  | ||||||
|         int mTexUnit; |  | ||||||
|         osg::Vec4f mColor; |  | ||||||
|         std::vector<osg::ref_ptr<osg::Texture2D> > mTextures; |  | ||||||
|         osg::ref_ptr<osg::Node> mNode; |  | ||||||
|         float mMaxDuration; |  | ||||||
|         float mStartingTime; |  | ||||||
|         Resource::ResourceSystem* mResourceSystem; |  | ||||||
|         bool mDone; |  | ||||||
|         osg::ref_ptr<GlowUpdater> mWatchedSpellGlow; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     class NodeMapVisitor : public osg::NodeVisitor |     class NodeMapVisitor : public osg::NodeVisitor | ||||||
|     { |     { | ||||||
|     public: |     public: | ||||||
|  | @ -366,6 +247,124 @@ namespace | ||||||
| 
 | 
 | ||||||
| namespace MWRender | namespace MWRender | ||||||
| { | { | ||||||
|  |     class GlowUpdater : public SceneUtil::StateSetUpdater | ||||||
|  |     { | ||||||
|  |     public: | ||||||
|  |         GlowUpdater(int texUnit, osg::Vec4f color, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures, | ||||||
|  |             osg::ref_ptr<osg::Node> node, float duration, Resource::ResourceSystem* resourcesystem) | ||||||
|  |             : mTexUnit(texUnit) | ||||||
|  |             , mColor(color) | ||||||
|  |             , mOriginalColor(color) | ||||||
|  |             , mTextures(textures) | ||||||
|  |             , mNode(node) | ||||||
|  |             , mDuration(duration) | ||||||
|  |             , mOriginalDuration(duration) | ||||||
|  |             , mStartingTime(0) | ||||||
|  |             , mResourceSystem(resourcesystem) | ||||||
|  |             , mColorChanged(false) | ||||||
|  |             , mDone(false) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         virtual void setDefaults(osg::StateSet *stateset) | ||||||
|  |         { | ||||||
|  |             stateset->setTextureMode(mTexUnit, GL_TEXTURE_2D, osg::StateAttribute::ON); | ||||||
|  | 
 | ||||||
|  |             osg::TexGen* texGen = new osg::TexGen; | ||||||
|  |             texGen->setMode(osg::TexGen::SPHERE_MAP); | ||||||
|  | 
 | ||||||
|  |             stateset->setTextureAttributeAndModes(mTexUnit, texGen, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); | ||||||
|  | 
 | ||||||
|  |             osg::TexEnvCombine* texEnv = new osg::TexEnvCombine; | ||||||
|  |             texEnv->setSource0_RGB(osg::TexEnvCombine::CONSTANT); | ||||||
|  |             texEnv->setConstantColor(mColor); | ||||||
|  |             texEnv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE); | ||||||
|  |             texEnv->setSource2_RGB(osg::TexEnvCombine::TEXTURE); | ||||||
|  |             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 (mColorChanged){ | ||||||
|  |                 this->reset(); | ||||||
|  |                 setDefaults(stateset); | ||||||
|  |                 mResourceSystem->getSceneManager()->recreateShaders(mNode); | ||||||
|  |                 mColorChanged = false; | ||||||
|  |             } | ||||||
|  |             if (mDone) | ||||||
|  |                 return; | ||||||
|  |              | ||||||
|  |             // Set the starting time to measure glow duration from if this is a temporary glow
 | ||||||
|  |             if ((mDuration >= 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 ((mDuration >= 0) && (time - mStartingTime > mDuration)) // If this is a temporary glow and it has finished its duration
 | ||||||
|  |             { | ||||||
|  |                 if (mOriginalDuration >= 0) // if this glowupdater was a temporary glow since its creation
 | ||||||
|  |                 { | ||||||
|  |                     for (size_t i = 0; i < mTextures.size(); i++) | ||||||
|  |                         stateset->removeTextureAttribute(mTexUnit, mTextures[i]); | ||||||
|  |                     this->reset(); | ||||||
|  |                     mDone = true; | ||||||
|  |                 } | ||||||
|  |                 if (mOriginalDuration < 0) // if this glowupdater was originally a permanent glow
 | ||||||
|  |                 { | ||||||
|  |                     mDuration = mOriginalDuration; | ||||||
|  |                     mStartingTime = 0; | ||||||
|  |                     mColor = mOriginalColor; | ||||||
|  |                     this->reset(); | ||||||
|  |                     setDefaults(stateset); | ||||||
|  |                     stateset->addUniform(new osg::Uniform("envMapColor", mColor)); | ||||||
|  |                 } | ||||||
|  |                 mResourceSystem->getSceneManager()->recreateShaders(mNode); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bool isPermanentGlowUpdater() | ||||||
|  |         { | ||||||
|  |             return (mDuration < 0); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bool isDone() | ||||||
|  |         { | ||||||
|  |             return mDone; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         void setColor(osg::Vec4f color) | ||||||
|  |         { | ||||||
|  |             mColor = color; | ||||||
|  |             mColorChanged = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         void setDuration(float duration) | ||||||
|  |         { | ||||||
|  |             mDuration = duration; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     private: | ||||||
|  |         int mTexUnit; | ||||||
|  |         osg::Vec4f mColor; | ||||||
|  |         osg::Vec4f mOriginalColor; // for restoring the color of a permanent glow after a temporary glow on the object finishes
 | ||||||
|  |         std::vector<osg::ref_ptr<osg::Texture2D> > mTextures; | ||||||
|  |         osg::ref_ptr<osg::Node> mNode; | ||||||
|  |         float mDuration; | ||||||
|  |         float mOriginalDuration; // for recording that this is originally a permanent glow if it is changed to a temporary one
 | ||||||
|  |         float mStartingTime; | ||||||
|  |         Resource::ResourceSystem* mResourceSystem; | ||||||
|  |         bool mColorChanged; | ||||||
|  |         bool mDone; | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     struct Animation::AnimSource |     struct Animation::AnimSource | ||||||
|     { |     { | ||||||
|  | @ -1190,14 +1189,10 @@ namespace MWRender | ||||||
|         glowColor.y() = effect->mData.mGreen / 255.f; |         glowColor.y() = effect->mData.mGreen / 255.f; | ||||||
|         glowColor.z() = effect->mData.mBlue / 255.f; |         glowColor.z() = effect->mData.mBlue / 255.f; | ||||||
| 
 | 
 | ||||||
|         if (!mObjectRoot->getUpdateCallback()) // If there is no glow on object
 |         if (!mGlowUpdater) // If there is no glow on object
 | ||||||
|             addGlow(mObjectRoot, glowColor, 1.5); // Glow length measured from in-game as about 1.5 seconds
 |             addGlow(mObjectRoot, glowColor, 1.5); // Glow length measured from in-game as about 1.5 seconds
 | ||||||
| 
 | 
 | ||||||
|         else if (dynamic_cast <GlowUpdater*>(mObjectRoot->getUpdateCallback())->isDone() == true) // If there was a temporary glow on object and it finished
 |         else if (mGlowUpdater->isDone() || (mGlowUpdater->isPermanentGlowUpdater() == true)) | ||||||
|             addGlow(mObjectRoot, glowColor, 1.5); |  | ||||||
| 
 |  | ||||||
|         else if (dynamic_cast <GlowUpdater*>(mObjectRoot->getUpdateCallback())->isEnchantmentGlowUpdater() == true |  | ||||||
|             && dynamic_cast <GlowUpdater*>(mObjectRoot->getUpdateCallback())->getWatchedSpellGlow() == NULL) // If there is a permanent enchantment glow on object and no temporary glow is running
 |  | ||||||
|             addGlow(mObjectRoot, glowColor, 1.5); |             addGlow(mObjectRoot, glowColor, 1.5); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1222,26 +1217,24 @@ namespace MWRender | ||||||
|             textures.push_back(tex); |             textures.push_back(tex); | ||||||
|         }      |         }      | ||||||
| 
 | 
 | ||||||
|         int texUnit; |         if (mGlowUpdater && mGlowUpdater->isDone()) | ||||||
| 
 |                 node->removeUpdateCallback(mGlowUpdater); | ||||||
|         // 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 <GlowUpdater*>(node->getUpdateCallback())->isSpellGlowUpdater() == true) |  | ||||||
|                 node->removeUpdateCallback(node->getUpdateCallback()); |  | ||||||
| 
 | 
 | ||||||
|         FindLowestUnusedTexUnitVisitor findLowestUnusedTexUnitVisitor; |         FindLowestUnusedTexUnitVisitor findLowestUnusedTexUnitVisitor; | ||||||
|         node->accept(findLowestUnusedTexUnitVisitor); |         node->accept(findLowestUnusedTexUnitVisitor); | ||||||
|         texUnit = findLowestUnusedTexUnitVisitor.mLowestUnusedTexUnit; |         int texUnit = findLowestUnusedTexUnitVisitor.mLowestUnusedTexUnit; | ||||||
| 
 | 
 | ||||||
|         osg::ref_ptr<GlowUpdater> glowUpdater = new GlowUpdater(texUnit, glowColor, textures, node, glowDuration, mResourceSystem); |         if (mGlowUpdater && mGlowUpdater->isPermanentGlowUpdater()) | ||||||
| 
 |         { | ||||||
|         node->addUpdateCallback(glowUpdater); |             mGlowUpdater->setColor(glowColor); | ||||||
|         if (node->getUpdateCallback() && |             mGlowUpdater->setDuration(glowDuration); | ||||||
|             dynamic_cast <GlowUpdater*>(node->getUpdateCallback())->isEnchantmentGlowUpdater() == true) |         } | ||||||
|             if (glowDuration >= 0)  |         else | ||||||
|                 dynamic_cast <GlowUpdater*>(node->getUpdateCallback())->setWatchedSpellGlow(glowUpdater); |         {     | ||||||
|  |             osg::ref_ptr<GlowUpdater> glowUpdater = new GlowUpdater(texUnit, glowColor, textures, node, glowDuration, mResourceSystem); | ||||||
|  |             mGlowUpdater = glowUpdater; | ||||||
|  |             node->addUpdateCallback(glowUpdater); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         // set a texture now so that the ShaderVisitor can find it
 |         // set a texture now so that the ShaderVisitor can find it
 | ||||||
|         osg::ref_ptr<osg::StateSet> writableStateSet = NULL; |         osg::ref_ptr<osg::StateSet> writableStateSet = NULL; | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ namespace MWRender | ||||||
| 
 | 
 | ||||||
| class ResetAccumRootCallback; | class ResetAccumRootCallback; | ||||||
| class RotateController; | class RotateController; | ||||||
|  | class GlowUpdater; | ||||||
| 
 | 
 | ||||||
| class EffectAnimationTime : public SceneUtil::ControllerSource | class EffectAnimationTime : public SceneUtil::ControllerSource | ||||||
| { | { | ||||||
|  | @ -262,6 +263,7 @@ protected: | ||||||
|     float mHeadPitchRadians; |     float mHeadPitchRadians; | ||||||
| 
 | 
 | ||||||
|     osg::ref_ptr<SceneUtil::LightSource> mGlowLight; |     osg::ref_ptr<SceneUtil::LightSource> mGlowLight; | ||||||
|  |     osg::ref_ptr<GlowUpdater> mGlowUpdater; | ||||||
| 
 | 
 | ||||||
|     float mAlpha; |     float mAlpha; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue