1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-06 10:15:33 +00:00

Closes #1086: Implement blood effects

This commit is contained in:
scrawl 2014-01-17 10:52:44 +01:00
parent 240d96a0f1
commit 805843d7ff
20 changed files with 263 additions and 23 deletions

View file

@ -680,7 +680,7 @@ std::string creatureFlags(int flags)
if (flags & ESM::Creature::Walks) properties += "Walks "; if (flags & ESM::Creature::Walks) properties += "Walks ";
if (flags & ESM::Creature::Swims) properties += "Swims "; if (flags & ESM::Creature::Swims) properties += "Swims ";
if (flags & ESM::Creature::Flies) properties += "Flies "; if (flags & ESM::Creature::Flies) properties += "Flies ";
if (flags & ESM::Creature::Biped) properties += "Biped "; if (flags & ESM::Creature::Bipedal) properties += "Bipedal ";
if (flags & ESM::Creature::Respawn) properties += "Respawn "; if (flags & ESM::Creature::Respawn) properties += "Respawn ";
if (flags & ESM::Creature::Weapon) properties += "Weapon "; if (flags & ESM::Creature::Weapon) properties += "Weapon ";
if (flags & ESM::Creature::Skeleton) properties += "Skeleton "; if (flags & ESM::Creature::Skeleton) properties += "Skeleton ";
@ -691,7 +691,7 @@ std::string creatureFlags(int flags)
ESM::Creature::Walks| ESM::Creature::Walks|
ESM::Creature::Swims| ESM::Creature::Swims|
ESM::Creature::Flies| ESM::Creature::Flies|
ESM::Creature::Biped| ESM::Creature::Bipedal|
ESM::Creature::Respawn| ESM::Creature::Respawn|
ESM::Creature::Weapon| ESM::Creature::Weapon|
ESM::Creature::Skeleton| ESM::Creature::Skeleton|

View file

@ -181,7 +181,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
unsigned int mFlag; unsigned int mFlag;
} sCreatureFlagTable[] = } sCreatureFlagTable[] =
{ {
{ Columns::ColumnId_Biped, ESM::Creature::Biped }, { Columns::ColumnId_Biped, ESM::Creature::Bipedal },
{ Columns::ColumnId_HasWeapon, ESM::Creature::Weapon }, { Columns::ColumnId_HasWeapon, ESM::Creature::Weapon },
{ Columns::ColumnId_NoMovement, ESM::Creature::None }, { Columns::ColumnId_NoMovement, ESM::Creature::None },
{ Columns::ColumnId_Swims, ESM::Creature::Swims }, { Columns::ColumnId_Swims, ESM::Creature::Swims },

View file

@ -20,7 +20,7 @@ add_openmw_dir (mwrender
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery water shadows actors objects renderinginterface localmap occlusionquery water shadows
characterpreview globalmap videoplayer ripplesimulation refraction characterpreview globalmap videoplayer ripplesimulation refraction
terrainstorage renderconst terrainstorage renderconst effectmanager
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput

View file

@ -463,6 +463,9 @@ namespace MWBase
/// Spawn a random creature from a levelled list next to the player /// Spawn a random creature from a levelled list next to the player
virtual void spawnRandomCreature(const std::string& creatureList) = 0; virtual void spawnRandomCreature(const std::string& creatureList) = 0;
/// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0;
}; };
} }

View file

@ -530,6 +530,17 @@ namespace MWClass
} }
} }
int Creature::getBloodTexture(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
if (ref->mBase->mFlags & ESM::Creature::Skeleton)
return 1;
if (ref->mBase->mFlags & ESM::Creature::Metal)
return 2;
return 0;
}
const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
const ESM::GameSetting *Creature::fEncumberedMoveEffect; const ESM::GameSetting *Creature::fEncumberedMoveEffect;

View file

