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:
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