Commit 805843d7 authored by scrawl's avatar scrawl

Closes #1086: Implement blood effects

parent 240d96a0
......@@ -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);
}
......
#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);
}
}
}
#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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment