Merge branch 'master' into graphics

Conflicts:
	apps/openmw/CMakeLists.txt
	apps/openmw/mwrender/npcanimation.cpp
	apps/openmw/mwrender/sky.cpp
actorid
scrawl 12 years ago
commit cacdb33b42

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

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

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

@ -37,24 +37,24 @@ namespace MWBase
virtual ~MechanicsManager() {}
virtual void addActor (const MWWorld::Ptr& ptr) = 0;
///< Register an actor for stats management
///
/// \note Dead actors are ignored.
virtual void add (const MWWorld::Ptr& ptr) = 0;
///< Register an object for management
virtual void remove (const MWWorld::Ptr& ptr) = 0;
///< Deregister an object for management
virtual void removeActor (const MWWorld::Ptr& ptr) = 0;
///< Deregister an actor for stats management
virtual void updateCell(const MWWorld::Ptr &ptr) = 0;
///< Moves an object to a new cell
virtual void dropActors (const MWWorld::CellStore *cellStore) = 0;
///< Deregister all actors in the given cell.
virtual void drop (const MWWorld::CellStore *cellStore) = 0;
///< Deregister all objects in the given cell.
virtual void watchActor (const MWWorld::Ptr& ptr) = 0;
///< On each update look for changes in a previously registered actor and update the
/// GUI accordingly.
virtual void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement,
float duration, bool paused) = 0;
///< Update actor stats and store desired velocity vectors in \a movement
virtual void update (float duration, bool paused) = 0;
///< Update objects
///
/// \param paused In game type does not currently advance (this usually means some GUI
/// component is up).
@ -98,6 +98,17 @@ namespace MWBase
virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type,
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0;
///< 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 "../mwworld/globals.hpp"
#include "../mwworld/ptr.hpp"
namespace Ogre
{
@ -19,6 +20,11 @@ namespace OEngine
{
class Fader;
}
namespace Physic
{
class PhysicEngine;
}
}
namespace ESM
@ -35,6 +41,7 @@ namespace ESM
namespace MWRender
{
class ExternalRendering;
class Animation;
}
namespace MWWorld
@ -42,10 +49,11 @@ namespace MWWorld
class CellStore;
class Player;
class LocalScripts;
class Ptr;
class TimeStamp;
class ESMStore;
class RefData;
typedef std::vector<std::pair<MWWorld::Ptr,Ogre::Vector3> > PtrMovementList;
}
namespace MWBase
@ -227,8 +235,7 @@ namespace MWBase
virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const = 0;
///< Convert position to cell numbers
virtual void doPhysics (const std::vector<std::pair<std::string, Ogre::Vector3> >& actors,
float duration) = 0;
virtual void doPhysics (const MWWorld::PtrMovementList &actors, float duration) = 0;
///< Run physics simulation and modify \a world accordingly.
virtual bool toggleCollisionMode() = 0;
@ -263,18 +270,6 @@ namespace MWBase
///< Create a new recrod (of type npc) in the ESM store.
/// \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 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 bool isSwimming(const MWWorld::Ptr &object) = 0;
virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos) = 0;
virtual bool isFlying(const MWWorld::Ptr &ptr) const = 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 togglePreviewMode(bool enable) = 0;
@ -311,6 +308,8 @@ namespace MWBase
/// 2 - player is underwater \n
/// 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
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;

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

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

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

