Merge remote-tracking branch 'chris/animation2'

actorid
Marc Zinnschlag 12 years ago
commit cd40d167ce

@ -94,8 +94,6 @@ set(OENGINE_BULLET
${LIBDIR}/openengine/bullet/physic.hpp ${LIBDIR}/openengine/bullet/physic.hpp
${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp ${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp
${LIBDIR}/openengine/bullet/BulletShapeLoader.h ${LIBDIR}/openengine/bullet/BulletShapeLoader.h
${LIBDIR}/openengine/bullet/pmove.cpp
${LIBDIR}/openengine/bullet/pmove.h
${LIBDIR}/openengine/bullet/trace.cpp ${LIBDIR}/openengine/bullet/trace.cpp
${LIBDIR}/openengine/bullet/trace.h ${LIBDIR}/openengine/bullet/trace.h

@ -14,8 +14,8 @@ set(GAME_HEADER
source_group(game FILES ${GAME} ${GAME_HEADER}) source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender add_openmw_dir (mwrender
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderingmanager debugging sky player animation npcanimation creatureanimation activatoranimation
renderinginterface localmap occlusionquery terrain terrainmaterial water shadows actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows
compositors characterpreview externalrendering globalmap videoplayer compositors characterpreview externalrendering globalmap videoplayer
) )
@ -62,8 +62,9 @@ add_openmw_dir (mwclass
) )
add_openmw_dir (mwmechanics add_openmw_dir (mwmechanics
mechanicsmanagerimp stat creaturestats magiceffects movement actors drawstate spells mechanicsmanagerimp stat character creaturestats magiceffects movement actors activators
activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
aiescort aiactivate
) )
add_openmw_dir (mwbase add_openmw_dir (mwbase

@ -9,6 +9,7 @@
#include <components/bsa/bsa_archive.hpp> #include <components/bsa/bsa_archive.hpp>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <components/translation/translation.hpp> #include <components/translation/translation.hpp>
#include <components/nif/nif_file.hpp>
#include <components/nifoverrides/nifoverrides.hpp> #include <components/nifoverrides/nifoverrides.hpp>
#include <components/nifbullet/bullet_nif_loader.hpp> #include <components/nifbullet/bullet_nif_loader.hpp>
@ -66,14 +67,15 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
{ {
try try
{ {
mEnvironment.setFrameDuration (evt.timeSinceLastFrame); float frametime = std::min(evt.timeSinceLastFrame, 0.2f);
mEnvironment.setFrameDuration(frametime);
// update input // update input
MWBase::Environment::get().getInputManager()->update(evt.timeSinceLastFrame, false); MWBase::Environment::get().getInputManager()->update(frametime, false);
// sound // sound
if (mUseSound) if (mUseSound)
MWBase::Environment::get().getSoundManager()->update (evt.timeSinceLastFrame); MWBase::Environment::get().getSoundManager()->update(frametime);
// global scripts // global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
@ -87,23 +89,19 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
// passing of time // passing of time
if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->advanceTime ( MWBase::Environment::get().getWorld()->advanceTime(
mEnvironment.getFrameDuration()*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600);
if (changed) // keep change flag for another frame, if cell changed happend in local script if (changed) // keep change flag for another frame, if cell changed happend in local script
MWBase::Environment::get().getWorld()->markCellAsUnchanged(); MWBase::Environment::get().getWorld()->markCellAsUnchanged();
// update actors // update actors
std::vector<std::pair<std::string, Ogre::Vector3> > movement; MWBase::Environment::get().getMechanicsManager()->update(frametime,
MWBase::Environment::get().getMechanicsManager()->update (movement, mEnvironment.getFrameDuration(),
MWBase::Environment::get().getWindowManager()->isGuiMode()); MWBase::Environment::get().getWindowManager()->isGuiMode());
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->doPhysics (movement, mEnvironment.getFrameDuration());
// update world // update world
MWBase::Environment::get().getWorld()->update (evt.timeSinceLastFrame, MWBase::Environment::get().getWindowManager()->isGuiMode()); MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode());
// update GUI // update GUI
Ogre::RenderWindow* window = mOgre->getWindow(); Ogre::RenderWindow* window = mOgre->getWindow();
@ -111,7 +109,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
MWBase::Environment::get().getWorld()->getTriangleBatchCount(tri, batch); MWBase::Environment::get().getWorld()->getTriangleBatchCount(tri, batch);
MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch); MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch);
MWBase::Environment::get().getWindowManager()->onFrame(evt.timeSinceLastFrame); MWBase::Environment::get().getWindowManager()->onFrame(frametime);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {

@ -37,24 +37,24 @@ namespace MWBase
virtual ~MechanicsManager() {} virtual ~MechanicsManager() {}
virtual void addActor (const MWWorld::Ptr& ptr) = 0; virtual void add (const MWWorld::Ptr& ptr) = 0;
///< Register an actor for stats management ///< Register an object for management
///
/// \note Dead actors are ignored. virtual void remove (const MWWorld::Ptr& ptr) = 0;
///< Deregister an object for management
virtual void removeActor (const MWWorld::Ptr& ptr) = 0; virtual void updateCell(const MWWorld::Ptr &ptr) = 0;
///< Deregister an actor for stats management ///< Moves an object to a new cell
virtual void dropActors (const MWWorld::CellStore *cellStore) = 0; virtual void drop (const MWWorld::CellStore *cellStore) = 0;
///< Deregister all actors in the given cell. ///< Deregister all objects in the given cell.
virtual void watchActor (const MWWorld::Ptr& ptr) = 0; virtual void watchActor (const MWWorld::Ptr& ptr) = 0;
///< On each update look for changes in a previously registered actor and update the ///< On each update look for changes in a previously registered actor and update the
/// GUI accordingly. /// GUI accordingly.
virtual void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement, virtual void update (float duration, bool paused) = 0;
float duration, bool paused) = 0; ///< Update objects
///< Update actor stats and store desired velocity vectors in \a movement
/// ///
/// \param paused In game type does not currently advance (this usually means some GUI /// \param paused In game type does not currently advance (this usually means some GUI
/// component is up). /// component is up).
@ -98,6 +98,17 @@ namespace MWBase
virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type,
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0; float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0;
///< Perform a persuasion action on NPC ///< Perform a persuasion action on NPC
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0;
///< Run animation for a MW-reference. Calls to this function for references that are currently not
/// in the scene should be ignored.
///
/// \param mode 0 normal, 1 immediate start, 2 immediate loop
/// \param count How many times the animation should be run
virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the scene should be ignored.
}; };
} }

@ -6,6 +6,7 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include "../mwworld/globals.hpp" #include "../mwworld/globals.hpp"
#include "../mwworld/ptr.hpp"
namespace Ogre namespace Ogre
{ {
@ -19,6 +20,11 @@ namespace OEngine
{ {
class Fader; class Fader;
} }
namespace Physic
{
class PhysicEngine;
}
} }
namespace ESM namespace ESM
@ -35,6 +41,7 @@ namespace ESM
namespace MWRender namespace MWRender
{ {
class ExternalRendering; class ExternalRendering;
class Animation;
} }
namespace MWWorld namespace MWWorld
@ -42,10 +49,11 @@ namespace MWWorld
class CellStore; class CellStore;
class Player; class Player;
class LocalScripts; class LocalScripts;
class Ptr;
class TimeStamp; class TimeStamp;
class ESMStore; class ESMStore;
class RefData; class RefData;
typedef std::vector<std::pair<MWWorld::Ptr,Ogre::Vector3> > PtrMovementList;
} }
namespace MWBase namespace MWBase
@ -227,8 +235,7 @@ namespace MWBase
virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const = 0; virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const = 0;
///< Convert position to cell numbers ///< Convert position to cell numbers
virtual void doPhysics (const std::vector<std::pair<std::string, Ogre::Vector3> >& actors, virtual void doPhysics (const MWWorld::PtrMovementList &actors, float duration) = 0;
float duration) = 0;
///< Run physics simulation and modify \a world accordingly. ///< Run physics simulation and modify \a world accordingly.
virtual bool toggleCollisionMode() = 0; virtual bool toggleCollisionMode() = 0;
@ -263,18 +270,6 @@ namespace MWBase
///< Create a new recrod (of type npc) in the ESM store. ///< Create a new recrod (of type npc) in the ESM store.
/// \return pointer to created record /// \return pointer to created record
virtual void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName,
int mode, int number = 1) = 0;
///< Run animation for a MW-reference. Calls to this function for references that are
/// currently not in the rendered scene should be ignored.
///
/// \param mode: 0 normal, 1 immediate start, 2 immediate loop
/// \param number How offen the animation should be run
virtual void skipAnimation (const MWWorld::Ptr& ptr) = 0;
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the rendered scene should be ignored.
virtual void update (float duration, bool paused) = 0; virtual void update (float duration, bool paused) = 0;
virtual bool placeObject(const MWWorld::Ptr& object, float cursorX, float cursorY) = 0; virtual bool placeObject(const MWWorld::Ptr& object, float cursorX, float cursorY) = 0;
@ -291,8 +286,10 @@ namespace MWBase
virtual void processChangedSettings (const Settings::CategorySettingVector& settings) = 0; virtual void processChangedSettings (const Settings::CategorySettingVector& settings) = 0;
virtual bool isSwimming(const MWWorld::Ptr &object) = 0; virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0;
virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0; virtual bool isSwimming(const MWWorld::Ptr &object) const = 0;
virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) const = 0;
virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0;
virtual void togglePOV() = 0; virtual void togglePOV() = 0;
virtual void togglePreviewMode(bool enable) = 0; virtual void togglePreviewMode(bool enable) = 0;
@ -311,6 +308,8 @@ namespace MWBase
/// 2 - player is underwater \n /// 2 - player is underwater \n
/// 3 - enemies are nearby (not implemented) /// 3 - enemies are nearby (not implemented)
/// \todo Probably shouldn't be here
virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0;
/// \todo this does not belong here /// \todo this does not belong here
virtual void playVideo(const std::string& name, bool allowSkipping) = 0; virtual void playVideo(const std::string& name, bool allowSkipping) = 0;

@ -5,12 +5,13 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld//cellstore.hpp" #include "../mwworld//cellstore.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/physicssystem.hpp" #include "../mwworld/physicssystem.hpp"
#include "../mwrender/objects.hpp" #include "../mwrender/actors.hpp"
#include "../mwrender/renderinginterface.hpp" #include "../mwrender/renderinginterface.hpp"
#include "../mwgui/tooltips.hpp" #include "../mwgui/tooltips.hpp"
@ -21,9 +22,8 @@ namespace MWClass
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);
if (!model.empty()) { if (!model.empty()) {
MWRender::Objects& objects = renderingInterface.getObjects(); MWRender::Actors& actors = renderingInterface.getActors();
objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false); actors.insertActivator(ptr);
objects.insertMesh(ptr, model);
} }
} }
@ -32,6 +32,7 @@ namespace MWClass
const std::string model = getModel(ptr); const std::string model = getModel(ptr);
if(!model.empty()) if(!model.empty())
physics.addObject(ptr); physics.addObject(ptr);
MWBase::Environment::get().getMechanicsManager()->add(ptr);
} }
std::string Activator::getModel(const MWWorld::Ptr &ptr) const std::string Activator::getModel(const MWWorld::Ptr &ptr) const

@ -18,6 +18,7 @@
#include "../mwworld/physicssystem.hpp" #include "../mwworld/physicssystem.hpp"
#include "../mwrender/renderinginterface.hpp" #include "../mwrender/renderinginterface.hpp"
#include "../mwrender/actors.hpp"
#include "../mwgui/tooltips.hpp" #include "../mwgui/tooltips.hpp"
@ -96,7 +97,7 @@ namespace MWClass
const std::string model = getModel(ptr); const std::string model = getModel(ptr);
if(!model.empty()) if(!model.empty())
physics.addActor(ptr); physics.addActor(ptr);
MWBase::Environment::get().getMechanicsManager()->addActor (ptr); MWBase::Environment::get().getMechanicsManager()->add(ptr);
} }
std::string Creature::getModel(const MWWorld::Ptr &ptr) const std::string Creature::getModel(const MWWorld::Ptr &ptr) const

@ -1,9 +1,7 @@
#ifndef GAME_MWCLASS_CREATURE_H #ifndef GAME_MWCLASS_CREATURE_H
#define GAME_MWCLASS_CREATURE_H #define GAME_MWCLASS_CREATURE_H
#include "../mwrender/renderinginterface.hpp" #include "../mwworld/class.hpp"
#include "../mwrender/actors.hpp"
namespace MWClass namespace MWClass
{ {

@ -36,14 +36,9 @@ namespace MWClass
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
const int color = ref->mBase->mData.mColor; objects.insertLight(ptr);
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);
objects.insertLight (ptr, r, g, b, radius);
} }
void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const

@ -55,9 +55,35 @@ namespace MWClass
{ {
void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const
{ {
static bool inited = false;
if(!inited)
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
fMinWalkSpeed = gmst.find("fMinWalkSpeed");
fMaxWalkSpeed = gmst.find("fMaxWalkSpeed");
fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect");
fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier");
fAthleticsRunBonus = gmst.find("fAthleticsRunBonus");
fBaseRunMultiplier = gmst.find("fBaseRunMultiplier");
fMinFlySpeed = gmst.find("fMinFlySpeed");
fMaxFlySpeed = gmst.find("fMaxFlySpeed");
fSwimRunBase = gmst.find("fSwimRunBase");
fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult");
fJumpEncumbranceBase = gmst.find("fJumpEncumbranceBase");
fJumpEncumbranceMultiplier = gmst.find("fJumpEncumbranceMultiplier");
fJumpAcrobaticsBase = gmst.find("fJumpAcrobaticsBase");
fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier");
fJumpRunMultiplier = gmst.find("fJumpRunMultiplier");
// Added in Tribunal/Bloodmoon, may not exist
fWereWolfRunMult = gmst.search("fWereWolfRunMult");
inited = true;
}
if (!ptr.getRefData().getCustomData()) if (!ptr.getRefData().getCustomData())
{ {
std::auto_ptr<CustomData> data (new CustomData); std::auto_ptr<CustomData> data(new CustomData);
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
@ -142,7 +168,7 @@ namespace MWClass
void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const void Npc::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const
{ {
physics.addActor(ptr); physics.addActor(ptr);
MWBase::Environment::get().getMechanicsManager()->addActor(ptr); MWBase::Environment::get().getMechanicsManager()->add(ptr);
} }
std::string Npc::getModel(const MWWorld::Ptr &ptr) const std::string Npc::getModel(const MWWorld::Ptr &ptr) const
@ -297,9 +323,88 @@ namespace MWClass
return false; return false;
} }
float Npc::getSpeed (const MWWorld::Ptr& ptr) const float Npc::getSpeed(const MWWorld::Ptr& ptr) const
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData());
const MWMechanics::MagicEffects &mageffects = npcdata->mCreatureStats.getMagicEffects();
const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr);
float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mCreatureStats.getAttribute(ESM::Attribute::Speed).getModified()*
(fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat());
walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance;
walkSpeed = std::max(0.0f, walkSpeed);
if(Npc::getStance(ptr, Sneak, false))
walkSpeed *= fSneakSpeedMultiplier->getFloat();
float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() *
fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat());
if(npcdata->mNpcStats.isWerewolf())
runSpeed *= fWereWolfRunMult->getFloat();
float moveSpeed;
if(normalizedEncumbrance >= 1.0f)
moveSpeed = 0.0f;
else if(mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0)
{
float flySpeed = 0.01f*(npcdata->mCreatureStats.getAttribute(ESM::Attribute::Speed).getModified() +
mageffects.get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude);
flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat());
flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
flySpeed = std::max(0.0f, flySpeed);
moveSpeed = flySpeed;
}
else if(world->isSwimming(ptr))
{
float swimSpeed = walkSpeed;
if(Npc::getStance(ptr, Run, false))
swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(MWMechanics::EffectKey(1/*swift swim*/)).mMagnitude;
swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
fSwimRunAthleticsMult->getFloat();
moveSpeed = swimSpeed;
}
else if(Npc::getStance(ptr, Run, false))
moveSpeed = runSpeed;
else
moveSpeed = walkSpeed;
if(getMovementSettings(ptr).mLeftRight != 0 && getMovementSettings(ptr).mForwardBackward == 0)
moveSpeed *= 0.75f;
return moveSpeed;
}
float Npc::getJump(const MWWorld::Ptr &ptr) const
{ {
return getStance (ptr, Run) ? 600 : 300; // TODO calculate these values from stats const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData());
const MWMechanics::MagicEffects &mageffects = npcdata->mCreatureStats.getMagicEffects();
const float encumbranceTerm = fJumpEncumbranceBase->getFloat() +
fJumpEncumbranceMultiplier->getFloat() *
(Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr));
float a = npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified();
float b = 0.0f;
if(a > 50.0f)
{
b = a - 50.0f;
a = 50.0f;
}
float x = fJumpAcrobaticsBase->getFloat() +
std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat());
x += 3 * b * fJumpAcroMultiplier->getFloat();
x += mageffects.get(MWMechanics::EffectKey(9/*jump*/)).mMagnitude * 64;
x *= encumbranceTerm;
if(Npc::getStance(ptr, Run, false))
x *= fJumpRunMultiplier->getFloat();
x *= 1.25f;//fatigueTerm;
x -= -627.2/*gravity constant*/;
x /= 3;
return x;
} }
MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const
@ -311,14 +416,10 @@ namespace MWClass
Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const
{ {
Ogre::Vector3 vector (0, 0, 0); Ogre::Vector3 vector;
vector.x = getMovementSettings(ptr).mLeftRight;
vector.x = getMovementSettings (ptr).mLeftRight * 127; vector.y = getMovementSettings(ptr).mForwardBackward;
vector.y = getMovementSettings (ptr).mForwardBackward * 127; vector.z = getMovementSettings(ptr).mUpDown;
vector.z = getMovementSettings(ptr).mUpDown * 127;
//if (getStance (ptr, Run, false))
// vector *= 2;
return vector; return vector;
} }
@ -420,4 +521,21 @@ namespace MWClass
return MWWorld::Ptr(&cell.mNpcs.insert(*ref), &cell); return MWWorld::Ptr(&cell.mNpcs.insert(*ref), &cell);
} }
const ESM::GameSetting *Npc::fMinWalkSpeed;
const ESM::GameSetting *Npc::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect;
const ESM::GameSetting *Npc::fSneakSpeedMultiplier;
const ESM::GameSetting *Npc::fAthleticsRunBonus;
const ESM::GameSetting *Npc::fBaseRunMultiplier;
const ESM::GameSetting *Npc::fMinFlySpeed;
const ESM::GameSetting *Npc::fMaxFlySpeed;
const ESM::GameSetting *Npc::fSwimRunBase;
const ESM::GameSetting *Npc::fSwimRunAthleticsMult;
const ESM::GameSetting *Npc::fJumpEncumbranceBase;
const ESM::GameSetting *Npc::fJumpEncumbranceMultiplier;
const ESM::GameSetting *Npc::fJumpAcrobaticsBase;
const ESM::GameSetting *Npc::fJumpAcroMultiplier;
const ESM::GameSetting *Npc::fJumpRunMultiplier;
const ESM::GameSetting *Npc::fWereWolfRunMult;
} }

@ -3,6 +3,11 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
namespace ESM
{
class GameSetting;
}
namespace MWClass namespace MWClass
{ {
class Npc : public MWWorld::Class class Npc : public MWWorld::Class
@ -12,6 +17,23 @@ namespace MWClass
virtual MWWorld::Ptr virtual MWWorld::Ptr
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
static const ESM::GameSetting *fMinWalkSpeed;
static const ESM::GameSetting *fMaxWalkSpeed;
static const ESM::GameSetting *fEncumberedMoveEffect;
static const ESM::GameSetting *fSneakSpeedMultiplier;
static const ESM::GameSetting *fAthleticsRunBonus;
static const ESM::GameSetting *fBaseRunMultiplier;
static const ESM::GameSetting *fMinFlySpeed;
static const ESM::GameSetting *fMaxFlySpeed;
static const ESM::GameSetting *fSwimRunBase;
static const ESM::GameSetting *fSwimRunAthleticsMult;
static const ESM::GameSetting *fJumpEncumbranceBase;
static const ESM::GameSetting *fJumpEncumbranceMultiplier;
static const ESM::GameSetting *fJumpAcrobaticsBase;
static const ESM::GameSetting *fJumpAcroMultiplier;
static const ESM::GameSetting *fJumpRunMultiplier;
static const ESM::GameSetting *fWereWolfRunMult;
public: public:
virtual std::string getId (const MWWorld::Ptr& ptr) const; virtual std::string getId (const MWWorld::Ptr& ptr) const;
@ -64,6 +86,9 @@ namespace MWClass
virtual float getSpeed (const MWWorld::Ptr& ptr) const; virtual float getSpeed (const MWWorld::Ptr& ptr) const;
///< Return movement speed. ///< Return movement speed.
virtual float getJump(const MWWorld::Ptr &ptr) const;
///< Return jump velocity (not accounting for movement)
virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const; virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const;
///< Return desired movement. ///< Return desired movement.

@ -271,12 +271,12 @@ namespace MWInput
if (actionIsActive(A_MoveLeft)) if (actionIsActive(A_MoveLeft))
{ {
mPlayer.setAutoMove (false); mPlayer.setAutoMove (false);
mPlayer.setLeftRight (1); mPlayer.setLeftRight (-1);
} }
else if (actionIsActive(A_MoveRight)) else if (actionIsActive(A_MoveRight))
{ {
mPlayer.setAutoMove (false); mPlayer.setAutoMove (false);
mPlayer.setLeftRight (-1); mPlayer.setLeftRight (1);
} }
else else
mPlayer.setLeftRight (0); mPlayer.setLeftRight (0);
@ -301,6 +301,11 @@ namespace MWInput
else else
mPlayer.setUpDown (0); mPlayer.setUpDown (0);
if(actionIsActive(A_Run))
mPlayer.setRunState(true);
else
mPlayer.setRunState(false);
if (mControlSwitch["playerviewswitch"]) { if (mControlSwitch["playerviewswitch"]) {
// work around preview mode toggle when pressing Alt+Tab // work around preview mode toggle when pressing Alt+Tab
@ -711,6 +716,7 @@ namespace MWInput
defaultKeyBindings[A_ToggleSpell] = OIS::KC_R; defaultKeyBindings[A_ToggleSpell] = OIS::KC_R;
defaultKeyBindings[A_QuickKeysMenu] = OIS::KC_F1; defaultKeyBindings[A_QuickKeysMenu] = OIS::KC_F1;
defaultKeyBindings[A_Console] = OIS::KC_F2; defaultKeyBindings[A_Console] = OIS::KC_F2;
defaultKeyBindings[A_Run] = OIS::KC_LSHIFT;
defaultKeyBindings[A_Crouch] = OIS::KC_LCONTROL; defaultKeyBindings[A_Crouch] = OIS::KC_LCONTROL;
defaultKeyBindings[A_AutoMove] = OIS::KC_Q; defaultKeyBindings[A_AutoMove] = OIS::KC_Q;
defaultKeyBindings[A_Jump] = OIS::KC_E; defaultKeyBindings[A_Jump] = OIS::KC_E;
@ -777,6 +783,7 @@ namespace MWInput
descriptions[A_ToggleWeapon] = "sReady_Weapon"; descriptions[A_ToggleWeapon] = "sReady_Weapon";
descriptions[A_ToggleSpell] = "sReady_Magic"; descriptions[A_ToggleSpell] = "sReady_Magic";
descriptions[A_Console] = "sConsoleTitle"; descriptions[A_Console] = "sConsoleTitle";
descriptions[A_Run] = "sRun";
descriptions[A_Crouch] = "sCrouch_Sneak"; descriptions[A_Crouch] = "sCrouch_Sneak";
descriptions[A_AutoMove] = "sAuto_Run"; descriptions[A_AutoMove] = "sAuto_Run";
descriptions[A_Jump] = "sJump"; descriptions[A_Jump] = "sJump";
@ -825,6 +832,7 @@ namespace MWInput
ret.push_back(A_MoveLeft); ret.push_back(A_MoveLeft);
ret.push_back(A_MoveRight); ret.push_back(A_MoveRight);
ret.push_back(A_TogglePOV); ret.push_back(A_TogglePOV);
ret.push_back(A_Run);
ret.push_back(A_Crouch); ret.push_back(A_Crouch);
ret.push_back(A_Activate); ret.push_back(A_Activate);
ret.push_back(A_ToggleWeapon); ret.push_back(A_ToggleWeapon);

@ -207,7 +207,7 @@ namespace MWInput
A_Journal, //Journal A_Journal, //Journal
A_Weapon, //Draw/Sheath weapon A_Weapon, //Draw/Sheath weapon
A_Spell, //Ready/Unready Casting A_Spell, //Ready/Unready Casting
A_AlwaysRun, //Toggle Always Run A_Run, //Run when held
A_CycleSpellLeft, //cycling through spells A_CycleSpellLeft, //cycling through spells
A_CycleSpellRight, A_CycleSpellRight,
A_CycleWeaponLeft,//Cycling through weapons A_CycleWeaponLeft,//Cycling through weapons

@ -0,0 +1,74 @@
#include "activators.hpp"
#include <OgreVector3.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
namespace MWMechanics
{
Activators::Activators()
{
}
void Activators::addActivator(const MWWorld::Ptr& ptr)
{
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
if(anim != NULL)
mActivators.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle, true)));
}
void Activators::removeActivator (const MWWorld::Ptr& ptr)
{
PtrControllerMap::iterator iter = mActivators.find(ptr);
if(iter != mActivators.end())
mActivators.erase(iter);
}
void Activators::updateActivatorCell(const MWWorld::Ptr &ptr)
{
PtrControllerMap::iterator iter = mActivators.find(ptr);
if(iter != mActivators.end())
{
CharacterController ctrl = iter->second;
mActivators.erase(iter);
mActivators.insert(std::make_pair(ptr, ctrl));
}
}
void Activators::dropActivators (const MWWorld::Ptr::CellStore *cellStore)
{
PtrControllerMap::iterator iter = mActivators.begin();
while(iter != mActivators.end())
{
if(iter->first.getCell()==cellStore)
mActivators.erase(iter++);
else
++iter;
}
}
void Activators::update(float duration, bool paused)
{
if(!paused)
{
for(PtrControllerMap::iterator iter(mActivators.begin());iter != mActivators.end();++iter)
iter->second.update(duration);
}
}
void Activators::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
{
PtrControllerMap::iterator iter = mActivators.find(ptr);
if(iter != mActivators.end())
iter->second.playGroup(groupName, mode, number);
}
void Activators::skipAnimation(const MWWorld::Ptr& ptr)
{
PtrControllerMap::iterator iter = mActivators.find(ptr);
if(iter != mActivators.end())
iter->second.skipAnim();
}
}

@ -0,0 +1,45 @@
#ifndef GAME_MWMECHANICS_ACTIVATORS_H
#define GAME_MWMECHANICS_ACTOVATRS_H
#include <string>
#include <map>
#include "character.hpp"
namespace MWWorld
{
class Ptr;
class CellStore;
}
namespace MWMechanics
{
class Activators
{
typedef std::map<MWWorld::Ptr,CharacterController> PtrControllerMap;
PtrControllerMap mActivators;
public:
Activators();
void addActivator (const MWWorld::Ptr& ptr);
///< Register an animated activator
void removeActivator (const MWWorld::Ptr& ptr);
///< Deregister an activator
void updateActivatorCell(const MWWorld::Ptr& ptr);
///< Updates an activator with a new cell store
void dropActivators (const MWWorld::CellStore *cellStore);
///< Deregister all activators in the given cell.
void update (float duration, bool paused);
///< Update activator animations
void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
void skipAnimation(const MWWorld::Ptr& ptr);
};
}
#endif

@ -165,35 +165,44 @@ namespace MWMechanics
void Actors::addActor (const MWWorld::Ptr& ptr) void Actors::addActor (const MWWorld::Ptr& ptr)
{ {
if (!MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead()) MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
mActors.insert (ptr); if(!MWWorld::Class::get(ptr).getCreatureStats(ptr).isDead())
mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle, true)));
else else
MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, "death1", 2); mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Death1, false)));
} }
void Actors::removeActor (const MWWorld::Ptr& ptr) void Actors::removeActor (const MWWorld::Ptr& ptr)
{ {
std::set<MWWorld::Ptr>::iterator iter = mActors.find (ptr); PtrControllerMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
mActors.erase(iter);
}
if (iter!=mActors.end()) void Actors::updateActorCell(const MWWorld::Ptr &ptr)
mActors.erase (iter); {
PtrControllerMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
{
CharacterController ctrl = iter->second;
mActors.erase(iter);
mActors.insert(std::make_pair(ptr, ctrl));
}
} }
void Actors::dropActors (const MWWorld::Ptr::CellStore *cellStore) void Actors::dropActors (const MWWorld::Ptr::CellStore *cellStore)
{ {
std::set<MWWorld::Ptr>::iterator iter = mActors.begin(); PtrControllerMap::iterator iter = mActors.begin();
while(iter != mActors.end())
while (iter!=mActors.end()) {
if (iter->getCell()==cellStore) if(iter->first.getCell()==cellStore)
{ mActors.erase(iter++);
mActors.erase (iter++);
}
else else
++iter; ++iter;
}
} }
void Actors::update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement, float duration, void Actors::update (float duration, bool paused)
bool paused)
{ {
mDuration += duration; mDuration += duration;
@ -201,79 +210,91 @@ namespace MWMechanics
{ {
float totalDuration = mDuration; float totalDuration = mDuration;
mDuration = 0; mDuration = 0;
std::set<MWWorld::Ptr>::iterator iter (mActors.begin());
while (iter!=mActors.end()) for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++)
{ {
if (!MWWorld::Class::get (*iter).getCreatureStats (*iter).isDead()) if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead())
{ {
updateActor (*iter, totalDuration); if(iter->second.getState() >= CharState_Death1)
iter->second.setState(CharState_Idle, true);
if (iter->getTypeName()==typeid (ESM::NPC).name()) updateActor(iter->first, totalDuration);
updateNpc (*iter, totalDuration, paused); if(iter->first.getTypeName() == typeid(ESM::NPC).name())
updateNpc(iter->first, totalDuration, paused);
if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead())
continue;
} }
if (MWWorld::Class::get (*iter).getCreatureStats (*iter).isDead()) // workaround: always keep player alive for now
// \todo remove workaround, once player death can be handled
if(iter->first.getRefData().getHandle()=="player")
{ {
// workaround: always keep player alive for now MWMechanics::DynamicStat<float> stat (
// \todo remove workaround, once player death can be handled MWWorld::Class::get(iter->first).getCreatureStats(iter->first).getHealth());
if (iter->getRefData().getHandle()=="player")
if (stat.getModified()<1)
{ {
MWMechanics::DynamicStat<float> stat ( stat.setModified (1, 0);
MWWorld::Class::get (*iter).getCreatureStats (*iter).getHealth()); MWWorld::Class::get(iter->first).getCreatureStats(iter->first).setHealth(stat);
if (stat.getModified()<1)
{
stat.setModified (1, 0);
MWWorld::Class::get (*iter).getCreatureStats (*iter).setHealth (stat);
}
MWWorld::Class::get (*iter).getCreatureStats (*iter).resurrect();
++iter;
continue;
} }
++mDeathCount[MWWorld::Class::get (*iter).getId (*iter)]; MWWorld::Class::get(iter->first).getCreatureStats(iter->first).resurrect();
continue;
}
MWBase::Environment::get().getWorld()->playAnimationGroup (*iter, "death1", 0); if(iter->second.getState() >= CharState_Death1)
continue;
if (MWWorld::Class::get (*iter).isEssential (*iter)) iter->second.setState(CharState_Death1, false);
MWBase::Environment::get().getWindowManager()->messageBox (
"#{sKilledEssential}", std::vector<std::string>());
mActors.erase (iter++); ++mDeathCount[MWWorld::Class::get(iter->first).getId(iter->first)];
}
else if(MWWorld::Class::get(iter->first).isEssential(iter->first))
++iter; MWBase::Environment::get().getWindowManager()->messageBox(
"#{sKilledEssential}", std::vector<std::string>());
} }
} }
for (std::set<MWWorld::Ptr>::iterator iter (mActors.begin()); iter!=mActors.end(); if(!paused)
++iter)
{ {
Ogre::Vector3 vector = MWWorld::Class::get (*iter).getMovementVector (*iter); mMovement.reserve(mActors.size());
if (vector!=Ogre::Vector3::ZERO) for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
movement.push_back (std::make_pair (iter->getRefData().getHandle(), vector)); {
Ogre::Vector3 movement = iter->second.update(duration);
mMovement.push_back(std::make_pair(iter->first, movement));
}
MWBase::Environment::get().getWorld()->doPhysics(mMovement, duration);
mMovement.clear();
} }
} }
void Actors::restoreDynamicStats() void Actors::restoreDynamicStats()
{ {
for (std::set<MWWorld::Ptr>::iterator iter (mActors.begin()); iter!=mActors.end(); ++iter) for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
{ calculateRestoration(iter->first, 3600);
calculateRestoration (*iter, 3600);
}
} }
int Actors::countDeaths (const std::string& id) const int Actors::countDeaths (const std::string& id) const
{ {
std::map<std::string, int>::const_iterator iter = mDeathCount.find (id); std::map<std::string, int>::const_iterator iter = mDeathCount.find(id);
if(iter != mDeathCount.end())
if (iter!=mDeathCount.end())
return iter->second; return iter->second;
return 0; return 0;
} }
void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
{
PtrControllerMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
iter->second.playGroup(groupName, mode, number);
}
void Actors::skipAnimation(const MWWorld::Ptr& ptr)
{
PtrControllerMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
iter->second.skipAnim();
}
} }

