From a700c50e84e06586e3a766940e43f33e156d67ee Mon Sep 17 00:00:00 2001
From: Chris Robinson <chris.kcat@gmail.com>
Date: Tue, 9 Apr 2013 15:10:14 -0700
Subject: [PATCH] Add a first-person view mode to NpcAnimation

And use it instead of showing/hiding the player.
---
 apps/openmw/mwrender/animation.cpp    |  2 +-
 apps/openmw/mwrender/npcanimation.cpp | 64 ++++++++++++++++++++++-----
 apps/openmw/mwrender/npcanimation.hpp |  3 ++
 apps/openmw/mwrender/player.cpp       | 12 +++--
 4 files changed, 65 insertions(+), 16 deletions(-)

diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp
index 067e85672..a849b20ef 100644
--- a/apps/openmw/mwrender/animation.cpp
+++ b/apps/openmw/mwrender/animation.cpp
@@ -476,7 +476,7 @@ void Animation::play(const std::string &groupname, const std::string &start, con
 
 Ogre::Vector3 Animation::runAnimation(float timepassed)
 {
-    Ogre::Vector3 movement = Ogre::Vector3::ZERO;
+    Ogre::Vector3 movement(0.0f);
 
     timepassed *= mAnimSpeedMult;
     while(mCurrentAnim && mPlaying)
diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp
index 685edb68c..7e59f8f6c 100644
--- a/apps/openmw/mwrender/npcanimation.cpp
+++ b/apps/openmw/mwrender/npcanimation.cpp
@@ -130,7 +130,40 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
         skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel));
     setAnimationSources(skelnames);
 
-    updateParts(true);
+    forceUpdate();
+}
+
+void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
+{
+    assert(viewMode != VM_HeadOnly);
+    mViewMode = viewMode;
+
+    /* FIXME: Enable this once first-person animations work. */
+#if 0
+    const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
+    const ESM::Race *race = store.get<ESM::Race>().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<std::string> skelnames(1, smodel);
+    if(!mNpc->isMale() && !isBeast)
+        skelnames.push_back("meshes\\base_anim_female.nif");
+    else if(mBodyPrefix.find("argonian") != std::string::npos)
+        skelnames.push_back("meshes\\argonian_swimkna.nif");
+    if(mNpc->mModel.length() > 0)
+        skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel));
+    if(mViewMode == VM_FirstPerson)
+    {
+        smodel = (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif");
+        skelnames.push_back(smodel);
+    }
+    setAnimationSources(skelnames);
+#endif
+
+    for(size_t i = 0;i < sPartListSize;i++)
+        removeIndividualPart(i);
+    forceUpdate();
 }
 
 void NpcAnimation::updateParts(bool forceupdate)
@@ -254,13 +287,18 @@ void NpcAnimation::updateParts(bool forceupdate)
             reserveIndividualPart(slotlist[i].reserveParts[res], slotlist[i].slot, prio);
     }
 
-    if(mPartPriorities[ESM::PRT_Head] < 1)
-        addOrReplaceIndividualPart(ESM::PRT_Head, -1,1, mHeadModel);
-    if(mPartPriorities[ESM::PRT_Hair] < 1 && mPartPriorities[ESM::PRT_Head] <= 1)
-        addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1, mHairModel);
-
+    if(mViewMode != VM_FirstPerson)
+    {
+        if(mPartPriorities[ESM::PRT_Head] < 1)
+            addOrReplaceIndividualPart(ESM::PRT_Head, -1,1, mHeadModel);
+        if(mPartPriorities[ESM::PRT_Hair] < 1 && mPartPriorities[ESM::PRT_Head] <= 1)
+            addOrReplaceIndividualPart(ESM::PRT_Hair, -1,1, mHairModel);
+    }
     if(mViewMode == VM_HeadOnly)
         return;
+    /* FIXME: Remove this once we figure out how to show what in first-person */
+    if(mViewMode == VM_FirstPerson)
+        return;
 
     static const struct {
         ESM::PartReferenceType type;
@@ -288,6 +326,7 @@ void NpcAnimation::updateParts(bool forceupdate)
         { ESM::PRT_Tail,      { "tail", "" } }
     };
 