@ -111,6 +111,9 @@ namespace MWClass
virtual bool isFlying (const MWWorld::Ptr &ptr) const; virtual bool isFlying (const MWWorld::Ptr &ptr) const;
virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::Ptr& ptr) const;
}; };
} }

View file

@ -455,7 +455,9 @@ namespace MWClass
weapon.get<ESM::Weapon>()->mBase->mData.mReach : weapon.get<ESM::Weapon>()->mBase->mData.mReach :
gmst.find("fHandToHandReach")->getFloat()); gmst.find("fHandToHandReach")->getFloat());
// TODO: Use second to work out the hit angle and where to spawn the blood effect // TODO: Use second to work out the hit angle and where to spawn the blood effect
MWWorld::Ptr victim = world->getHitContact(ptr, dist).first; std::pair<MWWorld::Ptr, Ogre::Vector3> result = world->getHitContact(ptr, dist);
MWWorld::Ptr victim = result.first;
Ogre::Vector3 hitPosition = result.second;
if(victim.isEmpty()) // Didn't hit anything if(victim.isEmpty()) // Didn't hit anything
return; return;
@ -602,6 +604,8 @@ namespace MWClass
} }
} }
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); othercls.onHit(victim, damage, healthdmg, weapon, ptr, true);
} }
@ -1216,6 +1220,17 @@ namespace MWClass
return ptr.getClass().getNpcStats(ptr).getSkill(skill).getModified(); return ptr.getClass().getNpcStats(ptr).getSkill(skill).getModified();
} }
int Npc::getBloodTexture(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
if (ref->mBase->mFlags & ESM::NPC::Skeleton)
return 1;
if (ref->mBase->mFlags & ESM::NPC::Metal)
return 2;
return 0;
}
const ESM::GameSetting *Npc::fMinWalkSpeed; const ESM::GameSetting *Npc::fMinWalkSpeed;
const ESM::GameSetting *Npc::fMaxWalkSpeed; const ESM::GameSetting *Npc::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect; const ESM::GameSetting *Npc::fEncumberedMoveEffect;

View file

@ -142,6 +142,9 @@ namespace MWClass
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::Ptr& ptr) const;
virtual bool isActor() const { virtual bool isActor() const {
return true; return true;
} }

View file

@ -1042,6 +1042,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
else else
params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model);
// TODO: turn off shadow casting
setRenderProperties(params.mObjects, RV_Misc, setRenderProperties(params.mObjects, RV_Misc,
RQG_Main, RQG_Alpha, 0.f, false, NULL); RQG_Main, RQG_Alpha, 0.f, false, NULL);

View file

@ -189,16 +189,18 @@ protected:
/** Adds an additional light to the given object list using the specified ESM record. */ /** Adds an additional light to the given object list using the specified ESM record. */
void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScenePtr objlist, const ESM::Light *light); void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScenePtr objlist, const ESM::Light *light);
static void setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue,
Ogre::uint8 transqueue, Ogre::Real dist=0.0f,
bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL);
void clearAnimSources(); void clearAnimSources();
// TODO: Should not be here // TODO: Should not be here
Ogre::Vector3 getEnchantmentColor(MWWorld::Ptr item); Ogre::Vector3 getEnchantmentColor(MWWorld::Ptr item);
public: public:
// FIXME: Move outside of this class
static void setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue,
Ogre::uint8 transqueue, Ogre::Real dist=0.0f,
bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL);
Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node); Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node);
virtual ~Animation(); virtual ~Animation();

View file

@ -24,7 +24,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
setObjectRoot(model, false); setObjectRoot(model, false);
setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha);
if((ref->mBase->mFlags&ESM::Creature::Biped)) if((ref->mBase->mFlags&ESM::Creature::Bipedal))
addAnimSource("meshes\\base_anim.nif"); addAnimSource("meshes\\base_anim.nif");
addAnimSource(model); addAnimSource(model);
} }

View file

@ -0,0 +1,117 @@
#include "effectmanager.hpp"
#include <OgreSceneManager.h>
#include <OgreParticleSystem.h>
#include "animation.hpp"
#include "renderconst.hpp"
namespace MWRender
{
class EffectAnimationTime : public Ogre::ControllerValue<Ogre::Real>
{
private:
float mTime;
public:
EffectAnimationTime() : mTime(0) { }
void addTime(float time) { mTime += time; }
virtual Ogre::Real getValue() const { return mTime; }
virtual void setValue(Ogre::Real value) {}
};
EffectManager::EffectManager(Ogre::SceneManager *sceneMgr)
: mSceneMgr(sceneMgr)
{
}
void EffectManager::addEffect(const std::string &model, std::string textureOverride, const Ogre::Vector3 &worldPosition)
{
Ogre::SceneNode* sceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(worldPosition);
// fix texture extension to .dds
if (textureOverride.size() > 4)
{
textureOverride[textureOverride.size()-3] = 'd';
textureOverride[textureOverride.size()-2] = 'd';
textureOverride[textureOverride.size()-1] = 's';
}
NifOgre::ObjectScenePtr scene = NifOgre::Loader::createObjects(sceneNode, model);
// TODO: turn off shadow casting
MWRender::Animation::setRenderProperties(scene, RV_Misc,
RQG_Main, RQG_Alpha, 0.f, false, NULL);
for(size_t i = 0;i < scene->mControllers.size();i++)
{
if(scene->mControllers[i].getSource().isNull())
scene->mControllers[i].setSource(Ogre::SharedPtr<EffectAnimationTime> (new EffectAnimationTime()));
}
if (!textureOverride.empty())
{
for(size_t i = 0;i < scene->mParticles.size(); ++i)
{
Ogre::ParticleSystem* partSys = scene->mParticles[i];
Ogre::MaterialPtr mat = scene->mMaterialControllerMgr.getWritableMaterial(partSys);
for (int t=0; t<mat->getNumTechniques(); ++t)
{
Ogre::Technique* tech = mat->getTechnique(t);
for (int p=0; p<tech->getNumPasses(); ++p)
{
Ogre::Pass* pass = tech->getPass(p);
for (int tex=0; tex<pass->getNumTextureUnitStates(); ++tex)
{
Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex);
tus->setTextureName("textures\\" + textureOverride);
}
}
}
}
}
mEffects.push_back(std::make_pair(sceneNode, scene));
}
void EffectManager::update(float dt)
{
for (std::vector<std::pair<Ogre::SceneNode*, NifOgre::ObjectScenePtr> >::iterator it = mEffects.begin(); it != mEffects.end(); )
{
NifOgre::ObjectScenePtr objects = it->second;
for(size_t i = 0; i < objects->mControllers.size() ;i++)
{
EffectAnimationTime* value = dynamic_cast<EffectAnimationTime*>(objects->mControllers[i].getSource().get());
if (value)
value->addTime(dt);
objects->mControllers[i].update();
}
// Finished playing?
if (objects->mControllers[0].getSource()->getValue() >= objects->mMaxControllerLength)
{
Ogre::SceneNode* node = it->first;
it = mEffects.erase(it);
mSceneMgr->destroySceneNode(node);
continue;
}
++it;
}
}
void EffectManager::clear()
{
for (std::vector<std::pair<Ogre::SceneNode*, NifOgre::ObjectScenePtr> >::iterator it = mEffects.begin(); it != mEffects.end(); )
{
Ogre::SceneNode* node = it->first;
it = mEffects.erase(it);
mSceneMgr->destroySceneNode(node);
}
}
}

View file