@ -6,6 +6,9 @@
#include <string> #include <string>
#include <map> #include <map>
#include "character.hpp"
#include "../mwbase/world.hpp"
namespace Ogre namespace Ogre
{ {
class Vector3; class Vector3;
@ -21,9 +24,14 @@ namespace MWMechanics
{ {
class Actors class Actors
{ {
std::set<MWWorld::Ptr> mActors; typedef std::map<MWWorld::Ptr,CharacterController> PtrControllerMap;
float mDuration; PtrControllerMap mActors;
std::map<std::string, int> mDeathCount;
MWWorld::PtrMovementList mMovement;
std::map<std::string, int> mDeathCount;
float mDuration;
void updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused); void updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused);
@ -50,11 +58,13 @@ namespace MWMechanics
/// ///
/// \note Ignored, if \a ptr is not a registered actor. /// \note Ignored, if \a ptr is not a registered actor.
void updateActorCell(const MWWorld::Ptr& ptr);
///< Updates an actor with a new cell store
void dropActors (const MWWorld::CellStore *cellStore); void dropActors (const MWWorld::CellStore *cellStore);
///< Deregister all actors in the given cell. ///< Deregister all actors in the given cell.
void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement, void update (float duration, bool paused);
float duration, bool paused);
///< Update actor stats and store desired velocity vectors in \a movement ///< Update actor stats and store desired velocity vectors in \a movement
void updateActor (const MWWorld::Ptr& ptr, float duration); void updateActor (const MWWorld::Ptr& ptr, float duration);
@ -66,6 +76,9 @@ namespace MWMechanics
int countDeaths (const std::string& id) const; int countDeaths (const std::string& id) const;
///< Return the number of deaths for actors with the given ID. ///< Return the number of deaths for actors with the given ID.
void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
void skipAnimation(const MWWorld::Ptr& ptr);
}; };
} }

@ -0,0 +1,290 @@
/*
* OpenMW - The completely unofficial reimplementation of Morrowind
*
* This file (character.cpp) is part of the OpenMW package.
*
* OpenMW is distributed as free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 3, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 3 along with this program. If not, see
* http://www.gnu.org/licenses/ .
*/
#include "character.hpp"
#include <OgreStringConverter.h>
#include "../mwrender/animation.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/class.hpp"
namespace MWMechanics
{
static const struct {
CharacterState state;
const char groupname[32];
} sStateList[] = {
{ CharState_Idle, "idle" },
{ CharState_Idle2, "idle2" },
{ CharState_Idle3, "idle3" },
{ CharState_Idle4, "idle4" },
{ CharState_Idle5, "idle5" },
{ CharState_Idle6, "idle6" },
{ CharState_Idle7, "idle7" },
{ CharState_Idle8, "idle8" },
{ CharState_Idle9, "idle9" },
{ CharState_IdleSwim, "idleswim" },
{ CharState_WalkForward, "walkforward" },
{ CharState_WalkBack, "walkback" },
{ CharState_WalkLeft, "walkleft" },
{ CharState_WalkRight, "walkright" },
{ CharState_SwimWalkForward, "swimwalkforward" },
{ CharState_SwimWalkBack, "swimwalkback" },
{ CharState_SwimWalkLeft, "swimwalkleft" },
{ CharState_SwimWalkRight, "swimwalkright" },
{ CharState_RunForward, "runforward" },
{ CharState_RunBack, "runback" },
{ CharState_RunLeft, "runleft" },
{ CharState_RunRight, "runright" },
{ CharState_SwimRunForward, "swimrunforward" },
{ CharState_SwimRunBack, "swimrunback" },
{ CharState_SwimRunLeft, "swimrunleft" },
{ CharState_SwimRunRight, "swimrunright" },
{ CharState_Jump, "jump" },
{ CharState_Death1, "death1" },
{ CharState_Death2, "death2" },
{ CharState_Death3, "death3" },
{ CharState_Death4, "death4" },
{ CharState_Death5, "death5" },
};
static const size_t sStateListSize = sizeof(sStateList)/sizeof(sStateList[0]);
static void getStateInfo(CharacterState state, std::string *group)
{
for(size_t i = 0;i < sStateListSize;i++)
{
if(sStateList[i].state == state)
{
*group = sStateList[i].groupname;
return;
}
}
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(state));
}
CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop)
: mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false)
{
if(!mAnimation)
return;
mAnimation->setController(this);
getStateInfo(mState, &mCurrentGroup);
if(ptr.getTypeName() == typeid(ESM::Activator).name())
{
/* Don't accumulate with activators (they don't get moved). */
mAnimation->setAccumulation(Ogre::Vector3::ZERO);
}
else
{
/* Accumulate along X/Y only for now, until we can figure out how we should
* handle knockout and death which moves the character down. */
mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f));
}
if(mAnimation->hasAnimation(mCurrentGroup))
mAnimation->play(mCurrentGroup, "stop", "stop", loop);
}
CharacterController::CharacterController(const CharacterController &rhs)
: mPtr(rhs.mPtr), mAnimation(rhs.mAnimation), mAnimQueue(rhs.mAnimQueue)
, mCurrentGroup(rhs.mCurrentGroup), mState(rhs.mState)
, mSkipAnim(rhs.mSkipAnim)
{
if(!mAnimation)
return;
/* We've been copied. Update the animation with the new controller. */
mAnimation->setController(this);
}
CharacterController::~CharacterController()
{
}
void CharacterController::markerEvent(float time, const std::string &evt)
{
if(evt == "stop")
{
if(mAnimQueue.size() >= 2 && mAnimQueue[0] == mAnimQueue[1])
{
mAnimQueue.pop_front();
mAnimation->play(mCurrentGroup, "loop start", "stop", false);
}
else if(mAnimQueue.size() > 0)
{
mAnimQueue.pop_front();
if(mAnimQueue.size() > 0)
{
mCurrentGroup = mAnimQueue.front();
mAnimation->play(mCurrentGroup, "start", "stop", false);
}
}
return;
}
std::cerr<< "Unhandled animation event: "<<evt <<std::endl;
}
Ogre::Vector3 CharacterController::update(float duration)
{
Ogre::Vector3 movement(0.0f);
float speed = 0.0f;
if(!(getState() >= CharState_Death1))
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
const Ogre::Vector3 &vec = cls.getMovementVector(mPtr);
bool onground = world->isOnGround(mPtr);
bool inwater = world->isSwimming(mPtr);
bool isrunning = cls.getStance(mPtr, MWWorld::Class::Run);
speed = cls.getSpeed(mPtr);
/* FIXME: The state should be set to Jump, and X/Y movement should be disallowed except
* for the initial thrust (which would be carried by "physics" until landing). */
if(onground && vec.z > 0.0f)
{
float x = cls.getJump(mPtr);
if(vec.x == 0 && vec.y == 0)
movement.z += x*duration;
else
{
/* FIXME: this would be more correct if we were going into a jumping state,
* rather than normal walking/idle states. */
//Ogre::Vector3 lat = Ogre::Vector3(vec.x, vec.y, 0.0f).normalisedCopy();
//movement += Ogre::Vector3(lat.x, lat.y, 1.0f) * x * 0.707f * duration;
movement.z += x * 0.707f * duration;
}
//decrease fatigue by fFatigueJumpBase + (1 - normalizedEncumbrance) * fFatigueJumpMult;
}
if(std::abs(vec.x/2.0f) > std::abs(vec.y) && speed > 0.0f)
{
if(vec.x > 0.0f)
setState(isrunning ?
(inwater ? CharState_SwimRunRight : CharState_RunRight) :
(inwater ? CharState_SwimWalkRight : CharState_WalkRight), true);
else if(vec.x < 0.0f)
setState(isrunning ?
(inwater ? CharState_SwimRunLeft: CharState_RunLeft) :
(inwater ? CharState_SwimWalkLeft : CharState_WalkLeft), true);
// Apply any forward/backward movement manually
movement.y += vec.y * (speed*duration);
}
else if(vec.y != 0.0f && speed > 0.0f)
{
if(vec.y > 0.0f)
setState(isrunning ?
(inwater ? CharState_SwimRunForward : CharState_RunForward) :
(inwater ? CharState_SwimWalkForward : CharState_WalkForward), true);
else if(vec.y < 0.0f)
setState(isrunning ?
(inwater ? CharState_SwimRunBack : CharState_RunBack) :
(inwater ? CharState_SwimWalkBack : CharState_WalkBack), true);
// Apply any sideways movement manually
movement.x += vec.x * (speed*duration);
}
else if(mAnimQueue.size() == 0)
setState((inwater ? CharState_IdleSwim : CharState_Idle), true);
}
if(mAnimation && !mSkipAnim)
{
mAnimation->setSpeed(speed);
movement += mAnimation->runAnimation(duration);
}
mSkipAnim = false;
return movement;
}
void CharacterController::playGroup(const std::string &groupname, int mode, int count)
{
if(!mAnimation || !mAnimation->hasAnimation(groupname))
std::cerr<< "Animation "<<groupname<<" not found" <<std::endl;
else
{
count = std::max(count, 1);
if(mode != 0 || mAnimQueue.size() == 0)
{
mAnimQueue.clear();
while(count-- > 0)
mAnimQueue.push_back(groupname);
mCurrentGroup = groupname;
mState = CharState_SpecialIdle;
mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", false);
}
else if(mode == 0)
{
mAnimQueue.resize(1);
while(count-- > 0)
mAnimQueue.push_back(groupname);
}
}
}
void CharacterController::skipAnim()
{
mSkipAnim = true;
}
void CharacterController::setState(CharacterState state, bool loop)
{
if(mState == state)
{
if(mAnimation)
mAnimation->setLooping(loop);
return;
}
mState = state;
if(!mAnimation)
return;
mAnimQueue.clear();
std::string anim;
getStateInfo(mState, &anim);
if(mAnimation->hasAnimation(anim))
{
mCurrentGroup = anim;
mAnimation->play(mCurrentGroup, "start", "stop", loop);
}
}
}

@ -0,0 +1,94 @@
#ifndef GAME_MWMECHANICS_CHARACTER_HPP
#define GAME_MWMECHANICS_CHARACTER_HPP
#include <OgreVector3.h>
#include "../mwworld/ptr.hpp"
namespace MWRender
{
class Animation;
}
namespace MWMechanics
{
enum CharacterState {
CharState_SpecialIdle,
CharState_Idle,
CharState_Idle2,
CharState_Idle3,
CharState_Idle4,
CharState_Idle5,
CharState_Idle6,
CharState_Idle7,
CharState_Idle8,
CharState_Idle9,
CharState_IdleSwim,
CharState_WalkForward,
CharState_WalkBack,
CharState_WalkLeft,
CharState_WalkRight,
CharState_SwimWalkForward,
CharState_SwimWalkBack,
CharState_SwimWalkLeft,
CharState_SwimWalkRight,
CharState_RunForward,
CharState_RunBack,
CharState_RunLeft,
CharState_RunRight,
CharState_SwimRunForward,
CharState_SwimRunBack,
CharState_SwimRunLeft,
CharState_SwimRunRight,
CharState_Jump,
/* Death states must be last! */
CharState_Death1,
CharState_Death2,
CharState_Death3,
CharState_Death4,
CharState_Death5
};
class CharacterController
{
MWWorld::Ptr mPtr;
MWRender::Animation *mAnimation;
typedef std::deque<std::string> AnimationQueue;
AnimationQueue mAnimQueue;
std::string mCurrentGroup;
CharacterState mState;
bool mSkipAnim;
protected:
/* Called by the animation whenever a new text key is reached. */
void markerEvent(float time, const std::string &evt);
friend class MWRender::Animation;
public:
CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop);
CharacterController(const CharacterController &rhs);
virtual ~CharacterController();
Ogre::Vector3 update(float duration);
void playGroup(const std::string &groupname, int mode, int count);
void skipAnim();
void setState(CharacterState state, bool loop);
CharacterState getState() const
{ return mState; }
};
}
#endif /* GAME_MWMECHANICS_CHARACTER_HPP */

@ -175,34 +175,47 @@ namespace MWMechanics
buildPlayer(); buildPlayer();
} }
void MechanicsManager::addActor (const MWWorld::Ptr& ptr) void MechanicsManager::add(const MWWorld::Ptr& ptr)
{ {
mActors.addActor (ptr); if(ptr.getTypeName() == typeid(ESM::Activator).name())
mActivators.addActivator(ptr);
else
mActors.addActor(ptr);
} }
void MechanicsManager::removeActor (const MWWorld::Ptr& ptr) void MechanicsManager::remove(const MWWorld::Ptr& ptr)
{ {
if (ptr==mWatched) if(ptr == mWatched)
mWatched = MWWorld::Ptr(); mWatched = MWWorld::Ptr();
mActors.removeActor(ptr);
mActivators.removeActivator(ptr);
}
mActors.removeActor (ptr); void MechanicsManager::updateCell(const MWWorld::Ptr &ptr)
{
if(ptr.getTypeName() == typeid(ESM::Activator).name())
mActivators.updateActivatorCell(ptr);
else
mActors.updateActorCell(ptr);
} }
void MechanicsManager::dropActors (const MWWorld::Ptr::CellStore *cellStore)
void MechanicsManager::drop(const MWWorld::CellStore *cellStore)
{ {
if (!mWatched.isEmpty() && mWatched.getCell()==cellStore) if(!mWatched.isEmpty() && mWatched.getCell() == cellStore)
mWatched = MWWorld::Ptr(); mWatched = MWWorld::Ptr();
mActors.dropActors (cellStore); mActors.dropActors(cellStore);
mActivators.dropActivators(cellStore);
} }
void MechanicsManager::watchActor (const MWWorld::Ptr& ptr)
void MechanicsManager::watchActor(const MWWorld::Ptr& ptr)
{ {
mWatched = ptr; mWatched = ptr;
} }
void MechanicsManager::update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement, void MechanicsManager::update(float duration, bool paused)
float duration, bool paused)
{ {
if (!mWatched.isEmpty()) if (!mWatched.isEmpty())
{ {
@ -296,9 +309,16 @@ namespace MWMechanics
} }
winMgr->configureSkills (majorSkills, minorSkills); winMgr->configureSkills (majorSkills, minorSkills);
// HACK? The player has been changed, so a new Animation object may
// have been made for them. Make sure they're properly updated.
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
mActors.removeActor(ptr);
mActors.addActor(ptr);
} }
mActors.update (movement, duration, paused); mActors.update(duration, paused);
mActivators.update(duration, paused);
} }
void MechanicsManager::restoreDynamicStats() void MechanicsManager::restoreDynamicStats()
@ -629,4 +649,20 @@ namespace MWMechanics
permChange = success ? -int(cappedDispositionChange/ fPerTempMult) : y; permChange = success ? -int(cappedDispositionChange/ fPerTempMult) : y;
} }
} }
void MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
{
if(ptr.getTypeName() == typeid(ESM::Activator).name())
mActivators.playAnimationGroup(ptr, groupName, mode, number);
else
mActors.playAnimationGroup(ptr, groupName, mode, number);
}
void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr)
{
if(ptr.getTypeName() == typeid(ESM::Activator).name())
mActivators.skipAnimation(ptr);
else
mActors.skipAnimation(ptr);
}
} }

@ -7,6 +7,7 @@
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
#include "activators.hpp"
#include "actors.hpp" #include "actors.hpp"
namespace Ogre namespace Ogre
@ -29,6 +30,8 @@ namespace MWMechanics
bool mUpdatePlayer; bool mUpdatePlayer;
bool mClassSelected; bool mClassSelected;
bool mRaceSelected; bool mRaceSelected;
Activators mActivators;
Actors mActors; Actors mActors;
void buildPlayer(); void buildPlayer();
@ -39,24 +42,24 @@ namespace MWMechanics
MechanicsManager(); MechanicsManager();
virtual void addActor (const MWWorld::Ptr& ptr); virtual void add (const MWWorld::Ptr& ptr);
///< Register an actor for stats management ///< Register an object for management
///
/// \note Dead actors are ignored.
virtual void removeActor (const MWWorld::Ptr& ptr); virtual void remove (const MWWorld::Ptr& ptr);
///< Deregister an actor for stats management ///< Deregister an object for management
virtual void dropActors (const MWWorld::CellStore *cellStore); virtual void updateCell(const MWWorld::Ptr &ptr);
///< Deregister all actors in the given cell. ///< Moves an object to a new cell
virtual void watchActor (const MWWorld::Ptr& ptr); virtual void drop(const MWWorld::CellStore *cellStore);
///< Deregister all objects in the given cell.
virtual void watchActor(const MWWorld::Ptr& ptr);
///< On each update look for changes in a previously registered actor and update the ///< On each update look for changes in a previously registered actor and update the
/// GUI accordingly. /// GUI accordingly.
virtual void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement, virtual void update (float duration, bool paused);
float duration, bool paused); ///< Update objects
///< Update actor stats and store desired velocity vectors in \a movement
/// ///
/// \param paused In game type does not currently advance (this usually means some GUI /// \param paused In game type does not currently advance (this usually means some GUI
/// component is up). /// component is up).
@ -92,6 +95,9 @@ namespace MWMechanics
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange); float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange);
void toLower(std::string npcFaction); void toLower(std::string npcFaction);
///< Perform a persuasion action on NPC ///< Perform a persuasion action on NPC
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
virtual void skipAnimation(const MWWorld::Ptr& ptr);
}; };
} }

@ -0,0 +1,58 @@
#include "activatoranimation.hpp"
#include <OgreEntity.h>
#include <OgreSceneManager.h>
#include <OgreSubEntity.h>
#include "renderconst.hpp"
#include "../mwbase/world.hpp"
namespace MWRender
{
ActivatorAnimation::~ActivatorAnimation()
{
}
ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr)
: Animation(ptr)
{
MWWorld::LiveCellRef<ESM::Activator> *ref = mPtr.get<ESM::Activator>();
assert (ref->mBase != NULL);
if(!ref->mBase->mModel.empty())
{
std::string mesh = "meshes\\" + ref->mBase->mModel;
createEntityList(mPtr.getRefData().getBaseNode(), mesh);
for(size_t i = 0;i < mEntityList.mEntities.size();i++)
{
Ogre::Entity *ent = mEntityList.mEntities[i];
bool transparent = false;
for (unsigned int j=0;j < ent->getNumSubEntities() && !transparent; ++j)
{
Ogre::MaterialPtr mat = ent->getSubEntity(j)->getMaterial();
Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator();
while (techIt.hasMoreElements() && !transparent)
{
Ogre::Technique* tech = techIt.getNext();
Ogre::Technique::PassIterator passIt = tech->getPassIterator();
while (passIt.hasMoreElements() && !transparent)
{
Ogre::Pass* pass = passIt.getNext();
if (pass->getDepthWriteEnabled() == false)
transparent = true;
}
}
}
ent->setVisibilityFlags(RV_Misc);
ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
}
setAnimationSource(mesh);
}
}
}

@ -0,0 +1,21 @@
#ifndef _GAME_RENDER_ACTIVATORANIMATION_H
#define _GAME_RENDER_ACTIVATORANIMATION_H
#include "animation.hpp"
namespace MWWorld
{
class Ptr;
}
namespace MWRender
{
class ActivatorAnimation : public Animation
{
public:
ActivatorAnimation(const MWWorld::Ptr& ptr);
virtual ~ActivatorAnimation();
};
}
#endif

@ -3,14 +3,23 @@
#include <OgreSceneNode.h> #include <OgreSceneNode.h>
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
#include "../mwworld/ptr.hpp"
#include "../mwworld/class.hpp"
#include "animation.hpp"
#include "activatoranimation.hpp"
#include "creatureanimation.hpp"
#include "npcanimation.hpp"
#include "renderconst.hpp" #include "renderconst.hpp"
namespace MWRender namespace MWRender
{ {
using namespace Ogre;
Actors::~Actors(){ Actors::~Actors()
{
PtrAnimationMap::iterator it = mAllActors.begin(); PtrAnimationMap::iterator it = mAllActors.begin();
for(;it != mAllActors.end();++it) for(;it != mAllActors.end();++it)
{ {
@ -22,15 +31,7 @@ Actors::~Actors(){
void Actors::setMwRoot(Ogre::SceneNode* root) void Actors::setMwRoot(Ogre::SceneNode* root)
{ mMwRoot = root; } { mMwRoot = root; }
void Actors::insertNPC(const MWWorld::Ptr &ptr, MWWorld::InventoryStore &inv) void Actors::insertBegin(const MWWorld::Ptr &ptr)
{
insertBegin(ptr, true, true);
NpcAnimation* anim = new MWRender::NpcAnimation(ptr, ptr.getRefData ().getBaseNode (), inv, RV_Actors);
mAllActors[ptr] = anim;
}
void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_)
{ {
Ogre::SceneNode* cellnode; Ogre::SceneNode* cellnode;
CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(ptr.getCell()); CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(ptr.getCell());
@ -62,17 +63,27 @@ void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_)
// Rotates first around z, then y, then x // Rotates first around z, then y, then x
insert->setOrientation(xr*yr*zr); insert->setOrientation(xr*yr*zr);
if (!enabled)
insert->setVisible (false);
ptr.getRefData().setBaseNode(insert); ptr.getRefData().setBaseNode(insert);
} }
void Actors::insertCreature (const MWWorld::Ptr& ptr){
insertBegin(ptr, true, true);
CreatureAnimation* anim = new MWRender::CreatureAnimation(ptr);
void Actors::insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv)
{
insertBegin(ptr);
NpcAnimation* anim = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(), inv, RV_Actors);
delete mAllActors[ptr];
mAllActors[ptr] = anim;
}
void Actors::insertCreature (const MWWorld::Ptr& ptr)
{
insertBegin(ptr);
CreatureAnimation* anim = new CreatureAnimation(ptr);
delete mAllActors[ptr];
mAllActors[ptr] = anim;
}
void Actors::insertActivator (const MWWorld::Ptr& ptr)
{
insertBegin(ptr);
ActivatorAnimation* anim = new ActivatorAnimation(ptr);
delete mAllActors[ptr]; delete mAllActors[ptr];
mAllActors[ptr] = anim; mAllActors[ptr] = anim;
} }
@ -125,28 +136,23 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store)
} }
} }
void Actors::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) void Actors::update (float duration)
{ {
PtrAnimationMap::const_iterator iter = mAllActors.find(ptr); // Nothing to do
if(iter != mAllActors.end())
iter->second->playGroup(groupName, mode, number);
} }
void Actors::skipAnimation (const MWWorld::Ptr& ptr)
Animation* Actors::getAnimation(const MWWorld::Ptr &ptr)
{ {
PtrAnimationMap::const_iterator iter = mAllActors.find(ptr); PtrAnimationMap::const_iterator iter = mAllActors.find(ptr);
if(iter != mAllActors.end()) if(iter != mAllActors.end())
iter->second->skipAnim(); return iter->second;
} return NULL;
void Actors::update (float duration)
{
for(PtrAnimationMap::const_iterator iter = mAllActors.begin();iter != mAllActors.end();iter++)
iter->second->runAnimation(duration);
} }
void Actors::updateObjectCell(const MWWorld::Ptr &ptr) void Actors::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur)
{ {
Ogre::SceneNode *node; Ogre::SceneNode *node;
MWWorld::CellStore *newCell = ptr.getCell(); MWWorld::CellStore *newCell = cur.getCell();
CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(newCell); CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(newCell);
if(celliter != mCellSceneNodes.end()) if(celliter != mCellSceneNodes.end())
@ -156,18 +162,15 @@ void Actors::updateObjectCell(const MWWorld::Ptr &ptr)
node = mMwRoot->createChildSceneNode(); node = mMwRoot->createChildSceneNode();
mCellSceneNodes[newCell] = node; mCellSceneNodes[newCell] = node;
} }
node->addChild(ptr.getRefData().getBaseNode()); node->addChild(cur.getRefData().getBaseNode());
PtrAnimationMap::iterator iter = mAllActors.find(ptr); PtrAnimationMap::iterator iter = mAllActors.find(old);
if(iter != mAllActors.end()) if(iter != mAllActors.end())
{ {
/// \note Update key (Ptr's are compared only with refdata so mCell Animation *anim = iter->second;
/// on key is outdated), maybe redundant
NpcAnimation *anim = static_cast<NpcAnimation *>(iter->second);
anim->updateParts(MWWorld::Class::get(ptr).getInventoryStore(ptr));
mAllActors.erase(iter); mAllActors.erase(iter);
mAllActors[ptr] = anim; anim->updatePtr(cur);
mAllActors[cur] = anim;
} }
} }

@ -1,17 +1,19 @@
#ifndef _GAME_RENDER_ACTORS_H #ifndef _GAME_RENDER_ACTORS_H
#define _GAME_RENDER_ACTORS_H #define _GAME_RENDER_ACTORS_H
#include "npcanimation.hpp" #include <openengine/ogre/renderer.hpp>
#include "creatureanimation.hpp"
namespace MWWorld namespace MWWorld
{ {
class Ptr; class Ptr;
class CellStore; class CellStore;
class InventoryStore;
} }
namespace MWRender namespace MWRender
{ {
class Animation;
class Actors class Actors
{ {
typedef std::map<MWWorld::CellStore*,Ogre::SceneNode*> CellSceneNodeMap; typedef std::map<MWWorld::CellStore*,Ogre::SceneNode*> CellSceneNodeMap;
@ -19,6 +21,7 @@ namespace MWRender
OEngine::Render::OgreRenderer &mRend; OEngine::Render::OgreRenderer &mRend;
Ogre::SceneNode* mMwRoot; Ogre::SceneNode* mMwRoot;
CellSceneNodeMap mCellSceneNodes; CellSceneNodeMap mCellSceneNodes;
PtrAnimationMap mAllActors; PtrAnimationMap mAllActors;
@ -27,30 +30,21 @@ namespace MWRender
~Actors(); ~Actors();
void setMwRoot(Ogre::SceneNode* root); void setMwRoot(Ogre::SceneNode* root);
void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_); void insertBegin (const MWWorld::Ptr& ptr);
void insertCreature (const MWWorld::Ptr& ptr);
void insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv); void insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv);
void insertCreature (const MWWorld::Ptr& ptr);
void insertActivator (const MWWorld::Ptr& ptr);
bool deleteObject (const MWWorld::Ptr& ptr); bool deleteObject (const MWWorld::Ptr& ptr);
///< \return found? ///< \return found?
void removeCell(MWWorld::CellStore* store); void removeCell(MWWorld::CellStore* store);
void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode,
int number = 1);
///< Run animation for a MW-reference. Calls to this function for references that are currently not
/// in the rendered scene should be ignored.
///
/// \param mode: 0 normal, 1 immediate start, 2 immediate loop
/// \param number How offen the animation should be run
void skipAnimation (const MWWorld::Ptr& ptr);
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the rendered scene should be ignored.
void update (float duration); void update (float duration);
/// Updates containing cell for object rendering data /// Updates containing cell for object rendering data
void updateObjectCell(const MWWorld::Ptr &ptr); void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur);
Animation* getAnimation(const MWWorld::Ptr &ptr);
}; };
} }
#endif #endif

@ -1,148 +1,460 @@
#include "animation.hpp" #include "animation.hpp"
#include <OgreHardwarePixelBuffer.h> #include <OgreSkeletonManager.h>
#include <OgreSkeletonInstance.h> #include <OgreSkeletonInstance.h>
#include <OgreEntity.h> #include <OgreEntity.h>
#include <OgreBone.h> #include <OgreBone.h>
#include <OgreSubMesh.h> #include <OgreSubMesh.h>
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwmechanics/character.hpp"
namespace MWRender namespace MWRender
{ {
Animation::Animation() Animation::Animation(const MWWorld::Ptr &ptr)
: mInsert(NULL) : mPtr(ptr)
, mTime(0.0f) , mController(NULL)
, mSkipFrame(false) , mInsert(NULL)
, mAccumRoot(NULL)
, mNonAccumRoot(NULL)
, mAccumulate(Ogre::Vector3::ZERO)
, mLastPosition(0.0f)
, mCurrentKeys(NULL)
, mCurrentAnim(NULL)
, mCurrentTime(0.0f)
, mStopTime(0.0f)
, mPlaying(false)
, mLooping(false)
, mAnimVelocity(0.0f)
, mAnimSpeedMult(1.0f)
{ {
} }
Animation::~Animation() Animation::~Animation()
{ {
Ogre::SceneManager *sceneMgr = mInsert->getCreator(); if(mInsert)
for(size_t i = 0;i < mEntityList.mEntities.size();i++) {
sceneMgr->destroyEntity(mEntityList.mEntities[i]); Ogre::SceneManager *sceneMgr = mInsert->getCreator();
for(size_t i = 0;i < mEntityList.mEntities.size();i++)
sceneMgr->destroyEntity(mEntityList.mEntities[i]);
}
mEntityList.mEntities.clear(); mEntityList.mEntities.clear();
mEntityList.mSkelBase = NULL;
} }
struct checklow { void Animation::setAnimationSources(const std::vector<std::string> &names)
bool operator()(const char &a, const char &b) const {
if(!mEntityList.mSkelBase)
return;
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton();
mCurrentAnim = NULL;
mCurrentKeys = NULL;
mAnimVelocity = 0.0f;
mAccumRoot = NULL;
mNonAccumRoot = NULL;
mSkeletonSources.clear();
std::vector<std::string>::const_iterator nameiter = names.begin();
for(nameiter = names.begin();nameiter != names.end();nameiter++)
{ {
return ::tolower(a) == ::tolower(b); Ogre::SkeletonPtr skel = skelMgr.getByName(*nameiter);
if(skel.isNull())
{
NifOgre::Loader::createSkeleton(*nameiter);
skel = skelMgr.getByName(*nameiter);
if(skel.isNull())
{
std::cerr<< "Failed to get skeleton source "<<*nameiter <<std::endl;
continue;
}
}
skel->touch();
Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator();
while(boneiter.hasMoreElements())
{
Ogre::Bone *bone = boneiter.getNext();
Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings();
const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID);
if(data.isEmpty() || !Ogre::any_cast<bool>(data))
continue;
if(!mNonAccumRoot)
{
mAccumRoot = mInsert;
mNonAccumRoot = mEntityList.mSkelBase->getSkeleton()->getBone(bone->getName());
}
mSkeletonSources.push_back(skel);
for(int i = 0;i < skel->getNumAnimations();i++)
{
Ogre::Animation *anim = skel->getAnimation(i);
const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+
"@"+anim->getName());
if(!groupdata.isEmpty())
mTextKeys[anim->getName()] = Ogre::any_cast<NifOgre::TextKeyMap>(groupdata);
}
break;
}
} }
}; }
bool Animation::findGroupTimes(const std::string &groupname, Animation::GroupTimes *times) void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model)
{ {
const std::string &start = groupname+": start"; mInsert = node->createChildSceneNode();
const std::string &startloop = groupname+": loop start"; assert(mInsert);
const std::string &stop = groupname+": stop";
const std::string &stoploop = groupname+": loop stop";
NifOgre::TextKeyMap::const_iterator iter; mEntityList = NifOgre::Loader::createEntities(mInsert, model);
for(iter = mTextKeys.begin();iter != mTextKeys.end();iter++) if(mEntityList.mSkelBase)
{ {
if(times->mStart >= 0.0f && times->mLoopStart >= 0.0f && times->mLoopStop >= 0.0f && times->mStop >= 0.0f) Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates();
Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator();
while(asiter.hasMoreElements())
{
Ogre::AnimationState *state = asiter.getNext();
state->setEnabled(false);
state->setLoop(false);
}
// Set the bones as manually controlled since we're applying the
// transformations manually (needed if we want to apply an animation
// from one skeleton onto another).
Ogre::SkeletonInstance *skelinst = mEntityList.mSkelBase->getSkeleton();
Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator();
while(boneiter.hasMoreElements())
boneiter.getNext()->setManuallyControlled(true);
}
}
bool Animation::hasAnimation(const std::string &anim)
{
for(std::vector<Ogre::SkeletonPtr>::const_iterator iter(mSkeletonSources.begin());iter != mSkeletonSources.end();iter++)
{
if((*iter)->hasAnimation(anim))
return true; return true;
}
return false;
}
void Animation::setController(MWMechanics::CharacterController *controller)
{
mController = controller;
}
void Animation::setAccumulation(const Ogre::Vector3 &accum)
{
mAccumulate = accum;
}
void Animation::setSpeed(float speed)
{
mAnimSpeedMult = 1.0f;
if(mAnimVelocity > 1.0f && speed > 0.0f)
mAnimSpeedMult = speed / mAnimVelocity;
}
void Animation::setLooping(bool loop)
{
mLooping = loop;
}
void Animation::updatePtr(const MWWorld::Ptr &ptr)
{
mPtr = ptr;
}
std::string::const_iterator strpos = iter->second.begin(); void Animation::calcAnimVelocity()
std::string::const_iterator strend = iter->second.end(); {
size_t strlen = strend-strpos; const Ogre::NodeAnimationTrack *track = 0;
if(start.size() <= strlen && std::mismatch(strpos, strend, start.begin(), checklow()).first == strend) Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator();
while(!track && trackiter.hasMoreElements())
{
const Ogre::NodeAnimationTrack *cur = trackiter.getNext();
if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName())
track = cur;
}
if(track && track->getNumKeyFrames() > 1)
{
float loopstarttime = 0.0f;
float loopstoptime = mCurrentAnim->getLength();
NifOgre::TextKeyMap::const_iterator keyiter = mCurrentKeys->begin();
while(keyiter != mCurrentKeys->end())
{ {
times->mStart = iter->first; if(keyiter->second == "loop start")
times->mLoopStart = iter->first; loopstarttime = keyiter->first;
else if(keyiter->second == "loop stop")
{
loopstoptime = keyiter->first;
break;
}
keyiter++;
} }
else if(startloop.size() <= strlen && std::mismatch(strpos, strend, startloop.begin(), checklow()).first == strend)
if(loopstoptime > loopstarttime)
{ {
times->mLoopStart = iter->first; Ogre::TransformKeyFrame startkf(0, loopstarttime);
Ogre::TransformKeyFrame endkf(0, loopstoptime);
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstarttime), &startkf);
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstoptime), &endkf);
mAnimVelocity = startkf.getTranslate().distance(endkf.getTranslate()) /
(loopstoptime-loopstarttime);
} }
else if(stoploop.size() <= strlen && std::mismatch(strpos, strend, stoploop.begin(), checklow()).first == strend) }
}
void Animation::applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel)
{
Ogre::TimeIndex timeindex = anim->_getTimeIndex(time);
Ogre::Animation::NodeTrackIterator tracks = anim->getNodeTrackIterator();
while(tracks.hasMoreElements())
{
Ogre::NodeAnimationTrack *track = tracks.getNext();
const Ogre::String &targetname = track->getAssociatedNode()->getName();
if(!skel->hasBone(targetname))
continue;
Ogre::Bone *bone = skel->getBone(targetname);
bone->setOrientation(Ogre::Quaternion::IDENTITY);
bone->setPosition(Ogre::Vector3::ZERO);
bone->setScale(Ogre::Vector3::UNIT_SCALE);
track->applyToNode(bone, timeindex);
}
// HACK: Dirty the animation state set so that Ogre will apply the
// transformations to entities this skeleton instance is shared with.
mEntityList.mSkelBase->getAllAnimationStates()->_notifyDirty();
}
static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone)
{
if(skelsrc->hasBone(bone->getName()))
{
Ogre::Bone *srcbone = skelsrc->getBone(bone->getName());
if(!srcbone->getParent() || !bone->getParent())
{ {
times->mLoopStop = iter->first; bone->setOrientation(srcbone->getOrientation());
bone->setPosition(srcbone->getPosition());
bone->setScale(srcbone->getScale());
} }
else if(stop.size() <= strlen && std::mismatch(strpos, strend, stop.begin(), checklow()).first == strend) else
{ {
times->mStop = iter->first; bone->_setDerivedOrientation(srcbone->_getDerivedOrientation());
if(times->mLoopStop < 0.0f) bone->_setDerivedPosition(srcbone->_getDerivedPosition());
times->mLoopStop = iter->first; bone->setScale(Ogre::Vector3::UNIT_SCALE);
break;
} }
} }
else
{
// No matching bone in the source. Make sure it stays properly offset
// from its parent.
bone->resetToInitialState();
}
Ogre::Node::ChildNodeIterator boneiter = bone->getChildIterator();
while(boneiter.hasMoreElements())
updateBoneTree(skelsrc, static_cast<Ogre::Bone*>(boneiter.getNext()));
}
return (times->mStart >= 0.0f && times->mLoopStart >= 0.0f && times->mLoopStop >= 0.0f && times->mStop >= 0.0f); void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel)
{
Ogre::Skeleton::BoneIterator boneiter = skel->getRootBoneIterator();
while(boneiter.hasMoreElements())
updateBoneTree(skelsrc, boneiter.getNext());
} }
void Animation::playGroup(std::string groupname, int mode, int loops) Ogre::Vector3 Animation::updatePosition(float time)
{ {
GroupTimes times; if(mLooping)
times.mLoops = loops; mCurrentTime = std::fmod(std::max(time, 0.0f), mCurrentAnim->getLength());
else
mCurrentTime = std::min(mCurrentAnim->getLength(), std::max(time, 0.0f));
applyAnimation(mCurrentAnim, mCurrentTime, mEntityList.mSkelBase->getSkeleton());
if(groupname == "all") Ogre::Vector3 posdiff = Ogre::Vector3::ZERO;
if(mNonAccumRoot)
{ {
times.mStart = times.mLoopStart = 0.0f; /* Get the non-accumulation root's difference from the last update. */
times.mLoopStop = times.mStop = 0.0f; posdiff = (mNonAccumRoot->getPosition() - mLastPosition) * mAccumulate;
NifOgre::TextKeyMap::const_reverse_iterator iter = mTextKeys.rbegin(); /* Translate the accumulation root back to compensate for the move. */
if(iter != mTextKeys.rend()) mLastPosition += posdiff;
times.mLoopStop = times.mStop = iter->first; mAccumRoot->setPosition(-mLastPosition);
} }
else if(!findGroupTimes(groupname, &times)) return posdiff;
throw std::runtime_error("Failed to find animation group "+groupname); }
void Animation::reset(const std::string &start, const std::string &stop)
{
mNextKey = mCurrentKeys->begin();
if(mode == 0 && mCurGroup.mLoops > 0) while(mNextKey != mCurrentKeys->end() && mNextKey->second != start)
mNextGroup = times; mNextKey++;
if(mNextKey != mCurrentKeys->end())
mCurrentTime = mNextKey->first;
else else
{ {
mCurGroup = times; mNextKey = mCurrentKeys->begin();
mNextGroup = GroupTimes(); mCurrentTime = 0.0f;
mTime = ((mode==2) ? mCurGroup.mLoopStart : mCurGroup.mStart); }
if(stop.length() > 0)
{
NifOgre::TextKeyMap::const_iterator stopKey = mNextKey;
while(stopKey != mCurrentKeys->end() && stopKey->second != stop)
stopKey++;
if(stopKey != mCurrentKeys->end())
mStopTime = stopKey->first;
else
mStopTime = mCurrentAnim->getLength();
}
if(mNonAccumRoot)
{
const Ogre::NodeAnimationTrack *track = 0;
Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator();
while(!track && trackiter.hasMoreElements())
{
const Ogre::NodeAnimationTrack *cur = trackiter.getNext();
if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName())
track = cur;
}
if(track)
{
Ogre::TransformKeyFrame kf(0, mCurrentTime);
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf);
mLastPosition = kf.getTranslate() * mAccumulate;
}
} }
} }
void Animation::skipAnim()
bool Animation::handleEvent(float time, const std::string &evt)
{ {
mSkipFrame = true; if(evt == "start" || evt == "loop start")
{
/* Do nothing */
return true;
}
if(evt.compare(0, 7, "sound: ") == 0)
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f);
return true;
}
if(evt.compare(0, 10, "soundgen: ") == 0)
{
// FIXME: Lookup the SoundGen (SNDG) for the specified sound that corresponds
// to this actor type
return true;
}
if(evt == "loop stop")
{
if(mLooping)
{
reset("loop start", "");
if(mCurrentTime >= time)
return false;
}
return true;
}
if(evt == "stop")
{
if(mLooping)
{
reset("loop start", "");
if(mCurrentTime >= time)
return false;
return true;
}
// fall-through
}
if(mController)
mController->markerEvent(time, evt);
return true;
} }
void Animation::runAnimation(float timepassed)
void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop)
{ {
if(mCurGroup.mLoops > 0 && !mSkipFrame) try {
{ bool found = false;
mTime += timepassed; /* Look in reverse; last-inserted source has priority. */
if(mTime >= mCurGroup.mLoopStop) for(std::vector<Ogre::SkeletonPtr>::const_reverse_iterator iter(mSkeletonSources.rbegin());iter != mSkeletonSources.rend();iter++)
{ {
if(mCurGroup.mLoops > 1) if((*iter)->hasAnimation(groupname))
{
mCurGroup.mLoops--;
mTime = mTime - mCurGroup.mLoopStop + mCurGroup.mLoopStart;
}
else if(mTime >= mCurGroup.mStop)
{ {
if(mNextGroup.mLoops > 0) mCurrentAnim = (*iter)->getAnimation(groupname);
mTime = mTime - mCurGroup.mStop + mNextGroup.mStart; mCurrentKeys = &mTextKeys[groupname];
else mAnimVelocity = 0.0f;
mTime = mCurGroup.mStop;
mCurGroup = mNextGroup; if(mNonAccumRoot)
mNextGroup = GroupTimes(); calcAnimVelocity();
found = true;
break;
} }
} }
if(!found)
throw std::runtime_error("Failed to find animation "+groupname);
reset(start, stop);
setLooping(loop);
mPlaying = true;
}
catch(std::exception &e) {
std::cerr<< e.what() <<std::endl;
}
}
if(mEntityList.mSkelBase) Ogre::Vector3 Animation::runAnimation(float timepassed)
{
Ogre::Vector3 movement = Ogre::Vector3::ZERO;
timepassed *= mAnimSpeedMult;
while(mCurrentAnim && mPlaying)
{
float targetTime = std::min(mStopTime, mCurrentTime+timepassed);
if(mNextKey == mCurrentKeys->end() || mNextKey->first > targetTime)
{ {
Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); movement += updatePosition(targetTime);
Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); mPlaying = (mLooping || mStopTime > targetTime);
while(as.hasMoreElements()) break;
{
Ogre::AnimationState *state = as.getNext();
state->setTimePosition(mTime);
}
} }
float time = mNextKey->first;
const std::string &evt = mNextKey->second;
mNextKey++;
movement += updatePosition(time);
mPlaying = (mLooping || mStopTime > time);
timepassed = targetTime - time;
if(!handleEvent(time, evt))
break;
} }
mSkipFrame = false;
return movement;
} }
} }

@ -1,55 +1,99 @@
#ifndef _GAME_RENDER_ANIMATION_H #ifndef _GAME_RENDER_ANIMATION_H
#define _GAME_RENDER_ANIMATION_H #define _GAME_RENDER_ANIMATION_H
#include <vector>
#include <components/nifogre/ogre_nif_loader.hpp> #include <components/nifogre/ogre_nif_loader.hpp>
#include <openengine/ogre/renderer.hpp>
#include "../mwworld/actiontalk.hpp"
#include <components/nif/node.hpp>
#include <openengine/bullet/physic.hpp>
#include "../mwworld/ptr.hpp"
namespace MWMechanics
{
class CharacterController;
}
namespace MWRender
{
class Animation
{
protected:
MWWorld::Ptr mPtr;
MWMechanics::CharacterController *mController;
Ogre::SceneNode* mInsert;
NifOgre::EntityList mEntityList;
std::map<std::string,NifOgre::TextKeyMap> mTextKeys;
Ogre::Node *mAccumRoot;
Ogre::Bone *mNonAccumRoot;
Ogre::Vector3 mAccumulate;
Ogre::Vector3 mLastPosition;
namespace MWRender { std::vector<Ogre::SkeletonPtr> mSkeletonSources;
class Animation { NifOgre::TextKeyMap *mCurrentKeys;
struct GroupTimes { NifOgre::TextKeyMap::const_iterator mNextKey;
float mStart; Ogre::Animation *mCurrentAnim;
float mStop; float mCurrentTime;
float mLoopStart; float mStopTime;
float mLoopStop; bool mPlaying;
bool mLooping;
size_t mLoops; float mAnimVelocity;
float mAnimSpeedMult;
GroupTimes() void calcAnimVelocity();
: mStart(-1.0f), mStop(-1.0f), mLoopStart(-1.0f), mLoopStop(-1.0f),
mLoops(0)
{ }
};
protected: /* Applies the given animation to the given skeleton instance, using the specified time. */
Ogre::SceneNode* mInsert; void applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel);
float mTime; /* Updates a skeleton instance so that all bones matching the source skeleton (based on
GroupTimes mCurGroup; * bone names) are positioned identically. */
GroupTimes mNextGroup; void updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel);
bool mSkipFrame; /* Updates the animation to the specified time, and returns the movement
* vector since the last update or reset. */
Ogre::Vector3 updatePosition(float time);
NifOgre::EntityList mEntityList; /* Resets the animation to the time of the specified start marker, without
NifOgre::TextKeyMap mTextKeys; * moving anything, and set the end time to the specified stop marker. If
* the marker is not found, it resets to the beginning or end respectively.
*/
void reset(const std::string &start, const std::string &stop);
bool findGroupTimes(const std::string &groupname, GroupTimes *times); bool handleEvent(float time, const std::string &evt);
/* Specifies a list of skeleton names to use as animation sources. */
void setAnimationSources(const std::vector<std::string> &names);
/* Specifies a single skeleton name to use as an animation source. */
void setAnimationSource(const std::string &name)
{
std::vector<std::string> names(1, name);
setAnimationSources(names);
}
void createEntityList(Ogre::SceneNode *node, const std::string &model);
public: public:
Animation(); Animation(const MWWorld::Ptr &ptr);
virtual ~Animation(); virtual ~Animation();
void playGroup(std::string groupname, int mode, int loops); void setController(MWMechanics::CharacterController *controller);
void skipAnim();
virtual void runAnimation(float timepassed); void updatePtr(const MWWorld::Ptr &ptr);
bool hasAnimation(const std::string &anim);
// Specifies the axis' to accumulate on. Non-accumulated axis will just
// move visually, but not affect the actual movement. Each x/y/z value
// should be on the scale of 0 to 1.
void setAccumulation(const Ogre::Vector3 &accum);
void setSpeed(float speed);
void setLooping(bool loop);
void play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop);
virtual Ogre::Vector3 runAnimation(float timepassed);
}; };
} }

@ -11,6 +11,7 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
#include "renderconst.hpp" #include "renderconst.hpp"
#include "npcanimation.hpp" #include "npcanimation.hpp"
@ -120,7 +121,8 @@ namespace MWRender
void InventoryPreview::update(int sizeX, int sizeY) void InventoryPreview::update(int sizeX, int sizeY)
{ {
mAnimation->forceUpdate (); mAnimation->forceUpdate();
mAnimation->runAnimation(0.0f);
mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024))); mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024)));
@ -143,8 +145,7 @@ namespace MWRender
{ {
mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, RV_PlayerPreview); mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, RV_PlayerPreview);
mAnimation->playGroup ("inventoryhandtohand", 0, 1); mAnimation->play("inventoryhandtohand", "start", "stop", false);
mAnimation->runAnimation (0);
} }
// -------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------

@ -8,25 +8,24 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
using namespace Ogre; namespace MWRender
using namespace NifOgre; {
namespace MWRender{
CreatureAnimation::~CreatureAnimation() CreatureAnimation::~CreatureAnimation()
{ {
} }
CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr): Animation() CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
: Animation(ptr)
{ {
mInsert = ptr.getRefData().getBaseNode(); MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
assert (ref->mBase != NULL); assert (ref->mBase != NULL);
if(!ref->mBase->mModel.empty()) if(!ref->mBase->mModel.empty())
{ {
std::string mesh = "meshes\\" + ref->mBase->mModel; std::string model = "meshes\\"+ref->mBase->mModel;
mEntityList = NifOgre::NIFLoader::createEntities(mInsert, &mTextKeys, mesh); createEntityList(mPtr.getRefData().getBaseNode(), model);
for(size_t i = 0;i < mEntityList.mEntities.size();i++) for(size_t i = 0;i < mEntityList.mEntities.size();i++)
{ {
Ogre::Entity *ent = mEntityList.mEntities[i]; Ogre::Entity *ent = mEntityList.mEntities[i];
@ -53,25 +52,12 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr): Animation()
ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
} }
if(mEntityList.mSkelBase) std::vector<std::string> names;
{ if((ref->mBase->mFlags&ESM::Creature::Biped))
Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); names.push_back("meshes\\base_anim.nif");
Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); names.push_back(model);
while(as.hasMoreElements()) setAnimationSources(names);
{
Ogre::AnimationState *state = as.getNext();
state->setEnabled(true);
state->setLoop(false);
}
}
} }
} }
void CreatureAnimation::runAnimation(float timepassed)
{
// Placeholder
Animation::runAnimation(timepassed);
}
} }

@ -3,18 +3,19 @@
#include "animation.hpp" #include "animation.hpp"
#include "components/nifogre/ogre_nif_loader.hpp" namespace MWWorld
{
class Ptr;
namespace MWRender{ }
class CreatureAnimation: public Animation namespace MWRender
{
class CreatureAnimation : public Animation
{ {
public: public:
virtual ~CreatureAnimation();
CreatureAnimation(const MWWorld::Ptr& ptr); CreatureAnimation(const MWWorld::Ptr& ptr);
virtual void runAnimation(float timepassed); virtual ~CreatureAnimation();
}; };
} }
#endif #endif

@ -8,6 +8,8 @@
#include <OgreMaterialManager.h> #include <OgreMaterialManager.h>
#include <OgreManualObject.h> #include <OgreManualObject.h>
#include <openengine/bullet/physic.hpp>
#include <components/esm/loadstat.hpp> #include <components/esm/loadstat.hpp>
#include <components/esm/loadpgrd.hpp> #include <components/esm/loadpgrd.hpp>

@ -3,7 +3,6 @@
#include <utility> #include <utility>
#include <openengine/ogre/renderer.hpp> #include <openengine/ogre/renderer.hpp>
#include <openengine/bullet/physic.hpp>
#include <vector> #include <vector>
#include <string> #include <string>
@ -13,6 +12,14 @@ namespace ESM
struct Pathgrid; struct Pathgrid;
} }
namespace OEngine
{
namespace Physic
{
class PhysicEngine;
}
}
namespace Ogre namespace Ogre
{ {
class Camera; class Camera;

@ -5,97 +5,107 @@
#include <OgreSubEntity.h> #include <OgreSubEntity.h>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "renderconst.hpp" #include "renderconst.hpp"
using namespace Ogre;
using namespace NifOgre;
namespace MWRender{ namespace MWRender
{
const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize] = {
{ ESM::PRT_Head, "Head" },
{ ESM::PRT_Hair, "Head" },
{ ESM::PRT_Neck, "Neck" },
{ ESM::PRT_Cuirass, "Chest" },
{ ESM::PRT_Groin, "Groin" },
{ ESM::PRT_Skirt, "Groin" },
{ ESM::PRT_RHand, "Right Hand" },
{ ESM::PRT_LHand, "Left Hand" },
{ ESM::PRT_RWrist, "Right Wrist" },
{ ESM::PRT_LWrist, "Left Wrist" },
{ ESM::PRT_Shield, "Shield" },
{ ESM::PRT_RForearm, "Right Forearm" },
{ ESM::PRT_LForearm, "Left Forearm" },
{ ESM::PRT_RUpperarm, "Right Upper Arm" },
{ ESM::PRT_LUpperarm, "Left Upper Arm" },
{ ESM::PRT_RFoot, "Right Foot" },
{ ESM::PRT_LFoot, "Left Foot" },
{ ESM::PRT_RAnkle, "Right Ankle" },
{ ESM::PRT_LAnkle, "Left Ankle" },
{ ESM::PRT_RKnee, "Right Knee" },
{ ESM::PRT_LKnee, "Left Knee" },
{ ESM::PRT_RLeg, "Right Upper Leg" },
{ ESM::PRT_LLeg, "Left Upper Leg" },
{ ESM::PRT_RPauldron, "Right Clavicle" },
{ ESM::PRT_LPauldron, "Left Clavicle" },
{ ESM::PRT_Weapon, "Weapon" },
{ ESM::PRT_Tail, "Tail" }
};
NpcAnimation::~NpcAnimation() NpcAnimation::~NpcAnimation()
{ {
removeEntities(mHead); for(size_t i = 0;i < sPartListSize;i++)
removeEntities(mHair); removeEntities(mEntityParts[i]);
removeEntities(mNeck);
removeEntities(mChest);
removeEntities(mGroin);
removeEntities(mSkirt);
removeEntities(mHandL);
removeEntities(mHandR);
removeEntities(mWristL);
removeEntities(mWristR);
removeEntities(mForearmL);
removeEntities(mForearmR);
removeEntities(mUpperArmL);
removeEntities(mUpperArmR);
removeEntities(mFootL);
removeEntities(mFootR);
removeEntities(mAnkleL);
removeEntities(mAnkleR);
removeEntities(mKneeL);
removeEntities(mKneeR);
removeEntities(mUpperLegL);
removeEntities(mUpperLegR);
removeEntities(mClavicleL);
removeEntities(mClavicleR);
removeEntities(mTail);
} }
NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags) NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags)
: Animation(), : Animation(ptr),
mStateID(-1), mStateID(-1),
mInv(&inv),
mTimeToChange(0), mTimeToChange(0),
mVisibilityFlags(visibilityFlags), mVisibilityFlags(visibilityFlags),
mRobe(mInv->end()), mRobe(inv.end()),
mHelmet(mInv->end()), mHelmet(inv.end()),
mShirt(mInv->end()), mShirt(inv.end()),
mCuirass(mInv->end()), mCuirass(inv.end()),
mGreaves(mInv->end()), mGreaves(inv.end()),
mPauldronL(mInv->end()), mPauldronL(inv.end()),
mPauldronR(mInv->end()), mPauldronR(inv.end()),
mBoots(mInv->end()), mBoots(inv.end()),
mPants(mInv->end()), mPants(inv.end()),
mGloveL(mInv->end()), mGloveL(inv.end()),
mGloveR(mInv->end()), mGloveR(inv.end()),
mSkirtIter(mInv->end()) mSkirtIter(inv.end())
{ {
mNpc = ptr.get<ESM::NPC>()->mBase; mNpc = mPtr.get<ESM::NPC>()->mBase;
for (int init = 0; init < 27; init++) for(size_t i = 0;i < sPartListSize;i++)
{ {
mPartslots[init] = -1; //each slot is empty mPartslots[i] = -1; //each slot is empty
mPartPriorities[init] = 0; mPartPriorities[i] = 0;
} }
const MWWorld::ESMStore &store = const MWWorld::ESMStore &store =
MWBase::Environment::get().getWorld()->getStore(); MWBase::Environment::get().getWorld()->getStore();
const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace); const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace);
float scale = race->mData.mHeight.mMale;
if(!mNpc->isMale())
scale = race->mData.mHeight.mFemale;
node->scale(Ogre::Vector3(scale));
mHeadModel = "meshes\\" + store.get<ESM::BodyPart>().find(mNpc->mHead)->mModel; mHeadModel = "meshes\\" + store.get<ESM::BodyPart>().find(mNpc->mHead)->mModel;
mHairModel = "meshes\\" + store.get<ESM::BodyPart>().find(mNpc->mHair)->mModel; mHairModel = "meshes\\" + store.get<ESM::BodyPart>().find(mNpc->mHair)->mModel;
mBodyPrefix = "b_n_" + mNpc->mRace; mBodyPrefix = "b_n_" + mNpc->mRace;
Misc::StringUtils::toLower(mBodyPrefix); Misc::StringUtils::toLower(mBodyPrefix);
mInsert = node;
assert(mInsert);
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
mEntityList = NifOgre::NIFLoader::createEntities(mInsert, &mTextKeys, smodel); createEntityList(node, smodel);
for(size_t i = 0;i < mEntityList.mEntities.size();i++) for(size_t i = 0;i < mEntityList.mEntities.size();i++)
{ {
Ogre::Entity *base = mEntityList.mEntities[i]; Ogre::Entity *base = mEntityList.mEntities[i];
base->getUserObjectBindings ().setUserAny (Ogre::Any(-1)); base->getUserObjectBindings().setUserAny(Ogre::Any(-1));
base->setVisibilityFlags(mVisibilityFlags); base->setVisibilityFlags(mVisibilityFlags);
bool transparent = false; bool transparent = false;
for(unsigned int j=0;j < base->getNumSubEntities();++j) for(unsigned int j=0;j < base->getNumSubEntities();++j)
{ {
@ -116,184 +126,137 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
} }
if(mEntityList.mSkelBase) std::vector<std::string> skelnames(1, smodel);
{ if(!mNpc->isMale() && !isBeast)
Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates(); skelnames.push_back("meshes\\base_anim_female.nif");
Ogre::AnimationStateIterator as = aset->getAnimationStateIterator(); else if(mBodyPrefix.find("argonian") != std::string::npos)
while(as.hasMoreElements()) skelnames.push_back("meshes\\argonian_swimkna.nif");
{ if(mNpc->mModel.length() > 0)
Ogre::AnimationState *state = as.getNext(); skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel));
state->setEnabled(true); setAnimationSources(skelnames);
state->setLoop(false);
}
}
float scale = race->mData.mHeight.mMale;
if (!mNpc->isMale()) {
scale = race->mData.mHeight.mFemale;
}
mInsert->scale(scale, scale, scale);
updateParts(); updateParts(true);
} }
void NpcAnimation::updateParts() void NpcAnimation::updateParts(bool forceupdate)
{ {
bool apparelChanged = false; static const struct {
int numRemoveParts; // Max: 1
ESM::PartReferenceType removeParts[1];
const struct { MWWorld::ContainerStoreIterator NpcAnimation::*part;
MWWorld::ContainerStoreIterator *iter;
int slot; int slot;
int numReserveParts; // Max: 12
ESM::PartReferenceType reserveParts[12];
} slotlist[] = { } slotlist[] = {
{ &mRobe, MWWorld::InventoryStore::Slot_Robe }, { 0, { },
{ &mSkirtIter, MWWorld::InventoryStore::Slot_Skirt }, &NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe,
{ &mHelmet, MWWorld::InventoryStore::Slot_Helmet }, 12, { ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg,
{ &mCuirass, MWWorld::InventoryStore::Slot_Cuirass }, ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee,
{ &mGreaves, MWWorld::InventoryStore::Slot_Greaves }, ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_RPauldron, ESM::PRT_LPauldron }
{ &mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron }, },
{ &mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron },
{ &mBoots, MWWorld::InventoryStore::Slot_Boots }, { 0, { },
{ &mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet }, &NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt,
{ &mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet }, 3, { ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg }
{ &mShirt, MWWorld::InventoryStore::Slot_Shirt }, },
{ &mPants, MWWorld::InventoryStore::Slot_Pants },
{ 1, { ESM::PRT_Hair },
&NpcAnimation::mHelmet, MWWorld::InventoryStore::Slot_Helmet,
0, { }
},
{ 0, { },
&NpcAnimation::mCuirass, MWWorld::InventoryStore::Slot_Cuirass,
0, { }
},
{ 0, { },
&NpcAnimation::mGreaves, MWWorld::InventoryStore::Slot_Greaves,
0, { }
},
{ 0, { },
&NpcAnimation::mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron,
0, { }
},
{ 0, { },
&NpcAnimation::mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron,
0, { }
},
{ 0, { },
&NpcAnimation::mBoots, MWWorld::InventoryStore::Slot_Boots,
0, { }
},
{ 0, { },
&NpcAnimation::mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet,
0, { }
},
{ 0, { },
&NpcAnimation::mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet,
0, { }
},
{ 0, { },
&NpcAnimation::mShirt, MWWorld::InventoryStore::Slot_Shirt,
0, { }
},
{ 0, { },
&NpcAnimation::mPants, MWWorld::InventoryStore::Slot_Pants,
0, { }
},
}; };
for(size_t i = 0;i < sizeof(slotlist)/sizeof(slotlist[0]);i++) static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]);
MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr);
for(size_t i = 0;!forceupdate && i < slotlistsize;i++)
{ {
MWWorld::ContainerStoreIterator iter = mInv->getSlot(slotlist[i].slot); MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot);
if(*slotlist[i].iter != iter) if(this->*slotlist[i].part != iter)
{ {
*slotlist[i].iter = iter; forceupdate = true;
removePartGroup(slotlist[i].slot); break;
apparelChanged = true;
} }
} }
if(!forceupdate)
return;
if(apparelChanged) for(size_t i = 0;i < slotlistsize;i++)
{ {
if(mRobe != mInv->end()) MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot);
{
MWWorld::Ptr ptr = *mRobe;
const ESM::Clothing *clothes = (ptr.get<ESM::Clothing>())->mBase;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Robe, 5, parts);
reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_Skirt, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_RUpperarm, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_LUpperarm, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_RKnee, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_LKnee, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_RForearm, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_LForearm, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_RPauldron, MWWorld::InventoryStore::Slot_Robe, 5);
reserveIndividualPart(ESM::PRT_LPauldron, MWWorld::InventoryStore::Slot_Robe, 5);
}
if(mSkirtIter != mInv->end())
{
MWWorld::Ptr ptr = *mSkirtIter;
const ESM::Clothing *clothes = (ptr.get<ESM::Clothing>())->mBase;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Skirt, 4, parts);
reserveIndividualPart(ESM::PRT_Groin, MWWorld::InventoryStore::Slot_Skirt, 4);
reserveIndividualPart(ESM::PRT_RLeg, MWWorld::InventoryStore::Slot_Skirt, 4);
reserveIndividualPart(ESM::PRT_LLeg, MWWorld::InventoryStore::Slot_Skirt, 4);
}
if(mHelmet != mInv->end()) this->*slotlist[i].part = iter;
{ removePartGroup(slotlist[i].slot);
removeIndividualPart(ESM::PRT_Hair);
const ESM::Armor *armor = (mHelmet->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Helmet, 3, parts);
}
if(mCuirass != mInv->end())
{
const ESM::Armor *armor = (mCuirass->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Cuirass, 3, parts);
}
if(mGreaves != mInv->end())
{
const ESM::Armor *armor = (mGreaves->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Greaves, 3, parts);
}
if(mPauldronL != mInv->end()) if(this->*slotlist[i].part == inv.end())
{ continue;
const ESM::Armor *armor = (mPauldronL->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_LeftPauldron, 3, parts);
}
if(mPauldronR != mInv->end())
{
const ESM::Armor *armor = (mPauldronR->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_RightPauldron, 3, parts);
}
if(mBoots != mInv->end())
{
if(mBoots->getTypeName() == typeid(ESM::Clothing).name())
{
const ESM::Clothing *clothes = (mBoots->get<ESM::Clothing>())->mBase;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Boots, 2, parts);
}
else if(mBoots->getTypeName() == typeid(ESM::Armor).name())
{
const ESM::Armor *armor = (mBoots->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Boots, 3, parts);
}
}
if(mGloveL != mInv->end())
{
if(mGloveL->getTypeName() == typeid(ESM::Clothing).name())
{
const ESM::Clothing *clothes = (mGloveL->get<ESM::Clothing>())->mBase;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 2, parts);
}
else
{
const ESM::Armor *armor = (mGloveL->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_LeftGauntlet, 3, parts);
}
}
if(mGloveR != mInv->end())
{
if(mGloveR->getTypeName() == typeid(ESM::Clothing).name())
{
const ESM::Clothing *clothes = (mGloveR->get<ESM::Clothing>())->mBase;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 2, parts);
}
else
{
const ESM::Armor *armor = (mGloveR->get<ESM::Armor>())->mBase;
std::vector<ESM::PartReference> parts = armor->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_RightGauntlet, 3, parts);
}
} for(int rem = 0;rem < slotlist[i].numRemoveParts;rem++)
removeIndividualPart(slotlist[i].removeParts[rem]);
if(mShirt != mInv->end()) int prio = 1;
MWWorld::ContainerStoreIterator &store = this->*slotlist[i].part;
if(store->getTypeName() == typeid(ESM::Clothing).name())
{ {
const ESM::Clothing *clothes = (mShirt->get<ESM::Clothing>())->mBase; prio = ((slotlist[i].numReserveParts+1)<<1) + 0;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts; const ESM::Clothing *clothes = store->get<ESM::Clothing>()->mBase;
addPartGroup(MWWorld::InventoryStore::Slot_Shirt, 2, parts); addPartGroup(slotlist[i].slot, prio, clothes->mParts.mParts);
} }
if(mPants != mInv->end()) else if(store->getTypeName() == typeid(ESM::Armor).name())
{ {
const ESM::Clothing *clothes = (mPants->get<ESM::Clothing>())->mBase; prio = ((slotlist[i].numReserveParts+1)<<1) + 1;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts; const ESM::Armor *armor = store->get<ESM::Armor>()->mBase;
addPartGroup(MWWorld::InventoryStore::Slot_Pants, 2, parts); addPartGroup(slotlist[i].slot, prio, armor->mParts.mParts);
} }
for(int res = 0;res < slotlist[i].numReserveParts;res++)
reserveIndividualPart(slotlist[i].reserveParts[res], slotlist[i].slot, prio);
} }
if(mPartPriorities[ESM::PRT_Head] < 1) if(mPartPriorities[ESM::PRT_Head] < 1)
@ -333,21 +296,18 @@ void NpcAnimation::updateParts()
if(mPartPriorities[PartTypeList[i].type] < 1) if(mPartPriorities[PartTypeList[i].type] < 1)
{ {
const ESM::BodyPart *part = NULL; const ESM::BodyPart *part = NULL;
const MWWorld::Store<ESM::BodyPart> &partStore = const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
store.get<ESM::BodyPart>();
if (!mNpc->isMale()) { if(!mNpc->isMale())
{
part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]); part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]);
if (part == 0) { if(part == 0)
part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]); part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]);
}
} }
if (part == 0) { if(part == 0)
part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]); part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]);
} if(part == 0)
if (part == 0) {
part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]); part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]);
}
if(part) if(part)
addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel); addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel);
@ -357,27 +317,51 @@ void NpcAnimation::updateParts()
NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int group, const std::string &bonename) NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int group, const std::string &bonename)
{ {
NifOgre::EntityList entities = NIFLoader::createEntities(mEntityList.mSkelBase, bonename, NifOgre::EntityList entities = NifOgre::Loader::createEntities(mEntityList.mSkelBase, bonename,
mInsert, mesh); mInsert, mesh);
std::vector<Ogre::Entity*> &parts = entities.mEntities; std::vector<Ogre::Entity*> &parts = entities.mEntities;
for(size_t i = 0;i < parts.size();i++) for(size_t i = 0;i < parts.size();i++)
{ {
parts[i]->setVisibilityFlags(mVisibilityFlags); parts[i]->setVisibilityFlags(mVisibilityFlags);
parts[i]->getUserObjectBindings ().setUserAny (Ogre::Any(group)); parts[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
}
if(entities.mSkelBase)
{
Ogre::AnimationStateSet *aset = entities.mSkelBase->getAllAnimationStates();
Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator();
while(asiter.hasMoreElements())
{
Ogre::AnimationState *state = asiter.getNext();
state->setEnabled(false);
state->setLoop(false);
}
Ogre::SkeletonInstance *skelinst = entities.mSkelBase->getSkeleton();
Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator();
while(boneiter.hasMoreElements())
boneiter.getNext()->setManuallyControlled(true);
} }
return entities; return entities;
} }
void NpcAnimation::runAnimation(float timepassed) Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
{ {
if(mTimeToChange > .2) if(mTimeToChange <= 0.0f)
{ {
mTimeToChange = 0; mTimeToChange = 0.2f;
updateParts(); updateParts();
} }
mTimeToChange += timepassed; mTimeToChange -= timepassed;
Animation::runAnimation(timepassed); Ogre::Vector3 ret = Animation::runAnimation(timepassed);
const Ogre::SkeletonInstance *skelsrc = mEntityList.mSkelBase->getSkeleton();
for(size_t i = 0;i < sPartListSize;i++)
{
Ogre::Entity *ent = mEntityParts[i].mSkelBase;
if(!ent) continue;
updateSkeletonInstance(skelsrc, ent->getSkeleton());
ent->getAllAnimationStates()->_notifyDirty();
}
return ret;
} }
void NpcAnimation::removeEntities(NifOgre::EntityList &entities) void NpcAnimation::removeEntities(NifOgre::EntityList &entities)
@ -399,62 +383,14 @@ void NpcAnimation::removeIndividualPart(int type)
mPartPriorities[type] = 0; mPartPriorities[type] = 0;
mPartslots[type] = -1; mPartslots[type] = -1;
if(type == ESM::PRT_Head) //0 for(size_t i = 0;i < sPartListSize;i++)
removeEntities(mHead);
else if(type == ESM::PRT_Hair) //1
removeEntities(mHair);
else if(type == ESM::PRT_Neck) //2
removeEntities(mNeck);
else if(type == ESM::PRT_Cuirass)//3
removeEntities(mChest);
else if(type == ESM::PRT_Groin)//4
removeEntities(mGroin);
else if(type == ESM::PRT_Skirt)//5
removeEntities(mSkirt);
else if(type == ESM::PRT_RHand)//6
removeEntities(mHandR);
else if(type == ESM::PRT_LHand)//7
removeEntities(mHandL);
else if(type == ESM::PRT_RWrist)//8
removeEntities(mWristR);
else if(type == ESM::PRT_LWrist) //9
removeEntities(mWristL);
else if(type == ESM::PRT_Shield) //10
{
}
else if(type == ESM::PRT_RForearm) //11
removeEntities(mForearmR);
else if(type == ESM::PRT_LForearm) //12
removeEntities(mForearmL);
else if(type == ESM::PRT_RUpperarm) //13
removeEntities(mUpperArmR);
else if(type == ESM::PRT_LUpperarm) //14
removeEntities(mUpperArmL);
else if(type == ESM::PRT_RFoot) //15
removeEntities(mFootR);
else if(type == ESM::PRT_LFoot) //16
removeEntities(mFootL);
else if(type == ESM::PRT_RAnkle) //17
removeEntities(mAnkleR);
else if(type == ESM::PRT_LAnkle) //18
removeEntities(mAnkleL);
else if(type == ESM::PRT_RKnee) //19
removeEntities(mKneeR);
else if(type == ESM::PRT_LKnee) //20
removeEntities(mKneeL);
else if(type == ESM::PRT_RLeg) //21
removeEntities(mUpperLegR);
else if(type == ESM::PRT_LLeg) //22
removeEntities(mUpperLegL);
else if(type == ESM::PRT_RPauldron) //23
removeEntities(mClavicleR);
else if(type == ESM::PRT_LPauldron) //24
removeEntities(mClavicleL);
else if(type == ESM::PRT_Weapon) //25
{ {
if(type == sPartList[i].type)
{
removeEntities(mEntityParts[i]);
break;
}
} }
else if(type == ESM::PRT_Tail) //26
removeEntities(mTail);
} }
void NpcAnimation::reserveIndividualPart(int type, int group, int priority) void NpcAnimation::reserveIndividualPart(int type, int group, int priority)
@ -484,96 +420,23 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority,
removeIndividualPart(type); removeIndividualPart(type);
mPartslots[type] = group; mPartslots[type] = group;
mPartPriorities[type] = priority; mPartPriorities[type] = priority;
switch(type)
for(size_t i = 0;i < sPartListSize;i++)
{ {
case ESM::PRT_Head: //0 if(type == sPartList[i].type)
mHead = insertBoundedPart(mesh, group, "Head"); {
break; mEntityParts[i] = insertBoundedPart(mesh, group, sPartList[i].name);
case ESM::PRT_Hair: //1
mHair = insertBoundedPart(mesh, group, "Head");
break;
case ESM::PRT_Neck: //2
mNeck = insertBoundedPart(mesh, group, "Neck");
break;
case ESM::PRT_Cuirass: //3
mChest = insertBoundedPart(mesh, group, "Chest");
break;
case ESM::PRT_Groin: //4
mGroin = insertBoundedPart(mesh, group, "Groin");
break;
case ESM::PRT_Skirt: //5
mSkirt = insertBoundedPart(mesh, group, "Groin");
break;
case ESM::PRT_RHand: //6
mHandR = insertBoundedPart(mesh, group, "Right Hand");
break;
case ESM::PRT_LHand: //7
mHandL = insertBoundedPart(mesh, group, "Left Hand");
break;
case ESM::PRT_RWrist: //8
mWristR = insertBoundedPart(mesh, group, "Right Wrist");
break;
case ESM::PRT_LWrist: //9
mWristL = insertBoundedPart(mesh, group, "Left Wrist");
break;
case ESM::PRT_Shield: //10
break;
case ESM::PRT_RForearm: //11
mForearmR = insertBoundedPart(mesh, group, "Right Forearm");
break;
case ESM::PRT_LForearm: //12
mForearmL = insertBoundedPart(mesh, group, "Left Forearm");
break;
case ESM::PRT_RUpperarm: //13
mUpperArmR = insertBoundedPart(mesh, group, "Right Upper Arm");
break;
case ESM::PRT_LUpperarm: //14
mUpperArmL = insertBoundedPart(mesh, group, "Left Upper Arm");
break;
case ESM::PRT_RFoot: //15
mFootR = insertBoundedPart(mesh, group, "Right Foot");
break;
case ESM::PRT_LFoot: //16
mFootL = insertBoundedPart(mesh, group, "Left Foot");
break;
case ESM::PRT_RAnkle: //17
mAnkleR = insertBoundedPart(mesh, group, "Right Ankle");
break;
case ESM::PRT_LAnkle: //18
mAnkleL = insertBoundedPart(mesh, group, "Left Ankle");
break;
case ESM::PRT_RKnee: //19
mKneeR = insertBoundedPart(mesh, group, "Right Knee");
break;
case ESM::PRT_LKnee: //20
mKneeL = insertBoundedPart(mesh, group, "Left Knee");
break;
case ESM::PRT_RLeg: //21
mUpperLegR = insertBoundedPart(mesh, group, "Right Upper Leg");
break;
case ESM::PRT_LLeg: //22
mUpperLegL = insertBoundedPart(mesh, group, "Left Upper Leg");
break;
case ESM::PRT_RPauldron: //23
mClavicleR = insertBoundedPart(mesh , group, "Right Clavicle");
break;
case ESM::PRT_LPauldron: //24
mClavicleL = insertBoundedPart(mesh, group, "Left Clavicle");
break;
case ESM::PRT_Weapon: //25
break;
case ESM::PRT_Tail: //26
mTail = insertBoundedPart(mesh, group, "Tail");
break; break;
}
} }
return true; return true;
} }
void NpcAnimation::addPartGroup(int group, int priority, std::vector<ESM::PartReference> &parts) void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts)
{ {
for(std::size_t i = 0; i < parts.size(); i++) for(std::size_t i = 0; i < parts.size(); i++)
{ {
ESM::PartReference &part = parts[i]; const ESM::PartReference &part = parts[i];
const MWWorld::Store<ESM::BodyPart> &partStore = const MWWorld::Store<ESM::BodyPart> &partStore =
MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();
@ -585,15 +448,10 @@ void NpcAnimation::addPartGroup(int group, int priority, std::vector<ESM::PartRe
bodypart = partStore.search(part.mMale); bodypart = partStore.search(part.mMale);
if(bodypart) if(bodypart)
addOrReplaceIndividualPart(part.mPart, group, priority,"meshes\\" + bodypart->mModel); addOrReplaceIndividualPart(part.mPart, group, priority, "meshes\\"+bodypart->mModel);
else else
reserveIndividualPart(part.mPart, group, priority); reserveIndividualPart(part.mPart, group, priority);
} }
} }
void NpcAnimation::forceUpdate ()
{
updateParts();
}
} }

@ -3,9 +3,6 @@
#include "animation.hpp" #include "animation.hpp"
#include "components/nifogre/ogre_nif_loader.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwclass/npc.hpp"
#include "../mwworld/containerstore.hpp" #include "../mwworld/containerstore.hpp"
namespace ESM namespace ESM
@ -13,49 +10,36 @@ namespace ESM
struct NPC; struct NPC;
} }
namespace MWRender{ namespace MWWorld
{
class InventoryStore;
}
namespace MWRender
{
class NpcAnimation : public Animation
{
public:
struct PartInfo {
ESM::PartReferenceType type;
const char name[32];
};
class NpcAnimation: public Animation{
private: private:
MWWorld::InventoryStore *mInv; static const size_t sPartListSize = 27;
static const PartInfo sPartList[sPartListSize];
int mStateID; int mStateID;
int mPartslots[27]; //Each part slot is taken by clothing, armor, or is empty // Bounded Parts
int mPartPriorities[27]; NifOgre::EntityList mEntityParts[sPartListSize];
//Bounded Parts
NifOgre::EntityList mClavicleL;
NifOgre::EntityList mClavicleR;
NifOgre::EntityList mUpperArmL;
NifOgre::EntityList mUpperArmR;
NifOgre::EntityList mUpperLegL;
NifOgre::EntityList mUpperLegR;
NifOgre::EntityList mForearmL;
NifOgre::EntityList mForearmR;
NifOgre::EntityList mWristL;
NifOgre::EntityList mWristR;
NifOgre::EntityList mKneeR;
NifOgre::EntityList mKneeL;
NifOgre::EntityList mNeck;
NifOgre::EntityList mAnkleL;
NifOgre::EntityList mAnkleR;
NifOgre::EntityList mGroin;
NifOgre::EntityList mSkirt;
NifOgre::EntityList mFootL;
NifOgre::EntityList mFootR;
NifOgre::EntityList mHair;
NifOgre::EntityList mHandL;
NifOgre::EntityList mHandR;
NifOgre::EntityList mHead;
NifOgre::EntityList mChest;
NifOgre::EntityList mTail;
const ESM::NPC *mNpc; const ESM::NPC *mNpc;
std::string mHeadModel; std::string mHeadModel;
std::string mHairModel; std::string mHairModel;
std::string mBodyPrefix; std::string mBodyPrefix;
float mTimeToChange; float mTimeToChange;
MWWorld::ContainerStoreIterator mRobe; MWWorld::ContainerStoreIterator mRobe;
MWWorld::ContainerStoreIterator mHelmet; MWWorld::ContainerStoreIterator mHelmet;
@ -72,18 +56,12 @@ private:
int mVisibilityFlags; int mVisibilityFlags;
public: int mPartslots[sPartListSize]; //Each part slot is taken by clothing, armor, or is empty
NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int mPartPriorities[sPartListSize];
MWWorld::InventoryStore& inv, int visibilityFlags);
virtual ~NpcAnimation();
NifOgre::EntityList insertBoundedPart(const std::string &mesh, int group, const std::string &bonename); NifOgre::EntityList insertBoundedPart(const std::string &mesh, int group, const std::string &bonename);
virtual void runAnimation(float timepassed);
void updateParts(); void updateParts(bool forceupdate = false);
void updateParts(MWWorld::InventoryStore &inventory) {
mInv = &inventory;
updateParts();
}
void removeEntities(NifOgre::EntityList &entities); void removeEntities(NifOgre::EntityList &entities);
void removeIndividualPart(int type); void removeIndividualPart(int type);
@ -91,10 +69,19 @@ public:
bool addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh); bool addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh);
void removePartGroup(int group); void removePartGroup(int group);
void addPartGroup(int group, int priority, std::vector<ESM::PartReference>& parts); void addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts);
void forceUpdate(); public:
NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node,
MWWorld::InventoryStore& inv, int visibilityFlags);
virtual ~NpcAnimation();
virtual Ogre::Vector3 runAnimation(float timepassed);
void forceUpdate()
{ updateParts(true); }
}; };
} }
#endif #endif

@ -34,9 +34,27 @@ void Objects::clearSceneNode (Ogre::SceneNode *node)
for (int i=node->numAttachedObjects()-1; i>=0; --i) for (int i=node->numAttachedObjects()-1; i>=0; --i)
{ {
Ogre::MovableObject *object = node->getAttachedObject (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); node->detachObject (object);
mRenderer.getScene()->destroyMovableObject (object); mRenderer.getScene()->destroyMovableObject (object);
} }
Ogre::Node::ChildNodeIterator it = node->getChildIterator ();
while (it.hasMoreElements ())
{
clearSceneNode(static_cast<Ogre::SceneNode*>(it.getNext ()));
}
} }
void Objects::setMwRoot(Ogre::SceneNode* root) void Objects::setMwRoot(Ogre::SceneNode* root)
@ -87,13 +105,13 @@ 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) void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light)
{ {
Ogre::SceneNode* insert = ptr.getRefData().getBaseNode(); Ogre::SceneNode* insert = ptr.getRefData().getBaseNode();
assert(insert); assert(insert);
Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL; Ogre::AxisAlignedBox bounds = Ogre::AxisAlignedBox::BOX_NULL;
NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(insert, NULL, mesh); NifOgre::EntityList entities = NifOgre::Loader::createEntities(insert, mesh);
for(size_t i = 0;i < entities.mEntities.size();i++) for(size_t i = 0;i < entities.mEntities.size();i++)
{ {
const Ogre::AxisAlignedBox &tmp = entities.mEntities[i]->getBoundingBox(); const Ogre::AxisAlignedBox &tmp = entities.mEntities[i]->getBoundingBox();
@ -193,26 +211,40 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
sg->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main); sg->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
for(size_t i = 0;i < entities.mEntities.size();i++) std::vector<Ogre::Entity*>::reverse_iterator iter = entities.mEntities.rbegin();
while(iter != entities.mEntities.rend())
{ {
Ogre::Entity *ent = entities.mEntities[i]; Ogre::Node *node = (*iter)->getParentNode();
insert->detachObject(ent); sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_getDerivedScale());
sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale());
mRenderer.getScene()->destroyEntity(ent); (*iter)->detachFromParent();
mRenderer.getScene()->destroyEntity(*iter);
iter++;
} }
} }
if (light)
{
insertLight(ptr, entities.mSkelBase, bounds.getCenter() - insert->_getDerivedPosition());
}
} }
void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius) void Objects::insertLight (const MWWorld::Ptr& ptr, Ogre::Entity* skelBase, Ogre::Vector3 fallbackCenter)
{ {
Ogre::SceneNode* insert = mRenderer.getScene()->getSceneNode(ptr.getRefData().getHandle()); Ogre::SceneNode* insert = mRenderer.getScene()->getSceneNode(ptr.getRefData().getHandle());
assert(insert); assert(insert);
Ogre::Light *light = mRenderer.getScene()->createLight();
light->setDiffuseColour (r, g, b);
MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>(); 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; LightInfo info;
info.name = light->getName(); info.name = light->getName();
info.radius = radius; info.radius = radius;
@ -263,7 +295,17 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, f
light->setAttenuation(r*10, 0, 0, attenuation); light->setAttenuation(r*10, 0, 0, attenuation);
} }
insert->attachObject(light); // If there's an AttachLight bone, attach the light to that, otherwise attach it to the base scene node
if (skelBase && skelBase->getSkeleton ()->hasBone ("AttachLight"))
{
skelBase->attachObjectToBone ("AttachLight", light);
}
else
{
Ogre::SceneNode* childNode = insert->createChildSceneNode (fallbackCenter);
childNode->attachObject(light);
}
mLights.push_back(info); mLights.push_back(info);
} }
@ -502,10 +544,10 @@ void Objects::rebuildStaticGeometry()
} }
} }
void Objects::updateObjectCell(const MWWorld::Ptr &ptr) void Objects::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur)
{ {
Ogre::SceneNode *node; Ogre::SceneNode *node;
MWWorld::CellStore *newCell = ptr.getCell(); MWWorld::CellStore *newCell = cur.getCell();
if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) { if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) {
node = mMwRoot->createChildSceneNode(); node = mMwRoot->createChildSceneNode();
@ -513,6 +555,6 @@ void Objects::updateObjectCell(const MWWorld::Ptr &ptr)
} else { } else {
node = mCellSceneNodes[newCell]; node = mCellSceneNodes[newCell];
} }
node->addChild(ptr.getRefData().getBaseNode()); node->addChild(cur.getRefData().getBaseNode());
} }

@ -2,6 +2,7 @@
#define _GAME_RENDER_OBJECTS_H #define _GAME_RENDER_OBJECTS_H
#include <OgreColourValue.h> #include <OgreColourValue.h>
#include <OgreAxisAlignedBox.h>
#include <openengine/ogre/renderer.hpp> #include <openengine/ogre/renderer.hpp>
@ -72,8 +73,8 @@ public:
Objects(OEngine::Render::OgreRenderer& renderer): mRenderer (renderer), mIsStatic(false) {} Objects(OEngine::Render::OgreRenderer& renderer): mRenderer (renderer), mIsStatic(false) {}
~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); void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh, bool light=false);
void insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius); 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();
@ -94,7 +95,7 @@ public:
void rebuildStaticGeometry(); void rebuildStaticGeometry();
/// Updates containing cell for object rendering data /// Updates containing cell for object rendering data
void updateObjectCell(const MWWorld::Ptr &ptr); void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur);
}; };
} }
#endif #endif

@ -129,14 +129,8 @@ namespace MWRender
MWBase::Environment::get().getWindowManager ()->showCrosshair MWBase::Environment::get().getWindowManager ()->showCrosshair
(!MWBase::Environment::get().getWindowManager ()->isGuiMode () && (mFirstPersonView && !mVanity.enabled && !mPreviewMode)); (!MWBase::Environment::get().getWindowManager ()->isGuiMode () && (mFirstPersonView && !mVanity.enabled && !mPreviewMode));
if (mAnimation) { /// \fixme We shouldn't hide the whole model, just certain components of the character (head, chest, feet, etc)
mAnimation->runAnimation(duration); mPlayerNode->setVisible(mVanity.enabled || mPreviewMode || !mFirstPersonView);
}
mPlayerNode->setVisible(
mVanity.enabled || mPreviewMode || !mFirstPersonView,
false
);
if (mFirstPersonView && !mVanity.enabled) { if (mFirstPersonView && !mVanity.enabled) {
return; return;
} }
@ -313,10 +307,7 @@ namespace MWRender
delete mAnimation; delete mAnimation;
mAnimation = anim; mAnimation = anim;
mPlayerNode->setVisible( mPlayerNode->setVisible(mVanity.enabled || mPreviewMode || !mFirstPersonView);
mVanity.enabled || mPreviewMode || !mFirstPersonView,
false
);
} }
void Player::setHeight(float height) void Player::setHeight(float height)

