From 7883087586a205d79cfaa1097ce3eb8550d7ab95 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 8 Jul 2013 14:05:53 -0700 Subject: [PATCH 01/12] Store a pointer to the character controller in the map --- apps/openmw/mwmechanics/actors.cpp | 32 ++++++++++++++++++------------ apps/openmw/mwmechanics/actors.hpp | 2 +- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e85aa9b83..082100b27 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -172,16 +172,19 @@ namespace MWMechanics MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); if(!MWWorld::Class::get(ptr).getCreatureStats(ptr).isDead()) - mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle))); + mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim, CharState_Idle))); else - mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Death1))); + mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim, CharState_Death1))); } void Actors::removeActor (const MWWorld::Ptr& ptr) { PtrControllerMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) + { + delete iter->second; mActors.erase(iter); + } } void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) @@ -189,10 +192,10 @@ namespace MWMechanics PtrControllerMap::iterator iter = mActors.find(old); if(iter != mActors.end()) { - CharacterController ctrl = iter->second; + CharacterController *ctrl = iter->second; mActors.erase(iter); - ctrl.updatePtr(ptr); + ctrl->updatePtr(ptr); mActors.insert(std::make_pair(ptr, ctrl)); } } @@ -203,7 +206,10 @@ namespace MWMechanics while(iter != mActors.end()) { if(iter->first.getCell()==cellStore) + { + delete iter->second; mActors.erase(iter++); + } else ++iter; } @@ -222,8 +228,8 @@ namespace MWMechanics { if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) { - if(iter->second.getState() >= CharState_Death1) - iter->second.setState(CharState_Idle); + if(iter->second->getState() >= CharState_Death1) + iter->second->setState(CharState_Idle); updateActor(iter->first, totalDuration); if(iter->first.getTypeName() == typeid(ESM::NPC).name()) @@ -250,10 +256,10 @@ namespace MWMechanics continue; } - if(iter->second.getState() >= CharState_Death1) + if(iter->second->getState() >= CharState_Death1) continue; - iter->second.setState(CharState_Death1); + iter->second->setState(CharState_Death1); ++mDeathCount[MWWorld::Class::get(iter->first).getId(iter->first)]; @@ -270,7 +276,7 @@ namespace MWMechanics for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { Movement movement; - iter->second.update(duration, movement); + iter->second->update(duration, movement); mMovement.push_back(std::make_pair(iter->first, movement)); } MWBase::Environment::get().getWorld()->doPhysics(mMovement, duration); @@ -297,27 +303,27 @@ namespace MWMechanics { PtrControllerMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) - iter->second.forceStateUpdate(); + iter->second->forceStateUpdate(); } void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) { PtrControllerMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) - iter->second.playGroup(groupName, mode, number); + iter->second->playGroup(groupName, mode, number); } void Actors::skipAnimation(const MWWorld::Ptr& ptr) { PtrControllerMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) - iter->second.skipAnim(); + iter->second->skipAnim(); } bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) { PtrControllerMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) - return iter->second.isAnimPlaying(groupName); + return iter->second->isAnimPlaying(groupName); return false; } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 386840e3a..1369d783c 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -25,7 +25,7 @@ namespace MWMechanics { class Actors { - typedef std::map PtrControllerMap; + typedef std::map PtrControllerMap; PtrControllerMap mActors; MWWorld::PtrMovementList mMovement; From 908f010c742a5f7b847257dc1dbfcf7f7c4378cc Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 8 Jul 2013 18:59:51 -0700 Subject: [PATCH 02/12] Reset the NPC object root when switching POV When in first person, the skeleton in the .1st.nif file is used. In particular, these have extra finger bones that are used by the first person models. --- apps/openmw/mwrender/animation.cpp | 8 +++- apps/openmw/mwrender/animation.hpp | 11 +++++ apps/openmw/mwrender/npcanimation.cpp | 58 ++++++++++++++++----------- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 853ffc375..286da2bc1 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -72,8 +72,9 @@ Animation::~Animation() void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly) { - OgreAssert(!mInsert, "Object already has a root!"); - mInsert = node->createChildSceneNode(); + OgreAssert(mAnimSources.size() != 0, "Setting object root while animation sources are set!"); + if(!mInsert) + mInsert = node->createChildSceneNode(); std::string mdlname = Misc::StringUtils::lowerCase(model); std::string::size_type p = mdlname.rfind('\\'); @@ -89,6 +90,9 @@ void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, b Misc::StringUtils::toLower(mdlname); } + mSkelBase = NULL; + destroyObjectList(mInsert->getCreator(), mObjectRoot); + mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) : NifOgre::Loader::createObjectBase(mInsert, mdlname)); if(mObjectRoot.mSkelBase) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 31be0fb2a..9fd4a606c 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -131,7 +131,18 @@ protected: bool handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key); + /* Sets the root model of the object. If 'baseonly' is true, then any meshes or particle + * systems in the model are ignored (useful for NPCs, where only the skeleton is needed for + * the root). + * + * Note that you must make sure all animation sources are cleared before reseting the object + * root. All nodes previously retrieved with getNode will also become invalidated. + */ void setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly); + + /* Adds the keyframe controllers in the specified model as a new animation source. Note that + * the filename portion of the provided model name will be prepended with 'x', and the .nif + * extension will be replaced with .kf. */ void addAnimSource(const std::string &model); static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index b5f2ea031..cc12d1d20 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -98,17 +98,22 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor Misc::StringUtils::toLower(mBodyPrefix); bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; - std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); + std::string smodel = (viewMode != VM_FirstPerson) ? + (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif") : + (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif") ; setObjectRoot(node, smodel, true); - addAnimSource(smodel); - if(mBodyPrefix.find("argonian") != std::string::npos) - addAnimSource("meshes\\argonian_swimkna.nif"); - else if(!mNpc->isMale() && !isBeast) - addAnimSource("meshes\\base_anim_female.nif"); - if(mNpc->mModel.length() > 0) - addAnimSource("meshes\\"+mNpc->mModel); - if(mViewMode == VM_FirstPerson) + if(mViewMode != VM_FirstPerson) + { + addAnimSource(smodel); + if(mBodyPrefix.find("argonian") != std::string::npos) + addAnimSource("meshes\\argonian_swimkna.nif"); + else if(!mNpc->isMale() && !isBeast) + addAnimSource("meshes\\base_anim_female.nif"); + if(mNpc->mModel.length() > 0) + addAnimSource("meshes\\"+mNpc->mModel); + } + else { /* A bit counter-intuitive, but unlike third-person anims, it seems * beast races get both base_anim.1st.nif and base_animkna.1st.nif. @@ -128,20 +133,28 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) assert(viewMode != VM_HeadOnly); mViewMode = viewMode; + clearAnimSources(); + 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"); - clearAnimSources(); - addAnimSource(smodel); - if(mBodyPrefix.find("argonian") != std::string::npos) - addAnimSource("meshes\\argonian_swimkna.nif"); - else if(!mNpc->isMale() && !isBeast) - addAnimSource("meshes\\base_anim_female.nif"); - if(mNpc->mModel.length() > 0) - addAnimSource("meshes\\"+mNpc->mModel); - if(mViewMode == VM_FirstPerson) + bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; + std::string smodel = (viewMode != VM_FirstPerson) ? + (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif") : + (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif") ; + setObjectRoot(mInsert->getParentSceneNode(), smodel, true); + + if(mViewMode != VM_FirstPerson) + { + addAnimSource(smodel); + if(mBodyPrefix.find("argonian") != std::string::npos) + addAnimSource("meshes\\argonian_swimkna.nif"); + else if(!mNpc->isMale() && !isBeast) + addAnimSource("meshes\\base_anim_female.nif"); + if(mNpc->mModel.length() > 0) + addAnimSource("meshes\\"+mNpc->mModel); + } + else { /* A bit counter-intuitive, but unlike third-person anims, it seems * beast races get both base_anim.1st.nif and base_animkna.1st.nif. @@ -207,9 +220,9 @@ void NpcAnimation::updateParts(bool forceupdate) for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) { - MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].mSlot); + MWWorld::ContainerStoreIterator store = inv.getSlot(slotlist[i].mSlot); - this->*slotlist[i].mPart = iter; + this->*slotlist[i].mPart = store; removePartGroup(slotlist[i].mSlot); if(this->*slotlist[i].mPart == inv.end()) @@ -219,7 +232,6 @@ void NpcAnimation::updateParts(bool forceupdate) removeIndividualPart(ESM::PRT_Hair); int prio = 1; - MWWorld::ContainerStoreIterator &store = this->*slotlist[i].mPart; if(store->getTypeName() == typeid(ESM::Clothing).name()) { prio = ((slotlist[i].mBasePriority+1)<<1) + 0; From a5e4faaed277434599607e78daf7958f83767bf7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 8 Jul 2013 22:37:44 -0700 Subject: [PATCH 03/12] Show arms and body parts in first person This isn't yet fully correct. The arms need to rotate up and down with the camera, and the mesh's bounding box is causing them to blink out at certain angles since they don't fit the animation. --- apps/openmw/mwrender/npcanimation.cpp | 38 ++++++++++++++++----------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index cc12d1d20..6f53010ae 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -210,14 +210,6 @@ void NpcAnimation::updateParts(bool forceupdate) if(!forceupdate) return; - /* FIXME: Remove this once we figure out how to show what in first-person */ - if(mViewMode == VM_FirstPerson) - { - for(size_t i = 0;i < slotlistsize;i++) - this->*slotlist[i].mPart = inv.getSlot(slotlist[i].mSlot); - return; - } - for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) { MWWorld::ContainerStoreIterator store = inv.getSlot(slotlist[i].mSlot); @@ -316,7 +308,8 @@ void NpcAnimation::updateParts(bool forceupdate) bodypartMap[ESM::PRT_Tail] = ESM::BodyPart::MP_Tail; } - sRaceMapping[thisCombination].resize(ESM::PRT_Count, NULL); + std::vector &parts = sRaceMapping[thisCombination]; + parts.resize(ESM::PRT_Count, NULL); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::Store &partStore = store.get(); @@ -337,11 +330,27 @@ void NpcAnimation::updateParts(bool forceupdate) && bodypart.mId[bodypart.mId.size()-3] == '1' && bodypart.mId[bodypart.mId.size()-2] == 's' && bodypart.mId[bodypart.mId.size()-1] == 't'; - if (firstPerson != (mViewMode == VM_FirstPerson)) + if(firstPerson != (mViewMode == VM_FirstPerson)) + { + if(mViewMode == VM_FirstPerson && (bodypart.mData.mPart == ESM::BodyPart::MP_Hand || + bodypart.mData.mPart == ESM::BodyPart::MP_Wrist || + bodypart.mData.mPart == ESM::BodyPart::MP_Forearm)) + { + /* Allow 3rd person skins as a fallback for the forearms if 1st person is missing. */ + for(std::map::iterator bIt = bodypartMap.begin();bIt != bodypartMap.end();++bIt) + { + if(bIt->second == bodypart.mData.mPart) + { + if(!parts[bIt->first]) + parts[bIt->first] = &*it; + } + } + } continue; - for (std::map::iterator bIt = bodypartMap.begin(); bIt != bodypartMap.end(); ++bIt ) - if (bIt->second == bodypart.mData.mPart) - sRaceMapping[thisCombination][bIt->first] = &*it; + } + for(std::map::iterator bIt = bodypartMap.begin();bIt != bodypartMap.end();++bIt) + if(bIt->second == bodypart.mData.mPart) + parts[bIt->first] = &*it; } } @@ -484,8 +493,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector Date: Tue, 9 Jul 2013 13:38:18 -0700 Subject: [PATCH 04/12] Use a multimap for the bodypart map --- apps/openmw/mwrender/npcanimation.cpp | 65 ++++++++++++++------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 6f53010ae..d39b339df 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -283,29 +283,30 @@ void NpcAnimation::updateParts(bool forceupdate) std::pair thisCombination = std::make_pair(race, flags); if (sRaceMapping.find(thisCombination) == sRaceMapping.end()) { - static std::map bodypartMap; - if(bodypartMap.size() == 0) + typedef std::multimap BodyPartMapType; + static BodyPartMapType sBodyPartMap; + if(sBodyPartMap.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; + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Neck, ESM::PRT_Neck)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Groin, ESM::PRT_Groin)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_RHand)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_LHand)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_RFoot)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_LFoot)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_RKnee)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_LKnee)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Tail, ESM::PRT_Tail)); } std::vector &parts = sRaceMapping[thisCombination]; @@ -337,20 +338,22 @@ void NpcAnimation::updateParts(bool forceupdate) bodypart.mData.mPart == ESM::BodyPart::MP_Forearm)) { /* Allow 3rd person skins as a fallback for the forearms if 1st person is missing. */ - for(std::map::iterator bIt = bodypartMap.begin();bIt != bodypartMap.end();++bIt) + BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); + while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) { - if(bIt->second == bodypart.mData.mPart) - { - if(!parts[bIt->first]) - parts[bIt->first] = &*it; - } + if(!parts[bIt->second]) + parts[bIt->second] = &*it; + bIt++; } } continue; } - for(std::map::iterator bIt = bodypartMap.begin();bIt != bodypartMap.end();++bIt) - if(bIt->second == bodypart.mData.mPart) - parts[bIt->first] = &*it; + BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); + while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) + { + parts[bIt->second] = &*it; + bIt++; + } } } From 6de56615aa52b2faa7739c408305a3b76d5a1f6c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 9 Jul 2013 23:50:44 -0700 Subject: [PATCH 05/12] Avoid casting shadows in first-person view --- apps/openmw/mwrender/npcanimation.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d39b339df..ee27f710a 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -371,7 +371,12 @@ NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, in setRenderProperties(objects, mVisibilityFlags, RQG_Main, RQG_Alpha); for(size_t i = 0;i < objects.mEntities.size();i++) - objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); + { + Ogre::Entity *ent = objects.mEntities[i]; + ent->getUserObjectBindings().setUserAny(Ogre::Any(group)); + if(mViewMode == VM_FirstPerson) + ent->setCastShadows(false); + } for(size_t i = 0;i < objects.mParticles.size();i++) objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); From 4df1f198a7c06723e817ee7503db731e84407319 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 12 Jul 2013 22:30:25 -0700 Subject: [PATCH 06/12] Avoid a map lookup for every skin body part when updating --- apps/openmw/mwrender/npcanimation.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index ee27f710a..b905a8bdc 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -357,11 +357,15 @@ void NpcAnimation::updateParts(bool forceupdate) } } - for (int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part) + const std::vector &parts = sRaceMapping[thisCombination]; + for(int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part) { - const ESM::BodyPart* bodypart = sRaceMapping[thisCombination][part]; - if (mPartPriorities[part] < 1 && bodypart) - addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel); + if(mPartPriorities[part] < 1) + { + const ESM::BodyPart* bodypart = parts[part]; + if(bodypart) + addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel); + } } } From 2d7620e774e3dfc24d513ef28b466758cfe1b71e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 13 Jul 2013 12:39:06 -0700 Subject: [PATCH 07/12] Rotate the neck when looking up and down in first-person --- apps/openmw/mwrender/animation.cpp | 1 + apps/openmw/mwrender/animation.hpp | 5 +++++ apps/openmw/mwrender/camera.cpp | 4 ++++ apps/openmw/mwrender/npcanimation.cpp | 8 ++++++++ 4 files changed, 18 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 286da2bc1..2d3b5942d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -45,6 +45,7 @@ void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectL Animation::Animation(const MWWorld::Ptr &ptr) : mPtr(ptr) + , mCamera(NULL) , mInsert(NULL) , mSkelBase(NULL) , mAccumRoot(NULL) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 9fd4a606c..050aa925a 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -11,6 +11,7 @@ namespace MWRender { +class Camera; class Animation { @@ -80,6 +81,7 @@ protected: typedef std::map AnimStateMap; MWWorld::Ptr mPtr; + Camera *mCamera; Ogre::SceneNode *mInsert; Ogre::Entity *mSkelBase; @@ -210,6 +212,9 @@ public: virtual void showWeapons(bool showWeapon); + void setCamera(Camera *cam) + { mCamera = cam; } + Ogre::Node *getNode(const std::string &name); }; diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index e71e694f9..42c78f753 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -283,10 +283,14 @@ namespace MWRender // 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->setCamera(NULL); + } mAnimation = anim; mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : NpcAnimation::VM_Normal); + mAnimation->setCamera(this); } void Camera::setHeight(float height) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index b905a8bdc..dbefb8b3c 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -14,6 +14,7 @@ #include "../mwbase/mechanicsmanager.hpp" #include "renderconst.hpp" +#include "camera.hpp" namespace MWRender @@ -415,6 +416,13 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) Ogre::Vector3 ret = Animation::runAnimation(timepassed); Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); + if(mViewMode == VM_FirstPerson && mCamera) + { + float pitch = mCamera->getPitch(); + Ogre::Node *node = baseinst->getBone("Bip01 Neck"); + node->pitch(Ogre::Radian(pitch*0.75f), Ogre::Node::TS_WORLD); + } + for(size_t i = 0;i < sPartListSize;i++) { Ogre::Entity *ent = mObjectParts[i].mSkelBase; From 5ee889e8b64d937c92a901e4f3310e7084b242ca Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 13 Jul 2013 16:12:38 -0700 Subject: [PATCH 08/12] Attach the camera to the Head node in first-person --- apps/openmw/mwrender/animation.cpp | 36 ++++++++++++++++ apps/openmw/mwrender/animation.hpp | 10 +++++ apps/openmw/mwrender/camera.cpp | 66 +++++++++++++++--------------- apps/openmw/mwrender/camera.hpp | 5 +-- 4 files changed, 80 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 2d3b5942d..a5ca5b81c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -115,7 +115,23 @@ void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, b Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); + + // Reattach any objects that have been attached to this one + ObjectAttachMap::iterator iter = mAttachedObjects.begin(); + while(iter != mAttachedObjects.end()) + { + if(!skelinst->hasBone(iter->second)) + mAttachedObjects.erase(iter++); + else + { + mSkelBase->attachObjectToBone(iter->second, iter->first); + iter++; + } + } } + else + mAttachedObjects.clear(); + for(size_t i = 0;i < mObjectRoot.mControllers.size();i++) { if(mObjectRoot.mControllers[i].getSource().isNull()) @@ -741,4 +757,24 @@ bool Animation::isPriorityActive(int priority) const return false; } +Ogre::TagPoint *Animation::attachObjectToBone(const Ogre::String &bonename, Ogre::MovableObject *obj) +{ + Ogre::TagPoint *tag = NULL; + Ogre::SkeletonInstance *skel = (mSkelBase ? mSkelBase->getSkeleton() : NULL); + if(skel && skel->hasBone(bonename)) + { + tag = mSkelBase->attachObjectToBone(bonename, obj); + mAttachedObjects[obj] = bonename; + } + return tag; +} + +void Animation::detachObjectFromBone(Ogre::MovableObject *obj) +{ + ObjectAttachMap::iterator iter = mAttachedObjects.find(obj); + if(iter != mAttachedObjects.end()) + mAttachedObjects.erase(iter); + mSkelBase->detachObjectFromBone(obj); +} + } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 050aa925a..f87fade55 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -80,6 +80,8 @@ protected: }; typedef std::map AnimStateMap; + typedef std::map ObjectAttachMap; + MWWorld::Ptr mPtr; Camera *mCamera; @@ -96,6 +98,8 @@ protected: Ogre::SharedPtr mAnimationValuePtr[sNumGroups]; + ObjectAttachMap mAttachedObjects; + float mAnimVelocity; float mAnimSpeedMult; @@ -216,6 +220,12 @@ public: { mCamera = cam; } Ogre::Node *getNode(const std::string &name); + + // Attaches the given object to a bone on this object's base skeleton. If the bone doesn't + // exist, the object isn't attached and NULL is returned. The returned TagPoint is only + // valid until the next setObjectRoot call. + Ogre::TagPoint *attachObjectToBone(const Ogre::String &bonename, Ogre::MovableObject *obj); + void detachObjectFromBone(Ogre::MovableObject *obj); }; } diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 42c78f753..d226c577c 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -57,10 +58,10 @@ namespace MWRender Ogre::Quaternion xr(Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X); if (!mVanity.enabled && !mPreviewMode) { - mCameraNode->setOrientation(xr); + mCamera->getParentNode()->setOrientation(xr); } else { Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z); - mCameraNode->setOrientation(zr * xr); + mCamera->getParentNode()->setOrientation(zr * xr); } } @@ -112,14 +113,12 @@ namespace MWRender void Camera::toggleViewMode() { mFirstPersonView = !mFirstPersonView; - mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : - NpcAnimation::VM_Normal); + processViewChange(); + if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); - setLowHeight(false); } else { mCamera->setPosition(0.f, 0.f, mCameraDistance); - setLowHeight(true); } } @@ -139,21 +138,16 @@ namespace MWRender return true; mVanity.enabled = enable; - mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : - NpcAnimation::VM_Normal); + processViewChange(); float offset = mPreviewCam.offset; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; - - setLowHeight(true); } else { rot.x = getPitch(); offset = mMainCam.offset; - - setLowHeight(!mFirstPersonView); } rot.z = getYaw(); @@ -169,20 +163,15 @@ namespace MWRender return; mPreviewMode = enable; - mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : - NpcAnimation::VM_Normal); + processViewChange(); float offset = mCamera->getPosition().z; if (mPreviewMode) { mMainCam.offset = offset; offset = mPreviewCam.offset; - - setLowHeight(true); } else { mPreviewCam.offset = offset; offset = mMainCam.offset; - - setLowHeight(!mFirstPersonView); } mCamera->setPosition(0.f, 0.f, offset); @@ -286,27 +275,45 @@ namespace MWRender { mAnimation->setViewMode(NpcAnimation::VM_Normal); mAnimation->setCamera(NULL); + mAnimation->detachObjectFromBone(mCamera); } mAnimation = anim; - mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : - NpcAnimation::VM_Normal); mAnimation->setCamera(this); + + processViewChange(); } - void Camera::setHeight(float height) + void Camera::processViewChange() { - mHeight = height; - mCameraNode->setPosition(0.f, 0.f, mHeight); + mAnimation->detachObjectFromBone(mCamera); + mCamera->detachFromParent(); + + if(isFirstPerson()) + { + mAnimation->setViewMode(NpcAnimation::VM_FirstPerson); + Ogre::TagPoint *tag = mAnimation->attachObjectToBone("Head", mCamera); + tag->setInheritOrientation(false); + } + else + { + mAnimation->setViewMode(NpcAnimation::VM_Normal); + mCameraNode->attachObject(mCamera); + } } float Camera::getHeight() { - return mHeight * mTrackingPtr.getRefData().getBaseNode()->getScale().z; + if(mCamera->isParentTagPoint()) + { + Ogre::TagPoint *tag = static_cast(mCamera->getParentNode()); + return tag->_getFullLocalTransform().getTrans().z; + } + return mCamera->getParentNode()->getPosition().z; } bool Camera::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera) { - mCamera->getParentSceneNode ()->needUpdate(true); + mCamera->getParentSceneNode()->needUpdate(true); camera = mCamera->getRealPosition(); player = mTrackingPtr.getRefData().getBaseNode()->getPosition(); @@ -329,15 +336,6 @@ namespace MWRender mFreeLook = enable; } - void Camera::setLowHeight(bool low) - { - if (low) { - mCameraNode->setPosition(0.f, 0.f, mHeight * 0.85); - } else { - mCameraNode->setPosition(0.f, 0.f, mHeight); - } - } - bool Camera::isVanityOrPreviewModeEnabled() { return mPreviewMode || mVanity.enabled; diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index ad5e35f93..3418efcc9 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -46,8 +46,6 @@ namespace MWRender /// Updates sound manager listener data void updateListener(); - void setLowHeight(bool low = true); - public: Camera(Ogre::Camera *camera); ~Camera(); @@ -80,6 +78,8 @@ namespace MWRender bool isFirstPerson() const { return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); } + void processViewChange(); + void update(float duration); /// Set camera distance for current mode. Don't work on 1st person view. @@ -93,7 +93,6 @@ namespace MWRender void setAnimation(NpcAnimation *anim); - void setHeight(float height); float getHeight(); /// Stores player and camera world positions in passed arguments From 3771e5839e9b007c4a984a87a9337e2810611b2c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 13 Jul 2013 17:03:10 -0700 Subject: [PATCH 09/12] Allow some more third person fallbacks in first person --- apps/openmw/mwrender/npcanimation.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index dbefb8b3c..92218a3f2 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -336,9 +336,10 @@ void NpcAnimation::updateParts(bool forceupdate) { if(mViewMode == VM_FirstPerson && (bodypart.mData.mPart == ESM::BodyPart::MP_Hand || bodypart.mData.mPart == ESM::BodyPart::MP_Wrist || - bodypart.mData.mPart == ESM::BodyPart::MP_Forearm)) + bodypart.mData.mPart == ESM::BodyPart::MP_Forearm || + bodypart.mData.mPart == ESM::BodyPart::MP_Upperarm)) { - /* Allow 3rd person skins as a fallback for the forearms if 1st person is missing. */ + /* Allow 3rd person skins as a fallback for the arms if 1st person is missing. */ BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) { @@ -499,9 +500,31 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectorisMale() && !part->mFemale.empty()) + { bodypart = partStore.search(part->mFemale+ext); + if(!bodypart && mViewMode == VM_FirstPerson) + { + bodypart = partStore.search(part->mFemale); + if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand || + bodypart->mData.mPart == ESM::BodyPart::MP_Wrist || + bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || + bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) + bodypart = NULL; + } + } if(!bodypart && !part->mMale.empty()) + { bodypart = partStore.search(part->mMale+ext); + if(!bodypart && mViewMode == VM_FirstPerson) + { + bodypart = partStore.search(part->mMale); + if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand || + bodypart->mData.mPart == ESM::BodyPart::MP_Wrist || + bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || + bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) + bodypart = NULL; + } + } if(bodypart) addOrReplaceIndividualPart(part->mPart, group, priority, "meshes\\"+bodypart->mModel); From a049638e7f86e83a8db6e04aaa6db022da6f2d15 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 14 Jul 2013 14:54:40 +0200 Subject: [PATCH 10/12] Fixes character previews getting shadowed randomly --- apps/openmw/mwrender/characterpreview.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index e9ecedc55..c6e6e158e 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -44,9 +44,14 @@ namespace MWRender { mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); - /// \todo Read the fallback values from INIImporter (Inventory:Directional*) + // This is a dummy light to turn off shadows without having to use a separate set of shaders Ogre::Light* l = mSceneMgr->createLight(); l->setType (Ogre::Light::LT_DIRECTIONAL); + l->setDiffuseColour (Ogre::ColourValue(0,0,0)); + + /// \todo Read the fallback values from INIImporter (Inventory:Directional*) + l = mSceneMgr->createLight(); + l->setType (Ogre::Light::LT_DIRECTIONAL); l->setDirection (Ogre::Vector3(0.3, -0.7, 0.3)); l->setDiffuseColour (Ogre::ColourValue(1,1,1)); From 61661c865336eedf814be19ef4983ffe55c26331 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 14 Jul 2013 14:55:07 +0200 Subject: [PATCH 11/12] Fix first person meshes casting shadows --- apps/openmw/mwrender/npcanimation.cpp | 4 +--- apps/openmw/mwrender/renderconst.hpp | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 92218a3f2..f2df5ccd5 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -374,14 +374,12 @@ void NpcAnimation::updateParts(bool forceupdate) NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) { NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); - setRenderProperties(objects, mVisibilityFlags, RQG_Main, RQG_Alpha); + setRenderProperties(objects, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha); for(size_t i = 0;i < objects.mEntities.size();i++) { Ogre::Entity *ent = objects.mEntities[i]; ent->getUserObjectBindings().setUserAny(Ogre::Any(group)); - if(mViewMode == VM_FirstPerson) - ent->setCastShadows(false); } for(size_t i = 0;i < objects.mParticles.size();i++) objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); diff --git a/apps/openmw/mwrender/renderconst.hpp b/apps/openmw/mwrender/renderconst.hpp index 1d2cdf1ea..44599ebee 100644 --- a/apps/openmw/mwrender/renderconst.hpp +++ b/apps/openmw/mwrender/renderconst.hpp @@ -59,6 +59,9 @@ enum VisibilityFlags // overlays, we only want these on the main render target RV_Overlay = 1024, + // First person meshes do not cast shadows + RV_FirstPerson = 2048, + RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water }; From fa5198d7b24cc2f444d5379f20ca9b7c2873658a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 14 Jul 2013 14:59:24 +0200 Subject: [PATCH 12/12] Fix an assertion --- apps/openmw/mwrender/animation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a5ca5b81c..f958b8286 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -73,7 +73,7 @@ Animation::~Animation() void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly) { - OgreAssert(mAnimSources.size() != 0, "Setting object root while animation sources are set!"); + OgreAssert(mAnimSources.size() == 0, "Setting object root while animation sources are set!"); if(!mInsert) mInsert = node->createChildSceneNode();