1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-27 03:35:27 +00:00

Restore head tracking

This commit is contained in:
scrawl 2015-05-31 18:04:14 +02:00
parent 60f55997fd
commit 71bafcb52b
6 changed files with 135 additions and 79 deletions

View File

@ -1,15 +1,12 @@
#include "actors.hpp" #include "actors.hpp"
#include <typeinfo> #include <typeinfo>
#include <OgreVector3.h> #include <osg/PositionAttitudeTransform>
#include <OgreSceneNode.h>
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/manualref.hpp" #include "../mwworld/manualref.hpp"
@ -20,6 +17,7 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwrender/animation.hpp" #include "../mwrender/animation.hpp"
@ -27,14 +25,9 @@
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "movement.hpp" #include "movement.hpp"
#include "character.hpp" #include "character.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "aicombat.hpp" #include "aicombat.hpp"
#include "aifollow.hpp" #include "aifollow.hpp"
#include "aipursue.hpp" #include "aipursue.hpp"
#include "actor.hpp" #include "actor.hpp"
#include "summoning.hpp" #include "summoning.hpp"
#include "combat.hpp" #include "combat.hpp"
@ -274,7 +267,6 @@ namespace MWMechanics
void Actors::updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor, void Actors::updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance) MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance)
{ {
/*
static const float fMaxHeadTrackDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static const float fMaxHeadTrackDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fMaxHeadTrackDistance")->getFloat(); .find("fMaxHeadTrackDistance")->getFloat();
static const float fInteriorHeadTrackMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static const float fInteriorHeadTrackMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
@ -286,7 +278,7 @@ namespace MWMechanics
const ESM::Position& actor1Pos = actor.getRefData().getPosition(); const ESM::Position& actor1Pos = actor.getRefData().getPosition();
const ESM::Position& actor2Pos = targetActor.getRefData().getPosition(); const ESM::Position& actor2Pos = targetActor.getRefData().getPosition();
float sqrDist = Ogre::Vector3(actor1Pos.pos).squaredDistance(Ogre::Vector3(actor2Pos.pos)); float sqrDist = (actor1Pos.asVec3() - actor2Pos.asVec3()).length2();
if (sqrDist > maxDistance*maxDistance) if (sqrDist > maxDistance*maxDistance)
return; return;
@ -294,12 +286,17 @@ namespace MWMechanics
if (targetActor.getClass().getCreatureStats(targetActor).isDead()) if (targetActor.getClass().getCreatureStats(targetActor).isDead())
return; return;
if (!actor.getRefData().getBaseNode())
return;
// stop tracking when target is behind the actor // stop tracking when target is behind the actor
Ogre::Vector3 actorDirection (actor.getRefData().getBaseNode()->getOrientation().yAxis()); osg::Vec3f actorDirection = actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0);
Ogre::Vector3 targetDirection (Ogre::Vector3(actor2Pos.pos) - Ogre::Vector3(actor1Pos.pos)); osg::Vec3f targetDirection (actor2Pos.asVec3() - actor1Pos.asVec3());
actorDirection.z = 0; actorDirection.z() = 0;
targetDirection.z = 0; targetDirection.z() = 0;
if (actorDirection.angleBetween(targetDirection) < Ogre::Degree(90) actorDirection.normalize();
targetDirection.normalize();
if (std::acos(actorDirection * targetDirection) < osg::DegreesToRadians(90.f)
&& sqrDist <= sqrHeadTrackDistance && sqrDist <= sqrHeadTrackDistance
&& MWBase::Environment::get().getWorld()->getLOS(actor, targetActor) // check LOS and awareness last as it's the most expensive function && MWBase::Environment::get().getWorld()->getLOS(actor, targetActor) // check LOS and awareness last as it's the most expensive function
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(targetActor, actor)) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(targetActor, actor))
@ -307,7 +304,6 @@ namespace MWMechanics
sqrHeadTrackDistance = sqrDist; sqrHeadTrackDistance = sqrDist;
headTrackTarget = targetActor; headTrackTarget = targetActor;
} }
*/
} }
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer) void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer)

View File

@ -21,6 +21,8 @@
#include <OgreStringConverter.h> #include <OgreStringConverter.h>
#include <osg/PositionAttitudeTransform>
#include "movement.hpp" #include "movement.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
#include "creaturestats.hpp" #include "creaturestats.hpp"
@ -46,12 +48,12 @@ namespace
{ {
// Wraps a value to (-PI, PI] // Wraps a value to (-PI, PI]
void wrap(Ogre::Radian& rad) void wrap(float& rad)
{ {
if (rad.valueRadians()>0) if (rad>0)
rad = Ogre::Radian(std::fmod(rad.valueRadians()+Ogre::Math::PI, 2.0f*Ogre::Math::PI)-Ogre::Math::PI); rad = std::fmod(rad+osg::PI, 2.0f*osg::PI)-osg::PI;
else else
rad = Ogre::Radian(std::fmod(rad.valueRadians()-Ogre::Math::PI, 2.0f*Ogre::Math::PI)+Ogre::Math::PI); rad = std::fmod(rad-osg::PI, 2.0f*osg::PI)+osg::PI;
} }
std::string getBestAttack (const ESM::Weapon* weapon) std::string getBestAttack (const ESM::Weapon* weapon)
@ -2034,50 +2036,61 @@ void CharacterController::setHeadTrackTarget(const MWWorld::Ptr &target)
void CharacterController::updateHeadTracking(float duration) void CharacterController::updateHeadTracking(float duration)
{ {
/* const osg::Node* head = mAnimation->getNode("Bip01 Head");
Ogre::Node* head = mAnimation->getNode("Bip01 Head");
if (!head) if (!head)
return; return;
Ogre::Radian zAngle (0.f);
Ogre::Radian xAngle (0.f); float zAngleRadians = 0.f;
float xAngleRadians = 0.f;
if (!mHeadTrackTarget.isEmpty()) if (!mHeadTrackTarget.isEmpty())
{ {
Ogre::Vector3 headPos = mPtr.getRefData().getBaseNode()->convertLocalToWorldPosition(head->_getDerivedPosition()); osg::MatrixList mats = head->getWorldMatrices();
Ogre::Vector3 targetPos (mHeadTrackTarget.getRefData().getPosition().pos); if (mats.empty())
return;
osg::Matrixf mat = mats[0];
osg::Vec3f headPos = mat.getTrans();
osg::Vec3f targetPos (mHeadTrackTarget.getRefData().getPosition().asVec3());
if (MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mHeadTrackTarget)) if (MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(mHeadTrackTarget))
{ {
Ogre::Node* targetHead = anim->getNode("Head"); const osg::Node* node = anim->getNode("Head");
if (!targetHead) if (node == NULL)
targetHead = anim->getNode("Bip01 Head"); node = anim->getNode("Bip01 Head");
if (targetHead) if (node != NULL)
targetPos = mHeadTrackTarget.getRefData().getBaseNode()->convertLocalToWorldPosition( {
targetHead->_getDerivedPosition()); osg::MatrixList mats = node->getWorldMatrices();
if (mats.size())
targetPos = mats[0].getTrans();
}
} }
Ogre::Vector3 direction = targetPos - headPos; osg::Vec3f direction = targetPos - headPos;
direction.normalise(); direction.normalize();
const Ogre::Vector3 actorDirection = mPtr.getRefData().getBaseNode()->getOrientation().yAxis(); if (!mPtr.getRefData().getBaseNode())
return;
const osg::Vec3f actorDirection = mPtr.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0);
zAngle = Ogre::Math::ATan2(direction.x,direction.y) - zAngleRadians = std::atan2(direction.x(), direction.y()) - std::atan2(actorDirection.x(), actorDirection.y());
Ogre::Math::ATan2(actorDirection.x, actorDirection.y); xAngleRadians = -std::asin(direction.z());
xAngle = -Ogre::Math::ASin(direction.z);
wrap(zAngle);
wrap(xAngle);
xAngle = Ogre::Degree(std::min(xAngle.valueDegrees(), 40.f));
xAngle = Ogre::Degree(std::max(xAngle.valueDegrees(), -40.f));
zAngle = Ogre::Degree(std::min(zAngle.valueDegrees(), 30.f));
zAngle = Ogre::Degree(std::max(zAngle.valueDegrees(), -30.f));
wrap(zAngleRadians);
wrap(xAngleRadians);
xAngleRadians = std::min(xAngleRadians, osg::DegreesToRadians(40.f));
xAngleRadians = std::max(xAngleRadians, osg::DegreesToRadians(-40.f));
zAngleRadians = std::min(zAngleRadians, osg::DegreesToRadians(30.f));
zAngleRadians = std::max(zAngleRadians, osg::DegreesToRadians(-30.f));
} }
float factor = duration*5; float factor = duration*5;
factor = std::min(factor, 1.f); factor = std::min(factor, 1.f);
xAngle = (1.f-factor) * mAnimation->getHeadPitch() + factor * (-xAngle); xAngleRadians = (1.f-factor) * mAnimation->getHeadPitch() + factor * (-xAngleRadians);
zAngle = (1.f-factor) * mAnimation->getHeadYaw() + factor * (-zAngle); zAngleRadians = (1.f-factor) * mAnimation->getHeadYaw() + factor * (-zAngleRadians);
mAnimation->setHeadPitch(xAngle); mAnimation->setHeadPitch(xAngleRadians);
mAnimation->setHeadYaw(zAngle); mAnimation->setHeadYaw(zAngleRadians);
*/
} }
} }

View File

@ -59,8 +59,9 @@ namespace MWPhysics
class MovementSolver class MovementSolver
{ {
private: private:
static float getSlope(const osg::Vec3f &normal) static float getSlope(osg::Vec3f normal)
{ {
normal.normalize();
return osg::RadiansToDegrees(std::acos(normal * osg::Vec3f(0.f, 0.f, 1.f))); return osg::RadiansToDegrees(std::acos(normal * osg::Vec3f(0.f, 0.f, 1.f)));
} }

View File

@ -391,6 +391,11 @@ public:
/// @param effect Controls the radius and intensity of the light. /// @param effect Controls the radius and intensity of the light.
virtual void setLightEffect(float effect) {} virtual void setLightEffect(float effect) {}
virtual void setHeadPitch(float pitchRadians) {}
virtual void setHeadYaw(float yawRadians) {}
virtual float getHeadPitch() const {return 0.f;}
virtual float getHeadYaw() const {return 0.f;}
private: private:
Animation(const Animation&); Animation(const Animation&);
void operator=(Animation&); void operator=(Animation&);

View File

@ -73,34 +73,37 @@ namespace MWRender
{ {
/// @note Assumes that the node being rotated has its "original" orientation set every frame by a different controller. /// @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. /// The rotation is then applied on top of that orientation.
/// @note Must be set on a MatrixTransform. /// @note Must be set on a MatrixTransform.
class RotateController : public osg::NodeCallback class RotateController : public osg::NodeCallback
{ {
public: public:
RotateController(osg::Node* relativeTo, osg::Vec3f axis) RotateController(osg::Node* relativeTo)
: mRotate(0.f) : mEnabled(true)
, mAxis(axis)
, mRelativeTo(relativeTo) , mRelativeTo(relativeTo)
{ {
} }
void setRotate(float rotate) void setEnabled(bool enabled)
{
mEnabled = enabled;
}
void setRotate(const osg::Quat& rotate)
{ {
mRotate = rotate; mRotate = rotate;
} }
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{ {
if (mRotate == 0.f) if (!mEnabled)
return; return;
osg::MatrixTransform* transform = static_cast<osg::MatrixTransform*>(node); osg::MatrixTransform* transform = static_cast<osg::MatrixTransform*>(node);
osg::Matrix matrix = transform->getMatrix(); osg::Matrix matrix = transform->getMatrix();
osg::Quat worldOrient = getWorldOrientation(node); osg::Quat worldOrient = getWorldOrientation(node);
osg::Quat rotate (mRotate, mAxis); osg::Quat orient = worldOrient * mRotate * worldOrient.inverse() * matrix.getRotate();
osg::Quat orient = worldOrient * rotate * worldOrient.inverse() * matrix.getRotate();
matrix.setRotate(orient); matrix.setRotate(orient);
transform->setMatrix(matrix); transform->setMatrix(matrix);
@ -113,7 +116,7 @@ public:
// this could be optimized later, we just need the world orientation, not the full matrix // this could be optimized later, we just need the world orientation, not the full matrix
osg::MatrixList worldMats = node->getWorldMatrices(mRelativeTo); osg::MatrixList worldMats = node->getWorldMatrices(mRelativeTo);
osg::Quat worldOrient; osg::Quat worldOrient;
if (worldMats.size()) if (!worldMats.empty())
{ {
osg::Matrixf worldMat = worldMats[0]; osg::Matrixf worldMat = worldMats[0];
worldOrient = worldMat.getRotate(); worldOrient = worldMat.getRotate();
@ -122,8 +125,8 @@ public:
} }
protected: protected:
float mRotate; bool mEnabled;
osg::Vec3f mAxis; osg::Quat mRotate;
osg::ref_ptr<osg::Node> mRelativeTo; osg::ref_ptr<osg::Node> mRelativeTo;
}; };
@ -134,7 +137,7 @@ class NeckController : public RotateController
{ {
public: public:
NeckController(osg::Node* relativeTo) NeckController(osg::Node* relativeTo)
: RotateController(relativeTo, osg::Vec3f(-1,0,0)) : RotateController(relativeTo)
{ {
} }
@ -149,8 +152,7 @@ public:
osg::Matrix matrix = transform->getMatrix(); osg::Matrix matrix = transform->getMatrix();
osg::Quat worldOrient = getWorldOrientation(node); osg::Quat worldOrient = getWorldOrientation(node);
osg::Quat rotate (mRotate, mAxis); osg::Quat orient = worldOrient * mRotate * worldOrient.inverse() * matrix.getRotate();
osg::Quat orient = worldOrient * rotate * worldOrient.inverse() * matrix.getRotate();
matrix.setRotate(orient); matrix.setRotate(orient);
matrix.setTrans(matrix.getTrans() + worldOrient.inverse() * mOffset); matrix.setTrans(matrix.getTrans() + worldOrient.inverse() * mOffset);
@ -293,9 +295,9 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> par
mNpcType(Type_Normal), mNpcType(Type_Normal),
mVisibilityFlags(visibilityFlags), mVisibilityFlags(visibilityFlags),
mAlpha(1.f), mAlpha(1.f),
mSoundsDisabled(disableSounds) mSoundsDisabled(disableSounds),
//mHeadPitch(0.f), mHeadYawRadians(0.f),
//mHeadYaw(0.f) mHeadPitchRadians(0.f)
{ {
mNpc = mPtr.get<ESM::NPC>()->mBase; mNpc = mPtr.get<ESM::NPC>()->mBase;
@ -695,10 +697,19 @@ osg::Vec3f NpcAnimation::runAnimation(float timepassed)
if (mFirstPersonNeckController) if (mFirstPersonNeckController)
{ {
mFirstPersonNeckController->setRotate(mPtr.getRefData().getPosition().rot[0]); mFirstPersonNeckController->setRotate(osg::Quat(mPtr.getRefData().getPosition().rot[0], osg::Vec3f(-1,0,0)));
mFirstPersonNeckController->setOffset(mFirstPersonOffset); mFirstPersonNeckController->setOffset(mFirstPersonOffset);
} }
if (mHeadController)
{
const float epsilon = 0.001f;
bool enable = (std::abs(mHeadPitchRadians) > epsilon || std::abs(mHeadYawRadians) > epsilon);
mHeadController->setEnabled(enable);
if (enable)
mHeadController->setRotate(osg::Quat(mHeadPitchRadians, osg::Vec3f(1,0,0)) * osg::Quat(mHeadYawRadians, osg::Vec3f(0,0,1)));
}
/* /*
if (mSkelBase) if (mSkelBase)
{ {
@ -707,10 +718,6 @@ osg::Vec3f NpcAnimation::runAnimation(float timepassed)
{ {
// 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);
Ogre::Node* node = baseinst->getBone("Bip01 Head");
if (node)
node->rotate(Ogre::Quaternion(mHeadYaw, Ogre::Vector3::UNIT_Z) * Ogre::Quaternion(mHeadPitch, Ogre::Vector3::UNIT_X), Ogre::Node::TS_WORLD);
} }
} }
*/ */
@ -718,6 +725,26 @@ osg::Vec3f NpcAnimation::runAnimation(float timepassed)
return ret; return ret;
} }
void NpcAnimation::setHeadPitch(float pitchRadians)
{
mHeadPitchRadians = pitchRadians;
}
void NpcAnimation::setHeadYaw(float yawRadians)
{
mHeadYawRadians = yawRadians;
}
float NpcAnimation::getHeadPitch() const
{
return mHeadPitchRadians;
}
float NpcAnimation::getHeadYaw() const
{
return mHeadYawRadians;
}
void NpcAnimation::removeIndividualPart(ESM::PartReferenceType type) void NpcAnimation::removeIndividualPart(ESM::PartReferenceType type)
{ {
mPartPriorities[type] = 0; mPartPriorities[type] = 0;
@ -878,6 +905,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::
void NpcAnimation::addControllers() void NpcAnimation::addControllers()
{ {
mFirstPersonNeckController = NULL; mFirstPersonNeckController = NULL;
mHeadController = NULL;
if (mViewMode == VM_FirstPerson) if (mViewMode == VM_FirstPerson)
{ {
NodeMap::iterator found = mNodeMap.find("bip01 neck"); NodeMap::iterator found = mNodeMap.find("bip01 neck");
@ -889,6 +917,17 @@ void NpcAnimation::addControllers()
mActiveControllers.insert(std::make_pair(node, mFirstPersonNeckController)); mActiveControllers.insert(std::make_pair(node, mFirstPersonNeckController));
} }
} }
else if (mViewMode == VM_Normal)
{
NodeMap::iterator found = mNodeMap.find("bip01 head");
if (found != mNodeMap.end() && dynamic_cast<osg::MatrixTransform*>(found->second.get()))
{
osg::Node* node = found->second;
mHeadController = new RotateController(mObjectRoot.get());
node->addUpdateCallback(mHeadController);
mActiveControllers.insert(std::make_pair(node, mHeadController));
}
}
} }
void NpcAnimation::showWeapons(bool showWeapon) void NpcAnimation::showWeapons(bool showWeapon)

View File

@ -49,6 +49,7 @@ public:
}; };
class NeckController; class NeckController;
class RotateController;
class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
{ {
@ -102,8 +103,8 @@ private:
float mAlpha; float mAlpha;
bool mSoundsDisabled; bool mSoundsDisabled;
//Ogre::Radian mHeadYaw; float mHeadYawRadians;
//Ogre::Radian mHeadPitch; float mHeadPitchRadians;
void updateNpcBase(); void updateNpcBase();
@ -122,6 +123,7 @@ 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; osg::ref_ptr<NeckController> mFirstPersonNeckController;
osg::ref_ptr<RotateController> mHeadController;
protected: protected:
virtual void addControllers(); virtual void addControllers();
@ -153,10 +155,10 @@ public:
/// to indicate the facing orientation of the character. /// to indicate the facing orientation of the character.
virtual void setPitchFactor(float factor) { mPitchFactor = factor; } virtual void setPitchFactor(float factor) { mPitchFactor = factor; }
//virtual void setHeadPitch(Ogre::Radian pitch); virtual void setHeadPitch(float pitchRadians);
//virtual void setHeadYaw(Ogre::Radian yaw); virtual void setHeadYaw(float yawRadians);
//virtual Ogre::Radian getHeadPitch() const; virtual float getHeadPitch() const;
//virtual Ogre::Radian getHeadYaw() const; virtual float getHeadYaw() const;
virtual void showWeapons(bool showWeapon); virtual void showWeapons(bool showWeapon);
virtual void showCarriedLeft(bool show); virtual void showCarriedLeft(bool show);