Merge branch 'master' into HEAD

actorid
scrawl 12 years ago
commit 2a42556aa5

@ -96,15 +96,15 @@ bool GameSettings::readFile(QTextStream &stream)
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
while (!stream.atEnd()) { while (!stream.atEnd()) {
QString line = stream.readLine().simplified(); QString line = stream.readLine();
if (line.isEmpty() || line.startsWith("#")) if (line.isEmpty() || line.startsWith("#"))
continue; continue;
if (keyRe.indexIn(line) != -1) { if (keyRe.indexIn(line) != -1) {
QString key = keyRe.cap(1).simplified(); QString key = keyRe.cap(1);
QString value = keyRe.cap(2).simplified(); QString value = keyRe.cap(2);
// Don't remove existing data entries // Don't remove existing data entries
if (key != QLatin1String("data")) if (key != QLatin1String("data"))

@ -52,7 +52,7 @@ public:
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
while (!stream.atEnd()) { while (!stream.atEnd()) {
QString line = stream.readLine().simplified(); QString line = stream.readLine();
if (line.isEmpty() || line.startsWith("#")) if (line.isEmpty() || line.startsWith("#"))
continue; continue;
@ -65,8 +65,8 @@ public:
if (keyRe.indexIn(line) != -1) { if (keyRe.indexIn(line) != -1) {
QString key = keyRe.cap(1).simplified(); QString key = keyRe.cap(1);
QString value = keyRe.cap(2).simplified(); QString value = keyRe.cap(2);
if (!sectionPrefix.isEmpty()) if (!sectionPrefix.isEmpty())
key.prepend(sectionPrefix); key.prepend(sectionPrefix);

@ -14,7 +14,7 @@ set(GAME_HEADER
source_group(game FILES ${GAME} ${GAME_HEADER}) source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender add_openmw_dir (mwrender
renderingmanager debugging sky player animation npcanimation creatureanimation activatoranimation renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows
compositors characterpreview externalrendering globalmap videoplayer ripplesimulation refraction compositors characterpreview externalrendering globalmap videoplayer ripplesimulation refraction
) )
@ -54,7 +54,7 @@ add_openmw_dir (mwworld
containerstore actiontalk actiontake manualref player cellfunctors failedaction containerstore actiontalk actiontake manualref player cellfunctors failedaction
cells localscripts customdata weather inventorystore ptr actionopen actionread cells localscripts customdata weather inventorystore ptr actionopen actionread
actionequip timestamp actionalchemy cellstore actionapply actioneat actionequip timestamp actionalchemy cellstore actionapply actioneat
esmstore store recordcmp fallback actionrepair actionsoulgem livecellref esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
) )
add_openmw_dir (mwclass add_openmw_dir (mwclass

@ -368,7 +368,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins, mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins,
mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap, mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap,
mActivationDistanceOverride)); mActivationDistanceOverride));
MWBase::Environment::get().getWorld()->setupPlayer(mNewGame); MWBase::Environment::get().getWorld()->setupPlayer();
//Load translation data //Load translation data
mTranslationDataStorage.setEncoder(mEncoder); mTranslationDataStorage.setEncoder(mEncoder);

@ -99,6 +99,9 @@ namespace MWBase
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0; float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0;
///< Perform a persuasion action on NPC ///< Perform a persuasion action on NPC
virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;
///< Forces an object to refresh its animation state.
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0; 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 ///< Run animation for a MW-reference. Calls to this function for references that are currently not
/// in the scene should be ignored. /// in the scene should be ignored.

@ -230,6 +230,8 @@ namespace MWBase
virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0; virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0;
virtual void localRotateObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0;
virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) = 0; virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) = 0;
///< place an object in a "safe" location (ie not in the void, etc). ///< place an object in a "safe" location (ie not in the void, etc).
@ -315,15 +317,20 @@ namespace MWBase
virtual void togglePOV() = 0; virtual void togglePOV() = 0;
virtual void togglePreviewMode(bool enable) = 0; virtual void togglePreviewMode(bool enable) = 0;
virtual bool toggleVanityMode(bool enable, bool force) = 0; virtual bool toggleVanityMode(bool enable) = 0;
virtual void allowVanityMode(bool allow) = 0; virtual void allowVanityMode(bool allow) = 0;
virtual void togglePlayerLooking(bool enable) = 0; virtual void togglePlayerLooking(bool enable) = 0;
virtual void changeVanityModeScale(float factor) = 0; virtual void changeVanityModeScale(float factor) = 0;
virtual bool vanityRotateCamera(float * rot) = 0; virtual bool vanityRotateCamera(float * rot) = 0;
virtual void setupPlayer(bool newGame) = 0; virtual void setupPlayer() = 0;
virtual void renderPlayer() = 0; virtual void renderPlayer() = 0;
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door) = 0;
///< if activated, should this door be opened or closed?
virtual void activateDoor(const MWWorld::Ptr& door) = 0;
///< activate (open or close) an non-teleport door
virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0;
virtual int canRest() = 0; virtual int canRest() = 0;

@ -375,12 +375,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mArmors.insert(*ref), &cell); return MWWorld::Ptr(&cell.mArmors.insert(*ref), &cell);
} }
short Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const float Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Armor> *ref = MWWorld::LiveCellRef<ESM::Armor> *ref =
ptr.get<ESM::Armor>(); ptr.get<ESM::Armor>();
return ref->mBase->mData.mEnchant; return ref->mBase->mData.mEnchant/10.f;
} }
bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const

@ -77,7 +77,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
}; };

@ -176,12 +176,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mBooks.insert(*ref), &cell); return MWWorld::Ptr(&cell.mBooks.insert(*ref), &cell);
} }
short Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const float Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Book> *ref = MWWorld::LiveCellRef<ESM::Book> *ref =
ptr.get<ESM::Book>(); ptr.get<ESM::Book>();
return ref->mBase->mData.mEnchant; return ref->mBase->mData.mEnchant/10.f;
} }
bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const

@ -58,7 +58,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
}; };

@ -295,12 +295,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mClothes.insert(*ref), &cell); return MWWorld::Ptr(&cell.mClothes.insert(*ref), &cell);
} }
short Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const float Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Clothing> *ref = MWWorld::LiveCellRef<ESM::Clothing> *ref =
ptr.get<ESM::Clothing>(); ptr.get<ESM::Clothing>();
return ref->mBase->mData.mEnchant; return ref->mBase->mData.mEnchant/10.f;
} }
bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const

@ -71,7 +71,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
}; };

@ -12,6 +12,7 @@
#include "../mwworld/nullaction.hpp" #include "../mwworld/nullaction.hpp"
#include "../mwworld/failedaction.hpp" #include "../mwworld/failedaction.hpp"
#include "../mwworld/actionteleport.hpp" #include "../mwworld/actionteleport.hpp"
#include "../mwworld/actiondoor.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwworld/physicssystem.hpp" #include "../mwworld/physicssystem.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
@ -71,7 +72,7 @@ namespace MWClass
ptr.get<ESM::Door>(); ptr.get<ESM::Door>();
const std::string &openSound = ref->mBase->mOpenSound; const std::string &openSound = ref->mBase->mOpenSound;
//const std::string &closeSound = ref->mBase->closeSound; const std::string &closeSound = ref->mBase->mCloseSound;
const std::string lockedSound = "LockedDoor"; const std::string lockedSound = "LockedDoor";
const std::string trapActivationSound = "Disarm Trap Fail"; const std::string trapActivationSound = "Disarm Trap Fail";
@ -139,12 +140,11 @@ namespace MWClass
else else
{ {
// animated door // animated door
// TODO return action for rotating the door boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionDoor(ptr));
if (MWBase::Environment::get().getWorld()->getOpenOrCloseDoor(ptr))
// This is a little pointless, but helps with testing action->setSound(openSound);
boost::shared_ptr<MWWorld::Action> action(new MWWorld::NullAction); else
action->setSound(closeSound);
action->setSound(openSound);
return action; return action;
} }

@ -181,4 +181,12 @@ namespace MWClass
{ {
return npcServices & ESM::NPC::Picks; return npcServices & ESM::NPC::Picks;
} }
int Lockpick::getItemMaxHealth (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Lockpick> *ref =
ptr.get<ESM::Lockpick>();
return ref->mBase->mData.mUses;
}
} }

@ -59,6 +59,9 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const;
///< Return item max health or throw an exception, if class does not have item health
}; };
} }

@ -180,4 +180,12 @@ namespace MWClass
{ {
return npcServices & ESM::NPC::Probes; return npcServices & ESM::NPC::Probes;
} }
int Probe::getItemMaxHealth (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Probe> *ref =
ptr.get<ESM::Probe>();
return ref->mBase->mData.mUses;
}
} }

@ -59,6 +59,9 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
virtual int getItemMaxHealth (const MWWorld::Ptr& ptr) const;
///< Return item max health or throw an exception, if class does not have item health
}; };
} }

@ -428,12 +428,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mWeapons.insert(*ref), &cell); return MWWorld::Ptr(&cell.mWeapons.insert(*ref), &cell);
} }
short Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const float Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Weapon> *ref = MWWorld::LiveCellRef<ESM::Weapon> *ref =
ptr.get<ESM::Weapon>(); ptr.get<ESM::Weapon>();
return ref->mBase->mData.mEnchant; return ref->mBase->mData.mEnchant/10.f;
} }
bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const

@ -79,7 +79,7 @@ namespace MWClass
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
}; };
} }

@ -3,7 +3,7 @@
#include <string> #include <string>
namespace MWWorld namespace MWWorld
{ {
struct ESMStore; struct ESMStore;
} }
@ -27,7 +27,7 @@ namespace MWDialogue
static std::string idFromIndex (const std::string& topic, int index); static std::string idFromIndex (const std::string& topic, int index);
}; };
/// \biref A quest entry with a timestamp. /// \brief A quest entry with a timestamp.
struct StampedJournalEntry : public JournalEntry struct StampedJournalEntry : public JournalEntry
{ {
int mDay; int mDay;

@ -397,11 +397,24 @@ namespace MWGui
void Console::setSelectedObject(const MWWorld::Ptr& object) void Console::setSelectedObject(const MWWorld::Ptr& object)
{ {
mPtr = object; if (!object.isEmpty())
if (!mPtr.isEmpty()) {
setTitle("#{sConsoleTitle} (" + mPtr.getCellRef().mRefID + ")"); if (object == mPtr)
{
setTitle("#{sConsoleTitle}");
mPtr=MWWorld::Ptr();
}
else
{
setTitle("#{sConsoleTitle} (" + object.getCellRef().mRefID + ")");
mPtr = object;
}
}
else else
{
setTitle("#{sConsoleTitle}"); setTitle("#{sConsoleTitle}");
mPtr = MWWorld::Ptr();
}
MyGUI::InputManager::getInstance().setKeyFocusWidget(command); MyGUI::InputManager::getInstance().setKeyFocusWidget(command);
} }

@ -57,8 +57,9 @@ namespace MWGui
void EnchantingDialog::updateLabels() void EnchantingDialog::updateLabels()
{ {
mEnchantmentPoints->setCaption(boost::lexical_cast<std::string>(mEnchanting.getEnchantCost()) std::stringstream enchantCost;
+ " / " + boost::lexical_cast<std::string>(mEnchanting.getMaxEnchantValue())); enchantCost << std::setprecision(1) << std::fixed << mEnchanting.getEnchantCost();
mEnchantmentPoints->setCaption(enchantCost.str() + " / " + boost::lexical_cast<std::string>(mEnchanting.getMaxEnchantValue()));
mCharge->setCaption(boost::lexical_cast<std::string>(mEnchanting.getGemCharge())); mCharge->setCaption(boost::lexical_cast<std::string>(mEnchanting.getGemCharge()));

@ -921,14 +921,16 @@ namespace MWGui
const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>() const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>()
.find(MWWorld::Class::get(item).getEnchantment(item)); .find(MWWorld::Class::get(item).getEnchantment(item));
int chargePercent = item.getCellRef().mEnchantmentCharge / static_cast<float>(ench->mData.mCharge) * 100; int chargePercent = (item.getCellRef().mEnchantmentCharge == -1) ? 100
: (item.getCellRef().mEnchantmentCharge / static_cast<float>(ench->mData.mCharge) * 100);
mHud->setSelectedEnchantItem(item, chargePercent); mHud->setSelectedEnchantItem(item, chargePercent);
mSpellWindow->setTitle(MWWorld::Class::get(item).getName(item)); mSpellWindow->setTitle(MWWorld::Class::get(item).getName(item));
} }
void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item) void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item)
{ {
int durabilityPercent = item.getCellRef().mCharge / static_cast<float>(MWWorld::Class::get(item).getItemMaxHealth(item)) * 100; int durabilityPercent = (item.getCellRef().mCharge == -1) ? 100
: (item.getCellRef().mCharge / static_cast<float>(MWWorld::Class::get(item).getItemMaxHealth(item)) * 100);
mHud->setSelectedWeapon(item, durabilityPercent); mHud->setSelectedWeapon(item, durabilityPercent);
mInventoryWindow->setTitle(MWWorld::Class::get(item).getName(item)); mInventoryWindow->setTitle(MWWorld::Class::get(item).getName(item));
} }
@ -1113,6 +1115,13 @@ namespace MWGui
{ {
mLoadingScreen->loadingDone (); mLoadingScreen->loadingDone ();
} }
bool WindowManager::getRestEnabled()
{
//Enable rest dialogue if character creation finished
if(mRestAllowed==false && MWBase::Environment::get().getWorld()->getGlobalVariable ("chargenstate").mFloat==-1)
mRestAllowed=true;
return mRestAllowed;
}
bool WindowManager::getPlayerSleeping () bool WindowManager::getPlayerSleeping ()
{ {

@ -220,7 +220,7 @@ namespace MWGui
virtual void loadingDone(); virtual void loadingDone();
virtual void enableRest() { mRestAllowed = true; } virtual void enableRest() { mRestAllowed = true; }
virtual bool getRestEnabled() { return mRestAllowed; } virtual bool getRestEnabled();
virtual bool getPlayerSleeping(); virtual bool getPlayerSleeping();
virtual void wakeUpPlayer(); virtual void wakeUpPlayer();

@ -679,7 +679,8 @@ namespace MWInput
void InputManager::quickKey (int index) void InputManager::quickKey (int index)
{ {
mWindows.activateQuickKey (index); if (!mWindows.isGuiMode())
mWindows.activateQuickKey (index);
} }
void InputManager::showQuickKeysMenu() void InputManager::showQuickKeysMenu()
@ -719,19 +720,17 @@ namespace MWInput
void InputManager::resetIdleTime() void InputManager::resetIdleTime()
{ {
if (mTimeIdle < 0) { if (mTimeIdle < 0)
MWBase::Environment::get().getWorld()->toggleVanityMode(false, false); MWBase::Environment::get().getWorld()->toggleVanityMode(false);
}
mTimeIdle = 0.f; mTimeIdle = 0.f;
} }
void InputManager::updateIdleTime(float dt) void InputManager::updateIdleTime(float dt)
{ {
if (mTimeIdle >= 0.f) { if (mTimeIdle >= 0.f)
mTimeIdle += dt; mTimeIdle += dt;
}
if (mTimeIdle > 30.f) { if (mTimeIdle > 30.f) {
MWBase::Environment::get().getWorld()->toggleVanityMode(true, false); MWBase::Environment::get().getWorld()->toggleVanityMode(true);
mTimeIdle = -1.f; mTimeIdle = -1.f;
} }
} }

@ -290,6 +290,13 @@ namespace MWMechanics
return 0; return 0;
} }
void Actors::forceStateUpdate(const MWWorld::Ptr & ptr)
{
PtrControllerMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
iter->second.forceStateUpdate();
}
void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
{ {
PtrControllerMap::iterator iter = mActors.find(ptr); PtrControllerMap::iterator iter = mActors.find(ptr);

@ -78,6 +78,8 @@ namespace MWMechanics
int countDeaths (const std::string& id) const; int countDeaths (const std::string& id) const;
///< Return the number of deaths for actors with the given ID. ///< Return the number of deaths for actors with the given ID.
void forceStateUpdate(const MWWorld::Ptr &ptr);
void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
void skipAnimation(const MWWorld::Ptr& ptr); void skipAnimation(const MWWorld::Ptr& ptr);
}; };

@ -28,6 +28,7 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -103,14 +104,13 @@ static void getStateInfo(CharacterState state, std::string *group)
CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop) CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop)
: mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false) : mPtr(ptr), mAnimation(anim), mCharState(state), mSkipAnim(false), mMovingAnim(false), mSecondsOfRunning(0), mSecondsOfSwimming(0)
{ {
if(!mAnimation) if(!mAnimation)
return; return;
mAnimation->setController(this); std::string group;
getStateInfo(mCharState, &group);
getStateInfo(mState, &mCurrentGroup);
if(MWWorld::Class::get(mPtr).isActor()) if(MWWorld::Class::get(mPtr).isActor())
{ {
/* Accumulate along X/Y only for now, until we can figure out how we should /* Accumulate along X/Y only for now, until we can figure out how we should
@ -122,19 +122,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
/* Don't accumulate with non-actors. */ /* Don't accumulate with non-actors. */
mAnimation->setAccumulation(Ogre::Vector3(0.0f)); mAnimation->setAccumulation(Ogre::Vector3(0.0f));
} }
if(mAnimation->hasAnimation(mCurrentGroup)) if(mAnimation->hasAnimation(group))
mAnimation->play(mCurrentGroup, "stop", "stop", loop); mMovingAnim = mAnimation->play(group, "start", "stop", 1.0f, loop ? (~(size_t)0) : 0);
}
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() CharacterController::~CharacterController()
@ -148,31 +137,6 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
} }
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;
}
void CharacterController::update(float duration, Movement &movement) void CharacterController::update(float duration, Movement &movement)
{ {
float speed = 0.0f; float speed = 0.0f;
@ -189,6 +153,29 @@ void CharacterController::update(float duration, Movement &movement)
const Ogre::Vector3 &rot = cls.getRotationVector(mPtr); const Ogre::Vector3 &rot = cls.getRotationVector(mPtr);
speed = cls.getSpeed(mPtr); speed = cls.getSpeed(mPtr);
// advance athletics
if (vec.squaredLength() > 0 && mPtr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer())
{
if (inwater)
{
mSecondsOfSwimming += duration;
while (mSecondsOfSwimming > 1)
{
MWWorld::Class::get(mPtr).skillUsageSucceeded(mPtr, ESM::Skill::Athletics, 1);
mSecondsOfSwimming -= 1;
}
}
else if (isrunning)
{
mSecondsOfRunning += duration;
while (mSecondsOfRunning > 1)
{
MWWorld::Class::get(mPtr).skillUsageSucceeded(mPtr, ESM::Skill::Athletics, 0);
mSecondsOfRunning -= 1;
}
}
}
/* FIXME: The state should be set to Jump, and X/Y movement should be disallowed except /* 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). */ * for the initial thrust (which would be carried by "physics" until landing). */
if(onground && vec.z > 0.0f) if(onground && vec.z > 0.0f)
@ -214,11 +201,13 @@ void CharacterController::update(float duration, Movement &movement)
if(vec.x > 0.0f) if(vec.x > 0.0f)
setState(inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight) setState(inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight)
: (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight)), true); : (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight)), true);
else if(vec.x < 0.0f) else if(vec.x < 0.0f)
setState(inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft) setState(inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft)
: (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft)), true); : (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft)), true);
// If this animation isn't moving us sideways, do it manually
if(!mMovingAnim)
movement.mPosition[0] += vec.x * (speed*duration);
// Apply any forward/backward movement manually // Apply any forward/backward movement manually
movement.mPosition[1] += vec.y * (speed*duration); movement.mPosition[1] += vec.y * (speed*duration);
} }
@ -227,12 +216,15 @@ void CharacterController::update(float duration, Movement &movement)
if(vec.y > 0.0f) if(vec.y > 0.0f)
setState(inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward) setState(inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward)
: (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward)), true); : (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward)), true);
else if(vec.y < 0.0f) else if(vec.y < 0.0f)
setState(inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack) setState(inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack)
: (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack)), true); : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack)), true);
// Apply any sideways movement manually // Apply any sideways movement manually
movement.mPosition[0] += vec.x * (speed*duration); movement.mPosition[0] += vec.x * (speed*duration);
// If this animation isn't moving us forward/backward, do it manually
if(!mMovingAnim)
movement.mPosition[1] += vec.y * (speed*duration);
} }
else if(rot.z != 0.0f && !inwater && !sneak) else if(rot.z != 0.0f && !inwater && !sneak)
{ {
@ -241,8 +233,18 @@ void CharacterController::update(float duration, Movement &movement)
else if(rot.z < 0.0f) else if(rot.z < 0.0f)
setState(CharState_TurnLeft, true); setState(CharState_TurnLeft, true);
} }
else if(mAnimQueue.size() == 0) else if(getState() != CharState_SpecialIdle || !mAnimation->isPlaying(0))
setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true); {
if(mAnimQueue.size() == 0)
setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true);
else
{
mMovingAnim = mAnimation->play(mAnimQueue.front().first,
"start", "stop", 0.0f,
mAnimQueue.front().second);
mAnimQueue.pop_front();
}
}
movement.mRotation[0] += rot.x * duration; movement.mRotation[0] += rot.x * duration;
movement.mRotation[1] += rot.y * duration; movement.mRotation[1] += rot.y * duration;
@ -268,20 +270,17 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int
else else
{ {
count = std::max(count, 1); count = std::max(count, 1);
if(mode != 0 || mAnimQueue.size() == 0) if(mode != 0 || getState() != CharState_SpecialIdle)
{ {
mAnimQueue.clear(); mAnimQueue.clear();
while(count-- > 0) mCharState = CharState_SpecialIdle;
mAnimQueue.push_back(groupname); mLooping = false;
mCurrentGroup = groupname; mMovingAnim = mAnimation->play(groupname, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1);
mState = CharState_SpecialIdle;
mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", false);
} }
else if(mode == 0) else if(mode == 0)
{ {
mAnimQueue.resize(1); mAnimQueue.clear();
while(count-- > 0) mAnimQueue.push_back(std::make_pair(groupname, count-1));
mAnimQueue.push_back(groupname);
} }
} }
} }
@ -294,25 +293,24 @@ void CharacterController::skipAnim()
void CharacterController::setState(CharacterState state, bool loop) void CharacterController::setState(CharacterState state, bool loop)
{ {
if(mState == state) if(mCharState == state)
{
if(mAnimation)
mAnimation->setLooping(loop);
return; return;
} mCharState = state;
mState = state; mLooping = loop;
forceStateUpdate();
}
void CharacterController::forceStateUpdate()
{
if(!mAnimation) if(!mAnimation)
return; return;
mAnimQueue.clear(); mAnimQueue.clear();
std::string anim; std::string anim;
getStateInfo(mState, &anim); getStateInfo(mCharState, &anim);
if(mAnimation->hasAnimation(anim)) if((mMovingAnim=mAnimation->hasAnimation(anim)) != false)
{ mMovingAnim = mAnimation->play(anim, "start", "stop", 0.0f, mLooping ? (~(size_t)0) : 0);
mCurrentGroup = anim;
mAnimation->play(mCurrentGroup, "start", "stop", loop);
}
} }
} }

@ -72,22 +72,21 @@ class CharacterController
MWWorld::Ptr mPtr; MWWorld::Ptr mPtr;
MWRender::Animation *mAnimation; MWRender::Animation *mAnimation;
typedef std::deque<std::string> AnimationQueue; typedef std::deque<std::pair<std::string,size_t> > AnimationQueue;
AnimationQueue mAnimQueue; AnimationQueue mAnimQueue;
std::string mCurrentGroup; CharacterState mCharState;
CharacterState mState; bool mLooping;
bool mSkipAnim; bool mSkipAnim;
protected: // counted for skill increase
/* Called by the animation whenever a new text key is reached. */ float mSecondsOfSwimming;
void markerEvent(float time, const std::string &evt); float mSecondsOfRunning;
friend class MWRender::Animation; bool mMovingAnim;
public: public:
CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop); CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop);
CharacterController(const CharacterController &rhs);
virtual ~CharacterController(); virtual ~CharacterController();
void updatePtr(const MWWorld::Ptr &ptr); void updatePtr(const MWWorld::Ptr &ptr);
@ -99,7 +98,9 @@ public:
void setState(CharacterState state, bool loop); void setState(CharacterState state, bool loop);
CharacterState getState() const CharacterState getState() const
{ return mState; } { return mCharState; }
void forceStateUpdate();
}; };
} }

@ -134,7 +134,7 @@ namespace MWMechanics
} }
} }
int Enchanting::getEnchantCost() const float Enchanting::getEnchantCost() const
{ {
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
float cost = 0; float cost = 0;
@ -193,7 +193,7 @@ namespace MWMechanics
return soul->mData.mSoul; return soul->mData.mSoul;
} }
int Enchanting::getMaxEnchantValue() const float Enchanting::getMaxEnchantValue() const
{ {
if (itemEmpty()) if (itemEmpty())
return 0; return 0;

@ -35,9 +35,9 @@ namespace MWMechanics
bool create(); //Return true if created, false if failed. bool create(); //Return true if created, false if failed.
void nextEnchantType(); //Set enchant type to next possible type (for mOldItemPtr object) void nextEnchantType(); //Set enchant type to next possible type (for mOldItemPtr object)
int getEnchantType() const; int getEnchantType() const;
int getEnchantCost() const; float getEnchantCost() const;
int getEnchantPrice() const; int getEnchantPrice() const;
int getMaxEnchantValue() const; float getMaxEnchantValue() const;
int getGemCharge() const; int getGemCharge() const;
float getEnchantChance() const; float getEnchantChance() const;
bool soulEmpty() const; //Return true if empty bool soulEmpty() const; //Return true if empty

@ -654,6 +654,12 @@ namespace MWMechanics
} }
} }
void MechanicsManager::forceStateUpdate(const MWWorld::Ptr &ptr)
{
if(MWWorld::Class::get(ptr).isActor())
mActors.forceStateUpdate(ptr);
}
void MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) void MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
{ {
if(MWWorld::Class::get(ptr).isActor()) if(MWWorld::Class::get(ptr).isActor())

@ -96,6 +96,8 @@ namespace MWMechanics
void toLower(std::string npcFaction); void toLower(std::string npcFaction);
///< Perform a persuasion action on NPC ///< Perform a persuasion action on NPC
virtual void forceStateUpdate(const MWWorld::Ptr &ptr);
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
virtual void skipAnimation(const MWWorld::Ptr& ptr); virtual void skipAnimation(const MWWorld::Ptr& ptr);
}; };

@ -169,8 +169,7 @@ float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& cla
if (specialisationFactor<=0) if (specialisationFactor<=0)
throw std::runtime_error ("invalid skill specialisation factor"); throw std::runtime_error ("invalid skill specialisation factor");
} }
return 1.0 / ((level+1) * (1.0/skillFactor) * typeFactor * specialisationFactor);
return 1.0 / (level +1) * (1.0 / (skillFactor)) * typeFactor * specialisationFactor;
} }
void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, int usageType) void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, int usageType)

@ -1,10 +1,5 @@
#include "activatoranimation.hpp" #include "activatoranimation.hpp"
#include <OgreEntity.h>
#include <OgreParticleSystem.h>
#include <OgreSceneManager.h>
#include <OgreSubEntity.h>
#include "renderconst.hpp" #include "renderconst.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -24,28 +19,10 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr)
assert (ref->mBase != NULL); assert (ref->mBase != NULL);
if(!ref->mBase->mModel.empty()) if(!ref->mBase->mModel.empty())
{ {
std::string mesh = "meshes\\" + ref->mBase->mModel; const std::string name = "meshes\\"+ref->mBase->mModel;
createObjectList(mPtr.getRefData().getBaseNode(), mesh); addObjectList(mPtr.getRefData().getBaseNode(), name, false);
for(size_t i = 0;i < mObjectList.mEntities.size();i++) setRenderProperties(mObjects.back().mObjectList, RV_Misc, RQG_Main, RQG_Alpha);
{
Ogre::Entity *ent = mObjectList.mEntities[i];
ent->setVisibilityFlags(RV_Misc);
for(unsigned int j=0; j < ent->getNumSubEntities(); ++j)
{
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
}
}
for(size_t i = 0;i < mObjectList.mParticles.size();i++)
{
Ogre::ParticleSystem *part = mObjectList.mParticles[i];
part->setVisibilityFlags(RV_Misc);
part->setRenderQueueGroup(RQG_Alpha);
}
setAnimationSource(mesh);
} }
} }