@ -95,7 +95,9 @@ namespace MWRender
/// Restore default camera distance for current mode. /// Restore default camera distance for current mode.
void setCameraDistance(); void setCameraDistance();
void setAnimation(MWRender::NpcAnimation *anim); void setAnimation(NpcAnimation *anim);
NpcAnimation *getAnimation() const
{ return mAnimation; }
void setHeight(float height); void setHeight(float height);
float getHeight(); float getHeight();

@ -18,9 +18,12 @@
#include <extern/shiny/Main/Factory.hpp> #include <extern/shiny/Main/Factory.hpp>
#include <extern/shiny/Platforms/Ogre/OgrePlatform.hpp> #include <extern/shiny/Platforms/Ogre/OgrePlatform.hpp>
#include <openengine/bullet/physic.hpp>
#include <components/esm/loadstat.hpp> #include <components/esm/loadstat.hpp>
#include "../mwworld/esmstore.hpp"
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -252,8 +255,7 @@ void RenderingManager::removeObject (const MWWorld::Ptr& ptr)
void RenderingManager::moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position) void RenderingManager::moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position)
{ {
/// \todo move this to the rendering-subsystems /// \todo move this to the rendering-subsystems
mRendering.getScene()->getSceneNode (ptr.getRefData().getHandle())-> ptr.getRefData().getBaseNode()->setPosition(position);
setPosition (position);
} }
void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale) void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale)
@ -300,23 +302,19 @@ bool RenderingManager::rotateObject( const MWWorld::Ptr &ptr, Ogre::Vector3 &rot
} }
void void
RenderingManager::moveObjectToCell( RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur)
const MWWorld::Ptr& ptr,
const Ogre::Vector3& pos,
MWWorld::CellStore *store)
{ {
Ogre::SceneNode *child = Ogre::SceneNode *child =
mRendering.getScene()->getSceneNode(ptr.getRefData().getHandle()); mRendering.getScene()->getSceneNode(old.getRefData().getHandle());
Ogre::SceneNode *parent = child->getParentSceneNode(); Ogre::SceneNode *parent = child->getParentSceneNode();
parent->removeChild(child); parent->removeChild(child);
if (MWWorld::Class::get(ptr).isActor()) { if (MWWorld::Class::get(old).isActor()) {
mActors.updateObjectCell(ptr); mActors.updateObjectCell(old, cur);
} else { } else {
mObjects.updateObjectCell(ptr); mObjects.updateObjectCell(old, cur);
} }
child->setPosition(pos);
} }
void RenderingManager::update (float duration, bool paused) void RenderingManager::update (float duration, bool paused)
@ -580,17 +578,6 @@ void RenderingManager::toggleLight()
setAmbientMode(); setAmbientMode();
} }
void RenderingManager::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName,
int mode, int number)
{
mActors.playAnimationGroup(ptr, groupName, mode, number);
}
void RenderingManager::skipAnimation (const MWWorld::Ptr& ptr)
{
mActors.skipAnimation(ptr);
}
void RenderingManager::setSunColour(const Ogre::ColourValue& colour) void RenderingManager::setSunColour(const Ogre::ColourValue& colour)
{ {
if (!mSunEnabled) return; if (!mSunEnabled) return;
@ -929,6 +916,15 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend
rendering.setup (mRendering.getScene()); rendering.setup (mRendering.getScene());
} }
Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr)
{
Animation *anim = mActors.getAnimation(ptr);
if(!anim && ptr.getRefData().getHandle() == "player")
anim = mPlayer->getAnimation();
return anim;
}
void RenderingManager::playVideo(const std::string& name, bool allowSkipping) void RenderingManager::playVideo(const std::string& name, bool allowSkipping)
{ {
mVideoPlayer->playVideo ("video/" + name, allowSkipping); mVideoPlayer->playVideo ("video/" + name, allowSkipping);

@ -46,6 +46,7 @@ namespace MWRender
class ExternalRendering; class ExternalRendering;
class GlobalMap; class GlobalMap;
class VideoPlayer; class VideoPlayer;
class Animation;
class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener { class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener {
@ -122,9 +123,10 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
void setWaterHeight(const float height); void setWaterHeight(const float height);
void toggleWater(); void toggleWater();
/// Moves object rendering part to proper container /// Updates object rendering after cell change
/// \param store Cell the object was in previously (\a ptr has already been updated to the new cell). /// \param old Object reference in previous cell
void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::CellStore *store); /// \param cur Object reference in new cell
void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur);
void update (float duration, bool paused); void update (float duration, bool paused);
@ -166,18 +168,6 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
/// configure fog manually /// configure fog manually
void configureFog(const float density, const Ogre::ColourValue& colour); void configureFog(const float density, const Ogre::ColourValue& colour);
void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode,
int number = 1);
///< Run animation for a MW-reference. Calls to this function for references that are currently not
/// in the rendered scene should be ignored.
///
/// \param mode: 0 normal, 1 immediate start, 2 immediate loop
/// \param number How offen the animation should be run
void skipAnimation (const MWWorld::Ptr& ptr);
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the rendered scene should be ignored.
Ogre::Vector4 boundingBoxToScreen(Ogre::AxisAlignedBox bounds); Ogre::Vector4 boundingBoxToScreen(Ogre::AxisAlignedBox bounds);
///< transform the specified bounding box (in world coordinates) into screen coordinates. ///< transform the specified bounding box (in world coordinates) into screen coordinates.
/// @return packed vector4 (min_x, min_y, max_x, max_y) /// @return packed vector4 (min_x, min_y, max_x, max_y)
@ -196,6 +186,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
void setupExternalRendering (MWRender::ExternalRendering& rendering); void setupExternalRendering (MWRender::ExternalRendering& rendering);
Animation* getAnimation(const MWWorld::Ptr &ptr);
void playVideo(const std::string& name, bool allowSkipping); void playVideo(const std::string& name, bool allowSkipping);
void stopVideo(); void stopVideo();

@ -282,7 +282,7 @@ void SkyManager::create()
// Stars // Stars
mAtmosphereNight = mRootNode->createChildSceneNode(); mAtmosphereNight = mRootNode->createChildSceneNode();
NifOgre::EntityList entities = NifOgre::NIFLoader::createEntities(mAtmosphereNight, NULL, "meshes\\sky_night_01.nif"); NifOgre::EntityList entities = NifOgre::Loader::createEntities(mAtmosphereNight, "meshes\\sky_night_01.nif");
for(size_t i = 0, matidx = 0;i < entities.mEntities.size();i++) for(size_t i = 0, matidx = 0;i < entities.mEntities.size();i++)
{ {
Entity* night1_ent = entities.mEntities[i]; Entity* night1_ent = entities.mEntities[i];
@ -307,26 +307,28 @@ void SkyManager::create()
// Atmosphere (day) // Atmosphere (day)
mAtmosphereDay = mRootNode->createChildSceneNode(); mAtmosphereDay = mRootNode->createChildSceneNode();
entities = NifOgre::NIFLoader::createEntities(mAtmosphereDay, NULL, "meshes\\sky_atmosphere.nif"); entities = NifOgre::Loader::createEntities(mAtmosphereDay, "meshes\\sky_atmosphere.nif");
for(size_t i = 0;i < entities.mEntities.size();i++) for(size_t i = 0;i < entities.mEntities.size();i++)
{ {
Entity* atmosphere_ent = entities.mEntities[i]; Entity* atmosphere_ent = entities.mEntities[i];
atmosphere_ent->setCastShadows(false); atmosphere_ent->setCastShadows(false);
atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly); atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly);
atmosphere_ent->setVisibilityFlags(RV_Sky); atmosphere_ent->setVisibilityFlags(RV_Sky);
atmosphere_ent->getSubEntity (0)->setMaterialName ("openmw_atmosphere"); for(unsigned int j = 0;j < atmosphere_ent->getNumSubEntities();j++)
atmosphere_ent->getSubEntity (j)->setMaterialName("openmw_atmosphere");
} }
// Clouds // Clouds
SceneNode* clouds_node = mRootNode->createChildSceneNode(); SceneNode* clouds_node = mRootNode->createChildSceneNode();
entities = NifOgre::NIFLoader::createEntities(clouds_node, NULL, "meshes\\sky_clouds_01.nif"); entities = NifOgre::Loader::createEntities(clouds_node, "meshes\\sky_clouds_01.nif");
for(size_t i = 0;i < entities.mEntities.size();i++) for(size_t i = 0;i < entities.mEntities.size();i++)
{ {
Entity* clouds_ent = entities.mEntities[i]; Entity* clouds_ent = entities.mEntities[i];
clouds_ent->setVisibilityFlags(RV_Sky); clouds_ent->setVisibilityFlags(RV_Sky);
clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5); clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5);
clouds_ent->getSubEntity(0)->setMaterialName ("openmw_clouds"); for(unsigned int j = 0;j < clouds_ent->getNumSubEntities();j++)
clouds_ent->getSubEntity(j)->setMaterialName("openmw_clouds");
clouds_ent->setCastShadows(false); clouds_ent->setCastShadows(false);
} }

@ -9,7 +9,7 @@
#include <components/interpreter/runtime.hpp> #include <components/interpreter/runtime.hpp>
#include <components/interpreter/opcodes.hpp> #include <components/interpreter/opcodes.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "interpretercontext.hpp" #include "interpretercontext.hpp"
#include "ref.hpp" #include "ref.hpp"
@ -27,7 +27,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
MWBase::Environment::get().getWorld()->skipAnimation (ptr); MWBase::Environment::get().getMechanicsManager()->skipAnimation (ptr);
} }
}; };
@ -54,7 +54,7 @@ namespace MWScript
throw std::runtime_error ("animation mode out of range"); throw std::runtime_error ("animation mode out of range");
} }
MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, group, mode, 1); MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, 1);
} }
}; };
@ -87,7 +87,7 @@ namespace MWScript
throw std::runtime_error ("animation mode out of range"); throw std::runtime_error ("animation mode out of range");
} }
MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, group, mode, loops); MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, loops);
} }
}; };