@ -36,14 +36,9 @@ namespace MWClass
objects.insertBegin(ptr, ptr.getRefData().isEnabled(), false);
if (!model.empty())
objects.insertMesh(ptr, "meshes\\" + model);
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);
objects.insertLight (ptr, r, g, b, radius);
objects.insertMesh(ptr, "meshes\\" + model, true);
else
objects.insertLight(ptr);
}
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
{
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())
{
std::auto_ptr<CustomData> data (new CustomData);
std::auto_ptr<CustomData> data(new CustomData);
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
{
physics.addActor(ptr);
MWBase::Environment::get().getMechanicsManager()->addActor(ptr);
MWBase::Environment::get().getMechanicsManager()->add(ptr);
}
std::string Npc::getModel(const MWWorld::Ptr &ptr) const
@ -297,9 +323,88 @@ namespace MWClass
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
@ -311,14 +416,10 @@ namespace MWClass
Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const
{
Ogre::Vector3 vector (0, 0, 0);
vector.x = getMovementSettings (ptr).mLeftRight * 127;
vector.y = getMovementSettings (ptr).mForwardBackward * 127;
vector.z = getMovementSettings(ptr).mUpDown * 127;
//if (getStance (ptr, Run, false))
// vector *= 2;
Ogre::Vector3 vector;
vector.x = getMovementSettings(ptr).mLeftRight;
vector.y = getMovementSettings(ptr).mForwardBackward;
vector.z = getMovementSettings(ptr).mUpDown;
return vector;
}
@ -420,4 +521,21 @@ namespace MWClass
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"
namespace ESM
{
class GameSetting;
}
namespace MWClass
{
class Npc : public MWWorld::Class
@ -12,6 +17,23 @@ namespace MWClass
virtual MWWorld::Ptr
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:
virtual std::string getId (const MWWorld::Ptr& ptr) const;
@ -64,6 +86,9 @@ namespace MWClass
virtual float getSpeed (const MWWorld::Ptr& ptr) const;
///< 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;
///< Return desired movement.

@ -25,7 +25,7 @@ bool sortBirthSigns(const std::pair<std::string, const ESM::BirthSign*>& left, c
}
BirthDialog::BirthDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_chargen_birth.layout", parWindowManager)
: WindowModal("openmw_chargen_birth.layout", parWindowManager)
{
// Centre dialog
center();
@ -66,7 +66,7 @@ void BirthDialog::setNextButtonShow(bool shown)
void BirthDialog::open()
{
WindowBase::open();
WindowModal::open();
updateBirths();
updateSpells();
}

@ -10,7 +10,7 @@
namespace MWGui
{
class BirthDialog : public WindowBase
class BirthDialog : public WindowModal
{
public:
BirthDialog(MWBase::WindowManager& parWindowManager);

@ -21,7 +21,7 @@ using namespace MWGui;
/* GenerateClassResultDialog */
GenerateClassResultDialog::GenerateClassResultDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_chargen_generate_class_result.layout", parWindowManager)
: WindowModal("openmw_chargen_generate_class_result.layout", parWindowManager)
{
// Centre dialog
center();
@ -68,7 +68,7 @@ void GenerateClassResultDialog::onBackClicked(MyGUI::Widget* _sender)
/* PickClassDialog */
PickClassDialog::PickClassDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_chargen_class.layout", parWindowManager)
: WindowModal("openmw_chargen_class.layout", parWindowManager)
{
// Centre dialog
center();
@ -122,6 +122,7 @@ void PickClassDialog::setNextButtonShow(bool shown)
void PickClassDialog::open()
{
WindowModal::open ();
updateClasses();
updateStats();
}
@ -276,7 +277,7 @@ void InfoBoxDialog::layoutVertically(MyGUI::WidgetPtr widget, int margin)
}
InfoBoxDialog::InfoBoxDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_infobox.layout", parWindowManager)
: WindowModal("openmw_infobox.layout", parWindowManager)
, mCurrentButton(-1)
{
getWidget(mTextBox, "TextBox");
@ -327,6 +328,7 @@ void InfoBoxDialog::setButtons(ButtonList &buttons)
void InfoBoxDialog::open()
{
WindowModal::open();
// Fix layout
layoutVertically(mTextBox, 4);
layoutVertically(mButtonBar, 6);
@ -373,7 +375,7 @@ ClassChoiceDialog::ClassChoiceDialog(MWBase::WindowManager& parWindowManager)
/* CreateClassDialog */
CreateClassDialog::CreateClassDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_chargen_create_class.layout", parWindowManager)
: WindowModal("openmw_chargen_create_class.layout", parWindowManager)
, mSpecDialog(nullptr)
, mAttribDialog(nullptr)
, mSkillDialog(nullptr)

@ -12,7 +12,7 @@
namespace MWGui
{
class InfoBoxDialog : public WindowBase
class InfoBoxDialog : public WindowModal
{
public:
InfoBoxDialog(MWBase::WindowManager& parWindowManager);
@ -63,7 +63,7 @@ namespace MWGui
ClassChoiceDialog(MWBase::WindowManager& parWindowManager);
};
class GenerateClassResultDialog : public WindowBase
class GenerateClassResultDialog : public WindowModal
{
public:
GenerateClassResultDialog(MWBase::WindowManager& parWindowManager);
@ -90,7 +90,7 @@ namespace MWGui
std::string mCurrentClassId;
};
class PickClassDialog : public WindowBase
class PickClassDialog : public WindowModal
{
public:
PickClassDialog(MWBase::WindowManager& parWindowManager);
@ -238,7 +238,7 @@ namespace MWGui
MyGUI::EditPtr mTextEdit;
};
class CreateClassDialog : public WindowBase
class CreateClassDialog : public WindowModal
{
public:
CreateClassDialog(MWBase::WindowManager& parWindowManager);

@ -20,7 +20,7 @@ using namespace MWGui;
using namespace Widgets;
RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_chargen_race.layout", parWindowManager)
: WindowModal("openmw_chargen_race.layout", parWindowManager)
, mGenderIndex(0)
, mFaceIndex(0)
, mHairIndex(0)
@ -100,6 +100,8 @@ void RaceDialog::setNextButtonShow(bool shown)
void RaceDialog::open()
{
WindowModal::open();
updateRaces();
updateSkills();
updateSpellPowers();

@ -23,7 +23,7 @@ namespace MWGui
namespace MWGui
{
class RaceDialog : public WindowBase
class RaceDialog : public WindowModal
{
public:
RaceDialog(MWBase::WindowManager& parWindowManager);

@ -23,7 +23,7 @@ using namespace Widgets;
const int ReviewDialog::sLineHeight = 18;
ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_chargen_review.layout", parWindowManager)
: WindowModal("openmw_chargen_review.layout", parWindowManager)
, mLastPos(0)
{
// Centre dialog
@ -97,6 +97,7 @@ ReviewDialog::ReviewDialog(MWBase::WindowManager& parWindowManager)
void ReviewDialog::open()
{
WindowModal::open();
updateSkillArea();
}

@ -17,7 +17,7 @@ Layout is defined by resources/mygui/openmw_chargen_review.layout.
namespace MWGui
{
class ReviewDialog : public WindowBase
class ReviewDialog : public WindowModal
{
public:
enum Dialogs {

@ -5,7 +5,7 @@
using namespace MWGui;
TextInputDialog::TextInputDialog(MWBase::WindowManager& parWindowManager)
: WindowBase("openmw_text_input.layout", parWindowManager)
: WindowModal("openmw_text_input.layout", parWindowManager)
{
// Centre dialog
center();
@ -39,6 +39,7 @@ void TextInputDialog::setTextLabel(const std::string &label)
void TextInputDialog::open()
{
WindowModal::open();
// Make sure the edit box has focus
MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit);
}

@ -13,7 +13,7 @@ namespace MWGui
namespace MWGui
{
class TextInputDialog : public WindowBase
class TextInputDialog : public WindowModal
{
public:
TextInputDialog(MWBase::WindowManager& parWindowManager);

@ -273,12 +273,12 @@ namespace MWInput
if (actionIsActive(A_MoveLeft))
{
mPlayer.setAutoMove (false);
mPlayer.setLeftRight (1);
mPlayer.setLeftRight (-1);
}
else if (actionIsActive(A_MoveRight))
{
mPlayer.setAutoMove (false);
mPlayer.setLeftRight (-1);
mPlayer.setLeftRight (1);
}
else
mPlayer.setLeftRight (0);
@ -303,6 +303,11 @@ namespace MWInput
else
mPlayer.setUpDown (0);
if(actionIsActive(A_Run))
mPlayer.setRunState(true);
else
mPlayer.setRunState(false);
if (mControlSwitch["playerviewswitch"]) {
// work around preview mode toggle when pressing Alt+Tab
@ -523,6 +528,9 @@ namespace MWInput
void InputManager::toggleMainMenu()
{
if (MyGUI::InputManager::getInstance ().isModalAny())
return;
if (mWindows.isGuiMode () && (mWindows.getMode () == MWGui::GM_MainMenu || mWindows.getMode () == MWGui::GM_Settings))
mWindows.popGuiMode();
else if (mWindows.isGuiMode () && mWindows.getMode () == MWGui::GM_Video)
@ -601,6 +609,9 @@ namespace MWInput
void InputManager::toggleConsole()
{
if (MyGUI::InputManager::getInstance ().isModalAny())
return;
bool gameMode = !mWindows.isGuiMode();
// Switch to console mode no matter what mode we are currently
@ -707,6 +718,7 @@ namespace MWInput
defaultKeyBindings[A_ToggleSpell] = OIS::KC_R;
defaultKeyBindings[A_QuickKeysMenu] = OIS::KC_F1;
defaultKeyBindings[A_Console] = OIS::KC_F2;
defaultKeyBindings[A_Run] = OIS::KC_LSHIFT;
defaultKeyBindings[A_Crouch] = OIS::KC_LCONTROL;
defaultKeyBindings[A_AutoMove] = OIS::KC_Q;
defaultKeyBindings[A_Jump] = OIS::KC_E;
@ -773,6 +785,7 @@ namespace MWInput
descriptions[A_ToggleWeapon] = "sReady_Weapon";
descriptions[A_ToggleSpell] = "sReady_Magic";
descriptions[A_Console] = "sConsoleTitle";
descriptions[A_Run] = "sRun";
descriptions[A_Crouch] = "sCrouch_Sneak";
descriptions[A_AutoMove] = "sAuto_Run";
descriptions[A_Jump] = "sJump";
@ -821,6 +834,7 @@ namespace MWInput
ret.push_back(A_MoveLeft);
ret.push_back(A_MoveRight);
ret.push_back(A_TogglePOV);
ret.push_back(A_Run);
ret.push_back(A_Crouch);
ret.push_back(A_Activate);
ret.push_back(A_ToggleWeapon);

@ -209,7 +209,7 @@ namespace MWInput
A_Journal, //Journal
A_Weapon, //Draw/Sheath weapon
A_Spell, //Ready/Unready Casting
A_AlwaysRun, //Toggle Always Run
A_Run, //Run when held
A_CycleSpellLeft, //cycling through spells
A_CycleSpellRight,
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)
{
if (!MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead())
mActors.insert (ptr);
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
if(!MWWorld::Class::get(ptr).getCreatureStats(ptr).isDead())
mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle, true)));
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)
{
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())
mActors.erase (iter);
void Actors::updateActorCell(const MWWorld::Ptr &ptr)
{
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)
{
std::set<MWWorld::Ptr>::iterator iter = mActors.begin();
while (iter!=mActors.end())
if (iter->getCell()==cellStore)
{
mActors.erase (iter++);
}
PtrControllerMap::iterator iter = mActors.begin();
while(iter != mActors.end())
{
if(iter->first.getCell()==cellStore)
mActors.erase(iter++);
else
++iter;
}
}
void Actors::update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement, float duration,
bool paused)
void Actors::update (float duration, bool paused)
{
mDuration += duration;
@ -201,79 +210,91 @@ namespace MWMechanics
{
float totalDuration = mDuration;
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())
updateNpc (*iter, totalDuration, paused);
updateActor(iter->first, totalDuration);
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
// \todo remove workaround, once player death can be handled
if (iter->getRefData().getHandle()=="player")
MWMechanics::DynamicStat<float> stat (
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).getHealth());
if (stat.getModified()<1)
{
MWMechanics::DynamicStat<float> stat (
MWWorld::Class::get (*iter).getCreatureStats (*iter).getHealth());
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;
stat.setModified (1, 0);
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).setHealth(stat);
}
++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))
MWBase::Environment::get().getWindowManager()->messageBox (
"#{sKilledEssential}", std::vector<std::string>());
iter->second.setState(CharState_Death1, false);
mActors.erase (iter++);
}
else
++iter;
++mDeathCount[MWWorld::Class::get(iter->first).getId(iter->first)];
if(MWWorld::Class::get(iter->first).isEssential(iter->first))
MWBase::Environment::get().getWindowManager()->messageBox(
"#{sKilledEssential}", std::vector<std::string>());
}
}
for (std::set<MWWorld::Ptr>::iterator iter (mActors.begin()); iter!=mActors.end();
++iter)
if(!paused)
{
Ogre::Vector3 vector = MWWorld::Class::get (*iter).getMovementVector (*iter);
mMovement.reserve(mActors.size());
if (vector!=Ogre::Vector3::ZERO)
movement.push_back (std::make_pair (iter->getRefData().getHandle(), vector));
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
{
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()
{
for (std::set<MWWorld::Ptr>::iterator iter (mActors.begin()); iter!=mActors.end(); ++iter)
{
calculateRestoration (*iter, 3600);
}
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
calculateRestoration(iter->first, 3600);
}
int Actors::countDeaths (const std::string& id) const
{
std::map<std::string, int>::const_iterator iter = mDeathCount.find (id);
if (iter!=mDeathCount.end())
std::map<std::string, int>::const_iterator iter = mDeathCount.find(id);
if(iter != mDeathCount.end())
return iter->second;
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 <map>
#include "character.hpp"
#include "../mwbase/world.hpp"
namespace Ogre
{
class Vector3;
@ -21,9 +24,14 @@ namespace MWMechanics
{
class Actors
{
std::set<MWWorld::Ptr> mActors;
float mDuration;
std::map<std::string, int> mDeathCount;
typedef std::map<MWWorld::Ptr,CharacterController> PtrControllerMap;
PtrControllerMap mActors;
MWWorld::PtrMovementList mMovement;
std::map<std::string, int> mDeathCount;
float mDuration;
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.
void updateActorCell(const MWWorld::Ptr& ptr);
///< Updates an actor with a new cell store
void dropActors (const MWWorld::CellStore *cellStore);
///< Deregister all actors in the given cell.
void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement,
float duration, bool paused);
void update (float duration, bool paused);
///< Update actor stats and store desired velocity vectors in \a movement
void updateActor (const MWWorld::Ptr& ptr, float duration);
@ -66,6 +76,9 @@ namespace MWMechanics
int countDeaths (const std::string& id) const;
///< 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();
}
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();
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();
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;
}
void MechanicsManager::update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement,
float duration, bool paused)
void MechanicsManager::update(float duration, bool paused)
{
if (!mWatched.isEmpty())
{
@ -296,9 +309,16 @@ namespace MWMechanics
}
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()
@ -629,4 +649,20 @@ namespace MWMechanics
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 "npcstats.hpp"
#include "activators.hpp"
#include "actors.hpp"
namespace Ogre
@ -29,6 +30,8 @@ namespace MWMechanics
bool mUpdatePlayer;
bool mClassSelected;
bool mRaceSelected;
Activators mActivators;
Actors mActors;
void buildPlayer();
@ -39,24 +42,24 @@ namespace MWMechanics
MechanicsManager();
virtual void addActor (const MWWorld::Ptr& ptr);
///< Register an actor for stats management
///
/// \note Dead actors are ignored.
virtual void add (const MWWorld::Ptr& ptr);
///< Register an object for management
virtual void removeActor (const MWWorld::Ptr& ptr);
///< Deregister an actor for stats management
virtual void remove (const MWWorld::Ptr& ptr);
///< Deregister an object for management
virtual void dropActors (const MWWorld::CellStore *cellStore);
///< Deregister all actors in the given cell.
virtual void updateCell(const MWWorld::Ptr &ptr);
///< 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
/// GUI accordingly.
virtual void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement,
float duration, bool paused);
///< Update actor stats and store desired velocity vectors in \a movement
virtual void update (float duration, bool paused);
///< Update objects
///
/// \param paused In game type does not currently advance (this usually means some GUI
/// component is up).
@ -92,6 +95,9 @@ namespace MWMechanics
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange);
void toLower(std::string npcFaction);
///< 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 <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"
namespace MWRender
{
using namespace Ogre;
Actors::~Actors(){
Actors::~Actors()
{
PtrAnimationMap::iterator it = mAllActors.begin();
for(;it != mAllActors.end();++it)
{
@ -22,15 +31,7 @@ Actors::~Actors(){
void Actors::setMwRoot(Ogre::SceneNode* root)
{ mMwRoot = root; }
void Actors::insertNPC(const MWWorld::Ptr &ptr, MWWorld::InventoryStore &inv)
{
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_)
void Actors::insertBegin(const MWWorld::Ptr &ptr)
{
Ogre::SceneNode* cellnode;
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
insert->setOrientation(xr*yr*zr);
if (!enabled)
insert->setVisible (false);
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];
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);
if(iter != mAllActors.end())
iter->second->playGroup(groupName, mode, number);
// Nothing to do
}
void Actors::skipAnimation (const MWWorld::Ptr& ptr)
Animation* Actors::getAnimation(const MWWorld::Ptr &ptr)
{
PtrAnimationMap::const_iterator iter = mAllActors.find(ptr);
if(iter != mAllActors.end())
iter->second->skipAnim();
}
void Actors::update (float duration)
{
for(PtrAnimationMap::const_iterator iter = mAllActors.begin();iter != mAllActors.end();iter++)
iter->second->runAnimation(duration);
return iter->second;
return NULL;
}
void Actors::updateObjectCell(const MWWorld::Ptr &ptr)
void Actors::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur)
{
Ogre::SceneNode *node;
MWWorld::CellStore *newCell = ptr.getCell();
MWWorld::CellStore *newCell = cur.getCell();
CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(newCell);
if(celliter != mCellSceneNodes.end())
@ -156,18 +162,15 @@ void Actors::updateObjectCell(const MWWorld::Ptr &ptr)
node = mMwRoot->createChildSceneNode();
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())
{
/// \note Update key (Ptr's are compared only with refdata so mCell
/// on key is outdated), maybe redundant
NpcAnimation *anim = static_cast<NpcAnimation *>(iter->second);
anim->updateParts(MWWorld::Class::get(ptr).getInventoryStore(ptr));
Animation *anim = iter->second;
mAllActors.erase(iter);
mAllActors[ptr] = anim;
anim->updatePtr(cur);
mAllActors[cur] = anim;
}
}

@ -1,17 +1,19 @@
#ifndef _GAME_RENDER_ACTORS_H
#define _GAME_RENDER_ACTORS_H
#include "npcanimation.hpp"
#include "creatureanimation.hpp"
#include <openengine/ogre/renderer.hpp>
namespace MWWorld
{
class Ptr;
class CellStore;
class InventoryStore;
}
namespace MWRender
{
class Animation;
class Actors
{
typedef std::map<MWWorld::CellStore*,Ogre::SceneNode*> CellSceneNodeMap;
@ -19,6 +21,7 @@ namespace MWRender
OEngine::Render::OgreRenderer &mRend;
Ogre::SceneNode* mMwRoot;
CellSceneNodeMap mCellSceneNodes;
PtrAnimationMap mAllActors;
@ -27,30 +30,21 @@ namespace MWRender
~Actors();
void setMwRoot(Ogre::SceneNode* root);
void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_);
void insertCreature (const MWWorld::Ptr& ptr);
void insertBegin (const MWWorld::Ptr& ptr);
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);
///< \return found?
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);
/// 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

@ -1,148 +1,460 @@
#include "animation.hpp"
#include <OgreHardwarePixelBuffer.h>
#include <OgreSkeletonManager.h>
#include <OgreSkeletonInstance.h>
#include <OgreEntity.h>
#include <OgreBone.h>
#include <OgreSubMesh.h>
#include <OgreSceneManager.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwmechanics/character.hpp"
namespace MWRender
{
Animation::Animation()
: mInsert(NULL)
, mTime(0.0f)
, mSkipFrame(false)
Animation::Animation(const MWWorld::Ptr &ptr)
: mPtr(ptr)
, mController(NULL)
, 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()
{
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
for(size_t i = 0;i < mEntityList.mEntities.size();i++)
sceneMgr->destroyEntity(mEntityList.mEntities[i]);
if(mInsert)
{
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
for(size_t i = 0;i < mEntityList.mEntities.size();i++)
sceneMgr->destroyEntity(mEntityList.mEntities[i]);
}
mEntityList.mEntities.clear();
mEntityList.mSkelBase = NULL;
}
struct checklow {
bool operator()(const char &a, const char &b) const
void Animation::setAnimationSources(const std::vector<std::string> &names)
{
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";
const std::string &startloop = groupname+": loop start";
const std::string &stop = groupname+": stop";
const std::string &stoploop = groupname+": loop stop";
mInsert = node->createChildSceneNode();
assert(mInsert);
NifOgre::TextKeyMap::const_iterator iter;
for(iter = mTextKeys.begin();iter != mTextKeys.end();iter++)
mEntityList = NifOgre::Loader::createEntities(mInsert, model);
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 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();
std::string::const_iterator strend = iter->second.end();
size_t strlen = strend-strpos;
void Animation::calcAnimVelocity()
{
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;
times->mLoopStart = iter->first;
if(keyiter->second == "loop start")
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;
if(times->mLoopStop < 0.0f)
times->mLoopStop = iter->first;
break;
bone->_setDerivedOrientation(srcbone->_getDerivedOrientation());
bone->_setDerivedPosition(srcbone->_getDerivedPosition());
bone->setScale(Ogre::Vector3::UNIT_SCALE);
}
}
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;
times.mLoops = loops;
if(mLooping)
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;
times.mLoopStop = times.mStop = 0.0f;
/* Get the non-accumulation root's difference from the last update. */
posdiff = (mNonAccumRoot->getPosition() - mLastPosition) * mAccumulate;
NifOgre::TextKeyMap::const_reverse_iterator iter = mTextKeys.rbegin();
if(iter != mTextKeys.rend())
times.mLoopStop = times.mStop = iter->first;
/* Translate the accumulation root back to compensate for the move. */
mLastPosition += posdiff;
mAccumRoot->setPosition(-mLastPosition);
}
else if(!findGroupTimes(groupname, &times))
throw std::runtime_error("Failed to find animation group "+groupname);
return posdiff;
}
void Animation::reset(const std::string &start, const std::string &stop)
{
mNextKey = mCurrentKeys->begin();
if(mode == 0 && mCurGroup.mLoops > 0)
mNextGroup = times;
while(mNextKey != mCurrentKeys->end() && mNextKey->second != start)
mNextKey++;
if(mNextKey != mCurrentKeys->end())
mCurrentTime = mNextKey->first;
else
{
mCurGroup = times;
mNextGroup = GroupTimes();
mTime = ((mode==2) ? mCurGroup.mLoopStart : mCurGroup.mStart);
mNextKey = mCurrentKeys->begin();
mCurrentTime = 0.0f;
}
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)
{
mTime += timepassed;
if(mTime >= mCurGroup.mLoopStop)
try {
bool found = false;
/* Look in reverse; last-inserted source has priority. */
for(std::vector<Ogre::SkeletonPtr>::const_reverse_iterator iter(mSkeletonSources.rbegin());iter != mSkeletonSources.rend();iter++)
{
if(mCurGroup.mLoops > 1)
{
mCurGroup.mLoops--;
mTime = mTime - mCurGroup.mLoopStop + mCurGroup.mLoopStart;
}
else if(mTime >= mCurGroup.mStop)
if((*iter)->hasAnimation(groupname))
{
if(mNextGroup.mLoops > 0)
mTime = mTime - mCurGroup.mStop + mNextGroup.mStart;
else
mTime = mCurGroup.mStop;
mCurGroup = mNextGroup;
mNextGroup = GroupTimes();
mCurrentAnim = (*iter)->getAnimation(groupname);
mCurrentKeys = &mTextKeys[groupname];
mAnimVelocity = 0.0f;
if(mNonAccumRoot)
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();
Ogre::AnimationStateIterator as = aset->getAnimationStateIterator();
while(as.hasMoreElements())
{
Ogre::AnimationState *state = as.getNext();
state->setTimePosition(mTime);
}
movement += updatePosition(targetTime);
mPlaying = (mLooping || mStopTime > targetTime);
break;
}
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
#define _GAME_RENDER_ANIMATION_H
#include <vector>
#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 {
struct GroupTimes {
float mStart;
float mStop;
float mLoopStart;
float mLoopStop;
NifOgre::TextKeyMap *mCurrentKeys;
NifOgre::TextKeyMap::const_iterator mNextKey;
Ogre::Animation *mCurrentAnim;
float mCurrentTime;
float mStopTime;
bool mPlaying;
bool mLooping;
size_t mLoops;
float mAnimVelocity;
float mAnimSpeedMult;
GroupTimes()
: mStart(-1.0f), mStop(-1.0f), mLoopStart(-1.0f), mLoopStop(-1.0f),
mLoops(0)
{ }
};
void calcAnimVelocity();
protected:
Ogre::SceneNode* mInsert;
/* Applies the given animation to the given skeleton instance, using the specified time. */
void applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel);
float mTime;
GroupTimes mCurGroup;
GroupTimes mNextGroup;
/* Updates a skeleton instance so that all bones matching the source skeleton (based on
* bone names) are positioned identically. */
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;
NifOgre::TextKeyMap mTextKeys;
/* Resets the animation to the time of the specified start marker, without
* 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:
Animation();
Animation(const MWWorld::Ptr &ptr);
virtual ~Animation();
void playGroup(std::string groupname, int mode, int loops);
void skipAnim();
virtual void runAnimation(float timepassed);
void setController(MWMechanics::CharacterController *controller);
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/world.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
#include "renderconst.hpp"
#include "npcanimation.hpp"
@ -128,7 +129,8 @@ namespace MWRender
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)));
@ -152,8 +154,7 @@ namespace MWRender
if (!mSelectionBuffer)
mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0);
mAnimation->playGroup ("inventoryhandtohand", 0, 1);
mAnimation->runAnimation (0);
mAnimation->play("inventoryhandtohand", "start", "stop", false);
}
// --------------------------------------------------------------------------------------------------

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

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

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

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

@ -5,98 +5,108 @@
#include <OgreSubEntity.h>
#include "../mwworld/esmstore.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.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()
{
removeEntities(mHead);
removeEntities(mHair);
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);
for(size_t i = 0;i < sPartListSize;i++)
removeEntities(mEntityParts[i]);
}
NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& inv, int visibilityFlags)
: Animation(),
: Animation(ptr),
mStateID(-1),
mInv(&inv),
mTimeToChange(0),
mVisibilityFlags(visibilityFlags),
mRobe(mInv->end()),
mHelmet(mInv->end()),
mShirt(mInv->end()),
mCuirass(mInv->end()),
mGreaves(mInv->end()),
mPauldronL(mInv->end()),
mPauldronR(mInv->end()),
mBoots(mInv->end()),
mPants(mInv->end()),
mGloveL(mInv->end()),
mGloveR(mInv->end()),
mSkirtIter(mInv->end())
mRobe(inv.end()),
mHelmet(inv.end()),
mShirt(inv.end()),
mCuirass(inv.end()),
mGreaves(inv.end()),
mPauldronL(inv.end()),
mPauldronR(inv.end()),
mBoots(inv.end()),
mPants(inv.end()),
mGloveL(inv.end()),
mGloveR(inv.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
mPartPriorities[init] = 0;
mPartslots[i] = -1; //each slot is empty
mPartPriorities[i] = 0;
}
const MWWorld::ESMStore &store =
MWBase::Environment::get().getWorld()->getStore();
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;
mHairModel = "meshes\\" + store.get<ESM::BodyPart>().find(mNpc->mHair)->mModel;
mBodyPrefix = "b_n_" + mNpc->mRace;
Misc::StringUtils::toLower(mBodyPrefix);
mInsert = node;
assert(mInsert);
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
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++)
{
Ogre::Entity *base = mEntityList.mEntities[i];
base->getUserObjectBindings ().setUserAny (Ogre::Any(-1));
base->getUserObjectBindings().setUserAny(Ogre::Any(-1));
if (mVisibilityFlags != 0)
base->setVisibilityFlags(mVisibilityFlags);
bool transparent = false;
for(unsigned int j=0;j < base->getNumSubEntities();++j)
{
@ -117,184 +127,137 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
}
if(mEntityList.mSkelBase)
{
Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates();
Ogre::AnimationStateIterator as = aset->getAnimationStateIterator();
while(as.hasMoreElements())
{
Ogre::AnimationState *state = as.getNext();
state->setEnabled(true);
state->setLoop(false);
}
}
float scale = race->mData.mHeight.mMale;
if (!mNpc->isMale()) {
scale = race->mData.mHeight.mFemale;
}
mInsert->scale(scale, scale, scale);
std::vector<std::string> skelnames(1, smodel);
if(!mNpc->isMale() && !isBeast)
skelnames.push_back("meshes\\base_anim_female.nif");
else if(mBodyPrefix.find("argonian") != std::string::npos)
skelnames.push_back("meshes\\argonian_swimkna.nif");
if(mNpc->mModel.length() > 0)
skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel));
setAnimationSources(skelnames);
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 *iter;
MWWorld::ContainerStoreIterator NpcAnimation::*part;
int slot;
int numReserveParts; // Max: 12
ESM::PartReferenceType reserveParts[12];
} slotlist[] = {
{ &mRobe, MWWorld::InventoryStore::Slot_Robe },
{ &mSkirtIter, MWWorld::InventoryStore::Slot_Skirt },
{ &mHelmet, MWWorld::InventoryStore::Slot_Helmet },
{ &mCuirass, MWWorld::InventoryStore::Slot_Cuirass },
{ &mGreaves, MWWorld::InventoryStore::Slot_Greaves },
{ &mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron },
{ &mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron },
{ &mBoots, MWWorld::InventoryStore::Slot_Boots },
{ &mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet },
{ &mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet },
{ &mShirt, MWWorld::InventoryStore::Slot_Shirt },
{ &mPants, MWWorld::InventoryStore::Slot_Pants },
{ 0, { },
&NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe,
12, { ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg,
ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee,
ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_RPauldron, ESM::PRT_LPauldron }
},
{ 0, { },
&NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt,
3, { ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg }
},
{ 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);
if(*slotlist[i].iter != iter)
MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot);
if(this->*slotlist[i].part != iter)
{
*slotlist[i].iter = iter;
removePartGroup(slotlist[i].slot);
apparelChanged = true;
forceupdate = true;
break;
}
}
if(!forceupdate)
return;
if(apparelChanged)
for(size_t i = 0;i < slotlistsize;i++)
{
if(mRobe != mInv->end())
{
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);
}
MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot);
if(mHelmet != mInv->end())
{
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);
}
this->*slotlist[i].part = iter;
removePartGroup(slotlist[i].slot);
if(mPauldronL != mInv->end())
{
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);
}
if(this->*slotlist[i].part == inv.end())
continue;
}
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;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Shirt, 2, parts);
prio = ((slotlist[i].numReserveParts+1)<<1) + 0;
const ESM::Clothing *clothes = store->get<ESM::Clothing>()->mBase;
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;
std::vector<ESM::PartReference> parts = clothes->mParts.mParts;
addPartGroup(MWWorld::InventoryStore::Slot_Pants, 2, parts);
prio = ((slotlist[i].numReserveParts+1)<<1) + 1;
const ESM::Armor *armor = store->get<ESM::Armor>()->mBase;
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)
@ -334,21 +297,18 @@ void NpcAnimation::updateParts()
if(mPartPriorities[PartTypeList[i].type] < 1)
{
const ESM::BodyPart *part = NULL;
const MWWorld::Store<ESM::BodyPart> &partStore =
store.get<ESM::BodyPart>();
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
if (!mNpc->isMale()) {
if(!mNpc->isMale())
{
part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]);
if (part == 0) {
if(part == 0)
part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]);
}
}
if (part == 0) {
if(part == 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]);
}
if(part)
addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel);
@ -358,8 +318,8 @@ void NpcAnimation::updateParts()
NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int group, const std::string &bonename)
{
NifOgre::EntityList entities = NIFLoader::createEntities(mEntityList.mSkelBase, bonename,
mInsert, mesh);
NifOgre::EntityList entities = NifOgre::Loader::createEntities(mEntityList.mSkelBase, bonename,
mInsert, mesh);
std::vector<Ogre::Entity*> &parts = entities.mEntities;
for(size_t i = 0;i < parts.size();i++)
{
@ -367,19 +327,43 @@ NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int
parts[i]->setVisibilityFlags(mVisibilityFlags);
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;
}
void NpcAnimation::runAnimation(float timepassed)
Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
{
if(mTimeToChange > .2)
if(mTimeToChange <= 0.0f)
{
mTimeToChange = 0;
mTimeToChange = 0.2f;
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)
@ -401,62 +385,14 @@ void NpcAnimation::removeIndividualPart(int type)
mPartPriorities[type] = 0;
mPartslots[type] = -1;
if(type == ESM::PRT_Head) //0
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
for(size_t i = 0;i < sPartListSize;i++)
{
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)
@ -486,96 +422,23 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority,
removeIndividualPart(type);
mPartslots[type] = group;
mPartPriorities[type] = priority;
switch(type)
for(size_t i = 0;i < sPartListSize;i++)
{
case ESM::PRT_Head: //0
mHead = insertBoundedPart(mesh, group, "Head");
break;
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");
if(type == sPartList[i].type)
{
mEntityParts[i] = insertBoundedPart(mesh, group, sPartList[i].name);
break;
}
}
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++)
{
ESM::PartReference &part = parts[i];
const ESM::PartReference &part = parts[i];
const MWWorld::Store<ESM::BodyPart> &partStore =
MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();
@ -587,15 +450,10 @@ void NpcAnimation::addPartGroup(int group, int priority, std::vector<ESM::PartRe
bodypart = partStore.search(part.mMale);
if(bodypart)
addOrReplaceIndividualPart(part.mPart, group, priority,"meshes\\" + bodypart->mModel);
addOrReplaceIndividualPart(part.mPart, group, priority, "meshes\\"+bodypart->mModel);
else
reserveIndividualPart(part.mPart, group, priority);
}
}
void NpcAnimation::forceUpdate ()
{
updateParts();
}
}

@ -3,9 +3,6 @@
#include "animation.hpp"
#include "components/nifogre/ogre_nif_loader.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwclass/npc.hpp"
#include "../mwworld/containerstore.hpp"
namespace ESM
@ -13,49 +10,36 @@ namespace ESM
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:
MWWorld::InventoryStore *mInv;
static const size_t sPartListSize = 27;
static const PartInfo sPartList[sPartListSize];
int mStateID;
int mPartslots[27]; //Each part slot is taken by clothing, armor, or is empty
int mPartPriorities[27];
//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;
// Bounded Parts
NifOgre::EntityList mEntityParts[sPartListSize];
const ESM::NPC *mNpc;
std::string mHeadModel;
std::string mHairModel;
std::string mBodyPrefix;
float mTimeToChange;
MWWorld::ContainerStoreIterator mRobe;
MWWorld::ContainerStoreIterator mHelmet;
@ -72,18 +56,12 @@ private:
int mVisibilityFlags;
public:
NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node,
MWWorld::InventoryStore& inv, int visibilityFlags);
virtual ~NpcAnimation();
int mPartslots[sPartListSize]; //Each part slot is taken by clothing, armor, or is empty
int mPartPriorities[sPartListSize];
NifOgre::EntityList insertBoundedPart(const std::string &mesh, int group, const std::string &bonename);
virtual void runAnimation(float timepassed);
void updateParts();
void updateParts(MWWorld::InventoryStore &inventory) {
mInv = &inventory;
updateParts();
}
void updateParts(bool forceupdate = false);
void removeEntities(NifOgre::EntityList &entities);
void removeIndividualPart(int type);
@ -91,10 +69,19 @@ public:
bool addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh);
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

@ -34,9 +34,27 @@ void Objects::clearSceneNode (Ogre::SceneNode *node)
for (int i=node->numAttachedObjects()-1; i>=0; --i)
{
Ogre::MovableObject *object = node->getAttachedObject (i);
// for entities, destroy any objects attached to bones
if (object->getTypeFlags () == Ogre::SceneManager::ENTITY_TYPE_MASK)
{
Ogre::Entity* ent = static_cast<Ogre::Entity*>(object);
Ogre::Entity::ChildObjectListIterator children = ent->getAttachedObjectIterator ();
while (children.hasMoreElements())
{
mRenderer.getScene ()->destroyMovableObject (children.getNext ());
}
}
node->detachObject (object);
mRenderer.getScene()->destroyMovableObject (object);
}
Ogre::Node::ChildNodeIterator it = node->getChildIterator ();
while (it.hasMoreElements ())
{
clearSceneNode(static_cast<Ogre::SceneNode*>(it.getNext ()));
}
}
void Objects::setMwRoot(Ogre::SceneNode* root)
@ -87,13 +105,13 @@ void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool 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();
assert(insert);
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++)
{
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);
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];
insert->detachObject(ent);
sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale());
Ogre::Node *node = (*iter)->getParentNode();
sg->addEntity(*iter, node->_getDerivedPosition(), node->_getDerivedOrientation(), node->_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());
assert(insert);
Ogre::Light *light = mRenderer.getScene()->createLight();
light->setDiffuseColour (r, g, b);
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;
info.name = light->getName();
info.radius = radius;
@ -262,7 +294,17 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, f
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);
}
@ -501,10 +543,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;
MWWorld::CellStore *newCell = ptr.getCell();
MWWorld::CellStore *newCell = cur.getCell();
if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) {
node = mMwRoot->createChildSceneNode();
@ -512,6 +554,6 @@ void Objects::updateObjectCell(const MWWorld::Ptr &ptr)
} else {
node = mCellSceneNodes[newCell];
}
node->addChild(ptr.getRefData().getBaseNode());
node->addChild(cur.getRefData().getBaseNode());
}

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

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

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

@ -18,9 +18,12 @@
#include <extern/shiny/Main/Factory.hpp>
#include <extern/shiny/Platforms/Ogre/OgrePlatform.hpp>
#include <openengine/bullet/physic.hpp>
#include <components/esm/loadstat.hpp>
#include "../mwworld/esmstore.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/environment.hpp"
@ -260,8 +263,7 @@ void RenderingManager::removeObject (const MWWorld::Ptr& ptr)
void RenderingManager::moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position)
{
/// \todo move this to the rendering-subsystems
mRendering.getScene()->getSceneNode (ptr.getRefData().getHandle())->
setPosition (position);
ptr.getRefData().getBaseNode()->setPosition(position);
}
void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale)
@ -308,23 +310,19 @@ bool RenderingManager::rotateObject( const MWWorld::Ptr &ptr, Ogre::Vector3 &rot
}
void
RenderingManager::moveObjectToCell(
const MWWorld::Ptr& ptr,
const Ogre::Vector3& pos,
MWWorld::CellStore *store)
RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur)
{
Ogre::SceneNode *child =
mRendering.getScene()->getSceneNode(ptr.getRefData().getHandle());
mRendering.getScene()->getSceneNode(old.getRefData().getHandle());
Ogre::SceneNode *parent = child->getParentSceneNode();
parent->removeChild(child);
if (MWWorld::Class::get(ptr).isActor()) {
mActors.updateObjectCell(ptr);
if (MWWorld::Class::get(old).isActor()) {
mActors.updateObjectCell(old, cur);
} else {
mObjects.updateObjectCell(ptr);
mObjects.updateObjectCell(old, cur);
}
child->setPosition(pos);
}
void RenderingManager::update (float duration, bool paused)
@ -603,17 +601,6 @@ void RenderingManager::toggleLight()
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)
{
if (!mSunEnabled) return;
@ -944,6 +931,15 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend
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)
{
mVideoPlayer->playVideo ("video/" + name, allowSkipping);

@ -48,6 +48,7 @@ namespace MWRender
class ExternalRendering;
class GlobalMap;
class VideoPlayer;
class Animation;
class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener {
@ -122,9 +123,10 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
void setWaterHeight(const float height);
void toggleWater();
/// Moves object rendering part to proper container
/// \param store Cell the object was in previously (\a ptr has already been updated to the new cell).
void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::CellStore *store);
/// Updates object rendering after cell change
/// \param old Object reference in previous cell
/// \param cur Object reference in new cell
void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur);
void update (float duration, bool paused);
@ -170,18 +172,6 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
/// configure fog manually
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);
///< transform the specified bounding box (in world coordinates) into screen coordinates.
/// @return packed vector4 (min_x, min_y, max_x, max_y)
@ -200,6 +190,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
void setupExternalRendering (MWRender::ExternalRendering& rendering);
Animation* getAnimation(const MWWorld::Ptr &ptr);
void playVideo(const std::string& name, bool allowSkipping);
void stopVideo();

@ -285,7 +285,7 @@ void SkyManager::create()
// Stars
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++)
{
Entity* night1_ent = entities.mEntities[i];
@ -311,28 +311,34 @@ void SkyManager::create()
// Atmosphere (day)
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++)
{
Entity* atmosphere_ent = entities.mEntities[i];
atmosphere_ent->setCastShadows(false);
atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly);
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");
// Using infinite AAB here to prevent being clipped by the custom near clip plane used for reflections/refractions
atmosphere_ent->getMesh()->_setBounds (aabInf);
}
// Clouds
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++)
{
Entity* clouds_ent = entities.mEntities[i];
clouds_ent->setVisibilityFlags(RV_Sky);
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);
// Using infinite AAB here to prevent being clipped by the custom near clip plane used for reflections/refractions
clouds_ent->getMesh()->_setBounds (aabInf);
}

@ -9,7 +9,7 @@
#include <components/interpreter/runtime.hpp>
#include <components/interpreter/opcodes.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "interpretercontext.hpp"
#include "ref.hpp"
@ -27,7 +27,7 @@ namespace MWScript
{
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");
}
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");
}
MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, group, mode, loops);
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, loops);
}
};

@ -153,6 +153,10 @@ void FFmpeg_Decoder::open(const std::string &fname)
try
{
for(size_t j = 0;j < mFormatCtx->nb_streams;j++)
if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
mFormatCtx->streams[j]->codec->request_sample_fmt = AV_SAMPLE_FMT_S16;
if(avformat_find_stream_info(mFormatCtx, NULL) < 0)
fail("Failed to find stream info in "+fname);

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

@ -140,6 +140,9 @@ namespace MWWorld
virtual float getSpeed (const Ptr& ptr) const;
///< 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;
///< Return desired movement.

@ -9,6 +9,10 @@
#include <OgreCamera.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 "../mwbase/world.hpp" // FIXME
@ -21,23 +25,191 @@ using namespace Ogre;
namespace MWWorld
{
PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) :
mRender(_rend), mEngine(0), mFreeFly (true)
static const float sMaxSlope = 60.0f;
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
NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader();
mEngine = new OEngine::Physic::PhysicEngine(shapeLoader);
playerphysics->mEngine = mEngine;
}
PhysicsSystem::~PhysicsSystem()
{
delete mEngine;
delete playerphysics;
}
OEngine::Physic::PhysicEngine* PhysicsSystem::getEngine()
{
return mEngine;
@ -106,15 +278,7 @@ namespace MWWorld
void PhysicsSystem::setCurrentWater(bool hasWater, int waterHeight)
{
playerphysics->hasWater = hasWater;
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);
}
// TODO: store and use
}
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
//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);
return MovementSolver::move(ptr, movement, time, gravity, mEngine);
}
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,
int x, int y, float yoffset,
@ -291,46 +395,20 @@ namespace MWWorld
void PhysicsSystem::moveObject (const Ptr& ptr)
{
Ogre::SceneNode* node = ptr.getRefData().getBaseNode();
std::string handle = node->getName();
Ogre::Vector3 position = node->getPosition();
if (OEngine::Physic::RigidBody* body = mEngine->getRigidBody(handle))
{
// TODO very dirty hack to avoid crash during setup -> needs cleaning up to allow
// start positions others than 0, 0, 0
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);
}
}
Ogre::SceneNode *node = ptr.getRefData().getBaseNode();
const std::string &handle = node->getName();
const Ogre::Vector3 &position = node->getPosition();
if(OEngine::Physic::RigidBody *body = mEngine->getRigidBody(handle))
body->getWorldTransform().setOrigin(btVector3(position.x,position.y,position.z));
else if(OEngine::Physic::PhysicActor *physact = mEngine->getCharacter(handle))
physact->setPosition(position);
}
void PhysicsSystem::rotateObject (const Ptr& ptr)
{
Ogre::SceneNode* node = ptr.getRefData().getBaseNode();
std::string handle = node->getName();
Ogre::Quaternion rotation = node->getOrientation();
const std::string &handle = node->getName();
const Ogre::Quaternion &rotation = node->getOrientation();
if (OEngine::Physic::PhysicActor* act = mEngine->getCharacter(handle))
{
//Needs to be changed
@ -348,7 +426,7 @@ namespace MWWorld
void PhysicsSystem::scaleObject (const Ptr& ptr)
{
Ogre::SceneNode* node = ptr.getRefData().getBaseNode();
std::string handle = node->getName();
const std::string &handle = node->getName();
if(handleToMesh.find(handle) != handleToMesh.end())
{
removeObject(handle);
@ -361,7 +439,6 @@ namespace MWWorld
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++)
{
if (it->first=="player")
@ -372,12 +449,10 @@ namespace MWWorld
if(cmode)
{
act->enableCollisions(false);
mFreeFly = true;
return false;
}
else
{
mFreeFly = false;
act->enableCollisions(true);
return true;
}

@ -1,12 +1,27 @@
#ifndef GAME_MWWORLD_PHYSICSSYSTEM_H
#define GAME_MWWORLD_PHYSICSSYSTEM_H
#include <openengine/ogre/renderer.hpp>
#include "ptr.hpp"
#include <openengine/bullet/pmove.h>
#include <OgreVector3.h>
#include <btBulletCollisionCommon.h>
namespace OEngine
{
namespace Render
{
class OgreRenderer;
}
namespace Physic
{
class PhysicEngine;
}
}
namespace MWWorld
{
class World;
class Ptr;
class PhysicsSystem
{
@ -14,12 +29,6 @@ namespace MWWorld
PhysicsSystem (OEngine::Render::OgreRenderer &_rend);
~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 addActor (const MWWorld::Ptr& ptr);
@ -41,6 +50,8 @@ namespace MWWorld
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::vector < std::pair <float, std::string> > getFacedHandles (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::Physic::PhysicEngine* mEngine;
bool mFreeFly;
playerMove* playerphysics;
std::map<std::string, std::string> handleToMesh;
PhysicsSystem (const PhysicsSystem&);

@ -71,6 +71,12 @@ namespace MWWorld
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()
{
MWWorld::Ptr ptr = getPlayer();

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

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

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

@ -1,5 +1,7 @@
#include "worldimp.hpp"
#include <libs/openengine/bullet/physic.hpp>
#include <components/bsa/bsa_archive.hpp>
#include <components/files/collections.hpp>
#include <components/compiler/locals.hpp>
@ -10,6 +12,8 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/scriptmanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwrender/sky.hpp"
#include "../mwrender/player.hpp"
@ -18,6 +22,7 @@
#include "player.hpp"
#include "manualref.hpp"
#include "cellfunctors.hpp"
#include "containerstore.hpp"
using namespace Ogre;
@ -183,6 +188,8 @@ namespace MWWorld
mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine);
mPhysEngine->setSceneManager(renderer.getScene());
mWeatherManager = new MWWorld::WeatherManager(mRendering);
int idx = 0;
@ -732,6 +739,7 @@ namespace MWWorld
removeContainerScripts(ptr);
if (isPlayer)
{
if (!newCell.isExterior())
changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.mCell->mName), pos);
else
@ -740,6 +748,7 @@ namespace MWWorld
int cellY = newCell.mCell->getGridY();
mWorldScene->changeCell(cellX, cellY, pos, false);
}
}
else
{
if (!mWorldScene->isCellActive(*currCell))
@ -761,7 +770,10 @@ namespace MWWorld
MWWorld::Ptr copy =
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 =
MWWorld::Class::get(ptr).getScript(ptr);
@ -772,15 +784,6 @@ namespace MWWorld
mLocalScripts.add(script, copy);
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);
}
@ -875,53 +878,33 @@ namespace MWWorld
--cellY;
}
void World::doPhysics (const std::vector<std::pair<std::string, Ogre::Vector3> >& actors,
float duration)
void World::doPhysics(const PtrMovementList &actors, float duration)
{
mPhysics->doPhysics(duration, actors);
const int tick = 16; // 16 ms ^= 60 Hz
/* No duration? Shouldn't be any movement, then. */
if(duration <= 0.0f)
return;
// Game clock part of the loop, contains everything that has to be executed in a fixed timestep
long long dt = mTimer.getMilliseconds() - lastTick;
if (dt >= 100)
PtrMovementList::const_iterator player(actors.end());
for(PtrMovementList::const_iterator iter(actors.begin());iter != actors.end();iter++)
{
// throw away wall clock time if necessary to keep the framerate above the minimum of 10 fps
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(iter->first.getRefData().getHandle() == "player")
{
if (it->first=="player")
{
player = it;
}
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
/* Handle player last, in case a cell transition occurs */
player = iter;
continue;
}
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()
@ -989,17 +972,6 @@ namespace MWWorld
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)
{
mWorldScene->update (duration, paused);
@ -1420,20 +1392,30 @@ namespace MWWorld
}
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
float *fpos = object.getRefData().getPosition().pos;
Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]);
/// \fixme should rely on object height
pos.z += 30;
/// \fixme 3/4ths submerged?
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);
}
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)) {
return false;
@ -1441,6 +1423,13 @@ namespace MWWorld
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()
{
mRendering->renderPlayer(mPlayer->getPlayer());
@ -1455,24 +1444,21 @@ namespace MWWorld
{
Ptr::CellStore *currentCell = mWorldScene->getCurrentCell();
Ogre::Vector3 playerPos;
float* pos = mPlayer->getPlayer ().getRefData ().getPosition ().pos;
playerPos.x = pos[0];
playerPos.y = pos[1];
playerPos.z = pos[2];
RefData &refdata = mPlayer->getPlayer().getRefData();
Ogre::Vector3 playerPos(refdata.getPosition().pos);
std::pair<bool, Ogre::Vector3> hit =
mPhysics->castRay(playerPos, Ogre::Vector3(0,0,-1), 50);
bool isOnGround = (hit.first ? (hit.second.distance (playerPos) < 25) : false);
if (!isOnGround || isUnderwater (*currentCell->mCell, playerPos))
const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle());
if(!physactor->getOnGround() || isUnderwater(*currentCell->mCell, playerPos))
return 2;
if (currentCell->mCell->mData.mFlags & ESM::Cell::NoSleep)
if((currentCell->mCell->mData.mFlags&ESM::Cell::NoSleep))
return 1;
return 0;
}
MWRender::Animation* World::getAnimation(const MWWorld::Ptr &ptr)
{
return mRendering->getAnimation(ptr);
}
void World::playVideo (const std::string &name, bool allowSkipping)

@ -37,6 +37,7 @@ namespace MWRender
{
class SkyManager;
class CellRender;
class Animation;
}
namespace MWWorld
@ -264,8 +265,7 @@ namespace MWWorld
virtual void positionToIndex (float x, float y, int &cellX, int &cellY) const;
///< Convert position to cell numbers
virtual void doPhysics (const std::vector<std::pair<std::string, Ogre::Vector3> >& actors,
float duration);
virtual void doPhysics(const PtrMovementList &actors, float duration);
///< Run physics simulation and modify \a world accordingly.
virtual bool toggleCollisionMode();
@ -298,18 +298,6 @@ namespace MWWorld
/// \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 bool placeObject (const Ptr& object, float cursorX, float cursorY);
@ -326,8 +314,10 @@ namespace MWWorld
virtual void processChangedSettings(const Settings::CategorySettingVector& settings);
virtual bool isSwimming(const MWWorld::Ptr &object);
virtual bool isUnderwater(const ESM::Cell &cell, const Ogre::Vector3 &pos);
virtual bool isFlying(const MWWorld::Ptr &ptr) const;
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() {
mRendering->togglePOV();
@ -360,6 +350,9 @@ namespace MWWorld
/// 2 - player is underwater \n
/// 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
virtual void playVideo(const std::string& name, bool allowSkipping);
virtual void stopVideo();

@ -68,6 +68,7 @@ macro(find_component _component _pkgconfig _library _header)
find_path(${_component}_INCLUDE_DIRS ${_header}
HINTS
${FFMPEGSDK_INC}
${PC_LIB${_component}_INCLUDEDIR}
${PC_LIB${_component}_INCLUDE_DIRS}
PATH_SUFFIXES
@ -76,6 +77,7 @@ macro(find_component _component _pkgconfig _library _header)
find_library(${_component}_LIBRARIES NAMES ${_library}
HINTS
${FFMPEGSDK_LIB}
${PC_LIB${_component}_LIBDIR}
${PC_LIB${_component}_LIBRARY_DIRS}
)
@ -97,6 +99,12 @@ endmacro()
# Check for cached results. If there are skip the costly part.
if (NOT FFMPEG_LIBRARIES)
set (FFMPEGSDK ENV${FFMPEG_HOME})
if (FFMPEGSDK)
set (FFMPEGSDK_INC "${FFMPEGSDK}/include")
set (FFMPEGSDK_LIB "${FFMPEGSDK}/lib")
endif ()
# Check for all possible component.
find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h)
find_component(AVFORMAT libavformat avformat libavformat/avformat.h)

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

@ -21,8 +21,6 @@
*/
//loadResource->handleNode->handleNiTriShape->createSubMesh
#include "ogre_nif_loader.hpp"
#include <algorithm>
@ -35,6 +33,7 @@
#include <OgreSubMesh.h>
#include <OgreRoot.h>
#include <OgreEntity.h>
#include <OgreSubEntity.h>
#include <OgreTagPoint.h>
#include <boost/lexical_cast.hpp>
@ -53,9 +52,6 @@ typedef unsigned char ubyte;
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
ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&)
{ 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)
{
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)
{
TextKeyMap textkeys;
@ -172,13 +284,15 @@ static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk)
std::string::size_type pos = 0;
while(pos < str.length())
{
while(pos < str.length() && ::isspace(str[pos]))
if(::isspace(str[pos]))
{
pos++;
if(pos >= str.length())
break;
continue;
}
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;
}
@ -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;
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->setScale(Ogre::Vector3(node->trafo.scale));
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;
while(!ctrl.empty())
{
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;
}
Nif::ExtraPtr e = node->extra;
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());
bone->getUserObjectBindings().setUserAny("TextKeyExtraData", Ogre::Any(extractTextKeys(tk)));
textkeys = extractTextKeys(tk);
animroot = bone;
}
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++)
{
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;
static LoaderMap sLoaders;
@ -252,12 +365,20 @@ void loadResource(Ogre::Resource *resource)
Ogre::Skeleton *skel = dynamic_cast<Ogre::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 & nif = *pnif.get ();
const Nif::Node *node = dynamic_cast<const Nif::Node*>(nif.getRecord(0));
Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName()));
const Nif::Node *node = static_cast<const Nif::Node*>(nif->getRecord(0));
std::vector<Nif::NiKeyframeController const*> ctrls;
buildBones(skel, node, ctrls);
std::vector<const Nif::NiKeyframeController*> 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;
// 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;
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);
Nif::Named *target = dynamic_cast<Nif::Named*>(ctrl->target.getPtr());
if(target != NULL)
@ -281,149 +402,90 @@ void loadResource(Ogre::Resource *resource)
return;
}
Ogre::Animation *anim = skel->createAnimation(skel->getName(), maxtime);
/* 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++)
if(!animroot)
{
Nif::NiKeyframeController const *kfc = ctrls[i];
Nif::NiKeyframeData const *kf = kfc->data.getPtr();
/* 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();
warn(Ogre::StringConverter::toString(ctrls.size())+" animated node(s) in "+
skel->getName()+", but no text keys.");
return;
}
Ogre::Bone *bone = skel->getBone(targets[i]);
const Ogre::Quaternion startquat = bone->getInitialOrientation();
const Ogre::Vector3 starttrans = bone->getInitialPosition();
const Ogre::Vector3 startscale = bone->getInitialScale();
Ogre::NodeAnimationTrack *nodetrack = anim->getNodeTrack(bone->getHandle());
Ogre::UserObjectBindings &bindings = animroot->getUserObjectBindings();
bindings.setUserAny(sTextKeyExtraDataID, Ogre::Any(true));
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 = startquat.Inverse() * quatiter->mValue;
if(traniter != trankeys.mKeys.end())
lasttrans = curtrans = traniter->mValue - starttrans;
if(scaleiter != scalekeys.mKeys.end())
lastscale = curscale = Ogre::Vector3(scaleiter->mValue) / startscale;
bool didlast = false;
while(!didlast)
std::string currentgroup;
TextKeyMap::const_iterator keyiter = textkeys.begin();
for(keyiter = textkeys.begin();keyiter != textkeys.end();keyiter++)
{
std::string::size_type sep = keyiter->second.find(':');
if((sep == currentgroup.length() && keyiter->second.compare(0, sep, currentgroup) == 0) ||
(sep == sizeof("soundgen")-1 && keyiter->second.compare(0, sep, "soundgen") == 0) ||
(sep == sizeof("sound")-1 && keyiter->second.compare(0, sep, "sound") == 0))
continue;
currentgroup = keyiter->second.substr(0, sep);
if(skel->hasAnimation(currentgroup))
continue;
TextKeyMap::const_iterator lastkeyiter = textkeys.end();
while((--lastkeyiter)->first > keyiter->first)
{
float curtime = kfc->timeStop;
//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, 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));
}
if(lastkeyiter->second.find(':') == currentgroup.length() &&
lastkeyiter->second.compare(0, currentgroup.length(), currentgroup) == 0)
break;
}
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)
{
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();
Ogre::SkeletonPtr skel = skelMgr.getByName(name);
if(skel.isNull())
{
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(node->recType == Nif::RC_NiTriShape)
return false;
if(node->controller.empty() && node->name != "AttachLight")
{
if(!children[i].empty())
if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode)
{
if(createSkeleton(name, group, children[i].getPtr()))
return true;
const Nif::NiNode *ninode = static_cast<const Nif::NiNode*>(node);
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;
}
};
@ -701,12 +763,15 @@ static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String
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
{
std::string mName;
std::string mGroup;
size_t mShapeIndex;
std::string mSkelName;
std::string mMaterialName;
std::string mShapeName;
@ -734,12 +799,11 @@ class NIFMeshLoader : Ogre::ManualResourceLoader
{
// Only set a skeleton when skinning. Unskinned meshes with a skeleton will be
// explicitly attached later.
mesh->setSkeletonName(mSkelName);
mesh->setSkeletonName(mName);
// Get the skeleton resource, so vertices can be transformed into the bones' initial state.
Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
skel = skelMgr->getByName(mSkelName);
skel->touch();
skel = skelMgr->getByName(mName);
// 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
@ -752,12 +816,10 @@ class NIFMeshLoader : Ogre::ManualResourceLoader
for(size_t b = 0;b < bones.length();b++)
{
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),
Ogre::Quaternion(data->bones[b].trafo.rotation));
mat2.makeTransform(bone->_getDerivedPosition(), bone->_getDerivedScale(),
bone->_getDerivedOrientation());
mat = mat2 * mat;
mat = bone->_getFullTransform() * mat;
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights;
for(size_t i = 0;i < weights.size();i++)
@ -778,22 +840,26 @@ class NIFMeshLoader : Ogre::ManualResourceLoader
srcVerts = newVerts;
srcNorms = newNorms;
}
else if(mSkelName.length() == 0)
else
{
// No skinning and no skeleton, so just transform the vertices and
// normals into position.
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::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
if(skelMgr->getByName(mName).isNull())
{
Ogre::Vector4 vec4(srcNorms[i].x, srcNorms[i].y, srcNorms[i].z, 0.0f);
vec4 = mat4*vec4;
srcNorms[i] = Ogre::Vector3(&vec4[0]);
// No skinning and no skeleton, so just transform the vertices and
// normals into position.
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]);
}
}
}
@ -953,19 +1019,21 @@ class NIFMeshLoader : Ogre::ManualResourceLoader
public:
NIFMeshLoader()
{ }
NIFMeshLoader(const std::string &name, const std::string &group, const std::string skelName)
: mName(name), mGroup(group), mShapeIndex(~(size_t)0), mSkelName(skelName)
NIFMeshLoader(const std::string &name, const std::string &group)
: mName(name), mGroup(group), mShapeIndex(~(size_t)0)
{ }
virtual void loadResource(Ogre::Resource *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);
if(mShapeIndex >= nif->numRecords())
{
mesh->setSkeletonName(mSkelName);
Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
if(!skelMgr->getByName(mName).isNull())
mesh->setSkeletonName(mName);
return;
}
@ -973,7 +1041,7 @@ public:
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;
@ -986,7 +1054,6 @@ public:
while(!e.empty())
{
Nif::NiStringExtraData *sd;
Nif::NiTextKeyExtraData *td;
if((sd=dynamic_cast<Nif::NiStringExtraData*>(e.getPtr())) != NULL)
{
// String markers may contain important information
@ -998,16 +1065,10 @@ public:
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;
}
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);
mShapeName = shape->name;
@ -1016,8 +1077,6 @@ public:
std::string fullname = mName+"@index="+Ogre::StringConverter::toString(shape->recIndex);
if(mShapeName.length() > 0)
fullname += "@shape="+mShapeName;
if(mSkelName.length() > 0 && mName != mSkelName)
fullname += "@skel="+mSkelName;
Misc::StringUtils::toLower(fullname);
Ogre::MeshPtr mesh = meshMgr.getByName(fullname);
@ -1025,21 +1084,15 @@ public:
{
NIFMeshLoader *loader = &sLoaders[fullname];
*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->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);
if(ninode)
@ -1052,28 +1105,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;
typedef std::map<std::string,MeshPairList> MeshPairMap;
static MeshPairMap sMeshPairMap;
typedef std::map<std::string,MeshInfoList> MeshInfoMap;
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);
Misc::StringUtils::toLower(skelName);
MeshPairMap::const_iterator meshiter = sMeshPairMap.find(name+"@skel="+skelName);
if(meshiter != sMeshPairMap.end())
MeshInfoMap::const_iterator meshiter = sMeshInfoMap.find(name);
if(meshiter != sMeshInfoMap.end())
return meshiter->second;
MeshPairList &meshes = sMeshPairMap[name+"@skel="+skelName];
Nif::NIFFile::ptr pnif = Nif::NIFFile::create (name);
Nif::NIFFile &nif = *pnif.get ();
if (nif.numRecords() < 1)
MeshInfoList &meshes = sMeshInfoMap[name];
Nif::NIFFile::ptr pnif = Nif::NIFFile::create(name);
Nif::NIFFile &nif = *pnif.get();
if(nif.numRecords() < 1)
{
nif.warn("Found no records in NIF.");
nif.warn("Found no NIF records in "+name+".");
return meshes;
}
@ -1084,109 +1156,87 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::
Nif::Node const *node = dynamic_cast<Nif::Node const *>(r);
if(node == NULL)
{
nif.warn("First record in file was not a node, but a "+
r->recName+". Skipping file.");
nif.warn("First record in "+name+" was not a node, but a "+
r->recName+".");
return meshes;
}
NIFSkeletonLoader skelldr;
bool hasSkel = skelldr.createSkeleton(name, group, node);
bool hasSkel = Ogre::SkeletonManager::getSingleton().resourceExists(name);
if(!hasSkel)
{
NIFSkeletonLoader skelldr;
hasSkel = skelldr.createSkeleton(name, group, node);
}
NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string()));
meshldr.createMeshes(node, meshes);
NIFMeshLoader meshldr(name, group);
if(hasSkel)
meshldr.createEmptyMesh(node, meshes);
meshldr.createMeshes(node, meshes, 0);
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;
MeshPairList meshes = load(name, name, group);
Misc::StringUtils::toLower(name);
MeshInfoList meshes = load(name, group);
if(meshes.size() == 0)
return entitylist;
Ogre::SceneManager *sceneMgr = parent->getCreator();
Ogre::SceneManager *sceneMgr = parentNode->getCreator();
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();
if(!entitylist.mSkelBase && entity->hasSkeleton())
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)
{
parent->attachObject(entitylist.mSkelBase);
parentNode->attachObject(entitylist.mSkelBase);
for(size_t i = 0;i < entitylist.mEntities.size();i++)
{
Ogre::Entity *entity = entitylist.mEntities[i];
if(entity != entitylist.mSkelBase && entity->hasSkeleton())
{
entity->shareSkeletonInstanceWith(entitylist.mSkelBase);
parent->attachObject(entity);
parentNode->attachObject(entity);
}
else if(entity != entitylist.mSkelBase)
entitylist.mSkelBase->attachObjectToBone(meshes[i].second, entity);
entitylist.mSkelBase->attachObjectToBone(meshes[i].mTargetNode, entity);
}
}
else
{
for(size_t i = 0;i < entitylist.mEntities.size();i++)
parent->attachObject(entitylist.mEntities[i]);
parentNode->attachObject(entitylist.mEntities[i]);
}
return entitylist;
}
EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bonename,
Ogre::SceneNode *parentNode,
const std::string &name,
const std::string &group)
EntityList Loader::createEntities(Ogre::Entity *parent, const std::string &bonename,
Ogre::SceneNode *parentNode,
std::string name, const std::string &group)
{
EntityList entitylist;
MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), group);
Misc::StringUtils::toLower(name);
MeshInfoList meshes = load(name, group);
if(meshes.size() == 0)
return entitylist;
Ogre::SceneManager *sceneMgr = parentNode->getCreator();
std::string filter = "tri "+bonename;
std::transform(filter.begin()+4, filter.end(), filter.begin()+4, ::tolower);
std::string filter = "@shape=tri "+bonename;
Misc::StringUtils::toLower(filter);
for(size_t i = 0;i < meshes.size();i++)
{
Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first);
if(ent->hasSkeleton())
{
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;
}
Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].mMeshName);
if(!entitylist.mSkelBase && ent->hasSkeleton())
entitylist.mSkelBase = ent;
entitylist.mEntities.push_back(ent);
}
@ -1196,20 +1246,20 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo
if(entitylist.mSkelBase)
{
entitylist.mSkelBase->shareSkeletonInstanceWith(parent);
parentNode->attachObject(entitylist.mSkelBase);
for(size_t i = 0;i < entitylist.mEntities.size();i++)
{
Ogre::Entity *entity = entitylist.mEntities[i];
if(entity != entitylist.mSkelBase && entity->hasSkeleton())
if(entity->hasSkeleton())
{
entity->shareSkeletonInstanceWith(parent);
parentNode->attachObject(entity);
if(entity != entitylist.mSkelBase)
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);
tag->setScale(scale);
if(entity->getMesh()->getName().find(filter) != std::string::npos)
entitylist.mSkelBase->attachObjectToBone(meshes[i].mTargetNode, entity);
}
}
}
@ -1226,6 +1276,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
used in the first attempt at loading NIF meshes, where each submesh
in the file was given a separate bone in a skeleton. Unfortunately

@ -31,18 +31,14 @@
#include <vector>
#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
{
// FIXME: These should not be in NifOgre, it works agnostic of what model format is used
typedef std::multimap<float,std::string> TextKeyMap;
static const char sTextKeyExtraDataID[] = "TextKeyExtraData";
struct EntityList {
std::vector<Ogre::Entity*> mEntities;
Ogre::Entity *mSkelBase;
@ -52,39 +48,45 @@ struct EntityList {
};
/** This holds a list of mesh names along with the names of their parent nodes */
typedef std::vector< std::pair<std::string,std::string> > MeshPairList;
/** Manual resource loader for NIF meshes. This is the main class
responsible for translating the internal NIF mesh structure into
something Ogre can use.
You have to insert meshes manually into Ogre like this:
/* This holds a list of mesh names, the names of their parent nodes, and the offset
* from their parent nodes. */
struct MeshInfo {
std::string mMeshName;
std::string mTargetNode;
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
their parent nodes (as they pertain to the skeleton, which is optionally
returned in the second argument if it exists).
*/
class NIFLoader
class Loader
{
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:
static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename,
Ogre::SceneNode *parentNode,
const std::string &name,
std::string name,
const std::string &group="General");
static EntityList createEntities(Ogre::SceneNode *parent,
TextKeyMap *textkeys,
const std::string &name,
static EntityList createEntities(Ogre::SceneNode *parentNode,
std::string name,
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

@ -29,6 +29,7 @@ Jannik Heller (scrawl)
Jason Hooks (jhooks)
Joel Graff (graffy)
Karl-Felix Glatzer (k1ll)
Lars Söderberg (Lazaroth)
lazydev
Leon Saunders (emoose)
Lukasz Gromanowski (lgro)

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

@ -2,7 +2,6 @@
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include "pmove.h"
#include <components/nifbullet/bullet_nif_loader.hpp>
#include "CMotionState.h"
#include "OgreRoot.h"
@ -27,91 +26,48 @@ namespace Physic
COL_RAYCASTING = BIT(3)
};
PhysicActor::PhysicActor(std::string name, std::string mesh, PhysicEngine* engine, Ogre::Vector3 position, 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)
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), 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);
Ogre::Quaternion inverse = mBoxRotation.Inverse();
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
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()
{
if(mBody){
if(mBody)
{
mEngine->dynamicsWorld->removeRigidBody(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)
{
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;
pm_ref.rightmove = rightmove;
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;
if(pos != getPosition())
mEngine->adjustRigidBody(mBody, pos, getRotation(), mBoxScaledTranslation, mBoxRotation);
}
void PhysicActor::setRotation(const Ogre::Quaternion quat)
void PhysicActor::setRotation(const Ogre::Quaternion &quat)
{
if(!quat.equals(getRotation(), Ogre::Radian(0))){
mEngine->adjustRigidBody(mBody, getPosition(), quat, mBoxScaledTranslation, mBoxRotation);
}
}
Ogre::Vector3 PhysicActor::getPosition()
{
btVector3 vec = mBody->getWorldTransform().getOrigin();
@ -128,13 +84,6 @@ namespace Physic
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){
Ogre::Vector3 position = getPosition();
Ogre::Quaternion rotation = getRotation();
@ -148,18 +97,40 @@ namespace Physic
//Create the newly scaled rigid body
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
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;
}
Ogre::Vector3 PhysicActor::getHalfExtents() const
{
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(){
Pmove(pmove);
Ogre::Vector3 newpos = pmove->ps.origin;
mBody->getWorldTransform().setOrigin(btVector3(newpos.x, newpos.y, newpos.z));
bool PhysicActor::getOnGround() const
{
return collisionMode && onGround;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -221,10 +192,7 @@ namespace Physic
{
if(!isDebugCreated)
{
Ogre::SceneManagerEnumerator::SceneManagerIterator iter = Ogre::Root::getSingleton().getSceneManagerIterator();
iter.begin();
Ogre::SceneManager* scn = iter.getNext();
Ogre::SceneNode* node = scn->getRootSceneNode()->createChildSceneNode();
Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
node->pitch(Ogre::Degree(-90));
mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld);
dynamicsWorld->setDebugDrawer(mDebugDrawer);
@ -249,6 +217,11 @@ namespace Physic
return mDebugActive;
}
void PhysicEngine::setSceneManager(Ogre::SceneManager* sceneMgr)
{
mSceneMgr = sceneMgr;
}
PhysicEngine::~PhysicEngine()
{
HeightFieldContainer::iterator hf_it = mHeightFieldMap.begin();
@ -356,18 +329,21 @@ namespace Physic
mHeightFieldMap.erase(name);
}
void PhysicEngine::adjustRigidBody(RigidBody* body, Ogre::Vector3 position, Ogre::Quaternion rotation,
Ogre::Vector3 scaledBoxTranslation, Ogre::Quaternion boxRotation){
void PhysicEngine::adjustRigidBody(RigidBody* body, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
const Ogre::Vector3 &scaledBoxTranslation, const Ogre::Quaternion &boxRotation)
{
btTransform tr;
rotation = rotation * boxRotation;
Ogre::Vector3 transrot = rotation * scaledBoxTranslation;
Ogre::Quaternion boxrot = rotation * boxRotation;
Ogre::Vector3 transrot = boxrot * scaledBoxTranslation;
Ogre::Vector3 newPosition = transrot + position;
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);
}
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 outputstring = mesh + sid;
//std::cout << "The string" << outputstring << "\n";
@ -380,7 +356,8 @@ namespace Physic
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)
{
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);
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);
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);
if (it != ObjectMap.end() )
@ -497,15 +474,16 @@ namespace Physic
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)
{
mDebugDrawer->step();
}
}
void PhysicEngine::addCharacter(std::string name, std::string mesh,
Ogre::Vector3 position, float scale, Ogre::Quaternion rotation)
void PhysicEngine::addCharacter(const std::string &name, const std::string &mesh,
const Ogre::Vector3 &position, float scale, const Ogre::Quaternion &rotation)
{
// Remove character with given name, so we don't make memory
// leak when character would be added twice
@ -518,9 +496,8 @@ namespace Physic
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);
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);
if (it != PhysicActorMap.end() )

@ -18,13 +18,17 @@ class btSequentialImpulseConstraintSolver;
class btCollisionDispatcher;
class btDiscreteDynamicsWorld;
class btHeightfieldTerrainShape;
struct playerMove;
namespace BtOgre
{
class DebugDrawer;
}
namespace Ogre
{
class SceneManager;
}
namespace MWWorld
{
class World;
@ -61,33 +65,26 @@ namespace Physic
class PhysicActor
{
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();
void setCurrentWater(bool hasWater, int waterHeight);
/**
* This function sets the movement keys for pmove
*/
void setMovement(signed char rightmove, signed char forwardmove, signed char upmove);
void setPosition(const Ogre::Vector3 &pos);
/**
* This adjusts the rotation of a PhysicActor
* 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.
*/
void setRotation(const Ogre::Quaternion quat);
void setGravity(float gravity);
void setSpeed(float speed);
void setJumpVelocity(float velocity);
void setRotation(const Ogre::Quaternion &quat);
void enableCollisions(bool collision);
bool getCollisionMode();
bool getCollisionMode() const
{
return collisionMode;
}
/**
* This returns the visual position of the PhysicActor (used to position a scenenode).
@ -101,26 +98,28 @@ namespace Physic
Ogre::Quaternion getRotation();
/**
* Sets the position of mBody from a visual position input.
* For most cases this should not be used. We should instead let pmove move the PhysicActor around for us
* Sets the scale of the PhysicActor
*/
void setPosition(const Ogre::Vector3 pos);
void setScale(float scale);
/**
* Sets the view angles for pmove directly.
* Remember, add 90 for yaw. Set roll to 0.
* Returns the half extents for this PhysiActor
*/
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
// must be 16, but the built in operator new & delete don't properly
@ -136,12 +135,12 @@ namespace Physic
Ogre::Vector3 mBoxScaledTranslation;
btQuaternion mBoxRotationInverse;
Ogre::Quaternion mBoxRotation;
float verticalForce;
bool onGround;
bool collisionMode;
std::string mMesh;
PhysicEngine* mEngine;
std::string mName;
playerMove* pmove;
};
/**
@ -190,19 +189,21 @@ namespace Physic
* Creates a RigidBody. It does not add it to the simulation.
* 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);
/**
* Adjusts a rigid body to the right position and rotation
*/
void adjustRigidBody(RigidBody* body, Ogre::Vector3 position, Ogre::Quaternion rotation,
Ogre::Vector3 scaledBoxTranslation = Ogre::Vector3::ZERO, Ogre::Quaternion boxRotation = Ogre::Quaternion::IDENTITY);
void adjustRigidBody(RigidBody* body, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
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.
*/
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
*/
@ -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.
*/
void removeRigidBody(std::string name);
void removeRigidBody(const std::string &name);
/**
* 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.
* 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.
*/
void addCharacter(std::string name, std::string mesh,
Ogre::Vector3 position, float scale, Ogre::Quaternion rotation);
void addCharacter(const std::string &name, const std::string &mesh,
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?
*/
void removeCharacter(std::string name);
void removeCharacter(const std::string &name);
/**
* Return a pointer to a character
* 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.
@ -279,6 +280,8 @@ namespace Physic
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).
*/
@ -315,11 +318,12 @@ namespace Physic
typedef std::map<std::string, PhysicActor*> PhysicActorContainer;
PhysicActorContainer PhysicActorMap;
Ogre::SceneManager* mSceneMgr;
//debug rendering
BtOgre::DebugDrawer* mDebugDrawer;
bool isDebugCreated;
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 <map>
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>
#include "physic.hpp"
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
enum traceWorldType
{
//static float lastyaw = 0.0f;
//static float lastpitch = 0.0f;
//if (!traceobj)
// 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;
collisionWorldTrace = 1,
pickWorldTrace = 2,
bothWorldTrace = collisionWorldTrace | pickWorldTrace
};
//results->allsolid = out.startSolid;
// 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)
enum collaborativePhysicsType
{
//if (!traceobj->incellptr)
// return false;
//if(enginePass->dynamicsWorld->getCollisionObjectArray().at(60)->getCollisionShape()->isConvex())
// std::cout << "It's convex\n";
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)
};
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(rotation.y, rotation.x, rotation.z); //y, x, 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 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 btCapsuleShapeZ newshape(BBHalfExtents.x, BBHalfExtents.z * 2 - BBHalfExtents.x * 2);
const btTransform from(btrot, btstart);
const btTransform to(btrot, btend);
// warning: unused variable ...
/*
float x = from.getOrigin().getX();
float y = from.getOrigin().getY();
float z = from.getOrigin().getZ();
float x2 = to.getOrigin().getX();
float y2 = to.getOrigin().getY();
float z2 = to.getOrigin().getZ();
*/
//std::cout << "BtFrom: " << x << "," << y << "," << z << "\n";
//std::cout << "BtTo: " << x2 << "," << y2 << "," << z2 << "\n";
//std::cout << "BtTo: " << to.getOrigin().getX() << "," << to.getOrigin().getY() << "," << to.getOrigin().getZ() << "\n";
btCollisionWorld::ClosestConvexResultCallback
newTraceCallback(btstart, btend);
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;
//const btCapsuleShapeZ newshape(BBHalfExtents.x, BBHalfExtents.z * 2 - BBHalfExtents.x * 2);
const btTransform from(btrot, btstart);
const btTransform to(btrot, btend);
btCollisionWorld::ClosestConvexResultCallback newTraceCallback(btstart, btend);
newTraceCallback.m_collisionFilterMask = Only_Collision;
enginePass->dynamicsWorld->convexSweepTest(&newshape, from, to, newTraceCallback);
// Copy the hit data over to our trace results struct:
if(newTraceCallback.hasHit())
{
const btVector3& tracehitnormal = newTraceCallback.m_hitNormalWorld;
results->fraction = newTraceCallback.m_closestHitFraction;
results->planenormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z());
results->endpos = (end-start)*results->fraction + start;
}
else
{
results->endpos = end;
results->planenormal = Ogre::Vector3(0.0f, 0.0f, 1.0f);
results->fraction = 1.0f;
}
}

@ -1,60 +1,26 @@
#ifndef 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"
enum traceWorldType
namespace OEngine
{
collisionWorldTrace = 1,
pickWorldTrace = 2,
bothWorldTrace = collisionWorldTrace | pickWorldTrace
};
namespace Physic
{
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
{
Ogre::Vector3 endpos;
Ogre::Vector3 planenormal;
Ogre::Vector3 endpos;
Ogre::Vector3 planenormal;
float fraction;
int surfaceFlags;
int contents;
int entityNum;
bool allsolid;
bool startsolid;
float fraction;
};
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);
void newtrace(traceResults *results, const Ogre::Vector3& start, const Ogre::Vector3& end, const Ogre::Vector3& BBHalfExtents, bool isInterior, OEngine::Physic::PhysicEngine* enginePass);
#endif

Loading…
Cancel
Save