1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-26 00:35:23 +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 <typeinfo>
#include <OgreVector3.h>
#include <OgreSceneNode.h>
#include <osg/PositionAttitudeTransform>
#include <components/esm/loadnpc.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/manualref.hpp"
@ -20,6 +17,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwrender/animation.hpp"
@ -27,14 +25,9 @@
#include "creaturestats.hpp"
#include "movement.hpp"
#include "character.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "aicombat.hpp"
#include "aifollow.hpp"
#include "aipursue.hpp"
#include "actor.hpp"
#include "summoning.hpp"
#include "combat.hpp"
@ -274,7 +267,6 @@ namespace MWMechanics
void Actors::updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance)
{
/*
static const float fMaxHeadTrackDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fMaxHeadTrackDistance")->getFloat();
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& 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)
return;
@ -294,12 +286,17 @@ namespace MWMechanics
if (targetActor.getClass().getCreatureStats(targetActor).isDead())
return;
if (!actor.getRefData().getBaseNode())
return;
// stop tracking when target is behind the actor
Ogre::Vector3 actorDirection (actor.getRefData().getBaseNode()->getOrientation().yAxis());
Ogre::Vector3 targetDirection (Ogre::Vector3(actor2Pos.pos) - Ogre::Vector3(actor1Pos.pos));
actorDirection.z = 0;
targetDirection.z = 0;
if (actorDirection.angleBetween(targetDirection) < Ogre::Degree(90)
osg::Vec3f actorDirection = actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0);
osg::Vec3f targetDirection (actor2Pos.asVec3() - actor1Pos.asVec3());
actorDirection.z() = 0;
targetDirection.z() = 0;
actorDirection.normalize();
targetDirection.normalize();
if (std::acos(actorDirection * targetDirection) < osg::DegreesToRadians(90.f)
&& sqrDist <= sqrHeadTrackDistance
&& 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))
@ -307,7 +304,6 @@ namespace MWMechanics
sqrHeadTrackDistance = sqrDist;
headTrackTarget = targetActor;
}
*/
}
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer)

View File

@ -21,6 +21,8 @@
#include <OgreStringConverter.h>
#include <osg/PositionAttitudeTransform>
#include "movement.hpp"
#include "npcstats.hpp"
#include "creaturestats.hpp"
@ -46,12 +48,12 @@ namespace
{
// Wraps a value to (-PI, PI]
void wrap(Ogre::Radian& rad)
void wrap(float& rad)
{
if (rad.valueRadians()>0)
rad = Ogre::Radian(std::fmod(rad.valueRadians()+Ogre::Math::PI, 2.0f*Ogre::Math::PI)-Ogre::Math::PI);
if (rad>0)
rad = std::fmod(rad+osg::PI, 2.0f*osg::PI)-osg::PI;
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)
@ -2034,50 +2036,61 @@ void CharacterController::setHeadTrackTarget(const MWWorld::Ptr &target)
void CharacterController::updateHeadTracking(float duration)
{
/*
Ogre::Node* head = mAnimation->getNode("Bip01 Head");
const osg::Node* head = mAnimation->getNode("Bip01 Head");
if (!head)
return;
Ogre::Radian zAngle (0.f);
Ogre::Radian xAngle (0.f);
float zAngleRadians = 0.f;
float xAngleRadians = 0.f;
if (!mHeadTrackTarget.isEmpty())
{
Ogre::Vector3 headPos = mPtr.getRefData().getBaseNode()->convertLocalToWorldPosition(head->_getDerivedPosition());
Ogre::Vector3 targetPos (mHeadTrackTarget.getRefData().getPosition().pos);
osg::MatrixList mats = head->getWorldMatrices();
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))
{
Ogre::Node* targetHead = anim->getNode("Head");
if (!targetHead)
targetHead = anim->getNode("Bip01 Head");
if (targetHead)
targetPos = mHeadTrackTarget.getRefData().getBaseNode()->convertLocalToWorldPosition(
targetHead->_getDerivedPosition());
const osg::Node* node = anim->getNode("Head");
if (node == NULL)
node = anim->getNode("Bip01 Head");
if (node != NULL)
{
osg::MatrixList mats = node->getWorldMatrices();
if (mats.size())
targetPos = mats[0].getTrans();
}
}
Ogre::Vector3 direction = targetPos - headPos;
direction.normalise();
osg::Vec3f direction = targetPos - headPos;
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) -
Ogre::Math::ATan2(actorDirection.x, actorDirection.y);
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));
zAngleRadians = std::atan2(direction.x(), direction.y()) - std::atan2(actorDirection.x(), actorDirection.y());
xAngleRadians = -std::asin(direction.z());
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;
factor = std::min(factor, 1.f);
xAngle = (1.f-factor) * mAnimation->getHeadPitch() + factor * (-xAngle);
zAngle = (1.f-factor) * mAnimation->getHeadYaw() + factor * (-zAngle);
xAngleRadians = (1.f-factor) * mAnimation->getHeadPitch() + factor * (-xAngleRadians);
zAngleRadians = (1.f-factor) * mAnimation->getHeadYaw() + factor * (-zAngleRadians);
mAnimation->setHeadPitch(xAngle);
mAnimation->setHeadYaw(zAngle);
*/
mAnimation->setHeadPitch(xAngleRadians);
mAnimation->setHeadYaw(zAngleRadians);
}
}

View File

@ -59,8 +59,9 @@ namespace MWPhysics
class MovementSolver
{
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)));
}

View File

@ -391,6 +391,11 @@ public:
/// @param effect Controls the radius and intensity of the light.
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:
Animation(const 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.
/// 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.
class RotateController : public osg::NodeCallback
{
public:
RotateController(osg::Node* relativeTo, osg::Vec3f axis)
: mRotate(0.f)
, mAxis(axis)
RotateController(osg::Node* relativeTo)
: mEnabled(true)
, mRelativeTo(relativeTo)
{
}
void setRotate(float rotate)
void setEnabled(bool enabled)
{
mEnabled = enabled;
}
void setRotate(const osg::Quat& rotate)
{
mRotate = rotate;
}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (mRotate == 0.f)
if (!mEnabled)
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();
osg::Quat orient = worldOrient * mRotate * worldOrient.inverse() * matrix.getRotate();
matrix.setRotate(orient);
transform->setMatrix(matrix);
@ -113,7 +116,7 @@ public:
// 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())
if (!worldMats.empty())
{
osg::Matrixf worldMat = worldMats[0];
worldOrient = worldMat.getRotate();
@ -122,8 +125,8 @@ public:
}
protected:
float mRotate;
osg::Vec3f mAxis;
bool mEnabled;
osg::Quat mRotate;
osg::ref_ptr<osg::Node> mRelativeTo;
};
@ -134,7 +137,7 @@ class NeckController : public RotateController
{
public:
NeckController(osg::Node* relativeTo)
: RotateController(relativeTo, osg::Vec3f(-1,0,0))
: RotateController(relativeTo)
{
}
@ -149,8 +152,7 @@ public:
osg::Matrix matrix = transform->getMatrix();
osg::Quat worldOrient = getWorldOrientation(node);
osg::Quat rotate (mRotate, mAxis);
osg::Quat orient = worldOrient * rotate * worldOrient.inverse() * matrix.getRotate();
osg::Quat orient = worldOrient * mRotate * worldOrient.inverse() * matrix.getRotate();
matrix.setRotate(orient);
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),
mVisibilityFlags(visibilityFlags),
mAlpha(1.f),
mSoundsDisabled(disableSounds)
//mHeadPitch(0.f),
//mHeadYaw(0.f)
mSoundsDisabled(disableSounds),
mHeadYawRadians(0.f),
mHeadPitchRadians(0.f)
{
mNpc = mPtr.get<ESM::NPC>()->mBase;
@ -695,10 +697,19 @@ osg::Vec3f NpcAnimation::runAnimation(float timepassed)
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);
}
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)
{
@ -707,10 +718,6 @@ osg::Vec3f NpcAnimation::runAnimation(float timepassed)
{
// In third person mode we may still need pitch for ranged weapon targeting
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;
}
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)
{
mPartPriorities[type] = 0;
@ -878,6 +905,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector<ESM::
void NpcAnimation::addControllers()
{
mFirstPersonNeckController = NULL;
mHeadController = NULL;
if (mViewMode == VM_FirstPerson)
{
NodeMap::iterator found = mNodeMap.find("bip01 neck");
@ -889,6 +917,17 @@ void NpcAnimation::addControllers()
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)

View File

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