+    const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : "";
     const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
     for(size_t i = 0;i < sizeof(PartTypeList)/sizeof(PartTypeList[0]);i++)
     {
@@ -298,14 +337,14 @@ void NpcAnimation::updateParts(bool forceupdate)
 
             if(!mNpc->isMale())
             {
-                part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]);
+                part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]+ext);
                 if(part == 0)
-                    part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]);
+                    part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]+ext);
             }
             if(part == 0)
-                part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]);
+                part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]+ext);
             if(part == 0)
-                part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]);
+                part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]+ext);
 
             if(part)
                 addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel);
@@ -431,6 +470,7 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority,
 
 void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::PartReference> &parts)
 {
+    const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : "";
     for(std::size_t i = 0; i < parts.size(); i++)
     {
         const ESM::PartReference &part = parts[i];
@@ -440,9 +480,9 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::
 
         const ESM::BodyPart *bodypart = 0;
         if(!mNpc->isMale())
-            bodypart = partStore.search(part.mFemale);
+            bodypart = partStore.search(part.mFemale+ext);
         if(!bodypart)
-            bodypart = partStore.search(part.mMale);
+            bodypart = partStore.search(part.mMale+ext);
 
         if(bodypart)
             addOrReplaceIndividualPart(part.mPart, group, priority, "meshes\\"+bodypart->mModel);
diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp
index 44639e94a..f2f4c6669 100644
--- a/apps/openmw/mwrender/npcanimation.hpp
+++ b/apps/openmw/mwrender/npcanimation.hpp
@@ -28,6 +28,7 @@ struct PartInfo {
 
 enum ViewMode {
     VM_Normal,
+    VM_FirstPerson,
     VM_HeadOnly
 };
 
@@ -84,6 +85,8 @@ public:
 
     virtual Ogre::Vector3 runAnimation(float timepassed);
 
+    void setViewMode(ViewMode viewMode);
+
     void forceUpdate()
     { updateParts(true); }
 };
diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp
index 55fda326f..439264f3a 100644
--- a/apps/openmw/mwrender/player.cpp
+++ b/apps/openmw/mwrender/player.cpp
@@ -124,8 +124,6 @@ namespace MWRender
         MWBase::Environment::get().getWindowManager ()->showCrosshair
                 (!MWBase::Environment::get().getWindowManager ()->isGuiMode () && (mFirstPersonView && !mVanity.enabled && !mPreviewMode));
 
-        /// \fixme We shouldn't hide the whole model, just certain components of the character (head, chest, feet, etc)
-        mPlayerNode->setVisible(mVanity.enabled || mPreviewMode || !mFirstPersonView);
         if (mFirstPersonView && !mVanity.enabled) {
             return;
         }
@@ -139,6 +137,8 @@ namespace MWRender
     void Player::toggleViewMode()
     {
         mFirstPersonView = !mFirstPersonView;
+        mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ?
+                                NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson);
         if (mFirstPersonView) {
             mCamera->setPosition(0.f, 0.f, 0.f);
             setLowHeight(false);
@@ -168,6 +168,9 @@ namespace MWRender
         mVanity.enabled = enable;
         mVanity.forced = force && enable;
 
+        mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ?
+                                NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson);
+
         float offset = mPreviewCam.offset;
         Ogre::Vector3 rot(0.f, 0.f, 0.f);
         if (mVanity.enabled) {
@@ -194,6 +197,8 @@ namespace MWRender
             return;
         }
         mPreviewMode = enable;
+        mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ?
+                                NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson);
         float offset = mCamera->getPosition().z;
         if (mPreviewMode) {
             mMainCam.offset = offset;
@@ -305,7 +310,8 @@ namespace MWRender
         delete mAnimation;
         mAnimation = anim;
 
-        mPlayerNode->setVisible(mVanity.enabled || mPreviewMode || !mFirstPersonView);
+        mAnimation->setViewMode((mVanity.enabled || mPreviewMode || !mFirstPersonView) ?
+                                NpcAnimation::VM_Normal : NpcAnimation::VM_FirstPerson);
     }
 
     void Player::setHeight(float height)