Merge remote-tracking branch 'kcat/light-objects'

actorid
Marc Zinnschlag 12 years ago
commit 932fffd1a7

@ -78,6 +78,7 @@ set(LIBDIR ${CMAKE_SOURCE_DIR}/libs)
set(OENGINE_OGRE set(OENGINE_OGRE
${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/renderer.cpp
${LIBDIR}/openengine/ogre/fader.cpp ${LIBDIR}/openengine/ogre/fader.cpp
${LIBDIR}/openengine/ogre/lights.cpp
${LIBDIR}/openengine/ogre/particles.cpp ${LIBDIR}/openengine/ogre/particles.cpp
${LIBDIR}/openengine/ogre/selectionbuffer.cpp ${LIBDIR}/openengine/ogre/selectionbuffer.cpp
${LIBDIR}/openengine/ogre/imagerotate.cpp ${LIBDIR}/openengine/ogre/imagerotate.cpp

@ -34,11 +34,10 @@ namespace MWClass
MWRender::Objects& objects = renderingInterface.getObjects(); MWRender::Objects& objects = renderingInterface.getObjects();
objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false);
if(!model.empty())
if (!model.empty()) objects.insertMesh(ptr, "meshes\\" + model);
objects.insertMesh(ptr, "meshes\\" + model, true);
else else
objects.insertLight(ptr); objects.insertMesh(ptr, "");
} }
void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const

@ -725,15 +725,15 @@ namespace MWClass
float x = fJumpAcrobaticsBase->getFloat() + float x = fJumpAcrobaticsBase->getFloat() +
std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat()); std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat());
x += 3 * b * fJumpAcroMultiplier->getFloat(); x += 3.0f * b * fJumpAcroMultiplier->getFloat();
x += mageffects.get(MWMechanics::EffectKey(9/*jump*/)).mMagnitude * 64; x += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Jump)).mMagnitude * 64;
x *= encumbranceTerm; x *= encumbranceTerm;
if(Npc::getStance(ptr, Run, false)) if(Npc::getStance(ptr, Run, false))
x *= fJumpRunMultiplier->getFloat(); x *= fJumpRunMultiplier->getFloat();
x *= 1.25f;//fatigueTerm; x *= npcdata->mCreatureStats.getFatigueTerm();
x -= -627.2/*gravity constant*/; x -= -627.2f;/*gravity constant*/
x /= 3; x /= 3.0f;
return x; return x;
} }

@ -268,10 +268,6 @@ namespace MWInput
case A_ToggleHUD: case A_ToggleHUD:
mWindows.toggleHud(); mWindows.toggleHud();
break; break;
case A_Use:
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
mPlayer.use();
break;
} }
} }
} }

@ -24,12 +24,14 @@
#include "movement.hpp" #include "movement.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "security.hpp"
#include "../mwrender/animation.hpp" #include "../mwrender/animation.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -465,7 +467,36 @@ bool CharacterController::updateNpcState()
sndMgr->playSound3D(mPtr, schools[effect->mData.mSchool]+" cast", 1.0f, 1.0f); sndMgr->playSound3D(mPtr, schools[effect->mData.mSchool]+" cast", 1.0f, 1.0f);
} }
} }
else if(mWeaponType != WeapType_PickProbe) else if(mWeaponType == WeapType_PickProbe)
{
MWWorld::Ptr item = *weapon;
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject();
std::string resultMessage, resultSound;
if(!target.isEmpty())
{
if(item.getTypeName() == typeid(ESM::Lockpick).name())
Security(mPtr).pickLock(target, item, resultMessage, resultSound);
else if(item.getTypeName() == typeid(ESM::Probe).name())
Security(mPtr).probeTrap(target, item, resultMessage, resultSound);
}
mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true,
1.0f, "start", "stop", 0.0, 0);
mUpperBodyState = UpperCharState_FollowStartToFollowStop;
if(!resultMessage.empty())
MWBase::Environment::get().getWindowManager()->messageBox(resultMessage);
if(!resultSound.empty())
MWBase::Environment::get().getSoundManager()->playSound(resultSound, 1.0f, 1.0f);
// tool used up?
if(!item.getRefData().getCount())
MWBase::Environment::get().getWindowManager()->unsetSelectedWeapon();
else
MWBase::Environment::get().getWindowManager()->setSelectedWeapon(item);
}
else
{ {
if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow ||
mWeaponType == WeapType_ThowWeapon) mWeaponType == WeapType_ThowWeapon)

@ -110,7 +110,7 @@ namespace MWMechanics
return mMagicEffects; return mMagicEffects;
} }
const bool &CreatureStats::getAttackingOrSpell() const bool CreatureStats::getAttackingOrSpell() const
{ {
return mAttackingOrSpell; return mAttackingOrSpell;
} }
@ -216,7 +216,7 @@ namespace MWMechanics
mMagicEffects = effects; mMagicEffects = effects;
} }
void CreatureStats::setAttackingOrSpell(const bool &attackingOrSpell) void CreatureStats::setAttackingOrSpell(bool attackingOrSpell)
{ {
mAttackingOrSpell = attackingOrSpell; mAttackingOrSpell = attackingOrSpell;
} }

