Merge pull request #1051 from Allofich/textures

Use spell effect particle textures
pull/59/head
scrawl 8 years ago committed by GitHub
commit cd4b182091

@ -1235,10 +1235,8 @@ bool CharacterController::updateWeaponState()
MWMechanics::CastSpell cast(mPtr, NULL);
cast.playSpellCastingEffects(spellid);
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
const ESM::ENAMstruct &lastEffect = spell->mEffects.mList.at(spell->mEffects.mList.size() - 1);
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
const ESM::ENAMstruct &lastEffect = spell->mEffects.mList.back();
const ESM::MagicEffect *effect;
effect = store.get<ESM::MagicEffect>().find(lastEffect.mEffectID); // use last effect of list for color of VFX_Hands

@ -30,7 +30,6 @@
namespace MWMechanics
{
ESM::Skill::SkillEnum spellSchoolToSkill(int school)
{
std::map<int, ESM::Skill::SkillEnum> schoolSkillMap; // maps spell school to skill id
@ -542,12 +541,14 @@ namespace MWMechanics
else
castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_DefaultHit");
std::string texture = magicEffect->mParticle;
// TODO: VFX are no longer active after saving/reloading the game
bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0;
// Note: in case of non actor, a free effect should be fine as well
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);
if (anim)
anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "");
anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "", texture);
}
}
}
@ -931,15 +932,18 @@ namespace MWMechanics
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster);
if (mCaster.getClass().isActor()) // TODO: Non-actors (except for large statics?) should also create a spell cast vfx
if (mCaster.getClass().isActor()) // TODO: Non-actors should also create a spell cast vfx
{
const ESM::Static* castStatic;
if (!effect->mCasting.empty())
castStatic = store.get<ESM::Static>().find (effect->mCasting);
else
castStatic = store.get<ESM::Static>().find ("VFX_DefaultCast");
animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex);
std::string texture = effect->mParticle;
animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture);
}
if (!mCaster.getClass().isActor())

@ -1342,7 +1342,7 @@ namespace MWRender
SceneUtil::AssignControllerSourcesVisitor assignVisitor(boost::shared_ptr<SceneUtil::ControllerSource>(params.mAnimTime));
node->accept(assignVisitor);
overrideTexture(texture, mResourceSystem, node);
overrideFirstRootTexture(texture, mResourceSystem, node);
// TODO: in vanilla morrowind the effect is scaled based on the host object's bounding box.

@ -25,7 +25,7 @@ EffectManager::~EffectManager()
clear();
}
void EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, float scale)
void EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)
{
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model);
@ -46,7 +46,10 @@ void EffectManager::addEffect(const std::string &model, const std::string& textu
SceneUtil::AssignControllerSourcesVisitor assignVisitor(effect.mAnimTime);
node->accept(assignVisitor);
overrideTexture(textureOverride, mResourceSystem, node);
if (isMagicVFX)
overrideFirstRootTexture(textureOverride, mResourceSystem, node);
else
overrideTexture(textureOverride, mResourceSystem, node);
mParentNode->addChild(trans);
mResourceSystem->getSceneManager()->notifyAttached(node);

@ -33,7 +33,7 @@ namespace MWRender
~EffectManager();
/// Add an effect. When it's finished playing, it will be removed automatically.
void addEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPosition, float scale);
void addEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPosition, float scale, bool isMagicVFX = true);
void update(float dt);

@ -970,12 +970,12 @@ Resource::ResourceSystem* NpcAnimation::getResourceSystem()
return mResourceSystem;
}
void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound)
void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew)
{
// During first auto equip, we don't play any sounds.
// Basically we don't want sounds when the actor is first loaded,
// the items should appear as if they'd always been equipped.
if (playSound)
if (isNew)
{
static const std::string schools[] = {
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
@ -994,7 +994,7 @@ void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, boo
bool loop = (magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0;
// Don't play particle VFX unless the effect is new or it should be looping.
if (isNew || loop)
addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "");
addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "", magicEffect->mParticle);
}
}

@ -23,7 +23,7 @@ class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::I
{
public:
virtual void equipmentChanged();
virtual void permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound);
virtual void permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew);
public:
typedef std::map<ESM::PartReferenceType,std::string> PartBoneMap;

@ -742,9 +742,9 @@ namespace MWRender
mObjects->updatePtr(old, updated);
}
void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale)
void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)
{
mEffectManager->addEffect(model, texture, worldPosition, scale);
mEffectManager->addEffect(model, texture, worldPosition, scale, isMagicVFX);
}
void RenderingManager::notifyWorldSpaceChanged()

@ -138,7 +138,7 @@ namespace MWRender
SkyManager* getSkyManager();
void spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale = 1.f);
void spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale = 1.f, bool isMagicVFX = true);
/// Clear all savegame-specific data
void clear();

@ -1,14 +1,47 @@
#include "util.hpp"
#include <osg/Node>
#include <osg/ValueObject>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/sceneutil/visitor.hpp>
namespace MWRender
{
class TextureOverrideVisitor : public osg::NodeVisitor
{
public:
TextureOverrideVisitor(std::string texture, Resource::ResourceSystem* resourcesystem)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mTexture(texture)
, mResourcesystem(resourcesystem)
{
}
virtual void apply(osg::Node& node)
{
int index;
osg::ref_ptr<osg::Node> nodePtr(&node);
if (node.getUserValue("overrideFx", index))
{
if (index == 1)
overrideTexture(mTexture, mResourcesystem, nodePtr);
}
traverse(node);
}
std::string mTexture;
Resource::ResourceSystem* mResourcesystem;
};
void overrideFirstRootTexture(const std::string &texture, Resource::ResourceSystem *resourceSystem, osg::ref_ptr<osg::Node> node)
{
TextureOverrideVisitor overrideVisitor(texture, resourceSystem);
node->accept(overrideVisitor);
}
void overrideTexture(const std::string &texture, Resource::ResourceSystem *resourceSystem, osg::ref_ptr<osg::Node> node)
{
if (texture.empty())

@ -17,6 +17,10 @@ namespace Resource
namespace MWRender
{
// Overrides the texture of nodes in the mesh that had the same NiTexturingProperty as the first NiTexturingProperty of the .NIF file's root node,
// if it had a NiTexturingProperty. Used for applying "particle textures" to magic effects.
void overrideFirstRootTexture(const std::string &texture, Resource::ResourceSystem *resourceSystem, osg::ref_ptr<osg::Node> node);
void overrideTexture(const std::string& texture, Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Node> node);
// Node callback to entirely skip the traversal.

@ -414,8 +414,7 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
// During first auto equip, we don't play any sounds.
// Basically we don't want sounds when the actor is first loaded,
// the items should appear as if they'd always been equipped.
mListener->permanentEffectAdded(magicEffect, !mFirstAutoEquip,
!mFirstAutoEquip && effectIt == enchantment.mEffects.mList.begin());
mListener->permanentEffectAdded(magicEffect, !mFirstAutoEquip);
}
if (magnitude)

@ -32,7 +32,7 @@ namespace MWWorld
* If it isn't new, non-looping VFX should not be played.
* @param playSound Play effect sound?
*/
virtual void permanentEffectAdded (const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) {}
virtual void permanentEffectAdded (const ESM::MagicEffect *magicEffect, bool isNew) {}
};

@ -30,6 +30,7 @@
#include "../mwrender/animation.hpp"
#include "../mwrender/vismask.hpp"
#include "../mwrender/renderingmanager.hpp"
#include "../mwrender/util.hpp"
#include "../mwsound/sound.hpp"
@ -37,7 +38,7 @@
namespace
{
ESM::EffectList getMagicBoltData(std::vector<std::string>& projectileIDs, std::vector<std::string>& sounds, float& speed, const ESM::EffectList& effects)
ESM::EffectList getMagicBoltData(std::vector<std::string>& projectileIDs, std::vector<std::string>& sounds, float& speed, std::string& texture, const ESM::EffectList& effects)
{
int count = 0;
speed = 0.0f;
@ -73,6 +74,14 @@ namespace
if (count != 0)
speed /= count;
// the particle texture is only used if there is only one projectile
if (projectileEffects.mList.size() == 1)
{
const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
effects.mList.begin()->mEffectID);
texture = magicEffect->mParticle;
}
if (projectileEffects.mList.size() > 1) // insert a VFX_Multiple projectile if there are multiple projectile effects
{
@ -127,7 +136,7 @@ namespace MWWorld
};
void ProjectileManager::createModel(State &state, const std::string &model, const osg::Vec3f& pos, const osg::Quat& orient, bool rotate)
void ProjectileManager::createModel(State &state, const std::string &model, const osg::Vec3f& pos, const osg::Quat& orient, bool rotate, std::string texture)
{
state.mNode = new osg::PositionAttitudeTransform;
state.mNode->setNodeMask(MWRender::Mask_Effect);
@ -144,7 +153,7 @@ namespace MWWorld
attachTo = rotateNode;
}
mResourceSystem->getSceneManager()->getInstance(model, attachTo);
osg::ref_ptr<osg::Node> projectile = mResourceSystem->getSceneManager()->getInstance(model, attachTo);
if (state.mIdMagic.size() > 1)
for (size_t iter = 1; iter != state.mIdMagic.size(); ++iter)
@ -169,6 +178,8 @@ namespace MWWorld
SceneUtil::AssignControllerSourcesVisitor assignVisitor (state.mEffectAnimationTime);
state.mNode->accept(assignVisitor);
MWRender::overrideFirstRootTexture(texture, mResourceSystem, projectile);
}
void ProjectileManager::update(State& state, float duration)
@ -207,7 +218,9 @@ namespace MWWorld
state.mActorId = -1;
state.mStack = stack;
state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, effects);
std::string texture = "";
state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, texture, effects);
// Non-projectile should have been removed by getMagicBoltData
if (state.mEffects.mList.empty())
@ -216,7 +229,7 @@ namespace MWWorld
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), state.mIdMagic.at(0));
MWWorld::Ptr ptr = ref.getPtr();
createModel(state, ptr.getClass().getModel(ptr), pos, orient, true);
createModel(state, ptr.getClass().getModel(ptr), pos, orient, true, texture);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
for (size_t it = 0; it != state.mSoundIds.size(); it++)
@ -486,7 +499,8 @@ namespace MWWorld
state.mSpellId = esm.mSpellId;
state.mActorId = esm.mActorId;
state.mStack = esm.mStack;
state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, esm.mEffects);
std::string texture = "";
state.mEffects = getMagicBoltData(state.mIdMagic, state.mSoundIds, state.mSpeed, texture, esm.mEffects);
state.mSpeed = esm.mSpeed; // speed is derived from non-projectile effects as well as
// projectile effects, so we can't calculate it from the save
// file's effect list, which is already trimmed of non-projectile
@ -504,7 +518,7 @@ namespace MWWorld
return true;
}
createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true);
createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true, texture);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();