@ -3,6 +3,8 @@
#include <OgreSkeletonManager.h> #include <OgreSkeletonManager.h>
#include <OgreSkeletonInstance.h> #include <OgreSkeletonInstance.h>
#include <OgreEntity.h> #include <OgreEntity.h>
#include <OgreSubEntity.h>
#include <OgreParticleSystem.h>
#include <OgreBone.h> #include <OgreBone.h>
#include <OgreSubMesh.h> #include <OgreSubMesh.h>
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
@ -16,6 +18,32 @@
namespace MWRender namespace MWRender
{ {
Animation::AnimLayer::AnimLayer()
: mControllers(NULL)
, mTextKeys(NULL)
, mTime(0.0f)
, mPlaying(false)
, mLoopCount(0)
{
}
Ogre::Real Animation::AnimationValue::getValue() const
{
size_t idx = mIndex;
while(idx > 0 && mAnimation->mLayer[idx].mGroupName.empty())
idx--;
if(!mAnimation->mLayer[idx].mGroupName.empty())
return mAnimation->mLayer[idx].mTime;
return 0.0f;
}
void Animation::AnimationValue::setValue(Ogre::Real value)
{
mAnimation->mLayer[mIndex].mTime = value;
}
void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects) void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects)
{ {
for(size_t i = 0;i < objects.mParticles.size();i++) for(size_t i = 0;i < objects.mParticles.size();i++)
@ -31,22 +59,22 @@ void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectL
Animation::Animation(const MWWorld::Ptr &ptr) Animation::Animation(const MWWorld::Ptr &ptr)
: mPtr(ptr) : mPtr(ptr)
, mController(NULL)
, mInsert(NULL) , mInsert(NULL)
, mSkelBase(NULL)
, mAccumRoot(NULL) , mAccumRoot(NULL)
, mNonAccumRoot(NULL) , mNonAccumRoot(NULL)
, mAccumulate(Ogre::Vector3::ZERO) , mNonAccumCtrl(NULL)
, mAccumulate(0.0f)
, mLastPosition(0.0f) , mLastPosition(0.0f)
, mCurrentControllers(NULL)
, mCurrentKeys(NULL)
, mCurrentAnim(NULL)
, mCurrentTime(0.0f)
, mStopTime(0.0f)
, mPlaying(false)
, mLooping(false)
, mAnimVelocity(0.0f) , mAnimVelocity(0.0f)
, mAnimSpeedMult(1.0f) , mAnimSpeedMult(1.0f)
{ {
for(size_t i = 0;i < sMaxLayers;i++)
mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this, i));
/* As long as we remain under 128 active controllers, we can avoid
* reallocations. */
mActiveCtrls.reserve(128);
} }
Animation::~Animation() Animation::~Animation()
@ -54,137 +82,223 @@ Animation::~Animation()
if(mInsert) if(mInsert)
{ {
Ogre::SceneManager *sceneMgr = mInsert->getCreator(); Ogre::SceneManager *sceneMgr = mInsert->getCreator();
destroyObjectList(sceneMgr, mObjectList); for(size_t i = 0;i < mObjects.size();i++)
destroyObjectList(sceneMgr, mObjects[i].mObjectList);
for(size_t i = 0;i < mAnimationSources.size();i++) mObjects.clear();
destroyObjectList(sceneMgr, mAnimationSources[i]);
mAnimationSources.clear();
} }
} }
void Animation::setAnimationSources(const std::vector<std::string> &names) void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly)
{ {
if(!mObjectList.mSkelBase) if(!mInsert)
return;
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
mCurrentControllers = &mObjectList.mControllers;
mCurrentAnim = NULL;
mCurrentKeys = NULL;
mAnimVelocity = 0.0f;
mAccumRoot = NULL;
mNonAccumRoot = NULL;
mTextKeys.clear();
for(size_t i = 0;i < mAnimationSources.size();i++)
destroyObjectList(sceneMgr, mAnimationSources[i]);
mAnimationSources.clear();
Ogre::SharedPtr<Ogre::ControllerValue<Ogre::Real> > ctrlval(OGRE_NEW AnimationValue(this));
Ogre::SkeletonInstance *skelinst = mObjectList.mSkelBase->getSkeleton();
std::vector<std::string>::const_iterator nameiter;
for(nameiter = names.begin();nameiter != names.end();nameiter++)
{ {
mAnimationSources.push_back(NifOgre::Loader::createObjectBase(sceneMgr, *nameiter)); mInsert = node->createChildSceneNode();
if(!mAnimationSources.back().mSkelBase) assert(mInsert);
{ }
std::cerr<< "Failed to get skeleton source "<<*nameiter <<std::endl;
destroyObjectList(sceneMgr, mAnimationSources.back());
mAnimationSources.pop_back();
continue;
}
NifOgre::ObjectList &objects = mAnimationSources.back();
for(size_t i = 0;i < objects.mControllers.size();i++)
{
NifOgre::NodeTargetValue<Ogre::Real> *dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objects.mControllers[i].getDestination().getPointer());
if(!dstval) continue;
const Ogre::String &trgtname = dstval->getNode()->getName(); mObjects.push_back(ObjectInfo());
if(!skelinst->hasBone(trgtname)) continue; ObjectInfo &obj = mObjects.back();
obj.mActiveLayers = 0;
obj.mObjectList = (!baseonly ? NifOgre::Loader::createObjects(mInsert, model) :
NifOgre::Loader::createObjectBase(mInsert, model));
Ogre::Bone *bone = skelinst->getBone(trgtname); NifOgre::ObjectList &objlist = obj.mObjectList;
dstval->setNode(bone); if(objlist.mSkelBase)
} {
if(mObjects.size() == 1)
mSkelBase = objlist.mSkelBase;
for(size_t i = 0;i < objects.mControllers.size();i++) Ogre::AnimationStateSet *aset = objlist.mSkelBase->getAllAnimationStates();
Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator();
while(asiter.hasMoreElements())
{ {
if(objects.mControllers[i].getSource().isNull()) Ogre::AnimationState *state = asiter.getNext();
objects.mControllers[i].setSource(ctrlval); state->setEnabled(false);
state->setLoop(false);
} }
Ogre::Entity *ent = objects.mSkelBase; // Set the bones as manually controlled since we're applying the
Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(ent->getSkeleton()->getName()); // transformations manually (needed if we want to apply an animation
Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator(); // from one skeleton onto another).
Ogre::SkeletonInstance *skelinst = objlist.mSkelBase->getSkeleton();
Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator();
while(boneiter.hasMoreElements()) while(boneiter.hasMoreElements())
boneiter.getNext()->setManuallyControlled(true);
}
if(objlist.mSkelBase && mSkelBase)
{
Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
if(mSkelBase == objlist.mSkelBase)
{ {
Ogre::Bone *bone = boneiter.getNext(); if(objlist.mTextKeys.size() > 0)
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; mAccumRoot = mInsert;
mNonAccumRoot = mObjectList.mSkelBase->getSkeleton()->getBone(bone->getName()); mNonAccumRoot = baseinst->getBone(objlist.mTextKeys.begin()->first);
} }
}
for(int i = 0;i < skel->getNumAnimations();i++) else
{
for(size_t i = 0;i < objlist.mControllers.size();i++)
{ {
Ogre::Animation *anim = skel->getAnimation(i); NifOgre::NodeTargetValue<Ogre::Real> *dstval;
const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+ dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objlist.mControllers[i].getDestination().getPointer());
"@"+anim->getName()); if(!dstval) continue;
if(!groupdata.isEmpty())
mTextKeys[anim->getName()] = Ogre::any_cast<NifOgre::TextKeyMap>(groupdata); const Ogre::String &trgtname = dstval->getNode()->getName();
if(!baseinst->hasBone(trgtname)) continue;
Ogre::Bone *bone = baseinst->getBone(trgtname);
dstval->setNode(bone);
} }
}
}
for(size_t i = 0;i < objlist.mControllers.size();i++)
{
if(objlist.mControllers[i].getSource().isNull())
objlist.mControllers[i].setSource(mAnimationValuePtr[0]);
}
break; mActiveCtrls.insert(mActiveCtrls.end(), objlist.mControllers.begin(), objlist.mControllers.end());
}
void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue)
{
for(size_t i = 0;i < objlist.mEntities.size();i++)
{
Ogre::Entity *ent = objlist.mEntities[i];
if(visflags != 0)
ent->setVisibilityFlags(visflags);
for(unsigned int j = 0;j < ent->getNumSubEntities();++j)
{
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? transqueue : solidqueue);
} }
} }
for(size_t i = 0;i < objlist.mParticles.size();i++)
{
Ogre::ParticleSystem *part = objlist.mParticles[i];
if(visflags != 0)
part->setVisibilityFlags(visflags);
// TODO: Check particle material for actual transparency
part->setRenderQueueGroup(transqueue);
}
} }
void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model)
void Animation::clearExtraSources()
{ {
mInsert = node->createChildSceneNode(); for(size_t layer = 0;layer < sMaxLayers;layer++)
assert(mInsert); {
mLayer[layer].mGroupName.clear();
mLayer[layer].mTextKeys = NULL;
mLayer[layer].mControllers = NULL;
mLayer[layer].mTime = 0.0f;
mLayer[layer].mLoopCount = 0;
mLayer[layer].mPlaying = false;
}
mNonAccumCtrl = NULL;
mAnimVelocity = 0.0f;
mLastPosition = Ogre::Vector3(0.0f);
if(mAccumRoot)
mAccumRoot->setPosition(mLastPosition);
mObjectList = NifOgre::Loader::createObjects(mInsert, model); if(mObjects.size() > 1)
if(mObjectList.mSkelBase)
{ {
Ogre::AnimationStateSet *aset = mObjectList.mSkelBase->getAllAnimationStates(); mObjects.resize(1);
Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); mObjects[0].mActiveLayers = 0;
while(asiter.hasMoreElements())
NifOgre::ObjectList &objlist = mObjects[0].mObjectList;
mActiveCtrls.clear();
mActiveCtrls.insert(mActiveCtrls.end(), objlist.mControllers.begin(), objlist.mControllers.end());
}
}
void Animation::updateActiveControllers()
{
mActiveCtrls.clear();
/* First, get all controllers that don't target a node, or that target
* nodes that don't belong to any particular layer.
*/
std::vector<ObjectInfo>::iterator obj(mObjects.begin());
for(;obj != mObjects.end();obj++)
{
std::vector<Ogre::Controller<Ogre::Real> >::const_iterator ctrl(obj->mObjectList.mControllers.begin());
for(;ctrl != obj->mObjectList.mControllers.end();ctrl++)
{ {
Ogre::AnimationState *state = asiter.getNext(); NifOgre::NodeTargetValue<Ogre::Real> *dstval;
state->setEnabled(false); dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(ctrl->getDestination().getPointer());
state->setLoop(false); if(dstval)
{
/*if(getLayerByName(dstval->getNode()->getName()) >= 0)*/
continue;
}
mActiveCtrls.insert(mActiveCtrls.end(), *ctrl);
} }
// 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 = mObjectList.mSkelBase->getSkeleton();
Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator();
while(boneiter.hasMoreElements())
boneiter.getNext()->setManuallyControlled(true);
} }
Ogre::SharedPtr<Ogre::ControllerValue<Ogre::Real> > ctrlval(OGRE_NEW AnimationValue(this)); std::vector<Ogre::Controller<Ogre::Real> > *ctrls = NULL;
for(size_t i = 0;i < mObjectList.mControllers.size();i++) size_t layer = 0;
while(layer < sMaxLayers)
{ {
if(mObjectList.mControllers[i].getSource().isNull()) /* Now get controllers that target nodes that belong to this layer from
mObjectList.mControllers[i].setSource(ctrlval); * whatever objectlist is active on this layer.
*/
std::vector<ObjectInfo>::iterator obj(mObjects.begin());
for(;obj != mObjects.end();obj++)
{
if((obj->mActiveLayers&(1<<layer)))
{
ctrls = &obj->mObjectList.mControllers;
break;
}
}
if(ctrls == NULL)
{
layer++;
continue;
}
/* Check if any objectlists are active on subsequent layers. Include
* those layers if not.
*/
size_t nextlayer = layer+1;
for(;nextlayer < sMaxLayers;nextlayer++)
{
for(obj = mObjects.begin();obj != mObjects.end();obj++)
{
if((obj->mActiveLayers&(1<<nextlayer)))
break;
}
}
std::vector<Ogre::Controller<Ogre::Real> >::const_iterator ctrl(ctrls->begin());
for(;ctrl != ctrls->end();ctrl++)
{
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(ctrl->getDestination().getPointer());
if(dstval)
{
/*ssize_t idx = getLayerByName(dstval->getNode()->getName());
if(idx >= (ssize_t)layer && idx < (ssize_t)nextlayer)*/
mActiveCtrls.insert(mActiveCtrls.end(), *ctrl);
}
}
layer = nextlayer;
} }
mCurrentControllers = &mObjectList.mControllers;
} }
Ogre::Node *Animation::getNode(const std::string &name) Ogre::Node *Animation::getNode(const std::string &name)
{ {
if(mObjectList.mSkelBase) if(mSkelBase)
{ {
Ogre::SkeletonInstance *skel = mObjectList.mSkelBase->getSkeleton(); Ogre::SkeletonInstance *skel = mSkelBase->getSkeleton();
if(skel->hasBone(name)) if(skel->hasBone(name))
return skel->getBone(name); return skel->getBone(name);
} }
@ -192,20 +306,34 @@ Ogre::Node *Animation::getNode(const std::string &name)
} }
bool Animation::hasAnimation(const std::string &anim) NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname)
{ {
for(std::vector<NifOgre::ObjectList>::const_iterator iter(mAnimationSources.begin());iter != mAnimationSources.end();iter++) NifOgre::TextKeyMap::const_iterator iter(keys.begin());
for(;iter != keys.end();iter++)
{ {
if(iter->mSkelBase->hasAnimationState(anim)) if(iter->second.compare(0, groupname.size(), groupname) == 0 &&
return true; iter->second.compare(groupname.size(), 2, ": ") == 0)
break;
} }
return false; return iter;
} }
void Animation::setController(MWMechanics::CharacterController *controller) bool Animation::hasAnimation(const std::string &anim)
{ {
mController = controller; if(!mSkelBase)
return false;
for(std::vector<ObjectInfo>::const_iterator iter(mObjects.begin());iter != mObjects.end();iter++)
{
if(iter->mObjectList.mTextKeys.size() == 0)
continue;
const NifOgre::TextKeyMap &keys = iter->mObjectList.mTextKeys.begin()->second;
if(findGroupStart(keys, anim) != keys.end())
return true;
}
return false;
} }
@ -221,10 +349,6 @@ void Animation::setSpeed(float speed)
mAnimSpeedMult = speed / mAnimVelocity; mAnimSpeedMult = speed / mAnimVelocity;
} }
void Animation::setLooping(bool loop)
{
mLooping = loop;
}
void Animation::updatePtr(const MWWorld::Ptr &ptr) void Animation::updatePtr(const MWWorld::Ptr &ptr)
{ {
@ -232,47 +356,36 @@ void Animation::updatePtr(const MWWorld::Ptr &ptr)
} }
void Animation::calcAnimVelocity() float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl, const Ogre::Vector3 &accum, const std::string &groupname)
{ {
const Ogre::NodeAnimationTrack *track = 0; const std::string start = groupname+": start";
const std::string loopstart = groupname+": loop start";
Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); const std::string loopstop = groupname+": loop stop";
while(!track && trackiter.hasMoreElements()) const std::string stop = groupname+": stop";
{ float starttime = std::numeric_limits<float>::max();
const Ogre::NodeAnimationTrack *cur = trackiter.getNext(); float stoptime = 0.0f;
if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName()) NifOgre::TextKeyMap::const_iterator keyiter(keys.begin());
track = cur; while(keyiter != keys.end())
}
if(track && track->getNumKeyFrames() > 1)
{ {
float loopstarttime = 0.0f; if(keyiter->second == start || keyiter->second == loopstart)
float loopstoptime = mCurrentAnim->getLength(); starttime = keyiter->first;
NifOgre::TextKeyMap::const_iterator keyiter = mCurrentKeys->begin(); else if(keyiter->second == loopstop || keyiter->second == stop)
while(keyiter != mCurrentKeys->end())
{ {
if(keyiter->second == "loop start") stoptime = keyiter->first;
loopstarttime = keyiter->first; break;
else if(keyiter->second == "loop stop")
{
loopstoptime = keyiter->first;
break;
}
keyiter++;
} }
keyiter++;
}
if(loopstoptime > loopstarttime) if(stoptime > starttime)
{ {
Ogre::TransformKeyFrame startkf(0, loopstarttime); Ogre::Vector3 startpos = nonaccumctrl->getTranslation(starttime) * accum;
Ogre::TransformKeyFrame endkf(0, loopstoptime); Ogre::Vector3 endpos = nonaccumctrl->getTranslation(stoptime) * accum;
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstarttime), &startkf);
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstoptime), &endkf);
mAnimVelocity = startkf.getTranslate().distance(endkf.getTranslate()) / return startpos.distance(endpos) / (stoptime-starttime);
(loopstoptime-loopstarttime);
}
} }
return 0.0f;
} }
static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone) static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone)
@ -313,93 +426,90 @@ void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Og
} }
Ogre::Vector3 Animation::updatePosition() void Animation::updatePosition(Ogre::Vector3 &position)
{ {
Ogre::Vector3 posdiff; Ogre::Vector3 posdiff;
Ogre::TransformKeyFrame kf(0, mCurrentTime); /* Get the non-accumulation root's difference from the last update, and move the position
Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); * accordingly.
while(trackiter.hasMoreElements()) */
{ posdiff = (mNonAccumCtrl->getTranslation(mLayer[0].mTime) - mLastPosition) * mAccumulate;
const Ogre::NodeAnimationTrack *track = trackiter.getNext(); position += posdiff;
if(track->getAssociatedNode()->getName() == mNonAccumRoot->getName())
{
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf);
break;
}
}
/* Get the non-accumulation root's difference from the last update. */
posdiff = (kf.getTranslate() - mLastPosition) * mAccumulate;
/* Translate the accumulation root back to compensate for the move. */ /* Translate the accumulation root back to compensate for the move. */
mLastPosition += posdiff; mLastPosition += posdiff;
mAccumRoot->setPosition(-mLastPosition); mAccumRoot->setPosition(-mLastPosition);
return posdiff;
} }
void Animation::reset(const std::string &start, const std::string &stop) bool Animation::reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint)
{ {
mNextKey = mCurrentKeys->begin(); std::string tag = groupname+": "+start;
NifOgre::TextKeyMap::const_iterator startkey(keys.begin());
while(mNextKey != mCurrentKeys->end() && mNextKey->second != start) while(startkey != keys.end() && startkey->second != tag)
mNextKey++; startkey++;
if(mNextKey != mCurrentKeys->end()) if(startkey == keys.end() && start == "loop start")
mCurrentTime = mNextKey->first;
else
{ {
mNextKey = mCurrentKeys->begin(); tag = groupname+": start";
while(mNextKey != mCurrentKeys->end() && mNextKey->second != "start") startkey = keys.begin();
mNextKey++; while(startkey != keys.end() && startkey->second != tag)
if(mNextKey != mCurrentKeys->end()) startkey++;
mCurrentTime = mNextKey->first;
else
{
mNextKey = mCurrentKeys->begin();
mCurrentTime = 0.0f;
}
} }
if(startkey == keys.end())
return false;
tag = groupname+": "+stop;
NifOgre::TextKeyMap::const_iterator stopkey(startkey);
while(stopkey != keys.end() && stopkey->second != tag)
stopkey++;
if(stopkey == keys.end())
return false;
if(stop.length() > 0) if(startkey == stopkey)
return false;
mLayer[layeridx].mStartKey = startkey;
mLayer[layeridx].mLoopStartKey = startkey;
mLayer[layeridx].mStopKey = stopkey;
mLayer[layeridx].mNextKey = startkey;
mLayer[layeridx].mTime = mLayer[layeridx].mStartKey->first + ((mLayer[layeridx].mStopKey->first-
mLayer[layeridx].mStartKey->first) * startpoint);
tag = groupname+": loop start";
while(mLayer[layeridx].mNextKey->first <= mLayer[layeridx].mTime && mLayer[layeridx].mNextKey != mLayer[layeridx].mStopKey)
{ {
NifOgre::TextKeyMap::const_iterator stopKey = mNextKey; if(mLayer[layeridx].mNextKey->second == tag)
while(stopKey != mCurrentKeys->end() && stopKey->second != stop) mLayer[layeridx].mLoopStartKey = mLayer[layeridx].mNextKey;
stopKey++; mLayer[layeridx].mNextKey++;
if(stopKey != mCurrentKeys->end())
mStopTime = stopKey->first;
else
mStopTime = mCurrentAnim->getLength();
} }
if(mNonAccumRoot) if(layeridx == 0 && nonaccumctrl)
{ mLastPosition = nonaccumctrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate;
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) return true;
{
Ogre::TransformKeyFrame kf(0, mCurrentTime);
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf);
mLastPosition = kf.getTranslate() * mAccumulate;
}
}
} }
bool Animation::doLoop(size_t layeridx)
{
if(mLayer[layeridx].mLoopCount == 0)
return false;
mLayer[layeridx].mLoopCount--;
bool Animation::handleEvent(float time, const std::string &evt) mLayer[layeridx].mTime = mLayer[layeridx].mLoopStartKey->first;
mLayer[layeridx].mNextKey = mLayer[layeridx].mLoopStartKey;
mLayer[layeridx].mNextKey++;
mLayer[layeridx].mPlaying = true;
if(layeridx == 0 && mNonAccumCtrl)
mLastPosition = mNonAccumCtrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate;
return true;
}
bool Animation::handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key)
{ {
if(evt == "start" || evt == "loop start") float time = key->first;
{ const std::string &evt = key->second;
/* Do nothing */
return true;
}
if(evt.compare(0, 7, "sound: ") == 0) if(evt.compare(0, 7, "sound: ") == 0)
{ {
@ -414,108 +524,219 @@ bool Animation::handleEvent(float time, const std::string &evt)
return true; return true;
} }
if(evt == "loop stop") if(evt.compare(0, mLayer[layeridx].mGroupName.size(), mLayer[layeridx].mGroupName) != 0 ||
evt.compare(mLayer[layeridx].mGroupName.size(), 2, ": ") != 0)
{ {
if(mLooping) // Not ours, skip it
{
reset("loop start", "");
if(mCurrentTime >= time)
return false;
}
return true; return true;
} }
if(evt == "stop") size_t off = mLayer[layeridx].mGroupName.size()+2;
size_t len = evt.size() - off;
if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0)
{ {
if(mLooping) mLayer[layeridx].mLoopStartKey = key;
return true;
}
if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0)
{
if(doLoop(layeridx))
{ {
reset("loop start", ""); if(mLayer[layeridx].mTime >= time)
if(mCurrentTime >= time)
return false; return false;
return true;
} }
// fall-through return true;
} }
if(mController)
mController->markerEvent(time, evt); std::cerr<< "Unhandled animation textkey: "<<evt <<std::endl;
return true; return true;
} }
void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop) bool Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops)
{ {
try { // TODO: parameterize this
bool found = false; size_t layeridx = 0;
/* Look in reverse; last-inserted source has priority. */
for(std::vector<NifOgre::ObjectList>::reverse_iterator iter(mAnimationSources.rbegin());iter != mAnimationSources.rend();iter++) if(!mSkelBase)
return false;
for(std::vector<ObjectInfo>::iterator iter(mObjects.begin());iter != mObjects.end();iter++)
iter->mActiveLayers &= ~(1<<layeridx);
mLayer[layeridx].mGroupName.clear();
mLayer[layeridx].mTextKeys = NULL;
mLayer[layeridx].mControllers = NULL;
mLayer[layeridx].mTime = 0.0f;
mLayer[layeridx].mLoopCount = 0;
mLayer[layeridx].mPlaying = false;
bool movinganim = false;
bool foundanim = false;
/* Look in reverse; last-inserted source has priority. */
for(std::vector<ObjectInfo>::reverse_iterator iter(mObjects.rbegin());iter != mObjects.rend();iter++)
{
NifOgre::ObjectList &objlist = iter->mObjectList;
if(objlist.mTextKeys.size() == 0)
continue;
const NifOgre::TextKeyMap &keys = objlist.mTextKeys.begin()->second;
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl = NULL;
if(layeridx == 0 && mNonAccumRoot)
{ {
if(iter->mSkelBase->hasAnimationState(groupname)) for(size_t i = 0;i < objlist.mControllers.size();i++)
{ {
mCurrentAnim = iter->mSkelBase->getSkeleton()->getAnimation(groupname); NifOgre::NodeTargetValue<Ogre::Real> *dstval;
mCurrentKeys = &mTextKeys[groupname]; dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objlist.mControllers[i].getDestination().getPointer());
mCurrentControllers = &iter->mControllers; if(dstval && dstval->getNode() == mNonAccumRoot)
{
nonaccumctrl = dstval;
break;
}
}
}
if(!foundanim)
{
if(!reset(layeridx, keys, nonaccumctrl, groupname, start, stop, startpoint))
continue;
mLayer[layeridx].mGroupName = groupname;
mLayer[layeridx].mTextKeys = &keys;
mLayer[layeridx].mControllers = &objlist.mControllers;
mLayer[layeridx].mLoopCount = loops;
mLayer[layeridx].mPlaying = true;
if(layeridx == 0)
{
mNonAccumCtrl = nonaccumctrl;
mAnimVelocity = 0.0f; mAnimVelocity = 0.0f;
}
if(mNonAccumRoot) iter->mActiveLayers |= (1<<layeridx);
calcAnimVelocity(); foundanim = true;
found = true; if(mAccumulate == Ogre::Vector3(0.0f))
break; break;
}
} }
if(!found)
throw std::runtime_error("Failed to find animation "+groupname);
reset(start, stop); if(!nonaccumctrl)
setLooping(loop); break;
mPlaying = true;
mAnimVelocity = calcAnimVelocity(keys, nonaccumctrl, mAccumulate, groupname);
if(mAnimVelocity > 1.0f)
{
movinganim = (nonaccumctrl==mNonAccumCtrl);
break;
}
} }
catch(std::exception &e) { if(!foundanim)
std::cerr<< e.what() <<std::endl; std::cerr<< "Failed to find animation "<<groupname <<std::endl;
updateActiveControllers();
return movinganim;
}
void Animation::disable(size_t layeridx)
{
if(mLayer[layeridx].mGroupName.empty())
return;
for(std::vector<ObjectInfo>::iterator iter(mObjects.begin());iter != mObjects.end();iter++)
iter->mActiveLayers &= ~(1<<layeridx);
mLayer[layeridx].mGroupName.clear();
mLayer[layeridx].mTextKeys = NULL;
mLayer[layeridx].mControllers = NULL;
mLayer[layeridx].mTime = 0.0f;
mLayer[layeridx].mLoopCount = 0;
mLayer[layeridx].mPlaying = false;
updateActiveControllers();
}
bool Animation::getInfo(size_t layeridx, float *complete, std::string *groupname, std::string *start, std::string *stop) const
{
if(mLayer[layeridx].mGroupName.empty())
{
if(complete) *complete = 0.0f;
if(groupname) *groupname = "";
if(start) *start = "";
if(stop) *stop = "";
return false;
} }
if(complete) *complete = (mLayer[layeridx].mTime - mLayer[layeridx].mStartKey->first) /
(mLayer[layeridx].mStopKey->first - mLayer[layeridx].mStartKey->first);
if(groupname) *groupname = mLayer[layeridx].mGroupName;
if(start) *start = mLayer[layeridx].mStartKey->second.substr(mLayer[layeridx].mGroupName.size()+2);
if(stop) *stop = mLayer[layeridx].mStopKey->second.substr(mLayer[layeridx].mGroupName.size()+2);
return true;
} }
Ogre::Vector3 Animation::runAnimation(float timepassed)
Ogre::Vector3 Animation::runAnimation(float duration)
{ {
Ogre::Vector3 movement(0.0f); Ogre::Vector3 movement(0.0f);
timepassed *= mAnimSpeedMult; duration *= mAnimSpeedMult;
while(mCurrentAnim && mPlaying) for(size_t layeridx = 0;layeridx < sMaxLayers;layeridx++)
{ {
float targetTime = mCurrentTime + timepassed; if(mLayer[layeridx].mGroupName.empty())
if(mNextKey == mCurrentKeys->end() || mNextKey->first > targetTime) continue;
float timepassed = duration;
while(mLayer[layeridx].mPlaying)
{ {
mCurrentTime = std::min(mStopTime, targetTime); float targetTime = mLayer[layeridx].mTime + timepassed;
if(mNonAccumRoot) if(mLayer[layeridx].mNextKey->first > targetTime)
movement += updatePosition(); {
mPlaying = (mLooping || mStopTime > mCurrentTime); mLayer[layeridx].mTime = targetTime;
timepassed = targetTime - mCurrentTime; if(layeridx == 0 && mNonAccumCtrl)
break; updatePosition(movement);
} break;
}
float time = mNextKey->first; NifOgre::TextKeyMap::const_iterator key(mLayer[layeridx].mNextKey++);
const std::string &evt = mNextKey->second; mLayer[layeridx].mTime = key->first;
mNextKey++; if(layeridx == 0 && mNonAccumCtrl)
updatePosition(movement);
mCurrentTime = time; mLayer[layeridx].mPlaying = (key != mLayer[layeridx].mStopKey);
if(mNonAccumRoot) timepassed = targetTime - mLayer[layeridx].mTime;
movement += updatePosition();
mPlaying = (mLooping || mStopTime > mCurrentTime);
timepassed = targetTime - mCurrentTime;
if(!handleEvent(time, evt)) if(!handleTextKey(layeridx, key))
break; break;
}
} }
for(size_t i = 0;i < mCurrentControllers->size();i++)
(*mCurrentControllers)[i].update();
if(mObjectList.mSkelBase) for(size_t i = 0;i < mActiveCtrls.size();i++)
mActiveCtrls[i].update();
if(mSkelBase)
{ {
// HACK: Dirty the animation state set so that Ogre will apply the const Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
// transformations to entities this skeleton instance is shared with. for(std::vector<ObjectInfo>::iterator iter(mObjects.begin());iter != mObjects.end();iter++)
mObjectList.mSkelBase->getAllAnimationStates()->_notifyDirty(); {
Ogre::Entity *ent = iter->mObjectList.mSkelBase;
if(!ent) continue;
Ogre::SkeletonInstance *inst = ent->getSkeleton();
if(baseinst != inst)
updateSkeletonInstance(baseinst, inst);
// HACK: Dirty the animation state set so that Ogre will apply the
// transformations to entities this skeleton instance is shared with.
ent->getAllAnimationStates()->_notifyDirty();
}
} }
return movement; return movement;
} }
void Animation::showWeapons(bool showWeapon){}
} }

