1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-20 19:39:41 +00:00

Rotate first person meshes, sneak camera offset in first person

This commit is contained in:
scrawl 2015-05-31 02:26:31 +02:00
parent a066b24303
commit 60f55997fd
5 changed files with 153 additions and 36 deletions

View file

@ -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;

View file

@ -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);

View file

@ -250,7 +250,7 @@ namespace MWRender
void Camera::setSneakOffset(float offset)
{
// TODO: implement
mAnimation->setFirstPersonOffset(osg::Vec3f(0,0,-offset));
}
float Camera::getYaw()

View file

@ -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);

View file

@ -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);
};