@ -59,7 +59,7 @@ namespace MWMechanics
const MagicEffects & getMagicEffects() const; const MagicEffects & getMagicEffects() const;
const bool & getAttackingOrSpell() const; bool getAttackingOrSpell() const;
int getLevel() const; int getLevel() const;
@ -90,7 +90,7 @@ namespace MWMechanics
void setMagicEffects(const MagicEffects &effects); void setMagicEffects(const MagicEffects &effects);
void setAttackingOrSpell(const bool &attackingOrSpell); void setAttackingOrSpell(bool attackingOrSpell);
enum AttackType enum AttackType
{ {

@ -12,16 +12,16 @@ ActivatorAnimation::~ActivatorAnimation()
} }
ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr)
: Animation(ptr) : Animation(ptr, ptr.getRefData().getBaseNode())
{ {
MWWorld::LiveCellRef<ESM::Activator> *ref = mPtr.get<ESM::Activator>(); MWWorld::LiveCellRef<ESM::Activator> *ref = mPtr.get<ESM::Activator>();
assert (ref->mBase != NULL); assert(ref->mBase != NULL);
if(!ref->mBase->mModel.empty()) if(!ref->mBase->mModel.empty())
{ {
const std::string name = "meshes\\"+ref->mBase->mModel; const std::string name = "meshes\\"+ref->mBase->mModel;
setObjectRoot(mPtr.getRefData().getBaseNode(), name, false); setObjectRoot(name, false);
setRenderProperties(mObjectRoot, RV_Misc, RQG_Main, RQG_Alpha); setRenderProperties(mObjectRoot, RV_Misc, RQG_Main, RQG_Alpha);
addAnimSource(name); addAnimSource(name);

@ -8,6 +8,10 @@
#include <OgreBone.h> #include <OgreBone.h>
#include <OgreSubMesh.h> #include <OgreSubMesh.h>
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
#include <OgreControllerManager.h>
#include <OgreStaticGeometry.h>
#include <libs/openengine/ogre/lights.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
@ -16,6 +20,9 @@
#include "../mwmechanics/character.hpp" #include "../mwmechanics/character.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/fallback.hpp"
#include "renderconst.hpp"
namespace MWRender namespace MWRender
{ {
@ -35,17 +42,26 @@ void Animation::AnimationValue::setValue(Ogre::Real)
void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects) void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects)
{ {
for(size_t i = 0;i < objects.mLights.size();i++)
{
Ogre::Light *light = objects.mLights[i];
// If parent is a scene node, it was created specifically for this light. Destroy it now.
if(light->isAttached() && !light->isParentTagPoint())
sceneMgr->destroySceneNode(light->getParentSceneNode());
sceneMgr->destroyLight(light);
}
for(size_t i = 0;i < objects.mParticles.size();i++) for(size_t i = 0;i < objects.mParticles.size();i++)
sceneMgr->destroyParticleSystem(objects.mParticles[i]); sceneMgr->destroyParticleSystem(objects.mParticles[i]);
for(size_t i = 0;i < objects.mEntities.size();i++) for(size_t i = 0;i < objects.mEntities.size();i++)
sceneMgr->destroyEntity(objects.mEntities[i]); sceneMgr->destroyEntity(objects.mEntities[i]);
objects.mControllers.clear(); objects.mControllers.clear();
objects.mLights.clear();
objects.mParticles.clear(); objects.mParticles.clear();
objects.mEntities.clear(); objects.mEntities.clear();
objects.mSkelBase = NULL; objects.mSkelBase = NULL;
} }
Animation::Animation(const MWWorld::Ptr &ptr) Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node)
: mPtr(ptr) : mPtr(ptr)
, mCamera(NULL) , mCamera(NULL)
, mInsert(NULL) , mInsert(NULL)
@ -58,6 +74,8 @@ Animation::Animation(const MWWorld::Ptr &ptr)
{ {
for(size_t i = 0;i < sNumGroups;i++) for(size_t i = 0;i < sNumGroups;i++)
mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this)); mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this));
mInsert = node ? node->createChildSceneNode() :
mPtr.getRefData().getBaseNode()->createChildSceneNode();
} }
Animation::~Animation() Animation::~Animation()
@ -72,11 +90,15 @@ Animation::~Animation()
} }
void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly) void Animation::setObjectRoot(const std::string &model, bool baseonly)
{ {
OgreAssert(mAnimSources.empty(), "Setting object root while animation sources are set!"); OgreAssert(mAnimSources.empty(), "Setting object root while animation sources are set!");
if(!mInsert)
mInsert = node->createChildSceneNode(); mSkelBase = NULL;
destroyObjectList(mInsert->getCreator(), mObjectRoot);
if(model.empty())
return;
std::string mdlname = Misc::StringUtils::lowerCase(model); std::string mdlname = Misc::StringUtils::lowerCase(model);
std::string::size_type p = mdlname.rfind('\\'); std::string::size_type p = mdlname.rfind('\\');
@ -92,9 +114,6 @@ void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, b
Misc::StringUtils::toLower(mdlname); Misc::StringUtils::toLower(mdlname);
} }
mSkelBase = NULL;
destroyObjectList(mInsert->getCreator(), mObjectRoot);
mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) : mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) :
NifOgre::Loader::createObjectBase(mInsert, mdlname)); NifOgre::Loader::createObjectBase(mInsert, mdlname));
if(mObjectRoot.mSkelBase) if(mObjectRoot.mSkelBase)
@ -140,28 +159,47 @@ void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, b
} }
} }
void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue)
{ class VisQueueSet {
for(size_t i = 0;i < objlist.mEntities.size();i++) Ogre::uint32 mVisFlags;
Ogre::uint8 mSolidQueue, mTransQueue;
Ogre::Real mDist;
public:
VisQueueSet(Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist)
: mVisFlags(visflags), mSolidQueue(solidqueue), mTransQueue(transqueue), mDist(dist)
{ }
void operator()(Ogre::Entity *entity) const
{ {
Ogre::Entity *ent = objlist.mEntities[i]; if(mVisFlags != 0)
if(visflags != 0) entity->setVisibilityFlags(mVisFlags);
ent->setVisibilityFlags(visflags); entity->setRenderingDistance(mDist);
for(unsigned int j = 0;j < ent->getNumSubEntities();++j) unsigned int numsubs = entity->getNumSubEntities();
for(unsigned int i = 0;i < numsubs;++i)
{ {
Ogre::SubEntity* subEnt = ent->getSubEntity(j); Ogre::SubEntity* subEnt = entity->getSubEntity(i);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? transqueue : solidqueue); subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? mTransQueue : mSolidQueue);
} }
} }
for(size_t i = 0;i < objlist.mParticles.size();i++)
void operator()(Ogre::ParticleSystem *psys) const
{ {
Ogre::ParticleSystem *part = objlist.mParticles[i]; if(mVisFlags != 0)
if(visflags != 0) psys->setVisibilityFlags(mVisFlags);
part->setVisibilityFlags(visflags); psys->setRenderingDistance(mDist);
// TODO: Check particle material for actual transparency // TODO: Check particle material for actual transparency
part->setRenderQueueGroup(transqueue); psys->setRenderQueueGroup(mTransQueue);
} }
};
void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist)
{
std::for_each(objlist.mEntities.begin(), objlist.mEntities.end(),
VisQueueSet(visflags, solidqueue, transqueue, dist));
std::for_each(objlist.mParticles.begin(), objlist.mParticles.end(),
VisQueueSet(visflags, solidqueue, transqueue, dist));
} }
@ -254,6 +292,76 @@ void Animation::clearAnimSources()
} }
void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objlist, const ESM::Light *light)
{
const MWWorld::Fallback *fallback = MWBase::Environment::get().getWorld()->getFallback();
const int clr = light->mData.mColor;
Ogre::ColourValue color(((clr >> 0) & 0xFF) / 255.0f,
((clr >> 8) & 0xFF) / 255.0f,
((clr >> 16) & 0xFF) / 255.0f);
const float radius = float(light->mData.mRadius);
if((light->mData.mFlags&ESM::Light::Negative))
color *= -1;
objlist.mLights.push_back(sceneMgr->createLight());
Ogre::Light *olight = objlist.mLights.back();
olight->setDiffuseColour(color);
Ogre::ControllerValueRealPtr src(Ogre::ControllerManager::getSingleton().getFrameTimeSource());
Ogre::ControllerValueRealPtr dest(OGRE_NEW OEngine::Render::LightValue(olight, color));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW OEngine::Render::LightFunction(
(light->mData.mFlags&ESM::Light::Flicker) ? OEngine::Render::LT_Flicker :
(light->mData.mFlags&ESM::Light::FlickerSlow) ? OEngine::Render::LT_FlickerSlow :
(light->mData.mFlags&ESM::Light::Pulse) ? OEngine::Render::LT_Pulse :
(light->mData.mFlags&ESM::Light::PulseSlow) ? OEngine::Render::LT_PulseSlow :
OEngine::Render::LT_Normal
));
objlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(src, dest, func));
bool interior = !(mPtr.isInCell() && mPtr.getCell()->mCell->isExterior());
bool quadratic = fallback->getFallbackBool("LightAttenuation_OutQuadInLin") ?
!interior : fallback->getFallbackBool("LightAttenuation_UseQuadratic");
// with the standard 1 / (c + d*l + d*d*q) equation the attenuation factor never becomes zero,
// so we ignore lights if their attenuation falls below this factor.
const float threshold = 0.03;
if (!quadratic)
{
float r = radius * fallback->getFallbackFloat("LightAttenuation_LinearRadiusMult");
float attenuation = fallback->getFallbackFloat("LightAttenuation_LinearValue") / r;
float activationRange = 1.0f / (threshold * attenuation);
olight->setAttenuation(activationRange, 0, attenuation, 0);
}
else
{
float r = radius * fallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult");
float attenuation = fallback->getFallbackFloat("LightAttenuation_QuadraticValue") / std::pow(r, 2);
float activationRange = std::sqrt(1.0f / (threshold * attenuation));
olight->setAttenuation(activationRange, 0, 0, attenuation);
}
// If there's an AttachLight bone, attach the light to that, otherwise put it in the center,
if(objlist.mSkelBase && objlist.mSkelBase->getSkeleton()->hasBone("AttachLight"))
objlist.mSkelBase->attachObjectToBone("AttachLight", olight);
else
{
Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL;
for(size_t i = 0;i < objlist.mEntities.size();i++)
{
Ogre::Entity *ent = objlist.mEntities[i];
bounds.merge(ent->getBoundingBox());
}
Ogre::SceneNode *node = bounds.isFinite() ? mInsert->createChildSceneNode(bounds.getCenter())
: mInsert->createChildSceneNode();
node->attachObject(olight);
}
}
Ogre::Node *Animation::getNode(const std::string &name) Ogre::Node *Animation::getNode(const std::string &name)
{ {
if(mSkelBase) if(mSkelBase)
@ -805,6 +913,43 @@ void Animation::showWeapons(bool showWeapon)
{ {
} }
class ToggleLight {
bool mEnable;
public:
ToggleLight(bool enable) : mEnable(enable) { }
void operator()(Ogre::Light *light) const
{ light->setVisible(mEnable); }
};
void Animation::enableLights(bool enable)
{
std::for_each(mObjectRoot.mLights.begin(), mObjectRoot.mLights.end(), ToggleLight(enable));
}
class MergeBounds {
Ogre::AxisAlignedBox *mBounds;
public:
MergeBounds(Ogre::AxisAlignedBox *bounds) : mBounds(bounds) { }
void operator()(Ogre::MovableObject *obj)
{
mBounds->merge(obj->getWorldBoundingBox(true));
}
};
Ogre::AxisAlignedBox Animation::getWorldBounds()
{
Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL;
std::for_each(mObjectRoot.mEntities.begin(), mObjectRoot.mEntities.end(), MergeBounds(&bounds));
return bounds;
}
bool Animation::isPriorityActive(int priority) const bool Animation::isPriorityActive(int priority) const
{ {
for (AnimStateMap::const_iterator it = mStates.begin(); it != mStates.end(); ++it) for (AnimStateMap::const_iterator it = mStates.begin(); it != mStates.end(); ++it)
@ -833,4 +978,65 @@ void Animation::detachObjectFromBone(Ogre::MovableObject *obj)
mSkelBase->detachObjectFromBone(obj); mSkelBase->detachObjectFromBone(obj);
} }
ObjectAnimation::ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model, bool isStatic)
: Animation(ptr, ptr.getRefData().getBaseNode())
{
setObjectRoot(model, false);
Ogre::AxisAlignedBox bounds = getWorldBounds();
Ogre::Vector3 extents = bounds.getSize();
extents *= mInsert->getParentSceneNode()->getScale();
float size = std::max(std::max(extents.x, extents.y), extents.z);
bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) &&
Settings::Manager::getBool("limit small object distance", "Viewing distance");
// do not fade out doors. that will cause holes and look stupid
if(ptr.getTypeName().find("Door") != std::string::npos)
small = false;
float dist = small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0.0f;
setRenderProperties(mObjectRoot, isStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc,
RQG_Main, RQG_Alpha, dist);
}
void ObjectAnimation::addLight(const ESM::Light *light)
{
addExtraLight(mInsert->getCreator(), mObjectRoot, light);
}
class FindEntityTransparency {
public:
bool operator()(Ogre::Entity *ent) const
{
unsigned int numsubs = ent->getNumSubEntities();
for(unsigned int i = 0;i < numsubs;++i)
{
if(ent->getSubEntity(i)->getMaterial()->isTransparent())
return true;
}
return false;
}
};
bool ObjectAnimation::canBatch() const
{
if(!mObjectRoot.mParticles.empty() || !mObjectRoot.mLights.empty() || !mObjectRoot.mControllers.empty())
return false;
return std::find_if(mObjectRoot.mEntities.begin(), mObjectRoot.mEntities.end(),
FindEntityTransparency()) == mObjectRoot.mEntities.end();
}
void ObjectAnimation::fillBatch(Ogre::StaticGeometry *sg)
{
std::vector<Ogre::Entity*>::reverse_iterator iter = mObjectRoot.mEntities.rbegin();
for(;iter != mObjectRoot.mEntities.rend();++iter)
{
Ogre::Node *node = (*iter)->getParentNode();
sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale());
}
}
} }