@ -8,10 +8,6 @@
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
namespace MWMechanics
{
class CharacterController;
}
namespace MWRender namespace MWRender
{ {
@ -23,48 +19,67 @@ protected:
{ {
private: private:
Animation *mAnimation; Animation *mAnimation;
size_t mIndex;
public: public:
AnimationValue(Animation *anim) : mAnimation(anim) AnimationValue(Animation *anim, size_t layeridx)
: mAnimation(anim), mIndex(layeridx)
{ } { }
virtual Ogre::Real getValue() const virtual Ogre::Real getValue() const;
{ virtual void setValue(Ogre::Real value);
return mAnimation->mCurrentTime; };
}
struct ObjectInfo {
NifOgre::ObjectList mObjectList;
/* Bit-field specifying which animation layers this object list is
* explicitly animating on (1 = layer 0, 2 = layer 1, 4 = layer 2.
* etc).
*/
int mActiveLayers;
};
struct AnimLayer {
std::string mGroupName;
std::vector<Ogre::Controller<Ogre::Real> > *mControllers;
const NifOgre::TextKeyMap *mTextKeys;
NifOgre::TextKeyMap::const_iterator mStartKey;
NifOgre::TextKeyMap::const_iterator mLoopStartKey;
NifOgre::TextKeyMap::const_iterator mStopKey;
NifOgre::TextKeyMap::const_iterator mNextKey;
float mTime;
bool mPlaying;
size_t mLoopCount;
virtual void setValue(Ogre::Real value) AnimLayer();
{
mAnimation->mCurrentTime = value;
}
}; };
MWWorld::Ptr mPtr; MWWorld::Ptr mPtr;
MWMechanics::CharacterController *mController;
Ogre::SceneNode* mInsert; Ogre::SceneNode *mInsert;
NifOgre::ObjectList mObjectList; Ogre::Entity *mSkelBase;
std::map<std::string,NifOgre::TextKeyMap> mTextKeys; std::vector<ObjectInfo> mObjects;
Ogre::Node *mAccumRoot; Ogre::Node *mAccumRoot;
Ogre::Bone *mNonAccumRoot; Ogre::Bone *mNonAccumRoot;
NifOgre::NodeTargetValue<Ogre::Real> *mNonAccumCtrl;
Ogre::Vector3 mAccumulate; Ogre::Vector3 mAccumulate;
Ogre::Vector3 mLastPosition; Ogre::Vector3 mLastPosition;
std::vector<NifOgre::ObjectList> mAnimationSources; std::vector<Ogre::Controller<Ogre::Real> > mActiveCtrls;
std::vector<Ogre::Controller<Ogre::Real> > *mCurrentControllers;
NifOgre::TextKeyMap *mCurrentKeys;
NifOgre::TextKeyMap::const_iterator mNextKey;
Ogre::Animation *mCurrentAnim;
float mCurrentTime;
float mStopTime;
bool mPlaying;
bool mLooping;
float mAnimVelocity; float mAnimVelocity;
float mAnimSpeedMult; float mAnimSpeedMult;
void calcAnimVelocity(); static const size_t sMaxLayers = 1;
AnimLayer mLayer[sMaxLayers];
Ogre::SharedPtr<Ogre::ControllerValue<Ogre::Real> > mAnimationValuePtr[sMaxLayers];
static float calcAnimVelocity(const NifOgre::TextKeyMap &keys,
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl,
const Ogre::Vector3 &accum,
const std::string &groupname);
/* Updates a skeleton instance so that all bones matching the source skeleton (based on /* Updates a skeleton instance so that all bones matching the source skeleton (based on
* bone names) are positioned identically. */ * bone names) are positioned identically. */
@ -72,34 +87,39 @@ protected:
/* Updates the position of the accum root node for the current time, and /* Updates the position of the accum root node for the current time, and
* returns the wanted movement vector from the previous update. */ * returns the wanted movement vector from the previous update. */
Ogre::Vector3 updatePosition(); void updatePosition(Ogre::Vector3 &position);
static NifOgre::TextKeyMap::const_iterator findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname);
/* Resets the animation to the time of the specified start marker, without /* 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 * 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. * the marker is not found, or if the markers are the same, it returns
* false.
*/ */
void reset(const std::string &start, const std::string &stop); bool reset(size_t layeridx, const NifOgre::TextKeyMap &keys,
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl,
bool handleEvent(float time, const std::string &evt); const std::string &groupname, const std::string &start, const std::string &stop,
float startpoint);
/* Specifies a list of skeleton names to use as animation sources. */ bool doLoop(size_t layeridx);
void setAnimationSources(const std::vector<std::string> &names);
/* Specifies a single skeleton name to use as an animation source. */ bool handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key);
void setAnimationSource(const std::string &name)
{
std::vector<std::string> names(1, name);
setAnimationSources(names);
}
void createObjectList(Ogre::SceneNode *node, const std::string &model); void addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly);
static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects);
static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue);
void updateActiveControllers();
public: public:
Animation(const MWWorld::Ptr &ptr); Animation(const MWWorld::Ptr &ptr);
virtual ~Animation(); virtual ~Animation();
void setController(MWMechanics::CharacterController *controller); /** Clears all ObjectLists except the first one. As a consequence, any
* playing animations are stopped.
*/
void clearExtraSources();
void updatePtr(const MWWorld::Ptr &ptr); void updatePtr(const MWWorld::Ptr &ptr);
@ -112,10 +132,40 @@ public:
void setSpeed(float speed); void setSpeed(float speed);
void setLooping(bool loop); /** Plays an animation.
* \param groupname Name of the animation group to play.
* \param start Key marker from which to start.
* \param stop Key marker to stop at.
* \param startpoint How far in between the two markers to start. 0 starts
* at the start marker, 1 starts at the stop marker.
* \param loops How many times to loop the animation. This will use the
* "loop start" and "loop stop" markers if they exist,
* otherwise it will use "start" and "stop".
* \return Boolean specifying whether the animation will return movement
* for the character at all.
*/
bool play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops);
/** Stops and removes the animation from the given layer. */
void disable(size_t layeridx);
/** Gets info about the given animation layer.
* \param layeridx Layer index to get info about.
* \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc.
* \param groupname Stores animation group being played.
* \param start Stores the start key
* \param stop Stores the stop key
* \return True if an animation is active on the layer, false otherwise.
*/
bool getInfo(size_t layeridx, float *complete=NULL, std::string *groupname=NULL, std::string *start=NULL, std::string *stop=NULL) const;
virtual Ogre::Vector3 runAnimation(float duration);
virtual void showWeapons(bool showWeapon);
void play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop); /* Returns if there's an animation playing on the given layer. */
virtual Ogre::Vector3 runAnimation(float timepassed); bool isPlaying(size_t layeridx) const
{ return mLayer[layeridx].mPlaying; }
Ogre::Node *getNode(const std::string &name); Ogre::Node *getNode(const std::string &name);
}; };

@ -1,7 +1,8 @@
#include "player.hpp" #include "camera.hpp"
#include <OgreSceneNode.h> #include <OgreSceneNode.h>
#include <OgreCamera.h> #include <OgreCamera.h>
#include <OgreSceneManager.h>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
@ -14,10 +15,9 @@
namespace MWRender namespace MWRender
{ {
Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) Camera::Camera (Ogre::Camera *camera)
: mCamera(camera), : mCamera(camera),
mPlayerNode(node), mCameraNode(NULL),
mCameraNode(mPlayerNode->createChildSceneNode()),
mFirstPersonView(true), mFirstPersonView(true),
mPreviewMode(false), mPreviewMode(false),
mFreeLook(true), mFreeLook(true),
@ -28,51 +28,16 @@ namespace MWRender
{ {
mVanity.enabled = false; mVanity.enabled = false;
mVanity.allowed = true; mVanity.allowed = true;
mVanity.forced = false;
mCameraNode->attachObject(mCamera);
mCameraNode->setPosition(0.f, 0.f, mHeight);
mPreviewCam.yaw = 0.f; mPreviewCam.yaw = 0.f;
mPreviewCam.offset = 400.f; mPreviewCam.offset = 400.f;
} }
Player::~Player() Camera::~Camera()
{
delete mAnimation;
}
bool Player::rotate(const Ogre::Vector3 &rot, bool adjust)
{ {
if (mVanity.enabled) {
toggleVanityMode(false);
}
Ogre::Vector3 trueRot = rot;
/// \note rotate player on forced vanity
if (mVanity.forced) {
if (mFreeLook) {
float diff = (adjust) ? rot.z : mMainCam.yaw - rot.z;
mVanity.enabled = false;
rotateCamera(rot, adjust);
mVanity.enabled = true;
compensateYaw(diff);
}
trueRot.z = 0.f;
}
if (mFreeLook || mVanity.enabled || mPreviewMode) {
rotateCamera(trueRot, adjust);
}
/// \note if vanity mode is forced by TVM then rotate player
return (!mVanity.enabled && !mPreviewMode) || mVanity.forced;
} }
void Player::rotateCamera(const Ogre::Vector3 &rot, bool adjust) void Camera::rotateCamera(const Ogre::Vector3 &rot, bool adjust)
{ {
if (adjust) { if (adjust) {
setYaw(getYaw() + rot.z); setYaw(getYaw() + rot.z);
@ -81,33 +46,37 @@ namespace MWRender
setYaw(rot.z); setYaw(rot.z);
setPitch(rot.x); setPitch(rot.x);
} }
Ogre::Quaternion xr(
Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Quaternion xr(Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X);
Ogre::Vector3::UNIT_X
);
Ogre::Quaternion zr(
Ogre::Radian(getYaw()),
Ogre::Vector3::NEGATIVE_UNIT_Z
);
if (!mVanity.enabled && !mPreviewMode) { if (!mVanity.enabled && !mPreviewMode) {
mPlayerNode->setOrientation(zr);
mCameraNode->setOrientation(xr); mCameraNode->setOrientation(xr);
} else { } else {
Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z);
mCameraNode->setOrientation(zr * xr); mCameraNode->setOrientation(zr * xr);
} }
} }
std::string Player::getHandle() const const std::string &Camera::getHandle() const
{ {
return mPlayerNode->getName(); return mTrackingPtr.getRefData().getHandle();
} }
void Player::attachTo(const MWWorld::Ptr &ptr) void Camera::attachTo(const MWWorld::Ptr &ptr)
{ {
ptr.getRefData().setBaseNode(mPlayerNode); mTrackingPtr = ptr;
Ogre::SceneNode *node = mTrackingPtr.getRefData().getBaseNode()->createChildSceneNode(Ogre::Vector3(0.0f, 0.0f, mHeight));
if(mCameraNode)
{
node->setOrientation(mCameraNode->getOrientation());
node->setPosition(mCameraNode->getPosition());
node->setScale(mCameraNode->getScale());
mCameraNode->getCreator()->destroySceneNode(mCameraNode);
}
mCameraNode = node;
mCameraNode->attachObject(mCamera);
} }
void Player::updateListener() void Camera::updateListener()
{ {
Ogre::Vector3 pos = mCamera->getRealPosition(); Ogre::Vector3 pos = mCamera->getRealPosition();
Ogre::Vector3 dir = mCamera->getRealDirection(); Ogre::Vector3 dir = mCamera->getRealDirection();
@ -116,29 +85,27 @@ namespace MWRender
MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up); MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up);
} }
void Player::update(float duration) void Camera::update(float duration)
{ {
updateListener(); updateListener();
// only show the crosshair in game mode and in first person mode. // only show the crosshair in game mode and in first person mode.
MWBase::Environment::get().getWindowManager ()->showCrosshair MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();
(!MWBase::Environment::get().getWindowManager ()->isGuiMode () && (mFirstPersonView && !mVanity.enabled && !mPreviewMode)); wm->showCrosshair(!wm->isGuiMode() && (mFirstPersonView && !mVanity.enabled && !mPreviewMode));
if (mFirstPersonView && !mVanity.enabled) { if(mVanity.enabled)
return; {
}
if (mVanity.enabled) {
Ogre::Vector3 rot(0.f, 0.f, 0.f); Ogre::Vector3 rot(0.f, 0.f, 0.f);
rot.z = Ogre::Degree(3.f * duration).valueRadians(); rot.z = Ogre::Degree(3.f * duration).valueRadians();
rotateCamera(rot, true); rotateCamera(rot, true);
} }
} }
void Player::toggleViewMode() void Camera::toggleViewMode()
{ {
mFirstPersonView = !mFirstPersonView; mFirstPersonView = !mFirstPersonView;
mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ? mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson :
NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson); NpcAnimation::VM_Normal);
if (mFirstPersonView) { if (mFirstPersonView) {
mCamera->setPosition(0.f, 0.f, 0.f); mCamera->setPosition(0.f, 0.f, 0.f);
setLowHeight(false); setLowHeight(false);
@ -148,28 +115,24 @@ namespace MWRender
} }
} }
void Player::allowVanityMode(bool allow) void Camera::allowVanityMode(bool allow)
{ {
if (!allow && mVanity.enabled && !mVanity.forced) { if (!allow && mVanity.enabled)
toggleVanityMode(false); toggleVanityMode(false);
}
mVanity.allowed = allow; mVanity.allowed = allow;
} }
bool Player::toggleVanityMode(bool enable, bool force) bool Camera::toggleVanityMode(bool enable)
{ {
if ((mVanity.forced && !force) || if(!mVanity.allowed && enable)
(!mVanity.allowed && (force || enable)))
{
return false; return false;
} else if (mVanity.enabled == enable) {
if(mVanity.enabled == enable)
return true; return true;
}
mVanity.enabled = enable; mVanity.enabled = enable;
mVanity.forced = force && enable;
mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ? mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson :
NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson); NpcAnimation::VM_Normal);
float offset = mPreviewCam.offset; float offset = mPreviewCam.offset;
Ogre::Vector3 rot(0.f, 0.f, 0.f); Ogre::Vector3 rot(0.f, 0.f, 0.f);
@ -185,20 +148,22 @@ namespace MWRender
setLowHeight(!mFirstPersonView); setLowHeight(!mFirstPersonView);
} }
rot.z = getYaw(); rot.z = getYaw();
mCamera->setPosition(0.f, 0.f, offset); mCamera->setPosition(0.f, 0.f, offset);
rotateCamera(rot, false); rotateCamera(rot, false);
return true; return true;
} }
void Player::togglePreviewMode(bool enable) void Camera::togglePreviewMode(bool enable)
{ {
if (mPreviewMode == enable) { if(mPreviewMode == enable)
return; return;
}
mPreviewMode = enable; mPreviewMode = enable;
mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ? mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson :
NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson); NpcAnimation::VM_Normal);
float offset = mCamera->getPosition().z; float offset = mCamera->getPosition().z;
if (mPreviewMode) { if (mPreviewMode) {
mMainCam.offset = offset; mMainCam.offset = offset;
@ -211,19 +176,19 @@ namespace MWRender
setLowHeight(!mFirstPersonView); setLowHeight(!mFirstPersonView);
} }
mCamera->setPosition(0.f, 0.f, offset); mCamera->setPosition(0.f, 0.f, offset);
rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false);
} }
float Player::getYaw() float Camera::getYaw()
{ {
if (mVanity.enabled || mPreviewMode) { if(mVanity.enabled || mPreviewMode)
return mPreviewCam.yaw; return mPreviewCam.yaw;
}
return mMainCam.yaw; return mMainCam.yaw;
} }
void Player::setYaw(float angle) void Camera::setYaw(float angle)
{ {
if (angle > Ogre::Math::PI) { if (angle > Ogre::Math::PI) {
angle -= Ogre::Math::TWO_PI; angle -= Ogre::Math::TWO_PI;
@ -237,7 +202,7 @@ namespace MWRender
} }
} }
float Player::getPitch() float Camera::getPitch()
{ {
if (mVanity.enabled || mPreviewMode) { if (mVanity.enabled || mPreviewMode) {
return mPreviewCam.pitch; return mPreviewCam.pitch;
@ -245,18 +210,18 @@ namespace MWRender
return mMainCam.pitch; return mMainCam.pitch;
} }
void Player::setPitch(float angle) void Camera::setPitch(float angle)
{ {
const float epsilon = 0.000001; const float epsilon = 0.000001f;
float limit = Ogre::Math::HALF_PI - epsilon; float limit = Ogre::Math::HALF_PI - epsilon;
if (mVanity.forced || mPreviewMode) { if(mPreviewMode)
limit /= 2; limit /= 2;
}
if (angle > limit) { if(angle > limit)
angle = limit; angle = limit;
} else if (angle < -limit) { else if(angle < -limit)
angle = -limit; angle = -limit;
}
if (mVanity.enabled || mPreviewMode) { if (mVanity.enabled || mPreviewMode) {
mPreviewCam.pitch = angle; mPreviewCam.pitch = angle;
} else { } else {
@ -264,11 +229,11 @@ namespace MWRender
} }
} }
void Player::setCameraDistance(float dist, bool adjust, bool override) void Camera::setCameraDistance(float dist, bool adjust, bool override)
{ {
if (mFirstPersonView && !mPreviewMode && !mVanity.enabled) { if(mFirstPersonView && !mPreviewMode && !mVanity.enabled)
return; return;
}
Ogre::Vector3 v(0.f, 0.f, dist); Ogre::Vector3 v(0.f, 0.f, dist);
if (adjust) { if (adjust) {
v += mCamera->getPosition(); v += mCamera->getPosition();
@ -293,7 +258,7 @@ namespace MWRender
} }
} }
void Player::setCameraDistance() void Camera::setCameraDistance()
{ {
if (mDistanceAdjusted) { if (mDistanceAdjusted) {
if (mVanity.enabled || mPreviewMode) { if (mVanity.enabled || mPreviewMode) {
@ -305,65 +270,54 @@ namespace MWRender
mDistanceAdjusted = false; mDistanceAdjusted = false;
} }
void Player::setAnimation(NpcAnimation *anim) void Camera::setAnimation(NpcAnimation *anim)
{ {
delete mAnimation; // If we're switching to a new NpcAnimation, ensure the old one is
// using a normal view mode
if(mAnimation && mAnimation != anim)
mAnimation->setViewMode(NpcAnimation::VM_Normal);
mAnimation = anim; mAnimation = anim;
mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson :
mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ? NpcAnimation::VM_Normal);
NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson);
} }
void Player::setHeight(float height) void Camera::setHeight(float height)
{ {
mHeight = height; mHeight = height;
mCameraNode->setPosition(0.f, 0.f, mHeight); mCameraNode->setPosition(0.f, 0.f, mHeight);
} }
float Player::getHeight() float Camera::getHeight()
{ {
return mHeight * mPlayerNode->getScale().z; return mHeight * mTrackingPtr.getRefData().getBaseNode()->getScale().z;
} }
bool Player::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera) bool Camera::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera)
{ {
mCamera->getParentSceneNode ()->needUpdate(true); mCamera->getParentSceneNode ()->needUpdate(true);
camera = mCamera->getRealPosition(); camera = mCamera->getRealPosition();
player = mPlayerNode->getPosition(); player = mTrackingPtr.getRefData().getBaseNode()->getPosition();
return mFirstPersonView && !mVanity.enabled && !mPreviewMode; return mFirstPersonView && !mVanity.enabled && !mPreviewMode;
} }
Ogre::Vector3 Player::getPosition() Ogre::Vector3 Camera::getPosition()
{ {
return mPlayerNode->getPosition(); return mTrackingPtr.getRefData().getBaseNode()->getPosition();
} }
void Player::getSightAngles(float &pitch, float &yaw) void Camera::getSightAngles(float &pitch, float &yaw)
{ {
pitch = mMainCam.pitch; pitch = mMainCam.pitch;
yaw = mMainCam.yaw; yaw = mMainCam.yaw;
} }
void Player::compensateYaw(float diff) void Camera::togglePlayerLooking(bool enable)
{
mPreviewCam.yaw -= diff;
Ogre::Quaternion zr(
Ogre::Radian(mPreviewCam.yaw),
Ogre::Vector3::NEGATIVE_UNIT_Z
);
Ogre::Quaternion xr(
Ogre::Radian(mPreviewCam.pitch),
Ogre::Vector3::UNIT_X);
mCameraNode->setOrientation(zr * xr);
}
void Player::togglePlayerLooking(bool enable)
{ {
mFreeLook = enable; mFreeLook = enable;
} }
void Player::setLowHeight(bool low) void Camera::setLowHeight(bool low)
{ {
if (low) { if (low) {
mCameraNode->setPosition(0.f, 0.f, mHeight * 0.85); mCameraNode->setPosition(0.f, 0.f, mHeight * 0.85);
@ -372,7 +326,7 @@ namespace MWRender
} }
} }
bool Player::isVanityOrPreviewModeEnabled() bool Camera::isVanityOrPreviewModeEnabled()
{ {
return mPreviewMode || mVanity.enabled; return mPreviewMode || mVanity.enabled;
} }

@ -1,8 +1,10 @@
#ifndef GAME_MWRENDER_PLAYER_H #ifndef GAME_MWRENDER_CAMERA_H
#define GAME_MWRENDER_PLAYER_H #define GAME_MWRENDER_CAMERA_H
#include <string> #include <string>
#include "../mwworld/ptr.hpp"
namespace Ogre namespace Ogre
{ {
class Vector3; class Vector3;
@ -10,24 +12,20 @@ namespace Ogre
class SceneNode; class SceneNode;
} }
namespace MWWorld
{
class Ptr;
}
namespace MWRender namespace MWRender
{ {
class NpcAnimation; class NpcAnimation;
/// \brief Player character rendering and camera control
class Player /// \brief Camera control
class Camera
{ {
struct CamData { struct CamData {
float pitch, yaw, offset; float pitch, yaw, offset;
}; };
Ogre::Camera *mCamera; MWWorld::Ptr mTrackingPtr;
Ogre::SceneNode *mPlayerNode; Ogre::Camera *mCamera;
Ogre::SceneNode *mCameraNode; Ogre::SceneNode *mCameraNode;
NpcAnimation *mAnimation; NpcAnimation *mAnimation;
@ -37,7 +35,7 @@ namespace MWRender
bool mFreeLook; bool mFreeLook;
struct { struct {
bool enabled, allowed, forced; bool enabled, allowed;
} mVanity; } mVanity;
float mHeight, mCameraDistance; float mHeight, mCameraDistance;
@ -51,15 +49,11 @@ namespace MWRender
void setLowHeight(bool low = true); void setLowHeight(bool low = true);
public: public:
Camera(Ogre::Camera *camera);
~Camera();
Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); /// Set where the camera is looking at. Uses Morrowind (euler) angles
~Player();
/// Set where the player is looking at. Uses Morrowind (euler) angles
/// \param rot Rotation angles in radians /// \param rot Rotation angles in radians
/// \return true if player object needs to bo rotated physically
bool rotate(const Ogre::Vector3 &rot, bool adjust);
void rotateCamera(const Ogre::Vector3 &rot, bool adjust); void rotateCamera(const Ogre::Vector3 &rot, bool adjust);
float getYaw(); float getYaw();
@ -68,22 +62,21 @@ namespace MWRender
float getPitch(); float getPitch();
void setPitch(float angle); void setPitch(float angle);
void compensateYaw(float diff); const std::string &getHandle() const;
std::string getHandle() const;
/// Attach camera to object /// Attach camera to object
/// \note there is no protection from attaching the same camera to
/// several different objects
void attachTo(const MWWorld::Ptr &); void attachTo(const MWWorld::Ptr &);
void toggleViewMode(); void toggleViewMode();
bool toggleVanityMode(bool enable, bool force = false); bool toggleVanityMode(bool enable);
void allowVanityMode(bool allow); void allowVanityMode(bool allow);
void togglePreviewMode(bool enable); void togglePreviewMode(bool enable);
bool isFirstPerson() const
{ return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); }
void update(float duration); void update(float duration);
/// Set camera distance for current mode. Don't work on 1st person view. /// Set camera distance for current mode. Don't work on 1st person view.
@ -96,8 +89,6 @@ namespace MWRender
void setCameraDistance(); void setCameraDistance();
void setAnimation(NpcAnimation *anim); void setAnimation(NpcAnimation *anim);
NpcAnimation *getAnimation() const
{ return mAnimation; }
void setHeight(float height); void setHeight(float height);
float getHeight(); float getHeight();

@ -12,6 +12,7 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
#include "renderconst.hpp" #include "renderconst.hpp"
#include "npcanimation.hpp" #include "npcanimation.hpp"
@ -134,6 +135,46 @@ namespace MWRender
void InventoryPreview::update(int sizeX, int sizeY) void InventoryPreview::update(int sizeX, int sizeY)
{ {
MWWorld::InventoryStore &inv = MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter);
MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
std::string groupname;
if(iter == inv.end())
groupname = "inventoryhandtohand";
else
{
const std::string &type = iter->getTypeName();
if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())
groupname = "inventoryweapononehand";
else if(type == typeid(ESM::Weapon).name())
{
MWWorld::LiveCellRef<ESM::Weapon> *ref = iter->get<ESM::Weapon>();
int type = ref->mBase->mData.mType;
if(type == ESM::Weapon::ShortBladeOneHand ||
type == ESM::Weapon::LongBladeOneHand ||
type == ESM::Weapon::BluntOneHand ||
type == ESM::Weapon::AxeOneHand)
groupname = "inventoryweapononehand";
else if(type == ESM::Weapon::LongBladeTwoHand ||
type == ESM::Weapon::BluntTwoClose ||
type == ESM::Weapon::AxeTwoHand)
groupname = "inventoryweapontwohand";
else if(type == ESM::Weapon::BluntTwoWide ||
type == ESM::Weapon::SpearTwoWide)
groupname = "inventoryweapontwowide";
else
groupname = "inventoryhandtohand";
}
else
groupname = "inventoryhandtohand";
}
if(groupname != mCurrentAnimGroup)
{
mCurrentAnimGroup = groupname;
mAnimation->play(mCurrentAnimGroup, "start", "stop", 0.0f, 0);
}
mAnimation->forceUpdate(); mAnimation->forceUpdate();
mAnimation->runAnimation(0.0f); mAnimation->runAnimation(0.0f);
@ -155,7 +196,10 @@ namespace MWRender
if (!mSelectionBuffer) if (!mSelectionBuffer)
mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0); mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0);
mAnimation->play("inventoryhandtohand", "start", "stop", false); mAnimation->showWeapons(true);
mCurrentAnimGroup = "inventoryhandtohand";
mAnimation->play(mCurrentAnimGroup, "start", "stop", 0.0f, 0);
} }
// -------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------
@ -189,7 +233,7 @@ namespace MWRender
void RaceSelectionPreview::onSetup () void RaceSelectionPreview::onSetup ()
{ {
mAnimation->play("idle", "start", "stop", false); mAnimation->play("idle", "start", "stop", 0.0f, 0);
updateCamera(); updateCamera();
} }

