Use glow for enchanted arrows (feature #5122)

pull/2476/head
Andrei Kortunov 5 years ago
parent 1eed511b4e
commit 8557346fbd

@ -157,6 +157,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

@ -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));
}
@ -272,7 +272,7 @@ void ActorAnimation::updateHolsteredWeapon(bool showHolsteredWeapons)
if (isEnchanted)
{
osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon);
addGlow(weaponNode, glowColor);
mGlowUpdater = SceneUtil::addEnchantedGlow(weaponNode, mResourceSystem, glowColor);
}
}
}
@ -354,7 +354,7 @@ void ActorAnimation::updateQuiver()
osg::ref_ptr<osg::Group> arrowNode = ammoNode->getChild(i)->asGroup();
osg::ref_ptr<osg::Node> arrow = mResourceSystem->getSceneManager()->getInstance(model, arrowNode);
if (!ammo->getClass().getEnchantment(*ammo).empty())
addGlow(arrow, glowColor);
mGlowUpdater = SceneUtil::addEnchantedGlow(arrow, mResourceSystem, glowColor);
}
}

@ -3,8 +3,6 @@
#include <iomanip>
#include <limits>
#include <osg/TexGen>
#include <osg/TexEnvCombine>
#include <osg/MatrixTransform>
#include <osg/BlendFunc>
#include <osg/Material>
@ -16,10 +14,8 @@
#include <components/debug/debuglog.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/resource/keyframemanager.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/misc/constants.hpp>
@ -519,135 +515,6 @@ namespace MWRender
float mAlpha;
};
class GlowUpdater : public SceneUtil::StateSetUpdater
{
public:
GlowUpdater(int texUnit, const osg::Vec4f& color, const std::vector<osg::ref_ptr<osg::Texture2D> >& 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<osg::ref_ptr<osg::Texture2D> > 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<const NifOsg::KeyframeHolder> 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,51 +1459,8 @@ namespace MWRender
mGlowUpdater->setDuration(glowDuration);
}
else
addGlow(mObjectRoot, glowColor, glowDuration);
}
}
void Animation::addGlow(osg::ref_ptr<osg::Node> node, osg::Vec4f glowColor, float glowDuration)
{
std::vector<osg::ref_ptr<osg::Texture2D> > 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<osg::Image> image = mResourceSystem->getImageManager()->getImage(stream.str());
osg::ref_ptr<osg::Texture2D> 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> 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<osg::StateSet> writableStateSet = nullptr;
if (!node->getStateSet())
writableStateSet = node->getOrCreateStateSet();
else
{
writableStateSet = new osg::StateSet(*node->getStateSet(), osg::CopyOp::SHALLOW_COPY);
node->setStateSet(writableStateSet);
mGlowUpdater = SceneUtil::addEnchantedGlow(mObjectRoot, mResourceSystem, glowColor, glowDuration);
}
writableStateSet->setTextureAttributeAndModes(texUnit, textures.front(), osg::StateAttribute::ON);
writableStateSet->addUniform(new osg::Uniform("envMapColor", glowColor));
mResourceSystem->getSceneManager()->recreateShaders(node);
}
void Animation::addExtraLight(osg::ref_ptr<osg::Group> parent, const ESM::Light *esmLight)
@ -1976,7 +1781,7 @@ namespace MWRender
addAnimSource(model, model);
if (!ptr.getClass().getEnchantment(ptr).empty())
addGlow(mObjectRoot, ptr.getClass().getEnchantmentColor(ptr));
mGlowUpdater = SceneUtil::addEnchantedGlow(mObjectRoot, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));
}
if (ptr.getTypeName() == typeid(ESM::Light).name() && allowLight)
addExtraLight(getOrCreateObjectRoot(), ptr.get<ESM::Light>()->mBase);

@ -4,6 +4,7 @@
#include "../mwworld/ptr.hpp"
#include <components/sceneutil/controller.hpp>
#include <components/sceneutil/util.hpp>
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<SceneUtil::LightSource> mGlowLight;
osg::ref_ptr<GlowUpdater> mGlowUpdater;
osg::ref_ptr<SceneUtil::GlowUpdater> mGlowUpdater;
osg::ref_ptr<TransparencyUpdater> mTransparencyUpdater;
float mAlpha;
@ -330,8 +330,6 @@ protected:
*/
virtual void addControllers();
void addGlow(osg::ref_ptr<osg::Node> 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();

@ -132,7 +132,7 @@ void CreatureWeaponAnimation::updatePart(PartHolderPtr& scene, int slot)
scene.reset(new PartHolder(attached));
if (!item.getClass().getEnchantment(item).empty())
addGlow(attached, item.getClass().getEnchantmentColor(item));
mGlowUpdater = SceneUtil::addEnchantedGlow(attached, mResourceSystem, item.getClass().getEnchantmentColor(item));
// Crossbows start out with a bolt attached
// FIXME: code duplicated from NpcAnimation
@ -180,7 +180,7 @@ void CreatureWeaponAnimation::attachArrow()
{
osg::Group* bone = getArrowBone();
if (bone != nullptr && bone->getNumChildren())
addGlow(bone->getChild(0), ammo->getClass().getEnchantmentColor(*ammo));
SceneUtil::addEnchantedGlow(bone->getChild(0), mResourceSystem, ammo->getClass().getEnchantmentColor(*ammo));
}
updateQuiver();

@ -664,7 +664,7 @@ PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const st
osg::ref_ptr<osg::Node> attached = SceneUtil::attach(instance, mObjectRoot, bonefilter, found->second);
if (enchantedGlow)
addGlow(attached, *glowColor);
mGlowUpdater = SceneUtil::addEnchantedGlow(attached, mResourceSystem, *glowColor);
return PartHolderPtr(new PartHolder(attached));
}
@ -954,7 +954,7 @@ void NpcAnimation::attachArrow()
{
osg::Group* bone = getArrowBone();
if (bone != nullptr && bone->getNumChildren())
addGlow(bone->getChild(0), ammo->getClass().getEnchantmentColor(*ammo));
SceneUtil::addEnchantedGlow(bone->getChild(0), mResourceSystem, ammo->getClass().getEnchantmentColor(*ammo));
}
updateQuiver();

@ -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);
}

@ -1,10 +1,151 @@
#include "util.hpp"
#include <algorithm>
#include <sstream>
#include <iomanip>
#include <osg/Node>
#include <osg/NodeVisitor>
#include <osg/TexGen>
#include <osg/TexEnvCombine>
#include <components/resource/imagemanager.hpp>
#include <components/resource/scenemanager.hpp>
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<osg::ref_ptr<osg::Texture2D> >& 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<GlowUpdater> addEnchantedGlow(osg::ref_ptr<osg::Node> node, Resource::ResourceSystem* resourceSystem, osg::Vec4f glowColor, float glowDuration)
{
std::vector<osg::ref_ptr<osg::Texture2D> > 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<osg::Image> image = resourceSystem->getImageManager()->getImage(stream.str());
osg::ref_ptr<osg::Texture2D> 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> 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<osg::StateSet> 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;
}
}

@ -3,10 +3,48 @@
#include <osg/Matrix>
#include <osg/BoundingSphere>
#include <osg/NodeCallback>
#include <osg/Texture2D>
#include <osg/Vec4f>
#include <components/resource/resourcesystem.hpp>
#include "statesetupdater.hpp"
namespace SceneUtil
{
class GlowUpdater : public SceneUtil::StateSetUpdater
{
public:
GlowUpdater(int texUnit, const osg::Vec4f& color, const std::vector<osg::ref_ptr<osg::Texture2D> >& 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<osg::ref_ptr<osg::Texture2D> > 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<GlowUpdater> addEnchantedGlow(osg::ref_ptr<osg::Node> node, Resource::ResourceSystem* resourceSystem, osg::Vec4f glowColor, float glowDuration=-1);
}
#endif

Loading…
Cancel
Save