@ -0,0 +1,31 @@
#ifndef OPENMW_MWRENDER_EFFECTMANAGER_H
#define OPENMW_MWRENDER_EFFECTMANAGER_H
#include <components/nifogre/ogrenifloader.hpp>
namespace MWRender
{
// Note: effects attached to another object should be managed by MWRender::Animation::addEffect.
// This class manages "free" effects, i.e. attached to a dedicated scene node in the world.
class EffectManager
{
public:
EffectManager(Ogre::SceneManager* sceneMgr);
~EffectManager() { clear(); }
/// Add an effect. When it's finished playing, it will be removed automatically.
void addEffect (const std::string& model, std::string textureOverride, const Ogre::Vector3& worldPosition);
void update(float dt);
/// Remove all effects
void clear();
private:
std::vector<std::pair<Ogre::SceneNode*, NifOgre::ObjectScenePtr> > mEffects;
Ogre::SceneManager* mSceneMgr;
};
}
#endif

View file

@ -41,6 +41,7 @@
#include "globalmap.hpp" #include "globalmap.hpp"
#include "videoplayer.hpp" #include "videoplayer.hpp"
#include "terrainstorage.hpp" #include "terrainstorage.hpp"
#include "effectmanager.hpp"
using namespace MWRender; using namespace MWRender;
using namespace Ogre; using namespace Ogre;
@ -57,9 +58,11 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
, mSunEnabled(0) , mSunEnabled(0)
, mPhysicsEngine(engine) , mPhysicsEngine(engine)
, mTerrain(NULL) , mTerrain(NULL)
, mEffectManager(NULL)
{ {
mActors = new MWRender::Actors(mRendering, this); mActors = new MWRender::Actors(mRendering, this);
mObjects = new MWRender::Objects(mRendering); mObjects = new MWRender::Objects(mRendering);
mEffectManager = new EffectManager(mRendering.getScene());
// select best shader mode // select best shader mode
bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos); bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos);
bool glES = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL ES") != std::string::npos); bool glES = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL ES") != std::string::npos);
@ -193,6 +196,7 @@ RenderingManager::~RenderingManager ()
delete mVideoPlayer; delete mVideoPlayer;
delete mActors; delete mActors;
delete mObjects; delete mObjects;
delete mEffectManager;
delete mFactory; delete mFactory;
} }
@ -374,6 +378,8 @@ void RenderingManager::update (float duration, bool paused)
if(paused) if(paused)
return; return;
mEffectManager->update(duration);
mActors->update (mRendering.getCamera()); mActors->update (mRendering.getCamera());
mPlayerAnimation->preRender(mRendering.getCamera()); mPlayerAnimation->preRender(mRendering.getCamera());
mObjects->update (duration, mRendering.getCamera()); mObjects->update (duration, mRendering.getCamera());
@ -675,14 +681,14 @@ Shadows* RenderingManager::getShadows()
void RenderingManager::switchToInterior() void RenderingManager::switchToInterior()
{ {
// causes light flicker in opengl when moving.. // TODO: also do this when switching worldspace
//mRendering.getScene()->setCameraRelativeRendering(false); mEffectManager->clear();
} }
void RenderingManager::switchToExterior() void RenderingManager::switchToExterior()
{ {
// causes light flicker in opengl when moving.. // TODO: also do this when switching worldspace
//mRendering.getScene()->setCameraRelativeRendering(true); mEffectManager->clear();
} }
Ogre::Vector4 RenderingManager::boundingBoxToScreen(Ogre::AxisAlignedBox bounds) Ogre::Vector4 RenderingManager::boundingBoxToScreen(Ogre::AxisAlignedBox bounds)
@ -1020,4 +1026,9 @@ float RenderingManager::getCameraDistance() const
return mCamera->getCameraDistance(); return mCamera->getCameraDistance();
} }
void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition)
{
mEffectManager->addEffect(model, texture, worldPosition);
}
} // namespace } // namespace

View file

@ -21,10 +21,7 @@
namespace Ogre namespace Ogre
{ {
class SceneManager;
class SceneNode; class SceneNode;
class Quaternion;
class Vector3;
} }
namespace MWWorld namespace MWWorld
@ -51,6 +48,7 @@ namespace MWRender
class GlobalMap; class GlobalMap;
class VideoPlayer; class VideoPlayer;
class Animation; class Animation;
class EffectManager;
class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener
{ {
@ -209,6 +207,8 @@ public:
void stopVideo(); void stopVideo();
void frameStarted(float dt, bool paused); void frameStarted(float dt, bool paused);
void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition);
protected: protected:
virtual void windowResized(int x, int y); virtual void windowResized(int x, int y);
@ -239,6 +239,8 @@ private:
MWRender::Objects* mObjects; MWRender::Objects* mObjects;
MWRender::Actors* mActors; MWRender::Actors* mActors;
MWRender::EffectManager* mEffectManager;
MWRender::NpcAnimation *mPlayerAnimation; MWRender::NpcAnimation *mPlayerAnimation;
// 0 normal, 1 more bright, 2 max // 0 normal, 1 more bright, 2 max

View file

@ -363,4 +363,8 @@ namespace MWWorld
throw std::runtime_error("class does not support skills"); throw std::runtime_error("class does not support skills");
} }
int Class::getBloodTexture (const MWWorld::Ptr& ptr) const
{
throw std::runtime_error("class does not support gore");
}
} }

View file

@ -278,6 +278,9 @@ namespace MWWorld
virtual bool isKey (const MWWorld::Ptr& ptr) const { return false; } virtual bool isKey (const MWWorld::Ptr& ptr) const { return false; }
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::Ptr& ptr) const;
virtual Ptr virtual Ptr
copyToCell(const Ptr &ptr, CellStore &cell) const; copyToCell(const Ptr &ptr, CellStore &cell) const;

View file

@ -2610,4 +2610,31 @@ namespace MWWorld
safePlaceObject(ref.getPtr(),*cell,ipos); safePlaceObject(ref.getPtr(),*cell,ipos);
} }
} }
void World::spawnBloodEffect(const Ptr &ptr, const Vector3 &worldPosition)
{
int type = ptr.getClass().getBloodTexture(ptr);
std::string texture;
switch (type)
{
case 2:
texture = getFallback()->getFallbackString("Blood_Texture_2");
break;
case 1:
texture = getFallback()->getFallbackString("Blood_Texture_1");
break;
case 0:
default:
texture = getFallback()->getFallbackString("Blood_Texture_0");
break;
}
std::stringstream modelName;
modelName << "Blood_Model_";
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 3; // [0, 2]
modelName << roll;
std::string model = "meshes\\" + getFallback()->getFallbackString(modelName.str());
mRendering->spawnEffect(model, texture, worldPosition);
}
} }

View file

@ -549,6 +549,9 @@ namespace MWWorld
/// Spawn a random creature from a levelled list next to the player /// Spawn a random creature from a levelled list next to the player
virtual void spawnRandomCreature(const std::string& creatureList); virtual void spawnRandomCreature(const std::string& creatureList);
/// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition);
}; };
} }

View file

@ -25,16 +25,20 @@ struct Creature
// Default is 0x48? // Default is 0x48?
enum Flags enum Flags
{ {
Biped = 0x001, // Movement types
Respawn = 0x002, Bipedal = 0x001,
Weapon = 0x004, // Has weapon and shield
None = 0x008, // ??
Swims = 0x010, Swims = 0x010,
Flies = 0x020, // Don't know what happens if several Flies = 0x020, // Don't know what happens if several
Walks = 0x040, // of these are set Walks = 0x040, // of these are set
Respawn = 0x002,
Weapon = 0x004, // Has weapon and shield
None = 0x008, // ??
Essential = 0x080, Essential = 0x080,
Skeleton = 0x400, // Does not have normal blood
Metal = 0x800 // Has 'golden' blood // Blood types
Skeleton = 0x400,
Metal = 0x800
}; };
enum Type enum Type