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:
parent
60f55997fd
commit
71bafcb52b
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
@ -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&);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user