@ -52,6 +52,7 @@ protected:
virtual void setValue(Ogre::Real value); virtual void setValue(Ogre::Real value);
}; };
class NullAnimationValue : public Ogre::ControllerValue<Ogre::Real> class NullAnimationValue : public Ogre::ControllerValue<Ogre::Real>
{ {
public: public:
@ -61,6 +62,7 @@ protected:
{ } { }
}; };
struct AnimSource : public Ogre::AnimationAlloc { struct AnimSource : public Ogre::AnimationAlloc {
NifOgre::TextKeyMap mTextKeys; NifOgre::TextKeyMap mTextKeys;
std::vector<Ogre::Controller<Ogre::Real> > mControllers[sNumGroups]; std::vector<Ogre::Controller<Ogre::Real> > mControllers[sNumGroups];
@ -151,21 +153,24 @@ protected:
* Note that you must make sure all animation sources are cleared before reseting the object * Note that you must make sure all animation sources are cleared before reseting the object
* root. All nodes previously retrieved with getNode will also become invalidated. * root. All nodes previously retrieved with getNode will also become invalidated.
*/ */
void setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly); void setObjectRoot(const std::string &model, bool baseonly);
/* Adds the keyframe controllers in the specified model as a new animation source. Note that /* Adds the keyframe controllers in the specified model as a new animation source. Note that
* the filename portion of the provided model name will be prepended with 'x', and the .nif * the filename portion of the provided model name will be prepended with 'x', and the .nif
* extension will be replaced with .kf. */ * extension will be replaced with .kf. */
void addAnimSource(const std::string &model); void addAnimSource(const std::string &model);
/** Adds an additional light to the given object list using the specified ESM record. */
void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objlist, const ESM::Light *light);
static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects);
static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue); static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue, Ogre::Real dist=0.0f);
void clearAnimSources(); void clearAnimSources();
public: public:
Animation(const MWWorld::Ptr &ptr); Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node);
virtual ~Animation(); virtual ~Animation();
void updatePtr(const MWWorld::Ptr &ptr); void updatePtr(const MWWorld::Ptr &ptr);
@ -224,6 +229,10 @@ public:
virtual void showWeapons(bool showWeapon); virtual void showWeapons(bool showWeapon);
void enableLights(bool enable);
Ogre::AxisAlignedBox getWorldBounds();
void setCamera(Camera *cam) void setCamera(Camera *cam)
{ mCamera = cam; } { mCamera = cam; }
@ -236,5 +245,15 @@ public:
void detachObjectFromBone(Ogre::MovableObject *obj); void detachObjectFromBone(Ogre::MovableObject *obj);
}; };
class ObjectAnimation : public Animation {
public:
ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model, bool isStatic);
void addLight(const ESM::Light *light);
bool canBatch() const;
void fillBatch(Ogre::StaticGeometry *sg);
};
} }
#endif #endif

@ -12,7 +12,7 @@ CreatureAnimation::~CreatureAnimation()
} }
CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
: Animation(ptr) : Animation(ptr, ptr.getRefData().getBaseNode())
{ {
MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>(); MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
@ -21,7 +21,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
{ {
std::string model = "meshes\\"+ref->mBase->mModel; std::string model = "meshes\\"+ref->mBase->mModel;
setObjectRoot(mPtr.getRefData().getBaseNode(), 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::Biped))

@ -20,46 +20,50 @@
namespace MWRender namespace MWRender
{ {
const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize] = { static NpcAnimation::PartBoneMap createPartListMap()
{ ESM::PRT_Head, "Head" }, {
{ ESM::PRT_Hair, "Head" }, NpcAnimation::PartBoneMap result;
{ ESM::PRT_Neck, "Neck" }, result.insert(std::make_pair(ESM::PRT_Head, "Head"));
{ ESM::PRT_Cuirass, "Chest" }, result.insert(std::make_pair(ESM::PRT_Hair, "Head"));
{ ESM::PRT_Groin, "Groin" }, result.insert(std::make_pair(ESM::PRT_Neck, "Neck"));
{ ESM::PRT_Skirt, "Groin" }, result.insert(std::make_pair(ESM::PRT_Cuirass, "Chest"));
{ ESM::PRT_RHand, "Right Hand" }, result.insert(std::make_pair(ESM::PRT_Groin, "Groin"));
{ ESM::PRT_LHand, "Left Hand" }, result.insert(std::make_pair(ESM::PRT_Skirt, "Groin"));
{ ESM::PRT_RWrist, "Right Wrist" }, result.insert(std::make_pair(ESM::PRT_RHand, "Right Hand"));
{ ESM::PRT_LWrist, "Left Wrist" }, result.insert(std::make_pair(ESM::PRT_LHand, "Left Hand"));
{ ESM::PRT_Shield, "Shield Bone" }, result.insert(std::make_pair(ESM::PRT_RWrist, "Right Wrist"));
{ ESM::PRT_RForearm, "Right Forearm" }, result.insert(std::make_pair(ESM::PRT_LWrist, "Left Wrist"));
{ ESM::PRT_LForearm, "Left Forearm" }, result.insert(std::make_pair(ESM::PRT_Shield, "Shield Bone"));
{ ESM::PRT_RUpperarm, "Right Upper Arm" }, result.insert(std::make_pair(ESM::PRT_RForearm, "Right Forearm"));
{ ESM::PRT_LUpperarm, "Left Upper Arm" }, result.insert(std::make_pair(ESM::PRT_LForearm, "Left Forearm"));
{ ESM::PRT_RFoot, "Right Foot" }, result.insert(std::make_pair(ESM::PRT_RUpperarm, "Right Upper Arm"));
{ ESM::PRT_LFoot, "Left Foot" }, result.insert(std::make_pair(ESM::PRT_LUpperarm, "Left Upper Arm"));
{ ESM::PRT_RAnkle, "Right Ankle" }, result.insert(std::make_pair(ESM::PRT_RFoot, "Right Foot"));
{ ESM::PRT_LAnkle, "Left Ankle" }, result.insert(std::make_pair(ESM::PRT_LFoot, "Left Foot"));
{ ESM::PRT_RKnee, "Right Knee" }, result.insert(std::make_pair(ESM::PRT_RAnkle, "Right Ankle"));
{ ESM::PRT_LKnee, "Left Knee" }, result.insert(std::make_pair(ESM::PRT_LAnkle, "Left Ankle"));
{ ESM::PRT_RLeg, "Right Upper Leg" }, result.insert(std::make_pair(ESM::PRT_RKnee, "Right Knee"));
{ ESM::PRT_LLeg, "Left Upper Leg" }, result.insert(std::make_pair(ESM::PRT_LKnee, "Left Knee"));
{ ESM::PRT_RPauldron, "Right Clavicle" }, result.insert(std::make_pair(ESM::PRT_RLeg, "Right Upper Leg"));
{ ESM::PRT_LPauldron, "Left Clavicle" }, result.insert(std::make_pair(ESM::PRT_LLeg, "Left Upper Leg"));
{ ESM::PRT_Weapon, "Weapon Bone" }, result.insert(std::make_pair(ESM::PRT_RPauldron, "Right Clavicle"));
{ ESM::PRT_Tail, "Tail" } result.insert(std::make_pair(ESM::PRT_LPauldron, "Left Clavicle"));
}; result.insert(std::make_pair(ESM::PRT_Weapon, "Weapon Bone"));
result.insert(std::make_pair(ESM::PRT_Tail, "Tail"));
return result;
}
const NpcAnimation::PartBoneMap NpcAnimation::sPartList = createPartListMap();
NpcAnimation::~NpcAnimation() NpcAnimation::~NpcAnimation()
{ {
Ogre::SceneManager *sceneMgr = mInsert->getCreator(); Ogre::SceneManager *sceneMgr = mInsert->getCreator();
for(size_t i = 0;i < sPartListSize;i++) for(size_t i = 0;i < ESM::PRT_Count;i++)
destroyObjectList(sceneMgr, mObjectParts[i]); destroyObjectList(sceneMgr, mObjectParts[i]);
} }
NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags, ViewMode viewMode) NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags, ViewMode viewMode)
: Animation(ptr), : Animation(ptr, node),
mStateID(-1), mStateID(-1),
mTimeToChange(0), mTimeToChange(0),
mVisibilityFlags(visibilityFlags), mVisibilityFlags(visibilityFlags),
@ -82,7 +86,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
{ {
mNpc = mPtr.get<ESM::NPC>()->mBase; mNpc = mPtr.get<ESM::NPC>()->mBase;
for(size_t i = 0;i < sPartListSize;i++) for(size_t i = 0;i < ESM::PRT_Count;i++)
{ {
mPartslots[i] = -1; //each slot is empty mPartslots[i] = -1; //each slot is empty
mPartPriorities[i] = 0; mPartPriorities[i] = 0;
@ -102,7 +106,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
std::string smodel = (viewMode != VM_FirstPerson) ? std::string smodel = (viewMode != VM_FirstPerson) ?
(!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif") : (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif") :
(!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif") ; (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif") ;
setObjectRoot(node, smodel, true); setObjectRoot(smodel, true);
if(mViewMode != VM_FirstPerson) if(mViewMode != VM_FirstPerson)
{ {
@ -143,7 +147,7 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
std::string smodel = (viewMode != VM_FirstPerson) ? std::string smodel = (viewMode != VM_FirstPerson) ?
(!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif") : (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif") :
(!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif") ; (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif") ;
setObjectRoot(mInsert->getParentSceneNode(), smodel, true); setObjectRoot(smodel, true);
if(mViewMode != VM_FirstPerson) if(mViewMode != VM_FirstPerson)
{ {
@ -168,8 +172,8 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
} }
MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr);
for(size_t i = 0;i < sPartListSize;i++) for(size_t i = 0;i < ESM::PRT_Count;i++)
removeIndividualPart(i); removeIndividualPart((ESM::PartReferenceType)i);
forceUpdate(); forceUpdate();
} }
@ -267,6 +271,19 @@ void NpcAnimation::updateParts(bool forceupdate)
if(mViewMode == VM_HeadOnly) if(mViewMode == VM_HeadOnly)
return; return;
if(mPartPriorities[ESM::PRT_Shield] < 1)
{
MWWorld::ContainerStoreIterator store = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
MWWorld::Ptr part;
if(store != inv.end() && (part=*store).getTypeName() == typeid(ESM::Light).name())
{
const ESM::Light *light = part.get<ESM::Light>()->mBase;
addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft,
1, "meshes\\"+light->mModel);
addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], light);
}
}
showWeapons(mShowWeapons); showWeapons(mShowWeapons);
const int Flag_Female = 0x01; const int Flag_Female = 0x01;
@ -366,23 +383,31 @@ void NpcAnimation::updateParts(bool forceupdate)
{ {
const ESM::BodyPart* bodypart = parts[part]; const ESM::BodyPart* bodypart = parts[part];
if(bodypart) if(bodypart)
addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel); addOrReplaceIndividualPart((ESM::PartReferenceType)part, -1, 1,
"meshes\\"+bodypart->mModel);
} }
} }
} }
class SetObjectGroup {
int mGroup;
public:
SetObjectGroup(int group) : mGroup(group) { }
void operator()(Ogre::MovableObject *obj) const
{
obj->getUserObjectBindings().setUserAny(Ogre::Any(mGroup));
}
};
NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename)
{ {
NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model);
setRenderProperties(objects, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha); setRenderProperties(objects, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha);
for(size_t i = 0;i < objects.mEntities.size();i++) std::for_each(objects.mEntities.begin(), objects.mEntities.end(), SetObjectGroup(group));
{ std::for_each(objects.mParticles.begin(), objects.mParticles.end(), SetObjectGroup(group));
Ogre::Entity *ent = objects.mEntities[i];
ent->getUserObjectBindings().setUserAny(Ogre::Any(group));
}
for(size_t i = 0;i < objects.mParticles.size();i++)
objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
if(objects.mSkelBase) if(objects.mSkelBase)
{ {
@ -422,7 +447,7 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
node->pitch(Ogre::Radian(pitch*0.75f), Ogre::Node::TS_WORLD); node->pitch(Ogre::Radian(pitch*0.75f), Ogre::Node::TS_WORLD);
} }
for(size_t i = 0;i < sPartListSize;i++) for(size_t i = 0;i < ESM::PRT_Count;i++)
{ {
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(mObjectParts[i].mControllers.begin()); std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(mObjectParts[i].mControllers.begin());
for(;ctrl != mObjectParts[i].mControllers.end();ctrl++) for(;ctrl != mObjectParts[i].mControllers.end();ctrl++)
@ -437,22 +462,15 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
return ret; return ret;
} }
void NpcAnimation::removeIndividualPart(int type) void NpcAnimation::removeIndividualPart(ESM::PartReferenceType type)
{ {
mPartPriorities[type] = 0; mPartPriorities[type] = 0;
mPartslots[type] = -1; mPartslots[type] = -1;
for(size_t i = 0;i < sPartListSize;i++) destroyObjectList(mInsert->getCreator(), mObjectParts[type]);
{
if(type == sPartList[i].type)
{
destroyObjectList(mInsert->getCreator(), mObjectParts[i]);
break;
}
}
} }
void NpcAnimation::reserveIndividualPart(int type, int group, int priority) void NpcAnimation::reserveIndividualPart(ESM::PartReferenceType type, int group, int priority)
{ {
if(priority > mPartPriorities[type]) if(priority > mPartPriorities[type])
{ {
@ -464,14 +482,14 @@ void NpcAnimation::reserveIndividualPart(int type, int group, int priority)
void NpcAnimation::removePartGroup(int group) void NpcAnimation::removePartGroup(int group)
{ {
for(int i = 0; i < 27; i++) for(int i = 0; i < ESM::PRT_Count; i++)
{ {
if(mPartslots[i] == group) if(mPartslots[i] == group)
removeIndividualPart(i); removeIndividualPart((ESM::PartReferenceType)i);
} }
} }
bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh) bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh)
{ {
if(priority <= mPartPriorities[type]) if(priority <= mPartPriorities[type])
return false; return false;
@ -480,28 +498,36 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority,
mPartslots[type] = group; mPartslots[type] = group;
mPartPriorities[type] = priority; mPartPriorities[type] = priority;
for(size_t i = 0;i < sPartListSize;i++) mObjectParts[type] = insertBoundedPart(mesh, group, sPartList.at(type));
if(mObjectParts[type].mSkelBase && mObjectParts[type].mSkelBase->isParentTagPoint())
{ {
if(type == sPartList[i].type) Ogre::Node *root = mObjectParts[type].mSkelBase->getParentNode();
Ogre::SkeletonInstance *skel = mObjectParts[type].mSkelBase->getSkeleton();
if(skel->hasBone("BoneOffset"))
{ {
mObjectParts[i] = insertBoundedPart(mesh, group, sPartList[i].name); Ogre::Bone *offset = skel->getBone("BoneOffset");
root->translate(offset->getPosition());
// TODO: root->rotate(offset->getOrientation());
// type == ESM::PRT_Head should get an animation source based on the current output of // HACK: Why an extra -90 degree rotation?
// the actor's 'say' sound (0 = silent, 1 = loud?). root->pitch(Ogre::Degree(-90.0f));
// type == ESM::PRT_Weapon should get an animation source based on the current offset root->scale(offset->getScale());
// of the weapon attack animation (from its beginning, or start marker?) root->setInitialState();
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(mObjectParts[i].mControllers.begin());
for(;ctrl != mObjectParts[i].mControllers.end();ctrl++)
{
if(ctrl->getSource().isNull())
ctrl->setSource(mNullAnimationValuePtr);
}
break;
} }
} }
return true;
// TODO:
// type == ESM::PRT_Head should get an animation source based on the current output of
// the actor's 'say' sound (0 = silent, 1 = loud?).
// type == ESM::PRT_Weapon should get an animation source based on the current offset
// of the weapon attack animation (from its beginning, or start marker?)
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(mObjectParts[type].mControllers.begin());
for(;ctrl != mObjectParts[type].mControllers.end();ctrl++)
{
if(ctrl->getSource().isNull())
ctrl->setSource(mNullAnimationValuePtr);
}
return true;
} }
void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts) void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts)
@ -542,9 +568,9 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::
} }
if(bodypart) if(bodypart)
addOrReplaceIndividualPart(part->mPart, group, priority, "meshes\\"+bodypart->mModel); addOrReplaceIndividualPart((ESM::PartReferenceType)part->mPart, group, priority, "meshes\\"+bodypart->mModel);
else else
reserveIndividualPart(part->mPart, group, priority); reserveIndividualPart((ESM::PartReferenceType)part->mPart, group, priority);
} }
} }
@ -557,7 +583,8 @@ void NpcAnimation::showWeapons(bool showWeapon)
mWeapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); mWeapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(mWeapon != inv.end()) // special case for weapons if(mWeapon != inv.end()) // special case for weapons
{ {
std::string mesh = MWWorld::Class::get(*mWeapon).getModel(*mWeapon); MWWorld::Ptr weapon = *mWeapon;
std::string mesh = MWWorld::Class::get(weapon).getModel(weapon);
addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh); addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh);
} }
} }

@ -21,25 +21,21 @@ namespace MWRender
class NpcAnimation : public Animation class NpcAnimation : public Animation
{ {
public: public:
struct PartInfo { typedef std::map<ESM::PartReferenceType,std::string> PartBoneMap;
ESM::PartReferenceType type;
const char name[32];
};
enum ViewMode { enum ViewMode {
VM_Normal, VM_Normal,
VM_FirstPerson, VM_FirstPerson,
VM_HeadOnly VM_HeadOnly
}; };
private: private:
static const size_t sPartListSize = 27; static const PartBoneMap sPartList;
static const PartInfo sPartList[sPartListSize];
int mStateID; int mStateID;
// Bounded Parts // Bounded Parts
NifOgre::ObjectList mObjectParts[sPartListSize]; NifOgre::ObjectList mObjectParts[ESM::PRT_Count];
const ESM::NPC *mNpc; const ESM::NPC *mNpc;
std::string mHeadModel; std::string mHeadModel;
@ -66,17 +62,17 @@ private:
int mVisibilityFlags; int mVisibilityFlags;
int mPartslots[sPartListSize]; //Each part slot is taken by clothing, armor, or is empty int mPartslots[ESM::PRT_Count]; //Each part slot is taken by clothing, armor, or is empty
int mPartPriorities[sPartListSize]; int mPartPriorities[ESM::PRT_Count];
NifOgre::ObjectList insertBoundedPart(const std::string &model, int group, const std::string &bonename); NifOgre::ObjectList insertBoundedPart(const std::string &model, int group, const std::string &bonename);
void updateParts(bool forceupdate = false); void updateParts(bool forceupdate = false);
void removeIndividualPart(int type); void removeIndividualPart(ESM::PartReferenceType type);
void reserveIndividualPart(int type, int group, int priority); void reserveIndividualPart(ESM::PartReferenceType type, int group, int priority);
bool addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh); bool addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string &mesh);
void removePartGroup(int group); void removePartGroup(int group);
void addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts); void addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts);

@ -18,64 +18,12 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "renderconst.hpp" #include "renderconst.hpp"
#include "animation.hpp"
using namespace MWRender; using namespace MWRender;
float Objects::lightLinearValue()
{
return mFallback->getFallbackFloat("LightAttenuation_LinearValue");
}
float Objects::lightLinearRadiusMult()
{
return mFallback->getFallbackFloat("LightAttenuation_LinearRadiusMult");
}
float Objects::lightQuadraticValue()
{
return mFallback->getFallbackFloat("LightAttenuation_QuadraticValue");
}
float Objects::lightQuadraticRadiusMult()
{
return mFallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult");
}
bool Objects::lightOutQuadInLin()
{
return mFallback->getFallbackBool("LightAttenuation_OutQuadInLin");
}
bool Objects::lightQuadratic()
{
return mFallback->getFallbackBool("LightAttenuation_UseQuadratic");
}
int Objects::uniqueID = 0; int Objects::uniqueID = 0;
void Objects::clearSceneNode (Ogre::SceneNode *node)
{
for (int i=node->numAttachedObjects()-1; i>=0; --i)
{
Ogre::MovableObject *object = node->getAttachedObject (i);
// for entities, destroy any objects attached to bones
if (object->getTypeFlags () == Ogre::SceneManager::ENTITY_TYPE_MASK)
{
Ogre::Entity* ent = static_cast<Ogre::Entity*>(object);
Ogre::Entity::ChildObjectListIterator children = ent->getAttachedObjectIterator ();
while (children.hasMoreElements())
{
mRenderer.getScene ()->destroyMovableObject (children.getNext ());
}
}
node->detachObject (object);
mRenderer.getScene()->destroyMovableObject (object);
}
Ogre::Node::ChildNodeIterator it = node->getChildIterator ();
while (it.hasMoreElements ())
{
clearSceneNode(static_cast<Ogre::SceneNode*>(it.getNext ()));
}
}
void Objects::setRootNode(Ogre::SceneNode* root) void Objects::setRootNode(Ogre::SceneNode* root)
{ {
mRootNode = root; mRootNode = root;
@ -124,73 +72,41 @@ void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_)
mIsStatic = static_; mIsStatic = static_;
} }
void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light) void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
{ {
Ogre::SceneNode* insert = ptr.getRefData().getBaseNode(); Ogre::SceneNode* insert = ptr.getRefData().getBaseNode();
assert(insert); assert(insert);
Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; std::auto_ptr<ObjectAnimation> anim(new ObjectAnimation(ptr, mesh, mIsStatic));
NifOgre::ObjectList objects = NifOgre::Loader::createObjects(insert, mesh);
for(size_t i = 0;i < objects.mEntities.size();i++)
bounds.merge(objects.mEntities[i]->getWorldBoundingBox(true));
Ogre::AxisAlignedBox bounds = anim->getWorldBounds();
Ogre::Vector3 extents = bounds.getSize(); Ogre::Vector3 extents = bounds.getSize();
extents *= insert->getScale(); extents *= insert->getScale();
float size = std::max(std::max(extents.x, extents.y), extents.z); float size = std::max(std::max(extents.x, extents.y), extents.z);
bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && Settings::Manager::getBool("limit small object distance", "Viewing distance"); bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) &&
Settings::Manager::getBool("limit small object distance", "Viewing distance");
// do not fade out doors. that will cause holes and look stupid // do not fade out doors. that will cause holes and look stupid
if (ptr.getTypeName().find("Door") != std::string::npos) if(ptr.getTypeName().find("Door") != std::string::npos)
small = false; small = false;
if (mBounds.find(ptr.getCell()) == mBounds.end()) if (mBounds.find(ptr.getCell()) == mBounds.end())
mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL; mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL;
mBounds[ptr.getCell()].merge(bounds); mBounds[ptr.getCell()].merge(bounds);
bool anyTransparency = false; if(ptr.getTypeName() == typeid(ESM::Light).name())
for(size_t i = 0;!anyTransparency && i < objects.mEntities.size();i++) anim->addLight(ptr.get<ESM::Light>()->mBase);
{
Ogre::Entity *ent = objects.mEntities[i];
for(unsigned int i=0;!anyTransparency && i < ent->getNumSubEntities(); ++i)
{
anyTransparency = ent->getSubEntity(i)->getMaterial()->isTransparent();
}
}
if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects") || if(mIsStatic && Settings::Manager::getBool("use static geometry", "Objects") && anim->canBatch())
anyTransparency || !objects.mParticles.empty())
{
for(size_t i = 0;i < objects.mEntities.size();i++)
{
Ogre::Entity *ent = objects.mEntities[i];
for(unsigned int i=0; i < ent->getNumSubEntities(); ++i)
{
Ogre::SubEntity* subEnt = ent->getSubEntity(i);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
}
ent->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0);
ent->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc);
}
for(size_t i = 0;i < objects.mParticles.size();i++)
{
Ogre::ParticleSystem *part = objects.mParticles[i];
// TODO: Check the particle system's material for actual transparency
part->setRenderQueueGroup(RQG_Alpha);
part->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0);
part->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc);
}
}
else
{ {
Ogre::StaticGeometry* sg = 0; Ogre::StaticGeometry* sg = 0;
if (small) if (small)
{ {
if( mStaticGeometrySmall.find(ptr.getCell()) == mStaticGeometrySmall.end()) if(mStaticGeometrySmall.find(ptr.getCell()) == mStaticGeometrySmall.end())
{ {
uniqueID = uniqueID +1; uniqueID = uniqueID+1;
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID)); sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID));
mStaticGeometrySmall[ptr.getCell()] = sg; mStaticGeometrySmall[ptr.getCell()] = sg;
sg->setRenderingDistance(Settings::Manager::getInt("small object distance", "Viewing distance")); sg->setRenderingDistance(Settings::Manager::getInt("small object distance", "Viewing distance"));
@ -200,11 +116,10 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool
} }
else else
{ {
if( mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end()) if(mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end())
{ {
uniqueID = uniqueID+1;
uniqueID = uniqueID +1; sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID));
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
mStaticGeometry[ptr.getCell()] = sg; mStaticGeometry[ptr.getCell()] = sg;
} }
else else
@ -225,155 +140,75 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool
sg->setRenderQueueGroup(RQG_Main); sg->setRenderQueueGroup(RQG_Main);
std::vector<Ogre::Entity*>::reverse_iterator iter = objects.mEntities.rbegin(); anim->fillBatch(sg);
while(iter != objects.mEntities.rend()) /* TODO: We could hold on to this and just detach it from the scene graph, so if the Ptr
{ * ever needs to modify we can reattach it and rebuild the StaticGeometry object without
Ogre::Node *node = (*iter)->getParentNode(); * it. Would require associating the Ptr with the StaticGeometry. */
sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale()); anim.reset();
(*iter)->detachFromParent();
mRenderer.getScene()->destroyEntity(*iter);
++iter;
}
} }
if (light) if(anim.get() != NULL)
{ mObjects.insert(std::make_pair(ptr, anim.release()));
insertLight(ptr, objects.mSkelBase, bounds.getCenter() - insert->_getDerivedPosition());
}
} }
void Objects::insertLight (const MWWorld::Ptr& ptr, Ogre::Entity* skelBase, Ogre::Vector3 fallbackCenter) bool Objects::deleteObject (const MWWorld::Ptr& ptr)
{ {
Ogre::SceneNode* insert = mRenderer.getScene()->getSceneNode(ptr.getRefData().getHandle()); if(!ptr.getRefData().getBaseNode())
assert(insert); return true;
MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
const int color = ref->mBase->mData.mColor;
const float r = ((color >> 0) & 0xFF) / 255.0f;
const float g = ((color >> 8) & 0xFF) / 255.0f;
const float b = ((color >> 16) & 0xFF) / 255.0f;
const float radius = float (ref->mBase->mData.mRadius);
Ogre::Light *light = mRenderer.getScene()->createLight();
light->setDiffuseColour (r, g, b);
LightInfo info; PtrAnimationMap::iterator iter = mObjects.find(ptr);
info.name = light->getName(); if(iter != mObjects.end())
info.radius = radius;
info.colour = Ogre::ColourValue(r, g, b);
if (ref->mBase->mData.mFlags & ESM::Light::Negative)
info.colour *= -1;
info.interior = !ptr.getCell()->mCell->isExterior();
if (ref->mBase->mData.mFlags & ESM::Light::Flicker)
info.type = LT_Flicker;
else if (ref->mBase->mData.mFlags & ESM::Light::FlickerSlow)
info.type = LT_FlickerSlow;
else if (ref->mBase->mData.mFlags & ESM::Light::Pulse)
info.type = LT_Pulse;
else if (ref->mBase->mData.mFlags & ESM::Light::PulseSlow)
info.type = LT_PulseSlow;
else
info.type = LT_Normal;
// randomize lights animations
info.time = Ogre::Math::RangeRandom(-500, +500);
info.phase = Ogre::Math::RangeRandom(-500, +500);
bool quadratic = lightOutQuadInLin() ? !info.interior : lightQuadratic();
// with the standard 1 / (c + d*l + d*d*q) equation the attenuation factor never becomes zero,
// so we ignore lights if their attenuation falls below this factor.
const float threshold = 0.03;
if (!quadratic)
{ {
float r = radius * lightLinearRadiusMult(); delete iter->second;
float attenuation = lightLinearValue() / r; mObjects.erase(iter);
float activationRange = 1 / (threshold * attenuation);
light->setAttenuation(activationRange, 0, attenuation, 0);
}
else
{
float r = radius * lightQuadraticRadiusMult();
float attenuation = lightQuadraticValue() / std::pow(r, 2);
float activationRange = std::sqrt(1 / (threshold * attenuation));
light->setAttenuation(activationRange, 0, 0, attenuation);
}
// If there's an AttachLight bone, attach the light to that, otherwise attach it to the base scene node mRenderer.getScene()->destroySceneNode(ptr.getRefData().getBaseNode());
if (skelBase && skelBase->getSkeleton ()->hasBone ("AttachLight")) ptr.getRefData().setBaseNode(0);
{ return true;
skelBase->attachObjectToBone ("AttachLight", light);
}
else
{
Ogre::SceneNode* childNode = insert->createChildSceneNode (fallbackCenter);
childNode->attachObject(light);
} }
mLights.push_back(info); return false;
} }
bool Objects::deleteObject (const MWWorld::Ptr& ptr)
{
if (Ogre::SceneNode *base = ptr.getRefData().getBaseNode())
{
Ogre::SceneNode *parent = base->getParentSceneNode();
for (std::map<MWWorld::Ptr::CellStore *, Ogre::SceneNode *>::const_iterator iter (
mCellSceneNodes.begin()); iter!=mCellSceneNodes.end(); ++iter)
if (iter->second==parent)
{
clearSceneNode (base);
base->removeAndDestroyAllChildren();
mRenderer.getScene()->destroySceneNode (base);
ptr.getRefData().setBaseNode (0);
return true;
}
return false;
}
return true;
}
void Objects::removeCell(MWWorld::Ptr::CellStore* store) void Objects::removeCell(MWWorld::Ptr::CellStore* store)
{ {
if(mCellSceneNodes.find(store) != mCellSceneNodes.end()) for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();)
{ {
Ogre::SceneNode* base = mCellSceneNodes[store]; if(iter->first.getCell() == store)
{
for (int i=0; i<base->numChildren(); ++i) delete iter->second;
clearSceneNode (static_cast<Ogre::SceneNode *> (base->getChild (i))); mObjects.erase(iter++);
}
base->removeAndDestroyAllChildren(); else
mCellSceneNodes.erase(store); ++iter;
mRenderer.getScene()->destroySceneNode(base);
base = 0;
} }
if(mStaticGeometry.find(store) != mStaticGeometry.end()) std::map<MWWorld::CellStore*,Ogre::StaticGeometry*>::iterator geom = mStaticGeometry.find(store);
if(geom != mStaticGeometry.end())
{ {
Ogre::StaticGeometry* sg = mStaticGeometry[store]; Ogre::StaticGeometry *sg = geom->second;
mStaticGeometry.erase(store); mStaticGeometry.erase(geom);
mRenderer.getScene()->destroyStaticGeometry (sg); mRenderer.getScene()->destroyStaticGeometry(sg);
sg = 0;
} }
if(mStaticGeometrySmall.find(store) != mStaticGeometrySmall.end())
geom = mStaticGeometrySmall.find(store);
if(geom != mStaticGeometrySmall.end())
{ {
Ogre::StaticGeometry* sg = mStaticGeometrySmall[store]; Ogre::StaticGeometry *sg = geom->second;
mStaticGeometrySmall.erase(store); mStaticGeometrySmall.erase(store);
mRenderer.getScene()->destroyStaticGeometry (sg); mRenderer.getScene()->destroyStaticGeometry(sg);
sg = 0;
} }
if(mBounds.find(store) != mBounds.end()) mBounds.erase(store);
mBounds.erase(store);
std::map<MWWorld::CellStore*,Ogre::SceneNode*>::iterator cell = mCellSceneNodes.find(store);
if(cell != mCellSceneNodes.end())
{
cell->second->removeAndDestroyAllChildren();
mRenderer.getScene()->destroySceneNode(cell->second);
mCellSceneNodes.erase(cell);
}
} }
void Objects::buildStaticGeometry(MWWorld::Ptr::CellStore& cell) void Objects::buildStaticGeometry(MWWorld::Ptr::CellStore& cell)
@ -397,146 +232,23 @@ Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell)
void Objects::enableLights() void Objects::enableLights()
{ {
std::vector<LightInfo>::iterator it = mLights.begin(); PtrAnimationMap::const_iterator it = mObjects.begin();
while (it != mLights.end()) for(;it != mObjects.end();it++)
{ it->second->enableLights(true);
if (mRootNode->getCreator()->hasLight(it->name))
{
mRootNode->getCreator()->getLight(it->name)->setVisible(true);
++it;
}
else
it = mLights.erase(it);
}
} }
void Objects::disableLights() void Objects::disableLights()
{ {
std::vector<LightInfo>::iterator it = mLights.begin(); PtrAnimationMap::const_iterator it = mObjects.begin();
while (it != mLights.end()) for(;it != mObjects.end();it++)
{ it->second->enableLights(false);
if (mRootNode->getCreator()->hasLight(it->name))
{
mRootNode->getCreator()->getLight(it->name)->setVisible(false);
++it;
}
else
it = mLights.erase(it);
}
}
namespace MWRender
{
namespace Pulse
{
static float amplitude (float phase)
{
return sin (phase);
}
}
namespace Flicker
{
static const float fa = 0.785398f;
static const float fb = 1.17024f;
static const float tdo = 0.94f;
static const float tdm = 2.48f;
static const float f [3] = { 1.5708f, 4.18774f, 5.19934f };
static const float o [3] = { 0.804248f, 2.11115f, 3.46832f };
static const float m [3] = { 1.0f, 0.785f, 0.876f };
static const float s = 0.394f;
static const float phase_wavelength = 120.0f * 3.14159265359f / fa;
static float frequency (float x)
{
return tdo + tdm * sin (fa * x);
}
static float amplitude (float x)
{
float v = 0.0f;
for (int i = 0; i < 3; ++i)
v += sin (fb*x*f[i] + o[1])*m[i];
return v * s;
}
}
} }
void Objects::update(const float dt) void Objects::update(const float dt)
{ {
std::vector<LightInfo>::iterator it = mLights.begin(); PtrAnimationMap::const_iterator it = mObjects.begin();
while (it != mLights.end()) for(;it != mObjects.end();it++)
{ it->second->runAnimation(dt);
if (mRootNode->getCreator()->hasLight(it->name))
{
Ogre::Light* light = mRootNode->getCreator()->getLight(it->name);
float brightness;
float cycle_time;
float time_distortion;
if ((it->type == LT_Pulse) && (it->type == LT_PulseSlow))
{
cycle_time = 2 * Ogre::Math::PI;
time_distortion = 20.0f;
}
else
{
cycle_time = 500.0f;
it->phase = fmod (it->phase + dt, Flicker::phase_wavelength);
time_distortion = Flicker::frequency (it->phase);
}
it->time += it->dir*dt*time_distortion;
if (it->dir > 0 && it->time > +cycle_time)
{
it->dir = -1.0f;
it->time = +2*cycle_time - it->time;
}
if (it->dir < 0 && it->time < -cycle_time)
{
it->dir = +1.0f;
it->time = -2*cycle_time - it->time;
}
static const float fast = 4.0f/1.0f;
static const float slow = 1.0f/1.0f;
// These formulas are just guesswork, but they work pretty well
if (it->type == LT_Normal)
{
// Less than 1/255 light modifier for a constant light:
brightness = (const float)(1.0 + Flicker::amplitude(it->time*slow) / 255.0 );
}
else if (it->type == LT_Flicker)
{
brightness = (const float)(0.75 + Flicker::amplitude(it->time*fast) * 0.25);
}
else if (it->type == LT_FlickerSlow)
{
brightness = (const float)(0.75 + Flicker::amplitude(it->time*slow) * 0.25);
}
else if (it->type == LT_Pulse)
{
brightness = (const float)(1.0 + Pulse::amplitude (it->time*fast) * 0.25);
}
else if (it->type == LT_PulseSlow)
{
brightness = (const float)(1.0 + Pulse::amplitude (it->time*slow) * 0.25);
}
else
assert(0 && "Invalid light type");
light->setDiffuseColour(it->colour * brightness);
++it;
}
else
it = mLights.erase(it);
}
} }
void Objects::rebuildStaticGeometry() void Objects::rebuildStaticGeometry()

@ -5,7 +5,6 @@
#include <OgreAxisAlignedBox.h> #include <OgreAxisAlignedBox.h>
#include <openengine/ogre/renderer.hpp> #include <openengine/ogre/renderer.hpp>
#include "../mwworld/fallback.hpp"
namespace MWWorld namespace MWWorld
{ {
@ -15,72 +14,32 @@ namespace MWWorld
namespace MWRender{ namespace MWRender{
/// information about light needed for rendering class ObjectAnimation;
enum LightType
{
// These are all mutually exclusive
LT_Normal=0,
LT_Flicker=1,
LT_FlickerSlow=2,
LT_Pulse=3,
LT_PulseSlow=4
};
struct LightInfo
{
// Constants
std::string name; // ogre handle
Ogre::ColourValue colour;
float radius;
bool interior; // Does this light belong to an interior or exterior cell
LightType type;
// Runtime variables
float dir; // direction time is running...
float time; // current time
float phase; // current phase
LightInfo() :
dir(1.0f), time(0.0f), phase (0.0f),
interior(true), type(LT_Normal), radius(1.0)
{
}
};
class Objects{ class Objects{
typedef std::map<MWWorld::Ptr,ObjectAnimation*> PtrAnimationMap;
OEngine::Render::OgreRenderer &mRenderer; OEngine::Render::OgreRenderer &mRenderer;
std::map<MWWorld::CellStore *, Ogre::SceneNode *> mCellSceneNodes;
std::map<MWWorld::CellStore *, Ogre::StaticGeometry*> mStaticGeometry; std::map<MWWorld::CellStore*,Ogre::SceneNode*> mCellSceneNodes;
std::map<MWWorld::CellStore *, Ogre::StaticGeometry*> mStaticGeometrySmall; std::map<MWWorld::CellStore*,Ogre::StaticGeometry*> mStaticGeometry;
std::map<MWWorld::CellStore *, Ogre::AxisAlignedBox> mBounds; std::map<MWWorld::CellStore*,Ogre::StaticGeometry*> mStaticGeometrySmall;
std::vector<LightInfo> mLights; std::map<MWWorld::CellStore*,Ogre::AxisAlignedBox> mBounds;
PtrAnimationMap mObjects;
Ogre::SceneNode* mRootNode; Ogre::SceneNode* mRootNode;
bool mIsStatic; bool mIsStatic;
static int uniqueID; static int uniqueID;
MWWorld::Fallback* mFallback;
float lightLinearValue();
float lightLinearRadiusMult();
bool lightQuadratic();
float lightQuadraticValue();
float lightQuadraticRadiusMult();
bool lightOutQuadInLin();
void clearSceneNode (Ogre::SceneNode *node);
///< Remove all movable objects from \a node.
public: public:
Objects(OEngine::Render::OgreRenderer& renderer, MWWorld::Fallback* fallback) Objects(OEngine::Render::OgreRenderer &renderer)
: mRenderer (renderer) : mRenderer(renderer)
, mIsStatic(false) , mIsStatic(false)
, mFallback(fallback)
, mRootNode(NULL) , mRootNode(NULL)
{} {}
~Objects(){} ~Objects(){}
void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_); void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_);
void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light=false); void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh);
void insertLight (const MWWorld::Ptr& ptr, Ogre::Entity *skelBase=0, Ogre::Vector3 fallbackCenter=Ogre::Vector3(0.0f));
void enableLights(); void enableLights();
void disableLights(); void disableLights();

@ -57,7 +57,7 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
MWWorld::Fallback* fallback) MWWorld::Fallback* fallback)
: mRendering(_rend) : mRendering(_rend)
, mFallback(fallback) , mFallback(fallback)
, mObjects(mRendering, mFallback) , mObjects(mRendering)
, mActors(mRendering, this) , mActors(mRendering, this)
, mPlayerAnimation(NULL) , mPlayerAnimation(NULL)
, mAmbientMode(0) , mAmbientMode(0)

@ -11,10 +11,6 @@
#include "../mwmechanics/movement.hpp" #include "../mwmechanics/movement.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/character.hpp"
#include "../mwmechanics/security.hpp"
#include "../mwrender/animation.hpp"
#include "class.hpp" #include "class.hpp"
@ -144,51 +140,6 @@ namespace MWWorld
MWWorld::Class::get(ptr).getMovementSettings(ptr).mRotation[1] += roll; MWWorld::Class::get(ptr).getMovementSettings(ptr).mRotation[1] += roll;
} }
void Player::use()
{
MWWorld::InventoryStore& store = MWWorld::Class::get(getPlayer()).getInventoryStore(getPlayer());
MWWorld::ContainerStoreIterator equipped = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if (getDrawState() == MWMechanics::DrawState_Weapon)
{
if (equipped != store.end())
{
MWWorld::Ptr item = *equipped;
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(getPlayer());
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject();
if (anim->isPriorityActive(MWMechanics::Priority_Weapon))
return;
std::string resultMessage, resultSound;
if (item.getTypeName() == typeid(ESM::Lockpick).name())
{
if (!target.isEmpty())
MWMechanics::Security(getPlayer()).pickLock(target, item, resultMessage, resultSound);
anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, 1.0f, "start", "stop", 0.0, 0);
}
else if (item.getTypeName() == typeid(ESM::Probe).name())
{
if (!target.isEmpty())
MWMechanics::Security(getPlayer()).probeTrap(target, item, resultMessage, resultSound);
anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, 1.0f, "start", "stop", 0.0, 0);
}
if (!resultMessage.empty())
MWBase::Environment::get().getWindowManager()->messageBox(resultMessage);
if (!resultSound.empty())
MWBase::Environment::get().getSoundManager()->playSound(resultSound,1,1);
// tool used up?
if (!item.getRefData().getCount())
MWBase::Environment::get().getWindowManager()->unsetSelectedWeapon();
else
MWBase::Environment::get().getWindowManager()->setSelectedWeapon(item);
}
}
}
MWMechanics::DrawState_ Player::getDrawState() MWMechanics::DrawState_ Player::getDrawState()
{ {
MWWorld::Ptr ptr = getPlayer(); MWWorld::Ptr ptr = getPlayer();

@ -58,9 +58,6 @@ namespace MWWorld
void setForwardBackward (int value); void setForwardBackward (int value);
void setUpDown(int value); void setUpDown(int value);
void use ();
///< Use item equipped on right hand, or fists
void setRunState(bool run); void setRunState(bool run);
void setSneak(bool sneak); void setSneak(bool sneak);

@ -74,7 +74,7 @@ namespace MWWorld
bool isInCell() const bool isInCell() const
{ {
return (mContainerStore == 0); return (mContainerStore == 0) && (mCell != 0);
} }
void setContainerStore (ContainerStore *store); void setContainerStore (ContainerStore *store);

@ -43,6 +43,7 @@ struct ObjectList {
Ogre::Entity *mSkelBase; Ogre::Entity *mSkelBase;
std::vector<Ogre::Entity*> mEntities; std::vector<Ogre::Entity*> mEntities;
std::vector<Ogre::ParticleSystem*> mParticles; std::vector<Ogre::ParticleSystem*> mParticles;
std::vector<Ogre::Light*> mLights;
std::map<int,TextKeyMap> mTextKeys; std::map<int,TextKeyMap> mTextKeys;

@ -0,0 +1,118 @@
#include "lights.hpp"
#include <OgreLight.h>
#include <OgreMath.h>
namespace OEngine {
namespace Render {
LightFunction::LightFunction(LightType type)
: ControllerFunction<Ogre::Real>(true)
, mType(type)
, mPhase(Ogre::Math::RangeRandom(-500.0f, +500.0f))
, mDirection(1.0f)
{
}
Ogre::Real LightFunction::pulseAmplitude(Ogre::Real time)
{
return std::sin(time);
}
Ogre::Real LightFunction::flickerAmplitude(Ogre::Real time)
{
static const float fb = 1.17024f;
static const float f[3] = { 1.5708f, 4.18774f, 5.19934f };
static const float o[3] = { 0.804248f, 2.11115f, 3.46832f };
static const float m[3] = { 1.0f, 0.785f, 0.876f };
static const float s = 0.394f;
float v = 0.0f;
for(int i = 0;i < 3;++i)
v += std::sin(fb*time*f[i] + o[1])*m[i];
return v * s;
}
Ogre::Real LightFunction::flickerFrequency(Ogre::Real phase)
{
static const float fa = 0.785398f;
static const float tdo = 0.94f;
static const float tdm = 2.48f;
return tdo + tdm*std::sin(fa * phase);
}
Ogre::Real LightFunction::calculate(Ogre::Real value)
{
Ogre::Real brightness = 1.0f;
float cycle_time;
float time_distortion;
if(mType == LT_Pulse || mType == LT_PulseSlow)
{
cycle_time = 2.0f * Ogre::Math::PI;
time_distortion = 20.0f;
}
else
{
static const float fa = 0.785398f;
static const float phase_wavelength = 120.0f * 3.14159265359f / fa;
cycle_time = 500.0f;
mPhase = std::fmod(mPhase + value, phase_wavelength);
time_distortion = flickerFrequency(mPhase);
}
mDeltaCount += mDirection*value*time_distortion;
if(mDirection > 0 && mDeltaCount > +cycle_time)
{
mDirection = -1.0f;
mDeltaCount = 2.0f*cycle_time - mDeltaCount;
}
if(mDirection < 0 && mDeltaCount < -cycle_time)
{
mDirection = +1.0f;
mDeltaCount = -2.0f*cycle_time - mDeltaCount;
}
static const float fast = 4.0f/1.0f;
static const float slow = 1.0f/1.0f;
// These formulas are just guesswork, but they work pretty well
if(mType == LT_Normal)
{
// Less than 1/255 light modifier for a constant light:
brightness = 1.0 + flickerAmplitude(mDeltaCount*slow)/255.0f;
}
else if(mType == LT_Flicker)
brightness = 0.75 + flickerAmplitude(mDeltaCount*fast)*0.25;
else if(mType == LT_FlickerSlow)
brightness = 0.75 + flickerAmplitude(mDeltaCount*slow)*0.25;
else if(mType == LT_Pulse)
brightness = 1.0 + pulseAmplitude(mDeltaCount*fast)*0.25;
else if(mType == LT_PulseSlow)
brightness = 1.0 + pulseAmplitude(mDeltaCount*slow)*0.25;
return brightness;
}
LightValue::LightValue(Ogre::Light *light, const Ogre::ColourValue &color)
: mTarget(light)
, mColor(color)
{
}
Ogre::Real LightValue::getValue() const
{
return 0.0f;
}
void LightValue::setValue(Ogre::Real value)
{
mTarget->setDiffuseColour(mColor * value);
}
}
}

@ -0,0 +1,51 @@
#ifndef OENGINE_OGRE_LIGHTS_H
#define OENGINE_OGRE_LIGHTS_H
#include <OgreController.h>
#include <OgreColourValue.h>
/*
* Controller classes to handle pulsing and flicker lights
*/
namespace OEngine {
namespace Render {
enum LightType {
LT_Normal,
LT_Flicker,
LT_FlickerSlow,
LT_Pulse,
LT_PulseSlow
};
class LightFunction : public Ogre::ControllerFunction<Ogre::Real>
{
LightType mType;
Ogre::Real mPhase;
Ogre::Real mDirection;
static Ogre::Real pulseAmplitude(Ogre::Real time);
static Ogre::Real flickerAmplitude(Ogre::Real time);
static Ogre::Real flickerFrequency(Ogre::Real phase);
public:
LightFunction(LightType type);
virtual Ogre::Real calculate(Ogre::Real value);
};
class LightValue : public Ogre::ControllerValue<Ogre::Real>
{
Ogre::Light *mTarget;
Ogre::ColourValue mColor;
public:
LightValue(Ogre::Light *light, const Ogre::ColourValue &color);
virtual Ogre::Real getValue() const;
virtual void setValue(Ogre::Real value);
};
}
}
#endif

@ -4,6 +4,7 @@
#include <OgreRenderTexture.h> #include <OgreRenderTexture.h>
#include <OgreSubEntity.h> #include <OgreSubEntity.h>
#include <OgreEntity.h> #include <OgreEntity.h>
#include <OgreTechnique.h>
#include <stdexcept> #include <stdexcept>
#include <extern/shiny/Main/Factory.hpp> #include <extern/shiny/Main/Factory.hpp>
@ -100,7 +101,16 @@ namespace Render
return m->getTechnique(1); return m->getTechnique(1);
} }
else else
throw std::runtime_error("selectionbuffer only works with entities"); {
m = Ogre::MaterialManager::getSingleton().getByName("NullMaterial");
if(m.isNull())
{
m = Ogre::MaterialManager::getSingleton().create("NullMaterial", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
m->getTechnique(0)->getPass(0)->setDepthCheckEnabled(true);
m->getTechnique(0)->getPass(0)->setDepthFunction(Ogre::CMPF_ALWAYS_FAIL);
}
return m->getTechnique(0);
}
} }
return NULL; return NULL;
} }

Loading…
Cancel
Save