@ -53,6 +53,7 @@ namespace MWRender
MWWorld::Ptr mCharacter; MWWorld::Ptr mCharacter;
MWRender::NpcAnimation* mAnimation; MWRender::NpcAnimation* mAnimation;
std::string mCurrentAnimGroup;
std::string mName; std::string mName;

@ -1,10 +1,5 @@
#include "creatureanimation.hpp" #include "creatureanimation.hpp"
#include <OgreEntity.h>
#include <OgreParticleSystem.h>
#include <OgreSceneManager.h>
#include <OgreSubEntity.h>
#include "renderconst.hpp" #include "renderconst.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -26,31 +21,11 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
{ {
std::string model = "meshes\\"+ref->mBase->mModel; std::string model = "meshes\\"+ref->mBase->mModel;
createObjectList(mPtr.getRefData().getBaseNode(), model);
for(size_t i = 0;i < mObjectList.mEntities.size();i++)
{
Ogre::Entity *ent = mObjectList.mEntities[i];
ent->setVisibilityFlags(RV_Actors);
for(unsigned int j=0; j < ent->getNumSubEntities(); ++j)
{
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
}
}
for(size_t i = 0;i < mObjectList.mParticles.size();i++)
{
Ogre::ParticleSystem *part = mObjectList.mParticles[i];
part->setVisibilityFlags(RV_Actors);
part->setRenderQueueGroup(RQG_Alpha);
}
std::vector<std::string> names;
if((ref->mBase->mFlags&ESM::Creature::Biped)) if((ref->mBase->mFlags&ESM::Creature::Biped))
names.push_back("meshes\\base_anim.nif"); addObjectList(mPtr.getRefData().getBaseNode(), "meshes\\base_anim.nif", true);
names.push_back(model);
setAnimationSources(names); addObjectList(mPtr.getRefData().getBaseNode(), model, false);
setRenderProperties(mObjects.back().mObjectList, RV_Actors, RQG_Main, RQG_Alpha);
} }
} }

@ -20,7 +20,6 @@
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "player.hpp"
#include "renderconst.hpp" #include "renderconst.hpp"
using namespace Ogre; using namespace Ogre;

@ -39,8 +39,6 @@ namespace MWWorld
namespace MWRender namespace MWRender
{ {
class Player;
class Debugging class Debugging
{ {
OEngine::Physic::PhysicEngine* mEngine; OEngine::Physic::PhysicEngine* mEngine;

@ -11,6 +11,7 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "renderconst.hpp" #include "renderconst.hpp"
@ -29,7 +30,7 @@ const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize
{ ESM::PRT_LHand, "Left Hand" }, { ESM::PRT_LHand, "Left Hand" },
{ ESM::PRT_RWrist, "Right Wrist" }, { ESM::PRT_RWrist, "Right Wrist" },
{ ESM::PRT_LWrist, "Left Wrist" }, { ESM::PRT_LWrist, "Left Wrist" },
{ ESM::PRT_Shield, "Shield" }, { ESM::PRT_Shield, "Shield Bone" },
{ ESM::PRT_RForearm, "Right Forearm" }, { ESM::PRT_RForearm, "Right Forearm" },
{ ESM::PRT_LForearm, "Left Forearm" }, { ESM::PRT_LForearm, "Left Forearm" },
{ ESM::PRT_RUpperarm, "Right Upper Arm" }, { ESM::PRT_RUpperarm, "Right Upper Arm" },
@ -44,7 +45,7 @@ const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize
{ ESM::PRT_LLeg, "Left Upper Leg" }, { ESM::PRT_LLeg, "Left Upper Leg" },
{ ESM::PRT_RPauldron, "Right Clavicle" }, { ESM::PRT_RPauldron, "Right Clavicle" },
{ ESM::PRT_LPauldron, "Left Clavicle" }, { ESM::PRT_LPauldron, "Left Clavicle" },
{ ESM::PRT_Weapon, "Weapon" }, { ESM::PRT_Weapon, "Weapon Bone" },
{ ESM::PRT_Tail, "Tail" } { ESM::PRT_Tail, "Tail" }
}; };
@ -73,7 +74,10 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
mGloveL(inv.end()), mGloveL(inv.end()),
mGloveR(inv.end()), mGloveR(inv.end()),
mSkirtIter(inv.end()), mSkirtIter(inv.end()),
mViewMode(viewMode) mWeapon(inv.end()),
mShield(inv.end()),
mViewMode(viewMode),
mShowWeapons(false)
{ {
mNpc = mPtr.get<ESM::NPC>()->mBase; mNpc = mPtr.get<ESM::NPC>()->mBase;
@ -96,40 +100,25 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
createObjectList(node, smodel); addObjectList(node, smodel, true);
for(size_t i = 0;i < mObjectList.mEntities.size();i++) if(mBodyPrefix.find("argonian") != std::string::npos)
{ addObjectList(node, "meshes\\argonian_swimkna.nif", true);
Ogre::Entity *base = mObjectList.mEntities[i]; else if(!mNpc->isMale() && !isBeast)
addObjectList(node, "meshes\\base_anim_female.nif", true);
base->getUserObjectBindings().setUserAny(Ogre::Any(-1)); if(mNpc->mModel.length() > 0)
if (mVisibilityFlags != 0) addObjectList(node, "meshes\\"+mNpc->mModel, true);
base->setVisibilityFlags(mVisibilityFlags); if(mViewMode == VM_FirstPerson)
for(unsigned int j=0; j < base->getNumSubEntities(); ++j)
{
Ogre::SubEntity* subEnt = base->getSubEntity(j);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
}
}
for(size_t i = 0;i < mObjectList.mParticles.size();i++)
{ {
Ogre::ParticleSystem *part = mObjectList.mParticles[i]; /* A bit counter-intuitive, but unlike third-person anims, it seems
* beast races get both base_anim.1st.nif and base_animkna.1st.nif.
part->getUserObjectBindings().setUserAny(Ogre::Any(-1)); */
if(mVisibilityFlags != 0) addObjectList(node, "meshes\\base_anim.1st.nif", true);
part->setVisibilityFlags(mVisibilityFlags); if(isBeast)
part->setRenderQueueGroup(RQG_Alpha); addObjectList(node, "meshes\\base_animkna.1st.nif", true);
if(!mNpc->isMale() && !isBeast)
addObjectList(node, "meshes\\base_anim_female.1st.nif", true);
} }
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);
forceUpdate(); forceUpdate();
} }
@ -138,28 +127,31 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
assert(viewMode != VM_HeadOnly); assert(viewMode != VM_HeadOnly);
mViewMode = viewMode; mViewMode = viewMode;
/* FIXME: Enable this once first-person animations work. */ Ogre::SceneNode *node = mInsert->getParentSceneNode();
#if 0
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace); const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace);
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
std::vector<std::string> skelnames(1, smodel); clearExtraSources();
if(!mNpc->isMale() && !isBeast) if(mBodyPrefix.find("argonian") != std::string::npos)
skelnames.push_back("meshes\\base_anim_female.nif"); addObjectList(node, "meshes\\argonian_swimkna.nif", true);
else if(mBodyPrefix.find("argonian") != std::string::npos) else if(!mNpc->isMale() && !isBeast)
skelnames.push_back("meshes\\argonian_swimkna.nif"); addObjectList(node, "meshes\\base_anim_female.nif", true);
if(mNpc->mModel.length() > 0) if(mNpc->mModel.length() > 0)
skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel)); addObjectList(node, "meshes\\"+mNpc->mModel, true);
if(mViewMode == VM_FirstPerson) if(mViewMode == VM_FirstPerson)
{ {
smodel = (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif"); /* A bit counter-intuitive, but unlike third-person anims, it seems
skelnames.push_back(smodel); * beast races get both base_anim.1st.nif and base_animkna.1st.nif.
*/
addObjectList(node, "meshes\\base_anim.1st.nif", true);
if(isBeast)
addObjectList(node, "meshes\\base_animkna.1st.nif", true);
if(!mNpc->isMale() && !isBeast)
addObjectList(node, "meshes\\base_anim_female.1st.nif", true);
} }
setAnimationSources(skelnames); MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr);
#endif
for(size_t i = 0;i < sPartListSize;i++) for(size_t i = 0;i < sPartListSize;i++)
removeIndividualPart(i); removeIndividualPart(i);
@ -169,84 +161,33 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
void NpcAnimation::updateParts(bool forceupdate) void NpcAnimation::updateParts(bool forceupdate)
{ {
static const struct { static const struct {
int numRemoveParts; // Max: 1 MWWorld::ContainerStoreIterator NpcAnimation::*mPart;
ESM::PartReferenceType removeParts[1]; int mSlot;
int mBasePriority;
MWWorld::ContainerStoreIterator NpcAnimation::*part;
int slot;
int numReserveParts; // Max: 12
ESM::PartReferenceType reserveParts[12];
} slotlist[] = { } slotlist[] = {
{ 0, { }, // FIXME: Priority is based on the number of reserved slots. There should be a better way.
&NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe, { &NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe, 12 },
12, { ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg, { &NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt, 3 },
ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, { &NpcAnimation::mHelmet, MWWorld::InventoryStore::Slot_Helmet, 0 },
ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_RPauldron, ESM::PRT_LPauldron } { &NpcAnimation::mCuirass, MWWorld::InventoryStore::Slot_Cuirass, 0 },
}, { &NpcAnimation::mGreaves, MWWorld::InventoryStore::Slot_Greaves, 0 },
{ &NpcAnimation::mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron, 0 },
{ 0, { }, { &NpcAnimation::mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron, 0 },
&NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt, { &NpcAnimation::mBoots, MWWorld::InventoryStore::Slot_Boots, 0 },
3, { ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg } { &NpcAnimation::mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet, 0 },
}, { &NpcAnimation::mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet, 0 },
{ &NpcAnimation::mShirt, MWWorld::InventoryStore::Slot_Shirt, 0 },
{ 1, { ESM::PRT_Hair }, { &NpcAnimation::mPants, MWWorld::InventoryStore::Slot_Pants, 0 },
&NpcAnimation::mHelmet, MWWorld::InventoryStore::Slot_Helmet, { &NpcAnimation::mShield, MWWorld::InventoryStore::Slot_CarriedLeft, 0 },
0, { } { &NpcAnimation::mWeapon, MWWorld::InventoryStore::Slot_CarriedRight, 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, { }
},
}; };
static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]);
MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr);
for(size_t i = 0;!forceupdate && i < slotlistsize;i++) for(size_t i = 0;!forceupdate && i < slotlistsize;i++)
{ {
MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot); MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].mSlot);
if(this->*slotlist[i].part != iter) if(this->*slotlist[i].mPart != iter)
{ {
forceupdate = true; forceupdate = true;
break; break;
@ -259,40 +200,55 @@ void NpcAnimation::updateParts(bool forceupdate)
if(mViewMode == VM_FirstPerson) if(mViewMode == VM_FirstPerson)
{ {
for(size_t i = 0;i < slotlistsize;i++) for(size_t i = 0;i < slotlistsize;i++)
this->*slotlist[i].part = inv.getSlot(slotlist[i].slot); this->*slotlist[i].mPart = inv.getSlot(slotlist[i].mSlot);
return; return;
} }
for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++)
{ {
MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot); MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].mSlot);
this->*slotlist[i].part = iter; this->*slotlist[i].mPart = iter;
removePartGroup(slotlist[i].slot); removePartGroup(slotlist[i].mSlot);
if(this->*slotlist[i].part == inv.end()) if(this->*slotlist[i].mPart == inv.end())
continue; continue;
for(int rem = 0;rem < slotlist[i].numRemoveParts;rem++) if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Helmet)
removeIndividualPart(slotlist[i].removeParts[rem]); removeIndividualPart(ESM::PRT_Hair);
int prio = 1; int prio = 1;
MWWorld::ContainerStoreIterator &store = this->*slotlist[i].part; MWWorld::ContainerStoreIterator &store = this->*slotlist[i].mPart;
if(store->getTypeName() == typeid(ESM::Clothing).name()) if(store->getTypeName() == typeid(ESM::Clothing).name())
{ {
prio = ((slotlist[i].numReserveParts+1)<<1) + 0; prio = ((slotlist[i].mBasePriority+1)<<1) + 0;
const ESM::Clothing *clothes = store->get<ESM::Clothing>()->mBase; const ESM::Clothing *clothes = store->get<ESM::Clothing>()->mBase;
addPartGroup(slotlist[i].slot, prio, clothes->mParts.mParts); addPartGroup(slotlist[i].mSlot, prio, clothes->mParts.mParts);
} }
else if(store->getTypeName() == typeid(ESM::Armor).name()) else if(store->getTypeName() == typeid(ESM::Armor).name())
{ {
prio = ((slotlist[i].numReserveParts+1)<<1) + 1; prio = ((slotlist[i].mBasePriority+1)<<1) + 1;
const ESM::Armor *armor = store->get<ESM::Armor>()->mBase; const ESM::Armor *armor = store->get<ESM::Armor>()->mBase;
addPartGroup(slotlist[i].slot, prio, armor->mParts.mParts); addPartGroup(slotlist[i].mSlot, prio, armor->mParts.mParts);
} }
for(int res = 0;res < slotlist[i].numReserveParts;res++) if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Robe)
reserveIndividualPart(slotlist[i].reserveParts[res], slotlist[i].slot, prio); {
ESM::PartReferenceType parts[] = {
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
};
size_t parts_size = sizeof(parts)/sizeof(parts[0]);
for(size_t p = 0;p < parts_size;++p)
reserveIndividualPart(parts[p], slotlist[i].mSlot, prio);
}
else if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Skirt)
{
reserveIndividualPart(ESM::PRT_Groin, slotlist[i].mSlot, prio);
reserveIndividualPart(ESM::PRT_RLeg, slotlist[i].mSlot, prio);
reserveIndividualPart(ESM::PRT_LLeg, slotlist[i].mSlot, prio);
}
} }
if(mViewMode != VM_FirstPerson) if(mViewMode != VM_FirstPerson)
@ -305,29 +261,7 @@ void NpcAnimation::updateParts(bool forceupdate)
if(mViewMode == VM_HeadOnly) if(mViewMode == VM_HeadOnly)
return; return;
std::map<int, int> bodypartMap; showWeapons(mShowWeapons);
bodypartMap[ESM::PRT_Neck] = ESM::BodyPart::MP_Neck;
bodypartMap[ESM::PRT_Cuirass] = ESM::BodyPart::MP_Chest;
bodypartMap[ESM::PRT_Groin] = ESM::BodyPart::MP_Groin;
bodypartMap[ESM::PRT_RHand] = ESM::BodyPart::MP_Hand;
bodypartMap[ESM::PRT_LHand] = ESM::BodyPart::MP_Hand;
bodypartMap[ESM::PRT_RWrist] = ESM::BodyPart::MP_Wrist;
bodypartMap[ESM::PRT_LWrist] = ESM::BodyPart::MP_Wrist;
bodypartMap[ESM::PRT_RForearm] = ESM::BodyPart::MP_Forearm;
bodypartMap[ESM::PRT_LForearm] = ESM::BodyPart::MP_Forearm;
bodypartMap[ESM::PRT_RUpperarm] = ESM::BodyPart::MP_Upperarm;
bodypartMap[ESM::PRT_LUpperarm] = ESM::BodyPart::MP_Upperarm;
bodypartMap[ESM::PRT_RFoot] = ESM::BodyPart::MP_Foot;
bodypartMap[ESM::PRT_LFoot] = ESM::BodyPart::MP_Foot;
bodypartMap[ESM::PRT_RAnkle] = ESM::BodyPart::MP_Ankle;
bodypartMap[ESM::PRT_LAnkle] = ESM::BodyPart::MP_Ankle;
bodypartMap[ESM::PRT_RKnee] = ESM::BodyPart::MP_Knee;
bodypartMap[ESM::PRT_LKnee] = ESM::BodyPart::MP_Knee;
bodypartMap[ESM::PRT_RLeg] = ESM::BodyPart::MP_Upperleg;
bodypartMap[ESM::PRT_LLeg] = ESM::BodyPart::MP_Upperleg;
bodypartMap[ESM::PRT_Tail] = ESM::BodyPart::MP_Tail;
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const int Flag_Female = 0x01; const int Flag_Female = 0x01;
const int Flag_FirstPerson = 0x02; const int Flag_FirstPerson = 0x02;
@ -344,21 +278,43 @@ void NpcAnimation::updateParts(bool forceupdate)
std::pair<std::string, int> thisCombination = std::make_pair(race, flags); std::pair<std::string, int> thisCombination = std::make_pair(race, flags);
if (sRaceMapping.find(thisCombination) == sRaceMapping.end()) if (sRaceMapping.find(thisCombination) == sRaceMapping.end())
{ {
sRaceMapping[thisCombination].resize(ESM::PRT_Count); static std::map<int, int> bodypartMap;
for (int i=0; i<ESM::PRT_Count; ++i) if(bodypartMap.size() == 0)
sRaceMapping[thisCombination][i] = NULL; {
bodypartMap[ESM::PRT_Neck] = ESM::BodyPart::MP_Neck;
bodypartMap[ESM::PRT_Cuirass] = ESM::BodyPart::MP_Chest;
bodypartMap[ESM::PRT_Groin] = ESM::BodyPart::MP_Groin;
bodypartMap[ESM::PRT_RHand] = ESM::BodyPart::MP_Hand;
bodypartMap[ESM::PRT_LHand] = ESM::BodyPart::MP_Hand;
bodypartMap[ESM::PRT_RWrist] = ESM::BodyPart::MP_Wrist;
bodypartMap[ESM::PRT_LWrist] = ESM::BodyPart::MP_Wrist;
bodypartMap[ESM::PRT_RForearm] = ESM::BodyPart::MP_Forearm;
bodypartMap[ESM::PRT_LForearm] = ESM::BodyPart::MP_Forearm;
bodypartMap[ESM::PRT_RUpperarm] = ESM::BodyPart::MP_Upperarm;
bodypartMap[ESM::PRT_LUpperarm] = ESM::BodyPart::MP_Upperarm;
bodypartMap[ESM::PRT_RFoot] = ESM::BodyPart::MP_Foot;
bodypartMap[ESM::PRT_LFoot] = ESM::BodyPart::MP_Foot;
bodypartMap[ESM::PRT_RAnkle] = ESM::BodyPart::MP_Ankle;
bodypartMap[ESM::PRT_LAnkle] = ESM::BodyPart::MP_Ankle;
bodypartMap[ESM::PRT_RKnee] = ESM::BodyPart::MP_Knee;
bodypartMap[ESM::PRT_LKnee] = ESM::BodyPart::MP_Knee;
bodypartMap[ESM::PRT_RLeg] = ESM::BodyPart::MP_Upperleg;
bodypartMap[ESM::PRT_LLeg] = ESM::BodyPart::MP_Upperleg;
bodypartMap[ESM::PRT_Tail] = ESM::BodyPart::MP_Tail;
}
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>(); sRaceMapping[thisCombination].resize(ESM::PRT_Count, NULL);
for (MWWorld::Store<ESM::BodyPart>::iterator it = partStore.begin(); it != partStore.end(); ++it) const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
for(MWWorld::Store<ESM::BodyPart>::iterator it = partStore.begin(); it != partStore.end(); ++it)
{ {
const ESM::BodyPart& bodypart = *it; const ESM::BodyPart& bodypart = *it;
if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)
continue; continue;
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
{
continue; continue;
}
if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))
continue; continue;
if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace)) if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace))
@ -382,31 +338,20 @@ void NpcAnimation::updateParts(bool forceupdate)
if (mPartPriorities[part] < 1 && bodypart) if (mPartPriorities[part] < 1 && bodypart)
addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel); addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel);
} }
showWeapons(mShowWeapons);
} }
NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename)
{ {
NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mObjectList.mSkelBase, bonename, NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model);
mInsert, model); setRenderProperties(objects, mVisibilityFlags, RQG_Main, RQG_Alpha);
for(size_t i = 0;i < objects.mEntities.size();i++) for(size_t i = 0;i < objects.mEntities.size();i++)
{
objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
if(mVisibilityFlags != 0)
objects.mEntities[i]->setVisibilityFlags(mVisibilityFlags);
for(unsigned int j=0; j < objects.mEntities[i]->getNumSubEntities(); ++j)
{
Ogre::SubEntity* subEnt = objects.mEntities[i]->getSubEntity(j);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
}
}
for(size_t i = 0;i < objects.mParticles.size();i++) for(size_t i = 0;i < objects.mParticles.size();i++)
{
objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
if(mVisibilityFlags != 0)
objects.mParticles[i]->setVisibilityFlags(mVisibilityFlags);
objects.mParticles[i]->setRenderQueueGroup(RQG_Alpha);
}
if(objects.mSkelBase) if(objects.mSkelBase)
{ {
Ogre::AnimationStateSet *aset = objects.mSkelBase->getAllAnimationStates(); Ogre::AnimationStateSet *aset = objects.mSkelBase->getAllAnimationStates();
@ -422,6 +367,7 @@ NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, in
while(boneiter.hasMoreElements()) while(boneiter.hasMoreElements())
boneiter.getNext()->setManuallyControlled(true); boneiter.getNext()->setManuallyControlled(true);
} }
return objects; return objects;
} }
@ -435,14 +381,16 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
mTimeToChange -= timepassed; mTimeToChange -= timepassed;
Ogre::Vector3 ret = Animation::runAnimation(timepassed); Ogre::Vector3 ret = Animation::runAnimation(timepassed);
const Ogre::SkeletonInstance *skelsrc = mObjectList.mSkelBase->getSkeleton();
Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
for(size_t i = 0;i < sPartListSize;i++) for(size_t i = 0;i < sPartListSize;i++)
{ {
Ogre::Entity *ent = mObjectParts[i].mSkelBase; Ogre::Entity *ent = mObjectParts[i].mSkelBase;
if(!ent) continue; if(!ent) continue;
updateSkeletonInstance(skelsrc, ent->getSkeleton()); updateSkeletonInstance(baseinst, ent->getSkeleton());
ent->getAllAnimationStates()->_notifyDirty(); ent->getAllAnimationStates()->_notifyDirty();
} }
return ret; return ret;
} }
@ -523,4 +471,23 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::
} }
} }
void NpcAnimation::showWeapons(bool showWeapon)
{
mShowWeapons = showWeapon;
if(showWeapon)
{
MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr);
mWeapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(mWeapon != inv.end()) // special case for weapons
{
std::string mesh = MWWorld::Class::get(*mWeapon).getModel(*mWeapon);
addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh);
}
}
else
{
removeIndividualPart(ESM::PRT_Weapon);
}
}
} }

@ -46,6 +46,7 @@ private:
std::string mHairModel; std::string mHairModel;
std::string mBodyPrefix; std::string mBodyPrefix;
ViewMode mViewMode; ViewMode mViewMode;
bool mShowWeapons;
float mTimeToChange; float mTimeToChange;
MWWorld::ContainerStoreIterator mRobe; MWWorld::ContainerStoreIterator mRobe;
@ -60,6 +61,8 @@ private:
MWWorld::ContainerStoreIterator mGloveL; MWWorld::ContainerStoreIterator mGloveL;
MWWorld::ContainerStoreIterator mGloveR; MWWorld::ContainerStoreIterator mGloveR;
MWWorld::ContainerStoreIterator mSkirtIter; MWWorld::ContainerStoreIterator mSkirtIter;
MWWorld::ContainerStoreIterator mWeapon;
MWWorld::ContainerStoreIterator mShield;
int mVisibilityFlags; int mVisibilityFlags;
@ -85,6 +88,8 @@ public:
virtual Ogre::Vector3 runAnimation(float timepassed); virtual Ogre::Vector3 runAnimation(float timepassed);
virtual void showWeapons(bool showWeapon);
void setViewMode(ViewMode viewMode); void setViewMode(ViewMode viewMode);
void forceUpdate() void forceUpdate()

