mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-22 13:09:41 +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()
|
void Animation::resetActiveGroups()
|
||||||
{
|
{
|
||||||
// remove all previous external controllers from the scene graph
|
// 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;
|
osg::Node* node = it->first;
|
||||||
node->removeUpdateCallback(it->second);
|
node->removeUpdateCallback(it->second);
|
||||||
|
@ -611,13 +611,8 @@ namespace MWRender
|
||||||
// Should be no longer needed with OSG 3.4
|
// Should be no longer needed with OSG 3.4
|
||||||
it->second->setNestedCallback(NULL);
|
it->second->setNestedCallback(NULL);
|
||||||
}
|
}
|
||||||
if (mResetAccumRootCallback && mAccumRoot)
|
|
||||||
{
|
mActiveControllers.clear();
|
||||||
mAccumRoot->removeUpdateCallback(mResetAccumRootCallback);
|
|
||||||
// Should be no longer needed with OSG 3.4
|
|
||||||
mResetAccumRootCallback->setNestedCallback(NULL);
|
|
||||||
}
|
|
||||||
mAnimSourceControllers.clear();
|
|
||||||
|
|
||||||
mAccumCtrl = NULL;
|
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
|
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);
|
node->addUpdateCallback(it->second);
|
||||||
mAnimSourceControllers[node] = it->second;
|
mActiveControllers.insert(std::make_pair(node, it->second));
|
||||||
|
|
||||||
if (grp == 0 && node == mAccumRoot)
|
if (grp == 0 && node == mAccumRoot)
|
||||||
{
|
{
|
||||||
|
@ -660,10 +655,12 @@ namespace MWRender
|
||||||
mResetAccumRootCallback->setAccumulate(mAccumulate);
|
mResetAccumRootCallback->setAccumulate(mAccumulate);
|
||||||
}
|
}
|
||||||
mAccumRoot->addUpdateCallback(mResetAccumRootCallback);
|
mAccumRoot->addUpdateCallback(mResetAccumRootCallback);
|
||||||
|
mActiveControllers.insert(std::make_pair(mAccumRoot, mResetAccumRootCallback));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
addControllers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::changeGroups(const std::string &groupname, int groups)
|
void Animation::changeGroups(const std::string &groupname, int groups)
|
||||||
|
@ -893,7 +890,7 @@ namespace MWRender
|
||||||
mObjectRoot = NULL;
|
mObjectRoot = NULL;
|
||||||
|
|
||||||
mNodeMap.clear();
|
mNodeMap.clear();
|
||||||
mAnimSourceControllers.clear();
|
mActiveControllers.clear();
|
||||||
mAccumRoot = NULL;
|
mAccumRoot = NULL;
|
||||||
mAccumCtrl = 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
|
// 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;
|
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.
|
// 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;
|
typedef std::multimap<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback> > ControllerMap;
|
||||||
AnimSourceControllerMap mAnimSourceControllers;
|
ControllerMap mActiveControllers;
|
||||||
|
|
||||||
boost::shared_ptr<AnimationTime> mAnimationTimePtr[sNumGroups];
|
boost::shared_ptr<AnimationTime> mAnimationTimePtr[sNumGroups];
|
||||||
|
|
||||||
|
@ -256,6 +256,12 @@ protected:
|
||||||
|
|
||||||
void clearAnimSources();
|
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);
|
osg::Vec4f getEnchantmentColor(MWWorld::Ptr item);
|
||||||
|
|
||||||
void addGlow(osg::ref_ptr<osg::Node> node, osg::Vec4f glowColor);
|
void addGlow(osg::ref_ptr<osg::Node> node, osg::Vec4f glowColor);
|
||||||
|
|
|
@ -250,7 +250,7 @@ namespace MWRender
|
||||||
|
|
||||||
void Camera::setSneakOffset(float offset)
|
void Camera::setSneakOffset(float offset)
|
||||||
{
|
{
|
||||||
// TODO: implement
|
mAnimation->setFirstPersonOffset(osg::Vec3f(0,0,-offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
float Camera::getYaw()
|
float Camera::getYaw()
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
#include "npcanimation.hpp"
|
#include "npcanimation.hpp"
|
||||||
|
|
||||||
#include <osg/UserDataContainer>
|
#include <osg/UserDataContainer>
|
||||||
|
#include <osg/MatrixTransform>
|
||||||
|
|
||||||
#include <osg/MatrixTransform> // XXX
|
|
||||||
|
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
|
@ -74,6 +72,100 @@ std::string getVampireHead(const std::string& race, bool female)
|
||||||
namespace MWRender
|
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)
|
HeadAnimationTime::HeadAnimationTime(MWWorld::Ptr reference)
|
||||||
: mReference(reference), mTalkStart(0), mTalkStop(0), mBlinkStart(0), mBlinkStop(0), mEnabled(true), mValue(0)
|
: 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;
|
mBlinkStop = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
static NpcAnimation::PartBoneMap createPartListMap()
|
static NpcAnimation::PartBoneMap createPartListMap()
|
||||||
{
|
{
|
||||||
NpcAnimation::PartBoneMap result;
|
NpcAnimation::PartBoneMap result;
|
||||||
|
@ -196,7 +290,6 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> par
|
||||||
mViewMode(viewMode),
|
mViewMode(viewMode),
|
||||||
mShowWeapons(false),
|
mShowWeapons(false),
|
||||||
mShowCarriedLeft(true),
|
mShowCarriedLeft(true),
|
||||||
//mFirstPersonOffset(0.f, 0.f, 0.f),
|
|
||||||
mNpcType(Type_Normal),
|
mNpcType(Type_Normal),
|
||||||
mVisibilityFlags(visibilityFlags),
|
mVisibilityFlags(visibilityFlags),
|
||||||
mAlpha(1.f),
|
mAlpha(1.f),
|
||||||
|
@ -583,11 +676,6 @@ void NpcAnimation::updateParts()
|
||||||
if (wasArrowAttached)
|
if (wasArrowAttached)
|
||||||
attachArrow();
|
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)
|
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);
|
mHeadAnimationTime->update(timepassed);
|
||||||
|
|
||||||
|
if (mFirstPersonNeckController)
|
||||||
|
{
|
||||||
|
mFirstPersonNeckController->setRotate(mPtr.getRefData().getPosition().rot[0]);
|
||||||
|
mFirstPersonNeckController->setOffset(mFirstPersonOffset);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (mSkelBase)
|
if (mSkelBase)
|
||||||
{
|
{
|
||||||
Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
|
Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
|
||||||
if(mViewMode == VM_FirstPerson)
|
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
|
|
||||||
{
|
{
|
||||||
// In third person mode we may still need pitch for ranged weapon targeting
|
// In third person mode we may still need pitch for ranged weapon targeting
|
||||||
pitchSkeleton(mPtr.getRefData().getPosition().rot[0], baseinst);
|
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);
|
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;
|
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)
|
void NpcAnimation::showWeapons(bool showWeapon)
|
||||||
{
|
{
|
||||||
mShowWeapons = 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)
|
void NpcAnimation::updatePtr(const MWWorld::Ptr &updated)
|
||||||
{
|
{
|
||||||
Animation::updatePtr(updated);
|
Animation::updatePtr(updated);
|
||||||
|
|
|
@ -48,6 +48,8 @@ public:
|
||||||
virtual float getValue(osg::NodeVisitor* nv);
|
virtual float getValue(osg::NodeVisitor* nv);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NeckController;
|
||||||
|
|
||||||
class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
|
class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -92,7 +94,7 @@ private:
|
||||||
int mPartslots[ESM::PRT_Count]; //Each part slot is taken by clothing, armor, or is empty
|
int mPartslots[ESM::PRT_Count]; //Each part slot is taken by clothing, armor, or is empty
|
||||||
int mPartPriorities[ESM::PRT_Count];
|
int mPartPriorities[ESM::PRT_Count];
|
||||||
|
|
||||||
//Ogre::Vector3 mFirstPersonOffset;
|
osg::Vec3f mFirstPersonOffset;
|
||||||
|
|
||||||
boost::shared_ptr<HeadAnimationTime> mHeadAnimationTime;
|
boost::shared_ptr<HeadAnimationTime> mHeadAnimationTime;
|
||||||
boost::shared_ptr<WeaponAnimationTime> mWeaponAnimationTime;
|
boost::shared_ptr<WeaponAnimationTime> mWeaponAnimationTime;
|
||||||
|
@ -119,6 +121,11 @@ private:
|
||||||
|
|
||||||
//void applyAlpha(float alpha, Ogre::Entity* ent, NifOgre::ObjectScenePtr scene);
|
//void applyAlpha(float alpha, Ogre::Entity* ent, NifOgre::ObjectScenePtr scene);
|
||||||
|
|
||||||
|
osg::ref_ptr<NeckController> mFirstPersonNeckController;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void addControllers();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @param ptr
|
* @param ptr
|
||||||
|
@ -186,6 +193,9 @@ public:
|
||||||
|
|
||||||
virtual void setVampire(bool vampire);
|
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);
|
virtual void updatePtr(const MWWorld::Ptr& updated);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue