forked from teamnwah/openmw-tes3coop
Closes #1086: Implement blood effects
This commit is contained in:
parent
240d96a0f1
commit
805843d7ff
20 changed files with 263 additions and 23 deletions
|
@ -680,7 +680,7 @@ std::string creatureFlags(int flags)
|
|||
if (flags & ESM::Creature::Walks) properties += "Walks ";
|
||||
if (flags & ESM::Creature::Swims) properties += "Swims ";
|
||||
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::Weapon) properties += "Weapon ";
|
||||
if (flags & ESM::Creature::Skeleton) properties += "Skeleton ";
|
||||
|
@ -691,7 +691,7 @@ std::string creatureFlags(int flags)
|
|||
ESM::Creature::Walks|
|
||||
ESM::Creature::Swims|
|
||||
ESM::Creature::Flies|
|
||||
ESM::Creature::Biped|
|
||||
ESM::Creature::Bipedal|
|
||||
ESM::Creature::Respawn|
|
||||
ESM::Creature::Weapon|
|
||||
ESM::Creature::Skeleton|
|
||||
|
|
|
@ -181,7 +181,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
|
|||
unsigned int mFlag;
|
||||
} sCreatureFlagTable[] =
|
||||
{
|
||||
{ Columns::ColumnId_Biped, ESM::Creature::Biped },
|
||||
{ Columns::ColumnId_Biped, ESM::Creature::Bipedal },
|
||||
{ Columns::ColumnId_HasWeapon, ESM::Creature::Weapon },
|
||||
{ Columns::ColumnId_NoMovement, ESM::Creature::None },
|
||||
{ Columns::ColumnId_Swims, ESM::Creature::Swims },
|
||||
|
|
|
@ -20,7 +20,7 @@ add_openmw_dir (mwrender
|
|||
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
|
||||
actors objects renderinginterface localmap occlusionquery water shadows
|
||||
characterpreview globalmap videoplayer ripplesimulation refraction
|
||||
terrainstorage renderconst
|
||||
terrainstorage renderconst effectmanager
|
||||
)
|
||||
|
||||
add_openmw_dir (mwinput
|
||||
|
|
|
@ -463,6 +463,9 @@ namespace MWBase
|
|||
|
||||
/// Spawn a random creature from a levelled list next to the player
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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::fMaxWalkSpeedCreature;
|
||||
const ESM::GameSetting *Creature::fEncumberedMoveEffect;
|
||||
|
|
|
@ -111,6 +111,9 @@ namespace MWClass
|
|||
virtual bool isFlying (const MWWorld::Ptr &ptr) 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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -455,7 +455,9 @@ namespace MWClass
|
|||
weapon.get<ESM::Weapon>()->mBase->mData.mReach :
|
||||
gmst.find("fHandToHandReach")->getFloat());
|
||||
// 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
|
||||
return;
|
||||
|
||||
|
@ -602,6 +604,8 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
|
||||
|
||||
othercls.onHit(victim, damage, healthdmg, weapon, ptr, true);
|
||||
}
|
||||
|
||||
|
@ -1216,6 +1220,17 @@ namespace MWClass
|
|||
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::fMaxWalkSpeed;
|
||||
const ESM::GameSetting *Npc::fEncumberedMoveEffect;
|
||||
|
|
|
@ -142,6 +142,9 @@ namespace MWClass
|
|||
|
||||
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 {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1042,6 +1042,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
|
|||
else
|
||||
params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model);
|
||||
|
||||
// TODO: turn off shadow casting
|
||||
setRenderProperties(params.mObjects, RV_Misc,
|
||||
RQG_Main, RQG_Alpha, 0.f, false, NULL);
|
||||
|
||||
|
|
|
@ -189,16 +189,18 @@ protected:
|
|||
/** 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);
|
||||
|
||||
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();
|
||||
|
||||
// TODO: Should not be here
|
||||
Ogre::Vector3 getEnchantmentColor(MWWorld::Ptr item);
|
||||
|
||||
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);
|
||||
virtual ~Animation();
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
|
|||
setObjectRoot(model, false);
|
||||
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(model);
|
||||
}
|
||||
|
|
117
apps/openmw/mwrender/effectmanager.cpp
Normal file
117
apps/openmw/mwrender/effectmanager.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
31
apps/openmw/mwrender/effectmanager.hpp
Normal file
31
apps/openmw/mwrender/effectmanager.hpp
Normal 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
|
|
@ -41,6 +41,7 @@
|
|||
#include "globalmap.hpp"
|
||||
#include "videoplayer.hpp"
|
||||
#include "terrainstorage.hpp"
|
||||
#include "effectmanager.hpp"
|
||||
|
||||
using namespace MWRender;
|
||||
using namespace Ogre;
|
||||
|
@ -57,9 +58,11 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
|
|||
, mSunEnabled(0)
|
||||
, mPhysicsEngine(engine)
|
||||
, mTerrain(NULL)
|
||||
, mEffectManager(NULL)
|
||||
{
|
||||
mActors = new MWRender::Actors(mRendering, this);
|
||||
mObjects = new MWRender::Objects(mRendering);
|
||||
mEffectManager = new EffectManager(mRendering.getScene());
|
||||
// select best shader mode
|
||||
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);
|
||||
|
@ -193,6 +196,7 @@ RenderingManager::~RenderingManager ()
|
|||
delete mVideoPlayer;
|
||||
delete mActors;
|
||||
delete mObjects;
|
||||
delete mEffectManager;
|
||||
delete mFactory;
|
||||
}
|
||||
|
||||
|
@ -374,6 +378,8 @@ void RenderingManager::update (float duration, bool paused)
|
|||
if(paused)
|
||||
return;
|
||||
|
||||
mEffectManager->update(duration);
|
||||
|
||||
mActors->update (mRendering.getCamera());
|
||||
mPlayerAnimation->preRender(mRendering.getCamera());
|
||||
mObjects->update (duration, mRendering.getCamera());
|
||||
|
@ -675,14 +681,14 @@ Shadows* RenderingManager::getShadows()
|
|||
|
||||
void RenderingManager::switchToInterior()
|
||||
{
|
||||
// causes light flicker in opengl when moving..
|
||||
//mRendering.getScene()->setCameraRelativeRendering(false);
|
||||
// TODO: also do this when switching worldspace
|
||||
mEffectManager->clear();
|
||||
}
|
||||
|
||||
void RenderingManager::switchToExterior()
|
||||
{
|
||||
// causes light flicker in opengl when moving..
|
||||
//mRendering.getScene()->setCameraRelativeRendering(true);
|
||||
// TODO: also do this when switching worldspace
|
||||
mEffectManager->clear();
|
||||
}
|
||||
|
||||
Ogre::Vector4 RenderingManager::boundingBoxToScreen(Ogre::AxisAlignedBox bounds)
|
||||
|
@ -1020,4 +1026,9 @@ float RenderingManager::getCameraDistance() const
|
|||
return mCamera->getCameraDistance();
|
||||
}
|
||||
|
||||
void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition)
|
||||
{
|
||||
mEffectManager->addEffect(model, texture, worldPosition);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -21,10 +21,7 @@
|
|||
|
||||
namespace Ogre
|
||||
{
|
||||
class SceneManager;
|
||||
class SceneNode;
|
||||
class Quaternion;
|
||||
class Vector3;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
|
@ -51,6 +48,7 @@ namespace MWRender
|
|||
class GlobalMap;
|
||||
class VideoPlayer;
|
||||
class Animation;
|
||||
class EffectManager;
|
||||
|
||||
class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener
|
||||
{
|
||||
|
@ -209,6 +207,8 @@ public:
|
|||
void stopVideo();
|
||||
void frameStarted(float dt, bool paused);
|
||||
|
||||
void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition);
|
||||
|
||||
protected:
|
||||
virtual void windowResized(int x, int y);
|
||||
|
||||
|
@ -239,6 +239,8 @@ private:
|
|||
MWRender::Objects* mObjects;
|
||||
MWRender::Actors* mActors;
|
||||
|
||||
MWRender::EffectManager* mEffectManager;
|
||||
|
||||
MWRender::NpcAnimation *mPlayerAnimation;
|
||||
|
||||
// 0 normal, 1 more bright, 2 max
|
||||
|
|
|
@ -363,4 +363,8 @@ namespace MWWorld
|
|||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -278,6 +278,9 @@ namespace MWWorld
|
|||
|
||||
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
|
||||
copyToCell(const Ptr &ptr, CellStore &cell) const;
|
||||
|
||||
|
|
|
@ -2610,4 +2610,31 @@ namespace MWWorld
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -549,6 +549,9 @@ namespace MWWorld
|
|||
|
||||
/// Spawn a random creature from a levelled list next to the player
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -25,16 +25,20 @@ struct Creature
|
|||
// Default is 0x48?
|
||||
enum Flags
|
||||
{
|
||||
Biped = 0x001,
|
||||
Respawn = 0x002,
|
||||
Weapon = 0x004, // Has weapon and shield
|
||||
None = 0x008, // ??
|
||||
// Movement types
|
||||
Bipedal = 0x001,
|
||||
Swims = 0x010,
|
||||
Flies = 0x020, // Don't know what happens if several
|
||||
Walks = 0x040, // of these are set
|
||||
|
||||
Respawn = 0x002,
|
||||
Weapon = 0x004, // Has weapon and shield
|
||||
None = 0x008, // ??
|
||||
Essential = 0x080,
|
||||
Skeleton = 0x400, // Does not have normal blood
|
||||
Metal = 0x800 // Has 'golden' blood
|
||||
|
||||
// Blood types
|
||||
Skeleton = 0x400,
|
||||
Metal = 0x800
|
||||
};
|
||||
|
||||
enum Type
|
||||
|
|
Loading…
Reference in a new issue