@ -30,6 +30,8 @@
#include "../mwbase/inputmanager.hpp" // FIXME #include "../mwbase/inputmanager.hpp" // FIXME
#include "../mwbase/windowmanager.hpp" // FIXME #include "../mwbase/windowmanager.hpp" // FIXME
#include "../mwmechanics/creaturestats.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
@ -47,12 +49,14 @@ using namespace Ogre;
namespace MWRender { namespace MWRender {
RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir,
const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine,MWWorld::Fallback* fallback) const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine,
MWWorld::Fallback* fallback)
: mRendering(_rend) : mRendering(_rend)
, mFallback(fallback) , mFallback(fallback)
, mObjects(mRendering,mFallback) , mObjects(mRendering, mFallback)
, mActors(mRendering, this) , mActors(mRendering, this)
, mPlayerAnimation(NULL)
, mAmbientMode(0) , mAmbientMode(0)
, mSunEnabled(0) , mSunEnabled(0)
, mPhysicsEngine(engine) , mPhysicsEngine(engine)
@ -146,14 +150,13 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
applyCompositors(); applyCompositors();
SceneNode *rt = mRendering.getScene()->getRootSceneNode(); mRootNode = mRendering.getScene()->getRootSceneNode();
mRootNode = rt; mRootNode->createChildSceneNode("player");
mObjects.setRootNode(mRootNode); mObjects.setRootNode(mRootNode);
mActors.setRootNode(mRootNode); mActors.setRootNode(mRootNode);
Ogre::SceneNode *playerNode = mRootNode->createChildSceneNode ("player"); mCamera = new MWRender::Camera(mRendering.getCamera());
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
mShadows = new Shadows(&mRendering); mShadows = new Shadows(&mRendering);
@ -181,7 +184,8 @@ RenderingManager::~RenderingManager ()
mRendering.getWindow()->removeListener(this); mRendering.getWindow()->removeListener(this);
mRendering.removeWindowEventListener(this); mRendering.removeWindowEventListener(this);
delete mPlayer; delete mPlayerAnimation;
delete mCamera;
delete mSkyManager; delete mSkyManager;
delete mDebugging; delete mDebugging;
delete mShadows; delete mShadows;
@ -262,37 +266,20 @@ void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3
ptr.getRefData().getBaseNode()->setScale(scale); ptr.getRefData().getBaseNode()->setScale(scale);
} }
bool RenderingManager::rotateObject(const MWWorld::Ptr &ptr, Ogre::Vector3 &rot, bool adjust) void RenderingManager::rotateObject(const MWWorld::Ptr &ptr)
{ {
bool isActive = ptr.getRefData().getBaseNode() != 0; Ogre::Vector3 rot(ptr.getRefData().getPosition().rot);
bool isPlayer = isActive && ptr.getRefData().getHandle() == "player";
bool force = true;
if (isPlayer) if(ptr.getRefData().getHandle() == mCamera->getHandle() &&
force = mPlayer->rotate(rot, adjust); !mCamera->isVanityOrPreviewModeEnabled())
mCamera->rotateCamera(rot, false);
MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z); Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z);
if (!isPlayer && isActive) if(!MWWorld::Class::get(ptr).isActor())
{ newo = Ogre::Quaternion(Ogre::Radian(-rot.x), Ogre::Vector3::UNIT_X) *
if(adjust) Ogre::Quaternion(Ogre::Radian(-rot.y), Ogre::Vector3::UNIT_Y) * newo;
{
const float *objRot = ptr.getRefData().getPosition().rot;
rot.x += objRot[0];
rot.y += objRot[1];
rot.z += objRot[2];
}
Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(-rot.x), Ogre::Vector3::UNIT_X) * ptr.getRefData().getBaseNode()->setOrientation(newo);
Ogre::Quaternion(Ogre::Radian(-rot.y), Ogre::Vector3::UNIT_Y) *
Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z);
ptr.getRefData().getBaseNode()->setOrientation(newo);
}
else if(isPlayer)
{
rot.x = -mPlayer->getPitch();
rot.z = mPlayer->getYaw();
}
return force;
} }
void void
@ -315,27 +302,29 @@ void RenderingManager::update (float duration, bool paused)
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayer().getPlayer();
int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude;
mRendering.getFader()->setFactor(1.f-(blind / 100.f));
setAmbientMode();
// player position // player position
MWWorld::RefData &data = MWWorld::RefData &data = player.getRefData();
MWBase::Environment::get()
.getWorld()
->getPlayer()
.getPlayer()
.getRefData();
float *_playerPos = data.getPosition().pos; float *_playerPos = data.getPosition().pos;
Ogre::Vector3 playerPos(_playerPos[0], _playerPos[1], _playerPos[2]); Ogre::Vector3 playerPos(_playerPos[0], _playerPos[1], _playerPos[2]);
Ogre::Vector3 orig, dest; Ogre::Vector3 orig, dest;
mPlayer->setCameraDistance(); mCamera->setCameraDistance();
if (!mPlayer->getPosition(orig, dest)) { if(!mCamera->getPosition(orig, dest))
orig.z += mPlayer->getHeight() * mRootNode->getScale().z; {
orig.z += mCamera->getHeight() * mRootNode->getScale().z;
btVector3 btOrig(orig.x, orig.y, orig.z); btVector3 btOrig(orig.x, orig.y, orig.z);
btVector3 btDest(dest.x, dest.y, dest.z); btVector3 btDest(dest.x, dest.y, dest.z);
std::pair<std::string, float> test = std::pair<std::string,float> test = mPhysicsEngine->rayTest(btOrig, btDest);
mPhysicsEngine->rayTest(btOrig, btDest);
if (!test.first.empty()) { if (!test.first.empty()) {
mPlayer->setCameraDistance(test.second * orig.distance(dest), false, false); mCamera->setCameraDistance(test.second * orig.distance(dest), false, false);
} }
} }
@ -349,14 +338,12 @@ void RenderingManager::update (float duration, bool paused)
Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition(); Ogre::Vector3 cam = mRendering.getCamera()->getRealPosition();
applyFog(world->isUnderwater (world->getPlayer().getPlayer().getCell(), cam)); applyFog(world->isUnderwater(player.getCell(), cam));
if(paused) if(paused)
{
return; return;
}
mPlayer->update(duration); mCamera->update(duration);
mActors.update (duration); mActors.update (duration);
mObjects.update (duration); mObjects.update (duration);
@ -367,18 +354,11 @@ void RenderingManager::update (float duration, bool paused)
mSkyManager->setGlare(mOcclusionQuery->getSunVisibility()); mSkyManager->setGlare(mOcclusionQuery->getSunVisibility());
Ogre::SceneNode *node = data.getBaseNode(); Ogre::SceneNode *node = data.getBaseNode();
//Ogre::Quaternion orient = Ogre::Quaternion orient = node->_getDerivedOrientation();
//node->convertLocalToWorldOrientation(node->_getDerivedOrientation());
Ogre::Quaternion orient =
node->_getDerivedOrientation();
mLocalMap->updatePlayer(playerPos, orient); mLocalMap->updatePlayer(playerPos, orient);
mWater->updateUnderwater( mWater->updateUnderwater(world->isUnderwater(player.getCell(), cam));
world->isUnderwater(
world->getPlayer().getPlayer().getCell(),
cam)
);
mWater->update(duration, playerPos); mWater->update(duration, playerPos);
} }
@ -597,8 +577,15 @@ void RenderingManager::setSunColour(const Ogre::ColourValue& colour)
void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour)
{ {
mRendering.getScene()->setAmbientLight(colour); mAmbientColor = colour;
mTerrainManager->setAmbient(colour);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
int nightEye = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::NightEye)).mMagnitude;
Ogre::ColourValue final = colour;
final += Ogre::ColourValue(0.7,0.7,0.7,0) * std::min(1.f, (nightEye/100.f));
mRendering.getScene()->setAmbientLight(final);
mTerrainManager->setAmbient(final);
} }
void RenderingManager::sunEnable(bool real) void RenderingManager::sunEnable(bool real)
@ -865,39 +852,49 @@ void RenderingManager::getTriangleBatchCount(unsigned int &triangles, unsigned i
} }
} }
void RenderingManager::attachCameraTo(const MWWorld::Ptr &ptr) void RenderingManager::setupPlayer(const MWWorld::Ptr &ptr)
{ {
mPlayer->attachTo(ptr); ptr.getRefData().setBaseNode(mRendering.getScene()->getSceneNode("player"));
mCamera->attachTo(ptr);
} }
void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr)
{ {
MWRender::NpcAnimation *anim = if(!mPlayerAnimation)
new MWRender::NpcAnimation( {
ptr, ptr.getRefData ().getBaseNode (), mPlayerAnimation = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(),
MWWorld::Class::get(ptr).getInventoryStore(ptr), RV_Actors MWWorld::Class::get(ptr).getInventoryStore(ptr),
); RV_Actors);
mPlayer->setAnimation(anim); }
mWater->removeEmitter (ptr); else
mWater->addEmitter (ptr); {
// Reconstruct the NpcAnimation in-place
mPlayerAnimation->~NpcAnimation();
new(mPlayerAnimation) NpcAnimation(ptr, ptr.getRefData().getBaseNode(),
MWWorld::Class::get(ptr).getInventoryStore(ptr),
RV_Actors);
}
mCamera->setAnimation(mPlayerAnimation);
mWater->removeEmitter(ptr);
mWater->addEmitter(ptr);
// apply race height // apply race height
MWBase::Environment::get().getWorld()->scaleObject(ptr, 1.f); MWBase::Environment::get().getWorld()->scaleObject(ptr, 1.f);
} }
void RenderingManager::getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw) void RenderingManager::getCameraData(Ogre::Vector3 &eyepos, float &pitch, float &yaw)
{ {
eyepos = mPlayer->getPosition(); eyepos = mCamera->getPosition();
eyepos.z += mPlayer->getHeight(); eyepos.z += mCamera->getHeight();
mPlayer->getSightAngles(pitch, yaw); mCamera->getSightAngles(pitch, yaw);
} }
bool RenderingManager::vanityRotateCamera(float* rot) bool RenderingManager::vanityRotateCamera(const float *rot)
{ {
if(!mPlayer->isVanityOrPreviewModeEnabled()) if(!mCamera->isVanityOrPreviewModeEnabled())
return false; return false;
Ogre::Vector3 vRot(rot); Ogre::Vector3 vRot(rot);
mPlayer->rotateCamera(vRot, true); mCamera->rotateCamera(vRot, true);
return true; return true;
} }
@ -920,7 +917,7 @@ Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr)
{ {
Animation *anim = mActors.getAnimation(ptr); Animation *anim = mActors.getAnimation(ptr);
if(!anim && ptr.getRefData().getHandle() == "player") if(!anim && ptr.getRefData().getHandle() == "player")
anim = mPlayer->getAnimation(); anim = mPlayerAnimation;
return anim; return anim;
} }

@ -17,7 +17,7 @@
#include "objects.hpp" #include "objects.hpp"
#include "actors.hpp" #include "actors.hpp"
#include "player.hpp" #include "camera.hpp"
#include "occlusionquery.hpp" #include "occlusionquery.hpp"
namespace Ogre namespace Ogre
@ -50,49 +50,44 @@ namespace MWRender
class VideoPlayer; class VideoPlayer;
class Animation; class Animation;
class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener { class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener
{
private: private:
virtual MWRender::Objects& getObjects(); virtual MWRender::Objects& getObjects();
virtual MWRender::Actors& getActors(); virtual MWRender::Actors& getActors();
public: public:
RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir,
const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine,MWWorld::Fallback* fallback); const boost::filesystem::path& cacheDir, OEngine::Physic::PhysicEngine* engine,
MWWorld::Fallback* fallback);
virtual ~RenderingManager(); virtual ~RenderingManager();
void togglePOV() { void togglePOV()
mPlayer->toggleViewMode(); { mCamera->toggleViewMode(); }
}
void togglePreviewMode(bool enable) { void togglePreviewMode(bool enable)
mPlayer->togglePreviewMode(enable); { mCamera->togglePreviewMode(enable); }
}
bool toggleVanityMode(bool enable, bool force) { bool toggleVanityMode(bool enable)
return mPlayer->toggleVanityMode(enable, force); { return mCamera->toggleVanityMode(enable); }
}
void allowVanityMode(bool allow) { void allowVanityMode(bool allow)
mPlayer->allowVanityMode(allow); { mCamera->allowVanityMode(allow); }
}
void togglePlayerLooking(bool enable) { void togglePlayerLooking(bool enable)
mPlayer->togglePlayerLooking(enable); { mCamera->togglePlayerLooking(enable); }
}
void changeVanityModeScale(float factor) { void changeVanityModeScale(float factor)
if (mPlayer->isVanityOrPreviewModeEnabled()) {
mPlayer->setCameraDistance(-factor/120.f*10, true, true); if(mCamera->isVanityOrPreviewModeEnabled())
mCamera->setCameraDistance(-factor/120.f*10, true, true);
} }
bool vanityRotateCamera(float* rot); bool vanityRotateCamera(const float *rot);
void getPlayerData(Ogre::Vector3 &eyepos, float &pitch, float &yaw); void getCameraData(Ogre::Vector3 &eyepos, float &pitch, float &yaw);
void attachCameraTo(const MWWorld::Ptr &ptr); void setupPlayer(const MWWorld::Ptr &ptr);
void renderPlayer(const MWWorld::Ptr &ptr); void renderPlayer(const MWWorld::Ptr &ptr);
SkyManager* getSkyManager(); SkyManager* getSkyManager();
@ -121,11 +116,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
void moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position); void moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position);
void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale); void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale);
/// Rotates object accordingly to its type /// Updates an object's rotation
/// \param rot euler angles in radians void rotateObject (const MWWorld::Ptr& ptr);
/// \param adjust indicates should rotation be set or adjusted
/// \return true if object needs to be rotated physically
bool rotateObject (const MWWorld::Ptr& ptr, Ogre::Vector3 &rot, bool adjust = false);
void setWaterHeight(const float height); void setWaterHeight(const float height);
void toggleWater(); void toggleWater();
@ -207,12 +199,11 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
void stopVideo(); void stopVideo();
void frameStarted(float dt); void frameStarted(float dt);
protected: protected:
virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowResized(Ogre::RenderWindow* rw);
virtual void windowClosed(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw);
private: private:
sh::Factory* mFactory; sh::Factory* mFactory;
void setAmbientMode(); void setAmbientMode();
@ -241,6 +232,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
MWRender::Objects mObjects; MWRender::Objects mObjects;
MWRender::Actors mActors; MWRender::Actors mActors;
MWRender::NpcAnimation *mPlayerAnimation;
// 0 normal, 1 more bright, 2 max // 0 normal, 1 more bright, 2 max
int mAmbientMode; int mAmbientMode;
@ -255,7 +248,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList
OEngine::Physic::PhysicEngine* mPhysicsEngine; OEngine::Physic::PhysicEngine* mPhysicsEngine;
MWRender::Player *mPlayer; MWRender::Camera *mCamera;
MWRender::Debugging *mDebugging; MWRender::Debugging *mDebugging;

@ -205,7 +205,7 @@ unsigned int Moon::getPhaseInt() const
return 0; return 0;
} }
SkyManager::SkyManager (SceneNode* root, Camera* pCamera) SkyManager::SkyManager(Ogre::SceneNode *root, Ogre::Camera *pCamera)
: mHour(0.0f) : mHour(0.0f)
, mDay(0) , mDay(0)
, mMonth(0) , mMonth(0)

@ -54,9 +54,10 @@ namespace MWRender
TerrainMaterial::Profile::~Profile() TerrainMaterial::Profile::~Profile()
{ {
if (mMaterial)
sh::Factory::getInstance().destroyMaterialInstance(mMaterial->getName());
} }
Ogre::MaterialPtr TerrainMaterial::Profile::generate(const Ogre::Terrain* terrain) Ogre::MaterialPtr TerrainMaterial::Profile::generate(const Ogre::Terrain* terrain)
{ {
const Ogre::String& matName = terrain->getMaterialName(); const Ogre::String& matName = terrain->getMaterialName();

@ -318,5 +318,18 @@ op 0x20001fb: DropSoulGem, explicit reference
op 0x20001fc: OnDeath op 0x20001fc: OnDeath
op 0x20001fd: IsWerewolf op 0x20001fd: IsWerewolf
op 0x20001fe: IsWerewolf, explicit reference op 0x20001fe: IsWerewolf, explicit reference
op 0x20001ff: Rotate
op 0x2000200: Rotate, explicit reference
op 0x2000201: RotateWorld
op 0x2000202: RotateWorld, explicit reference
op 0x2000203: SetAtStart
op 0x2000204: SetAtStart, explicit
op 0x2000205: OnDeath, explicit
op 0x2000206: Move
op 0x2000207: Move, explicit
op 0x2000208: MoveWorld
op 0x2000209: MoveWorld, explicit
op 0x200020a: Fall
op 0x200020b: Fall, explicit
opcodes 0x20001ff-0x3ffffff unused opcodes 0x200020c-0x3ffffff unused

@ -282,10 +282,8 @@ namespace MWScript
MWBase::World *world = MWBase::World *world =
MWBase::Environment::get().getWorld(); MWBase::Environment::get().getWorld();
if (world->toggleVanityMode(sActivate, true)) { if (world->toggleVanityMode(sActivate)) {
context.report( context.report(sActivate ? "Vanity Mode -> On" : "Vanity Mode -> Off");
(sActivate) ? "Vanity Mode -> On" : "Vanity Mode -> Off"
);
sActivate = !sActivate; sActivate = !sActivate;
} else { } else {
context.report("Vanity Mode -> No"); context.report("Vanity Mode -> No");
@ -557,6 +555,16 @@ namespace MWScript
} }
}; };
template <class R>
class OpFall : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
}
};
const int opcodeXBox = 0x200000c; const int opcodeXBox = 0x200000c;
const int opcodeOnActivate = 0x200000d; const int opcodeOnActivate = 0x200000d;
const int opcodeActivate = 0x2000075; const int opcodeActivate = 0x2000075;
@ -598,6 +606,8 @@ namespace MWScript
const int opcodeSetDelete = 0x20001e5; const int opcodeSetDelete = 0x20001e5;
const int opcodeSetDeleteExplicit = 0x20001e6; const int opcodeSetDeleteExplicit = 0x20001e6;
const int opcodeGetSquareRoot = 0x20001e7; const int opcodeGetSquareRoot = 0x20001e7;
const int opcodeFall = 0x200020a;
const int opcodeFallExplicit = 0x200020b;
const int opcodePlayBink = 0x20001f7; const int opcodePlayBink = 0x20001f7;
@ -639,6 +649,7 @@ namespace MWScript
extensions.registerFunction ("getcurrenttime", 'f', "", opcodeGetCurrentTime); extensions.registerFunction ("getcurrenttime", 'f', "", opcodeGetCurrentTime);
extensions.registerInstruction ("setdelete", "l", opcodeSetDelete, opcodeSetDeleteExplicit); extensions.registerInstruction ("setdelete", "l", opcodeSetDelete, opcodeSetDeleteExplicit);
extensions.registerFunction ("getsquareroot", 'f', "f", opcodeGetSquareRoot); extensions.registerFunction ("getsquareroot", 'f', "f", opcodeGetSquareRoot);
extensions.registerInstruction ("fall", "", opcodeFall, opcodeFallExplicit);
} }
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
@ -685,6 +696,9 @@ namespace MWScript
interpreter.installSegment5 (opcodeSetDelete, new OpSetDelete<ImplicitRef>); interpreter.installSegment5 (opcodeSetDelete, new OpSetDelete<ImplicitRef>);
interpreter.installSegment5 (opcodeSetDeleteExplicit, new OpSetDelete<ExplicitRef>); interpreter.installSegment5 (opcodeSetDeleteExplicit, new OpSetDelete<ExplicitRef>);
interpreter.installSegment5 (opcodeGetSquareRoot, new OpGetSquareRoot); interpreter.installSegment5 (opcodeGetSquareRoot, new OpGetSquareRoot);
interpreter.installSegment5 (opcodeFall, new OpFall<ImplicitRef>);
interpreter.installSegment5 (opcodeFallExplicit, new OpFall<ExplicitRef>);
} }
} }
} }

@ -1025,16 +1025,14 @@ namespace MWScript
} }
}; };
template <class R>
class OpOnDeath : public Interpreter::Opcode0 class OpOnDeath : public Interpreter::Opcode0
{ {
public: public:
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
MWScript::InterpreterContext& context MWWorld::Ptr ptr = R()(runtime);
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
MWWorld::Ptr ptr = context.getReference();
Interpreter::Type_Integer value = Interpreter::Type_Integer value =
MWWorld::Class::get (ptr).getCreatureStats (ptr).hasDied(); MWWorld::Class::get (ptr).getCreatureStats (ptr).hasDied();
@ -1146,9 +1144,8 @@ namespace MWScript
const int opcodeRaiseRankExplicit = 0x20001e9; const int opcodeRaiseRankExplicit = 0x20001e9;
const int opcodeLowerRank = 0x20001ea; const int opcodeLowerRank = 0x20001ea;
const int opcodeLowerRankExplicit = 0x20001eb; const int opcodeLowerRankExplicit = 0x20001eb;
const int opcodeOnDeath = 0x20001fc; const int opcodeOnDeath = 0x20001fc;
const int opcodeOnDeathExplicit = 0x2000205;
const int opcodeIsWerewolf = 0x20001fd; const int opcodeIsWerewolf = 0x20001fd;
const int opcodeIsWerewolfExplicit = 0x20001fe; const int opcodeIsWerewolfExplicit = 0x20001fe;
@ -1266,7 +1263,7 @@ namespace MWScript
extensions.registerInstruction ("raiserank", "", opcodeRaiseRank, opcodeRaiseRankExplicit); extensions.registerInstruction ("raiserank", "", opcodeRaiseRank, opcodeRaiseRankExplicit);
extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit); extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit);
extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath); extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit);
extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit); extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit);
} }
@ -1384,7 +1381,8 @@ namespace MWScript
interpreter.installSegment5 (opcodeLowerRank, new OpLowerRank<ImplicitRef>); interpreter.installSegment5 (opcodeLowerRank, new OpLowerRank<ImplicitRef>);
interpreter.installSegment5 (opcodeLowerRankExplicit, new OpLowerRank<ExplicitRef>); interpreter.installSegment5 (opcodeLowerRankExplicit, new OpLowerRank<ExplicitRef>);
interpreter.installSegment5 (opcodeOnDeath, new OpOnDeath); interpreter.installSegment5 (opcodeOnDeath, new OpOnDeath<ImplicitRef>);
interpreter.installSegment5 (opcodeOnDeathExplicit, new OpOnDeath<ExplicitRef>);
interpreter.installSegment5 (opcodeIsWerewolf, new OpIsWerewolf<ImplicitRef>); interpreter.installSegment5 (opcodeIsWerewolf, new OpIsWerewolf<ImplicitRef>);
interpreter.installSegment5 (opcodeIsWerewolfExplicit, new OpIsWerewolf<ExplicitRef>); interpreter.installSegment5 (opcodeIsWerewolfExplicit, new OpIsWerewolf<ExplicitRef>);

@ -84,21 +84,27 @@ namespace MWScript
Interpreter::Type_Float angle = runtime[0].mFloat; Interpreter::Type_Float angle = runtime[0].mFloat;
runtime.pop(); runtime.pop();
float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees();
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees();
float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees(); float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees();
float *objRot = ptr.getRefData().getPosition().rot;
float lx = Ogre::Radian(objRot[0]).valueDegrees();
float ly = Ogre::Radian(objRot[1]).valueDegrees();
float lz = Ogre::Radian(objRot[2]).valueDegrees();
if (axis == "x") if (axis == "x")
{ {
MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az); MWBase::Environment::get().getWorld()->localRotateObject(ptr,angle-lx,ay,az);
} }
else if (axis == "y") else if (axis == "y")
{ {
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az); MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,angle-ly,az);
} }
else if (axis == "z") else if (axis == "z")
{ {
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay,angle-lz);
} }
else else
throw std::runtime_error ("invalid ration axis: " + axis); throw std::runtime_error ("invalid ration axis: " + axis);
@ -148,15 +154,15 @@ namespace MWScript
if (axis=="x") if (axis=="x")
{ {
runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees()); runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[0]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees());
} }
else if (axis=="y") else if (axis=="y")
{ {
runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees()); runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[1]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees());
} }
else if (axis=="z") else if (axis=="z")
{ {
runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees()); runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[2]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees());
} }
else else
throw std::runtime_error ("invalid ration axis: " + axis); throw std::runtime_error ("invalid ration axis: " + axis);
@ -542,6 +548,165 @@ namespace MWScript
} }
}; };
template<class R>
class OpRotate : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
const MWWorld::Ptr& ptr = R()(runtime);
std::string axis = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
Interpreter::Type_Float rotation = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());
runtime.pop();
float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees();
float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees();
float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees();
if (axis == "x")
{
MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax+rotation,ay,az);
}
else if (axis == "y")
{
MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay+rotation,az);
}
else if (axis == "z")
{
MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay,az+rotation);
}
else
throw std::runtime_error ("invalid rotation axis: " + axis);
}
};
template<class R>
class OpRotateWorld : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
std::string axis = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
Interpreter::Type_Float rotation = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());
runtime.pop();
float *objRot = ptr.getRefData().getPosition().rot;
float ax = Ogre::Radian(objRot[0]).valueDegrees();
float ay = Ogre::Radian(objRot[1]).valueDegrees();
float az = Ogre::Radian(objRot[2]).valueDegrees();
if (axis == "x")
{
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax+rotation,ay,az);
}
else if (axis == "y")
{
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay+rotation,az);
}
else if (axis == "z")
{
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,az+rotation);
}
else
throw std::runtime_error ("invalid rotation axis: " + axis);
}
};
template<class R>
class OpSetAtStart : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
ptr.getRefData().getLocalRotation().rot[0] = 0;
ptr.getRefData().getLocalRotation().rot[1] = 0;
ptr.getRefData().getLocalRotation().rot[2] = 0;
MWBase::Environment::get().getWorld()->rotateObject(ptr, 0,0,0,true);
MWBase::Environment::get().getWorld()->moveObject(ptr, ptr.getCellRef().mPos.pos[0],
ptr.getCellRef().mPos.pos[1], ptr.getCellRef().mPos.pos[2]);
}
};
template<class R>
class OpMove : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
const MWWorld::Ptr& ptr = R()(runtime);
std::string axis = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());
runtime.pop();
Ogre::Vector3 posChange;
if (axis == "x")
{
posChange=Ogre::Vector3(movement, 0, 0);
}
else if (axis == "y")
{
posChange=Ogre::Vector3(0, movement, 0);
}
else if (axis == "z")
{
posChange=Ogre::Vector3(0, 0, movement);
}
else
throw std::runtime_error ("invalid movement axis: " + axis);
Ogre::Vector3 worldPos = ptr.getRefData().getBaseNode()->convertLocalToWorldPosition(posChange);
MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x, worldPos.y, worldPos.z);
}
};
template<class R>
class OpMoveWorld : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
std::string axis = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
Interpreter::Type_Float movement = (runtime[0].mFloat*MWBase::Environment::get().getFrameDuration());
runtime.pop();
float *objPos = ptr.getRefData().getPosition().pos;
if (axis == "x")
{
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+movement, objPos[1], objPos[2]);
}
else if (axis == "y")
{
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1]+movement, objPos[2]);
}
else if (axis == "z")
{
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1], objPos[2]+movement);
}
else
throw std::runtime_error ("invalid movement axis: " + axis);
}
};
const int opcodeSetScale = 0x2000164; const int opcodeSetScale = 0x2000164;
const int opcodeSetScaleExplicit = 0x2000165; const int opcodeSetScaleExplicit = 0x2000165;
const int opcodeSetAngle = 0x2000166; const int opcodeSetAngle = 0x2000166;
@ -568,6 +733,16 @@ namespace MWScript
const int opcodePlaceAtMeExplicit = 0x200019e; const int opcodePlaceAtMeExplicit = 0x200019e;
const int opcodeModScale = 0x20001e3; const int opcodeModScale = 0x20001e3;
const int opcodeModScaleExplicit = 0x20001e4; const int opcodeModScaleExplicit = 0x20001e4;
const int opcodeRotate = 0x20001ff;
const int opcodeRotateExplicit = 0x2000200;
const int opcodeRotateWorld = 0x2000201;
const int opcodeRotateWorldExplicit = 0x2000202;
const int opcodeSetAtStart = 0x2000203;
const int opcodeSetAtStartExplicit = 0x2000204;
const int opcodeMove = 0x2000206;
const int opcodeMoveExplicit = 0x2000207;
const int opcodeMoveWorld = 0x2000208;
const int opcodeMoveWorldExplicit = 0x2000209;
void registerExtensions (Compiler::Extensions& extensions) void registerExtensions (Compiler::Extensions& extensions)
{ {
@ -585,6 +760,11 @@ namespace MWScript
extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc); extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc);
extensions.registerInstruction("placeatme","clfl",opcodePlaceAtMe,opcodePlaceAtMeExplicit); extensions.registerInstruction("placeatme","clfl",opcodePlaceAtMe,opcodePlaceAtMeExplicit);
extensions.registerInstruction("modscale","f",opcodeModScale,opcodeModScaleExplicit); extensions.registerInstruction("modscale","f",opcodeModScale,opcodeModScaleExplicit);
extensions.registerInstruction("rotate","cf",opcodeRotate,opcodeRotateExplicit);
extensions.registerInstruction("rotateworld","cf",opcodeRotateWorld,opcodeRotateWorldExplicit);
extensions.registerInstruction("setatstart","",opcodeSetAtStart,opcodeSetAtStartExplicit);
extensions.registerInstruction("move","cf",opcodeMove,opcodeMoveExplicit);
extensions.registerInstruction("moveworld","cf",opcodeMoveWorld,opcodeMoveWorldExplicit);
} }
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
@ -614,6 +794,16 @@ namespace MWScript
interpreter.installSegment5(opcodePlaceAtMeExplicit,new OpPlaceAtMe<ExplicitRef>); interpreter.installSegment5(opcodePlaceAtMeExplicit,new OpPlaceAtMe<ExplicitRef>);
interpreter.installSegment5(opcodeModScale,new OpModScale<ImplicitRef>); interpreter.installSegment5(opcodeModScale,new OpModScale<ImplicitRef>);
interpreter.installSegment5(opcodeModScaleExplicit,new OpModScale<ExplicitRef>); interpreter.installSegment5(opcodeModScaleExplicit,new OpModScale<ExplicitRef>);
interpreter.installSegment5(opcodeRotate,new OpRotate<ImplicitRef>);
interpreter.installSegment5(opcodeRotateExplicit,new OpRotate<ExplicitRef>);
interpreter.installSegment5(opcodeRotateWorld,new OpRotateWorld<ImplicitRef>);
interpreter.installSegment5(opcodeRotateWorldExplicit,new OpRotateWorld<ExplicitRef>);
interpreter.installSegment5(opcodeSetAtStart,new OpSetAtStart<ImplicitRef>);
interpreter.installSegment5(opcodeSetAtStartExplicit,new OpSetAtStart<ExplicitRef>);
interpreter.installSegment5(opcodeMove,new OpMove<ImplicitRef>);
interpreter.installSegment5(opcodeMoveExplicit,new OpMove<ExplicitRef>);
interpreter.installSegment5(opcodeMoveWorld,new OpMoveWorld<ImplicitRef>);
interpreter.installSegment5(opcodeMoveWorldExplicit,new OpMoveWorld<ExplicitRef>);
} }
} }
} }

