diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 2d3b5942d0..a5ca5b81cb 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 050aa925ae..f87fade556 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 42c78f7531..d226c577cd 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 ad5e35f939..3418efcc91 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