@ -122,6 +122,11 @@ namespace MWWorld
return 0; return 0;
} }
float Class::getJump (const Ptr& ptr) const
{
return 0;
}
MWMechanics::Movement& Class::getMovementSettings (const Ptr& ptr) const MWMechanics::Movement& Class::getMovementSettings (const Ptr& ptr) const
{ {
throw std::runtime_error ("movement settings not supported by class"); throw std::runtime_error ("movement settings not supported by class");

@ -140,6 +140,9 @@ namespace MWWorld
virtual float getSpeed (const Ptr& ptr) const; virtual float getSpeed (const Ptr& ptr) const;
///< Return movement speed. ///< Return movement speed.
virtual float getJump(const MWWorld::Ptr &ptr) const;
///< Return jump velocity (not accounting for movement)
virtual MWMechanics::Movement& getMovementSettings (const Ptr& ptr) const; virtual MWMechanics::Movement& getMovementSettings (const Ptr& ptr) const;
///< Return desired movement. ///< Return desired movement.

@ -9,6 +9,10 @@
#include <OgreCamera.h> #include <OgreCamera.h>
#include <OgreTextureManager.h> #include <OgreTextureManager.h>
#include <openengine/bullet/trace.h>
#include <openengine/bullet/physic.hpp>
#include <openengine/ogre/renderer.hpp>
#include <components/nifbullet/bullet_nif_loader.hpp> #include <components/nifbullet/bullet_nif_loader.hpp>
//#include "../mwbase/world.hpp" // FIXME //#include "../mwbase/world.hpp" // FIXME
@ -21,23 +25,191 @@ using namespace Ogre;
namespace MWWorld namespace MWWorld
{ {
PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) : static const float sMaxSlope = 60.0f;
mRender(_rend), mEngine(0), mFreeFly (true) static const float sStepSize = 30.0f;
// Arbitrary number. To prevent infinite loops. They shouldn't happen but it's good to be prepared.
static const int sMaxIterations = 50;
class MovementSolver
{ {
private:
static bool stepMove(Ogre::Vector3& position, const Ogre::Vector3 &velocity, float remainingTime,
const Ogre::Vector3 &halfExtents, bool isInterior,
OEngine::Physic::PhysicEngine *engine)
{
traceResults trace; // no initialization needed
newtrace(&trace, position, position+Ogre::Vector3(0.0f,0.0f,sStepSize),
halfExtents, isInterior, engine);
if(trace.fraction == 0.0f)
return false;
newtrace(&trace, trace.endpos, trace.endpos + velocity*remainingTime,
halfExtents, isInterior, engine);
if(trace.fraction == 0.0f || (trace.fraction != 1.0f && getSlope(trace.planenormal) > sMaxSlope))
return false;
newtrace(&trace, trace.endpos, trace.endpos-Ogre::Vector3(0.0f,0.0f,sStepSize), halfExtents, isInterior, engine);
if(getSlope(trace.planenormal) <= sMaxSlope)
{
// only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall.
position = trace.endpos;
return true;
}
return false;
}
static void clipVelocity(Ogre::Vector3& inout, const Ogre::Vector3& normal, float overbounce=1.0f)
{
//Math stuff. Basically just project the velocity vector onto the plane represented by the normal.
//More specifically, it projects velocity onto the normal, takes that result, multiplies it by overbounce and then subtracts it from velocity.
float backoff = inout.dotProduct(normal);
if(backoff < 0.0f)
backoff *= overbounce;
else
backoff /= overbounce;
inout -= normal*backoff;
}
static void projectVelocity(Ogre::Vector3& velocity, const Ogre::Vector3& direction)
{
Ogre::Vector3 normalizedDirection(direction);
normalizedDirection.normalise();
// no divide by normalizedDirection.length necessary because it's normalized
velocity = normalizedDirection * velocity.dotProduct(normalizedDirection);
}
static float getSlope(const Ogre::Vector3 &normal)
{
return normal.angleBetween(Ogre::Vector3(0.0f,0.0f,1.0f)).valueDegrees();
}
public:
static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time,
bool gravity, OEngine::Physic::PhysicEngine *engine)
{
const ESM::Position &refpos = ptr.getRefData().getPosition();
Ogre::Vector3 position(refpos.pos);
/* Anything to collide with? */
OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle());
if(!physicActor || !physicActor->getCollisionMode())
{
// FIXME: This works, but it's inconcsistent with how the rotations are applied elsewhere. Why?
return position + (Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)*
Ogre::Quaternion(Ogre::Radian( refpos.rot[1]), Ogre::Vector3::UNIT_Y)*
Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) *
movement;
}
traceResults trace; //no initialization needed
bool onground = false;
float remainingTime = time;
bool isInterior = !ptr.getCell()->isExterior();
Ogre::Vector3 halfExtents = physicActor->getHalfExtents();
Ogre::Vector3 velocity;
if(!gravity)
{
velocity = (Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)*
Ogre::Quaternion(Ogre::Radian( refpos.rot[1]), Ogre::Vector3::UNIT_Y)*
Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) *
movement / time;
}
else
{
if(!(movement.z > 0.0f))
{
newtrace(&trace, position, position-Ogre::Vector3(0,0,4), halfExtents, isInterior, engine);
if(trace.fraction < 1.0f && getSlope(trace.planenormal) <= sMaxSlope)
onground = true;
}
velocity = Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z) *
movement / time;
velocity.z += physicActor->getVerticalForce();
}
Ogre::Vector3 clippedVelocity(velocity);
if(onground)
{
// if we're on the ground, force velocity to track it
clippedVelocity.z = velocity.z = std::max(0.0f, velocity.z);
clipVelocity(clippedVelocity, trace.planenormal);
}
const Ogre::Vector3 up(0.0f, 0.0f, 1.0f);
Ogre::Vector3 newPosition = position;
int iterations = 0;
do {
// trace to where character would go if there were no obstructions
newtrace(&trace, newPosition, newPosition+clippedVelocity*remainingTime, halfExtents, isInterior, engine);
newPosition = trace.endpos;
remainingTime = remainingTime * (1.0f-trace.fraction);
// check for obstructions
if(trace.fraction < 1.0f)
{
//std::cout<<"angle: "<<getSlope(trace.planenormal)<<"\n";
if(getSlope(trace.planenormal) <= sMaxSlope)
{
// We hit a slope we can walk on. Update velocity accordingly.
clipVelocity(clippedVelocity, trace.planenormal);
// We're only on the ground if gravity is affecting us
onground = gravity;
}
else
{
// Can't walk on this. Try to step up onto it.
if((gravity && !onground) ||
!stepMove(newPosition, velocity, remainingTime, halfExtents, isInterior, engine))
{
Ogre::Vector3 resultantDirection = trace.planenormal.crossProduct(up);
resultantDirection.normalise();
clippedVelocity = velocity;
projectVelocity(clippedVelocity, resultantDirection);
// just this isn't enough sometimes. It's the same problem that causes steps to be necessary on even uphill terrain.
clippedVelocity += trace.planenormal*clippedVelocity.length()/50.0f;
}
}
}
iterations++;
} while(iterations < sMaxIterations && remainingTime > 0.0f);
if(onground)
{
newtrace(&trace, newPosition, newPosition-Ogre::Vector3(0,0,sStepSize+4.0f), halfExtents, isInterior, engine);
if(trace.fraction < 1.0f && getSlope(trace.planenormal) <= sMaxSlope)
newPosition.z = trace.endpos.z + 2.0f;
else
onground = false;
}
physicActor->setOnGround(onground);
physicActor->setVerticalForce(!onground ? clippedVelocity.z - time*627.2f : 0.0f);
return newPosition;
}
};
playerphysics = new playerMove;
PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) :
mRender(_rend), mEngine(0)
{
// Create physics. shapeLoader is deleted by the physic engine // Create physics. shapeLoader is deleted by the physic engine
NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader();
mEngine = new OEngine::Physic::PhysicEngine(shapeLoader); mEngine = new OEngine::Physic::PhysicEngine(shapeLoader);
playerphysics->mEngine = mEngine;
} }
PhysicsSystem::~PhysicsSystem() PhysicsSystem::~PhysicsSystem()
{ {
delete mEngine; delete mEngine;
delete playerphysics;
} }
OEngine::Physic::PhysicEngine* PhysicsSystem::getEngine() OEngine::Physic::PhysicEngine* PhysicsSystem::getEngine()
{ {
return mEngine; return mEngine;
@ -106,15 +278,7 @@ namespace MWWorld
void PhysicsSystem::setCurrentWater(bool hasWater, int waterHeight) void PhysicsSystem::setCurrentWater(bool hasWater, int waterHeight)
{ {
playerphysics->hasWater = hasWater; // TODO: store and use
if(hasWater){
playerphysics->waterHeight = waterHeight;
}
for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++)
{
it->second->setCurrentWater(hasWater, waterHeight);
}
} }
btVector3 PhysicsSystem::getRayPoint(float extent) btVector3 PhysicsSystem::getRayPoint(float extent)
@ -185,71 +349,11 @@ namespace MWWorld
} }
} }
void PhysicsSystem::doPhysics(float dt, const std::vector<std::pair<std::string, Ogre::Vector3> >& actors) Ogre::Vector3 PhysicsSystem::move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity)
{ {
//set the DebugRenderingMode. To disable it,set it to 0 return MovementSolver::move(ptr, movement, time, gravity, mEngine);
//eng->setDebugRenderingMode(1);
//set the movement keys to 0 (no movement) for every actor)
for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++)
{
OEngine::Physic::PhysicActor* act = it->second;
act->setMovement(0,0,0);
}
playerMove::playercmd& pm_ref = playerphysics->cmd;
pm_ref.rightmove = 0;
pm_ref.forwardmove = 0;
pm_ref.upmove = 0;
//playerphysics->ps.move_type = PM_NOCLIP;
for (std::vector<std::pair<std::string, Ogre::Vector3> >::const_iterator iter (actors.begin());
iter!=actors.end(); ++iter)
{
//dirty stuff to get the camera orientation. Must be changed!
if (iter->first == "player") {
playerphysics->ps.viewangles.x =
Ogre::Radian(mPlayerData.pitch).valueDegrees();
playerphysics->ps.viewangles.y =
Ogre::Radian(mPlayerData.yaw).valueDegrees() + 90;
pm_ref.rightmove = iter->second.x;
pm_ref.forwardmove = -iter->second.y;
pm_ref.upmove = iter->second.z;
}
}
mEngine->stepSimulation(dt);
} }
std::vector< std::pair<std::string, Ogre::Vector3> > PhysicsSystem::doPhysicsFixed (
const std::vector<std::pair<std::string, Ogre::Vector3> >& actors)
{
Pmove(playerphysics);
std::vector< std::pair<std::string, Ogre::Vector3> > response;
for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++)
{
Ogre::Vector3 coord = it->second->getPosition();
if(it->first == "player"){
coord = playerphysics->ps.origin ;
}
response.push_back(std::pair<std::string, Ogre::Vector3>(it->first, coord));
}
return response;
}
void PhysicsSystem::addHeightField (float* heights, void PhysicsSystem::addHeightField (float* heights,
int x, int y, float yoffset, int x, int y, float yoffset,
@ -291,46 +395,20 @@ namespace MWWorld
void PhysicsSystem::moveObject (const Ptr& ptr) void PhysicsSystem::moveObject (const Ptr& ptr)
{ {
Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); Ogre::SceneNode *node = ptr.getRefData().getBaseNode();
std::string handle = node->getName(); const std::string &handle = node->getName();
Ogre::Vector3 position = node->getPosition(); const Ogre::Vector3 &position = node->getPosition();
if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle)) if(OEngine::Physic::RigidBody *body = mEngine->getRigidBody(handle))
{ body->getWorldTransform().setOrigin(btVector3(position.x,position.y,position.z));
// TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow else if(OEngine::Physic::PhysicActor *physact = mEngine->getCharacter(handle))
// start positions others than 0, 0, 0 physact->setPosition(position);
if(dynamic_cast<btBoxShape*>(body->getCollisionShape()) == NULL){
btTransform tr = body->getWorldTransform();
tr.setOrigin(btVector3(position.x,position.y,position.z));
body->setWorldTransform(tr);
}
else{
//For objects that contain a box shape.
//Do any such objects exist? Perhaps animated objects?
mEngine->boxAdjustExternal(handleToMesh[handle], body, node->getScale().x, position, node->getOrientation());
}
}
if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle))
{
// TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow
// start positions others than 0, 0, 0
if (handle == "player")
{
playerphysics->ps.origin = position;
}
else
{
act->setPosition(position);
}
}
} }
void PhysicsSystem::rotateObject (const Ptr& ptr) void PhysicsSystem::rotateObject (const Ptr& ptr)
{ {
Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); Ogre::SceneNode* node = ptr.getRefData().getBaseNode();
std::string handle = node->getName(); const std::string &handle = node->getName();
Ogre::Quaternion rotation = node->getOrientation(); const Ogre::Quaternion &rotation = node->getOrientation();
if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle)) if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle))
{ {
//Needs to be changed //Needs to be changed
@ -348,7 +426,7 @@ namespace MWWorld
void PhysicsSystem::scaleObject (const Ptr& ptr) void PhysicsSystem::scaleObject (const Ptr& ptr)
{ {
Ogre::SceneNode* node = ptr.getRefData().getBaseNode(); Ogre::SceneNode* node = ptr.getRefData().getBaseNode();
std::string handle = node->getName(); const std::string &handle = node->getName();
if(handleToMesh.find(handle) != handleToMesh.end()) if(handleToMesh.find(handle) != handleToMesh.end())
{ {
removeObject(handle); removeObject(handle);
@ -361,7 +439,6 @@ namespace MWWorld
bool PhysicsSystem::toggleCollisionMode() bool PhysicsSystem::toggleCollisionMode()
{ {
playerphysics->ps.move_type = (playerphysics->ps.move_type == PM_NOCLIP ? PM_NORMAL : PM_NOCLIP);
for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++) for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = mEngine->PhysicActorMap.begin(); it != mEngine->PhysicActorMap.end();it++)
{ {
if (it->first=="player") if (it->first=="player")
@ -372,12 +449,10 @@ namespace MWWorld
if(cmode) if(cmode)
{ {
act->enableCollisions(false); act->enableCollisions(false);
mFreeFly = true;
return false; return false;
} }
else else
{ {
mFreeFly = false;
act->enableCollisions(true); act->enableCollisions(true);
return true; return true;
} }

@ -1,12 +1,27 @@
#ifndef GAME_MWWORLD_PHYSICSSYSTEM_H #ifndef GAME_MWWORLD_PHYSICSSYSTEM_H
#define GAME_MWWORLD_PHYSICSSYSTEM_H #define GAME_MWWORLD_PHYSICSSYSTEM_H
#include <openengine/ogre/renderer.hpp> #include <OgreVector3.h>
#include "ptr.hpp"
#include <openengine/bullet/pmove.h> #include <btBulletCollisionCommon.h>
namespace OEngine
{
namespace Render
{
class OgreRenderer;
}
namespace Physic
{
class PhysicEngine;
}
}
namespace MWWorld namespace MWWorld
{ {
class World;
class Ptr;
class PhysicsSystem class PhysicsSystem
{ {
@ -14,12 +29,6 @@ namespace MWWorld
PhysicsSystem (OEngine::Render::OgreRenderer &_rend); PhysicsSystem (OEngine::Render::OgreRenderer &_rend);
~PhysicsSystem (); ~PhysicsSystem ();
void doPhysics(float duration, const std::vector<std::pair<std::string, Ogre::Vector3> >& actors);
///< do physics with dt - Usage: first call doPhysics with frame dt, then call doPhysicsFixed as often as time steps have passed
std::vector< std::pair<std::string, Ogre::Vector3> > doPhysicsFixed (const std::vector<std::pair<std::string, Ogre::Vector3> >& actors);
///< do physics with fixed timestep - Usage: first call doPhysics with frame dt, then call doPhysicsFixed as often as time steps have passed
void addObject (const MWWorld::Ptr& ptr); void addObject (const MWWorld::Ptr& ptr);
void addActor (const MWWorld::Ptr& ptr); void addActor (const MWWorld::Ptr& ptr);
@ -41,6 +50,8 @@ namespace MWWorld
bool toggleCollisionMode(); bool toggleCollisionMode();
Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity);
std::pair<float, std::string> getFacedHandle (MWWorld::World& world, float queryDistance); std::pair<float, std::string> getFacedHandle (MWWorld::World& world, float queryDistance);
std::vector < std::pair <float, std::string> > getFacedHandles (float queryDistance); std::vector < std::pair <float, std::string> > getFacedHandles (float queryDistance);
std::vector < std::pair <float, std::string> > getFacedHandles (float mouseX, float mouseY, float queryDistance); std::vector < std::pair <float, std::string> > getFacedHandles (float mouseX, float mouseY, float queryDistance);
@ -74,8 +85,6 @@ namespace MWWorld
OEngine::Render::OgreRenderer &mRender; OEngine::Render::OgreRenderer &mRender;
OEngine::Physic::PhysicEngine* mEngine; OEngine::Physic::PhysicEngine* mEngine;
bool mFreeFly;
playerMove* playerphysics;
std::map<std::string, std::string> handleToMesh; std::map<std::string, std::string> handleToMesh;
PhysicsSystem (const PhysicsSystem&); PhysicsSystem (const PhysicsSystem&);

@ -71,6 +71,12 @@ namespace MWWorld
MWWorld::Class::get (ptr).getMovementSettings (ptr).mUpDown = value; MWWorld::Class::get (ptr).getMovementSettings (ptr).mUpDown = value;
} }
void Player::setRunState(bool run)
{
MWWorld::Ptr ptr = getPlayer();
MWWorld::Class::get(ptr).setStance(ptr, MWWorld::Class::Run, run);
}
void Player::toggleRunning() void Player::toggleRunning()
{ {
MWWorld::Ptr ptr = getPlayer(); MWWorld::Ptr ptr = getPlayer();

@ -65,6 +65,7 @@ namespace MWWorld
void setForwardBackward (int value); void setForwardBackward (int value);
void setUpDown(int value); void setUpDown(int value);
void setRunState(bool run);
void toggleRunning(); void toggleRunning();
}; };
} }

@ -1,5 +1,6 @@
#include "scene.hpp" #include "scene.hpp"
#include <components/nif/nif_file.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" /// FIXME #include "../mwbase/world.hpp" /// FIXME
@ -7,9 +8,11 @@
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "physicssystem.hpp"
#include "player.hpp" #include "player.hpp"
#include "localscripts.hpp" #include "localscripts.hpp"
#include "esmstore.hpp" #include "esmstore.hpp"
#include "class.hpp"
#include "cellfunctors.hpp" #include "cellfunctors.hpp"
@ -98,7 +101,7 @@ namespace MWWorld
//mPhysics->removeObject("Unnamed_43"); //mPhysics->removeObject("Unnamed_43");
MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter); MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter);
MWBase::Environment::get().getMechanicsManager()->dropActors (*iter); MWBase::Environment::get().getMechanicsManager()->drop (*iter);
MWBase::Environment::get().getSoundManager()->stopSound (*iter); MWBase::Environment::get().getSoundManager()->stopSound (*iter);
mActiveCells.erase(*iter); mActiveCells.erase(*iter);
} }
@ -164,7 +167,7 @@ namespace MWWorld
MWBase::MechanicsManager *mechMgr = MWBase::MechanicsManager *mechMgr =
MWBase::Environment::get().getMechanicsManager(); MWBase::Environment::get().getMechanicsManager();
mechMgr->addActor(player); mechMgr->add(player);
mechMgr->watchActor(player); mechMgr->watchActor(player);
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
@ -177,7 +180,7 @@ namespace MWWorld
mRendering.preCellChange(mCurrentCell); mRendering.preCellChange(mCurrentCell);
// remove active // remove active
MWBase::Environment::get().getMechanicsManager()->removeActor (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); MWBase::Environment::get().getMechanicsManager()->remove(MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
CellStoreCollection::iterator active = mActiveCells.begin(); CellStoreCollection::iterator active = mActiveCells.begin();
@ -441,7 +444,7 @@ namespace MWWorld
void Scene::removeObjectFromScene (const Ptr& ptr) void Scene::removeObjectFromScene (const Ptr& ptr)
{ {
MWBase::Environment::get().getMechanicsManager()->removeActor (ptr); MWBase::Environment::get().getMechanicsManager()->remove (ptr);
MWBase::Environment::get().getSoundManager()->stopSound3D (ptr); MWBase::Environment::get().getSoundManager()->stopSound3D (ptr);
mPhysics->removeObject (ptr.getRefData().getHandle()); mPhysics->removeObject (ptr.getRefData().getHandle());
mRendering.removeObject (ptr); mRendering.removeObject (ptr);

@ -3,7 +3,7 @@
#include "../mwrender/renderingmanager.hpp" #include "../mwrender/renderingmanager.hpp"
#include "physicssystem.hpp" #include "ptr.hpp"
#include "globals.hpp" #include "globals.hpp"
namespace Ogre namespace Ogre
@ -34,9 +34,9 @@ namespace MWRender
namespace MWWorld namespace MWWorld
{ {
class PhysicsSystem;
class Player; class Player;
class CellStore; class CellStore;
class Ptr;
class Scene class Scene
{ {

@ -1,5 +1,7 @@
#include "worldimp.hpp" #include "worldimp.hpp"
#include <libs/openengine/bullet/physic.hpp>
#include <components/bsa/bsa_archive.hpp> #include <components/bsa/bsa_archive.hpp>
#include <components/files/collections.hpp> #include <components/files/collections.hpp>
#include <components/compiler/locals.hpp> #include <components/compiler/locals.hpp>
@ -10,6 +12,8 @@
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/scriptmanager.hpp" #include "../mwbase/scriptmanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwrender/sky.hpp" #include "../mwrender/sky.hpp"
#include "../mwrender/player.hpp" #include "../mwrender/player.hpp"
@ -18,6 +22,7 @@
#include "player.hpp" #include "player.hpp"
#include "manualref.hpp" #include "manualref.hpp"
#include "cellfunctors.hpp" #include "cellfunctors.hpp"
#include "containerstore.hpp"
using namespace Ogre; using namespace Ogre;
@ -183,6 +188,8 @@ namespace MWWorld
mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine); mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine);
mPhysEngine->setSceneManager(renderer.getScene());
mWeatherManager = new MWWorld::WeatherManager(mRendering); mWeatherManager = new MWWorld::WeatherManager(mRendering);
int idx = 0; int idx = 0;
@ -732,6 +739,7 @@ namespace MWWorld
removeContainerScripts(ptr); removeContainerScripts(ptr);
if (isPlayer) if (isPlayer)
{
if (!newCell.isExterior()) if (!newCell.isExterior())
changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.mCell->mName), pos); changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.mCell->mName), pos);
else else
@ -740,6 +748,7 @@ namespace MWWorld
int cellY = newCell.mCell->getGridY(); int cellY = newCell.mCell->getGridY();
mWorldScene->changeCell(cellX, cellY, pos, false); mWorldScene->changeCell(cellX, cellY, pos, false);
} }
}
else else
{ {
if (!mWorldScene->isCellActive(*currCell)) if (!mWorldScene->isCellActive(*currCell))
@ -761,7 +770,10 @@ namespace MWWorld
MWWorld::Ptr copy = MWWorld::Ptr copy =
MWWorld::Class::get(ptr).copyToCell(ptr, newCell); MWWorld::Class::get(ptr).copyToCell(ptr, newCell);
mRendering->moveObjectToCell(copy, vec, currCell); mRendering->updateObjectCell(ptr, copy);
MWBase::MechanicsManager *mechMgr = MWBase::Environment::get().getMechanicsManager();
mechMgr->updateCell(copy);
std::string script = std::string script =
MWWorld::Class::get(ptr).getScript(ptr); MWWorld::Class::get(ptr).getScript(ptr);
@ -772,15 +784,6 @@ namespace MWWorld
mLocalScripts.add(script, copy); mLocalScripts.add(script, copy);
addContainerScripts (copy, &newCell); addContainerScripts (copy, &newCell);
} }
if (MWWorld::Class::get(ptr).isActor())
{
MWBase::MechanicsManager *mechMgr =
MWBase::Environment::get().getMechanicsManager();
mechMgr->removeActor(ptr);
mechMgr->addActor(copy);
}
} }
ptr.getRefData().setCount(0); ptr.getRefData().setCount(0);
} }
@ -875,53 +878,33 @@ namespace MWWorld
--cellY; --cellY;
} }
void World::doPhysics (const std::vector<std::pair<std::string, Ogre::Vector3> >& actors, void World::doPhysics(const PtrMovementList &actors, float duration)
float duration)
{ {
mPhysics->doPhysics(duration, actors); /* No duration? Shouldn't be any movement, then. */
if(duration <= 0.0f)
const int tick = 16; // 16 ms ^= 60 Hz return;
// Game clock part of the loop, contains everything that has to be executed in a fixed timestep PtrMovementList::const_iterator player(actors.end());
long long dt = mTimer.getMilliseconds() - lastTick; for(PtrMovementList::const_iterator iter(actors.begin());iter != actors.end();iter++)
if (dt >= 100)
{ {
// throw away wall clock time if necessary to keep the framerate above the minimum of 10 fps if(iter->first.getRefData().getHandle() == "player")
lastTick += (dt - 100);
dt = 100;
}
while (dt >= tick)
{
dt -= tick;
lastTick += tick;
std::vector< std::pair<std::string, Ogre::Vector3> > vectors = mPhysics->doPhysicsFixed (actors);
std::vector< std::pair<std::string, Ogre::Vector3> >::iterator player = vectors.end();
for (std::vector< std::pair<std::string, Ogre::Vector3> >::iterator it = vectors.begin();
it!= vectors.end(); ++it)
{ {
if (it->first=="player") /* Handle player last, in case a cell transition occurs */
{ player = iter;
player = it; continue;
}
else
{
MWWorld::Ptr ptr = getPtrViaHandle (it->first);
moveObjectImp (ptr, it->second.x, it->second.y, it->second.z);
}
}
// Make sure player is moved last (otherwise the cell might change in the middle of an update
// loop)
if (player!=vectors.end())
{
if (moveObjectImp (getPtrViaHandle (player->first),
player->second.x, player->second.y, player->second.z) == true)
return; // abort the current loop if the cell has changed
} }
Ogre::Vector3 vec = mPhysics->move(iter->first, iter->second, duration,
!isSwimming(iter->first) && !isFlying(iter->first));
moveObjectImp(iter->first, vec.x, vec.y, vec.z);
}
if(player != actors.end())
{
Ogre::Vector3 vec = mPhysics->move(player->first, player->second, duration,
!isSwimming(player->first) && !isFlying(player->first));
moveObjectImp(player->first, vec.x, vec.y, vec.z);
} }
// the only purpose this has currently is to update the debug drawer
mPhysEngine->stepSimulation (duration);
} }
bool World::toggleCollisionMode() bool World::toggleCollisionMode()
@ -989,17 +972,6 @@ namespace MWWorld
return ret; return ret;
} }
void World::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode,
int number)
{
mRendering->playAnimationGroup (ptr, groupName, mode, number);
}
void World::skipAnimation (const MWWorld::Ptr& ptr)
{
mRendering->skipAnimation (ptr);
}
void World::update (float duration, bool paused) void World::update (float duration, bool paused)
{ {
mWorldScene->update (duration, paused); mWorldScene->update (duration, paused);
@ -1420,20 +1392,30 @@ namespace MWWorld
} }
bool bool
World::isSwimming(const MWWorld::Ptr &object) World::isFlying(const MWWorld::Ptr &ptr) const
{
const MWWorld::Class &cls = MWWorld::Class::get(ptr);
if(cls.isActor() && cls.getCreatureStats(ptr).getMagicEffects().get(MWMechanics::EffectKey(10/*levitate*/)).mMagnitude > 0)
return true;
return false;
}
bool
World::isSwimming(const MWWorld::Ptr &object) const
{ {
/// \todo add check ifActor() - only actors can swim /// \todo add check ifActor() - only actors can swim
float *fpos = object.getRefData().getPosition().pos; float *fpos = object.getRefData().getPosition().pos;
Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]);
/// \fixme should rely on object height /// \fixme 3/4ths submerged?
pos.z += 30; const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(object.getRefData().getHandle());
if(actor) pos.z += actor->getHalfExtents().z * 1.5;
return isUnderwater(*object.getCell()->mCell, pos); return isUnderwater(*object.getCell()->mCell, pos);
} }
bool bool
World::isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) World::isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) const
{ {
if (!(cell.mData.mFlags & ESM::Cell::HasWater)) { if (!(cell.mData.mFlags & ESM::Cell::HasWater)) {
return false; return false;
@ -1441,6 +1423,13 @@ namespace MWWorld
return pos.z < cell.mWater; return pos.z < cell.mWater;
} }
bool World::isOnGround(const MWWorld::Ptr &ptr) const
{
RefData &refdata = ptr.getRefData();
const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle());
return physactor && physactor->getOnGround();
}
void World::renderPlayer() void World::renderPlayer()
{ {
mRendering->renderPlayer(mPlayer->getPlayer()); mRendering->renderPlayer(mPlayer->getPlayer());
@ -1455,24 +1444,21 @@ namespace MWWorld
{ {
Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); Ptr::CellStore *currentCell = mWorldScene->getCurrentCell();
Ogre::Vector3 playerPos; RefData &refdata = mPlayer->getPlayer().getRefData();
float* pos = mPlayer->getPlayer ().getRefData ().getPosition ().pos; Ogre::Vector3 playerPos(refdata.getPosition().pos);
playerPos.x = pos[0];
playerPos.y = pos[1];
playerPos.z = pos[2];
std::pair<bool, Ogre::Vector3> hit = const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle());
mPhysics->castRay(playerPos, Ogre::Vector3(0,0,-1), 50); if(!physactor->getOnGround() || isUnderwater(*currentCell->mCell, playerPos))
bool isOnGround = (hit.first ? (hit.second.distance (playerPos) < 25) : false);
if (!isOnGround || isUnderwater (*currentCell->mCell, playerPos))
return 2; return 2;
if((currentCell->mCell->mData.mFlags&ESM::Cell::NoSleep))
if (currentCell->mCell->mData.mFlags & ESM::Cell::NoSleep)
return 1; return 1;
return 0; return 0;
}
MWRender::Animation* World::getAnimation(const MWWorld::Ptr &ptr)
{
return mRendering->getAnimation(ptr);
} }
void World::playVideo (const std::string &name, bool allowSkipping) void World::playVideo (const std::string &name, bool allowSkipping)

@ -37,6 +37,7 @@ namespace MWRender
{ {
class SkyManager; class SkyManager;
class CellRender; class CellRender;
class Animation;
} }
namespace MWWorld namespace MWWorld
@ -264,8 +265,7 @@ namespace MWWorld
virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const; virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const;
///< Convert position to cell numbers ///< Convert position to cell numbers
virtual void doPhysics (const std::vector<std::pair<std::string, Ogre::Vector3> >& actors, virtual void doPhysics(const PtrMovementList &actors, float duration);
float duration);
///< Run physics simulation and modify \a world accordingly. ///< Run physics simulation and modify \a world accordingly.
virtual bool toggleCollisionMode(); virtual bool toggleCollisionMode();
@ -298,18 +298,6 @@ namespace MWWorld
/// \return pointer to created record /// \return pointer to created record
virtual void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName,
int mode, int number = 1);
///< Run animation for a MW-reference. Calls to this function for references that are
/// currently not in the rendered scene should be ignored.
///
/// \param mode: 0 normal, 1 immediate start, 2 immediate loop
/// \param number How offen the animation should be run
virtual void skipAnimation (const MWWorld::Ptr& ptr);
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the rendered scene should be ignored.
virtual void update (float duration, bool paused); virtual void update (float duration, bool paused);
virtual bool placeObject (const Ptr& object, float cursorX, float cursorY); virtual bool placeObject (const Ptr& object, float cursorX, float cursorY);
@ -326,8 +314,10 @@ namespace MWWorld
virtual void processChangedSettings(const Settings::CategorySettingVector& settings); virtual void processChangedSettings(const Settings::CategorySettingVector& settings);
virtual bool isSwimming(const MWWorld::Ptr &object); virtual bool isFlying(const MWWorld::Ptr &ptr) const;
virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos); virtual bool isSwimming(const MWWorld::Ptr &object) const;
virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) const;
virtual bool isOnGround(const MWWorld::Ptr &ptr) const;
virtual void togglePOV() { virtual void togglePOV() {
mRendering->togglePOV(); mRendering->togglePOV();
@ -360,6 +350,9 @@ namespace MWWorld
/// 2 - player is underwater \n /// 2 - player is underwater \n
/// 3 - enemies are nearby (not implemented) /// 3 - enemies are nearby (not implemented)
/// \todo Probably shouldn't be here
virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr);
/// \todo this does not belong here /// \todo this does not belong here
virtual void playVideo(const std::string& name, bool allowSkipping); virtual void playVideo(const std::string& name, bool allowSkipping);
virtual void stopVideo(); virtual void stopVideo();

@ -3,17 +3,17 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <list>
#include "esmcommon.hpp" #include "esmcommon.hpp"
#include "defs.hpp" #include "defs.hpp"
#include "apps/openmw/mwbase/world.hpp"
/*
namespace MWWorld { namespace MWWorld
class ESMStore; {
class CellStore; class ESMStore;
} }
*/
namespace ESM namespace ESM
{ {

@ -21,8 +21,6 @@
*/ */
//loadResource->handleNode->handleNiTriShape->createSubMesh
#include "ogre_nif_loader.hpp" #include "ogre_nif_loader.hpp"
#include <algorithm> #include <algorithm>
@ -35,6 +33,7 @@
#include <OgreSubMesh.h> #include <OgreSubMesh.h>
#include <OgreRoot.h> #include <OgreRoot.h>
#include <OgreEntity.h> #include <OgreEntity.h>
#include <OgreSubEntity.h>
#include <OgreTagPoint.h> #include <OgreTagPoint.h>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
@ -53,9 +52,6 @@ typedef unsigned char ubyte;
namespace std namespace std
{ {
// These operators allow extra data types to be stored in an Ogre::Any
// object, which can then be stored in user object bindings on the nodes
// TODO: Do something useful // TODO: Do something useful
ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&) ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&)
{ return o; } { return o; }
@ -149,8 +145,12 @@ public:
}; };
class NIFSkeletonLoader : public Ogre::ManualResourceLoader { /** Manual resource loader for NIF skeletons. This is the main class
responsible for translating the internal NIF skeleton structure into
something Ogre can use (includes animations and node TextKeyData).
*/
class NIFSkeletonLoader : public Ogre::ManualResourceLoader
{
static void warn(const std::string &msg) static void warn(const std::string &msg)
{ {
std::cerr << "NIFSkeletonLoader: Warn: " << msg << std::endl; std::cerr << "NIFSkeletonLoader: Warn: " << msg << std::endl;
@ -163,6 +163,118 @@ static void fail(const std::string &msg)
} }
static void buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector<const Nif::NiKeyframeController*> &ctrls, const std::vector<std::string> &targets, float startTime, float stopTime)
{
Ogre::Animation *anim = skel->createAnimation(name, stopTime-startTime);
for(size_t i = 0;i < ctrls.size();i++)
{
const Nif::NiKeyframeController *kfc = ctrls[i];
if(kfc->data.empty())
continue;
const Nif::NiKeyframeData *kf = kfc->data.getPtr();
/* Get the keyframes and make sure they're sorted first to last */
const Nif::QuaternionKeyList &quatkeys = kf->mRotations;
const Nif::Vector3KeyList &trankeys = kf->mTranslations;
const Nif::FloatKeyList &scalekeys = kf->mScales;
Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin();
Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin();
Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin();
Ogre::Bone *bone = skel->getBone(targets[i]);
// NOTE: For some reason, Ogre doesn't like the node track ID being different from
// the bone ID
Ogre::NodeAnimationTrack *nodetrack = anim->hasNodeTrack(bone->getHandle()) ?
anim->getNodeTrack(bone->getHandle()) :
anim->createNodeTrack(bone->getHandle(), bone);
Ogre::Quaternion lastquat, curquat;
Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f);
Ogre::Vector3 lastscale(1.0f), curscale(1.0f);
if(quatiter != quatkeys.mKeys.end())
lastquat = curquat = quatiter->mValue;
if(traniter != trankeys.mKeys.end())
lasttrans = curtrans = traniter->mValue;
if(scaleiter != scalekeys.mKeys.end())
lastscale = curscale = Ogre::Vector3(scaleiter->mValue);
float begTime = std::max(kfc->timeStart, startTime);
float endTime = std::min(kfc->timeStop, stopTime);
bool didlast = false;
while(!didlast)
{
float curtime = std::numeric_limits<float>::max();
//Get latest time
if(quatiter != quatkeys.mKeys.end())
curtime = std::min(curtime, quatiter->mTime);
if(traniter != trankeys.mKeys.end())
curtime = std::min(curtime, traniter->mTime);
if(scaleiter != scalekeys.mKeys.end())
curtime = std::min(curtime, scaleiter->mTime);
curtime = std::max(curtime, begTime);
if(curtime >= endTime)
{
didlast = true;
curtime = endTime;
}
// Get the latest quaternions, translations, and scales for the
// current time
while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime)
{
lastquat = curquat;
if(++quatiter != quatkeys.mKeys.end())
curquat = quatiter->mValue;
}
while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime)
{
lasttrans = curtrans;
if(++traniter != trankeys.mKeys.end())
curtrans = traniter->mValue;
}
while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime)
{
lastscale = curscale;
if(++scaleiter != scalekeys.mKeys.end())
curscale = Ogre::Vector3(scaleiter->mValue);
}
Ogre::TransformKeyFrame *kframe;
kframe = nodetrack->createNodeKeyFrame(curtime-startTime);
if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin())
kframe->setRotation(curquat);
else
{
Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1;
float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime);
kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat));
}
if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin())
kframe->setTranslate(curtrans);
else
{
Nif::Vector3KeyList::VecType::const_iterator last = traniter-1;
float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime);
kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff));
}
if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin())
kframe->setScale(curscale);
else
{
Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1;
float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime);
kframe->setScale(lastscale + ((curscale-lastscale)*diff));
}
}
}
anim->optimise();
}
static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk) static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk)
{ {
TextKeyMap textkeys; TextKeyMap textkeys;
@ -172,13 +284,15 @@ static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk)
std::string::size_type pos = 0; std::string::size_type pos = 0;
while(pos < str.length()) while(pos < str.length())
{ {
while(pos < str.length() && ::isspace(str[pos])) if(::isspace(str[pos]))
{
pos++; pos++;
if(pos >= str.length()) continue;
break; }
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos));
textkeys.insert(std::make_pair(tk->list[i].time, str.substr(pos, nextpos-pos))); std::string result = str.substr(pos, nextpos-pos);
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
pos = nextpos; pos = nextpos;
} }
@ -187,7 +301,7 @@ static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk)
} }
void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent=NULL) void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent=NULL)
{ {
Ogre::Bone *bone; Ogre::Bone *bone;
if(!skel->hasBone(node->name)) if(!skel->hasBone(node->name))
@ -200,23 +314,31 @@ void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vector<Nif::Ni
bone->setPosition(node->trafo.pos); bone->setPosition(node->trafo.pos);
bone->setScale(Ogre::Vector3(node->trafo.scale)); bone->setScale(Ogre::Vector3(node->trafo.scale));
bone->setBindingPose(); bone->setBindingPose();
bone->setInitialState();
if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */
node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */
node->recType == Nif::RC_NiTriShape /* Handled in the mesh loader */
))
warn("Unhandled "+node->recName+" "+node->name+" in "+skel->getName());
Nif::ControllerPtr ctrl = node->controller; Nif::ControllerPtr ctrl = node->controller;
while(!ctrl.empty()) while(!ctrl.empty())
{ {
if(ctrl->recType == Nif::RC_NiKeyframeController) if(ctrl->recType == Nif::RC_NiKeyframeController)
ctrls.push_back(static_cast<Nif::NiKeyframeController*>(ctrl.getPtr())); ctrls.push_back(static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr()));
else
warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName());
ctrl = ctrl->next; ctrl = ctrl->next;
} }
Nif::ExtraPtr e = node->extra; Nif::ExtraPtr e = node->extra;
while(!e.empty()) while(!e.empty())
{ {
if(e->recType == Nif::RC_NiTextKeyExtraData) if(e->recType == Nif::RC_NiTextKeyExtraData && !animroot)
{ {
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr()); const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
bone->getUserObjectBindings().setUserAny("TextKeyExtraData", Ogre::Any(extractTextKeys(tk))); textkeys = extractTextKeys(tk);
animroot = bone;
} }
e = e->extra; e = e->extra;
} }
@ -228,21 +350,12 @@ void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vector<Nif::Ni
for(size_t i = 0;i < children.length();i++) for(size_t i = 0;i < children.length();i++)
{ {
if(!children[i].empty()) if(!children[i].empty())
buildBones(skel, children[i].getPtr(), ctrls, bone); buildBones(skel, children[i].getPtr(), animroot, textkeys, ctrls, bone);
} }
} }
} }
/* Comparitor to help sort Key<> vectors */
template<class T>
struct KeyTimeSort
{
bool operator()(const Nif::KeyT<T> &lhs, const Nif::KeyT<T> &rhs) const
{ return lhs.mTime < rhs.mTime; }
};
typedef std::map<std::string,NIFSkeletonLoader> LoaderMap; typedef std::map<std::string,NIFSkeletonLoader> LoaderMap;
static LoaderMap sLoaders; static LoaderMap sLoaders;
@ -252,12 +365,20 @@ void loadResource(Ogre::Resource *resource)
Ogre::Skeleton *skel = dynamic_cast<Ogre::Skeleton*>(resource); Ogre::Skeleton *skel = dynamic_cast<Ogre::Skeleton*>(resource);
OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!"); OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!");
Nif::NIFFile::ptr pnif(Nif::NIFFile::create (skel->getName())); Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName()));
Nif::NIFFile & nif = *pnif.get (); const Nif::Node *node = static_cast<const Nif::Node*>(nif->getRecord(0));
const Nif::Node *node = dynamic_cast<const Nif::Node*>(nif.getRecord(0));
std::vector<Nif::NiKeyframeController const*> ctrls; std::vector<const Nif::NiKeyframeController*> ctrls;
buildBones(skel, node, ctrls); Ogre::Bone *animroot = NULL;
TextKeyMap textkeys;
try {
buildBones(skel, node, animroot, textkeys, ctrls);
}
catch(std::exception &e) {
std::cerr<< "Exception while loading "<<skel->getName() <<std::endl;
std::cerr<< e.what() <<std::endl;
return;
}
std::vector<std::string> targets; std::vector<std::string> targets;
// TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file // TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file
@ -267,7 +388,7 @@ void loadResource(Ogre::Resource *resource)
float maxtime = 0.0f; float maxtime = 0.0f;
for(size_t i = 0;i < ctrls.size();i++) for(size_t i = 0;i < ctrls.size();i++)
{ {
Nif::NiKeyframeController const *ctrl = ctrls[i]; const Nif::NiKeyframeController *ctrl = ctrls[i];
maxtime = std::max(maxtime, ctrl->timeStop); maxtime = std::max(maxtime, ctrl->timeStop);
Nif::Named *target = dynamic_cast<Nif::Named*>(ctrl->target.getPtr()); Nif::Named *target = dynamic_cast<Nif::Named*>(ctrl->target.getPtr());
if(target != NULL) if(target != NULL)
@ -281,149 +402,90 @@ void loadResource(Ogre::Resource *resource)
return; return;
} }
Ogre::Animation *anim = skel->createAnimation(skel->getName(), maxtime); if(!animroot)
/* HACK: Pre-create the node tracks by matching the track IDs with the
* bone IDs. Otherwise, Ogre animates the wrong bones. */
size_t bonecount = skel->getNumBones();
for(size_t i = 0;i < bonecount;i++)
anim->createNodeTrack(i, skel->getBone(i));
for(size_t i = 0;i < ctrls.size();i++)
{ {
Nif::NiKeyframeController const *kfc = ctrls[i]; warn(Ogre::StringConverter::toString(ctrls.size())+" animated node(s) in "+
Nif::NiKeyframeData const *kf = kfc->data.getPtr(); skel->getName()+", but no text keys.");
return;
/* Get the keyframes and make sure they're sorted first to last */ }
Nif::QuaternionKeyList quatkeys = kf->mRotations;
Nif::Vector3KeyList trankeys = kf->mTranslations;
Nif::FloatKeyList scalekeys = kf->mScales;
std::sort(quatkeys.mKeys.begin(), quatkeys.mKeys.end(), KeyTimeSort<Ogre::Quaternion>());
std::sort(trankeys.mKeys.begin(), trankeys.mKeys.end(), KeyTimeSort<Ogre::Vector3>());
std::sort(scalekeys.mKeys.begin(), scalekeys.mKeys.end(), KeyTimeSort<float>());
Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin();
Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin();
Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin();
Ogre::Bone *bone = skel->getBone(targets[i]); Ogre::UserObjectBindings &bindings = animroot->getUserObjectBindings();
const Ogre::Quaternion startquat = bone->getInitialOrientation(); bindings.setUserAny(sTextKeyExtraDataID, Ogre::Any(true));
const Ogre::Vector3 starttrans = bone->getInitialPosition();
const Ogre::Vector3 startscale = bone->getInitialScale();
Ogre::NodeAnimationTrack *nodetrack = anim->getNodeTrack(bone->getHandle());
Ogre::Quaternion lastquat, curquat; std::string currentgroup;
Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f); TextKeyMap::const_iterator keyiter = textkeys.begin();
Ogre::Vector3 lastscale(1.0f), curscale(1.0f); for(keyiter = textkeys.begin();keyiter != textkeys.end();keyiter++)
if(quatiter != quatkeys.mKeys.end()) {
lastquat = curquat = startquat.Inverse() * quatiter->mValue; std::string::size_type sep = keyiter->second.find(':');
if(traniter != trankeys.mKeys.end()) if((sep == currentgroup.length() && keyiter->second.compare(0, sep, currentgroup) == 0) ||
lasttrans = curtrans = traniter->mValue - starttrans; (sep == sizeof("soundgen")-1 && keyiter->second.compare(0, sep, "soundgen") == 0) ||
if(scaleiter != scalekeys.mKeys.end()) (sep == sizeof("sound")-1 && keyiter->second.compare(0, sep, "sound") == 0))
lastscale = curscale = Ogre::Vector3(scaleiter->mValue) / startscale; continue;
bool didlast = false; currentgroup = keyiter->second.substr(0, sep);
while(!didlast) if(skel->hasAnimation(currentgroup))
continue;
TextKeyMap::const_iterator lastkeyiter = textkeys.end();
while((--lastkeyiter)->first > keyiter->first)
{ {
float curtime = kfc->timeStop; if(lastkeyiter->second.find(':') == currentgroup.length() &&
lastkeyiter->second.compare(0, currentgroup.length(), currentgroup) == 0)
//Get latest time break;
if(quatiter != quatkeys.mKeys.end())
curtime = std::min(curtime, quatiter->mTime);
if(traniter != trankeys.mKeys.end())
curtime = std::min(curtime, traniter->mTime);
if(scaleiter != scalekeys.mKeys.end())
curtime = std::min(curtime, scaleiter->mTime);
curtime = std::max(curtime, kfc->timeStart);
if(curtime >= kfc->timeStop)
{
didlast = true;
curtime = kfc->timeStop;
}
// Get the latest quaternions, translations, and scales for the
// current time
while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime)
{
lastquat = curquat;
quatiter++;
if(quatiter != quatkeys.mKeys.end())
curquat = startquat.Inverse() * quatiter->mValue ;
}
while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime)
{
lasttrans = curtrans;
traniter++;
if(traniter != trankeys.mKeys.end())
curtrans = traniter->mValue - starttrans;
}
while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime)
{
lastscale = curscale;
scaleiter++;
if(scaleiter != scalekeys.mKeys.end())
curscale = Ogre::Vector3(scaleiter->mValue) / startscale;
}
Ogre::TransformKeyFrame *kframe;
kframe = nodetrack->createNodeKeyFrame(curtime);
if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin())
kframe->setRotation(curquat);
else
{
Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1;
float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime);
kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat));
}
if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin())
kframe->setTranslate(curtrans);
else
{
Nif::Vector3KeyList::VecType::const_iterator last = traniter-1;
float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime);
kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff));
}
if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin())
kframe->setScale(curscale);
else
{
Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1;
float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime);
kframe->setScale(lastscale + ((curscale-lastscale)*diff));
}
} }
buildAnimation(skel, currentgroup, ctrls, targets, keyiter->first, lastkeyiter->first);
TextKeyMap::const_iterator insiter = keyiter;
TextKeyMap groupkeys;
do {
sep = insiter->second.find(':');
if(sep == currentgroup.length() && insiter->second.compare(0, sep, currentgroup) == 0)
groupkeys.insert(std::make_pair(insiter->first - keyiter->first,
insiter->second.substr(sep+2)));
else if((sep == sizeof("soundgen")-1 && insiter->second.compare(0, sep, "soundgen") == 0) ||
(sep == sizeof("sound")-1 && insiter->second.compare(0, sep, "sound") == 0))
groupkeys.insert(std::make_pair(insiter->first - keyiter->first, insiter->second));
} while(insiter++ != lastkeyiter);
bindings.setUserAny(std::string(sTextKeyExtraDataID)+"@"+currentgroup, Ogre::Any(groupkeys));
} }
anim->optimise();
} }
bool createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node) bool createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node)
{ {
if(node->boneTrafo != NULL) /* We need to be a little aggressive here, since some NIFs have a crap-ton
* of nodes and Ogre only supports 256 bones. We will skip a skeleton if:
* There are no bones used for skinning, there are no controllers on non-
* NiTriShape nodes, there are no nodes named "AttachLight", and the tree
* consists of NiNode, NiTriShape, and RootCollisionNode types only.
*/
if(!node->boneTrafo)
{ {
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); if(node->recType == Nif::RC_NiTriShape)
Ogre::SkeletonPtr skel = skelMgr.getByName(name); return false;
if(skel.isNull()) if(node->controller.empty() && node->name != "AttachLight")
{
NIFSkeletonLoader *loader = &sLoaders[name];
skel = skelMgr.create(name, group, true, loader);
}
return true;
}
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
if(ninode)
{
const Nif::NodeList &children = ninode->children;
for(size_t i = 0;i < children.length();i++)
{ {
if(!children[i].empty()) if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode)
{ {
if(createSkeleton(name, group, children[i].getPtr())) const Nif::NiNode *ninode = static_cast<const Nif::NiNode*>(node);
return true; const Nif::NodeList &children = ninode->children;
for(size_t i = 0;i < children.length();i++)
{
if(!children[i].empty())
{
if(createSkeleton(name, group, children[i].getPtr()))
return true;
}
}
return false;
} }
} }
} }
return false;
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton();
skelMgr.create(name, group, true, &sLoaders[name]);
return true;
} }
}; };
@ -691,12 +753,15 @@ static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String
std::map<size_t,std::string> NIFMaterialLoader::MaterialMap; std::map<size_t,std::string> NIFMaterialLoader::MaterialMap;
/** Manual resource loader for NIF meshes. This is the main class
responsible for translating the internal NIF mesh structure into
something Ogre can use.
*/
class NIFMeshLoader : Ogre::ManualResourceLoader class NIFMeshLoader : Ogre::ManualResourceLoader
{ {
std::string mName; std::string mName;
std::string mGroup; std::string mGroup;
size_t mShapeIndex; size_t mShapeIndex;
std::string mSkelName;
std::string mMaterialName; std::string mMaterialName;
std::string mShapeName; std::string mShapeName;
@ -724,12 +789,11 @@ class NIFMeshLoader : Ogre::ManualResourceLoader
{ {
// Only set a skeleton when skinning. Unskinned meshes with a skeleton will be // Only set a skeleton when skinning. Unskinned meshes with a skeleton will be
// explicitly attached later. // explicitly attached later.
mesh->setSkeletonName(mSkelName); mesh->setSkeletonName(mName);
// Get the skeleton resource, so vertices can be transformed into the bones' initial state. // Get the skeleton resource, so vertices can be transformed into the bones' initial state.
Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr(); Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
skel = skelMgr->getByName(mSkelName); skel = skelMgr->getByName(mName);
skel->touch();
// Convert vertices and normals to bone space from bind position. It would be // Convert vertices and normals to bone space from bind position. It would be
// better to transform the bones into bind position, but there doesn't seem to // better to transform the bones into bind position, but there doesn't seem to
@ -742,12 +806,10 @@ class NIFMeshLoader : Ogre::ManualResourceLoader
for(size_t b = 0;b < bones.length();b++) for(size_t b = 0;b < bones.length();b++)
{ {
Ogre::Bone *bone = skel->getBone(bones[b]->name); Ogre::Bone *bone = skel->getBone(bones[b]->name);
Ogre::Matrix4 mat, mat2; Ogre::Matrix4 mat;
mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale),
Ogre::Quaternion(data->bones[b].trafo.rotation)); Ogre::Quaternion(data->bones[b].trafo.rotation));
mat2.makeTransform(bone->_getDerivedPosition(), bone->_getDerivedScale(), mat = bone->_getFullTransform() * mat;
bone->_getDerivedOrientation());
mat = mat2 * mat;
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights; const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights;
for(size_t i = 0;i < weights.size();i++) for(size_t i = 0;i < weights.size();i++)
@ -768,22 +830,26 @@ class NIFMeshLoader : Ogre::ManualResourceLoader
srcVerts = newVerts; srcVerts = newVerts;
srcNorms = newNorms; srcNorms = newNorms;
} }
else if(mSkelName.length() == 0) else
{ {
// No skinning and no skeleton, so just transform the vertices and Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
// normals into position. if(skelMgr->getByName(mName).isNull())
Ogre::Matrix4 mat4 = shape->getWorldTransform();
for(size_t i = 0;i < srcVerts.size();i++)
{
Ogre::Vector4 vec4(srcVerts[i].x, srcVerts[i].y, srcVerts[i].z, 1.0f);
vec4 = mat4*vec4;
srcVerts[i] = Ogre::Vector3(&vec4[0]);
}
for(size_t i = 0;i < srcNorms.size();i++)
{ {
Ogre::Vector4 vec4(srcNorms[i].x, srcNorms[i].y, srcNorms[i].z, 0.0f); // No skinning and no skeleton, so just transform the vertices and
vec4 = mat4*vec4; // normals into position.
srcNorms[i] = Ogre::Vector3(&vec4[0]); Ogre::Matrix4 mat4 = shape->getWorldTransform();
for(size_t i = 0;i < srcVerts.size();i++)
{
Ogre::Vector4 vec4(srcVerts[i].x, srcVerts[i].y, srcVerts[i].z, 1.0f);
vec4 = mat4*vec4;
srcVerts[i] = Ogre::Vector3(&vec4[0]);
}
for(size_t i = 0;i < srcNorms.size();i++)
{
Ogre::Vector4 vec4(srcNorms[i].x, srcNorms[i].y, srcNorms[i].z, 0.0f);
vec4 = mat4*vec4;
srcNorms[i] = Ogre::Vector3(&vec4[0]);
}
} }
} }
@ -943,19 +1009,21 @@ class NIFMeshLoader : Ogre::ManualResourceLoader
public: public:
NIFMeshLoader() NIFMeshLoader()
{ } { }
NIFMeshLoader(const std::string &name, const std::string &group, const std::string skelName) NIFMeshLoader(const std::string &name, const std::string &group)
: mName(name), mGroup(group), mShapeIndex(~(size_t)0), mSkelName(skelName) : mName(name), mGroup(group), mShapeIndex(~(size_t)0)
{ } { }
virtual void loadResource(Ogre::Resource *resource) virtual void loadResource(Ogre::Resource *resource)
{ {
Ogre::Mesh *mesh = dynamic_cast<Ogre::Mesh*>(resource); Ogre::Mesh *mesh = dynamic_cast<Ogre::Mesh*>(resource);
assert(mesh && "Attempting to load a mesh into a non-mesh resource!"); OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!");
Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName); Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName);
if(mShapeIndex >= nif->numRecords()) if(mShapeIndex >= nif->numRecords())
{ {
mesh->setSkeletonName(mSkelName); Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
if(!skelMgr->getByName(mName).isNull())
mesh->setSkeletonName(mName);
return; return;
} }
@ -963,7 +1031,7 @@ public:
findTriShape(mesh, node); findTriShape(mesh, node);
} }
void createMeshes(const Nif::Node *node, MeshPairList &meshes, int flags=0) void createMeshes(const Nif::Node *node, MeshInfoList &meshes, int flags=0)
{ {
flags |= node->flags; flags |= node->flags;
@ -976,7 +1044,6 @@ public:
while(!e.empty()) while(!e.empty())
{ {
Nif::NiStringExtraData *sd; Nif::NiStringExtraData *sd;
Nif::NiTextKeyExtraData *td;
if((sd=dynamic_cast<Nif::NiStringExtraData*>(e.getPtr())) != NULL) if((sd=dynamic_cast<Nif::NiStringExtraData*>(e.getPtr())) != NULL)
{ {
// String markers may contain important information // String markers may contain important information
@ -988,16 +1055,10 @@ public:
flags |= 0x01; flags |= 0x01;
} }
} }
else if((td=dynamic_cast<Nif::NiTextKeyExtraData*>(e.getPtr())) != NULL)
{
// TODO: Read and store text keys somewhere
}
else
warn("Unhandled extra data type "+e->recName);
e = e->extra; e = e->extra;
} }
if(node->recType == Nif::RC_NiTriShape) if(node->recType == Nif::RC_NiTriShape && !(flags&0x01)) // Not hidden
{ {
const Nif::NiTriShape *shape = dynamic_cast<const Nif::NiTriShape*>(node); const Nif::NiTriShape *shape = dynamic_cast<const Nif::NiTriShape*>(node);
mShapeName = shape->name; mShapeName = shape->name;
@ -1006,8 +1067,6 @@ public:
std::string fullname = mName+"@index="+Ogre::StringConverter::toString(shape->recIndex); std::string fullname = mName+"@index="+Ogre::StringConverter::toString(shape->recIndex);
if(mShapeName.length() > 0) if(mShapeName.length() > 0)
fullname += "@shape="+mShapeName; fullname += "@shape="+mShapeName;
if(mSkelName.length() > 0 && mName != mSkelName)
fullname += "@skel="+mSkelName;
Misc::StringUtils::toLower(fullname); Misc::StringUtils::toLower(fullname);
Ogre::MeshPtr mesh = meshMgr.getByName(fullname); Ogre::MeshPtr mesh = meshMgr.getByName(fullname);
@ -1015,21 +1074,15 @@ public:
{ {
NIFMeshLoader *loader = &sLoaders[fullname]; NIFMeshLoader *loader = &sLoaders[fullname];
*loader = *this; *loader = *this;
if(!(flags&0x01)) // Not hidden loader->mShapeIndex = shape->recIndex;
{ loader->mMaterialName = NIFMaterialLoader::getMaterial(shape, fullname, mGroup);
loader->mShapeIndex = shape->recIndex;
loader->mMaterialName = NIFMaterialLoader::getMaterial(shape, fullname, mGroup);
}
mesh = meshMgr.createManual(fullname, mGroup, loader); mesh = meshMgr.createManual(fullname, mGroup, loader);
mesh->setAutoBuildEdgeLists(false); mesh->setAutoBuildEdgeLists(false);
} }
meshes.push_back(std::make_pair(mesh->getName(), shape->name)); meshes.push_back(MeshInfo(mesh->getName(), shape->name));
} }
else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode &&
node->recType != Nif::RC_NiRotatingParticles)
warn("Unhandled mesh node type: "+node->recName);
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node); const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
if(ninode) if(ninode)
@ -1042,28 +1095,47 @@ public:
} }
} }
} }
void createEmptyMesh(const Nif::Node *node, MeshInfoList &meshes)
{
/* This creates an empty mesh to which a skeleton gets attached. This
* is to ensure we have an entity with a skeleton instance, even if all
* other meshes are hidden or entities attached to a specific node
* instead of skinned. */
std::string fullname = mName;
Misc::StringUtils::toLower(fullname);
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
Ogre::MeshPtr mesh = meshMgr.getByName(fullname);
if(mesh.isNull())
{
NIFMeshLoader *loader = &sLoaders[fullname];
*loader = *this;
mesh = meshMgr.createManual(fullname, mGroup, loader);
mesh->setAutoBuildEdgeLists(false);
}
meshes.push_back(MeshInfo(mesh->getName(), node->name));
}
}; };
NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders;
typedef std::map<std::string,MeshPairList> MeshPairMap; typedef std::map<std::string,MeshInfoList> MeshInfoMap;
static MeshPairMap sMeshPairMap; static MeshInfoMap sMeshInfoMap;
MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::string &group) MeshInfoList Loader::load(const std::string &name, const std::string &group)
{ {
Misc::StringUtils::toLower(name); MeshInfoMap::const_iterator meshiter = sMeshInfoMap.find(name);
Misc::StringUtils::toLower(skelName); if(meshiter != sMeshInfoMap.end())
MeshPairMap::const_iterator meshiter = sMeshPairMap.find(name+"@skel="+skelName);
if(meshiter != sMeshPairMap.end())
return meshiter->second; return meshiter->second;
MeshPairList &meshes = sMeshPairMap[name+"@skel="+skelName]; MeshInfoList &meshes = sMeshInfoMap[name];
Nif::NIFFile::ptr pnif = Nif::NIFFile::create (name); Nif::NIFFile::ptr pnif = Nif::NIFFile::create(name);
Nif::NIFFile &nif = *pnif.get (); Nif::NIFFile &nif = *pnif.get();
if (nif.numRecords() < 1) if(nif.numRecords() < 1)
{ {
nif.warn("Found no records in NIF."); nif.warn("Found no NIF records in "+name+".");
return meshes; return meshes;
} }
@ -1074,109 +1146,87 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::
Nif::Node const *node = dynamic_cast<Nif::Node const *>(r); Nif::Node const *node = dynamic_cast<Nif::Node const *>(r);
if(node == NULL) if(node == NULL)
{ {
nif.warn("First record in file was not a node, but a "+ nif.warn("First record in "+name+" was not a node, but a "+
r->recName+". Skipping file."); r->recName+".");
return meshes; return meshes;
} }
NIFSkeletonLoader skelldr; bool hasSkel = Ogre::SkeletonManager::getSingleton().resourceExists(name);
bool hasSkel = skelldr.createSkeleton(name, group, node); if(!hasSkel)
{
NIFSkeletonLoader skelldr;
hasSkel = skelldr.createSkeleton(name, group, node);
}
NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string())); NIFMeshLoader meshldr(name, group);
meshldr.createMeshes(node, meshes); if(hasSkel)
meshldr.createEmptyMesh(node, meshes);
meshldr.createMeshes(node, meshes, 0);
return meshes; return meshes;
} }
EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textkeys, const std::string &name, const std::string &group) EntityList Loader::createEntities(Ogre::SceneNode *parentNode, std::string name, const std::string &group)
{ {
EntityList entitylist; EntityList entitylist;
MeshPairList meshes = load(name, name, group); Misc::StringUtils::toLower(name);
MeshInfoList meshes = load(name, group);
if(meshes.size() == 0) if(meshes.size() == 0)
return entitylist; return entitylist;
Ogre::SceneManager *sceneMgr = parent->getCreator(); Ogre::SceneManager *sceneMgr = parentNode->getCreator();
for(size_t i = 0;i < meshes.size();i++) for(size_t i = 0;i < meshes.size();i++)
{ {
entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first)); entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].mMeshName));
Ogre::Entity *entity = entitylist.mEntities.back(); Ogre::Entity *entity = entitylist.mEntities.back();
if(!entitylist.mSkelBase && entity->hasSkeleton()) if(!entitylist.mSkelBase && entity->hasSkeleton())
entitylist.mSkelBase = entity; entitylist.mSkelBase = entity;
} }
if(entitylist.mSkelBase && textkeys)
{
// Would be nice if Ogre::SkeletonInstance allowed access to the 'master' Ogre::SkeletonPtr.
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton();
Ogre::SkeletonPtr skel = skelMgr.getByName(entitylist.mSkelBase->getSkeleton()->getName());
Ogre::Skeleton::BoneIterator iter = skel->getBoneIterator();
while(iter.hasMoreElements())
{
Ogre::Bone *bone = iter.getNext();
const Ogre::Any &data = bone->getUserObjectBindings().getUserAny("TextKeyExtraData");
if(!data.isEmpty())
{
*textkeys = Ogre::any_cast<TextKeyMap>(data);
break;
}
}
}
if(entitylist.mSkelBase) if(entitylist.mSkelBase)
{ {
parent->attachObject(entitylist.mSkelBase); parentNode->attachObject(entitylist.mSkelBase);
for(size_t i = 0;i < entitylist.mEntities.size();i++) for(size_t i = 0;i < entitylist.mEntities.size();i++)
{ {
Ogre::Entity *entity = entitylist.mEntities[i]; Ogre::Entity *entity = entitylist.mEntities[i];
if(entity != entitylist.mSkelBase && entity->hasSkeleton()) if(entity != entitylist.mSkelBase && entity->hasSkeleton())
{ {
entity->shareSkeletonInstanceWith(entitylist.mSkelBase); entity->shareSkeletonInstanceWith(entitylist.mSkelBase);
parent->attachObject(entity); parentNode->attachObject(entity);
} }
else if(entity != entitylist.mSkelBase) else if(entity != entitylist.mSkelBase)
entitylist.mSkelBase->attachObjectToBone(meshes[i].second, entity); entitylist.mSkelBase->attachObjectToBone(meshes[i].mTargetNode, entity);
} }
} }
else else
{ {
for(size_t i = 0;i < entitylist.mEntities.size();i++) for(size_t i = 0;i < entitylist.mEntities.size();i++)
parent->attachObject(entitylist.mEntities[i]); parentNode->attachObject(entitylist.mEntities[i]);
} }
return entitylist; return entitylist;
} }
EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bonename, EntityList Loader::createEntities(Ogre::Entity *parent, const std::string &bonename,
Ogre::SceneNode *parentNode, Ogre::SceneNode *parentNode,
const std::string &name, std::string name, const std::string &group)
const std::string &group)
{ {
EntityList entitylist; EntityList entitylist;
MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), group); Misc::StringUtils::toLower(name);
MeshInfoList meshes = load(name, group);
if(meshes.size() == 0) if(meshes.size() == 0)
return entitylist; return entitylist;
Ogre::SceneManager *sceneMgr = parentNode->getCreator(); Ogre::SceneManager *sceneMgr = parentNode->getCreator();
std::string filter = "tri "+bonename; std::string filter = "@shape=tri "+bonename;
std::transform(filter.begin()+4, filter.end(), filter.begin()+4, ::tolower); Misc::StringUtils::toLower(filter);
for(size_t i = 0;i < meshes.size();i++) for(size_t i = 0;i < meshes.size();i++)
{ {
Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first); Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].mMeshName);
if(ent->hasSkeleton()) if(!entitylist.mSkelBase && ent->hasSkeleton())
{ entitylist.mSkelBase = ent;
Misc::StringUtils::toLower(meshes[i].second);
if(meshes[i].second.length() < filter.length() ||
meshes[i].second.compare(0, filter.length(), filter) != 0)
{
sceneMgr->destroyEntity(ent);
continue;
}
if(!entitylist.mSkelBase)
entitylist.mSkelBase = ent;
}
entitylist.mEntities.push_back(ent); entitylist.mEntities.push_back(ent);
} }
@ -1186,20 +1236,20 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo
if(entitylist.mSkelBase) if(entitylist.mSkelBase)
{ {
entitylist.mSkelBase->shareSkeletonInstanceWith(parent);
parentNode->attachObject(entitylist.mSkelBase);
for(size_t i = 0;i < entitylist.mEntities.size();i++) for(size_t i = 0;i < entitylist.mEntities.size();i++)
{ {
Ogre::Entity *entity = entitylist.mEntities[i]; Ogre::Entity *entity = entitylist.mEntities[i];
if(entity != entitylist.mSkelBase && entity->hasSkeleton()) if(entity->hasSkeleton())
{ {
entity->shareSkeletonInstanceWith(parent); if(entity != entitylist.mSkelBase)
parentNode->attachObject(entity); entity->shareSkeletonInstanceWith(entitylist.mSkelBase);
if(entity->getMesh()->getName().find(filter) != std::string::npos)
parentNode->attachObject(entity);
} }
else if(entity != entitylist.mSkelBase) else
{ {
Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entity); if(entity->getMesh()->getName().find(filter) != std::string::npos)
tag->setScale(scale); entitylist.mSkelBase->attachObjectToBone(meshes[i].mTargetNode, entity);
} }
} }
} }
@ -1216,6 +1266,33 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo
} }
bool Loader::createSkeleton(const std::string &name, const std::string &group)
{
Nif::NIFFile::ptr pnif = Nif::NIFFile::create(name);
Nif::NIFFile &nif = *pnif.get();
if(nif.numRecords() < 1)
{
nif.warn("Found no NIF records in "+name+".");
return false;
}
// The first record is assumed to be the root node
Nif::Record const *r = nif.getRecord(0);
assert(r != NULL);
Nif::Node const *node = dynamic_cast<Nif::Node const *>(r);
if(node == NULL)
{
nif.warn("First record in "+name+" was not a node, but a "+
r->recName+".");
return false;
}
NIFSkeletonLoader skelldr;
return skelldr.createSkeleton(name, group, node);
}
/* More code currently not in use, from the old D source. This was /* More code currently not in use, from the old D source. This was
used in the first attempt at loading NIF meshes, where each submesh used in the first attempt at loading NIF meshes, where each submesh
in the file was given a separate bone in a skeleton. Unfortunately in the file was given a separate bone in a skeleton. Unfortunately

@ -31,18 +31,14 @@
#include <vector> #include <vector>
#include <string> #include <string>
namespace Nif
{
class Node;
class Transformation;
class NiTriShape;
}
// FIXME: This namespace really doesn't do anything Nif-specific. Any supportable
// model format should go through this.
namespace NifOgre namespace NifOgre
{ {
// FIXME: These should not be in NifOgre, it works agnostic of what model format is used
typedef std::multimap<float,std::string> TextKeyMap; typedef std::multimap<float,std::string> TextKeyMap;
static const char sTextKeyExtraDataID[] = "TextKeyExtraData";
struct EntityList { struct EntityList {
std::vector<Ogre::Entity*> mEntities; std::vector<Ogre::Entity*> mEntities;
Ogre::Entity *mSkelBase; Ogre::Entity *mSkelBase;
@ -52,39 +48,45 @@ struct EntityList {
}; };
/** This holds a list of mesh names along with the names of their parent nodes */ /* This holds a list of mesh names, the names of their parent nodes, and the offset
typedef std::vector< std::pair<std::string,std::string> > MeshPairList; * from their parent nodes. */
struct MeshInfo {
/** Manual resource loader for NIF meshes. This is the main class std::string mMeshName;
responsible for translating the internal NIF mesh structure into std::string mTargetNode;
something Ogre can use.
You have to insert meshes manually into Ogre like this:
NIFLoader::load("somemesh.nif"); MeshInfo(const std::string &name, const std::string &target)
: mMeshName(name), mTargetNode(target)
{ }
};
typedef std::vector<MeshInfo> MeshInfoList;
This returns a list of meshes used by the model, as well as the names of class Loader
their parent nodes (as they pertain to the skeleton, which is optionally
returned in the second argument if it exists).
*/
class NIFLoader
{ {
static MeshPairList load(std::string name, std::string skelName, const std::string &group); static MeshInfoList load(const std::string &name, const std::string &group);
public: public:
static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename,
Ogre::SceneNode *parentNode, Ogre::SceneNode *parentNode,
const std::string &name, std::string name,
const std::string &group="General"); const std::string &group="General");
static EntityList createEntities(Ogre::SceneNode *parent, static EntityList createEntities(Ogre::SceneNode *parentNode,
TextKeyMap *textkeys, std::string name,
const std::string &name,
const std::string &group="General"); const std::string &group="General");
static bool createSkeleton(const std::string &name, const std::string &group="General");
}; };
} }
#endif namespace std
{
// These operators allow extra data types to be stored in an Ogre::Any
// object, which can then be stored in user object bindings on the nodes
ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&);
}
#endif

