diff --git a/CHANGELOG.md b/CHANGELOG.md index aa4f03420..57551301f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -158,6 +158,7 @@ Feature #5051: Provide a separate textures for scrollbars Feature #5094: Unix like console hotkeys Feature #5098: Allow user controller bindings + Feature #5122: Use magic glow for enchanted arrows Task #4686: Upgrade media decoder to a more current FFmpeg API Task #4695: Optimize Distant Terrain memory consumption Task #4789: Optimize cell transitions diff --git a/apps/openmw/mwrender/actoranimation.cpp b/apps/openmw/mwrender/actoranimation.cpp index c54e28426..8eac63b2f 100644 --- a/apps/openmw/mwrender/actoranimation.cpp +++ b/apps/openmw/mwrender/actoranimation.cpp @@ -79,7 +79,7 @@ PartHolderPtr ActorAnimation::getWeaponPart(const std::string& model, const std: return PartHolderPtr(); if (enchantedGlow) - addGlow(instance, *glowColor); + mGlowUpdater = SceneUtil::addEnchantedGlow(instance, mResourceSystem, *glowColor); return PartHolderPtr(new PartHolder(instance)); } @@ -238,7 +238,7 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons) { if (showHolsteredWeapons) { - osg::Vec4f glowColor = getEnchantmentColor(*weapon); + osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon); mScabbard = getWeaponPart(mesh, boneName, isEnchanted, &glowColor); if (mScabbard) resetControllers(mScabbard->getNode()); @@ -271,8 +271,8 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons) if (isEnchanted) { - osg::Vec4f glowColor = getEnchantmentColor(*weapon); - addGlow(weaponNode, glowColor); + osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon); + mGlowUpdater = SceneUtil::addEnchantedGlow(weaponNode, mResourceSystem, glowColor); } } } @@ -347,15 +347,18 @@ void ActorAnimation::updateQuiver() } // Add new ones - osg::Vec4f glowColor = getEnchantmentColor(*ammo); + osg::Vec4f glowColor = ammo->getClass().getEnchantmentColor(*ammo); std::string model = ammo->getClass().getModel(*ammo); for (unsigned int i=0; i arrowNode = ammoNode->getChild(i)->asGroup(); osg::ref_ptr arrow = mResourceSystem->getSceneManager()->getInstance(model, arrowNode); - if (!ammo->getClass().getEnchantment(*ammo).empty()) - addGlow(arrow, glowColor); } + + // Assign GlowUpdater for ammo sheathing bone itself to do not attach it to every arrow + ammoNode->setUpdateCallback(nullptr); + if (ammoCount > 0 && !ammo->getClass().getEnchantment(*ammo).empty()) + SceneUtil::addEnchantedGlow(ammoNode, mResourceSystem, glowColor); } void ActorAnimation::itemAdded(const MWWorld::ConstPtr& item, int /*count*/) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index e341778ac..8f8d7e245 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -3,8 +3,6 @@ #include #include -#include -#include #include #include #include @@ -16,10 +14,8 @@ #include -#include #include #include -#include #include @@ -519,135 +515,6 @@ namespace MWRender float mAlpha; }; - class GlowUpdater : public SceneUtil::StateSetUpdater - { - public: - GlowUpdater(int texUnit, const osg::Vec4f& color, const std::vector >& textures, - 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) - { - if (mDone) - removeTexture(stateset); - else - { - 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); - stateset->addUniform(new osg::Uniform("envMapColor", mColor)); - } - } - - void removeTexture(osg::StateSet* stateset) - { - stateset->removeTextureAttribute(mTexUnit, osg::StateAttribute::TEXTURE); - stateset->removeTextureAttribute(mTexUnit, osg::StateAttribute::TEXGEN); - stateset->removeTextureAttribute(mTexUnit, osg::StateAttribute::TEXENV); - stateset->removeTextureMode(mTexUnit, GL_TEXTURE_2D); - stateset->removeUniform("envMapColor"); - - osg::StateSet::TextureAttributeList& list = stateset->getTextureAttributeList(); - while (list.size() && list.rbegin()->empty()) - list.pop_back(); - } - - virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv) - { - if (mColorChanged){ - this->reset(); - setDefaults(stateset); - 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 - { - removeTexture(stateset); - this->reset(); - mDone = true; - mResourceSystem->getSceneManager()->recreateShaders(mNode); - } - if (mOriginalDuration < 0) // if this glowupdater was originally a permanent glow - { - mDuration = mOriginalDuration; - mStartingTime = 0; - mColor = mOriginalColor; - this->reset(); - setDefaults(stateset); - } - } - } - - bool isPermanentGlowUpdater() - { - return (mDuration < 0); - } - - bool isDone() - { - return mDone; - } - - void setColor(const 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 > mTextures; - 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 { osg::ref_ptr mKeyframes; @@ -1574,25 +1441,6 @@ namespace MWRender return mObjectRoot.get(); } - class FindLowestUnusedTexUnitVisitor : public osg::NodeVisitor - { - public: - FindLowestUnusedTexUnitVisitor() - : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) - , mLowestUnusedTexUnit(0) - { - } - - virtual void apply(osg::Node& node) - { - if (osg::StateSet* stateset = node.getStateSet()) - mLowestUnusedTexUnit = std::max(mLowestUnusedTexUnit, int(stateset->getTextureAttributeList().size())); - - traverse(node); - } - int mLowestUnusedTexUnit; - }; - void Animation::addSpellCastGlow(const ESM::MagicEffect *effect, float glowDuration) { osg::Vec4f glowColor(1,1,1,1); @@ -1611,78 +1459,10 @@ namespace MWRender mGlowUpdater->setDuration(glowDuration); } else - addGlow(mObjectRoot, glowColor, glowDuration); + mGlowUpdater = SceneUtil::addEnchantedGlow(mObjectRoot, mResourceSystem, glowColor, glowDuration); } } - void Animation::addGlow(osg::ref_ptr node, osg::Vec4f glowColor, float glowDuration) - { - std::vector > textures; - for (int i=0; i<32; ++i) - { - std::stringstream stream; - stream << "textures/magicitem/caust"; - stream << std::setw(2); - stream << std::setfill('0'); - stream << i; - stream << ".dds"; - - osg::ref_ptr image = mResourceSystem->getImageManager()->getImage(stream.str()); - osg::ref_ptr tex (new osg::Texture2D(image)); - tex->setName("envMap"); - 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); - } - - FindLowestUnusedTexUnitVisitor findLowestUnusedTexUnitVisitor; - node->accept(findLowestUnusedTexUnitVisitor); - int texUnit = findLowestUnusedTexUnitVisitor.mLowestUnusedTexUnit; - - osg::ref_ptr 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 - osg::ref_ptr writableStateSet = nullptr; - if (!node->getStateSet()) - writableStateSet = node->getOrCreateStateSet(); - else - { - writableStateSet = new osg::StateSet(*node->getStateSet(), osg::CopyOp::SHALLOW_COPY); - node->setStateSet(writableStateSet); - } - writableStateSet->setTextureAttributeAndModes(texUnit, textures.front(), osg::StateAttribute::ON); - writableStateSet->addUniform(new osg::Uniform("envMapColor", glowColor)); - mResourceSystem->getSceneManager()->recreateShaders(node); - } - - // TODO: Should not be here - osg::Vec4f Animation::getEnchantmentColor(const MWWorld::ConstPtr& item) const - { - osg::Vec4f result(1,1,1,1); - std::string enchantmentName = item.getClass().getEnchantment(item); - if (enchantmentName.empty()) - return result; - - const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().search(enchantmentName); - if (!enchantment) - return result; - - assert (enchantment->mEffects.mList.size()); - - const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().search( - enchantment->mEffects.mList.front().mEffectID); - if (!magicEffect) - return result; - - result.x() = magicEffect->mData.mRed / 255.f; - result.y() = magicEffect->mData.mGreen / 255.f; - result.z() = magicEffect->mData.mBlue / 255.f; - return result; - } - void Animation::addExtraLight(osg::ref_ptr parent, const ESM::Light *esmLight) { bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior(); @@ -2001,7 +1781,7 @@ namespace MWRender addAnimSource(model, model); if (!ptr.getClass().getEnchantment(ptr).empty()) - addGlow(mObjectRoot, getEnchantmentColor(ptr)); + mGlowUpdater = SceneUtil::addEnchantedGlow(mObjectRoot, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr)); } if (ptr.getTypeName() == typeid(ESM::Light).name() && allowLight) addExtraLight(getOrCreateObjectRoot(), ptr.get()->mBase); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 95007d888..763c7a917 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -4,6 +4,7 @@ #include "../mwworld/ptr.hpp" #include +#include namespace ESM { @@ -34,7 +35,6 @@ namespace MWRender class ResetAccumRootCallback; class RotateController; -class GlowUpdater; class TransparencyUpdater; class EffectAnimationTime : public SceneUtil::ControllerSource @@ -266,7 +266,7 @@ protected: bool mHasMagicEffects; osg::ref_ptr mGlowLight; - osg::ref_ptr mGlowUpdater; + osg::ref_ptr mGlowUpdater; osg::ref_ptr mTransparencyUpdater; float mAlpha; @@ -330,10 +330,6 @@ protected: */ virtual void addControllers(); - osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const; - - 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(); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index aeee69ab0..db88c3487 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -132,7 +132,7 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot) scene.reset(new PartHolder(attached)); if (!item.getClass().getEnchantment(item).empty()) - addGlow(attached, getEnchantmentColor(item)); + mGlowUpdater = SceneUtil::addEnchantedGlow(attached, mResourceSystem, item.getClass().getEnchantmentColor(item)); // Crossbows start out with a bolt attached // FIXME: code duplicated from NpcAnimation @@ -173,6 +173,16 @@ bool CreatureWeaponAnimation::isArrowAttached() const void CreatureWeaponAnimation::attachArrow() { WeaponAnimation::attachArrow(mPtr); + + const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (ammo != inv.end() && !ammo->getClass().getEnchantment(*ammo).empty()) + { + osg::Group* bone = getArrowBone(); + if (bone != nullptr && bone->getNumChildren()) + SceneUtil::addEnchantedGlow(bone->getChild(0), mResourceSystem, ammo->getClass().getEnchantmentColor(*ammo)); + } + updateQuiver(); } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index e7f7d878b..86fc54f68 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -572,7 +572,7 @@ void NpcAnimation::updateParts() int prio = 1; bool enchantedGlow = !store->getClass().getEnchantment(*store).empty(); - osg::Vec4f glowColor = getEnchantmentColor(*store); + osg::Vec4f glowColor = store->getClass().getEnchantmentColor(*store); if(store->getTypeName() == typeid(ESM::Clothing).name()) { prio = ((slotlist[i].mBasePriority+1)<<1) + 0; @@ -664,7 +664,7 @@ PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const st osg::ref_ptr attached = SceneUtil::attach(instance, mObjectRoot, bonefilter, found->second); if (enchantedGlow) - addGlow(attached, *glowColor); + mGlowUpdater = SceneUtil::addEnchantedGlow(attached, mResourceSystem, *glowColor); return PartHolderPtr(new PartHolder(attached)); } @@ -897,7 +897,7 @@ void NpcAnimation::showWeapons(bool showWeapon) MWWorld::ConstContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if(weapon != inv.end()) { - osg::Vec4f glowColor = getEnchantmentColor(*weapon); + osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon); std::string mesh = weapon->getClass().getModel(*weapon); addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor); @@ -931,7 +931,7 @@ void NpcAnimation::showCarriedLeft(bool show) MWWorld::ConstContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(show && iter != inv.end()) { - osg::Vec4f glowColor = getEnchantmentColor(*iter); + osg::Vec4f glowColor = iter->getClass().getEnchantmentColor(*iter); std::string mesh = iter->getClass().getModel(*iter); if (addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor)) @@ -947,6 +947,16 @@ void NpcAnimation::showCarriedLeft(bool show) void NpcAnimation::attachArrow() { WeaponAnimation::attachArrow(mPtr); + + const MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); + MWWorld::ConstContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (ammo != inv.end() && !ammo->getClass().getEnchantment(*ammo).empty()) + { + osg::Group* bone = getArrowBone(); + if (bone != nullptr && bone->getNumChildren()) + SceneUtil::addEnchantedGlow(bone->getChild(0), mResourceSystem, ammo->getClass().getEnchantmentColor(*ammo)); + } + updateQuiver(); } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 472db15c5..f84c7bb8b 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -507,4 +507,28 @@ namespace MWWorld { throw std::runtime_error("class does not support armor ratings"); } + + osg::Vec4f Class::getEnchantmentColor(const MWWorld::ConstPtr& item) const + { + osg::Vec4f result(1,1,1,1); + std::string enchantmentName = item.getClass().getEnchantment(item); + if (enchantmentName.empty()) + return result; + + const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().search(enchantmentName); + if (!enchantment) + return result; + + assert (enchantment->mEffects.mList.size()); + + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().search( + enchantment->mEffects.mList.front().mEffectID); + if (!magicEffect) + return result; + + result.x() = magicEffect->mData.mRed / 255.f; + result.y() = magicEffect->mData.mGreen / 255.f; + result.z() = magicEffect->mData.mBlue / 255.f; + return result; + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 2be54e7ba..8e5cfb981 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -6,6 +6,8 @@ #include #include +#include + #include "ptr.hpp" namespace ESM @@ -367,6 +369,8 @@ namespace MWWorld /// Get the effective armor rating, factoring in the actor's skills, for the given armor. virtual float getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const; + + virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const; }; } diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 19a255d8d..e7a3e8af1 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -323,6 +323,8 @@ namespace MWWorld MWWorld::Ptr ptr = ref.getPtr(); createModel(state, ptr.getClass().getModel(ptr), pos, orient, false, false, osg::Vec4(0,0,0,0)); + if (!ptr.getClass().getEnchantment(ptr).empty()) + SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr)); mProjectiles.push_back(state); } diff --git a/components/sceneutil/util.cpp b/components/sceneutil/util.cpp index a9857bed3..ff46329e5 100644 --- a/components/sceneutil/util.cpp +++ b/components/sceneutil/util.cpp @@ -1,10 +1,151 @@ #include "util.hpp" +#include +#include +#include + #include +#include +#include +#include + +#include +#include namespace SceneUtil { +class FindLowestUnusedTexUnitVisitor : public osg::NodeVisitor +{ +public: + FindLowestUnusedTexUnitVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mLowestUnusedTexUnit(0) + { + } + + virtual void apply(osg::Node& node) + { + if (osg::StateSet* stateset = node.getStateSet()) + mLowestUnusedTexUnit = std::max(mLowestUnusedTexUnit, int(stateset->getTextureAttributeList().size())); + + traverse(node); + } + int mLowestUnusedTexUnit; +}; + +GlowUpdater::GlowUpdater(int texUnit, const osg::Vec4f& color, const std::vector >& textures, + 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) +{ +} + +void GlowUpdater::setDefaults(osg::StateSet *stateset) +{ + if (mDone) + removeTexture(stateset); + else + { + 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); + stateset->addUniform(new osg::Uniform("envMapColor", mColor)); + } +} + +void GlowUpdater::removeTexture(osg::StateSet* stateset) +{ + stateset->removeTextureAttribute(mTexUnit, osg::StateAttribute::TEXTURE); + stateset->removeTextureAttribute(mTexUnit, osg::StateAttribute::TEXGEN); + stateset->removeTextureAttribute(mTexUnit, osg::StateAttribute::TEXENV); + stateset->removeTextureMode(mTexUnit, GL_TEXTURE_2D); + stateset->removeUniform("envMapColor"); + + osg::StateSet::TextureAttributeList& list = stateset->getTextureAttributeList(); + while (list.size() && list.rbegin()->empty()) + list.pop_back(); +} + +void GlowUpdater::apply(osg::StateSet *stateset, osg::NodeVisitor *nv) +{ + if (mColorChanged){ + this->reset(); + setDefaults(stateset); + 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 + { + removeTexture(stateset); + this->reset(); + mDone = true; + mResourceSystem->getSceneManager()->recreateShaders(mNode); + } + if (mOriginalDuration < 0) // if this glowupdater was originally a permanent glow + { + mDuration = mOriginalDuration; + mStartingTime = 0; + mColor = mOriginalColor; + this->reset(); + setDefaults(stateset); + } + } +} + +bool GlowUpdater::isPermanentGlowUpdater() +{ + return (mDuration < 0); +} + +bool GlowUpdater::isDone() +{ + return mDone; +} + +void GlowUpdater::setColor(const osg::Vec4f& color) +{ + mColor = color; + mColorChanged = true; +} + +void GlowUpdater::setDuration(float duration) +{ + mDuration = duration; +} + void transformBoundingSphere (const osg::Matrixf& matrix, osg::BoundingSphere& bsphere) { osg::BoundingSphere::vec_type xdash = bsphere._center; @@ -73,4 +214,48 @@ bool hasUserDescription(const osg::Node* node, const std::string pattern) return false; } +osg::ref_ptr addEnchantedGlow(osg::ref_ptr node, Resource::ResourceSystem* resourceSystem, osg::Vec4f glowColor, float glowDuration) +{ + std::vector > textures; + for (int i=0; i<32; ++i) + { + std::stringstream stream; + stream << "textures/magicitem/caust"; + stream << std::setw(2); + stream << std::setfill('0'); + stream << i; + stream << ".dds"; + + osg::ref_ptr image = resourceSystem->getImageManager()->getImage(stream.str()); + osg::ref_ptr tex (new osg::Texture2D(image)); + tex->setName("envMap"); + tex->setWrap(osg::Texture::WRAP_S, osg::Texture2D::REPEAT); + tex->setWrap(osg::Texture::WRAP_T, osg::Texture2D::REPEAT); + resourceSystem->getSceneManager()->applyFilterSettings(tex); + textures.push_back(tex); + } + + FindLowestUnusedTexUnitVisitor findLowestUnusedTexUnitVisitor; + node->accept(findLowestUnusedTexUnitVisitor); + int texUnit = findLowestUnusedTexUnitVisitor.mLowestUnusedTexUnit; + + osg::ref_ptr glowUpdater = new GlowUpdater(texUnit, glowColor, textures, node, glowDuration, resourceSystem); + node->addUpdateCallback(glowUpdater); + + // set a texture now so that the ShaderVisitor can find it + osg::ref_ptr writableStateSet = nullptr; + if (!node->getStateSet()) + writableStateSet = node->getOrCreateStateSet(); + else + { + writableStateSet = new osg::StateSet(*node->getStateSet(), osg::CopyOp::SHALLOW_COPY); + node->setStateSet(writableStateSet); + } + writableStateSet->setTextureAttributeAndModes(texUnit, textures.front(), osg::StateAttribute::ON); + writableStateSet->addUniform(new osg::Uniform("envMapColor", glowColor)); + resourceSystem->getSceneManager()->recreateShaders(node); + + return glowUpdater; +} + } diff --git a/components/sceneutil/util.hpp b/components/sceneutil/util.hpp index f293b9975..5cc8e3a9d 100644 --- a/components/sceneutil/util.hpp +++ b/components/sceneutil/util.hpp @@ -3,10 +3,48 @@ #include #include +#include +#include #include +#include + +#include "statesetupdater.hpp" + namespace SceneUtil { + class GlowUpdater : public SceneUtil::StateSetUpdater + { + public: + GlowUpdater(int texUnit, const osg::Vec4f& color, const std::vector >& textures, + osg::Node* node, float duration, Resource::ResourceSystem* resourcesystem); + + virtual void setDefaults(osg::StateSet *stateset); + + void removeTexture(osg::StateSet* stateset); + virtual void apply(osg::StateSet *stateset, osg::NodeVisitor *nv); + + bool isPermanentGlowUpdater(); + + bool isDone(); + + void setColor(const osg::Vec4f& color); + + void setDuration(float 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 > mTextures; + 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; + }; // Transform a bounding sphere by a matrix // based off private code in osg::Transform @@ -20,6 +58,8 @@ namespace SceneUtil float makeOsgColorComponent (unsigned int value, unsigned int shift); bool hasUserDescription(const osg::Node* node, const std::string pattern); + + osg::ref_ptr addEnchantedGlow(osg::ref_ptr node, Resource::ResourceSystem* resourceSystem, osg::Vec4f glowColor, float glowDuration=-1); } #endif