@ -0,0 +1,16 @@
#include "actiondoor.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
namespace MWWorld
{
ActionDoor::ActionDoor (const MWWorld::Ptr& object) : Action (false, object)
{
}
void ActionDoor::executeImp (const MWWorld::Ptr& actor)
{
MWBase::Environment::get().getWorld()->activateDoor(getTarget());
}
}

@ -0,0 +1,18 @@
#ifndef GAME_MWWORLD_ACTIONDOOR_H
#define GAME_MWWORLD_ACTIONDOOR_H
#include "action.hpp"
#include "ptr.hpp"
namespace MWWorld
{
class ActionDoor : public Action
{
virtual void executeImp (const MWWorld::Ptr& actor);
public:
ActionDoor (const Ptr& object);
};
}
#endif

@ -132,7 +132,7 @@ namespace MWWorld
return 0; return 0;
} }
short Class::getEnchantmentPoints (const MWWorld::Ptr& ptr) const float Class::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{ {
throw std::runtime_error ("class does not support enchanting"); throw std::runtime_error ("class does not support enchanting");
} }

@ -231,7 +231,7 @@ namespace MWWorld
///< @return the enchantment ID if the object is enchanted, otherwise an empty string ///< @return the enchantment ID if the object is enchanted, otherwise an empty string
/// (default implementation: return empty string) /// (default implementation: return empty string)
virtual short getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
///< @return the number of enchantment points available for possible enchanting ///< @return the number of enchantment points available for possible enchanting
virtual void adjustScale(const MWWorld::Ptr& ptr,float& scale) const; virtual void adjustScale(const MWWorld::Ptr& ptr,float& scale) const;

@ -193,59 +193,68 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
{ {
count = std::abs(count); /// \todo implement item restocking (indicated by negative count) count = std::abs(count); /// \todo implement item restocking (indicated by negative count)
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id); try
if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
{ {
const ESM::ItemLevList* levItem = ref.getPtr().get<ESM::ItemLevList>()->mBase; ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id);
const std::vector<ESM::LeveledListBase::LevelItem>& items = levItem->mList;
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
int playerLevel = MWWorld::Class::get(player).getCreatureStats(player).getLevel(); {
const ESM::ItemLevList* levItem = ref.getPtr().get<ESM::ItemLevList>()->mBase;
const std::vector<ESM::LeveledListBase::LevelItem>& items = levItem->mList;
failChance += levItem->mChanceNone; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
int playerLevel = MWWorld::Class::get(player).getCreatureStats(player).getLevel();
if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each) failChance += levItem->mChanceNone;
{
for (int i=0; i<count; ++i)
addInitialItem(id, owner, 1, failChance, false);
return;
}
float random = static_cast<float> (std::rand()) / RAND_MAX; if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each)
if (random >= failChance/100.f)
{
std::vector<std::string> candidates;
int highestLevel = 0;
for (std::vector<ESM::LeveledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it)
{ {
if (it->mLevel > highestLevel) for (int i=0; i<count; ++i)
highestLevel = it->mLevel; addInitialItem(id, owner, 1, failChance, false);
return;
} }
std::pair<int, std::string> highest = std::make_pair(-1, ""); float random = static_cast<float> (std::rand()) / RAND_MAX;
for (std::vector<ESM::LeveledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it) if (random >= failChance/100.f)
{ {
if (playerLevel >= it->mLevel std::vector<std::string> candidates;
&& (levItem->mFlags & ESM::ItemLevList::AllLevels || it->mLevel == highestLevel)) int highestLevel = 0;
for (std::vector<ESM::LeveledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it)
{ {
candidates.push_back(it->mId); if (it->mLevel > highestLevel)
if (it->mLevel >= highest.first) highestLevel = it->mLevel;
highest = std::make_pair(it->mLevel, it->mId);
} }
std::pair<int, std::string> highest = std::make_pair(-1, "");
for (std::vector<ESM::LeveledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it)
{
if (playerLevel >= it->mLevel
&& (levItem->mFlags & ESM::ItemLevList::AllLevels || it->mLevel == highestLevel))
{
candidates.push_back(it->mId);
if (it->mLevel >= highest.first)
highest = std::make_pair(it->mLevel, it->mId);
}
}
if (!candidates.size())
return;
std::string item = candidates[std::rand()%candidates.size()];
addInitialItem(item, owner, count, failChance, false);
} }
if (!candidates.size()) }
return; else
std::string item = candidates[std::rand()%candidates.size()]; {
addInitialItem(item, owner, count, failChance, false); ref.getPtr().getRefData().setCount (count);
ref.getPtr().getCellRef().mOwner = owner;
addImp (ref.getPtr());
} }
} }
else catch (std::logic_error& e)
{ {
ref.getPtr().getRefData().setCount (count); // Vanilla doesn't fail on nonexistent items in levelled lists
ref.getPtr().getCellRef().mOwner = owner; std::cerr << "Warning: ignoring nonexistent item '" << id << "'" << std::endl;
addImp (ref.getPtr()); return;
} }
} }

@ -131,7 +131,6 @@ namespace MWWorld
return position; return position;
} }
static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time,
bool gravity, OEngine::Physic::PhysicEngine *engine) bool gravity, OEngine::Physic::PhysicEngine *engine)
{ {
@ -145,7 +144,7 @@ namespace MWWorld
// FIXME: This works, but it's inconcsistent with how the rotations are applied elsewhere. Why? // 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)* 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[1]), Ogre::Vector3::UNIT_Y)*
Ogre::Quaternion(Ogre::Radian( -refpos.rot[0]), Ogre::Vector3::UNIT_X)) * Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) *
movement; movement;
} }
@ -161,7 +160,7 @@ namespace MWWorld
{ {
velocity = (Ogre::Quaternion(Ogre::Radian( -refpos.rot[2]), Ogre::Vector3::UNIT_Z)* 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[1]), Ogre::Vector3::UNIT_Y)*
Ogre::Quaternion(Ogre::Radian( -refpos.rot[0]), Ogre::Vector3::UNIT_X)) * Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) *
movement / time; movement / time;
} }
else else
@ -261,14 +260,13 @@ namespace MWWorld
std::pair<float, std::string> PhysicsSystem::getFacedHandle (MWWorld::World& world, float queryDistance) std::pair<float, std::string> PhysicsSystem::getFacedHandle (MWWorld::World& world, float queryDistance)
{ {
btVector3 dir(0, 1, 0); btVector3 dir(0, 1, 0);
dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); dir = dir.rotate(btVector3(1, 0, 0), mCameraData.pitch);
dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw); dir = dir.rotate(btVector3(0, 0, 1), mCameraData.yaw);
dir.setX(-dir.x()); dir.setX(-dir.x());
btVector3 origin( btVector3 origin(mCameraData.eyepos.x,
mPlayerData.eyepos.x, mCameraData.eyepos.y,
mPlayerData.eyepos.y, mCameraData.eyepos.z);
mPlayerData.eyepos.z);
origin += dir * 5; origin += dir * 5;
btVector3 dest = origin + dir * queryDistance; btVector3 dest = origin + dir * queryDistance;
@ -281,14 +279,13 @@ namespace MWWorld
std::vector < std::pair <float, std::string> > PhysicsSystem::getFacedHandles (float queryDistance) std::vector < std::pair <float, std::string> > PhysicsSystem::getFacedHandles (float queryDistance)
{ {
btVector3 dir(0, 1, 0); btVector3 dir(0, 1, 0);
dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); dir = dir.rotate(btVector3(1, 0, 0), mCameraData.pitch);
dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw); dir = dir.rotate(btVector3(0, 0, 1), mCameraData.yaw);
dir.setX(-dir.x()); dir.setX(-dir.x());
btVector3 origin( btVector3 origin(mCameraData.eyepos.x,
mPlayerData.eyepos.x, mCameraData.eyepos.y,
mPlayerData.eyepos.y, mCameraData.eyepos.z);
mPlayerData.eyepos.z);
origin += dir * 5; origin += dir * 5;
btVector3 dest = origin + dir * queryDistance; btVector3 dest = origin + dir * queryDistance;
@ -390,6 +387,11 @@ namespace MWWorld
} }
} }
std::vector<std::string> PhysicsSystem::getCollisions(const Ptr &ptr)
{
return mEngine->getCollisions(ptr.getRefData().getBaseNode()->getName());
}
Ogre::Vector3 PhysicsSystem::move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity) Ogre::Vector3 PhysicsSystem::move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity)
{ {
return MovementSolver::move(ptr, movement, time, gravity, mEngine); return MovementSolver::move(ptr, movement, time, gravity, mEngine);
@ -548,10 +550,10 @@ namespace MWWorld
return true; return true;
} }
void PhysicsSystem::updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw) void PhysicsSystem::updateCameraData(const Ogre::Vector3 &eyepos, float pitch, float yaw)
{ {
mPlayerData.eyepos = eyepos; mCameraData.eyepos = eyepos;
mPlayerData.pitch = pitch; mCameraData.pitch = pitch;
mPlayerData.yaw = yaw; mCameraData.yaw = yaw;
} }
} }

@ -51,6 +51,7 @@ namespace MWWorld
bool toggleCollisionMode(); bool toggleCollisionMode();
Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity); Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity);
std::vector<std::string> getCollisions(const MWWorld::Ptr &ptr); ///< get handles this object collides with
Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr); Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr);
std::pair<float, std::string> getFacedHandle (MWWorld::World& world, float queryDistance); std::pair<float, std::string> getFacedHandle (MWWorld::World& world, float queryDistance);
@ -76,13 +77,13 @@ namespace MWWorld
bool getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max); bool getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max);
void updatePlayerData(Ogre::Vector3 &eyepos, float pitch, float yaw); void updateCameraData(const Ogre::Vector3 &eyepos, float pitch, float yaw);
private: private:
struct { struct {
Ogre::Vector3 eyepos; Ogre::Vector3 eyepos;
float pitch, yaw; float pitch, yaw;
} mPlayerData; } mCameraData;
OEngine::Render::OgreRenderer &mRender; OEngine::Render::OgreRenderer &mRender;
OEngine::Physic::PhysicEngine* mEngine; OEngine::Physic::PhysicEngine* mEngine;

@ -19,6 +19,7 @@ namespace MWWorld
mEnabled = refData.mEnabled; mEnabled = refData.mEnabled;
mCount = refData.mCount; mCount = refData.mCount;
mPosition = refData.mPosition; mPosition = refData.mPosition;
mLocalRotation = refData.mLocalRotation;
mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0; mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0;
} }
@ -34,7 +35,11 @@ namespace MWWorld
RefData::RefData (const ESM::CellRef& cellRef) RefData::RefData (const ESM::CellRef& cellRef)
: mBaseNode(0), mHasLocals (false), mEnabled (true), mCount (1), mPosition (cellRef.mPos), : mBaseNode(0), mHasLocals (false), mEnabled (true), mCount (1), mPosition (cellRef.mPos),
mCustomData (0) mCustomData (0)
{} {
mLocalRotation.rot[0]=0;
mLocalRotation.rot[1]=0;
mLocalRotation.rot[2]=0;
}
RefData::RefData (const RefData& refData) RefData::RefData (const RefData& refData)
: mBaseNode(0), mCustomData (0) : mBaseNode(0), mCustomData (0)
@ -76,10 +81,13 @@ namespace MWWorld
{} {}
} }
std::string RefData::getHandle() const std::string &RefData::getHandle()
{ {
if (!mBaseNode) if(!mBaseNode)
return ""; {
static const std::string empty;
return empty;
}
return mBaseNode->getName(); return mBaseNode->getName();
} }
@ -141,6 +149,11 @@ namespace MWWorld
return mPosition; return mPosition;
} }
LocalRotation& RefData::getLocalRotation()
{
return mLocalRotation;
}
void RefData::setCustomData (CustomData *data) void RefData::setCustomData (CustomData *data)
{ {
delete mCustomData; delete mCustomData;

@ -18,6 +18,10 @@ namespace ESM
namespace MWWorld namespace MWWorld
{ {
struct LocalRotation{
float rot[3];
};
class CustomData; class CustomData;
class RefData class RefData
@ -34,6 +38,8 @@ namespace MWWorld
ESM::Position mPosition; ESM::Position mPosition;
LocalRotation mLocalRotation;
CustomData *mCustomData; CustomData *mCustomData;
void copy (const RefData& refData); void copy (const RefData& refData);
@ -54,7 +60,7 @@ namespace MWWorld
RefData& operator= (const RefData& refData); RefData& operator= (const RefData& refData);
/// Return OGRE handle (may be empty). /// Return OGRE handle (may be empty).
std::string getHandle(); const std::string &getHandle();
/// Return OGRE base node (can be a null pointer). /// Return OGRE base node (can be a null pointer).
Ogre::SceneNode* getBaseNode(); Ogre::SceneNode* getBaseNode();
@ -78,6 +84,8 @@ namespace MWWorld
ESM::Position& getPosition(); ESM::Position& getPosition();
LocalRotation& getLocalRotation();
void setCustomData (CustomData *data); void setCustomData (CustomData *data);
///< Set custom data (potentially replacing old custom data). The ownership of \æ data is ///< Set custom data (potentially replacing old custom data). The ownership of \æ data is
/// transferred to this. /// transferred to this.

@ -49,7 +49,12 @@ namespace
{ {
rendering.addObject(ptr); rendering.addObject(ptr);
class_.insertObject(ptr, physics); class_.insertObject(ptr, physics);
MWBase::Environment::get().getWorld()->rotateObject(ptr, 0, 0, 0, true);
float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees();
float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees();
float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees();
MWBase::Environment::get().getWorld()->localRotateObject(ptr, ax, ay, az);
MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().mScale); MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().mScale);
class_.adjustPosition(ptr); class_.adjustPosition(ptr);
} }

@ -1,4 +1,5 @@
#include "store.hpp" #include "store.hpp"
#include "esmstore.hpp"
namespace MWWorld { namespace MWWorld {
@ -15,8 +16,39 @@ void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
ESM::Cell *cell = new ESM::Cell; ESM::Cell *cell = new ESM::Cell;
cell->mName = id; cell->mName = id;
// The cell itself takes care of some of the hairy details //First part of cell loading
cell->load(esm, *mEsmStore); cell->preLoad(esm);
//Handling MovedCellRefs, there is no way to do it inside loadcell
while (esm.isNextSub("MVRF")) {
ESM::CellRef ref;
ESM::MovedCellRef cMRef;
cell->getNextMVRF(esm, cMRef);
MWWorld::Store<ESM::Cell> &cStore = const_cast<MWWorld::Store<ESM::Cell>&>(mEsmStore->get<ESM::Cell>());
ESM::Cell *cellAlt = const_cast<ESM::Cell*>(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1]));
// Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following
// implementation when the oher implementation works as well.
cell->getNextRef(esm, ref);
std::string lowerCase;
std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase),
(int(*)(int)) std::tolower);
// Add data required to make reference appear in the correct cell.
// We should not need to test for duplicates, as this part of the code is pre-cell merge.
cell->mMovedRefs.push_back(cMRef);
// But there may be duplicates here!
ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefnum);
if (iter == cellAlt->mLeasedRefs.end())
cellAlt->mLeasedRefs.push_back(ref);
else
*iter = ref;
}
//Second part of cell loading
cell->postLoad(esm);
if(cell->mData.mFlags & ESM::Cell::Interior) if(cell->mData.mFlags & ESM::Cell::Interior)
{ {
@ -62,4 +94,4 @@ void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
delete cell; delete cell;
} }
} }

@ -872,7 +872,28 @@ namespace MWWorld
} }
void setUp() { void setUp() {
std::sort(mStatic.begin(), mStatic.end(), Compare()); /// \note This method sorts indexed values for further
/// searches. Every loaded item is present in storage, but
/// latest loaded shadows any previous while searching.
/// If memory cost will be too high, it is possible to remove
/// unused values.
Compare cmp;
std::stable_sort(mStatic.begin(), mStatic.end(), cmp);
typename std::vector<T>::iterator first, next;
next = first = mStatic.begin();
while (first != mStatic.end() && ++next != mStatic.end()) {
while (next != mStatic.end() && !cmp(*first, *next)) {
++next;
}
if (first != --next) {
std::swap(*first, *next);
}
first = ++next;
}
} }
const T *search(int index) const { const T *search(int index) const {

@ -289,8 +289,7 @@ void WeatherManager::update(float duration)
if (exterior) if (exterior)
{ {
std::string regionstr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion; std::string regionstr = Misc::StringUtils::lowerCase(MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion);
Misc::StringUtils::toLower(regionstr);
if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion)
{ {
@ -621,6 +620,9 @@ unsigned int WeatherManager::getWeatherID() const
void WeatherManager::changeWeather(const std::string& region, const unsigned int id) void WeatherManager::changeWeather(const std::string& region, const unsigned int id)
{ {
// make sure this region exists
MWBase::Environment::get().getWorld()->getStore().get<ESM::Region>().find(region);
std::string weather; std::string weather;
if (id==0) if (id==0)
weather = "clear"; weather = "clear";
@ -645,5 +647,9 @@ void WeatherManager::changeWeather(const std::string& region, const unsigned int
else else
weather = "clear"; weather = "clear";
mRegionOverrides[region] = weather; mRegionOverrides[Misc::StringUtils::lowerCase(region)] = weather;
std::string playerRegion = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion;
if (Misc::StringUtils::ciEqual(region, playerRegion))
setWeather(weather);
} }

@ -6,6 +6,8 @@
#include <components/files/collections.hpp> #include <components/files/collections.hpp>
#include <components/compiler/locals.hpp> #include <components/compiler/locals.hpp>
#include <boost/math/special_functions/sign.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
@ -16,7 +18,6 @@
#include "../mwmechanics/movement.hpp" #include "../mwmechanics/movement.hpp"
#include "../mwrender/sky.hpp" #include "../mwrender/sky.hpp"
#include "../mwrender/player.hpp"
#include "../mwclass/door.hpp" #include "../mwclass/door.hpp"
@ -163,7 +164,8 @@ namespace MWWorld
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride) ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride)
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
mSky (true), mCells (mStore, mEsm), mSky (true), mCells (mStore, mEsm),
mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride),mFallback(fallbackMap) mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride),
mFallback(fallbackMap), mNewGame(newGame)
{ {
mPhysics = new PhysicsSystem(renderer); mPhysics = new PhysicsSystem(renderer);
mPhysEngine = mPhysics->getEngine(); mPhysEngine = mPhysics->getEngine();
@ -214,7 +216,7 @@ namespace MWWorld
// global variables // global variables
mGlobalVariables = new Globals (mStore); mGlobalVariables = new Globals (mStore);
if (newGame) if (mNewGame)
{ {
// set new game mark // set new game mark
mGlobalVariables->setInt ("chargenstate", 1); mGlobalVariables->setInt ("chargenstate", 1);
@ -810,17 +812,89 @@ namespace MWWorld
void World::rotateObjectImp (const Ptr& ptr, Ogre::Vector3 rot, bool adjust) void World::rotateObjectImp (const Ptr& ptr, Ogre::Vector3 rot, bool adjust)
{ {
if (mRendering->rotateObject(ptr, rot, adjust)) const float two_pi = Ogre::Math::TWO_PI;
const float pi = Ogre::Math::PI;
float *objRot = ptr.getRefData().getPosition().rot;
if(adjust)
{
objRot[0] += rot.x;
objRot[1] += rot.y;
objRot[2] += rot.z;
}
else
{ {
// rotate physically iff renderer confirm so
float *objRot = ptr.getRefData().getPosition().rot;
objRot[0] = rot.x; objRot[0] = rot.x;
objRot[1] = rot.y; objRot[1] = rot.y;
objRot[2] = rot.z; objRot[2] = rot.z;
}
if (ptr.getRefData().getBaseNode() != 0) { if(Class::get(ptr).isActor())
mPhysics->rotateObject(ptr); {
} /* HACK? Actors shouldn't really be rotating around X (or Y), but
* currently it's done so for rotating the camera, which needs
* clamping.
*/
const float half_pi = Ogre::Math::HALF_PI;
if(objRot[0] < -half_pi) objRot[0] = -half_pi;
else if(objRot[0] > half_pi) objRot[0] = half_pi;
}
else
{
while(objRot[0] < -pi) objRot[0] += two_pi;
while(objRot[0] > pi) objRot[0] -= two_pi;
}
while(objRot[1] < -pi) objRot[1] += two_pi;
while(objRot[1] > pi) objRot[1] -= two_pi;
while(objRot[2] < -pi) objRot[2] += two_pi;
while(objRot[2] > pi) objRot[2] -= two_pi;
if(ptr.getRefData().getBaseNode() != 0)
{
mRendering->rotateObject(ptr);
mPhysics->rotateObject(ptr);
}
}
void World::localRotateObject (const Ptr& ptr, float x, float y, float z)
{
if (ptr.getRefData().getBaseNode() != 0) {
ptr.getRefData().getLocalRotation().rot[0]=Ogre::Degree(x).valueRadians();
ptr.getRefData().getLocalRotation().rot[1]=Ogre::Degree(y).valueRadians();
ptr.getRefData().getLocalRotation().rot[2]=Ogre::Degree(z).valueRadians();
float fullRotateRad=Ogre::Degree(360).valueRadians();
while(ptr.getRefData().getLocalRotation().rot[0]>=fullRotateRad)
ptr.getRefData().getLocalRotation().rot[0]-=fullRotateRad;
while(ptr.getRefData().getLocalRotation().rot[1]>=fullRotateRad)
ptr.getRefData().getLocalRotation().rot[1]-=fullRotateRad;
while(ptr.getRefData().getLocalRotation().rot[2]>=fullRotateRad)
ptr.getRefData().getLocalRotation().rot[2]-=fullRotateRad;
while(ptr.getRefData().getLocalRotation().rot[0]<=-fullRotateRad)
ptr.getRefData().getLocalRotation().rot[0]+=fullRotateRad;
while(ptr.getRefData().getLocalRotation().rot[1]<=-fullRotateRad)
ptr.getRefData().getLocalRotation().rot[1]+=fullRotateRad;
while(ptr.getRefData().getLocalRotation().rot[2]<=-fullRotateRad)
ptr.getRefData().getLocalRotation().rot[2]+=fullRotateRad;
float *worldRot = ptr.getRefData().getPosition().rot;
Ogre::Quaternion worldRotQuat(Ogre::Quaternion(Ogre::Radian(-worldRot[0]), Ogre::Vector3::UNIT_X)*
Ogre::Quaternion(Ogre::Radian(-worldRot[1]), Ogre::Vector3::UNIT_Y)*
Ogre::Quaternion(Ogre::Radian(-worldRot[2]), Ogre::Vector3::UNIT_Z));
Ogre::Quaternion rot(Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-x).valueRadians()), Ogre::Vector3::UNIT_X)*
Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-y).valueRadians()), Ogre::Vector3::UNIT_Y)*
Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-z).valueRadians()), Ogre::Vector3::UNIT_Z));
ptr.getRefData().getBaseNode()->setOrientation(worldRotQuat*rot);
mPhysics->rotateObject(ptr);
} }
} }
@ -923,10 +997,66 @@ namespace MWWorld
!isSwimming(player->first) && !isFlying(player->first)); !isSwimming(player->first) && !isFlying(player->first));
moveObjectImp(player->first, vec.x, vec.y, vec.z); moveObjectImp(player->first, vec.x, vec.y, vec.z);
} }
// the only purpose this has currently is to update the debug drawer
processDoors(duration);
mPhysEngine->stepSimulation (duration); mPhysEngine->stepSimulation (duration);
} }
void World::processDoors(float duration)
{
std::map<MWWorld::Ptr, int>::iterator it = mDoorStates.begin();
while (it != mDoorStates.end())
{
if (!mWorldScene->isCellActive(*it->first.getCell()))
mDoorStates.erase(it++);
else
{
float oldRot = Ogre::Radian(it->first.getRefData().getLocalRotation().rot[2]).valueDegrees();
float diff = duration * 90;
float targetRot = std::min(std::max(0.f, oldRot + diff * (it->second ? 1 : -1)), 90.f);
localRotateObject(it->first, 0, 0, targetRot);
// AABB of the door
Ogre::Vector3 min,max;
mPhysics->getObjectAABB(it->first, min, max);
Ogre::Vector3 dimensions = max-min;
std::vector<std::string> collisions = mPhysics->getCollisions(it->first);
for (std::vector<std::string>::iterator cit = collisions.begin(); cit != collisions.end(); ++cit)
{
MWWorld::Ptr ptr = getPtrViaHandle(*cit);
if (MWWorld::Class::get(ptr).isActor())
{
// we collided with an actor, we need to undo the rotation and push the door away from the actor
// figure out on which side of the door the actor we collided with is
Ogre::Vector3 relativePos = it->first.getRefData().getBaseNode()->
convertWorldToLocalPosition(ptr.getRefData().getBaseNode()->_getDerivedPosition());
float axisToCheck;
if (dimensions.x > dimensions.y)
axisToCheck = relativePos.y * boost::math::sign((min+max).y);
else
axisToCheck = relativePos.x * boost::math::sign((min+max).x);
if (axisToCheck >= 0)
targetRot = std::min(std::max(0.f, oldRot + diff*0.5f), 90.f);
else
targetRot = std::min(std::max(0.f, oldRot - diff*0.5f), 90.f);
localRotateObject(it->first, 0, 0, targetRot);
break;
}
}
if ((targetRot == 90.f && it->second) || targetRot == 0.f)
mDoorStates.erase(it++);
else
++it;
}
}
}
bool World::toggleCollisionMode() bool World::toggleCollisionMode()
{ {
return mPhysics->toggleCollisionMode();; return mPhysics->toggleCollisionMode();;
@ -1022,8 +1152,8 @@ namespace MWWorld
float pitch, yaw; float pitch, yaw;
Ogre::Vector3 eyepos; Ogre::Vector3 eyepos;
mRendering->getPlayerData(eyepos, pitch, yaw); mRendering->getCameraData(eyepos, pitch, yaw);
mPhysics->updatePlayerData(eyepos, pitch, yaw); mPhysics->updateCameraData(eyepos, pitch, yaw);
performUpdateSceneQueries (); performUpdateSceneQueries ();
@ -1367,15 +1497,17 @@ namespace MWWorld
return mRendering->vanityRotateCamera(rot); return mRendering->vanityRotateCamera(rot);
} }
void World::setupPlayer(bool newGame) void World::setupPlayer()
{ {
const ESM::NPC* player = mStore.get<ESM::NPC>().find ("player"); const ESM::NPC *player = mStore.get<ESM::NPC>().find("player");
mPlayer = new MWWorld::Player (player, *this); mPlayer = new MWWorld::Player(player, *this);
mRendering->attachCameraTo(mPlayer->getPlayer());
if (newGame) Ptr ptr = mPlayer->getPlayer();
mRendering->setupPlayer(ptr);
if (mNewGame)
{ {
MWWorld::Class::get(mPlayer->getPlayer()).getContainerStore(mPlayer->getPlayer()).fill(player->mInventory, "", mStore); MWWorld::Class::get(ptr).getContainerStore(ptr).fill(player->mInventory, "", mStore);
MWWorld::Class::get(mPlayer->getPlayer()).getInventoryStore(mPlayer->getPlayer()).autoEquip (mPlayer->getPlayer()); MWWorld::Class::get(ptr).getInventoryStore(ptr).autoEquip(ptr);
} }
} }
@ -1383,6 +1515,8 @@ namespace MWWorld
{ {
mRendering->renderPlayer(mPlayer->getPlayer()); mRendering->renderPlayer(mPlayer->getPlayer());
mPhysics->addActor(mPlayer->getPlayer()); mPhysics->addActor(mPlayer->getPlayer());
if (mNewGame)
toggleCollisionMode();
} }
void World::setupExternalRendering (MWRender::ExternalRendering& rendering) void World::setupExternalRendering (MWRender::ExternalRendering& rendering)
@ -1398,7 +1532,7 @@ namespace MWWorld
Ogre::Vector3 playerPos(refdata.getPosition().pos); Ogre::Vector3 playerPos(refdata.getPosition().pos);
const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle());
if(!physactor->getOnGround() || isUnderwater(currentCell, playerPos)) if((!physactor->getOnGround()&&physactor->getCollisionMode()) || isUnderwater(currentCell, playerPos))
return 2; return 2;
if((currentCell->mCell->mData.mFlags&ESM::Cell::NoSleep)) if((currentCell->mCell->mData.mFlags&ESM::Cell::NoSleep))
return 1; return 1;
@ -1425,4 +1559,27 @@ namespace MWWorld
{ {
mRendering->frameStarted(dt); mRendering->frameStarted(dt);
} }
void World::activateDoor(const MWWorld::Ptr& door)
{
if (mDoorStates.find(door) != mDoorStates.end())
{
// if currently opening, then close, if closing, then open
mDoorStates[door] = !mDoorStates[door];
}
else
{
if (door.getRefData().getLocalRotation().rot[2] == 0)
mDoorStates[door] = 1; // open
else
mDoorStates[door] = 0; // close
}
}
bool World::getOpenOrCloseDoor(const Ptr &door)
{
if (mDoorStates.find(door) != mDoorStates.end())
return !mDoorStates[door]; // if currently opening or closing, then do the opposite
return door.getRefData().getLocalRotation().rot[2] == 0;
}
} }