@ -207,7 +207,7 @@ public:
mLineDrawer->setMaterial("BtOgre/DebugLines"); mLineDrawer->setMaterial("BtOgre/DebugLines");
mLineDrawer->setVisibilityFlags (1024); //mLineDrawer->setVisibilityFlags (1024);
} }
~DebugDrawer() ~DebugDrawer()

@ -2,7 +2,6 @@
#include <btBulletDynamicsCommon.h> #include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h> #include <btBulletCollisionCommon.h>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h> #include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include "pmove.h"
#include <components/nifbullet/bullet_nif_loader.hpp> #include <components/nifbullet/bullet_nif_loader.hpp>
#include "CMotionState.h" #include "CMotionState.h"
#include "OgreRoot.h" #include "OgreRoot.h"
@ -27,91 +26,48 @@ namespace Physic
COL_RAYCASTING = BIT(3) COL_RAYCASTING = BIT(3)
}; };
PhysicActor::PhysicActor(std::string name, std::string mesh, PhysicEngine* engine, Ogre::Vector3 position, Ogre::Quaternion rotation, float scale): PhysicActor::PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale)
mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0), mBody(0), collisionMode(false), mBoxRotation(0,0,0,0) : mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0)
, mBody(0), onGround(false), collisionMode(true), mBoxRotation(0,0,0,0), verticalForce(0.0f)
{ {
// FIXME: Force player to start in no-collision mode for now, until he spawns at a proper door marker.
if(name == "player")
collisionMode = false;
mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation); mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation);
Ogre::Quaternion inverse = mBoxRotation.Inverse(); Ogre::Quaternion inverse = mBoxRotation.Inverse();
mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w); mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w);
mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map
pmove = new playerMove;
pmove->mEngine = mEngine;
btBoxShape* box = static_cast<btBoxShape*> (mBody->getCollisionShape());
if(box != NULL){
btVector3 size = box->getHalfExtentsWithMargin();
Ogre::Vector3 halfExtents = Ogre::Vector3(size.getX(), size.getY(), size.getZ());
pmove->ps.halfExtents = halfExtents;
}
} }
PhysicActor::~PhysicActor() PhysicActor::~PhysicActor()
{ {
if(mBody){ if(mBody)
{
mEngine->dynamicsWorld->removeRigidBody(mBody); mEngine->dynamicsWorld->removeRigidBody(mBody);
delete mBody; delete mBody;
} }
delete pmove;
}
void PhysicActor::setCurrentWater(bool hasWater, int waterHeight){
pmove->hasWater = hasWater;
if(hasWater){
pmove->waterHeight = waterHeight;
}
}
void PhysicActor::setGravity(float gravity)
{
pmove->ps.gravity = gravity;
}
void PhysicActor::setSpeed(float speed)
{
pmove->ps.speed = speed;
} }
void PhysicActor::enableCollisions(bool collision) void PhysicActor::enableCollisions(bool collision)
{ {
collisionMode = collision; collisionMode = collision;
if(collisionMode)
pmove->ps.move_type=PM_NORMAL;
else
pmove->ps.move_type=PM_NOCLIP;
} }
void PhysicActor::setJumpVelocity(float velocity)
{
pmove->ps.jump_velocity = velocity;
}
bool PhysicActor::getCollisionMode()
{
return collisionMode;
}
void PhysicActor::setMovement(signed char rightmove, signed char forwardmove, signed char upmove) void PhysicActor::setPosition(const Ogre::Vector3 &pos)
{ {
playerMove::playercmd& pm_ref = pmove->cmd; if(pos != getPosition())
pm_ref.rightmove = rightmove; mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation);
pm_ref.forwardmove = forwardmove;
pm_ref.upmove = upmove;
}
void PhysicActor::setPmoveViewAngles(float pitch, float yaw, float roll){
pmove->ps.viewangles.x = pitch;
pmove->ps.viewangles.y = yaw;
pmove->ps.viewangles.z = roll;
} }
void PhysicActor::setRotation(const Ogre::Quaternion &quat)
void PhysicActor::setRotation(const Ogre::Quaternion quat)
{ {
if(!quat.equals(getRotation(), Ogre::Radian(0))){ if(!quat.equals(getRotation(), Ogre::Radian(0))){
mEngine->adjustRigidBody(mBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation); mEngine->adjustRigidBody(mBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation);
} }
} }
Ogre::Vector3 PhysicActor::getPosition() Ogre::Vector3 PhysicActor::getPosition()
{ {
btVector3 vec = mBody->getWorldTransform().getOrigin(); btVector3 vec = mBody->getWorldTransform().getOrigin();
@ -128,13 +84,6 @@ namespace Physic
return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()); return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ());
} }
void PhysicActor::setPosition(const Ogre::Vector3 pos)
{
mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation);
btVector3 vec = mBody->getWorldTransform().getOrigin();
pmove->ps.origin = Ogre::Vector3(vec.getX(), vec.getY(), vec.getZ());
}
void PhysicActor::setScale(float scale){ void PhysicActor::setScale(float scale){
Ogre::Vector3 position = getPosition(); Ogre::Vector3 position = getPosition();
Ogre::Quaternion rotation = getRotation(); Ogre::Quaternion rotation = getRotation();
@ -148,18 +97,40 @@ namespace Physic
//Create the newly scaled rigid body //Create the newly scaled rigid body
mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation); mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation);
mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map mEngine->addRigidBody(mBody, false); //Add rigid body to dynamics world, but do not add to object map
btBoxShape* box = static_cast<btBoxShape*> (mBody->getCollisionShape()); }
if(box != NULL){
btVector3 size = box->getHalfExtentsWithMargin(); Ogre::Vector3 PhysicActor::getHalfExtents() const
Ogre::Vector3 halfExtents = Ogre::Vector3(size.getX(), size.getY(), size.getZ()); {
pmove->ps.halfExtents = halfExtents; if(mBody)
{
btBoxShape *box = static_cast<btBoxShape*>(mBody->getCollisionShape());
if(box != NULL)
{
btVector3 size = box->getHalfExtentsWithMargin();
return Ogre::Vector3(size.getX(), size.getY(), size.getZ());
}
} }
return Ogre::Vector3(0.0f);
}
void PhysicActor::setVerticalForce(float force)
{
verticalForce = force;
}
float PhysicActor::getVerticalForce() const
{
return verticalForce;
}
void PhysicActor::setOnGround(bool grounded)
{
onGround = grounded;
} }
void PhysicActor::runPmove(){ bool PhysicActor::getOnGround() const
Pmove(pmove); {
Ogre::Vector3 newpos = pmove->ps.origin; return collisionMode && onGround;
mBody->getWorldTransform().setOrigin(btVector3(newpos.x, newpos.y, newpos.z));
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -221,10 +192,7 @@ namespace Physic
{ {
if(!isDebugCreated) if(!isDebugCreated)
{ {
Ogre::SceneManagerEnumerator::SceneManagerIterator iter = Ogre::Root::getSingleton().getSceneManagerIterator(); Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
iter.begin();
Ogre::SceneManager* scn = iter.getNext();
Ogre::SceneNode* node = scn->getRootSceneNode()->createChildSceneNode();
node->pitch(Ogre::Degree(-90)); node->pitch(Ogre::Degree(-90));
mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld); mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld);
dynamicsWorld->setDebugDrawer(mDebugDrawer); dynamicsWorld->setDebugDrawer(mDebugDrawer);
@ -249,6 +217,11 @@ namespace Physic
return mDebugActive; return mDebugActive;
} }
void PhysicEngine::setSceneManager(Ogre::SceneManager* sceneMgr)
{
mSceneMgr = sceneMgr;
}
PhysicEngine::~PhysicEngine() PhysicEngine::~PhysicEngine()
{ {
HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin(); HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin();
@ -356,18 +329,21 @@ namespace Physic
mHeightFieldMap.erase(name); mHeightFieldMap.erase(name);
} }
void PhysicEngine::adjustRigidBody(RigidBody* body, Ogre::Vector3 position, Ogre::Quaternion rotation, void PhysicEngine::adjustRigidBody(RigidBody* body, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
Ogre::Vector3 scaledBoxTranslation, Ogre::Quaternion boxRotation){ const Ogre::Vector3 &scaledBoxTranslation, const Ogre::Quaternion &boxRotation)
{
btTransform tr; btTransform tr;
rotation = rotation * boxRotation; Ogre::Quaternion boxrot = rotation * boxRotation;
Ogre::Vector3 transrot = rotation * scaledBoxTranslation; Ogre::Vector3 transrot = boxrot * scaledBoxTranslation;
Ogre::Vector3 newPosition = transrot + position; Ogre::Vector3 newPosition = transrot + position;
tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z)); tr.setOrigin(btVector3(newPosition.x, newPosition.y, newPosition.z));
tr.setRotation(btQuaternion(rotation.x,rotation.y,rotation.z,rotation.w)); tr.setRotation(btQuaternion(boxrot.x,boxrot.y,boxrot.z,boxrot.w));
body->setWorldTransform(tr); body->setWorldTransform(tr);
} }
void PhysicEngine::boxAdjustExternal(std::string mesh, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation){ void PhysicEngine::boxAdjustExternal(const std::string &mesh, RigidBody* body,
float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation)
{
std::string sid = (boost::format("%07.3f") % scale).str(); std::string sid = (boost::format("%07.3f") % scale).str();
std::string outputstring = mesh + sid; std::string outputstring = mesh + sid;
//std::cout << "The string" << outputstring << "\n"; //std::cout << "The string" << outputstring << "\n";
@ -380,7 +356,8 @@ namespace Physic
adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation); adjustRigidBody(body, position, rotation, shape->boxTranslation * scale, shape->boxRotation);
} }
RigidBody* PhysicEngine::createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, RigidBody* PhysicEngine::createAndAdjustRigidBody(const std::string &mesh, const std::string &name,
float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation) Ogre::Vector3* scaledBoxTranslation, Ogre::Quaternion* boxRotation)
{ {
std::string sid = (boost::format("%07.3f") % scale).str(); std::string sid = (boost::format("%07.3f") % scale).str();
@ -441,7 +418,7 @@ namespace Physic
} }
} }
void PhysicEngine::removeRigidBody(std::string name) void PhysicEngine::removeRigidBody(const std::string &name)
{ {
RigidBodyContainer::iterator it = ObjectMap.find(name); RigidBodyContainer::iterator it = ObjectMap.find(name);
if (it != ObjectMap.end() ) if (it != ObjectMap.end() )
@ -461,7 +438,7 @@ namespace Physic
} }
} }
void PhysicEngine::deleteRigidBody(std::string name) void PhysicEngine::deleteRigidBody(const std::string &name)
{ {
RigidBodyContainer::iterator it = ObjectMap.find(name); RigidBodyContainer::iterator it = ObjectMap.find(name);
if (it != ObjectMap.end() ) if (it != ObjectMap.end() )
@ -481,7 +458,7 @@ namespace Physic
} }
} }
RigidBody* PhysicEngine::getRigidBody(std::string name) RigidBody* PhysicEngine::getRigidBody(const std::string &name)
{ {
RigidBodyContainer::iterator it = ObjectMap.find(name); RigidBodyContainer::iterator it = ObjectMap.find(name);
if (it != ObjectMap.end() ) if (it != ObjectMap.end() )
@ -497,15 +474,16 @@ namespace Physic
void PhysicEngine::stepSimulation(double deltaT) void PhysicEngine::stepSimulation(double deltaT)
{ {
dynamicsWorld->stepSimulation(deltaT,10, 1/60.0); // This isn't needed as there are no dynamic objects at this point
//dynamicsWorld->stepSimulation(deltaT,10, 1/60.0);
if(isDebugCreated) if(isDebugCreated)
{ {
mDebugDrawer->step(); mDebugDrawer->step();
} }
} }
void PhysicEngine::addCharacter(std::string name, std::string mesh, void PhysicEngine::addCharacter(const std::string &name, const std::string &mesh,
Ogre::Vector3 position, float scale, Ogre::Quaternion rotation) const Ogre::Vector3 &position, float scale, const Ogre::Quaternion &rotation)
{ {
// Remove character with given name, so we don't make memory // Remove character with given name, so we don't make memory
// leak when character would be added twice // leak when character would be added twice
@ -518,9 +496,8 @@ namespace Physic
PhysicActorMap[name] = newActor; PhysicActorMap[name] = newActor;
} }
void PhysicEngine::removeCharacter(std::string name) void PhysicEngine::removeCharacter(const std::string &name)
{ {
//std::cout << "remove";
PhysicActorContainer::iterator it = PhysicActorMap.find(name); PhysicActorContainer::iterator it = PhysicActorMap.find(name);
if (it != PhysicActorMap.end() ) if (it != PhysicActorMap.end() )
{ {
@ -534,7 +511,7 @@ namespace Physic
} }
} }
PhysicActor* PhysicEngine::getCharacter(std::string name) PhysicActor* PhysicEngine::getCharacter(const std::string &name)
{ {
PhysicActorContainer::iterator it = PhysicActorMap.find(name); PhysicActorContainer::iterator it = PhysicActorMap.find(name);
if (it != PhysicActorMap.end() ) if (it != PhysicActorMap.end() )

@ -18,13 +18,17 @@ class btSequentialImpulseConstraintSolver;
class btCollisionDispatcher; class btCollisionDispatcher;
class btDiscreteDynamicsWorld; class btDiscreteDynamicsWorld;
class btHeightfieldTerrainShape; class btHeightfieldTerrainShape;
struct playerMove;
namespace BtOgre namespace BtOgre
{ {
class DebugDrawer; class DebugDrawer;
} }
namespace Ogre
{
class SceneManager;
}
namespace MWWorld namespace MWWorld
{ {
class World; class World;
@ -61,33 +65,26 @@ namespace Physic
class PhysicActor class PhysicActor
{ {
public: public:
PhysicActor(std::string name, std::string mesh, PhysicEngine *engine, Ogre::Vector3 position, Ogre::Quaternion rotation, float scale); PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale);
~PhysicActor(); ~PhysicActor();
void setCurrentWater(bool hasWater, int waterHeight); void setPosition(const Ogre::Vector3 &pos);
/**
* This function sets the movement keys for pmove
*/
void setMovement(signed char rightmove, signed char forwardmove, signed char upmove);
/** /**
* This adjusts the rotation of a PhysicActor * This adjusts the rotation of a PhysicActor
* If we have any problems with this (getting stuck in pmove) we should change it * If we have any problems with this (getting stuck in pmove) we should change it
* from setting the visual orientation to setting the orientation of the rigid body directly. * from setting the visual orientation to setting the orientation of the rigid body directly.
*/ */
void setRotation(const Ogre::Quaternion quat); void setRotation(const Ogre::Quaternion &quat);
void setGravity(float gravity);
void setSpeed(float speed);
void setJumpVelocity(float velocity);
void enableCollisions(bool collision); void enableCollisions(bool collision);
bool getCollisionMode(); bool getCollisionMode() const
{
return collisionMode;
}
/** /**
* This returns the visual position of the PhysicActor (used to position a scenenode). * This returns the visual position of the PhysicActor (used to position a scenenode).
@ -101,26 +98,28 @@ namespace Physic
Ogre::Quaternion getRotation(); Ogre::Quaternion getRotation();
/** /**
* Sets the position of mBody from a visual position input. * Sets the scale of the PhysicActor
* For most cases this should not be used. We should instead let pmove move the PhysicActor around for us
*/ */
void setPosition(const Ogre::Vector3 pos); void setScale(float scale);
/** /**
* Sets the view angles for pmove directly. * Returns the half extents for this PhysiActor
* Remember, add 90 for yaw. Set roll to 0.
*/ */
void setPmoveViewAngles(float pitch, float yaw, float roll); Ogre::Vector3 getHalfExtents() const;
/** /**
* Sets the scale of the PhysicActor * Sets the current amount of vertical force (gravity) affecting this physic actor
*/ */
void setScale(float scale); void setVerticalForce(float force);
/** /**
* Runs pmove for this PhysicActor * Gets the current amount of vertical force (gravity) affecting this physic actor
*/ */
void runPmove(); float getVerticalForce() const;
void setOnGround(bool grounded);
bool getOnGround() const;
//HACK: in Visual Studio 2010 and presumably above, this structures alignment //HACK: in Visual Studio 2010 and presumably above, this structures alignment
// must be 16, but the built in operator new & delete don't properly // must be 16, but the built in operator new & delete don't properly
@ -136,12 +135,12 @@ namespace Physic
Ogre::Vector3 mBoxScaledTranslation; Ogre::Vector3 mBoxScaledTranslation;
btQuaternion mBoxRotationInverse; btQuaternion mBoxRotationInverse;
Ogre::Quaternion mBoxRotation; Ogre::Quaternion mBoxRotation;
float verticalForce;
bool onGround;
bool collisionMode; bool collisionMode;
std::string mMesh; std::string mMesh;
PhysicEngine* mEngine; PhysicEngine* mEngine;
std::string mName; std::string mName;
playerMove* pmove;
}; };
/** /**
@ -190,19 +189,21 @@ namespace Physic
* Creates a RigidBody. It does not add it to the simulation. * Creates a RigidBody. It does not add it to the simulation.
* After created, the body is set to the correct rotation, position, and scale * After created, the body is set to the correct rotation, position, and scale
*/ */
RigidBody* createAndAdjustRigidBody(std::string mesh,std::string name,float scale, Ogre::Vector3 position, Ogre::Quaternion rotation, RigidBody* createAndAdjustRigidBody(const std::string &mesh, const std::string &name,
float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
Ogre::Vector3* scaledBoxTranslation = 0, Ogre::Quaternion* boxRotation = 0); Ogre::Vector3* scaledBoxTranslation = 0, Ogre::Quaternion* boxRotation = 0);
/** /**
* Adjusts a rigid body to the right position and rotation * Adjusts a rigid body to the right position and rotation
*/ */
void adjustRigidBody(RigidBody* body, Ogre::Vector3 position, Ogre::Quaternion rotation, void adjustRigidBody(RigidBody* body, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
Ogre::Vector3 scaledBoxTranslation = Ogre::Vector3::ZERO, Ogre::Quaternion boxRotation = Ogre::Quaternion::IDENTITY); const Ogre::Vector3 &scaledBoxTranslation = Ogre::Vector3::ZERO,
const Ogre::Quaternion &boxRotation = Ogre::Quaternion::IDENTITY);
/** /**
Mainly used to (but not limited to) adjust rigid bodies based on box shapes to the right position and rotation. Mainly used to (but not limited to) adjust rigid bodies based on box shapes to the right position and rotation.
*/ */
void boxAdjustExternal(std::string mesh, RigidBody* body, float scale, Ogre::Vector3 position, Ogre::Quaternion rotation); void boxAdjustExternal(const std::string &mesh, RigidBody* body, float scale, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation);
/** /**
* Add a HeightField to the simulation * Add a HeightField to the simulation
*/ */
@ -223,35 +224,35 @@ namespace Physic
/** /**
* Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap. * Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap.
*/ */
void removeRigidBody(std::string name); void removeRigidBody(const std::string &name);
/** /**
* Delete a RigidBody, and remove it from RigidBodyMap. * Delete a RigidBody, and remove it from RigidBodyMap.
*/ */
void deleteRigidBody(std::string name); void deleteRigidBody(const std::string &name);
/** /**
* Return a pointer to a given rigid body. * Return a pointer to a given rigid body.
* TODO:check if exist * TODO:check if exist
*/ */
RigidBody* getRigidBody(std::string name); RigidBody* getRigidBody(const std::string &name);
/** /**
* Create and add a character to the scene, and add it to the ActorMap. * Create and add a character to the scene, and add it to the ActorMap.
*/ */
void addCharacter(std::string name, std::string mesh, void addCharacter(const std::string &name, const std::string &mesh,
Ogre::Vector3 position, float scale, Ogre::Quaternion rotation); const Ogre::Vector3 &position, float scale, const Ogre::Quaternion &rotation);
/** /**
* Remove a character from the scene. TODO:delete it! for now, a small memory leak^^ done? * Remove a character from the scene. TODO:delete it! for now, a small memory leak^^ done?
*/ */
void removeCharacter(std::string name); void removeCharacter(const std::string &name);
/** /**
* Return a pointer to a character * Return a pointer to a character
* TODO:check if the actor exist... * TODO:check if the actor exist...
*/ */
PhysicActor* getCharacter(std::string name); PhysicActor* getCharacter(const std::string &name);
/** /**
* This step the simulation of a given time. * This step the simulation of a given time.
@ -279,6 +280,8 @@ namespace Physic
void getObjectAABB(const std::string &mesh, float scale, btVector3 &min, btVector3 &max); void getObjectAABB(const std::string &mesh, float scale, btVector3 &min, btVector3 &max);
void setSceneManager(Ogre::SceneManager* sceneMgr);
/** /**
* Return the closest object hit by a ray. If there are no objects, it will return ("",-1). * Return the closest object hit by a ray. If there are no objects, it will return ("",-1).
*/ */
@ -315,11 +318,12 @@ namespace Physic
typedef std::map<std::string, PhysicActor*> PhysicActorContainer; typedef std::map<std::string, PhysicActor*> PhysicActorContainer;
PhysicActorContainer PhysicActorMap; PhysicActorContainer PhysicActorMap;
Ogre::SceneManager* mSceneMgr;
//debug rendering //debug rendering
BtOgre::DebugDrawer* mDebugDrawer; BtOgre::DebugDrawer* mDebugDrawer;
bool isDebugCreated; bool isDebugCreated;
bool mDebugActive; bool mDebugActive;
}; };

File diff suppressed because it is too large Load Diff

@ -1,200 +0,0 @@
#ifndef OENGINE_BULLET_PMOVE_H
#define OENGINE_BULLET_PMOVE_H
/*
This source file is a *modified* version of various header files from the Quake 3 Arena source code,
which was released under the GNU GPL (v2) in 2005.
Quake 3 Arena is copyright (C) 1999-2005 Id Software, Inc.
*/
#include <OgreMath.h>
#include <float.h>
#include "trace.h"
#include "physic.hpp"
#include <OgreVector3.h>
//#include "GameMath.h"
//#include "GameTime.h"
// Forwards-declare it!
/*#ifndef COMPILING_PMOVE
#include "Scene.h"
extern SceneInstance* global_lastscene;
#endif*/
static const Ogre::Vector3 halfExtentsDefault(14.64f * 2, 14.24f * 2, 33.25f * 2);
#define MAX_CLIP_PLANES 5
#define OVERCLIP 1.001f
//#define STEPSIZE 18 // 18 is way too much
#define STEPSIZE (9)
#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif
#define YAW 0
#define PITCH /*1*/2
#define ROLL /*2*/1
#define SHORT2ANGLE(x) ( (x) * (360.0f / 65536.0f) )
#define ANGLE2SHORT(x) ( (const short)( (x) / (360.0f / 65536.0f) ) )
#define GENTITYNUM_BITS 10 // don't need to send any more
#define MAX_GENTITIES (1 << GENTITYNUM_BITS)
#define ENTITYNUM_NONE (MAX_GENTITIES - 1)
#define ENTITYNUM_WORLD (MAX_GENTITIES - 2)
#define MIN_WALK_NORMAL .7f // can't walk on very steep slopes
#define PS_PMOVEFRAMECOUNTBITS 6
#define MINS_Z -24
#define DEFAULT_VIEWHEIGHT 26
#define CROUCH_VIEWHEIGHT 12
#define DEAD_VIEWHEIGHT (-16)
#define CONTENTS_SOLID 1 // an eye is never valid in a solid
#define CONTENTS_LAVA 8
#define CONTENTS_SLIME 16
#define CONTENTS_WATER 32
#define CONTENTS_FOG 64
static const float pm_accelerate = 10.0f;
static const float pm_stopspeed = 100.0f;
static const float pm_friction = 12.0f;
static const float pm_flightfriction = 3.0f;
static const float pm_waterfriction = 1.0f;
static const float pm_airaccelerate = 1.0f;
static const float pm_swimScale = 0.50f;
static const float pm_duckScale = 0.25f;
static const float pm_flyaccelerate = 8.0f;
static const float pm_wateraccelerate = 4.0f;
enum pmtype_t
{
PM_NORMAL, // can accelerate and turn
PM_NOCLIP, // noclip movement
PM_SPECTATOR, // still run into walls
PM_DEAD, // no acceleration or turning, but free falling
PM_FREEZE, // stuck in place with no control
PM_INTERMISSION, // no movement or status bar
PM_SPINTERMISSION // no movement or status bar
};
enum waterlevel_t
{
WL_DRYLAND = 0,
WL_ANKLE,
WL_WAIST,
WL_UNDERWATER
};
//#include "bprintf.h"
struct playerMove
{
struct playerStruct
{
playerStruct() : gravity(800.0f), speed(480.0f), jump_velocity(270), pmove_framecount(20), groundEntityNum(ENTITYNUM_NONE), commandTime(40), move_type(PM_NOCLIP), pm_time(0), snappingImplemented(true), bSnap(false), counter(-1), halfExtents(halfExtentsDefault)
{
origin = Ogre::Vector3(0.0f, 0.0f, 0.0f);
velocity = Ogre::Vector3(0.0f, 0.0f, 0.0f);
viewangles = Ogre::Vector3(0.0f, 0.0f, 0.0f);
delta_angles[0] = delta_angles[1] = delta_angles[2] = 0;
lastframe_origin.x = lastframe_origin.y = lastframe_origin.z = 0;
lerp_multiplier.x = lerp_multiplier.y = lerp_multiplier.z = 0;
}
inline void SpeedUp(void)
{
//printf("speed up to: %f\n", speed);
speed *= 1.25f;
}
inline void SpeedDown(void)
{
//printf("speed down to %f\n", speed);
speed /= 1.25f;
}
Ogre::Vector3 velocity;
Ogre::Vector3 origin;
Ogre::Vector3 halfExtents;
bool bSnap;
bool snappingImplemented;
int counter;
float gravity; // default = 800
float speed; // default = 320
float jump_velocity; //default = 270
int commandTime; // the time at which this command was issued (in milliseconds)
int pm_time;
Ogre::Vector3 viewangles;
int groundEntityNum;
int pmove_framecount;
int watertype;
waterlevel_t waterlevel;
signed short delta_angles[3];
pmtype_t move_type;
float last_compute_time;
Ogre::Vector3 lastframe_origin;
Ogre::Vector3 lerp_multiplier;
} ps;
struct playercmd
{
enum CMDstateChange
{
NO_CHANGE,
KEYDOWN,
KEYUP
};
playercmd() : forwardmove(0), rightmove(0), upmove(0), serverTime(50), ducking(false),
activating(false), lastActivatingState(false), procActivating(NO_CHANGE),
dropping(false), lastDroppingState(false), procDropping(NO_CHANGE)
{
angles[0] = angles[1] = angles[2] = 0;
}
int serverTime;
short angles[3];
signed char forwardmove;
signed char rightmove;
signed char upmove;
bool ducking;
bool activating; // if the user is holding down the activate button
bool dropping; // if the user is dropping an item
bool lastActivatingState;
bool lastDroppingState;
CMDstateChange procActivating;
CMDstateChange procDropping;
} cmd;
playerMove() : msec(50), pmove_fixed(false), pmove_msec(50), waterHeight(0), isInterior(true), hasWater(false)
{
}
int msec;
int pmove_msec;
bool pmove_fixed;
int waterHeight;
bool hasWater;
bool isInterior;
OEngine::Physic::PhysicEngine* mEngine;
};
void Pmove (playerMove* const pmove);
void Ext_UpdateViewAngles(playerMove* const pm);
void AngleVectors( const Ogre::Vector3& angles, Ogre::Vector3* const forward, Ogre::Vector3* const right, Ogre::Vector3* const up) ;
#endif

@ -1,196 +1,57 @@
#include "trace.h" #include "trace.h"
#include <map> #include <map>
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include "physic.hpp"
enum traceWorldType
void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, const float rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass) //Traceobj was a Aedra Object
{ {
//static float lastyaw = 0.0f; collisionWorldTrace = 1,
//static float lastpitch = 0.0f; pickWorldTrace = 2,
//if (!traceobj) bothWorldTrace = collisionWorldTrace | pickWorldTrace
// return; };
//if (!traceobj->incellptr)
// return;
const Ogre::Vector3 rayDir = end - start;
NewPhysTraceResults out;
//std::cout << "Starting trace\n";
//Ogre::Vector3 startReplace = Ogre::Vector3(650,950, 45);
//Ogre::Vector3 endReplace = startReplace;
//endReplace.z -= .25;
const bool hasHit = NewPhysicsTrace<collisionWorldTrace>(&out, start, end, BBHalfExtents, Ogre::Vector3(0.0f, 0.0f, 0.0f), isInterior, enginePass);
if (out.fraction < 0.001f)
results->startsolid = true;
else
results->startsolid = false;
//results->allsolid = out.startSolid; enum collaborativePhysicsType
// If outside and underground, we're solid
/*if (isInterior)
{
const Ogre::Vector3 height = GetGroundPosition(start, CellCoords(traceCell->data->gridX, traceCell->data->gridY) );
if (start.yPos - height.yPos < (-2.0f * BBHalfExtents.yPos) )
{
results->allsolid = true;
}
else
results->allsolid = false;
}*/
// If inside and out of the tree, we're solid
//else
//{
results->allsolid = out.startSolid;
//std::cout << "allsolid" << results->allsolid << "\n";
//}
if (!hasHit)
{
results->endpos = end;
results->planenormal = Ogre::Vector3(0.0f, 0.0f, 1.0f);
results->entityNum = ENTITYNUM_NONE;
results->fraction = 1.0f;
}
else
{
results->fraction = out.fraction;
results->planenormal = out.hitNormal;
results->endpos = rayDir * results->fraction + start;
results->entityNum = ENTITYNUM_WORLD;
/*bprintf("Start: (%f, %f, %f) End: (%f, %f, %f) TraceDir: (%f, %f, %f) HitNormal: (%f, %f, %f) Fraction: %f Hitpos: (%f, %f, %f) CompensatedHitpos: (%f, %f, %f)\n",
start.xPos, start.yPos, start.zPos,
end.xPos, end.yPos, end.zPos,
rayDir.xPos, rayDir.yPos, rayDir.zPos,
results->planenormal.xPos, results->planenormal.yPos, results->planenormal.zPos, results->fraction,
out.endPos.xPos, out.endPos.yPos, out.endPos.zPos,
results->endpos.xPos, results->endpos.yPos, results->endpos.zPos);*/
}
}
template <const traceWorldType traceType>
const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end,
const Ogre::Vector3& BBHalfExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass)
{ {
//if (!traceobj->incellptr) No_Physics = 0, // Both are empty (example: statics you can walk through, like tall grass)
// return false; Only_Collision = 1, // This object only has collision physics but no pickup physics (example: statics)
//if(enginePass->dynamicsWorld->getCollisionObjectArray().at(60)->getCollisionShape()->isConvex()) Only_Pickup = 2, // This object only has pickup physics but no collision physics (example: items dropped on the ground)
// std::cout << "It's convex\n"; Both_Physics = 3 // This object has both kinds of physics (example: activators)
};
const btVector3 btstart(start.x, start.y, start.z + BBHalfExtents.z); void newtrace(traceResults *results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine *enginePass) //Traceobj was a Aedra Object
const btVector3 btend(end.x, end.y, end.z + BBHalfExtents.z); {
const btQuaternion btrot(rotation.y, rotation.x, rotation.z); //y, x, z const btVector3 btstart(start.x, start.y, start.z + BBHalfExtents.z);
const btVector3 btend(end.x, end.y, end.z + BBHalfExtents.z);
const btQuaternion btrot(0.0f, 0.0f, 0.0f); //y, x, z
const btBoxShape newshape(btVector3(BBHalfExtents.x, BBHalfExtents.y, BBHalfExtents.z)); const btBoxShape newshape(btVector3(BBHalfExtents.x, BBHalfExtents.y, BBHalfExtents.z));
//const btCapsuleShapeZ newshape(BBHalfExtents.x, BBHalfExtents.z * 2 - BBHalfExtents.x * 2); //const btCapsuleShapeZ newshape(BBHalfExtents.x, BBHalfExtents.z * 2 - BBHalfExtents.x * 2);
const btTransform from(btrot, btstart); const btTransform from(btrot, btstart);
const btTransform to(btrot, btend); const btTransform to(btrot, btend);
// warning: unused variable ... btCollisionWorld::ClosestConvexResultCallback newTraceCallback(btstart, btend);
/* newTraceCallback.m_collisionFilterMask = Only_Collision;
float x = from.getOrigin().getX();
float y = from.getOrigin().getY(); enginePass->dynamicsWorld->convexSweepTest(&newshape, from, to, newTraceCallback);
float z = from.getOrigin().getZ();
float x2 = to.getOrigin().getX(); // Copy the hit data over to our trace results struct:
float y2 = to.getOrigin().getY(); if(newTraceCallback.hasHit())
float z2 = to.getOrigin().getZ(); {
*/ const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld;
results->fraction = newTraceCallback.m_closestHitFraction;
//std::cout << "BtFrom: " << x << "," << y << "," << z << "\n"; results->planenormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z());
//std::cout << "BtTo: " << x2 << "," << y2 << "," << z2 << "\n"; results->endpos = (end-start)*results->fraction + start;
//std::cout << "BtTo: " << to.getOrigin().getX() << "," << to.getOrigin().getY() << "," << to.getOrigin().getZ() << "\n"; }
else
{
btCollisionWorld::ClosestConvexResultCallback results->endpos = end;
newTraceCallback(btstart, btend); results->planenormal = Ogre::Vector3(0.0f, 0.0f, 1.0f);
results->fraction = 1.0f;
newTraceCallback.m_collisionFilterMask = (traceType == collisionWorldTrace) ? Only_Collision : Only_Pickup; }
enginePass->dynamicsWorld->convexSweepTest(&newshape, from, to, newTraceCallback);
//newTraceCallback.
//std::cout << "NUM: " << enginePass->dynamicsWorld->getNumCollisionObjects() << "\n";
// Copy the hit data over to our trace results struct:
out->fraction = newTraceCallback.m_closestHitFraction;
Ogre::Vector3& outhitnormal = out->hitNormal;
const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld;
outhitnormal.x = tracehitnormal.x();
outhitnormal.y = tracehitnormal.y();
outhitnormal.z = tracehitnormal.z();
Ogre::Vector3& outhitpos = out->endPos;
const btVector3& tracehitpos = newTraceCallback.m_hitPointWorld;
outhitpos.x = tracehitpos.x();
outhitpos.y = tracehitpos.y();
outhitpos.z= tracehitpos.z();
// StartSolid test:
{
out->startSolid = false;
//btCollisionObject collision;
//collision.setCollisionShape(const_cast<btBoxShape* const>(&newshape) );
//CustomContactCallback crb;
//world.world->contactTest(&collision, crb);
//out->startSolid = crb.hit;
// If outside and underground, we're solid
if (!isInterior) //Check if we are interior
{
}
// If inside and out of the tree, we're solid
else
{
btVector3 aabbMin, aabbMax;
enginePass->broadphase->getBroadphaseAabb(aabbMin, aabbMax);
//std::cout << "AABBMIN" << aabbMin.getX() <<"," <<aabbMin.getY() << "," << aabbMin.getZ() << "\n";
//std::cout << "AABBMAX" << aabbMax.getX() <<"," <<aabbMax.getY() << "," << aabbMax.getZ() << "\n";
//std::cout << "AABBMAX" << aabbMax << "\n";
if (!TestPointAgainstAabb2(aabbMin, aabbMax, *(const btVector3* const)&(start) ) )
{
//We're solid
//THIS NEEDS TO BE TURNED OFF IF WE WANT FALLING IN EXTERIORS TO WORK CORRECTLY!!!!!!!
//out->startSolid = true;
}
}
}
const bool hasHit = newTraceCallback.hasHit();
return hasHit;
} }

@ -1,60 +1,26 @@
#ifndef OENGINE_BULLET_TRACE_H #ifndef OENGINE_BULLET_TRACE_H
#define OENGINE_BULLET_TRACE_H #define OENGINE_BULLET_TRACE_H
#include <OgreVector3.h>
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include <components/nifbullet/bullet_nif_loader.hpp>
#include <openengine/bullet/physic.hpp>
#include "pmove.h"
namespace OEngine
enum traceWorldType
{ {
collisionWorldTrace = 1, namespace Physic
pickWorldTrace = 2, {
bothWorldTrace = collisionWorldTrace | pickWorldTrace class PhysicEngine;
}; }
}
enum collaborativePhysicsType
{
No_Physics = 0, // Both are empty (example: statics you can walk through, like tall grass)
Only_Collision = 1, // This object only has collision physics but no pickup physics (example: statics)
Only_Pickup = 2, // This object only has pickup physics but no collision physics (example: items dropped on the ground)
Both_Physics = 3 // This object has both kinds of physics (example: activators)
};
struct NewPhysTraceResults
{
Ogre::Vector3 endPos;
Ogre::Vector3 hitNormal;
float fraction;
bool startSolid;
//const Object* hitObj;
};
struct traceResults struct traceResults
{ {
Ogre::Vector3 endpos; Ogre::Vector3 endpos;
Ogre::Vector3 planenormal; Ogre::Vector3 planenormal;
float fraction; float fraction;
int surfaceFlags;
int contents;
int entityNum;
bool allsolid;
bool startsolid;
}; };
void newtrace(traceResults *results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine* enginePass);
template <const traceWorldType traceType>
const bool NewPhysicsTrace(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass);
//template const bool NewPhysicsTrace<collisionWorldTrace>(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass);
//template const bool NewPhysicsTrace<pickWorldTrace>(NewPhysTraceResults* const out, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const Ogre::Vector3& rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass);
void newtrace(traceResults* const results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBExtents, const float rotation, bool isInterior, OEngine::Physic::PhysicEngine* enginePass);
#endif #endif

Loading…
Cancel
Save