mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 07:56:40 +00:00 
			
		
		
		
	Rotate first person meshes, sneak camera offset in first person
This commit is contained in:
		
							parent
							
								
									a066b24303
								
							
						
					
					
						commit
						60f55997fd
					
				
					 5 changed files with 153 additions and 36 deletions
				
			
		| 
						 | 
				
			
			@ -603,7 +603,7 @@ namespace MWRender
 | 
			
		|||
    void Animation::resetActiveGroups()
 | 
			
		||||
    {
 | 
			
		||||
        // remove all previous external controllers from the scene graph
 | 
			
		||||
        for (AnimSourceControllerMap::iterator it = mAnimSourceControllers.begin(); it != mAnimSourceControllers.end(); ++it)
 | 
			
		||||
        for (ControllerMap::iterator it = mActiveControllers.begin(); it != mActiveControllers.end(); ++it)
 | 
			
		||||
        {
 | 
			
		||||
            osg::Node* node = it->first;
 | 
			
		||||
            node->removeUpdateCallback(it->second);
 | 
			
		||||
| 
						 | 
				
			
			@ -611,13 +611,8 @@ namespace MWRender
 | 
			
		|||
            // Should be no longer needed with OSG 3.4
 | 
			
		||||
            it->second->setNestedCallback(NULL);
 | 
			
		||||
        }
 | 
			
		||||
        if (mResetAccumRootCallback && mAccumRoot)
 | 
			
		||||
        {
 | 
			
		||||
            mAccumRoot->removeUpdateCallback(mResetAccumRootCallback);
 | 
			
		||||
            // Should be no longer needed with OSG 3.4
 | 
			
		||||
            mResetAccumRootCallback->setNestedCallback(NULL);
 | 
			
		||||
        }
 | 
			
		||||
        mAnimSourceControllers.clear();
 | 
			
		||||
 | 
			
		||||
        mActiveControllers.clear();
 | 
			
		||||
 | 
			
		||||
        mAccumCtrl = NULL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -647,7 +642,7 @@ namespace MWRender
 | 
			
		|||
                    osg::ref_ptr<osg::Node> node = mNodeMap.at(it->first); // this should not throw, we already checked for the node existing in addAnimSource
 | 
			
		||||
 | 
			
		||||
                    node->addUpdateCallback(it->second);
 | 
			
		||||
                    mAnimSourceControllers[node] = it->second;
 | 
			
		||||
                    mActiveControllers.insert(std::make_pair(node, it->second));
 | 
			
		||||
 | 
			
		||||
                    if (grp == 0 && node == mAccumRoot)
 | 
			
		||||
                    {
 | 
			
		||||
| 
						 | 
				
			
			@ -660,10 +655,12 @@ namespace MWRender
 | 
			
		|||
                            mResetAccumRootCallback->setAccumulate(mAccumulate);
 | 
			
		||||
                        }
 | 
			
		||||
                        mAccumRoot->addUpdateCallback(mResetAccumRootCallback);
 | 
			
		||||
                        mActiveControllers.insert(std::make_pair(mAccumRoot, mResetAccumRootCallback));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        addControllers();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Animation::changeGroups(const std::string &groupname, int groups)
 | 
			
		||||
| 
						 | 
				
			
			@ -893,7 +890,7 @@ namespace MWRender
 | 
			
		|||
        mObjectRoot = NULL;
 | 
			
		||||
 | 
			
		||||
        mNodeMap.clear();
 | 
			
		||||
        mAnimSourceControllers.clear();
 | 
			
		||||
        mActiveControllers.clear();
 | 
			
		||||
        mAccumRoot = NULL;
 | 
			
		||||
        mAccumCtrl = NULL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -182,10 +182,10 @@ protected:
 | 
			
		|||
    // Used to reset the position of the accumulation root every frame - the movement should be applied to the physics system
 | 
			
		||||
    osg::ref_ptr<ResetAccumRootCallback> mResetAccumRootCallback;
 | 
			
		||||
 | 
			
		||||
    // Keep track of keyframe controllers from external files that we added to our scene graph.
 | 
			
		||||
    // Keep track of controllers that we added to our scene graph.
 | 
			
		||||
    // We may need to rebuild these controllers when the active animation groups / sources change.
 | 
			
		||||
    typedef std::map<osg::ref_ptr<osg::Node>, osg::ref_ptr<NifOsg::KeyframeController> > AnimSourceControllerMap;
 | 
			
		||||
    AnimSourceControllerMap mAnimSourceControllers;
 | 
			
		||||
    typedef std::multimap<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback> > ControllerMap;
 | 
			
		||||
    ControllerMap mActiveControllers;
 | 
			
		||||
 | 
			
		||||
    boost::shared_ptr<AnimationTime> mAnimationTimePtr[sNumGroups];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -256,6 +256,12 @@ protected:
 | 
			
		|||
 | 
			
		||||
    void clearAnimSources();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Provided to allow derived classes adding their own controllers. Note, the controllers must be added to mActiveControllers
 | 
			
		||||
     * so they get cleaned up properly on the next controller rebuild. A controller rebuild may be necessary to ensure correct ordering.
 | 
			
		||||
     */
 | 
			
		||||
    virtual void addControllers() {}
 | 
			
		||||
 | 
			
		||||
    osg::Vec4f getEnchantmentColor(MWWorld::Ptr item);
 | 
			
		||||
 | 
			
		||||
    void addGlow(osg::ref_ptr<osg::Node> node, osg::Vec4f glowColor);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -250,7 +250,7 @@ namespace MWRender
 | 
			
		|||
 | 
			
		||||
    void Camera::setSneakOffset(float offset)
 | 
			
		||||
    {
 | 
			
		||||
        // TODO: implement
 | 
			
		||||
        mAnimation->setFirstPersonOffset(osg::Vec3f(0,0,-offset));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    float Camera::getYaw()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,7 @@
 | 
			
		|||
#include "npcanimation.hpp"
 | 
			
		||||
 | 
			
		||||
#include <osg/UserDataContainer>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <osg/MatrixTransform> // XXX
 | 
			
		||||
#include <osg/MatrixTransform>
 | 
			
		||||
 | 
			
		||||
#include <components/misc/rng.hpp>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -74,6 +72,100 @@ std::string getVampireHead(const std::string& race, bool female)
 | 
			
		|||
namespace MWRender
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
/// @note Assumes that the node being rotated has its "original" orientation set every frame by a different controller.
 | 
			
		||||
/// The pitch is then applied on top of that orientation.
 | 
			
		||||
/// @note Must be set on a MatrixTransform.
 | 
			
		||||
class RotateController : public osg::NodeCallback
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    RotateController(osg::Node* relativeTo, osg::Vec3f axis)
 | 
			
		||||
        : mRotate(0.f)
 | 
			
		||||
        , mAxis(axis)
 | 
			
		||||
        , mRelativeTo(relativeTo)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setRotate(float rotate)
 | 
			
		||||
    {
 | 
			
		||||
        mRotate = rotate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
 | 
			
		||||
    {
 | 
			
		||||
        if (mRotate == 0.f)
 | 
			
		||||
            return;
 | 
			
		||||
        osg::MatrixTransform* transform = static_cast<osg::MatrixTransform*>(node);
 | 
			
		||||
        osg::Matrix matrix = transform->getMatrix();
 | 
			
		||||
        osg::Quat worldOrient = getWorldOrientation(node);
 | 
			
		||||
 | 
			
		||||
        osg::Quat rotate (mRotate, mAxis);
 | 
			
		||||
        osg::Quat orient = worldOrient * rotate * worldOrient.inverse() * matrix.getRotate();
 | 
			
		||||
        matrix.setRotate(orient);
 | 
			
		||||
 | 
			
		||||
        transform->setMatrix(matrix);
 | 
			
		||||
 | 
			
		||||
        traverse(node,nv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    osg::Quat getWorldOrientation(osg::Node* node)
 | 
			
		||||
    {
 | 
			
		||||
        // this could be optimized later, we just need the world orientation, not the full matrix
 | 
			
		||||
        osg::MatrixList worldMats = node->getWorldMatrices(mRelativeTo);
 | 
			
		||||
        osg::Quat worldOrient;
 | 
			
		||||
        if (worldMats.size())
 | 
			
		||||
        {
 | 
			
		||||
            osg::Matrixf worldMat = worldMats[0];
 | 
			
		||||
            worldOrient = worldMat.getRotate();
 | 
			
		||||
        }
 | 
			
		||||
        return worldOrient;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    float mRotate;
 | 
			
		||||
    osg::Vec3f mAxis;
 | 
			
		||||
    osg::ref_ptr<osg::Node> mRelativeTo;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Subclass RotateController to add a Z-offset for sneaking in first person mode.
 | 
			
		||||
/// @note We use inheritance instead of adding another controller, so that we do not have to compute the worldOrient twice.
 | 
			
		||||
/// @note Must be set on a MatrixTransform.
 | 
			
		||||
class NeckController : public RotateController
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    NeckController(osg::Node* relativeTo)
 | 
			
		||||
        : RotateController(relativeTo, osg::Vec3f(-1,0,0))
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setOffset(osg::Vec3f offset)
 | 
			
		||||
    {
 | 
			
		||||
        mOffset = offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
 | 
			
		||||
    {
 | 
			
		||||
        osg::MatrixTransform* transform = static_cast<osg::MatrixTransform*>(node);
 | 
			
		||||
        osg::Matrix matrix = transform->getMatrix();
 | 
			
		||||
 | 
			
		||||
        osg::Quat worldOrient = getWorldOrientation(node);
 | 
			
		||||
        osg::Quat rotate (mRotate, mAxis);
 | 
			
		||||
        osg::Quat orient = worldOrient * rotate * worldOrient.inverse() * matrix.getRotate();
 | 
			
		||||
 | 
			
		||||
        matrix.setRotate(orient);
 | 
			
		||||
        matrix.setTrans(matrix.getTrans() + worldOrient.inverse() * mOffset);
 | 
			
		||||
 | 
			
		||||
        transform->setMatrix(matrix);
 | 
			
		||||
 | 
			
		||||
        traverse(node,nv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    osg::Vec3f mOffset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// --------------------------------------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
HeadAnimationTime::HeadAnimationTime(MWWorld::Ptr reference)
 | 
			
		||||
    : mReference(reference), mTalkStart(0), mTalkStop(0), mBlinkStart(0), mBlinkStop(0), mEnabled(true), mValue(0)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -150,6 +242,8 @@ void HeadAnimationTime::setBlinkStop(float value)
 | 
			
		|||
    mBlinkStop = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----------------------------------------------------
 | 
			
		||||
 | 
			
		||||
static NpcAnimation::PartBoneMap createPartListMap()
 | 
			
		||||
{
 | 
			
		||||
    NpcAnimation::PartBoneMap result;
 | 
			
		||||
| 
						 | 
				
			
			@ -196,7 +290,6 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> par
 | 
			
		|||
    mViewMode(viewMode),
 | 
			
		||||
    mShowWeapons(false),
 | 
			
		||||
    mShowCarriedLeft(true),
 | 
			
		||||
    //mFirstPersonOffset(0.f, 0.f, 0.f),
 | 
			
		||||
    mNpcType(Type_Normal),
 | 
			
		||||
    mVisibilityFlags(visibilityFlags),
 | 
			
		||||
    mAlpha(1.f),
 | 
			
		||||
| 
						 | 
				
			
			@ -583,11 +676,6 @@ void NpcAnimation::updateParts()
 | 
			
		|||
    if (wasArrowAttached)
 | 
			
		||||
        attachArrow();
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
void NpcAnimation::addFirstPersonOffset(const Ogre::Vector3 &offset)
 | 
			
		||||
{
 | 
			
		||||
    mFirstPersonOffset += offset;
 | 
			
		||||
}*/
 | 
			
		||||
 | 
			
		||||
PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const std::string& bonename, const std::string& bonefilter, bool enchantedGlow, osg::Vec4f* glowColor)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -605,21 +693,17 @@ osg::Vec3f NpcAnimation::runAnimation(float timepassed)
 | 
			
		|||
 | 
			
		||||
    mHeadAnimationTime->update(timepassed);
 | 
			
		||||
 | 
			
		||||
    if (mFirstPersonNeckController)
 | 
			
		||||
    {
 | 
			
		||||
        mFirstPersonNeckController->setRotate(mPtr.getRefData().getPosition().rot[0]);
 | 
			
		||||
        mFirstPersonNeckController->setOffset(mFirstPersonOffset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    if (mSkelBase)
 | 
			
		||||
    {
 | 
			
		||||
        Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
 | 
			
		||||
        if(mViewMode == VM_FirstPerson)
 | 
			
		||||
        {
 | 
			
		||||
            float pitch = mPtr.getRefData().getPosition().rot[0];
 | 
			
		||||
            Ogre::Node *node = baseinst->getBone("Bip01 Neck");
 | 
			
		||||
            node->pitch(Ogre::Radian(-pitch), Ogre::Node::TS_WORLD);
 | 
			
		||||
 | 
			
		||||
            // This has to be done before this function ends;
 | 
			
		||||
            // updateSkeletonInstance, below, touches the hands.
 | 
			
		||||
            node->translate(mFirstPersonOffset, Ogre::Node::TS_WORLD);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        if(mViewMode != VM_FirstPerson)
 | 
			
		||||
        {
 | 
			
		||||
            // In third person mode we may still need pitch for ranged weapon targeting
 | 
			
		||||
            pitchSkeleton(mPtr.getRefData().getPosition().rot[0], baseinst);
 | 
			
		||||
| 
						 | 
				
			
			@ -629,7 +713,6 @@ osg::Vec3f NpcAnimation::runAnimation(float timepassed)
 | 
			
		|||
                node->rotate(Ogre::Quaternion(mHeadYaw, Ogre::Vector3::UNIT_Z) * Ogre::Quaternion(mHeadPitch, Ogre::Vector3::UNIT_X), Ogre::Node::TS_WORLD);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame.
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
| 
						 | 
				
			
			@ -792,6 +875,22 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NpcAnimation::addControllers()
 | 
			
		||||
{
 | 
			
		||||
    mFirstPersonNeckController = NULL;
 | 
			
		||||
    if (mViewMode == VM_FirstPerson)
 | 
			
		||||
    {
 | 
			
		||||
        NodeMap::iterator found = mNodeMap.find("bip01 neck");
 | 
			
		||||
        if (found != mNodeMap.end() && dynamic_cast<osg::MatrixTransform*>(found->second.get()))
 | 
			
		||||
        {
 | 
			
		||||
            osg::Node* node = found->second;
 | 
			
		||||
            mFirstPersonNeckController = new NeckController(mObjectRoot.get());
 | 
			
		||||
            node->addUpdateCallback(mFirstPersonNeckController);
 | 
			
		||||
            mActiveControllers.insert(std::make_pair(node, mFirstPersonNeckController));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NpcAnimation::showWeapons(bool showWeapon)
 | 
			
		||||
{
 | 
			
		||||
    mShowWeapons = showWeapon;
 | 
			
		||||
| 
						 | 
				
			
			@ -947,6 +1046,11 @@ void NpcAnimation::setVampire(bool vampire)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NpcAnimation::setFirstPersonOffset(const osg::Vec3f &offset)
 | 
			
		||||
{
 | 
			
		||||
    mFirstPersonOffset = offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NpcAnimation::updatePtr(const MWWorld::Ptr &updated)
 | 
			
		||||
{
 | 
			
		||||
    Animation::updatePtr(updated);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,6 +48,8 @@ public:
 | 
			
		|||
    virtual float getValue(osg::NodeVisitor* nv);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class NeckController;
 | 
			
		||||
 | 
			
		||||
class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
| 
						 | 
				
			
			@ -92,7 +94,7 @@ private:
 | 
			
		|||
    int mPartslots[ESM::PRT_Count];  //Each part slot is taken by clothing, armor, or is empty
 | 
			
		||||
    int mPartPriorities[ESM::PRT_Count];
 | 
			
		||||
 | 
			
		||||
    //Ogre::Vector3 mFirstPersonOffset;
 | 
			
		||||
    osg::Vec3f mFirstPersonOffset;
 | 
			
		||||
 | 
			
		||||
    boost::shared_ptr<HeadAnimationTime> mHeadAnimationTime;
 | 
			
		||||
    boost::shared_ptr<WeaponAnimationTime> mWeaponAnimationTime;
 | 
			
		||||
| 
						 | 
				
			
			@ -119,6 +121,11 @@ private:
 | 
			
		|||
 | 
			
		||||
    //void applyAlpha(float alpha, Ogre::Entity* ent, NifOgre::ObjectScenePtr scene);
 | 
			
		||||
 | 
			
		||||
    osg::ref_ptr<NeckController> mFirstPersonNeckController;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    virtual void addControllers();
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    /**
 | 
			
		||||
     * @param ptr
 | 
			
		||||
| 
						 | 
				
			
			@ -186,6 +193,9 @@ public:
 | 
			
		|||
 | 
			
		||||
    virtual void setVampire(bool vampire);
 | 
			
		||||
 | 
			
		||||
    /// Set a translation offset (in object root space) to apply to meshes when in first person mode.
 | 
			
		||||
    void setFirstPersonOffset(const osg::Vec3f& offset);
 | 
			
		||||
 | 
			
		||||
    virtual void updatePtr(const MWWorld::Ptr& updated);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue