diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 3333511d3f..84e97487a2 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1,15 +1,12 @@ - #include "actors.hpp" #include -#include -#include +#include #include #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() .find("fMaxHeadTrackDistance")->getFloat(); static const float fInteriorHeadTrackMult = MWBase::Environment::get().getWorld()->getStore().get() @@ -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) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 5765d8b04d..354ca998c0 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -21,6 +21,8 @@ #include +#include + #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); } } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 8e63fecfbe..d36aded241 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -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))); } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index e30f2b0828..bd864c9361 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -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&); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 598e2fba9c..92e61801e9 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -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(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 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 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()->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(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) diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 28ae43144b..db0b4ea0ce 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -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 mFirstPersonNeckController; + osg::ref_ptr 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);