@ -122,7 +122,7 @@ namespace MWWorld
void moveProjectiles(float dt);
void moveMagicBolts(float dt);
void createModel (State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient, bool rotate);
void createModel (State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient, bool rotate, std::string texture = "");
void update (State& state, float duration);
void operator=(const ProjectileManager&);

@ -3145,7 +3145,7 @@ namespace MWWorld
modelName << roll;
std::string model = "meshes\\" + getFallback()->getFallbackString(modelName.str());
mRendering->spawnEffect(model, texture, worldPosition);
mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false);
}
void World::spawnEffect(const std::string &model, const std::string &textureOverride, const osg::Vec3f &worldPos)
@ -3162,7 +3162,7 @@ namespace MWWorld
{
const ESM::MagicEffect* effect = getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
if ((effectIt->mArea <= 0 && !ignore.isEmpty() && ignore.getClass().isActor()) || effectIt->mRange != rangeType)
if (effectIt->mRange != rangeType || (effectIt->mArea <= 0 && !ignore.isEmpty() && ignore.getClass().isActor()))
continue; // Not right range type, or not area effect and hit an actor
// Spawn the explosion orb effect
@ -3172,13 +3172,15 @@ namespace MWWorld
else
areaStatic = getStore().get<ESM::Static>().find ("VFX_DefaultArea");
std::string texture = effect->mParticle;
if (effectIt->mArea <= 0)
{
mRendering->spawnEffect("meshes\\" + areaStatic->mModel, "", origin, 1.0f);
mRendering->spawnEffect("meshes\\" + areaStatic->mModel, texture, origin, 1.0f);
continue;
}
else
mRendering->spawnEffect("meshes\\" + areaStatic->mModel, "", origin, static_cast<float>(effectIt->mArea * 2));
mRendering->spawnEffect("meshes\\" + areaStatic->mModel, texture, origin, static_cast<float>(effectIt->mArea * 2));
// Play explosion sound (make sure to use NoTrack, since we will delete the projectile now)
static const std::string schools[] = {

@ -6,6 +6,7 @@
#include <osg/Array>
#include <osg/LOD>
#include <osg/TexGen>
#include <osg/ValueObject>
// resource
#include <components/misc/stringops.hpp>
@ -276,11 +277,13 @@ namespace NifOsg
public:
/// @param filename used for warning messages.
LoaderImpl(const std::string& filename)
: mFilename(filename)
: mFilename(filename), mFirstRootTextureIndex(-1), mFoundFirstRootTexturingProperty(false)
{
}
std::string mFilename;
size_t mFirstRootTextureIndex;
bool mFoundFirstRootTexturingProperty;
static void loadKf(Nif::NIFFilePtr nif, KeyframeHolder& target)
{
@ -371,10 +374,24 @@ namespace NifOsg
void applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, std::vector<int>& boundTextures, int animflags)
{
const Nif::PropertyList& props = nifNode->props;
for (size_t i = 0; i <props.length();++i)
for (size_t i = 0; i <props.length(); ++i)
{
if (!props[i].empty())
{
// Get the lowest numbered recIndex of the NiTexturingProperty root node.
// This is what is overridden when a spell effect "particle texture" is used.
if (nifNode->parent == NULL && !mFoundFirstRootTexturingProperty && props[i].getPtr()->recType == Nif::RC_NiTexturingProperty)
{
mFirstRootTextureIndex = props[i].getPtr()->recIndex;
mFoundFirstRootTexturingProperty = true;
}
else if (props[i].getPtr()->recType == Nif::RC_NiTexturingProperty)
{
if (props[i].getPtr()->recIndex == mFirstRootTextureIndex)
applyTo->setUserValue("overrideFx", 1);
}
handleProperty(props[i].getPtr(), applyTo, composite, imageManager, boundTextures, animflags);
}
}
}

Loading…
Cancel
Save