diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index 56c08582f..9a9b8df41 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -96,15 +96,15 @@ bool GameSettings::readFile(QTextStream &stream) QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); while (!stream.atEnd()) { - QString line = stream.readLine().simplified(); + QString line = stream.readLine(); if (line.isEmpty() || line.startsWith("#")) continue; if (keyRe.indexIn(line) != -1) { - QString key = keyRe.cap(1).simplified(); - QString value = keyRe.cap(2).simplified(); + QString key = keyRe.cap(1); + QString value = keyRe.cap(2); // Don't remove existing data entries if (key != QLatin1String("data")) diff --git a/apps/launcher/settings/settingsbase.hpp b/apps/launcher/settings/settingsbase.hpp index bbfad1fbb..155f5f9a9 100644 --- a/apps/launcher/settings/settingsbase.hpp +++ b/apps/launcher/settings/settingsbase.hpp @@ -52,7 +52,7 @@ public: QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); while (!stream.atEnd()) { - QString line = stream.readLine().simplified(); + QString line = stream.readLine(); if (line.isEmpty() || line.startsWith("#")) continue; @@ -65,8 +65,8 @@ public: if (keyRe.indexIn(line) != -1) { - QString key = keyRe.cap(1).simplified(); - QString value = keyRe.cap(2).simplified(); + QString key = keyRe.cap(1); + QString value = keyRe.cap(2); if (!sectionPrefix.isEmpty()) key.prepend(sectionPrefix); diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 02356cc07..c56cffb74 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -14,7 +14,7 @@ set(GAME_HEADER source_group(game FILES ${GAME} ${GAME_HEADER}) 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 compositors characterpreview externalrendering globalmap videoplayer ripplesimulation refraction ) @@ -54,7 +54,7 @@ add_openmw_dir (mwworld containerstore actiontalk actiontake manualref player cellfunctors failedaction cells localscripts customdata weather inventorystore ptr actionopen actionread 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 diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 4472b205f..39e1137ca 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -368,7 +368,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins, mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap, mActivationDistanceOverride)); - MWBase::Environment::get().getWorld()->setupPlayer(mNewGame); + MWBase::Environment::get().getWorld()->setupPlayer(); //Load translation data mTranslationDataStorage.setEncoder(mEncoder); diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 020647744..38794269b 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -99,6 +99,9 @@ namespace MWBase float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0; ///< 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; ///< Run animation for a MW-reference. Calls to this function for references that are currently not /// in the scene should be ignored. diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index bee744386..a3b9d2cf2 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -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 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; ///< 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 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 togglePlayerLooking(bool enable) = 0; virtual void changeVanityModeScale(float factor) = 0; virtual bool vanityRotateCamera(float * rot) = 0; - virtual void setupPlayer(bool newGame) = 0; + virtual void setupPlayer() = 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 int canRest() = 0; diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 8a8f72c88..fa0d47107 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -375,12 +375,12 @@ namespace MWClass 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 *ref = ptr.get(); - return ref->mBase->mData.mEnchant; + return ref->mBase->mData.mEnchant/10.f; } bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 9b8e9dd14..964ba7146 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -77,7 +77,7 @@ namespace MWClass 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; }; diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index b658295f8..472d17da9 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -176,12 +176,12 @@ namespace MWClass 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 *ref = ptr.get(); - return ref->mBase->mData.mEnchant; + return ref->mBase->mData.mEnchant/10.f; } bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index 3f083f552..3d728e506 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -58,7 +58,7 @@ namespace MWClass 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; }; diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 3072f852d..599efa51a 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -295,12 +295,12 @@ namespace MWClass 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 *ref = ptr.get(); - return ref->mBase->mData.mEnchant; + return ref->mBase->mData.mEnchant/10.f; } bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index a6de0cb4f..db4b7a2e9 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -71,7 +71,7 @@ namespace MWClass 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; }; diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 163cf0277..91d38b8ef 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -12,6 +12,7 @@ #include "../mwworld/nullaction.hpp" #include "../mwworld/failedaction.hpp" #include "../mwworld/actionteleport.hpp" +#include "../mwworld/actiondoor.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/inventorystore.hpp" @@ -71,7 +72,7 @@ namespace MWClass ptr.get(); 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 trapActivationSound = "Disarm Trap Fail"; @@ -139,12 +140,11 @@ namespace MWClass else { // animated door - // TODO return action for rotating the door - - // This is a little pointless, but helps with testing - boost::shared_ptr action(new MWWorld::NullAction); - - action->setSound(openSound); + boost::shared_ptr action(new MWWorld::ActionDoor(ptr)); + if (MWBase::Environment::get().getWorld()->getOpenOrCloseDoor(ptr)) + action->setSound(openSound); + else + action->setSound(closeSound); return action; } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index bfbf10756..52a22d28e 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -181,4 +181,12 @@ namespace MWClass { return npcServices & ESM::NPC::Picks; } + + int Lockpick::getItemMaxHealth (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mUses; + } } diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index edd884a3e..df794debd 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -59,6 +59,9 @@ namespace MWClass virtual std::string getModel(const MWWorld::Ptr &ptr) 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 }; } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 4ec6a8340..1c14c894d 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -180,4 +180,12 @@ namespace MWClass { return npcServices & ESM::NPC::Probes; } + + int Probe::getItemMaxHealth (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mData.mUses; + } } diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index 75ebaa01c..70390c85c 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -59,6 +59,9 @@ namespace MWClass virtual std::string getModel(const MWWorld::Ptr &ptr) 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 }; } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 6c6b3daa6..306a2d671 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -428,12 +428,12 @@ namespace MWClass 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 *ref = ptr.get(); - return ref->mBase->mData.mEnchant; + return ref->mBase->mData.mEnchant/10.f; } bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 05b1aee22..ba444ba05 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -79,7 +79,7 @@ namespace MWClass 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; }; } diff --git a/apps/openmw/mwdialogue/journalentry.hpp b/apps/openmw/mwdialogue/journalentry.hpp index 19a9f42b5..9d009b48b 100644 --- a/apps/openmw/mwdialogue/journalentry.hpp +++ b/apps/openmw/mwdialogue/journalentry.hpp @@ -3,7 +3,7 @@ #include -namespace MWWorld +namespace MWWorld { struct ESMStore; } @@ -27,7 +27,7 @@ namespace MWDialogue 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 { int mDay; diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index 463c645dc..cba3412e9 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -397,11 +397,24 @@ namespace MWGui void Console::setSelectedObject(const MWWorld::Ptr& object) { - mPtr = object; - if (!mPtr.isEmpty()) - setTitle("#{sConsoleTitle} (" + mPtr.getCellRef().mRefID + ")"); + if (!object.isEmpty()) + { + if (object == mPtr) + { + setTitle("#{sConsoleTitle}"); + mPtr=MWWorld::Ptr(); + } + else + { + setTitle("#{sConsoleTitle} (" + object.getCellRef().mRefID + ")"); + mPtr = object; + } + } else + { setTitle("#{sConsoleTitle}"); + mPtr = MWWorld::Ptr(); + } MyGUI::InputManager::getInstance().setKeyFocusWidget(command); } diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 2ae98f358..6f7cfbf40 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -57,8 +57,9 @@ namespace MWGui void EnchantingDialog::updateLabels() { - mEnchantmentPoints->setCaption(boost::lexical_cast(mEnchanting.getEnchantCost()) - + " / " + boost::lexical_cast(mEnchanting.getMaxEnchantValue())); + std::stringstream enchantCost; + enchantCost << std::setprecision(1) << std::fixed << mEnchanting.getEnchantCost(); + mEnchantmentPoints->setCaption(enchantCost.str() + " / " + boost::lexical_cast(mEnchanting.getMaxEnchantValue())); mCharge->setCaption(boost::lexical_cast(mEnchanting.getGemCharge())); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index c76faf6f4..024c0d4c6 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -921,14 +921,16 @@ namespace MWGui const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get() .find(MWWorld::Class::get(item).getEnchantment(item)); - int chargePercent = item.getCellRef().mEnchantmentCharge / static_cast(ench->mData.mCharge) * 100; + int chargePercent = (item.getCellRef().mEnchantmentCharge == -1) ? 100 + : (item.getCellRef().mEnchantmentCharge / static_cast(ench->mData.mCharge) * 100); mHud->setSelectedEnchantItem(item, chargePercent); mSpellWindow->setTitle(MWWorld::Class::get(item).getName(item)); } void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item) { - int durabilityPercent = item.getCellRef().mCharge / static_cast(MWWorld::Class::get(item).getItemMaxHealth(item)) * 100; + int durabilityPercent = (item.getCellRef().mCharge == -1) ? 100 + : (item.getCellRef().mCharge / static_cast(MWWorld::Class::get(item).getItemMaxHealth(item)) * 100); mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(MWWorld::Class::get(item).getName(item)); } @@ -1113,6 +1115,13 @@ namespace MWGui { 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 () { diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 652ad870f..acac77bf3 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -220,7 +220,7 @@ namespace MWGui virtual void loadingDone(); virtual void enableRest() { mRestAllowed = true; } - virtual bool getRestEnabled() { return mRestAllowed; } + virtual bool getRestEnabled(); virtual bool getPlayerSleeping(); virtual void wakeUpPlayer(); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 6a34161e0..1cd716413 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -679,7 +679,8 @@ namespace MWInput void InputManager::quickKey (int index) { - mWindows.activateQuickKey (index); + if (!mWindows.isGuiMode()) + mWindows.activateQuickKey (index); } void InputManager::showQuickKeysMenu() @@ -719,19 +720,17 @@ namespace MWInput void InputManager::resetIdleTime() { - if (mTimeIdle < 0) { - MWBase::Environment::get().getWorld()->toggleVanityMode(false, false); - } + if (mTimeIdle < 0) + MWBase::Environment::get().getWorld()->toggleVanityMode(false); mTimeIdle = 0.f; } void InputManager::updateIdleTime(float dt) { - if (mTimeIdle >= 0.f) { + if (mTimeIdle >= 0.f) mTimeIdle += dt; - } if (mTimeIdle > 30.f) { - MWBase::Environment::get().getWorld()->toggleVanityMode(true, false); + MWBase::Environment::get().getWorld()->toggleVanityMode(true); mTimeIdle = -1.f; } } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 82e46ea46..a20bf6d1f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -290,6 +290,13 @@ namespace MWMechanics 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) { PtrControllerMap::iterator iter = mActors.find(ptr); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index aabd86dc7..c01d63093 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -78,6 +78,8 @@ namespace MWMechanics int countDeaths (const std::string& id) const; ///< 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 skipAnimation(const MWWorld::Ptr& ptr); }; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 02a5aa100..b82fc4dc5 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -28,6 +28,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/player.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) - : mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false) + : mPtr(ptr), mAnimation(anim), mCharState(state), mSkipAnim(false), mMovingAnim(false), mSecondsOfRunning(0), mSecondsOfSwimming(0) { if(!mAnimation) return; - mAnimation->setController(this); - - getStateInfo(mState, &mCurrentGroup); + std::string group; + getStateInfo(mCharState, &group); if(MWWorld::Class::get(mPtr).isActor()) { /* 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. */ mAnimation->setAccumulation(Ogre::Vector3(0.0f)); } - if(mAnimation->hasAnimation(mCurrentGroup)) - mAnimation->play(mCurrentGroup, "stop", "stop", loop); -} - -CharacterController::CharacterController(const CharacterController &rhs) - : mPtr(rhs.mPtr), mAnimation(rhs.mAnimation), mAnimQueue(rhs.mAnimQueue) - , mCurrentGroup(rhs.mCurrentGroup), mState(rhs.mState) - , mSkipAnim(rhs.mSkipAnim) -{ - if(!mAnimation) - return; - /* We've been copied. Update the animation with the new controller. */ - mAnimation->setController(this); + if(mAnimation->hasAnimation(group)) + mMovingAnim = mAnimation->play(group, "start", "stop", 1.0f, loop ? (~(size_t)0) : 0); } 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: "< 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 * for the initial thrust (which would be carried by "physics" until landing). */ if(onground && vec.z > 0.0f) @@ -214,11 +201,13 @@ void CharacterController::update(float duration, Movement &movement) if(vec.x > 0.0f) setState(inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight) : (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight)), true); - else if(vec.x < 0.0f) setState(inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft) : (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 movement.mPosition[1] += vec.y * (speed*duration); } @@ -227,12 +216,15 @@ void CharacterController::update(float duration, Movement &movement) if(vec.y > 0.0f) setState(inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward) : (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward)), true); - else if(vec.y < 0.0f) setState(inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack) : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack)), true); + // Apply any sideways movement manually 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) { @@ -241,8 +233,18 @@ void CharacterController::update(float duration, Movement &movement) else if(rot.z < 0.0f) setState(CharState_TurnLeft, true); } - else if(mAnimQueue.size() == 0) - setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true); + else if(getState() != CharState_SpecialIdle || !mAnimation->isPlaying(0)) + { + 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[1] += rot.y * duration; @@ -268,20 +270,17 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int else { count = std::max(count, 1); - if(mode != 0 || mAnimQueue.size() == 0) + if(mode != 0 || getState() != CharState_SpecialIdle) { mAnimQueue.clear(); - while(count-- > 0) - mAnimQueue.push_back(groupname); - mCurrentGroup = groupname; - mState = CharState_SpecialIdle; - mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", false); + mCharState = CharState_SpecialIdle; + mLooping = false; + mMovingAnim = mAnimation->play(groupname, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1); } else if(mode == 0) { - mAnimQueue.resize(1); - while(count-- > 0) - mAnimQueue.push_back(groupname); + mAnimQueue.clear(); + mAnimQueue.push_back(std::make_pair(groupname, count-1)); } } } @@ -294,25 +293,24 @@ void CharacterController::skipAnim() void CharacterController::setState(CharacterState state, bool loop) { - if(mState == state) - { - if(mAnimation) - mAnimation->setLooping(loop); + if(mCharState == state) return; - } - mState = state; + mCharState = state; + mLooping = loop; + forceStateUpdate(); +} + +void CharacterController::forceStateUpdate() +{ if(!mAnimation) return; mAnimQueue.clear(); std::string anim; - getStateInfo(mState, &anim); - if(mAnimation->hasAnimation(anim)) - { - mCurrentGroup = anim; - mAnimation->play(mCurrentGroup, "start", "stop", loop); - } + getStateInfo(mCharState, &anim); + if((mMovingAnim=mAnimation->hasAnimation(anim)) != false) + mMovingAnim = mAnimation->play(anim, "start", "stop", 0.0f, mLooping ? (~(size_t)0) : 0); } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 5b5a65f79..4ee217d11 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -72,22 +72,21 @@ class CharacterController MWWorld::Ptr mPtr; MWRender::Animation *mAnimation; - typedef std::deque AnimationQueue; + typedef std::deque > AnimationQueue; AnimationQueue mAnimQueue; - std::string mCurrentGroup; - CharacterState mState; + CharacterState mCharState; + bool mLooping; bool mSkipAnim; -protected: - /* Called by the animation whenever a new text key is reached. */ - void markerEvent(float time, const std::string &evt); + // counted for skill increase + float mSecondsOfSwimming; + float mSecondsOfRunning; - friend class MWRender::Animation; + bool mMovingAnim; public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop); - CharacterController(const CharacterController &rhs); virtual ~CharacterController(); void updatePtr(const MWWorld::Ptr &ptr); @@ -99,7 +98,9 @@ public: void setState(CharacterState state, bool loop); CharacterState getState() const - { return mState; } + { return mCharState; } + + void forceStateUpdate(); }; } diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index a38cb0037..ded75f03a 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -134,7 +134,7 @@ namespace MWMechanics } } - int Enchanting::getEnchantCost() const + float Enchanting::getEnchantCost() const { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); float cost = 0; @@ -193,7 +193,7 @@ namespace MWMechanics return soul->mData.mSoul; } - int Enchanting::getMaxEnchantValue() const + float Enchanting::getMaxEnchantValue() const { if (itemEmpty()) return 0; diff --git a/apps/openmw/mwmechanics/enchanting.hpp b/apps/openmw/mwmechanics/enchanting.hpp index d8a6342ac..2831f9ddb 100644 --- a/apps/openmw/mwmechanics/enchanting.hpp +++ b/apps/openmw/mwmechanics/enchanting.hpp @@ -35,9 +35,9 @@ namespace MWMechanics bool create(); //Return true if created, false if failed. void nextEnchantType(); //Set enchant type to next possible type (for mOldItemPtr object) int getEnchantType() const; - int getEnchantCost() const; + float getEnchantCost() const; int getEnchantPrice() const; - int getMaxEnchantValue() const; + float getMaxEnchantValue() const; int getGemCharge() const; float getEnchantChance() const; bool soulEmpty() const; //Return true if empty diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 66d20e662..969a233bc 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -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) { if(MWWorld::Class::get(ptr).isActor()) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 4b8d42cd3..f3a38bf36 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -96,6 +96,8 @@ namespace MWMechanics void toLower(std::string npcFaction); ///< 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 skipAnimation(const MWWorld::Ptr& ptr); }; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 7216e8fe0..def91a6c5 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -169,8 +169,7 @@ float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& cla if (specialisationFactor<=0) 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) diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index 4630208b4..4eb844607 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -1,10 +1,5 @@ #include "activatoranimation.hpp" -#include -#include -#include -#include - #include "renderconst.hpp" #include "../mwbase/world.hpp" @@ -24,28 +19,10 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) assert (ref->mBase != NULL); 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); - for(size_t i = 0;i < mObjectList.mEntities.size();i++) - { - 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); + addObjectList(mPtr.getRefData().getBaseNode(), name, false); + setRenderProperties(mObjects.back().mObjectList, RV_Misc, RQG_Main, RQG_Alpha); } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a849b20ef..39bffbded 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -16,6 +18,32 @@ 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) { 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) : mPtr(ptr) - , mController(NULL) , mInsert(NULL) + , mSkelBase(NULL) , mAccumRoot(NULL) , mNonAccumRoot(NULL) - , mAccumulate(Ogre::Vector3::ZERO) + , mNonAccumCtrl(NULL) + , mAccumulate(0.0f) , mLastPosition(0.0f) - , mCurrentControllers(NULL) - , mCurrentKeys(NULL) - , mCurrentAnim(NULL) - , mCurrentTime(0.0f) - , mStopTime(0.0f) - , mPlaying(false) - , mLooping(false) , mAnimVelocity(0.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() @@ -54,105 +82,34 @@ Animation::~Animation() if(mInsert) { Ogre::SceneManager *sceneMgr = mInsert->getCreator(); - destroyObjectList(sceneMgr, mObjectList); - - for(size_t i = 0;i < mAnimationSources.size();i++) - destroyObjectList(sceneMgr, mAnimationSources[i]); - mAnimationSources.clear(); + for(size_t i = 0;i < mObjects.size();i++) + destroyObjectList(sceneMgr, mObjects[i].mObjectList); + mObjects.clear(); } } -void Animation::setAnimationSources(const std::vector &names) +void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly) { - if(!mObjectList.mSkelBase) - 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 > ctrlval(OGRE_NEW AnimationValue(this)); - Ogre::SkeletonInstance *skelinst = mObjectList.mSkelBase->getSkeleton(); - std::vector::const_iterator nameiter; - for(nameiter = names.begin();nameiter != names.end();nameiter++) + if(!mInsert) { - mAnimationSources.push_back(NifOgre::Loader::createObjectBase(sceneMgr, *nameiter)); - if(!mAnimationSources.back().mSkelBase) - { - std::cerr<< "Failed to get skeleton source "<<*nameiter < *dstval = dynamic_cast*>(objects.mControllers[i].getDestination().getPointer()); - if(!dstval) continue; - - const Ogre::String &trgtname = dstval->getNode()->getName(); - if(!skelinst->hasBone(trgtname)) continue; - - Ogre::Bone *bone = skelinst->getBone(trgtname); - dstval->setNode(bone); - } - - for(size_t i = 0;i < objects.mControllers.size();i++) - { - if(objects.mControllers[i].getSource().isNull()) - objects.mControllers[i].setSource(ctrlval); - } - - Ogre::Entity *ent = objects.mSkelBase; - Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(ent->getSkeleton()->getName()); - Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator(); - while(boneiter.hasMoreElements()) - { - Ogre::Bone *bone = boneiter.getNext(); - Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings(); - const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID); - if(data.isEmpty() || !Ogre::any_cast(data)) - continue; - - if(!mNonAccumRoot) - { - mAccumRoot = mInsert; - mNonAccumRoot = mObjectList.mSkelBase->getSkeleton()->getBone(bone->getName()); - } - - for(int i = 0;i < skel->getNumAnimations();i++) - { - Ogre::Animation *anim = skel->getAnimation(i); - const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+ - "@"+anim->getName()); - if(!groupdata.isEmpty()) - mTextKeys[anim->getName()] = Ogre::any_cast(groupdata); - } - - break; - } + mInsert = node->createChildSceneNode(); + assert(mInsert); } -} -void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model) -{ - mInsert = node->createChildSceneNode(); - assert(mInsert); + mObjects.push_back(ObjectInfo()); + ObjectInfo &obj = mObjects.back(); + obj.mActiveLayers = 0; + obj.mObjectList = (!baseonly ? NifOgre::Loader::createObjects(mInsert, model) : + NifOgre::Loader::createObjectBase(mInsert, model)); - mObjectList = NifOgre::Loader::createObjects(mInsert, model); - if(mObjectList.mSkelBase) + NifOgre::ObjectList &objlist = obj.mObjectList; + if(objlist.mSkelBase) { - Ogre::AnimationStateSet *aset = mObjectList.mSkelBase->getAllAnimationStates(); + if(mObjects.size() == 1) + mSkelBase = objlist.mSkelBase; + + Ogre::AnimationStateSet *aset = objlist.mSkelBase->getAllAnimationStates(); Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator(); while(asiter.hasMoreElements()) { @@ -164,27 +121,184 @@ void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model // 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::SkeletonInstance *skelinst = objlist.mSkelBase->getSkeleton(); Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); } - - Ogre::SharedPtr > ctrlval(OGRE_NEW AnimationValue(this)); - for(size_t i = 0;i < mObjectList.mControllers.size();i++) + if(objlist.mSkelBase && mSkelBase) { - if(mObjectList.mControllers[i].getSource().isNull()) - mObjectList.mControllers[i].setSource(ctrlval); + Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); + if(mSkelBase == objlist.mSkelBase) + { + if(objlist.mTextKeys.size() > 0) + { + mAccumRoot = mInsert; + mNonAccumRoot = baseinst->getBone(objlist.mTextKeys.begin()->first); + } + } + else + { + for(size_t i = 0;i < objlist.mControllers.size();i++) + { + NifOgre::NodeTargetValue *dstval; + dstval = dynamic_cast*>(objlist.mControllers[i].getDestination().getPointer()); + if(!dstval) continue; + + 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]); + } + + 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::clearExtraSources() +{ + for(size_t layer = 0;layer < sMaxLayers;layer++) + { + 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); + + if(mObjects.size() > 1) + { + mObjects.resize(1); + mObjects[0].mActiveLayers = 0; + + 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::iterator obj(mObjects.begin()); + for(;obj != mObjects.end();obj++) + { + std::vector >::const_iterator ctrl(obj->mObjectList.mControllers.begin()); + for(;ctrl != obj->mObjectList.mControllers.end();ctrl++) + { + NifOgre::NodeTargetValue *dstval; + dstval = dynamic_cast*>(ctrl->getDestination().getPointer()); + if(dstval) + { + /*if(getLayerByName(dstval->getNode()->getName()) >= 0)*/ + continue; + } + mActiveCtrls.insert(mActiveCtrls.end(), *ctrl); + } + } + + std::vector > *ctrls = NULL; + size_t layer = 0; + while(layer < sMaxLayers) + { + /* Now get controllers that target nodes that belong to this layer from + * whatever objectlist is active on this layer. + */ + std::vector::iterator obj(mObjects.begin()); + for(;obj != mObjects.end();obj++) + { + if((obj->mActiveLayers&(1<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< >::const_iterator ctrl(ctrls->begin()); + for(;ctrl != ctrls->end();ctrl++) + { + NifOgre::NodeTargetValue *dstval; + dstval = dynamic_cast*>(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) { - if(mObjectList.mSkelBase) + if(mSkelBase) { - Ogre::SkeletonInstance *skel = mObjectList.mSkelBase->getSkeleton(); + Ogre::SkeletonInstance *skel = mSkelBase->getSkeleton(); if(skel->hasBone(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::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)) - return true; + if(iter->second.compare(0, groupname.size(), groupname) == 0 && + 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::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; } -void Animation::setLooping(bool loop) -{ - mLooping = loop; -} 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 *nonaccumctrl, const Ogre::Vector3 &accum, const std::string &groupname) { - const Ogre::NodeAnimationTrack *track = 0; - - Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); - while(!track && trackiter.hasMoreElements()) + const std::string start = groupname+": start"; + const std::string loopstart = groupname+": loop start"; + const std::string loopstop = groupname+": loop stop"; + const std::string stop = groupname+": stop"; + float starttime = std::numeric_limits::max(); + float stoptime = 0.0f; + NifOgre::TextKeyMap::const_iterator keyiter(keys.begin()); + while(keyiter != keys.end()) { - const Ogre::NodeAnimationTrack *cur = trackiter.getNext(); - if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName()) - track = cur; + if(keyiter->second == start || keyiter->second == loopstart) + starttime = keyiter->first; + else if(keyiter->second == loopstop || keyiter->second == stop) + { + stoptime = keyiter->first; + break; + } + keyiter++; } - if(track && track->getNumKeyFrames() > 1) + if(stoptime > starttime) { - float loopstarttime = 0.0f; - float loopstoptime = mCurrentAnim->getLength(); - NifOgre::TextKeyMap::const_iterator keyiter = mCurrentKeys->begin(); - while(keyiter != mCurrentKeys->end()) - { - if(keyiter->second == "loop start") - loopstarttime = keyiter->first; - else if(keyiter->second == "loop stop") - { - loopstoptime = keyiter->first; - break; - } - keyiter++; - } + Ogre::Vector3 startpos = nonaccumctrl->getTranslation(starttime) * accum; + Ogre::Vector3 endpos = nonaccumctrl->getTranslation(stoptime) * accum; - if(loopstoptime > loopstarttime) - { - Ogre::TransformKeyFrame startkf(0, loopstarttime); - Ogre::TransformKeyFrame endkf(0, loopstoptime); - - track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstarttime), &startkf); - track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstoptime), &endkf); - - mAnimVelocity = startkf.getTranslate().distance(endkf.getTranslate()) / - (loopstoptime-loopstarttime); - } + return startpos.distance(endpos) / (stoptime-starttime); } + + return 0.0f; } 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::TransformKeyFrame kf(0, mCurrentTime); - Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); - while(trackiter.hasMoreElements()) - { - const Ogre::NodeAnimationTrack *track = trackiter.getNext(); - 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; + /* Get the non-accumulation root's difference from the last update, and move the position + * accordingly. + */ + posdiff = (mNonAccumCtrl->getTranslation(mLayer[0].mTime) - mLastPosition) * mAccumulate; + position += posdiff; /* Translate the accumulation root back to compensate for the move. */ mLastPosition += posdiff; 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 *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint) { - mNextKey = mCurrentKeys->begin(); - - while(mNextKey != mCurrentKeys->end() && mNextKey->second != start) - mNextKey++; - if(mNextKey != mCurrentKeys->end()) - mCurrentTime = mNextKey->first; - else + std::string tag = groupname+": "+start; + NifOgre::TextKeyMap::const_iterator startkey(keys.begin()); + while(startkey != keys.end() && startkey->second != tag) + startkey++; + if(startkey == keys.end() && start == "loop start") { - mNextKey = mCurrentKeys->begin(); - while(mNextKey != mCurrentKeys->end() && mNextKey->second != "start") - mNextKey++; - if(mNextKey != mCurrentKeys->end()) - mCurrentTime = mNextKey->first; - else - { - mNextKey = mCurrentKeys->begin(); - mCurrentTime = 0.0f; - } + tag = groupname+": start"; + startkey = keys.begin(); + while(startkey != keys.end() && startkey->second != tag) + startkey++; + } + 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(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) + { + if(mLayer[layeridx].mNextKey->second == tag) + mLayer[layeridx].mLoopStartKey = mLayer[layeridx].mNextKey; + mLayer[layeridx].mNextKey++; } - if(stop.length() > 0) - { - NifOgre::TextKeyMap::const_iterator stopKey = mNextKey; - while(stopKey != mCurrentKeys->end() && stopKey->second != stop) - stopKey++; - if(stopKey != mCurrentKeys->end()) - mStopTime = stopKey->first; - else - mStopTime = mCurrentAnim->getLength(); - } + if(layeridx == 0 && nonaccumctrl) + mLastPosition = nonaccumctrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate; - if(mNonAccumRoot) - { - const Ogre::NodeAnimationTrack *track = 0; - Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator(); - while(!track && trackiter.hasMoreElements()) - { - const Ogre::NodeAnimationTrack *cur = trackiter.getNext(); - if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName()) - track = cur; - } + return true; +} - if(track) - { - 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--; + + 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::handleEvent(float time, const std::string &evt) +bool Animation::handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key) { - if(evt == "start" || evt == "loop start") - { - /* Do nothing */ - return true; - } + float time = key->first; + const std::string &evt = key->second; if(evt.compare(0, 7, "sound: ") == 0) { @@ -414,108 +524,219 @@ bool Animation::handleEvent(float time, const std::string &evt) 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 + return true; + } + 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) + { + 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(mCurrentTime >= time) + if(mLayer[layeridx].mTime >= time) return false; } return true; } - if(evt == "stop") - { - if(mLooping) - { - reset("loop start", ""); - if(mCurrentTime >= time) - return false; - return true; - } - // fall-through - } - if(mController) - mController->markerEvent(time, evt); + + std::cerr<< "Unhandled animation textkey: "<::reverse_iterator iter(mAnimationSources.rbegin());iter != mAnimationSources.rend();iter++) + // TODO: parameterize this + size_t layeridx = 0; + + if(!mSkelBase) + return false; + + for(std::vector::iterator iter(mObjects.begin());iter != mObjects.end();iter++) + iter->mActiveLayers &= ~(1<::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 *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); - mCurrentKeys = &mTextKeys[groupname]; - mCurrentControllers = &iter->mControllers; - mAnimVelocity = 0.0f; - - if(mNonAccumRoot) - calcAnimVelocity(); - - found = true; - break; + NifOgre::NodeTargetValue *dstval; + dstval = dynamic_cast*>(objlist.mControllers[i].getDestination().getPointer()); + if(dstval && dstval->getNode() == mNonAccumRoot) + { + nonaccumctrl = dstval; + break; + } } } - if(!found) - throw std::runtime_error("Failed to find animation "+groupname); - reset(start, stop); - setLooping(loop); - mPlaying = true; - } - catch(std::exception &e) { - std::cerr<< e.what() <mActiveLayers |= (1< 1.0f) + { + movinganim = (nonaccumctrl==mNonAccumCtrl); + break; + } } + if(!foundanim) + std::cerr<< "Failed to find animation "<::iterator iter(mObjects.begin());iter != mObjects.end();iter++) + iter->mActiveLayers &= ~(1<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 duration) { Ogre::Vector3 movement(0.0f); - timepassed *= mAnimSpeedMult; - while(mCurrentAnim && mPlaying) + duration *= mAnimSpeedMult; + for(size_t layeridx = 0;layeridx < sMaxLayers;layeridx++) { - float targetTime = mCurrentTime + timepassed; - if(mNextKey == mCurrentKeys->end() || mNextKey->first > targetTime) + if(mLayer[layeridx].mGroupName.empty()) + continue; + + float timepassed = duration; + while(mLayer[layeridx].mPlaying) { - mCurrentTime = std::min(mStopTime, targetTime); - if(mNonAccumRoot) - movement += updatePosition(); - mPlaying = (mLooping || mStopTime > mCurrentTime); - timepassed = targetTime - mCurrentTime; - break; + float targetTime = mLayer[layeridx].mTime + timepassed; + if(mLayer[layeridx].mNextKey->first > targetTime) + { + mLayer[layeridx].mTime = targetTime; + if(layeridx == 0 && mNonAccumCtrl) + updatePosition(movement); + break; + } + + NifOgre::TextKeyMap::const_iterator key(mLayer[layeridx].mNextKey++); + mLayer[layeridx].mTime = key->first; + if(layeridx == 0 && mNonAccumCtrl) + updatePosition(movement); + + mLayer[layeridx].mPlaying = (key != mLayer[layeridx].mStopKey); + timepassed = targetTime - mLayer[layeridx].mTime; + + if(!handleTextKey(layeridx, key)) + break; } - - float time = mNextKey->first; - const std::string &evt = mNextKey->second; - mNextKey++; - - mCurrentTime = time; - if(mNonAccumRoot) - movement += updatePosition(); - mPlaying = (mLooping || mStopTime > mCurrentTime); - timepassed = targetTime - mCurrentTime; - - if(!handleEvent(time, evt)) - 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 - // transformations to entities this skeleton instance is shared with. - mObjectList.mSkelBase->getAllAnimationStates()->_notifyDirty(); + const Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); + for(std::vector::iterator iter(mObjects.begin());iter != mObjects.end();iter++) + { + 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; } +void Animation::showWeapons(bool showWeapon){} + } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 029c56523..b2d4bc6e7 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -8,10 +8,6 @@ #include "../mwworld/ptr.hpp" -namespace MWMechanics -{ - class CharacterController; -} namespace MWRender { @@ -23,48 +19,67 @@ protected: { private: Animation *mAnimation; + size_t mIndex; public: - AnimationValue(Animation *anim) : mAnimation(anim) + AnimationValue(Animation *anim, size_t layeridx) + : mAnimation(anim), mIndex(layeridx) { } - virtual Ogre::Real getValue() const - { - return mAnimation->mCurrentTime; - } + virtual Ogre::Real getValue() const; + virtual void setValue(Ogre::Real value); + }; - virtual void setValue(Ogre::Real value) - { - mAnimation->mCurrentTime = value; - } + 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 > *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; + + AnimLayer(); }; MWWorld::Ptr mPtr; - MWMechanics::CharacterController *mController; - Ogre::SceneNode* mInsert; - NifOgre::ObjectList mObjectList; - std::map mTextKeys; + Ogre::SceneNode *mInsert; + Ogre::Entity *mSkelBase; + std::vector mObjects; Ogre::Node *mAccumRoot; Ogre::Bone *mNonAccumRoot; + NifOgre::NodeTargetValue *mNonAccumCtrl; Ogre::Vector3 mAccumulate; Ogre::Vector3 mLastPosition; - std::vector mAnimationSources; - - std::vector > *mCurrentControllers; - NifOgre::TextKeyMap *mCurrentKeys; - NifOgre::TextKeyMap::const_iterator mNextKey; - Ogre::Animation *mCurrentAnim; - float mCurrentTime; - float mStopTime; - bool mPlaying; - bool mLooping; + std::vector > mActiveCtrls; float mAnimVelocity; float mAnimSpeedMult; - void calcAnimVelocity(); + static const size_t sMaxLayers = 1; + AnimLayer mLayer[sMaxLayers]; + Ogre::SharedPtr > mAnimationValuePtr[sMaxLayers]; + + static float calcAnimVelocity(const NifOgre::TextKeyMap &keys, + NifOgre::NodeTargetValue *nonaccumctrl, + const Ogre::Vector3 &accum, + const std::string &groupname); /* Updates a skeleton instance so that all bones matching the source skeleton (based on * bone names) are positioned identically. */ @@ -72,34 +87,39 @@ protected: /* Updates the position of the accum root node for the current time, and * 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 * 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 *nonaccumctrl, + const std::string &groupname, const std::string &start, const std::string &stop, + float startpoint); - bool handleEvent(float time, const std::string &evt); + bool doLoop(size_t layeridx); - /* Specifies a list of skeleton names to use as animation sources. */ - void setAnimationSources(const std::vector &names); + bool handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key); - /* Specifies a single skeleton name to use as an animation source. */ - void setAnimationSource(const std::string &name) - { - std::vector 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 setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue); + + void updateActiveControllers(); + public: Animation(const MWWorld::Ptr &ptr); 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); @@ -112,10 +132,40 @@ public: 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); - void play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop); - virtual Ogre::Vector3 runAnimation(float timepassed); + /** 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); + + /* Returns if there's an animation playing on the given layer. */ + bool isPlaying(size_t layeridx) const + { return mLayer[layeridx].mPlaying; } Ogre::Node *getNode(const std::string &name); }; diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/camera.cpp similarity index 54% rename from apps/openmw/mwrender/player.cpp rename to apps/openmw/mwrender/camera.cpp index 439264f3a..b4fd6a543 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -1,7 +1,8 @@ -#include "player.hpp" +#include "camera.hpp" #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -14,10 +15,9 @@ namespace MWRender { - Player::Player (Ogre::Camera *camera, Ogre::SceneNode* node) + Camera::Camera (Ogre::Camera *camera) : mCamera(camera), - mPlayerNode(node), - mCameraNode(mPlayerNode->createChildSceneNode()), + mCameraNode(NULL), mFirstPersonView(true), mPreviewMode(false), mFreeLook(true), @@ -28,51 +28,16 @@ namespace MWRender { mVanity.enabled = false; mVanity.allowed = true; - mVanity.forced = false; - - mCameraNode->attachObject(mCamera); - mCameraNode->setPosition(0.f, 0.f, mHeight); mPreviewCam.yaw = 0.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) { setYaw(getYaw() + rot.z); @@ -81,33 +46,37 @@ namespace MWRender setYaw(rot.z); setPitch(rot.x); } - Ogre::Quaternion xr( - Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), - Ogre::Vector3::UNIT_X - ); - Ogre::Quaternion zr( - Ogre::Radian(getYaw()), - Ogre::Vector3::NEGATIVE_UNIT_Z - ); + + Ogre::Quaternion xr(Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X); if (!mVanity.enabled && !mPreviewMode) { - mPlayerNode->setOrientation(zr); mCameraNode->setOrientation(xr); } else { + Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z); 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 dir = mCamera->getRealDirection(); @@ -116,29 +85,27 @@ namespace MWRender MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up); } - void Player::update(float duration) + void Camera::update(float duration) { updateListener(); // only show the crosshair in game mode and in first person mode. - MWBase::Environment::get().getWindowManager ()->showCrosshair - (!MWBase::Environment::get().getWindowManager ()->isGuiMode () && (mFirstPersonView && !mVanity.enabled && !mPreviewMode)); + MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); + wm->showCrosshair(!wm->isGuiMode() && (mFirstPersonView && !mVanity.enabled && !mPreviewMode)); - if (mFirstPersonView && !mVanity.enabled) { - return; - } - if (mVanity.enabled) { + if(mVanity.enabled) + { Ogre::Vector3 rot(0.f, 0.f, 0.f); rot.z = Ogre::Degree(3.f * duration).valueRadians(); rotateCamera(rot, true); } } - void Player::toggleViewMode() + void Camera::toggleViewMode() { mFirstPersonView = !mFirstPersonView; - mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ? - NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson); + mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : + NpcAnimation::VM_Normal); if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); 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); - } mVanity.allowed = allow; } - bool Player::toggleVanityMode(bool enable, bool force) + bool Camera::toggleVanityMode(bool enable) { - if ((mVanity.forced && !force) || - (!mVanity.allowed && (force || enable))) - { + if(!mVanity.allowed && enable) return false; - } else if (mVanity.enabled == enable) { - return true; - } - mVanity.enabled = enable; - mVanity.forced = force && enable; - mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ? - NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson); + if(mVanity.enabled == enable) + return true; + mVanity.enabled = enable; + + mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : + NpcAnimation::VM_Normal); float offset = mPreviewCam.offset; Ogre::Vector3 rot(0.f, 0.f, 0.f); @@ -185,20 +148,22 @@ namespace MWRender setLowHeight(!mFirstPersonView); } rot.z = getYaw(); + mCamera->setPosition(0.f, 0.f, offset); rotateCamera(rot, false); return true; } - void Player::togglePreviewMode(bool enable) + void Camera::togglePreviewMode(bool enable) { - if (mPreviewMode == enable) { + if(mPreviewMode == enable) return; - } + mPreviewMode = enable; - mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ? - NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson); + mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : + NpcAnimation::VM_Normal); + float offset = mCamera->getPosition().z; if (mPreviewMode) { mMainCam.offset = offset; @@ -211,19 +176,19 @@ namespace MWRender setLowHeight(!mFirstPersonView); } + mCamera->setPosition(0.f, 0.f, offset); 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 mMainCam.yaw; } - void Player::setYaw(float angle) + void Camera::setYaw(float angle) { if (angle > Ogre::Math::PI) { angle -= Ogre::Math::TWO_PI; @@ -237,7 +202,7 @@ namespace MWRender } } - float Player::getPitch() + float Camera::getPitch() { if (mVanity.enabled || mPreviewMode) { return mPreviewCam.pitch; @@ -245,18 +210,18 @@ namespace MWRender 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; - if (mVanity.forced || mPreviewMode) { - limit /= 2; - } - if (angle > limit) { + if(mPreviewMode) + limit /= 2; + + if(angle > limit) angle = limit; - } else if (angle < -limit) { + else if(angle < -limit) angle = -limit; - } + if (mVanity.enabled || mPreviewMode) { mPreviewCam.pitch = angle; } 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; - } + Ogre::Vector3 v(0.f, 0.f, dist); if (adjust) { v += mCamera->getPosition(); @@ -293,7 +258,7 @@ namespace MWRender } } - void Player::setCameraDistance() + void Camera::setCameraDistance() { if (mDistanceAdjusted) { if (mVanity.enabled || mPreviewMode) { @@ -305,65 +270,54 @@ namespace MWRender 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->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ? - NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson); + mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : + NpcAnimation::VM_Normal); } - void Player::setHeight(float height) + void Camera::setHeight(float height) { mHeight = height; 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); camera = mCamera->getRealPosition(); - player = mPlayerNode->getPosition(); + player = mTrackingPtr.getRefData().getBaseNode()->getPosition(); 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; yaw = mMainCam.yaw; } - void Player::compensateYaw(float diff) - { - 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) + void Camera::togglePlayerLooking(bool enable) { mFreeLook = enable; } - void Player::setLowHeight(bool low) + void Camera::setLowHeight(bool low) { if (low) { 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; } diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/camera.hpp similarity index 71% rename from apps/openmw/mwrender/player.hpp rename to apps/openmw/mwrender/camera.hpp index 9de41823d..7bbdd303d 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -1,8 +1,10 @@ -#ifndef GAME_MWRENDER_PLAYER_H -#define GAME_MWRENDER_PLAYER_H +#ifndef GAME_MWRENDER_CAMERA_H +#define GAME_MWRENDER_CAMERA_H #include +#include "../mwworld/ptr.hpp" + namespace Ogre { class Vector3; @@ -10,24 +12,20 @@ namespace Ogre class SceneNode; } -namespace MWWorld -{ - class Ptr; -} - namespace MWRender { class NpcAnimation; - /// \brief Player character rendering and camera control - class Player + + /// \brief Camera control + class Camera { struct CamData { float pitch, yaw, offset; }; - Ogre::Camera *mCamera; + MWWorld::Ptr mTrackingPtr; - Ogre::SceneNode *mPlayerNode; + Ogre::Camera *mCamera; Ogre::SceneNode *mCameraNode; NpcAnimation *mAnimation; @@ -37,7 +35,7 @@ namespace MWRender bool mFreeLook; struct { - bool enabled, allowed, forced; + bool enabled, allowed; } mVanity; float mHeight, mCameraDistance; @@ -51,15 +49,11 @@ namespace MWRender void setLowHeight(bool low = true); public: + Camera(Ogre::Camera *camera); + ~Camera(); - Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); - ~Player(); - - /// Set where the player is looking at. Uses Morrowind (euler) angles + /// Set where the camera is looking at. Uses Morrowind (euler) angles /// \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); float getYaw(); @@ -68,22 +62,21 @@ namespace MWRender float getPitch(); void setPitch(float angle); - void compensateYaw(float diff); - - std::string getHandle() const; + const std::string &getHandle() const; /// Attach camera to object - /// \note there is no protection from attaching the same camera to - /// several different objects void attachTo(const MWWorld::Ptr &); void toggleViewMode(); - bool toggleVanityMode(bool enable, bool force = false); + bool toggleVanityMode(bool enable); void allowVanityMode(bool allow); void togglePreviewMode(bool enable); + bool isFirstPerson() const + { return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); } + void update(float duration); /// Set camera distance for current mode. Don't work on 1st person view. @@ -96,8 +89,6 @@ namespace MWRender void setCameraDistance(); void setAnimation(NpcAnimation *anim); - NpcAnimation *getAnimation() const - { return mAnimation; } void setHeight(float height); float getHeight(); diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 41770d489..4d9680b2c 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -12,6 +12,7 @@ #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" #include "renderconst.hpp" #include "npcanimation.hpp" @@ -134,6 +135,46 @@ namespace MWRender 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 *ref = iter->get(); + + 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->runAnimation(0.0f); @@ -155,7 +196,10 @@ namespace MWRender if (!mSelectionBuffer) 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 () { - mAnimation->play("idle", "start", "stop", false); + mAnimation->play("idle", "start", "stop", 0.0f, 0); updateCamera(); } diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index 08cbd5108..d8f43f71b 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -53,6 +53,7 @@ namespace MWRender MWWorld::Ptr mCharacter; MWRender::NpcAnimation* mAnimation; + std::string mCurrentAnimGroup; std::string mName; diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index c714a2372..97c28c0ae 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -1,10 +1,5 @@ #include "creatureanimation.hpp" -#include -#include -#include -#include - #include "renderconst.hpp" #include "../mwbase/world.hpp" @@ -26,31 +21,11 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) { 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 names; if((ref->mBase->mFlags&ESM::Creature::Biped)) - names.push_back("meshes\\base_anim.nif"); - names.push_back(model); - setAnimationSources(names); + addObjectList(mPtr.getRefData().getBaseNode(), "meshes\\base_anim.nif", true); + + addObjectList(mPtr.getRefData().getBaseNode(), model, false); + setRenderProperties(mObjects.back().mObjectList, RV_Actors, RQG_Main, RQG_Alpha); } } diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 54f288bff..b318c2d56 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -20,7 +20,6 @@ #include "../mwworld/ptr.hpp" -#include "player.hpp" #include "renderconst.hpp" using namespace Ogre; diff --git a/apps/openmw/mwrender/debugging.hpp b/apps/openmw/mwrender/debugging.hpp index 6a4eef58f..4a574017c 100644 --- a/apps/openmw/mwrender/debugging.hpp +++ b/apps/openmw/mwrender/debugging.hpp @@ -39,8 +39,6 @@ namespace MWWorld namespace MWRender { - class Player; - class Debugging { OEngine::Physic::PhysicEngine* mEngine; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index ecc0869cc..a92092020 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -11,6 +11,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "renderconst.hpp" @@ -29,7 +30,7 @@ const NpcAnimation::PartInfo NpcAnimation::sPartList[NpcAnimation::sPartListSize { ESM::PRT_LHand, "Left Hand" }, { ESM::PRT_RWrist, "Right Wrist" }, { ESM::PRT_LWrist, "Left Wrist" }, - { ESM::PRT_Shield, "Shield" }, + { ESM::PRT_Shield, "Shield Bone" }, { ESM::PRT_RForearm, "Right Forearm" }, { ESM::PRT_LForearm, "Left Forearm" }, { 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_RPauldron, "Right Clavicle" }, { ESM::PRT_LPauldron, "Left Clavicle" }, - { ESM::PRT_Weapon, "Weapon" }, + { ESM::PRT_Weapon, "Weapon Bone" }, { ESM::PRT_Tail, "Tail" } }; @@ -73,7 +74,10 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor mGloveL(inv.end()), mGloveR(inv.end()), mSkirtIter(inv.end()), - mViewMode(viewMode) + mWeapon(inv.end()), + mShield(inv.end()), + mViewMode(viewMode), + mShowWeapons(false) { mNpc = mPtr.get()->mBase; @@ -96,39 +100,24 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - createObjectList(node, smodel); - for(size_t i = 0;i < mObjectList.mEntities.size();i++) - { - Ogre::Entity *base = mObjectList.mEntities[i]; - - base->getUserObjectBindings().setUserAny(Ogre::Any(-1)); - if (mVisibilityFlags != 0) - base->setVisibilityFlags(mVisibilityFlags); - - 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]; - - part->getUserObjectBindings().setUserAny(Ogre::Any(-1)); - if(mVisibilityFlags != 0) - part->setVisibilityFlags(mVisibilityFlags); - part->setRenderQueueGroup(RQG_Alpha); - } - - std::vector 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"); + addObjectList(node, smodel, true); + if(mBodyPrefix.find("argonian") != std::string::npos) + addObjectList(node, "meshes\\argonian_swimkna.nif", true); + else if(!mNpc->isMale() && !isBeast) + addObjectList(node, "meshes\\base_anim_female.nif", true); if(mNpc->mModel.length() > 0) - skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel)); - setAnimationSources(skelnames); + addObjectList(node, "meshes\\"+mNpc->mModel, true); + if(mViewMode == VM_FirstPerson) + { + /* A bit counter-intuitive, but unlike third-person anims, it seems + * 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); + } forceUpdate(); } @@ -138,28 +127,31 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) assert(viewMode != VM_HeadOnly); mViewMode = viewMode; - /* FIXME: Enable this once first-person animations work. */ -#if 0 + Ogre::SceneNode *node = mInsert->getParentSceneNode(); + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.get().find(mNpc->mRace); - bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; - std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - std::vector 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"); + clearExtraSources(); + if(mBodyPrefix.find("argonian") != std::string::npos) + addObjectList(node, "meshes\\argonian_swimkna.nif", true); + else if(!mNpc->isMale() && !isBeast) + addObjectList(node, "meshes\\base_anim_female.nif", true); if(mNpc->mModel.length() > 0) - skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel)); + addObjectList(node, "meshes\\"+mNpc->mModel, true); if(mViewMode == VM_FirstPerson) { - smodel = (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif"); - skelnames.push_back(smodel); + /* A bit counter-intuitive, but unlike third-person anims, it seems + * 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); -#endif + MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); for(size_t i = 0;i < sPartListSize;i++) removeIndividualPart(i); @@ -169,84 +161,33 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) void NpcAnimation::updateParts(bool forceupdate) { static const struct { - int numRemoveParts; // Max: 1 - ESM::PartReferenceType removeParts[1]; - - MWWorld::ContainerStoreIterator NpcAnimation::*part; - int slot; - - int numReserveParts; // Max: 12 - ESM::PartReferenceType reserveParts[12]; + MWWorld::ContainerStoreIterator NpcAnimation::*mPart; + int mSlot; + int mBasePriority; } slotlist[] = { - { 0, { }, - &NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe, - 12, { ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg, - ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, - ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_RPauldron, ESM::PRT_LPauldron } - }, - - { 0, { }, - &NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt, - 3, { ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg } - }, - - { 1, { ESM::PRT_Hair }, - &NpcAnimation::mHelmet, MWWorld::InventoryStore::Slot_Helmet, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mCuirass, MWWorld::InventoryStore::Slot_Cuirass, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mGreaves, MWWorld::InventoryStore::Slot_Greaves, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mBoots, MWWorld::InventoryStore::Slot_Boots, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mShirt, MWWorld::InventoryStore::Slot_Shirt, - 0, { } - }, - - { 0, { }, - &NpcAnimation::mPants, MWWorld::InventoryStore::Slot_Pants, - 0, { } - }, + // FIXME: Priority is based on the number of reserved slots. There should be a better way. + { &NpcAnimation::mRobe, MWWorld::InventoryStore::Slot_Robe, 12 }, + { &NpcAnimation::mSkirtIter, MWWorld::InventoryStore::Slot_Skirt, 3 }, + { &NpcAnimation::mHelmet, MWWorld::InventoryStore::Slot_Helmet, 0 }, + { &NpcAnimation::mCuirass, MWWorld::InventoryStore::Slot_Cuirass, 0 }, + { &NpcAnimation::mGreaves, MWWorld::InventoryStore::Slot_Greaves, 0 }, + { &NpcAnimation::mPauldronL, MWWorld::InventoryStore::Slot_LeftPauldron, 0 }, + { &NpcAnimation::mPauldronR, MWWorld::InventoryStore::Slot_RightPauldron, 0 }, + { &NpcAnimation::mBoots, MWWorld::InventoryStore::Slot_Boots, 0 }, + { &NpcAnimation::mGloveL, MWWorld::InventoryStore::Slot_LeftGauntlet, 0 }, + { &NpcAnimation::mGloveR, MWWorld::InventoryStore::Slot_RightGauntlet, 0 }, + { &NpcAnimation::mShirt, MWWorld::InventoryStore::Slot_Shirt, 0 }, + { &NpcAnimation::mPants, MWWorld::InventoryStore::Slot_Pants, 0 }, + { &NpcAnimation::mShield, MWWorld::InventoryStore::Slot_CarriedLeft, 0 }, + { &NpcAnimation::mWeapon, MWWorld::InventoryStore::Slot_CarriedRight, 0 } }; static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); for(size_t i = 0;!forceupdate && i < slotlistsize;i++) { - MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].slot); - if(this->*slotlist[i].part != iter) + MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].mSlot); + if(this->*slotlist[i].mPart != iter) { forceupdate = true; break; @@ -259,40 +200,55 @@ void NpcAnimation::updateParts(bool forceupdate) if(mViewMode == VM_FirstPerson) { 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; } 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; - removePartGroup(slotlist[i].slot); + this->*slotlist[i].mPart = iter; + removePartGroup(slotlist[i].mSlot); - if(this->*slotlist[i].part == inv.end()) + if(this->*slotlist[i].mPart == inv.end()) continue; - for(int rem = 0;rem < slotlist[i].numRemoveParts;rem++) - removeIndividualPart(slotlist[i].removeParts[rem]); + if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Helmet) + removeIndividualPart(ESM::PRT_Hair); int prio = 1; - MWWorld::ContainerStoreIterator &store = this->*slotlist[i].part; + MWWorld::ContainerStoreIterator &store = this->*slotlist[i].mPart; 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()->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()) { - prio = ((slotlist[i].numReserveParts+1)<<1) + 1; + prio = ((slotlist[i].mBasePriority+1)<<1) + 1; const ESM::Armor *armor = store->get()->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++) - reserveIndividualPart(slotlist[i].reserveParts[res], slotlist[i].slot, prio); + if(slotlist[i].mSlot == MWWorld::InventoryStore::Slot_Robe) + { + 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) @@ -305,29 +261,7 @@ void NpcAnimation::updateParts(bool forceupdate) if(mViewMode == VM_HeadOnly) return; - std::map bodypartMap; - 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(); + showWeapons(mShowWeapons); const int Flag_Female = 0x01; const int Flag_FirstPerson = 0x02; @@ -344,21 +278,43 @@ void NpcAnimation::updateParts(bool forceupdate) std::pair thisCombination = std::make_pair(race, flags); if (sRaceMapping.find(thisCombination) == sRaceMapping.end()) { - sRaceMapping[thisCombination].resize(ESM::PRT_Count); - for (int i=0; i bodypartMap; + if(bodypartMap.size() == 0) + { + 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; + } + sRaceMapping[thisCombination].resize(ESM::PRT_Count, NULL); + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::Store &partStore = store.get(); - - for (MWWorld::Store::iterator it = partStore.begin(); it != partStore.end(); ++it) + for(MWWorld::Store::iterator it = partStore.begin(); it != partStore.end(); ++it) { const ESM::BodyPart& bodypart = *it; if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) continue; if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) - { continue; - } + if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) continue; if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace)) @@ -382,31 +338,20 @@ void NpcAnimation::updateParts(bool forceupdate) if (mPartPriorities[part] < 1 && bodypart) 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 objects = NifOgre::Loader::createObjects(mObjectList.mSkelBase, bonename, - mInsert, model); - for(size_t i = 0;i < objects.mEntities.size();i++) - { - objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); - if(mVisibilityFlags != 0) - objects.mEntities[i]->setVisibilityFlags(mVisibilityFlags); + NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); + setRenderProperties(objects, mVisibilityFlags, RQG_Main, RQG_Alpha); - 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.mEntities.size();i++) + objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); for(size_t i = 0;i < objects.mParticles.size();i++) - { 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) { Ogre::AnimationStateSet *aset = objects.mSkelBase->getAllAnimationStates(); @@ -422,6 +367,7 @@ NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, in while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); } + return objects; } @@ -435,14 +381,16 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) mTimeToChange -= 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++) { Ogre::Entity *ent = mObjectParts[i].mSkelBase; if(!ent) continue; - updateSkeletonInstance(skelsrc, ent->getSkeleton()); + updateSkeletonInstance(baseinst, ent->getSkeleton()); ent->getAllAnimationStates()->_notifyDirty(); } + return ret; } @@ -523,4 +471,23 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectorgetRootSceneNode(); - mRootNode = rt; + mRootNode = mRendering.getScene()->getRootSceneNode(); + mRootNode->createChildSceneNode("player"); mObjects.setRootNode(mRootNode); mActors.setRootNode(mRootNode); - Ogre::SceneNode *playerNode = mRootNode->createChildSceneNode ("player"); - mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); + mCamera = new MWRender::Camera(mRendering.getCamera()); mShadows = new Shadows(&mRendering); @@ -181,7 +184,8 @@ RenderingManager::~RenderingManager () mRendering.getWindow()->removeListener(this); mRendering.removeWindowEventListener(this); - delete mPlayer; + delete mPlayerAnimation; + delete mCamera; delete mSkyManager; delete mDebugging; delete mShadows; @@ -262,37 +266,20 @@ void RenderingManager::scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3 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; - bool isPlayer = isActive && ptr.getRefData().getHandle() == "player"; - bool force = true; + Ogre::Vector3 rot(ptr.getRefData().getPosition().rot); - if (isPlayer) - force = mPlayer->rotate(rot, adjust); + if(ptr.getRefData().getHandle() == mCamera->getHandle() && + !mCamera->isVanityOrPreviewModeEnabled()) + mCamera->rotateCamera(rot, false); - MWWorld::Class::get(ptr).adjustRotation(ptr, rot.x, rot.y, rot.z); - if (!isPlayer && isActive) - { - if(adjust) - { - 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.z), Ogre::Vector3::UNIT_Z); + if(!MWWorld::Class::get(ptr).isActor()) + newo = Ogre::Quaternion(Ogre::Radian(-rot.x), Ogre::Vector3::UNIT_X) * + Ogre::Quaternion(Ogre::Radian(-rot.y), Ogre::Vector3::UNIT_Y) * newo; - Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(-rot.x), Ogre::Vector3::UNIT_X) * - 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; + ptr.getRefData().getBaseNode()->setOrientation(newo); } void @@ -315,27 +302,29 @@ void RenderingManager::update (float duration, bool paused) { 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 - MWWorld::RefData &data = - MWBase::Environment::get() - .getWorld() - ->getPlayer() - .getPlayer() - .getRefData(); + MWWorld::RefData &data = player.getRefData(); float *_playerPos = data.getPosition().pos; Ogre::Vector3 playerPos(_playerPos[0], _playerPos[1], _playerPos[2]); Ogre::Vector3 orig, dest; - mPlayer->setCameraDistance(); - if (!mPlayer->getPosition(orig, dest)) { - orig.z += mPlayer->getHeight() * mRootNode->getScale().z; + mCamera->setCameraDistance(); + if(!mCamera->getPosition(orig, dest)) + { + orig.z += mCamera->getHeight() * mRootNode->getScale().z; btVector3 btOrig(orig.x, orig.y, orig.z); btVector3 btDest(dest.x, dest.y, dest.z); - std::pair test = - mPhysicsEngine->rayTest(btOrig, btDest); + std::pair test = mPhysicsEngine->rayTest(btOrig, btDest); 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(); - applyFog(world->isUnderwater (world->getPlayer().getPlayer().getCell(), cam)); + applyFog(world->isUnderwater(player.getCell(), cam)); if(paused) - { return; - } - mPlayer->update(duration); + mCamera->update(duration); mActors.update (duration); mObjects.update (duration); @@ -367,18 +354,11 @@ void RenderingManager::update (float duration, bool paused) mSkyManager->setGlare(mOcclusionQuery->getSunVisibility()); Ogre::SceneNode *node = data.getBaseNode(); - //Ogre::Quaternion orient = - //node->convertLocalToWorldOrientation(node->_getDerivedOrientation()); - Ogre::Quaternion orient = -node->_getDerivedOrientation(); + Ogre::Quaternion orient = node->_getDerivedOrientation(); mLocalMap->updatePlayer(playerPos, orient); - mWater->updateUnderwater( - world->isUnderwater( - world->getPlayer().getPlayer().getCell(), - cam) - ); + mWater->updateUnderwater(world->isUnderwater(player.getCell(), cam)); mWater->update(duration, playerPos); } @@ -597,8 +577,15 @@ void RenderingManager::setSunColour(const Ogre::ColourValue& colour) void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) { - mRendering.getScene()->setAmbientLight(colour); - mTerrainManager->setAmbient(colour); + mAmbientColor = 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) @@ -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) { - MWRender::NpcAnimation *anim = - new MWRender::NpcAnimation( - ptr, ptr.getRefData ().getBaseNode (), - MWWorld::Class::get(ptr).getInventoryStore(ptr), RV_Actors - ); - mPlayer->setAnimation(anim); - mWater->removeEmitter (ptr); - mWater->addEmitter (ptr); + if(!mPlayerAnimation) + { + mPlayerAnimation = new NpcAnimation(ptr, ptr.getRefData().getBaseNode(), + MWWorld::Class::get(ptr).getInventoryStore(ptr), + RV_Actors); + } + else + { + // 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 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.z += mPlayer->getHeight(); - mPlayer->getSightAngles(pitch, yaw); + eyepos = mCamera->getPosition(); + eyepos.z += mCamera->getHeight(); + mCamera->getSightAngles(pitch, yaw); } -bool RenderingManager::vanityRotateCamera(float* rot) +bool RenderingManager::vanityRotateCamera(const float *rot) { - if(!mPlayer->isVanityOrPreviewModeEnabled()) + if(!mCamera->isVanityOrPreviewModeEnabled()) return false; Ogre::Vector3 vRot(rot); - mPlayer->rotateCamera(vRot, true); + mCamera->rotateCamera(vRot, true); return true; } @@ -920,7 +917,7 @@ Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr) { Animation *anim = mActors.getAnimation(ptr); if(!anim && ptr.getRefData().getHandle() == "player") - anim = mPlayer->getAnimation(); + anim = mPlayerAnimation; return anim; } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index cece7a95f..6a35bfb6d 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -17,7 +17,7 @@ #include "objects.hpp" #include "actors.hpp" -#include "player.hpp" +#include "camera.hpp" #include "occlusionquery.hpp" namespace Ogre @@ -50,49 +50,44 @@ namespace MWRender class VideoPlayer; class Animation; -class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener { - - private: - - +class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener +{ +private: virtual MWRender::Objects& getObjects(); virtual MWRender::Actors& getActors(); - public: +public: 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(); - void togglePOV() { - mPlayer->toggleViewMode(); + void togglePOV() + { mCamera->toggleViewMode(); } + + void togglePreviewMode(bool enable) + { mCamera->togglePreviewMode(enable); } + + bool toggleVanityMode(bool enable) + { return mCamera->toggleVanityMode(enable); } + + void allowVanityMode(bool allow) + { mCamera->allowVanityMode(allow); } + + void togglePlayerLooking(bool enable) + { mCamera->togglePlayerLooking(enable); } + + void changeVanityModeScale(float factor) + { + if(mCamera->isVanityOrPreviewModeEnabled()) + mCamera->setCameraDistance(-factor/120.f*10, true, true); } - void togglePreviewMode(bool enable) { - mPlayer->togglePreviewMode(enable); - } + bool vanityRotateCamera(const float *rot); - bool toggleVanityMode(bool enable, bool force) { - return mPlayer->toggleVanityMode(enable, force); - } + void getCameraData(Ogre::Vector3 &eyepos, float &pitch, float &yaw); - void allowVanityMode(bool allow) { - mPlayer->allowVanityMode(allow); - } - - void togglePlayerLooking(bool enable) { - mPlayer->togglePlayerLooking(enable); - } - - void changeVanityModeScale(float factor) { - if (mPlayer->isVanityOrPreviewModeEnabled()) - mPlayer->setCameraDistance(-factor/120.f*10, true, true); - } - - bool vanityRotateCamera(float* rot); - - void getPlayerData(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); SkyManager* getSkyManager(); @@ -121,11 +116,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void moveObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& position); void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale); - /// Rotates object accordingly to its type - /// \param rot euler angles in radians - /// \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); + /// Updates an object's rotation + void rotateObject (const MWWorld::Ptr& ptr); void setWaterHeight(const float height); void toggleWater(); @@ -207,12 +199,11 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void stopVideo(); void frameStarted(float dt); - protected: - virtual void windowResized(Ogre::RenderWindow* rw); +protected: + virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw); - private: - +private: sh::Factory* mFactory; void setAmbientMode(); @@ -241,6 +232,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList MWRender::Objects mObjects; MWRender::Actors mActors; + MWRender::NpcAnimation *mPlayerAnimation; + // 0 normal, 1 more bright, 2 max int mAmbientMode; @@ -255,7 +248,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList OEngine::Physic::PhysicEngine* mPhysicsEngine; - MWRender::Player *mPlayer; + MWRender::Camera *mCamera; MWRender::Debugging *mDebugging; diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 7f3abce70..516b48dde 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -205,7 +205,7 @@ unsigned int Moon::getPhaseInt() const return 0; } -SkyManager::SkyManager (SceneNode* root, Camera* pCamera) +SkyManager::SkyManager(Ogre::SceneNode *root, Ogre::Camera *pCamera) : mHour(0.0f) , mDay(0) , mMonth(0) diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index dd74254be..b9c055acd 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -54,9 +54,10 @@ namespace MWRender TerrainMaterial::Profile::~Profile() { + if (mMaterial) + sh::Factory::getInstance().destroyMaterialInstance(mMaterial->getName()); } - Ogre::MaterialPtr TerrainMaterial::Profile::generate(const Ogre::Terrain* terrain) { const Ogre::String& matName = terrain->getMaterialName(); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 7e9827062..d02fc9289 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -318,5 +318,18 @@ op 0x20001fb: DropSoulGem, explicit reference op 0x20001fc: OnDeath op 0x20001fd: IsWerewolf 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 diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 489f6bd3d..a08563877 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -282,10 +282,8 @@ namespace MWScript MWBase::World *world = MWBase::Environment::get().getWorld(); - if (world->toggleVanityMode(sActivate, true)) { - context.report( - (sActivate) ? "Vanity Mode -> On" : "Vanity Mode -> Off" - ); + if (world->toggleVanityMode(sActivate)) { + context.report(sActivate ? "Vanity Mode -> On" : "Vanity Mode -> Off"); sActivate = !sActivate; } else { context.report("Vanity Mode -> No"); @@ -557,6 +555,16 @@ namespace MWScript } }; + template + class OpFall : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -598,6 +606,8 @@ namespace MWScript const int opcodeSetDelete = 0x20001e5; const int opcodeSetDeleteExplicit = 0x20001e6; const int opcodeGetSquareRoot = 0x20001e7; + const int opcodeFall = 0x200020a; + const int opcodeFallExplicit = 0x200020b; const int opcodePlayBink = 0x20001f7; @@ -639,6 +649,7 @@ namespace MWScript extensions.registerFunction ("getcurrenttime", 'f', "", opcodeGetCurrentTime); extensions.registerInstruction ("setdelete", "l", opcodeSetDelete, opcodeSetDeleteExplicit); extensions.registerFunction ("getsquareroot", 'f', "f", opcodeGetSquareRoot); + extensions.registerInstruction ("fall", "", opcodeFall, opcodeFallExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -685,6 +696,9 @@ namespace MWScript interpreter.installSegment5 (opcodeSetDelete, new OpSetDelete); interpreter.installSegment5 (opcodeSetDeleteExplicit, new OpSetDelete); interpreter.installSegment5 (opcodeGetSquareRoot, new OpGetSquareRoot); + interpreter.installSegment5 (opcodeFall, new OpFall); + interpreter.installSegment5 (opcodeFallExplicit, new OpFall); + } } } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 1d321fbbb..04e89edc6 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1025,16 +1025,14 @@ namespace MWScript } }; + template class OpOnDeath : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { - MWScript::InterpreterContext& context - = static_cast (runtime.getContext()); - - MWWorld::Ptr ptr = context.getReference(); + MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = MWWorld::Class::get (ptr).getCreatureStats (ptr).hasDied(); @@ -1146,9 +1144,8 @@ namespace MWScript const int opcodeRaiseRankExplicit = 0x20001e9; const int opcodeLowerRank = 0x20001ea; const int opcodeLowerRankExplicit = 0x20001eb; - const int opcodeOnDeath = 0x20001fc; - + const int opcodeOnDeathExplicit = 0x2000205; const int opcodeIsWerewolf = 0x20001fd; const int opcodeIsWerewolfExplicit = 0x20001fe; @@ -1266,7 +1263,7 @@ namespace MWScript extensions.registerInstruction ("raiserank", "", opcodeRaiseRank, opcodeRaiseRankExplicit); extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit); - extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath); + extensions.registerFunction ("ondeath", 'l', "", opcodeOnDeath, opcodeOnDeathExplicit); extensions.registerFunction ("iswerewolf", 'l', "", opcodeIsWerewolf, opcodeIsWerewolfExplicit); } @@ -1384,7 +1381,8 @@ namespace MWScript interpreter.installSegment5 (opcodeLowerRank, new OpLowerRank); interpreter.installSegment5 (opcodeLowerRankExplicit, new OpLowerRank); - interpreter.installSegment5 (opcodeOnDeath, new OpOnDeath); + interpreter.installSegment5 (opcodeOnDeath, new OpOnDeath); + interpreter.installSegment5 (opcodeOnDeathExplicit, new OpOnDeath); interpreter.installSegment5 (opcodeIsWerewolf, new OpIsWerewolf); interpreter.installSegment5 (opcodeIsWerewolfExplicit, new OpIsWerewolf); diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 49688efb5..327047cdc 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -84,21 +84,27 @@ namespace MWScript Interpreter::Type_Float angle = runtime[0].mFloat; runtime.pop(); - float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); - float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); - float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees(); + 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(); + + 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") { - MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az); + MWBase::Environment::get().getWorld()->localRotateObject(ptr,angle-lx,ay,az); } 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") { - MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); + MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay,angle-lz); } else throw std::runtime_error ("invalid ration axis: " + axis); @@ -148,15 +154,15 @@ namespace MWScript 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") { - 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") { - 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 throw std::runtime_error ("invalid ration axis: " + axis); @@ -542,6 +548,165 @@ namespace MWScript } }; + template + 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 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 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 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 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 opcodeSetScaleExplicit = 0x2000165; const int opcodeSetAngle = 0x2000166; @@ -568,6 +733,16 @@ namespace MWScript const int opcodePlaceAtMeExplicit = 0x200019e; const int opcodeModScale = 0x20001e3; 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) { @@ -585,6 +760,11 @@ namespace MWScript extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc); extensions.registerInstruction("placeatme","clfl",opcodePlaceAtMe,opcodePlaceAtMeExplicit); 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) @@ -614,6 +794,16 @@ namespace MWScript interpreter.installSegment5(opcodePlaceAtMeExplicit,new OpPlaceAtMe); interpreter.installSegment5(opcodeModScale,new OpModScale); interpreter.installSegment5(opcodeModScaleExplicit,new OpModScale); + interpreter.installSegment5(opcodeRotate,new OpRotate); + interpreter.installSegment5(opcodeRotateExplicit,new OpRotate); + interpreter.installSegment5(opcodeRotateWorld,new OpRotateWorld); + interpreter.installSegment5(opcodeRotateWorldExplicit,new OpRotateWorld); + interpreter.installSegment5(opcodeSetAtStart,new OpSetAtStart); + interpreter.installSegment5(opcodeSetAtStartExplicit,new OpSetAtStart); + interpreter.installSegment5(opcodeMove,new OpMove); + interpreter.installSegment5(opcodeMoveExplicit,new OpMove); + interpreter.installSegment5(opcodeMoveWorld,new OpMoveWorld); + interpreter.installSegment5(opcodeMoveWorldExplicit,new OpMoveWorld); } } } diff --git a/apps/openmw/mwworld/actiondoor.cpp b/apps/openmw/mwworld/actiondoor.cpp new file mode 100644 index 000000000..6e3441e22 --- /dev/null +++ b/apps/openmw/mwworld/actiondoor.cpp @@ -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()); + } +} diff --git a/apps/openmw/mwworld/actiondoor.hpp b/apps/openmw/mwworld/actiondoor.hpp new file mode 100644 index 000000000..2dc5ad8c1 --- /dev/null +++ b/apps/openmw/mwworld/actiondoor.hpp @@ -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 diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 451f0c5c1..03a55c7f0 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -132,7 +132,7 @@ namespace MWWorld 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"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index b901950e3..e203fedc3 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -231,7 +231,7 @@ namespace MWWorld ///< @return the enchantment ID if the object is enchanted, otherwise an 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 virtual void adjustScale(const MWWorld::Ptr& ptr,float& scale) const; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 05026a98b..5cf534239 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -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) - ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id); - - if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) + try { - const ESM::ItemLevList* levItem = ref.getPtr().get()->mBase; - const std::vector& items = levItem->mList; + ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id); - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - int playerLevel = MWWorld::Class::get(player).getCreatureStats(player).getLevel(); - - failChance += levItem->mChanceNone; - - if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each) + if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) { - for (int i=0; i()->mBase; + const std::vector& items = levItem->mList; - float random = static_cast (std::rand()) / RAND_MAX; - if (random >= failChance/100.f) - { - std::vector candidates; - int highestLevel = 0; - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + int playerLevel = MWWorld::Class::get(player).getCreatureStats(player).getLevel(); + + failChance += levItem->mChanceNone; + + if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each) { - if (it->mLevel > highestLevel) - highestLevel = it->mLevel; + for (int i=0; i highest = std::make_pair(-1, ""); - for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + float random = static_cast (std::rand()) / RAND_MAX; + if (random >= failChance/100.f) { - if (playerLevel >= it->mLevel - && (levItem->mFlags & ESM::ItemLevList::AllLevels || it->mLevel == highestLevel)) + std::vector candidates; + int highestLevel = 0; + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { - candidates.push_back(it->mId); - if (it->mLevel >= highest.first) - highest = std::make_pair(it->mLevel, it->mId); + if (it->mLevel > highestLevel) + highestLevel = it->mLevel; } + std::pair highest = std::make_pair(-1, ""); + for (std::vector::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; - std::string item = candidates[std::rand()%candidates.size()]; - addInitialItem(item, owner, count, failChance, false); + } + else + { + ref.getPtr().getRefData().setCount (count); + ref.getPtr().getCellRef().mOwner = owner; + addImp (ref.getPtr()); } } - else + catch (std::logic_error& e) { - ref.getPtr().getRefData().setCount (count); - ref.getPtr().getCellRef().mOwner = owner; - addImp (ref.getPtr()); + // Vanilla doesn't fail on nonexistent items in levelled lists + std::cerr << "Warning: ignoring nonexistent item '" << id << "'" << std::endl; + return; } } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 19ee2e517..ce32b79e5 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -131,7 +131,6 @@ namespace MWWorld return position; } - static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, 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? return position + (Ogre::Quaternion(Ogre::Radian( -refpos.rot[2]), Ogre::Vector3::UNIT_Z)* Ogre::Quaternion(Ogre::Radian( -refpos.rot[1]), Ogre::Vector3::UNIT_Y)* - Ogre::Quaternion(Ogre::Radian( -refpos.rot[0]), Ogre::Vector3::UNIT_X)) * + Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) * movement; } @@ -161,7 +160,7 @@ namespace MWWorld { velocity = (Ogre::Quaternion(Ogre::Radian( -refpos.rot[2]), Ogre::Vector3::UNIT_Z)* Ogre::Quaternion(Ogre::Radian( -refpos.rot[1]), Ogre::Vector3::UNIT_Y)* - Ogre::Quaternion(Ogre::Radian( -refpos.rot[0]), Ogre::Vector3::UNIT_X)) * + Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) * movement / time; } else @@ -261,14 +260,13 @@ namespace MWWorld std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world, float queryDistance) { btVector3 dir(0, 1, 0); - dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); - dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw); + dir = dir.rotate(btVector3(1, 0, 0), mCameraData.pitch); + dir = dir.rotate(btVector3(0, 0, 1), mCameraData.yaw); dir.setX(-dir.x()); - btVector3 origin( - mPlayerData.eyepos.x, - mPlayerData.eyepos.y, - mPlayerData.eyepos.z); + btVector3 origin(mCameraData.eyepos.x, + mCameraData.eyepos.y, + mCameraData.eyepos.z); origin += dir * 5; btVector3 dest = origin + dir * queryDistance; @@ -281,14 +279,13 @@ namespace MWWorld std::vector < std::pair > PhysicsSystem::getFacedHandles (float queryDistance) { btVector3 dir(0, 1, 0); - dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); - dir = dir.rotate(btVector3(0, 0, 1), mPlayerData.yaw); + dir = dir.rotate(btVector3(1, 0, 0), mCameraData.pitch); + dir = dir.rotate(btVector3(0, 0, 1), mCameraData.yaw); dir.setX(-dir.x()); - btVector3 origin( - mPlayerData.eyepos.x, - mPlayerData.eyepos.y, - mPlayerData.eyepos.z); + btVector3 origin(mCameraData.eyepos.x, + mCameraData.eyepos.y, + mCameraData.eyepos.z); origin += dir * 5; btVector3 dest = origin + dir * queryDistance; @@ -390,6 +387,11 @@ namespace MWWorld } } + std::vector 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) { return MovementSolver::move(ptr, movement, time, gravity, mEngine); @@ -548,10 +550,10 @@ namespace MWWorld 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; - mPlayerData.pitch = pitch; - mPlayerData.yaw = yaw; + mCameraData.eyepos = eyepos; + mCameraData.pitch = pitch; + mCameraData.yaw = yaw; } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 4eec9367c..3322c38a6 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -51,6 +51,7 @@ namespace MWWorld bool toggleCollisionMode(); Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, bool gravity); + std::vector getCollisions(const MWWorld::Ptr &ptr); ///< get handles this object collides with Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr); std::pair getFacedHandle (MWWorld::World& world, float queryDistance); @@ -76,13 +77,13 @@ namespace MWWorld 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: struct { Ogre::Vector3 eyepos; float pitch, yaw; - } mPlayerData; + } mCameraData; OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 4be287810..c1a3ae785 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -19,6 +19,7 @@ namespace MWWorld mEnabled = refData.mEnabled; mCount = refData.mCount; mPosition = refData.mPosition; + mLocalRotation = refData.mLocalRotation; mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0; } @@ -34,7 +35,11 @@ namespace MWWorld RefData::RefData (const ESM::CellRef& cellRef) : mBaseNode(0), mHasLocals (false), mEnabled (true), mCount (1), mPosition (cellRef.mPos), mCustomData (0) - {} + { + mLocalRotation.rot[0]=0; + mLocalRotation.rot[1]=0; + mLocalRotation.rot[2]=0; + } RefData::RefData (const RefData& refData) : mBaseNode(0), mCustomData (0) @@ -76,10 +81,13 @@ namespace MWWorld {} } - std::string RefData::getHandle() + const std::string &RefData::getHandle() { - if (!mBaseNode) - return ""; + if(!mBaseNode) + { + static const std::string empty; + return empty; + } return mBaseNode->getName(); } @@ -141,6 +149,11 @@ namespace MWWorld return mPosition; } + LocalRotation& RefData::getLocalRotation() + { + return mLocalRotation; + } + void RefData::setCustomData (CustomData *data) { delete mCustomData; diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 3a6e0fc9f..642f5412c 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -18,6 +18,10 @@ namespace ESM namespace MWWorld { + struct LocalRotation{ + float rot[3]; + }; + class CustomData; class RefData @@ -34,6 +38,8 @@ namespace MWWorld ESM::Position mPosition; + LocalRotation mLocalRotation; + CustomData *mCustomData; void copy (const RefData& refData); @@ -54,7 +60,7 @@ namespace MWWorld RefData& operator= (const RefData& refData); /// Return OGRE handle (may be empty). - std::string getHandle(); + const std::string &getHandle(); /// Return OGRE base node (can be a null pointer). Ogre::SceneNode* getBaseNode(); @@ -78,6 +84,8 @@ namespace MWWorld ESM::Position& getPosition(); + LocalRotation& getLocalRotation(); + void setCustomData (CustomData *data); ///< Set custom data (potentially replacing old custom data). The ownership of \æ data is /// transferred to this. diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index b9b0aa19c..da808a9a8 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -49,7 +49,12 @@ namespace { rendering.addObject(ptr); 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); class_.adjustPosition(ptr); } diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 005601cd1..ebc7ef03f 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1,4 +1,5 @@ #include "store.hpp" +#include "esmstore.hpp" namespace MWWorld { @@ -15,8 +16,39 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) ESM::Cell *cell = new ESM::Cell; cell->mName = id; - // The cell itself takes care of some of the hairy details - cell->load(esm, *mEsmStore); + //First part of cell loading + 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 &cStore = const_cast&>(mEsmStore->get()); + ESM::Cell *cellAlt = const_cast(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) { @@ -62,4 +94,4 @@ void Store::load(ESM::ESMReader &esm, const std::string &id) delete cell; } -} \ No newline at end of file +} diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 2bd61c2b0..7949cbe00 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -872,7 +872,28 @@ namespace MWWorld } 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::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 { diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 3e8525312..c940398fa 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -289,8 +289,7 @@ void WeatherManager::update(float duration) if (exterior) { - std::string regionstr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion; - Misc::StringUtils::toLower(regionstr); + std::string regionstr = Misc::StringUtils::lowerCase(MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion); 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) { + // make sure this region exists + MWBase::Environment::get().getWorld()->getStore().get().find(region); + std::string weather; if (id==0) weather = "clear"; @@ -645,5 +647,9 @@ void WeatherManager::changeWeather(const std::string& region, const unsigned int else 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); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 424f6f508..a903369ee 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -16,7 +18,6 @@ #include "../mwmechanics/movement.hpp" #include "../mwrender/sky.hpp" -#include "../mwrender/player.hpp" #include "../mwclass/door.hpp" @@ -163,7 +164,8 @@ namespace MWWorld ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mCells (mStore, mEsm), - mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride),mFallback(fallbackMap) + mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride), + mFallback(fallbackMap), mNewGame(newGame) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -214,7 +216,7 @@ namespace MWWorld // global variables mGlobalVariables = new Globals (mStore); - if (newGame) + if (mNewGame) { // set new game mark mGlobalVariables->setInt ("chargenstate", 1); @@ -810,17 +812,89 @@ namespace MWWorld 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[1] = rot.y; objRot[2] = rot.z; + } - if (ptr.getRefData().getBaseNode() != 0) { - mPhysics->rotateObject(ptr); - } + if(Class::get(ptr).isActor()) + { + /* 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)); 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); } + void World::processDoors(float duration) + { + std::map::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 collisions = mPhysics->getCollisions(it->first); + for (std::vector::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() { return mPhysics->toggleCollisionMode();; @@ -1022,8 +1152,8 @@ namespace MWWorld float pitch, yaw; Ogre::Vector3 eyepos; - mRendering->getPlayerData(eyepos, pitch, yaw); - mPhysics->updatePlayerData(eyepos, pitch, yaw); + mRendering->getCameraData(eyepos, pitch, yaw); + mPhysics->updateCameraData(eyepos, pitch, yaw); performUpdateSceneQueries (); @@ -1367,15 +1497,17 @@ namespace MWWorld return mRendering->vanityRotateCamera(rot); } - void World::setupPlayer(bool newGame) + void World::setupPlayer() { - const ESM::NPC* player = mStore.get().find ("player"); - mPlayer = new MWWorld::Player (player, *this); - mRendering->attachCameraTo(mPlayer->getPlayer()); - if (newGame) + const ESM::NPC *player = mStore.get().find("player"); + mPlayer = new MWWorld::Player(player, *this); + + Ptr ptr = mPlayer->getPlayer(); + mRendering->setupPlayer(ptr); + if (mNewGame) { - MWWorld::Class::get(mPlayer->getPlayer()).getContainerStore(mPlayer->getPlayer()).fill(player->mInventory, "", mStore); - MWWorld::Class::get(mPlayer->getPlayer()).getInventoryStore(mPlayer->getPlayer()).autoEquip (mPlayer->getPlayer()); + MWWorld::Class::get(ptr).getContainerStore(ptr).fill(player->mInventory, "", mStore); + MWWorld::Class::get(ptr).getInventoryStore(ptr).autoEquip(ptr); } } @@ -1383,6 +1515,8 @@ namespace MWWorld { mRendering->renderPlayer(mPlayer->getPlayer()); mPhysics->addActor(mPlayer->getPlayer()); + if (mNewGame) + toggleCollisionMode(); } void World::setupExternalRendering (MWRender::ExternalRendering& rendering) @@ -1398,7 +1532,7 @@ namespace MWWorld Ogre::Vector3 playerPos(refdata.getPosition().pos); 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; if((currentCell->mCell->mData.mFlags&ESM::Cell::NoSleep)) return 1; @@ -1425,4 +1559,27 @@ namespace MWWorld { 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; + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 99e7cc79d..f61f67062 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -85,6 +85,11 @@ namespace MWWorld float mFaced2Distance; int mNumFacing; + bool mNewGame; + + std::map mDoorStates; + ///< only holds doors that are currently moving. 0 means closing, 1 opening + unsigned long lastTick; Ogre::Timer mTimer; @@ -110,6 +115,9 @@ namespace MWWorld void addContainerScripts(const Ptr& reference, Ptr::CellStore* cell); void PCDropped (const Ptr& item); + virtual void processDoors(float duration); + ///< Run physics simulation and modify \a world accordingly. + public: World (OEngine::Render::OgreRenderer& renderer, @@ -254,6 +262,8 @@ namespace MWWorld /// \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 localRotateObject (const Ptr& ptr, float x, float y, float z); + 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. @@ -345,8 +355,8 @@ namespace MWWorld mRendering->togglePreviewMode(enable); } - virtual bool toggleVanityMode(bool enable, bool force) { - return mRendering->toggleVanityMode(enable, force); + virtual bool toggleVanityMode(bool enable) { + return mRendering->toggleVanityMode(enable); } virtual void allowVanityMode(bool allow) { @@ -363,9 +373,14 @@ namespace MWWorld virtual bool vanityRotateCamera(float * rot); - virtual void setupPlayer(bool newGame); + virtual void setupPlayer(); 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 int canRest(); diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index c97ca4562..7d3df1590 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -313,21 +313,26 @@ public: void destroyInstance( Archive* arch) { delete arch; } }; -class DirArchiveFactory : public FileSystemArchiveFactory +class DirArchiveFactory : public ArchiveFactory { public: - const String& getType() const - { - static String name = "Dir"; - return name; - } + const String& getType() const + { + static String name = "Dir"; + return name; + } - Archive *createInstance( const String& name ) - { - return new DirArchive(name); - } + Archive *createInstance( const String& 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; } }; diff --git a/components/compiler/fileparser.cpp b/components/compiler/fileparser.cpp index 98be2d3d1..185af4a51 100644 --- a/components/compiler/fileparser.cpp +++ b/components/compiler/fileparser.cpp @@ -71,6 +71,19 @@ namespace Compiler 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); } diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 77e4d3691..779f303f3 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -8,9 +8,6 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -#include -#include - 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); +} - // preload moved references - while (esm.isNextSub("MVRF")) { - CellRef ref; - MovedCellRef cMRef; - getNextMVRF(esm, cMRef); - - MWWorld::Store &cStore = const_cast&>(store.get()); - ESM::Cell *cellAlt = const_cast(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; - } - +void Cell::postLoad(ESMReader &esm) +{ // Save position of the cell references and move on mContextList.push_back(esm.getContext()); esm.skipRecord(); diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index d7f64817f..eda8a5418 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -96,7 +96,8 @@ struct Cell CellRefTracker mLeasedRefs; 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 // passing ESMStore, bit it does not know about this parameter, so we do it this way. diff --git a/components/esm/loadweap.hpp b/components/esm/loadweap.hpp index e482d3b10..384af125d 100644 --- a/components/esm/loadweap.hpp +++ b/components/esm/loadweap.hpp @@ -48,7 +48,7 @@ struct Weapon short mType; short mHealth; 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 int mFlags; }; // 32 bytes diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index aa6a9ef4f..011e0e445 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -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 #endif diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 3b41e96a7..cb7c2feb0 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -207,8 +207,8 @@ struct RecordFactoryEntry { static const RecordFactoryEntry recordFactories [] = { { "NiNode", &construct , RC_NiNode }, - { "AvoidNode", &construct , RC_NiNode }, - { "NiBSParticleNode", &construct , RC_NiNode }, + { "AvoidNode", &construct , RC_AvoidNode }, + { "NiBSParticleNode", &construct , RC_NiBSParticleNode }, { "NiBSAnimationNode", &construct , RC_NiBSAnimationNode }, { "NiBillboardNode", &construct , RC_NiNode }, { "NiTriShape", &construct , RC_NiTriShape }, @@ -235,6 +235,7 @@ static const RecordFactoryEntry recordFactories [] = { { "NiMaterialColorController", &construct , RC_NiMaterialColorController }, { "NiBSPArrayController", &construct , RC_NiBSPArrayController }, { "NiParticleSystemController", &construct , RC_NiParticleSystemController }, + { "NiFlipController", &construct , RC_NiFlipController }, { "NiAmbientLight", &construct , RC_NiLight }, { "NiDirectionalLight", &construct , RC_NiLight }, { "NiTextureEffect", &construct , RC_NiTextureEffect }, diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 9c345baab..917bc8add 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -128,13 +128,17 @@ struct NiNode : Node NodeList children; NodeList effects; - /* Known NiNode flags: - 0x01 hidden - 0x02 use mesh for collision - 0x04 use bounding box for collision (?) - 0x08 unknown, but common - 0x20, 0x40, 0x80 unknown - */ + enum Flags { + Flag_Hidden = 0x0001, + Flag_MeshCollision = 0x0002, + Flag_BBoxCollision = 0x0004 + }; + enum BSAnimFlags { + AnimFlag_AutoPlay = 0x0020 + }; + enum BSParticleFlags { + ParticleFlag_AutoPlay = 0x0020 + }; void read(NIFStream *nif) { diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 361af3f64..87e342dca 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -36,9 +36,11 @@ enum RecordType { RC_MISSING = 0, RC_NiNode, + RC_AvoidNode, RC_NiTriShape, RC_NiRotatingParticles, RC_NiAutoNormalParticles, + RC_NiBSParticleNode, RC_NiCamera, RC_NiTexturingProperty, RC_NiMaterialProperty, @@ -59,6 +61,7 @@ enum RecordType RC_NiMaterialColorController, RC_NiBSPArrayController, RC_NiParticleSystemController, + RC_NiFlipController, RC_NiBSAnimationNode, RC_NiLight, RC_NiTextureEffect, diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index c5bafea12..2ecde7f60 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -163,6 +163,7 @@ typedef RecordPtrT NiAutoNormalParticlesDataPtr; typedef RecordListT NodeList; typedef RecordListT PropertyList; +typedef RecordListT NiSourceTextureList; } // Namespace #endif diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 6bd43f6e3..a1e2cd8d1 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -185,6 +185,10 @@ void ManualBulletShapeLoader::handleNode(btTriangleMesh* mesh, const Nif::Node * else isCollisionNode = isCollisionNode && (node->recType != Nif::RC_RootCollisionNode); + // Don't collide with AvoidNode shapes + if(node->recType == Nif::RC_AvoidNode) + flags |= 0x800; + // Marker objects /// \todo don't do this in the editor std::string nodename = node->name; @@ -255,9 +259,9 @@ void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif:: assert(shape != NULL); // Interpret flags - bool hidden = (flags & 0x01) != 0; // Not displayed - bool collide = (flags & 0x02) != 0; // Use mesh for collision - bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision + bool hidden = (flags&Nif::NiNode::Flag_Hidden) != 0; + bool collide = (flags&Nif::NiNode::Flag_MeshCollision) != 0; + bool bbcollide = (flags&Nif::NiNode::Flag_BBoxCollision) != 0; // If the object was marked "NCO" earlier, it shouldn't collide with // anything. So don't do anything. diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index 6d940deed..ca92f62d4 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -109,13 +109,26 @@ NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape) { - Ogre::SkeletonPtr skel; const Nif::NiTriShapeData *data = shape->data.getPtr(); const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr()); std::vector srcVerts = data->vertices; std::vector srcNorms = data->normals; Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC; 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) { vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; @@ -125,10 +138,6 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape // explicitly attached later. 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 // better to transform the bones into bind position, but there doesn't seem to // 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; for(size_t b = 0;b < bones.length();b++) { - Ogre::Bone *bone = skel->getBone(bones[b]->name); Ogre::Matrix4 mat; mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale), Ogre::Quaternion(data->bones[b].trafo.rotation)); - mat = bone->_getFullTransform() * mat; + mat = bones[b]->getWorldTransform() * mat; const std::vector &weights = data->bones[b].weights; 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 if(skin != NULL) { + Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(mName); + const Nif::NiSkinData *data = skin->data.getPtr(); const Nif::NodeList &bones = skin->bones; for(size_t i = 0;i < bones.length();i++) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index a26f43131..d36199f86 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -44,15 +44,6 @@ #include "material.hpp" #include "mesh.hpp" -namespace std -{ - -// TODO: Do something useful -ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&) -{ return o; } - -} - namespace NifOgre { @@ -74,11 +65,7 @@ public: , mStopTime(ctrl->timeStop) { if(mDeltaInput) - { mDeltaCount = mPhase; - while(mDeltaCount < mStartTime) - mDeltaCount += (mStopTime-mStartTime); - } } virtual Ogre::Real calculate(Ogre::Real value) @@ -86,6 +73,9 @@ public: if(mDeltaInput) { mDeltaCount += value*mFrequency; + if(mDeltaCount < mStartTime) + mDeltaCount = mStopTime - std::fmod(mStartTime - mDeltaCount, + mStopTime - mStartTime); mDeltaCount = std::fmod(mDeltaCount - mStartTime, mStopTime - mStartTime) + mStartTime; return mDeltaCount; @@ -104,7 +94,7 @@ public: private: std::vector mData; - virtual bool calculate(Ogre::Real time) + bool calculate(Ogre::Real time) const { if(mData.size() == 0) return true; @@ -144,10 +134,19 @@ public: , 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 { // Should not be called - return 1.0f; + return 0.0f; } virtual void setValue(Ogre::Real time) @@ -170,6 +169,60 @@ public: Nif::Vector3KeyList mTranslations; 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: Value(Ogre::Node *target, const Nif::NiKeyframeData *data) : NodeTargetValue(target) @@ -178,6 +231,27 @@ public: , 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 { // Should not be called @@ -187,68 +261,11 @@ public: virtual void setValue(Ogre::Real time) { if(mRotations.mKeys.size() > 0) - { - 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; - } - } - } + mNode->setOrientation(interpKey(mRotations.mKeys, time)); if(mTranslations.mKeys.size() > 0) - { - 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; - } - } - } + mNode->setPosition(interpKey(mTranslations.mKeys, time)); if(mScales.mKeys.size() > 0) - { - 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; - } - } - } + mNode->setScale(Ogre::Vector3(interpKey(mScales.mKeys, time))); } }; @@ -289,7 +306,7 @@ public: } public: - Value(const Ogre::MaterialPtr &material, Nif::NiUVData *data) + Value(const Ogre::MaterialPtr &material, const Nif::NiUVData *data) : mMaterial(material) , mUTrans(data->mKeyList[0]) , mVTrans(data->mKeyList[1]) @@ -329,11 +346,70 @@ public: typedef DefaultFunction Function; }; +class ParticleSystemController +{ +public: + class Value : public Ogre::ControllerValue + { + 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; } + + void setValue(Ogre::Real value) + { + mParticleSys->setEmitting(value >= mEmitStart && value < mEmitStop); + } + }; + + typedef DefaultFunction Function; +}; + +class GeomMorpherController +{ +public: + class Value : public Ogre::ControllerValue + { + private: + Ogre::SubEntity *mSubEntity; + std::vector 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; +}; -/** Manual resource loader for NIF objects (meshes, particle systems, etc). - * This is the main class responsible for translating the internal NIF - * structures into something Ogre can use. +/** 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 { @@ -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(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(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(srcval, dstval, func)); + } + else if(ctrl->recType == Nif::RC_NiGeomMorpherController) + { + const Nif::NiGeomMorpherController *geom = static_cast(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(srcval, dstval, func)); + } + ctrl = ctrl->next; + } + } + + static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl) { Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif"); - emitter->setParticleVelocity(partctrl->velocity-partctrl->velocityRandom, - partctrl->velocity+partctrl->velocityRandom); + emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f, + partctrl->velocity + partctrl->velocityRandom*0.5f); emitter->setEmissionRate(partctrl->emitRate); - emitter->setTimeToLive(partctrl->lifetime-partctrl->lifetimeRandom, - partctrl->lifetime+partctrl->lifetimeRandom); + emitter->setTimeToLive(partctrl->lifetime - partctrl->lifetimeRandom*0.5f, + partctrl->lifetime + partctrl->lifetimeRandom*0.5f); emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x)); emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y)); 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, - Ogre::SceneManager *sceneMgr, Ogre::Entity *entitybase, - const Nif::Node *partnode) + static void createParticleSystem(const std::string &name, const std::string &group, + Ogre::SceneManager *sceneMgr, ObjectList &objectlist, + const Nif::Node *partnode, int flags, int partflags) { const Nif::NiAutoNormalParticlesData *particledata = NULL; if(partnode->recType == Nif::RC_NiAutoNormalParticles) @@ -426,73 +567,139 @@ class NIFObjectLoader else if(partnode->recType == Nif::RC_NiRotatingParticles) particledata = static_cast(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(); - try { - std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex); - if(partnode->name.length() > 0) - fullname += "@type="+partnode->name; - Misc::StringUtils::toLower(fullname); - const Nif::NiTexturingProperty *texprop = NULL; - const Nif::NiMaterialProperty *matprop = NULL; - const Nif::NiAlphaProperty *alphaprop = NULL; - const Nif::NiVertexColorProperty *vertprop = NULL; - const Nif::NiZBufferProperty *zprop = NULL; - const Nif::NiSpecularProperty *specprop = NULL; - const Nif::NiWireframeProperty *wireprop = NULL; - bool needTangents = false; + const Nif::NiTexturingProperty *texprop = NULL; + const Nif::NiMaterialProperty *matprop = NULL; + const Nif::NiAlphaProperty *alphaprop = NULL; + const Nif::NiVertexColorProperty *vertprop = NULL; + const Nif::NiZBufferProperty *zprop = NULL; + const Nif::NiSpecularProperty *specprop = NULL; + const Nif::NiWireframeProperty *wireprop = NULL; + bool needTangents = false; - partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop); - partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group, - texprop, matprop, alphaprop, - vertprop, zprop, specprop, - wireprop, needTangents)); + partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop); + partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group, + texprop, matprop, alphaprop, + vertprop, zprop, specprop, + wireprop, needTangents)); - partsys->setDefaultDimensions(particledata->particleRadius*2.0f, - particledata->particleRadius*2.0f); - partsys->setCullIndividually(false); - partsys->setParticleQuota(particledata->numParticles); - // TODO: There is probably a field or flag to specify this, as some - // particle effects have it and some don't. - partsys->setKeepParticlesInLocalSpace(true); + partsys->setDefaultDimensions(particledata->particleRadius*2.0f, + particledata->particleRadius*2.0f); + partsys->setCullIndividually(false); + partsys->setParticleQuota(particledata->numParticles); + // TODO: There is probably a field or flag to specify this, as some + // particle effects have it and some don't. + partsys->setKeepParticlesInLocalSpace(true); - Nif::ControllerPtr ctrl = partnode->controller; - while(!ctrl.empty()) + Nif::ControllerPtr ctrl = partnode->controller; + while(!ctrl.empty()) + { + if(ctrl->recType == Nif::RC_NiParticleSystemController) { - if(ctrl->recType == Nif::RC_NiParticleSystemController) + const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); + + createParticleEmitterAffectors(partsys, partctrl); + if(!partctrl->emitter.empty() && !partsys->isAttached()) { - const Nif::NiParticleSystemController *partctrl = static_cast(ctrl.getPtr()); - - createParticleEmitterAffectors(partsys, partctrl); - 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); - } + int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); + Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid); + objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys); } - ctrl = ctrl->next; - } - if(!partsys->isAttached()) + 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(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); + } + + + 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); - Ogre::Bone *trgtbone = entitybase->getSkeleton()->getBone(trgtid); - entitybase->attachObjectToBone(trgtbone->getName(), partsys); + const Nif::NiVisController *vis = static_cast(ctrl.getPtr()); + + 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(srcval, dstval, func)); + } + else if(ctrl->recType == Nif::RC_NiKeyframeController) + { + const Nif::NiKeyframeController *key = static_cast(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(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: "<destroyParticleSystem(partsys); - partsys = NULL; - }; - return partsys; } static void createObjects(const std::string &name, const std::string &group, 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) if(node->recType == Nif::RC_RootCollisionNode) @@ -503,12 +710,24 @@ class NIFObjectLoader if (node->name.find("marker") != std::string::npos) 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; while(!e.empty()) { - if(e->recType == Nif::RC_NiStringExtraData) + if(e->recType == Nif::RC_NiTextKeyExtraData) + { + const Nif::NiTextKeyExtraData *tk = static_cast(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(e.getPtr()); // String markers may contain important information @@ -520,9 +739,13 @@ class NIFObjectLoader flags |= 0x80000000; } } + e = e->extra; } + if(!node->controller.empty()) + createNodeControllers(name, node->controller, objectlist, animflags); + if(node->recType == Nif::RC_NiCamera) { int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex); @@ -530,94 +753,15 @@ class NIFObjectLoader 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(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(srcval, dstval, func)); - } - else if(ctrl->recType == Nif::RC_NiKeyframeController) - { - const Nif::NiKeyframeController *key = static_cast(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(srcval, dstval, func)); - } - } - ctrl = ctrl->next; - } - if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000)) { - const Nif::NiTriShape *shape = static_cast(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&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(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(srcval, dstval, func)); - } - ctrl = ctrl->next; - } + createEntity(name, group, sceneMgr, objectlist, node, flags, animflags); } if((node->recType == Nif::RC_NiAutoNormalParticles || node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000)) { - Ogre::ParticleSystem *partsys = createParticleSystem(name, group, sceneMgr, objectlist.mSkelBase, node); - if(partsys != NULL) - { - partsys->setVisible(!(flags&0x01)); - objectlist.mParticles.push_back(partsys); - } + createParticleSystem(name, group, sceneMgr, objectlist, node, flags, partflags); } const Nif::NiNode *ninode = dynamic_cast(node); @@ -627,7 +771,7 @@ class NIFObjectLoader for(size_t i = 0;i < children.length();i++) { 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 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; 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; } diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index fa5182aea..40577e451 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -50,6 +50,8 @@ struct ObjectList { // bones in the mSkelBase which are NiCamera nodes. std::vector mCameras; + std::map mTextKeys; + std::vector > mControllers; ObjectList() : mSkelBase(0) @@ -69,7 +71,7 @@ public: std::string name, const std::string &group="General"); - static ObjectList createObjectBase(Ogre::SceneManager *sceneMgr, + static ObjectList createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group="General"); }; @@ -85,6 +87,10 @@ public: 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) { mNode = target; } Ogre::Node *getNode() const @@ -94,14 +100,4 @@ typedef Ogre::SharedPtr > 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 diff --git a/components/nifogre/skeleton.cpp b/components/nifogre/skeleton.cpp index e97e91ef0..3aec920fc 100644 --- a/components/nifogre/skeleton.cpp +++ b/components/nifogre/skeleton.cpp @@ -11,142 +11,7 @@ namespace NifOgre { -void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector &ctrls, const std::vector &targets, float startTime, float stopTime) -{ - 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::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 &ctrls, Ogre::Bone *parent) +void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent) { Ogre::Bone *bone; 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 */ node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */ 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_NiAutoNormalParticles || node->recType == Nif::RC_NiRotatingParticles @@ -173,28 +40,16 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Nif::ControllerPtr ctrl = node->controller; while(!ctrl.empty()) { - if(ctrl->recType == Nif::RC_NiKeyframeController) - ctrls.push_back(static_cast(ctrl.getPtr())); - else if(!(ctrl->recType == Nif::RC_NiParticleSystemController || - ctrl->recType == Nif::RC_NiVisController || - ctrl->recType == Nif::RC_NiUVController - )) + if(!(ctrl->recType == Nif::RC_NiParticleSystemController || + ctrl->recType == Nif::RC_NiVisController || + ctrl->recType == Nif::RC_NiUVController || + ctrl->recType == Nif::RC_NiKeyframeController || + ctrl->recType == Nif::RC_NiGeomMorpherController + )) warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName()); ctrl = ctrl->next; } - Nif::ExtraPtr e = node->extra; - while(!e.empty()) - { - if(e->recType == Nif::RC_NiTextKeyExtraData && !animroot) - { - const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - textkeys = extractTextKeys(tk); - animroot = bone; - } - e = e->extra; - } - const Nif::NiNode *ninode = dynamic_cast(node); 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++) { 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())); const Nif::Node *node = static_cast(nif->getRoot(0)); - std::vector ctrls; - Ogre::Bone *animroot = NULL; - TextKeyMap textkeys; try { - buildBones(skel, node, animroot, textkeys, ctrls); + buildBones(skel, node); } catch(std::exception &e) { std::cerr<< "Exception while loading "<getName() < 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(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 * of nodes and Ogre only supports 256 bones. We will skip a skeleton if: - * There are no bones used for skinning, there are no controllers on non- - * NiTriShape nodes, there are no nodes named "AttachLight", and the tree - * consists of NiNode, NiTriShape, and RootCollisionNode types only. + * There are no bones used for skinning, there are no controllers, there + * are no nodes named "AttachLight", and the tree consists of NiNode, + * NiTriShape, and RootCollisionNode types only. */ if(!node->boneTrafo) { - if(node->recType == Nif::RC_NiTriShape) - return Ogre::SkeletonPtr(); 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) { const Nif::NiNode *ninode = static_cast(node); diff --git a/components/nifogre/skeleton.hpp b/components/nifogre/skeleton.hpp index c69c2a12f..38cfe14e3 100644 --- a/components/nifogre/skeleton.hpp +++ b/components/nifogre/skeleton.hpp @@ -36,10 +36,7 @@ class NIFSkeletonLoader : public Ogre::ManualResourceLoader abort(); } - static void buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector &ctrls, const std::vector &targets, float startTime, float stopTime); - - static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk); - void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector &ctrls, Ogre::Bone *parent=NULL); + void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent=NULL); // Lookup to retrieve an Ogre bone handle for a given Nif record index std::map mNifToOgreHandleMap; diff --git a/files/mygui/openmw_font.xml b/files/mygui/openmw_font.xml index 0d4091fed..0cb1486de 100644 --- a/files/mygui/openmw_font.xml +++ b/files/mygui/openmw_font.xml @@ -9,6 +9,7 @@ + diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index eaeae7dc3..dbb42a645 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -509,6 +509,43 @@ namespace Physic } } + class ContactTestResultCallback : public btCollisionWorld::ContactResultCallback + { + public: + std::vector 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(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(col0); + if (body) + mResult.push_back(body->mName); + return 0.f; + } +#endif + }; + + std::vector PhysicEngine::getCollisions(const std::string& name) + { + RigidBody* body = getRigidBody(name); + ContactTestResultCallback callback; + dynamicsWorld->contactTest(body, callback); + return callback.mResult; + } + void PhysicEngine::stepSimulation(double deltaT) { // This seems to be needed for character controller objects diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 6ce4edba3..6d88fcb55 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -298,6 +298,8 @@ namespace Physic */ std::vector< std::pair > rayTest2(btVector3& from, btVector3& to); + std::vector getCollisions(const std::string& name); + //event list of non player object std::list NPEventList; diff --git a/libs/openengine/ogre/fader.cpp b/libs/openengine/ogre/fader.cpp index 9390d0664..923b0b7e3 100644 --- a/libs/openengine/ogre/fader.cpp +++ b/libs/openengine/ogre/fader.cpp @@ -19,6 +19,7 @@ Fader::Fader(Ogre::SceneManager* sceneMgr) , mTargetAlpha(0.f) , mCurrentAlpha(0.f) , mStartAlpha(0.f) + , mFactor(1.f) { // Create the fading material 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); if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha; } - - applyAlpha(); - + 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() { 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) diff --git a/libs/openengine/ogre/fader.hpp b/libs/openengine/ogre/fader.hpp index bddf5dc91..53124e2f6 100644 --- a/libs/openengine/ogre/fader.hpp +++ b/libs/openengine/ogre/fader.hpp @@ -29,6 +29,8 @@ namespace Render void fadeOut(const float time); void fadeTo(const int percent, const float time); + void setFactor (float factor) { mFactor = factor; } + private: enum FadingMode { @@ -49,6 +51,8 @@ namespace Render float mCurrentAlpha; float mStartAlpha; + float mFactor; + Ogre::SceneManager* mSceneMgr; }; }}