@ -85,6 +85,11 @@ namespace MWWorld
float mFaced2Distance; float mFaced2Distance;
int mNumFacing; int mNumFacing;
bool mNewGame;
std::map<MWWorld::Ptr, int> mDoorStates;
///< only holds doors that are currently moving. 0 means closing, 1 opening
unsigned long lastTick; unsigned long lastTick;
Ogre::Timer mTimer; Ogre::Timer mTimer;
@ -110,6 +115,9 @@ namespace MWWorld
void addContainerScripts(const Ptr& reference, Ptr::CellStore* cell); void addContainerScripts(const Ptr& reference, Ptr::CellStore* cell);
void PCDropped (const Ptr& item); void PCDropped (const Ptr& item);
virtual void processDoors(float duration);
///< Run physics simulation and modify \a world accordingly.
public: public:
World (OEngine::Render::OgreRenderer& renderer, World (OEngine::Render::OgreRenderer& renderer,
@ -254,6 +262,8 @@ namespace MWWorld
/// \param adjust indicates rotation should be set or adjusted /// \param adjust indicates rotation should be set or adjusted
virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false); virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false);
virtual void localRotateObject (const Ptr& ptr, float x, float y, float z);
virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos); virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos);
///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr.
@ -345,8 +355,8 @@ namespace MWWorld
mRendering->togglePreviewMode(enable); mRendering->togglePreviewMode(enable);
} }
virtual bool toggleVanityMode(bool enable, bool force) { virtual bool toggleVanityMode(bool enable) {
return mRendering->toggleVanityMode(enable, force); return mRendering->toggleVanityMode(enable);
} }
virtual void allowVanityMode(bool allow) { virtual void allowVanityMode(bool allow) {
@ -363,9 +373,14 @@ namespace MWWorld
virtual bool vanityRotateCamera(float * rot); virtual bool vanityRotateCamera(float * rot);
virtual void setupPlayer(bool newGame); virtual void setupPlayer();
virtual void renderPlayer(); virtual void renderPlayer();
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door);
///< if activated, should this door be opened or closed?
virtual void activateDoor(const MWWorld::Ptr& door);
///< activate (open or close) an non-teleport door
virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); virtual void setupExternalRendering (MWRender::ExternalRendering& rendering);
virtual int canRest(); virtual int canRest();

@ -313,21 +313,26 @@ public:
void destroyInstance( Archive* arch) { delete arch; } void destroyInstance( Archive* arch) { delete arch; }
}; };
class DirArchiveFactory : public FileSystemArchiveFactory class DirArchiveFactory : public ArchiveFactory
{ {
public: public:
const String& getType() const const String& getType() const
{ {
static String name = "Dir"; static String name = "Dir";
return name; return name;
} }
Archive *createInstance( const String& name ) Archive *createInstance( const String& name )
{ {
return new DirArchive(name); return new DirArchive(name);
} }
void destroyInstance( Archive* arch) { delete arch; } virtual Archive* createInstance(const String& name, bool readOnly)
{
return new DirArchive(name);
}
void destroyInstance( Archive* arch) { delete arch; }
}; };

@ -71,6 +71,19 @@ namespace Compiler
return true; return true;
} }
if (mState==EndNameState)
{
// optional repeated name after end statement
if (mName!=loc.mLiteral)
reportWarning ("Names for script " + mName + " do not match", loc);
mState = EndCompleteState;
return false; // we are stopping here, because there might be more garbage on the end line,
// that we must ignore.
//
/// \todo allow this workaround to be disabled for newer scripts
}
return Parser::parseKeyword (keyword, loc, scanner); return Parser::parseKeyword (keyword, loc, scanner);
} }

@ -8,9 +8,6 @@
#include "esmreader.hpp" #include "esmreader.hpp"
#include "esmwriter.hpp" #include "esmwriter.hpp"
#include <apps/openmw/mwworld/store.hpp>
#include <apps/openmw/mwworld/cellstore.hpp>
namespace ESM namespace ESM
{ {
@ -132,38 +129,13 @@ void Cell::load(ESMReader &esm, bool saveContext)
} }
} }
void Cell::load(ESMReader &esm, MWWorld::ESMStore &store) void Cell::preLoad(ESMReader &esm) //Can't be "load" because it conflicts with function in esmtool
{ {
this->load(esm, false); this->load(esm, false);
}
// preload moved references void Cell::postLoad(ESMReader &esm)
while (esm.isNextSub("MVRF")) { {
CellRef ref;
MovedCellRef cMRef;
getNextMVRF(esm, cMRef);
MWWorld::Store<ESM::Cell> &cStore = const_cast<MWWorld::Store<ESM::Cell>&>(store.get<ESM::Cell>());
ESM::Cell *cellAlt = const_cast<ESM::Cell*>(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1]));
// Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following
// implementation when the oher implementation works as well.
getNextRef(esm, ref);
std::string lowerCase;
std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase),
(int(*)(int)) std::tolower);
// Add data required to make reference appear in the correct cell.
// We should not need to test for duplicates, as this part of the code is pre-cell merge.
mMovedRefs.push_back(cMRef);
// But there may be duplicates here!
ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefnum);
if (iter == cellAlt->mLeasedRefs.end())
cellAlt->mLeasedRefs.push_back(ref);
else
*iter = ref;
}
// Save position of the cell references and move on // Save position of the cell references and move on
mContextList.push_back(esm.getContext()); mContextList.push_back(esm.getContext());
esm.skipRecord(); esm.skipRecord();

@ -96,7 +96,8 @@ struct Cell
CellRefTracker mLeasedRefs; CellRefTracker mLeasedRefs;
MovedCellRefTracker mMovedRefs; MovedCellRefTracker mMovedRefs;
void load(ESMReader &esm, MWWorld::ESMStore &store); void preLoad(ESMReader &esm);
void postLoad(ESMReader &esm);
// This method is left in for compatibility with esmtool. Parsing moved references currently requires // This method is left in for compatibility with esmtool. Parsing moved references currently requires
// passing ESMStore, bit it does not know about this parameter, so we do it this way. // passing ESMStore, bit it does not know about this parameter, so we do it this way.

@ -48,7 +48,7 @@ struct Weapon
short mType; short mType;
short mHealth; short mHealth;
float mSpeed, mReach; float mSpeed, mReach;
short mEnchant; // Enchantment points short mEnchant; // Enchantment points. The real value is mEnchant/10.f
unsigned char mChop[2], mSlash[2], mThrust[2]; // Min and max unsigned char mChop[2], mSlash[2], mThrust[2]; // Min and max
int mFlags; int mFlags;
}; // 32 bytes }; // 32 bytes

@ -303,5 +303,28 @@ public:
} }
}; };
class NiFlipController : public Controller
{
public:
int mTexSlot;
float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources
NiSourceTextureList mSources;
void read(NIFStream *nif)
{
Controller::read(nif);
mTexSlot = nif->getUInt();
/*unknown=*/nif->getUInt();/*0?*/
mDelta = nif->getFloat();
mSources.read(nif);
}
void post(NIFFile *nif)
{
Controller::post(nif);
mSources.post(nif);
}
};
} // Namespace } // Namespace
#endif #endif

@ -207,8 +207,8 @@ struct RecordFactoryEntry {
static const RecordFactoryEntry recordFactories [] = { static const RecordFactoryEntry recordFactories [] = {
{ "NiNode", &construct <NiNode >, RC_NiNode }, { "NiNode", &construct <NiNode >, RC_NiNode },
{ "AvoidNode", &construct <NiNode >, RC_NiNode }, { "AvoidNode", &construct <NiNode >, RC_AvoidNode },
{ "NiBSParticleNode", &construct <NiNode >, RC_NiNode }, { "NiBSParticleNode", &construct <NiNode >, RC_NiBSParticleNode },
{ "NiBSAnimationNode", &construct <NiNode >, RC_NiBSAnimationNode }, { "NiBSAnimationNode", &construct <NiNode >, RC_NiBSAnimationNode },
{ "NiBillboardNode", &construct <NiNode >, RC_NiNode }, { "NiBillboardNode", &construct <NiNode >, RC_NiNode },
{ "NiTriShape", &construct <NiTriShape >, RC_NiTriShape }, { "NiTriShape", &construct <NiTriShape >, RC_NiTriShape },
@ -235,6 +235,7 @@ static const RecordFactoryEntry recordFactories [] = {
{ "NiMaterialColorController", &construct <NiMaterialColorController >, RC_NiMaterialColorController }, { "NiMaterialColorController", &construct <NiMaterialColorController >, RC_NiMaterialColorController },
{ "NiBSPArrayController", &construct <NiBSPArrayController >, RC_NiBSPArrayController }, { "NiBSPArrayController", &construct <NiBSPArrayController >, RC_NiBSPArrayController },
{ "NiParticleSystemController", &construct <NiParticleSystemController >, RC_NiParticleSystemController }, { "NiParticleSystemController", &construct <NiParticleSystemController >, RC_NiParticleSystemController },
{ "NiFlipController", &construct <NiFlipController >, RC_NiFlipController },
{ "NiAmbientLight", &construct <NiLight >, RC_NiLight }, { "NiAmbientLight", &construct <NiLight >, RC_NiLight },
{ "NiDirectionalLight", &construct <NiLight >, RC_NiLight }, { "NiDirectionalLight", &construct <NiLight >, RC_NiLight },
{ "NiTextureEffect", &construct <NiTextureEffect >, RC_NiTextureEffect }, { "NiTextureEffect", &construct <NiTextureEffect >, RC_NiTextureEffect },

@ -128,13 +128,17 @@ struct NiNode : Node
NodeList children; NodeList children;
NodeList effects; NodeList effects;
/* Known NiNode flags: enum Flags {
0x01 hidden Flag_Hidden = 0x0001,
0x02 use mesh for collision Flag_MeshCollision = 0x0002,
0x04 use bounding box for collision (?) Flag_BBoxCollision = 0x0004
0x08 unknown, but common };
0x20, 0x40, 0x80 unknown enum BSAnimFlags {
*/ AnimFlag_AutoPlay = 0x0020
};
enum BSParticleFlags {
ParticleFlag_AutoPlay = 0x0020
};
void read(NIFStream *nif) void read(NIFStream *nif)
{ {

@ -36,9 +36,11 @@ enum RecordType
{ {
RC_MISSING = 0, RC_MISSING = 0,
RC_NiNode, RC_NiNode,
RC_AvoidNode,
RC_NiTriShape, RC_NiTriShape,
RC_NiRotatingParticles, RC_NiRotatingParticles,
RC_NiAutoNormalParticles, RC_NiAutoNormalParticles,
RC_NiBSParticleNode,
RC_NiCamera, RC_NiCamera,
RC_NiTexturingProperty, RC_NiTexturingProperty,
RC_NiMaterialProperty, RC_NiMaterialProperty,
@ -59,6 +61,7 @@ enum RecordType
RC_NiMaterialColorController, RC_NiMaterialColorController,
RC_NiBSPArrayController, RC_NiBSPArrayController,
RC_NiParticleSystemController, RC_NiParticleSystemController,
RC_NiFlipController,
RC_NiBSAnimationNode, RC_NiBSAnimationNode,
RC_NiLight, RC_NiLight,
RC_NiTextureEffect, RC_NiTextureEffect,

@ -163,6 +163,7 @@ typedef RecordPtrT<NiAutoNormalParticlesData> NiAutoNormalParticlesDataPtr;
typedef RecordListT<Node> NodeList; typedef RecordListT<Node> NodeList;
typedef RecordListT<Property> PropertyList; typedef RecordListT<Property> PropertyList;
typedef RecordListT<NiSourceTexture> NiSourceTextureList;
} // Namespace } // Namespace
#endif #endif

@ -185,6 +185,10 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node *
else else
isCollisionNode = isCollisionNode && (node->recType != Nif::RC_RootCollisionNode); isCollisionNode = isCollisionNode && (node->recType != Nif::RC_RootCollisionNode);
// Don't collide with AvoidNode shapes
if(node->recType == Nif::RC_AvoidNode)
flags |= 0x800;
// Marker objects // Marker objects
/// \todo don't do this in the editor /// \todo don't do this in the editor
std::string nodename = node->name; std::string nodename = node->name;
@ -255,9 +259,9 @@ void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif::
assert(shape != NULL); assert(shape != NULL);
// Interpret flags // Interpret flags
bool hidden = (flags & 0x01) != 0; // Not displayed bool hidden = (flags&Nif::NiNode::Flag_Hidden) != 0;
bool collide = (flags & 0x02) != 0; // Use mesh for collision bool collide = (flags&Nif::NiNode::Flag_MeshCollision) != 0;
bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision bool bbcollide = (flags&Nif::NiNode::Flag_BBoxCollision) != 0;
// If the object was marked "NCO" earlier, it shouldn't collide with // If the object was marked "NCO" earlier, it shouldn't collide with
// anything. So don't do anything. // anything. So don't do anything.

@ -109,13 +109,26 @@ NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders;
void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape) void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape)
{ {
Ogre::SkeletonPtr skel;
const Nif::NiTriShapeData *data = shape->data.getPtr(); const Nif::NiTriShapeData *data = shape->data.getPtr();
const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr()); const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr());
std::vector<Ogre::Vector3> srcVerts = data->vertices; std::vector<Ogre::Vector3> srcVerts = data->vertices;
std::vector<Ogre::Vector3> srcNorms = data->normals; std::vector<Ogre::Vector3> srcNorms = data->normals;
Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC; Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC;
bool vertShadowBuffer = false; bool vertShadowBuffer = false;
if(!shape->controller.empty())
{
Nif::ControllerPtr ctrl = shape->controller;
do {
if(ctrl->recType == Nif::RC_NiGeomMorpherController)
{
vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY;
vertShadowBuffer = true;
break;
}
} while(!(ctrl=ctrl->next).empty());
}
if(skin != NULL) if(skin != NULL)
{ {
vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY;
@ -125,10 +138,6 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
// explicitly attached later. // explicitly attached later.
mesh->setSkeletonName(mName); 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(mName);
// Convert vertices and normals to bone space from bind position. It would be // Convert vertices and normals to bone space from bind position. It would be
// better to transform the bones into bind position, but there doesn't seem to // better to transform the bones into bind position, but there doesn't seem to
// be a reliable way to do that. // be a reliable way to do that.
@ -139,11 +148,10 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
const Nif::NodeList &bones = skin->bones; const Nif::NodeList &bones = skin->bones;
for(size_t b = 0;b < bones.length();b++) for(size_t b = 0;b < bones.length();b++)
{ {
Ogre::Bone *bone = skel->getBone(bones[b]->name);
Ogre::Matrix4 mat; Ogre::Matrix4 mat;
mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale),
Ogre::Quaternion(data->bones[b].trafo.rotation)); Ogre::Quaternion(data->bones[b].trafo.rotation));
mat = bone->_getFullTransform() * mat; mat = bones[b]->getWorldTransform() * mat;
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights; const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights;
for(size_t i = 0;i < weights.size();i++) for(size_t i = 0;i < weights.size();i++)
@ -296,6 +304,8 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
// Assign bone weights for this TriShape // Assign bone weights for this TriShape
if(skin != NULL) if(skin != NULL)
{ {
Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(mName);
const Nif::NiSkinData *data = skin->data.getPtr(); const Nif::NiSkinData *data = skin->data.getPtr();
const Nif::NodeList &bones = skin->bones; const Nif::NodeList &bones = skin->bones;
for(size_t i = 0;i < bones.length();i++) for(size_t i = 0;i < bones.length();i++)

@ -44,15 +44,6 @@
#include "material.hpp" #include "material.hpp"
#include "mesh.hpp" #include "mesh.hpp"
namespace std
{
// TODO: Do something useful
ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&)
{ return o; }
}
namespace NifOgre namespace NifOgre
{ {
@ -74,11 +65,7 @@ public:
, mStopTime(ctrl->timeStop) , mStopTime(ctrl->timeStop)
{ {
if(mDeltaInput) if(mDeltaInput)
{
mDeltaCount = mPhase; mDeltaCount = mPhase;
while(mDeltaCount < mStartTime)
mDeltaCount += (mStopTime-mStartTime);
}
} }
virtual Ogre::Real calculate(Ogre::Real value) virtual Ogre::Real calculate(Ogre::Real value)
@ -86,6 +73,9 @@ public:
if(mDeltaInput) if(mDeltaInput)
{ {
mDeltaCount += value*mFrequency; mDeltaCount += value*mFrequency;
if(mDeltaCount < mStartTime)
mDeltaCount = mStopTime - std::fmod(mStartTime - mDeltaCount,
mStopTime - mStartTime);
mDeltaCount = std::fmod(mDeltaCount - mStartTime, mDeltaCount = std::fmod(mDeltaCount - mStartTime,
mStopTime - mStartTime) + mStartTime; mStopTime - mStartTime) + mStartTime;
return mDeltaCount; return mDeltaCount;
@ -104,7 +94,7 @@ public:
private: private:
std::vector<Nif::NiVisData::VisData> mData; std::vector<Nif::NiVisData::VisData> mData;
virtual bool calculate(Ogre::Real time) bool calculate(Ogre::Real time) const
{ {
if(mData.size() == 0) if(mData.size() == 0)
return true; return true;
@ -144,10 +134,19 @@ public:
, mData(data->mVis) , mData(data->mVis)
{ } { }
virtual Ogre::Quaternion getRotation(float time) const
{ return Ogre::Quaternion(); }
virtual Ogre::Vector3 getTranslation(float time) const
{ return Ogre::Vector3(0.0f); }
virtual Ogre::Vector3 getScale(float time) const
{ return Ogre::Vector3(1.0f); }
virtual Ogre::Real getValue() const virtual Ogre::Real getValue() const
{ {
// Should not be called // Should not be called
return 1.0f; return 0.0f;
} }
virtual void setValue(Ogre::Real time) virtual void setValue(Ogre::Real time)
@ -170,6 +169,60 @@ public:
Nif::Vector3KeyList mTranslations; Nif::Vector3KeyList mTranslations;
Nif::FloatKeyList mScales; Nif::FloatKeyList mScales;
static float interpKey(const Nif::FloatKeyList::VecType &keys, float time)
{
if(time <= keys.front().mTime)
return keys.front().mValue;
Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1);
for(;iter != keys.end();iter++)
{
if(iter->mTime < time)
continue;
Nif::FloatKeyList::VecType::const_iterator last(iter-1);
float a = (time-last->mTime) / (iter->mTime-last->mTime);
return last->mValue + ((iter->mValue - last->mValue)*a);
}
return keys.back().mValue;
}
static Ogre::Vector3 interpKey(const Nif::Vector3KeyList::VecType &keys, float time)
{
if(time <= keys.front().mTime)
return keys.front().mValue;
Nif::Vector3KeyList::VecType::const_iterator iter(keys.begin()+1);
for(;iter != keys.end();iter++)
{
if(iter->mTime < time)
continue;
Nif::Vector3KeyList::VecType::const_iterator last(iter-1);
float a = (time-last->mTime) / (iter->mTime-last->mTime);
return last->mValue + ((iter->mValue - last->mValue)*a);
}
return keys.back().mValue;
}
static Ogre::Quaternion interpKey(const Nif::QuaternionKeyList::VecType &keys, float time)
{
if(time <= keys.front().mTime)
return keys.front().mValue;
Nif::QuaternionKeyList::VecType::const_iterator iter(keys.begin()+1);
for(;iter != keys.end();iter++)
{
if(iter->mTime < time)
continue;
Nif::QuaternionKeyList::VecType::const_iterator last(iter-1);
float a = (time-last->mTime) / (iter->mTime-last->mTime);
return Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue);
}
return keys.back().mValue;
}
public: public:
Value(Ogre::Node *target, const Nif::NiKeyframeData *data) Value(Ogre::Node *target, const Nif::NiKeyframeData *data)
: NodeTargetValue<Ogre::Real>(target) : NodeTargetValue<Ogre::Real>(target)
@ -178,6 +231,27 @@ public:
, mScales(data->mScales) , mScales(data->mScales)
{ } { }
virtual Ogre::Quaternion getRotation(float time) const
{
if(mRotations.mKeys.size() > 0)
return interpKey(mRotations.mKeys, time);
return mNode->getOrientation();
}
virtual Ogre::Vector3 getTranslation(float time) const
{
if(mTranslations.mKeys.size() > 0)
return interpKey(mTranslations.mKeys, time);
return mNode->getPosition();
}
virtual Ogre::Vector3 getScale(float time) const
{
if(mScales.mKeys.size() > 0)
return Ogre::Vector3(interpKey(mScales.mKeys, time));
return mNode->getScale();
}
virtual Ogre::Real getValue() const virtual Ogre::Real getValue() const
{ {
// Should not be called // Should not be called
@ -187,68 +261,11 @@ public:
virtual void setValue(Ogre::Real time) virtual void setValue(Ogre::Real time)
{ {
if(mRotations.mKeys.size() > 0) if(mRotations.mKeys.size() > 0)
{ mNode->setOrientation(interpKey(mRotations.mKeys, time));
if(time <= mRotations.mKeys.front().mTime)
mNode->setOrientation(mRotations.mKeys.front().mValue);
else if(time >= mRotations.mKeys.back().mTime)
mNode->setOrientation(mRotations.mKeys.back().mValue);
else
{
Nif::QuaternionKeyList::VecType::const_iterator iter(mRotations.mKeys.begin()+1);
for(;iter != mRotations.mKeys.end();iter++)
{
if(iter->mTime < time)
continue;
Nif::QuaternionKeyList::VecType::const_iterator last(iter-1);
float a = (time-last->mTime) / (iter->mTime-last->mTime);
mNode->setOrientation(Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue));
break;
}
}
}
if(mTranslations.mKeys.size() > 0) if(mTranslations.mKeys.size() > 0)
{ mNode->setPosition(interpKey(mTranslations.mKeys, time));
if(time <= mTranslations.mKeys.front().mTime)
mNode->setPosition(mTranslations.mKeys.front().mValue);
else if(time >= mTranslations.mKeys.back().mTime)
mNode->setPosition(mTranslations.mKeys.back().mValue);
else
{
Nif::Vector3KeyList::VecType::const_iterator iter(mTranslations.mKeys.begin()+1);
for(;iter != mTranslations.mKeys.end();iter++)
{
if(iter->mTime < time)
continue;
Nif::Vector3KeyList::VecType::const_iterator last(iter-1);
float a = (time-last->mTime) / (iter->mTime-last->mTime);
mNode->setPosition(last->mValue + ((iter->mValue - last->mValue)*a));
break;
}
}
}
if(mScales.mKeys.size() > 0) if(mScales.mKeys.size() > 0)
{ mNode->setScale(Ogre::Vector3(interpKey(mScales.mKeys, time)));
if(time <= mScales.mKeys.front().mTime)
mNode->setScale(Ogre::Vector3(mScales.mKeys.front().mValue));
else if(time >= mScales.mKeys.back().mTime)
mNode->setScale(Ogre::Vector3(mScales.mKeys.back().mValue));
else
{
Nif::FloatKeyList::VecType::const_iterator iter(mScales.mKeys.begin()+1);
for(;iter != mScales.mKeys.end();iter++)
{
if(iter->mTime < time)
continue;
Nif::FloatKeyList::VecType::const_iterator last(iter-1);
float a = (time-last->mTime) / (iter->mTime-last->mTime);
mNode->setScale(Ogre::Vector3(last->mValue + ((iter->mValue - last->mValue)*a)));
break;
}
}
}
} }
}; };
@ -289,7 +306,7 @@ public:
} }
public: public:
Value(const Ogre::MaterialPtr &material, Nif::NiUVData *data) Value(const Ogre::MaterialPtr &material, const Nif::NiUVData *data)
: mMaterial(material) : mMaterial(material)
, mUTrans(data->mKeyList[0]) , mUTrans(data->mKeyList[0])
, mVTrans(data->mKeyList[1]) , mVTrans(data->mKeyList[1])
@ -329,11 +346,70 @@ public:
typedef DefaultFunction Function; typedef DefaultFunction Function;
}; };
class ParticleSystemController
{
public:
class Value : public Ogre::ControllerValue<Ogre::Real>
{
private:
Ogre::ParticleSystem *mParticleSys;
float mEmitStart;
float mEmitStop;
public:
Value(Ogre::ParticleSystem *psys, const Nif::NiParticleSystemController *pctrl)
: mParticleSys(psys)
, mEmitStart(pctrl->startTime)
, mEmitStop(pctrl->stopTime)
{
}
Ogre::Real getValue() const
{ return 0.0f; }
/** Manual resource loader for NIF objects (meshes, particle systems, etc). void setValue(Ogre::Real value)
* This is the main class responsible for translating the internal NIF {
* structures into something Ogre can use. mParticleSys->setEmitting(value >= mEmitStart && value < mEmitStop);
}
};
typedef DefaultFunction Function;
};
class GeomMorpherController
{
public:
class Value : public Ogre::ControllerValue<Ogre::Real>
{
private:
Ogre::SubEntity *mSubEntity;
std::vector<Nif::NiMorphData::MorphData> mMorphs;
public:
Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data)
: mSubEntity(subent)
, mMorphs(data->mMorphs)
{ }
virtual Ogre::Real getValue() const
{
// Should not be called
return 0.0f;
}
virtual void setValue(Ogre::Real value)
{
// TODO: Implement
}
};
typedef DefaultFunction Function;
};
/** Object creator for NIFs. This is the main class responsible for creating
* "live" Ogre objects (entities, particle systems, controllers, etc) from
* their NIF equivalents.
*/ */
class NIFObjectLoader class NIFObjectLoader
{ {
@ -349,14 +425,79 @@ class NIFObjectLoader
} }
static void createEntity(const std::string &name, const std::string &group,
Ogre::SceneManager *sceneMgr, ObjectList &objectlist,
const Nif::Node *node, int flags, int animflags)
{
const Nif::NiTriShape *shape = static_cast<const Nif::NiTriShape*>(node);
std::string fullname = name+"@index="+Ogre::StringConverter::toString(shape->recIndex);
if(shape->name.length() > 0)
fullname += "@shape="+shape->name;
Misc::StringUtils::toLower(fullname);
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
if(meshMgr.getByName(fullname).isNull())
NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex);
Ogre::Entity *entity = sceneMgr->createEntity(fullname);
entity->setVisible(!(flags&Nif::NiNode::Flag_Hidden));
objectlist.mEntities.push_back(entity);
if(objectlist.mSkelBase)
{
if(entity->hasSkeleton())
entity->shareSkeletonInstanceWith(objectlist.mSkelBase);
else
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity);
}
}
Nif::ControllerPtr ctrl = node->controller;
while(!ctrl.empty())
{
if(ctrl->recType == Nif::RC_NiUVController)
{
const Nif::NiUVController *uv = static_cast<const Nif::NiUVController*>(ctrl.getPtr());
const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial();
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
else if(ctrl->recType == Nif::RC_NiGeomMorpherController)
{
const Nif::NiGeomMorpherController *geom = static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr());
Ogre::SubEntity *subent = entity->getSubEntity(0);
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(subent, geom->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
ctrl = ctrl->next;
}
}
static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl) static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl)
{ {
Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif"); Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif");
emitter->setParticleVelocity(partctrl->velocity-partctrl->velocityRandom, emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f,
partctrl->velocity+partctrl->velocityRandom); partctrl->velocity + partctrl->velocityRandom*0.5f);
emitter->setEmissionRate(partctrl->emitRate); emitter->setEmissionRate(partctrl->emitRate);
emitter->setTimeToLive(partctrl->lifetime-partctrl->lifetimeRandom, emitter->setTimeToLive(partctrl->lifetime - partctrl->lifetimeRandom*0.5f,
partctrl->lifetime+partctrl->lifetimeRandom); partctrl->lifetime + partctrl->lifetimeRandom*0.5f);
emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x));
emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y)); emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y));
emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z)); emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z));
@ -416,9 +557,9 @@ class NIFObjectLoader
} }
} }
static Ogre::ParticleSystem *createParticleSystem(const std::string &name, const std::string &group, static void createParticleSystem(const std::string &name, const std::string &group,
Ogre::SceneManager *sceneMgr, Ogre::Entity *entitybase, Ogre::SceneManager *sceneMgr, ObjectList &objectlist,
const Nif::Node *partnode) const Nif::Node *partnode, int flags, int partflags)
{ {
const Nif::NiAutoNormalParticlesData *particledata = NULL; const Nif::NiAutoNormalParticlesData *particledata = NULL;
if(partnode->recType == Nif::RC_NiAutoNormalParticles) if(partnode->recType == Nif::RC_NiAutoNormalParticles)
@ -426,73 +567,139 @@ class NIFObjectLoader
else if(partnode->recType == Nif::RC_NiRotatingParticles) else if(partnode->recType == Nif::RC_NiRotatingParticles)
particledata = static_cast<const Nif::NiRotatingParticles*>(partnode)->data.getPtr(); particledata = static_cast<const Nif::NiRotatingParticles*>(partnode)->data.getPtr();
std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex);
if(partnode->name.length() > 0)
fullname += "@type="+partnode->name;
Misc::StringUtils::toLower(fullname);
Ogre::ParticleSystem *partsys = sceneMgr->createParticleSystem(); Ogre::ParticleSystem *partsys = sceneMgr->createParticleSystem();
try {
std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex); const Nif::NiTexturingProperty *texprop = NULL;
if(partnode->name.length() > 0) const Nif::NiMaterialProperty *matprop = NULL;
fullname += "@type="+partnode->name; const Nif::NiAlphaProperty *alphaprop = NULL;
Misc::StringUtils::toLower(fullname); const Nif::NiVertexColorProperty *vertprop = NULL;
const Nif::NiZBufferProperty *zprop = NULL;
const Nif::NiTexturingProperty *texprop = NULL; const Nif::NiSpecularProperty *specprop = NULL;
const Nif::NiMaterialProperty *matprop = NULL; const Nif::NiWireframeProperty *wireprop = NULL;
const Nif::NiAlphaProperty *alphaprop = NULL; bool needTangents = false;
const Nif::NiVertexColorProperty *vertprop = NULL;
const Nif::NiZBufferProperty *zprop = NULL; partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
const Nif::NiSpecularProperty *specprop = NULL; partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group,
const Nif::NiWireframeProperty *wireprop = NULL; texprop, matprop, alphaprop,
bool needTangents = false; vertprop, zprop, specprop,
wireprop, needTangents));
partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group, partsys->setDefaultDimensions(particledata->particleRadius*2.0f,
texprop, matprop, alphaprop, particledata->particleRadius*2.0f);
vertprop, zprop, specprop, partsys->setCullIndividually(false);
wireprop, needTangents)); partsys->setParticleQuota(particledata->numParticles);
// TODO: There is probably a field or flag to specify this, as some
partsys->setDefaultDimensions(particledata->particleRadius*2.0f, // particle effects have it and some don't.
particledata->particleRadius*2.0f); partsys->setKeepParticlesInLocalSpace(true);
partsys->setCullIndividually(false);
partsys->setParticleQuota(particledata->numParticles); Nif::ControllerPtr ctrl = partnode->controller;
// TODO: There is probably a field or flag to specify this, as some while(!ctrl.empty())
// particle effects have it and some don't. {
partsys->setKeepParticlesInLocalSpace(true); if(ctrl->recType == Nif::RC_NiParticleSystemController)
Nif::ControllerPtr ctrl = partnode->controller;
while(!ctrl.empty())
{ {
if(ctrl->recType == Nif::RC_NiParticleSystemController) const Nif::NiParticleSystemController *partctrl = static_cast<const Nif::NiParticleSystemController*>(ctrl.getPtr());
createParticleEmitterAffectors(partsys, partctrl);
if(!partctrl->emitter.empty() && !partsys->isAttached())
{ {
const Nif::NiParticleSystemController *partctrl = static_cast<const Nif::NiParticleSystemController*>(ctrl.getPtr()); int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
createParticleEmitterAffectors(partsys, partctrl); objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys);
if(!partctrl->emitter.empty() && !partsys->isAttached())
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex);
Ogre::Bone *trgtbone = entitybase->getSkeleton()->getBone(trgtid);
entitybase->attachObjectToBone(trgtbone->getName(), partsys);
}
} }
ctrl = ctrl->next;
Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW ParticleSystemController::Value(partsys, partctrl));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&Nif::NiNode::ParticleFlag_AutoPlay)));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
} }
ctrl = ctrl->next;
}
if(!partsys->isAttached())
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys);
}
partsys->setVisible(!(flags&Nif::NiNode::Flag_Hidden));
objectlist.mParticles.push_back(partsys);
}
if(!partsys->isAttached())
static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectList &objectlist, int animflags)
{
do {
if(ctrl->recType == Nif::RC_NiVisController)
{ {
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex); const Nif::NiVisController *vis = static_cast<const Nif::NiVisController*>(ctrl.getPtr());
Ogre::Bone *trgtbone = entitybase->getSkeleton()->getBone(trgtid);
entitybase->attachObjectToBone(trgtbone->getName(), partsys); int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
else if(ctrl->recType == Nif::RC_NiKeyframeController)
{
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
if(!key->data.empty())
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
}
ctrl = ctrl->next;
} while(!ctrl.empty());
}
static void extractTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap &textkeys)
{
for(size_t i = 0;i < tk->list.size();i++)
{
const std::string &str = tk->list[i].text;
std::string::size_type pos = 0;
while(pos < str.length())
{
if(::isspace(str[pos]))
{
pos++;
continue;
}
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos));
std::string result = str.substr(pos, nextpos-pos);
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
pos = nextpos;
} }
} }
catch(std::exception &e) {
std::cerr<< "Particles exception: "<<e.what() <<std::endl;
sceneMgr->destroyParticleSystem(partsys);
partsys = NULL;
};
return partsys;
} }
static void createObjects(const std::string &name, const std::string &group, static void createObjects(const std::string &name, const std::string &group,
Ogre::SceneManager *sceneMgr, const Nif::Node *node, Ogre::SceneManager *sceneMgr, const Nif::Node *node,
ObjectList &objectlist, int flags) ObjectList &objectlist, int flags, int animflags, int partflags)
{ {
// Do not create objects for the collision shape (includes all children) // Do not create objects for the collision shape (includes all children)
if(node->recType == Nif::RC_RootCollisionNode) if(node->recType == Nif::RC_RootCollisionNode)
@ -503,12 +710,24 @@ class NIFObjectLoader
if (node->name.find("marker") != std::string::npos) if (node->name.find("marker") != std::string::npos)
return; return;
flags |= node->flags; if(node->recType == Nif::RC_NiBSAnimationNode)
animflags |= node->flags;
else if(node->recType == Nif::RC_NiBSParticleNode)
partflags |= node->flags;
else
flags |= node->flags;
Nif::ExtraPtr e = node->extra; Nif::ExtraPtr e = node->extra;
while(!e.empty()) while(!e.empty())
{ {
if(e->recType == Nif::RC_NiStringExtraData) if(e->recType == Nif::RC_NiTextKeyExtraData)
{
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex);
extractTextKeys(tk, objectlist.mTextKeys[trgtid]);
}
else if(e->recType == Nif::RC_NiStringExtraData)
{ {
const Nif::NiStringExtraData *sd = static_cast<const Nif::NiStringExtraData*>(e.getPtr()); const Nif::NiStringExtraData *sd = static_cast<const Nif::NiStringExtraData*>(e.getPtr());
// String markers may contain important information // String markers may contain important information
@ -520,9 +739,13 @@ class NIFObjectLoader
flags |= 0x80000000; flags |= 0x80000000;
} }
} }
e = e->extra; e = e->extra;
} }
if(!node->controller.empty())
createNodeControllers(name, node->controller, objectlist, animflags);
if(node->recType == Nif::RC_NiCamera) if(node->recType == Nif::RC_NiCamera)
{ {
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex);
@ -530,94 +753,15 @@ class NIFObjectLoader
objectlist.mCameras.push_back(trgtbone); objectlist.mCameras.push_back(trgtbone);
} }
Nif::ControllerPtr ctrl = node->controller;
while(!ctrl.empty())
{
if(ctrl->recType == Nif::RC_NiVisController)
{
const Nif::NiVisController *vis = static_cast<const Nif::NiVisController*>(ctrl.getPtr());
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
Ogre::ControllerValueRealPtr srcval; /* Filled in later */
Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, false));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
else if(ctrl->recType == Nif::RC_NiKeyframeController)
{
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
if(!key->data.empty())
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
Ogre::ControllerValueRealPtr srcval; /* Filled in later */
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
}
ctrl = ctrl->next;
}
if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000)) if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000))
{ {
const Nif::NiTriShape *shape = static_cast<const Nif::NiTriShape*>(node); createEntity(name, group, sceneMgr, objectlist, node, flags, animflags);
std::string fullname = name+"@index="+Ogre::StringConverter::toString(shape->recIndex);
if(shape->name.length() > 0)
fullname += "@shape="+shape->name;
Misc::StringUtils::toLower(fullname);
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
if(meshMgr.getByName(fullname).isNull())
NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex);
Ogre::Entity *entity = sceneMgr->createEntity(fullname);
entity->setVisible(!(flags&0x01));
objectlist.mEntities.push_back(entity);
if(objectlist.mSkelBase)
{
if(entity->hasSkeleton())
entity->shareSkeletonInstanceWith(objectlist.mSkelBase);
else
{
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex);
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity);
}
}
Nif::ControllerPtr ctrl = node->controller;
while(!ctrl.empty())
{
if(ctrl->recType == Nif::RC_NiUVController)
{
const Nif::NiUVController *uv = static_cast<const Nif::NiUVController*>(ctrl.getPtr());
const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial();
Ogre::ControllerValueRealPtr srcval(Ogre::ControllerManager::getSingleton().getFrameTimeSource());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, true));
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
ctrl = ctrl->next;
}
} }
if((node->recType == Nif::RC_NiAutoNormalParticles || if((node->recType == Nif::RC_NiAutoNormalParticles ||
node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000)) node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000))
{ {
Ogre::ParticleSystem *partsys = createParticleSystem(name, group, sceneMgr, objectlist.mSkelBase, node); createParticleSystem(name, group, sceneMgr, objectlist, node, flags, partflags);
if(partsys != NULL)
{
partsys->setVisible(!(flags&0x01));
objectlist.mParticles.push_back(partsys);
}
} }
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node); const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
@ -627,7 +771,7 @@ class NIFObjectLoader
for(size_t i = 0;i < children.length();i++) for(size_t i = 0;i < children.length();i++)
{ {
if(!children[i].empty()) if(!children[i].empty())
createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags); createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags, animflags, partflags);
} }
} }
} }
@ -674,7 +818,7 @@ public:
// Create a base skeleton entity if this NIF needs one // Create a base skeleton entity if this NIF needs one
createSkelBase(name, group, sceneMgr, node, objectlist); createSkelBase(name, group, sceneMgr, node, objectlist);
} }
createObjects(name, group, sceneMgr, node, objectlist, flags); createObjects(name, group, sceneMgr, node, objectlist, flags, 0, 0);
} }
}; };
@ -757,12 +901,15 @@ ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonena
} }
ObjectList Loader::createObjectBase(Ogre::SceneManager *sceneMgr, std::string name, const std::string &group) ObjectList Loader::createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group)
{ {
ObjectList objectlist; ObjectList objectlist;
Misc::StringUtils::toLower(name); Misc::StringUtils::toLower(name);
NIFObjectLoader::load(sceneMgr, objectlist, name, group, 0xC0000000); NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group, 0xC0000000);
if(objectlist.mSkelBase)
parentNode->attachObject(objectlist.mSkelBase);
return objectlist; return objectlist;
} }

@ -50,6 +50,8 @@ struct ObjectList {
// bones in the mSkelBase which are NiCamera nodes. // bones in the mSkelBase which are NiCamera nodes.
std::vector<Ogre::Bone*> mCameras; std::vector<Ogre::Bone*> mCameras;
std::map<int,TextKeyMap> mTextKeys;
std::vector<Ogre::Controller<Ogre::Real> > mControllers; std::vector<Ogre::Controller<Ogre::Real> > mControllers;
ObjectList() : mSkelBase(0) ObjectList() : mSkelBase(0)
@ -69,7 +71,7 @@ public:
std::string name, std::string name,
const std::string &group="General"); const std::string &group="General");
static ObjectList createObjectBase(Ogre::SceneManager *sceneMgr, static ObjectList createObjectBase(Ogre::SceneNode *parentNode,
std::string name, std::string name,
const std::string &group="General"); const std::string &group="General");
}; };
@ -85,6 +87,10 @@ public:
NodeTargetValue(Ogre::Node *target) : mNode(target) NodeTargetValue(Ogre::Node *target) : mNode(target)
{ } { }
virtual Ogre::Quaternion getRotation(T value) const = 0;
virtual Ogre::Vector3 getTranslation(T value) const = 0;
virtual Ogre::Vector3 getScale(T value) const = 0;
void setNode(Ogre::Node *target) void setNode(Ogre::Node *target)
{ mNode = target; } { mNode = target; }
Ogre::Node *getNode() const Ogre::Node *getNode() const
@ -94,14 +100,4 @@ typedef Ogre::SharedPtr<NodeTargetValue<Ogre::Real> > NodeTargetValueRealPtr;
} }
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 #endif

@ -11,142 +11,7 @@
namespace NifOgre namespace NifOgre
{ {
void NIFSkeletonLoader::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) void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent)
{
Ogre::Animation *anim = skel->createAnimation(name, stopTime);
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);
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, startTime);
if(curtime >= stopTime)
{
didlast = true;
curtime = stopTime;
}
// 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);
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();
}
TextKeyMap NIFSkeletonLoader::extractTextKeys(const Nif::NiTextKeyExtraData *tk)
{
TextKeyMap textkeys;
for(size_t i = 0;i < tk->list.size();i++)
{
const std::string &str = tk->list[i].text;
std::string::size_type pos = 0;
while(pos < str.length())
{
if(::isspace(str[pos]))
{
pos++;
continue;
}
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos));
std::string result = str.substr(pos, nextpos-pos);
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
pos = nextpos;
}
}
return textkeys;
}
void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent)
{ {
Ogre::Bone *bone; Ogre::Bone *bone;
if(!skel->hasBone(node->name)) if(!skel->hasBone(node->name))
@ -164,6 +29,8 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */ if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */
node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */ node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */
node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */ node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */
node->recType == Nif::RC_NiBSAnimationNode || /* Handled in the object loader */
node->recType == Nif::RC_NiBSParticleNode ||
node->recType == Nif::RC_NiCamera || node->recType == Nif::RC_NiCamera ||
node->recType == Nif::RC_NiAutoNormalParticles || node->recType == Nif::RC_NiAutoNormalParticles ||
node->recType == Nif::RC_NiRotatingParticles node->recType == Nif::RC_NiRotatingParticles
@ -173,28 +40,16 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
Nif::ControllerPtr ctrl = node->controller; Nif::ControllerPtr ctrl = node->controller;
while(!ctrl.empty()) while(!ctrl.empty())
{ {
if(ctrl->recType == Nif::RC_NiKeyframeController) if(!(ctrl->recType == Nif::RC_NiParticleSystemController ||
ctrls.push_back(static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr())); ctrl->recType == Nif::RC_NiVisController ||
else if(!(ctrl->recType == Nif::RC_NiParticleSystemController || ctrl->recType == Nif::RC_NiUVController ||
ctrl->recType == Nif::RC_NiVisController || ctrl->recType == Nif::RC_NiKeyframeController ||
ctrl->recType == Nif::RC_NiUVController ctrl->recType == Nif::RC_NiGeomMorpherController
)) ))
warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName()); warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName());
ctrl = ctrl->next; ctrl = ctrl->next;
} }
Nif::ExtraPtr e = node->extra;
while(!e.empty())
{
if(e->recType == Nif::RC_NiTextKeyExtraData && !animroot)
{
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
textkeys = extractTextKeys(tk);
animroot = bone;
}
e = e->extra;
}
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node); const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
if(ninode) if(ninode)
{ {
@ -202,7 +57,7 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
for(size_t i = 0;i < children.length();i++) for(size_t i = 0;i < children.length();i++)
{ {
if(!children[i].empty()) if(!children[i].empty())
buildBones(skel, children[i].getPtr(), animroot, textkeys, ctrls, bone); buildBones(skel, children[i].getPtr(), bone);
} }
} }
} }
@ -215,84 +70,14 @@ void NIFSkeletonLoader::loadResource(Ogre::Resource *resource)
Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName())); Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName()));
const Nif::Node *node = static_cast<const Nif::Node*>(nif->getRoot(0)); const Nif::Node *node = static_cast<const Nif::Node*>(nif->getRoot(0));
std::vector<const Nif::NiKeyframeController*> ctrls;
Ogre::Bone *animroot = NULL;
TextKeyMap textkeys;
try { try {
buildBones(skel, node, animroot, textkeys, ctrls); buildBones(skel, node);
} }
catch(std::exception &e) { catch(std::exception &e) {
std::cerr<< "Exception while loading "<<skel->getName() <<std::endl; std::cerr<< "Exception while loading "<<skel->getName() <<std::endl;
std::cerr<< e.what() <<std::endl; std::cerr<< e.what() <<std::endl;
return; return;
} }
/* Animations without textkeys don't get Ogre::Animation objects. */
if(!animroot)
return;
std::vector<std::string> targets;
// TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file
if(ctrls.size() == 0) // No animations? Then we're done.
return;
float maxtime = 0.0f;
for(size_t i = 0;i < ctrls.size();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)
targets.push_back(target->name);
}
if(targets.size() != ctrls.size())
{
warn("Target size mismatch ("+Ogre::StringConverter::toString(targets.size())+" targets, "+
Ogre::StringConverter::toString(ctrls.size())+" controllers)");
return;
}
Ogre::UserObjectBindings &bindings = animroot->getUserObjectBindings();
bindings.setUserAny(sTextKeyExtraDataID, Ogre::Any(true));
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)
{
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, 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, insiter->second));
} while(insiter++ != lastkeyiter);
bindings.setUserAny(std::string(sTextKeyExtraDataID)+"@"+currentgroup, Ogre::Any(groupkeys));
}
} }
@ -300,16 +85,16 @@ Ogre::SkeletonPtr NIFSkeletonLoader::createSkeleton(const std::string &name, con
{ {
/* We need to be a little aggressive here, since some NIFs have a crap-ton /* 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: * 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- * There are no bones used for skinning, there are no controllers, there
* NiTriShape nodes, there are no nodes named "AttachLight", and the tree * are no nodes named "AttachLight", and the tree consists of NiNode,
* consists of NiNode, NiTriShape, and RootCollisionNode types only. * NiTriShape, and RootCollisionNode types only.
*/ */
if(!node->boneTrafo) if(!node->boneTrafo)
{ {
if(node->recType == Nif::RC_NiTriShape)
return Ogre::SkeletonPtr();
if(node->controller.empty() && node->name != "AttachLight") if(node->controller.empty() && node->name != "AttachLight")
{ {
if(node->recType == Nif::RC_NiTriShape)
return Ogre::SkeletonPtr();
if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode) if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode)
{ {
const Nif::NiNode *ninode = static_cast<const Nif::NiNode*>(node); const Nif::NiNode *ninode = static_cast<const Nif::NiNode*>(node);

@ -36,10 +36,7 @@ class NIFSkeletonLoader : public Ogre::ManualResourceLoader
abort(); abort();
} }
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); void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent=NULL);
static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk);
void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent=NULL);
// Lookup to retrieve an Ogre bone handle for a given Nif record index // Lookup to retrieve an Ogre bone handle for a given Nif record index
std::map<int,int> mNifToOgreHandleMap; std::map<int,int> mNifToOgreHandleMap;

@ -9,6 +9,7 @@
<Property key="OffsetHeight" value="0"/> <Property key="OffsetHeight" value="0"/>
<Codes> <Codes>
<Code range="33 126"/> <Code range="33 126"/>
<Code range="160"/> <!-- Non-breaking space -->
<Code range="192 382"/> <!-- Central and Eastern European languages glyphs --> <Code range="192 382"/> <!-- Central and Eastern European languages glyphs -->
<Code range="1025 1105"/> <Code range="1025 1105"/>
<Code range="2026"/> <!-- Ellipsis --> <Code range="2026"/> <!-- Ellipsis -->

@ -509,6 +509,43 @@ namespace Physic
} }
} }
class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback
{
public:
std::vector<std::string> mResult;
// added in bullet 2.81
// this is just a quick hack, as there does not seem to be a BULLET_VERSION macro?
#if defined(BT_COLLISION_OBJECT_WRAPPER_H)
virtual btScalar addSingleResult(btManifoldPoint& cp,
const btCollisionObjectWrapper* colObj0Wrap,int partId0,int index0,
const btCollisionObjectWrapper* colObj1Wrap,int partId1,int index1)
{
const RigidBody* body = dynamic_cast<const RigidBody*>(colObj0Wrap->m_collisionObject);
if (body)
mResult.push_back(body->mName);
return 0.f;
}
#else
virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* col0, int partId0, int index0,
const btCollisionObject* col1, int partId1, int index1)
{
const RigidBody* body = dynamic_cast<const RigidBody*>(col0);
if (body)
mResult.push_back(body->mName);
return 0.f;
}
#endif
};
std::vector<std::string> PhysicEngine::getCollisions(const std::string& name)
{
RigidBody* body = getRigidBody(name);
ContactTestResultCallback callback;
dynamicsWorld->contactTest(body, callback);
return callback.mResult;
}
void PhysicEngine::stepSimulation(double deltaT) void PhysicEngine::stepSimulation(double deltaT)
{ {
// This seems to be needed for character controller objects // This seems to be needed for character controller objects

@ -298,6 +298,8 @@ namespace Physic
*/ */
std::vector< std::pair<float, std::string> > rayTest2(btVector3& from, btVector3& to); std::vector< std::pair<float, std::string> > rayTest2(btVector3& from, btVector3& to);
std::vector<std::string> getCollisions(const std::string& name);
//event list of non player object //event list of non player object
std::list<PhysicEvent> NPEventList; std::list<PhysicEvent> NPEventList;

@ -19,6 +19,7 @@ Fader::Fader(Ogre::SceneManager* sceneMgr)
, mTargetAlpha(0.f) , mTargetAlpha(0.f)
, mCurrentAlpha(0.f) , mCurrentAlpha(0.f)
, mStartAlpha(0.f) , mStartAlpha(0.f)
, mFactor(1.f)
{ {
// Create the fading material // Create the fading material
MaterialPtr material = MaterialManager::getSingleton().create("FadeInOutMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); MaterialPtr material = MaterialManager::getSingleton().create("FadeInOutMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME );
@ -62,19 +63,20 @@ void Fader::update(float dt)
mCurrentAlpha += dt/mTargetTime * (mTargetAlpha-mStartAlpha); mCurrentAlpha += dt/mTargetTime * (mTargetAlpha-mStartAlpha);
if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha; if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha;
} }
applyAlpha();
mRemainingTime -= dt; mRemainingTime -= dt;
} }
if (mCurrentAlpha == 0.f) mRectangle->setVisible(false); if (1.f-((1.f-mCurrentAlpha) * mFactor) == 0.f)
mRectangle->setVisible(false);
else
applyAlpha();
} }
void Fader::applyAlpha() void Fader::applyAlpha()
{ {
mRectangle->setVisible(true); mRectangle->setVisible(true);
mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, mCurrentAlpha); mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, 1.f-((1.f-mCurrentAlpha) * mFactor));
} }
void Fader::fadeIn(float time) void Fader::fadeIn(float time)

@ -29,6 +29,8 @@ namespace Render
void fadeOut(const float time); void fadeOut(const float time);
void fadeTo(const int percent, const float time); void fadeTo(const int percent, const float time);
void setFactor (float factor) { mFactor = factor; }
private: private:
enum FadingMode enum FadingMode
{ {
@ -49,6 +51,8 @@ namespace Render
float mCurrentAlpha; float mCurrentAlpha;
float mStartAlpha; float mStartAlpha;
float mFactor;
Ogre::SceneManager* mSceneMgr; Ogre::SceneManager* mSceneMgr;
}; };
}} }}

Loading…
Cancel
Save