diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 2193f4ca0..3cf43749d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -20,7 +20,7 @@ set(GAME_HEADER source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender - actors objects renderingmanager animation sky npcanimation vismask + actors objects renderingmanager animation rotatecontroller sky npcanimation vismask creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation bulletdebugdraw globalmap characterpreview camera localmap # occlusionquery water shadows diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index b98b26179..0dd647c9c 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -171,14 +171,16 @@ Resource::ResourceSystem *CreatureWeaponAnimation::getResourceSystem() return mResourceSystem; } +void CreatureWeaponAnimation::addControllers() +{ + WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get()); +} + osg::Vec3f CreatureWeaponAnimation::runAnimation(float duration) { osg::Vec3f ret = Animation::runAnimation(duration); - /* - if (mSkelBase) - pitchSkeleton(mPtr.getRefData().getPosition().rot[0], mSkelBase->getSkeleton()); - */ + WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0]); return ret; } diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index 61d077df3..8a4ebb930 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -46,11 +46,13 @@ namespace MWRender virtual void showWeapon(bool show) { showWeapons(show); } virtual void setWeaponGroup(const std::string& group) { mWeaponAnimationTime->setGroup(group); } + virtual void addControllers(); + virtual osg::Vec3f runAnimation(float duration); /// A relative factor (0-1) that decides if and how much the skeleton should be pitched /// to indicate the facing orientation of the character. - //virtual void setPitchFactor(float factor) { mPitchFactor = factor; } + virtual void setPitchFactor(float factor) { mPitchFactor = factor; } private: diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 92e61801e..7283a621a 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -26,6 +26,7 @@ #include "../mwbase/soundmanager.hpp" #include "camera.hpp" +#include "rotatecontroller.hpp" namespace { @@ -72,64 +73,6 @@ 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 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) - : mEnabled(true) - , mRelativeTo(relativeTo) - { - - } - - void setEnabled(bool enabled) - { - mEnabled = enabled; - } - - void setRotate(const osg::Quat& rotate) - { - mRotate = rotate; - } - - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) - { - if (!mEnabled) - return; - osg::MatrixTransform* transform = static_cast(node); - osg::Matrix matrix = transform->getMatrix(); - osg::Quat worldOrient = getWorldOrientation(node); - - osg::Quat orient = worldOrient * mRotate * 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.empty()) - { - osg::Matrixf worldMat = worldMats[0]; - worldOrient = worldMat.getRotate(); - } - return worldOrient; - } - -protected: - bool mEnabled; - osg::Quat mRotate; - osg::ref_ptr 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. @@ -710,17 +653,7 @@ osg::Vec3f NpcAnimation::runAnimation(float timepassed) mHeadController->setRotate(osg::Quat(mHeadPitchRadians, osg::Vec3f(1,0,0)) * osg::Quat(mHeadYawRadians, osg::Vec3f(0,0,1))); } - /* - if (mSkelBase) - { - Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); - if(mViewMode != VM_FirstPerson) - { - // In third person mode we may still need pitch for ranged weapon targeting - pitchSkeleton(mPtr.getRefData().getPosition().rot[0], baseinst); - } - } - */ + WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0]); return ret; } @@ -906,6 +839,8 @@ void NpcAnimation::addControllers() { mFirstPersonNeckController = NULL; mHeadController = NULL; + WeaponAnimation::deleteControllers(); + if (mViewMode == VM_FirstPerson) { NodeMap::iterator found = mNodeMap.find("bip01 neck"); @@ -927,6 +862,8 @@ void NpcAnimation::addControllers() node->addUpdateCallback(mHeadController); mActiveControllers.insert(std::make_pair(node, mHeadController)); } + + WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get()); } } diff --git a/apps/openmw/mwrender/rotatecontroller.cpp b/apps/openmw/mwrender/rotatecontroller.cpp new file mode 100644 index 000000000..11f5b943d --- /dev/null +++ b/apps/openmw/mwrender/rotatecontroller.cpp @@ -0,0 +1,57 @@ +#include "rotatecontroller.hpp" + +#include + +namespace MWRender +{ + +RotateController::RotateController(osg::Node *relativeTo) + : mEnabled(true) + , mRelativeTo(relativeTo) +{ + +} + +void RotateController::setEnabled(bool enabled) +{ + mEnabled = enabled; +} + +void RotateController::setRotate(const osg::Quat &rotate) +{ + mRotate = rotate; +} + +void RotateController::operator()(osg::Node *node, osg::NodeVisitor *nv) +{ + if (!mEnabled) + { + traverse(node, nv); + return; + } + osg::MatrixTransform* transform = static_cast(node); + osg::Matrix matrix = transform->getMatrix(); + osg::Quat worldOrient = getWorldOrientation(node); + + osg::Quat orient = worldOrient * mRotate * worldOrient.inverse() * matrix.getRotate(); + matrix.setRotate(orient); + + transform->setMatrix(matrix); + + traverse(node,nv); +} + +osg::Quat RotateController::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.empty()) + { + osg::Matrixf worldMat = worldMats[0]; + worldOrient = worldMat.getRotate(); + } + return worldOrient; +} + +} diff --git a/apps/openmw/mwrender/rotatecontroller.hpp b/apps/openmw/mwrender/rotatecontroller.hpp new file mode 100644 index 000000000..8c3758cb0 --- /dev/null +++ b/apps/openmw/mwrender/rotatecontroller.hpp @@ -0,0 +1,36 @@ +#ifndef OPENMW_MWRENDER_ROTATECONTROLLER_H +#define OPENMW_MWRENDER_ROTATECONTROLLER_H + +#include +#include + +namespace MWRender +{ + +/// Applies a rotation in \a relativeTo's space. +/// @note Assumes that the node being rotated has its "original" orientation set every frame by a different controller. +/// 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); + + void setEnabled(bool enabled); + + void setRotate(const osg::Quat& rotate); + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + +protected: + osg::Quat getWorldOrientation(osg::Node* node); + + bool mEnabled; + osg::Quat mRotate; + osg::ref_ptr mRelativeTo; +}; + + +} + +#endif diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp index 23a74fb95..301779c1e 100644 --- a/apps/openmw/mwrender/weaponanimation.cpp +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -1,5 +1,7 @@ #include "weaponanimation.hpp" +#include + #include #include @@ -15,6 +17,7 @@ #include "../mwmechanics/combat.hpp" #include "animation.hpp" +#include "rotatecontroller.hpp" namespace MWRender { @@ -46,6 +49,11 @@ WeaponAnimation::WeaponAnimation() { } +WeaponAnimation::~WeaponAnimation() +{ + +} + void WeaponAnimation::attachArrow(MWWorld::Ptr actor) { MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); @@ -149,28 +157,49 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor) } } -/* -void WeaponAnimation::pitchSkeleton(float xrot, Ogre::SkeletonInstance* skel) +void WeaponAnimation::addControllers(const std::map >& nodes, + std::multimap, osg::ref_ptr > &map, osg::Node* objectRoot) { - if (mPitchFactor == 0) - return; + for (int i=0; i<2; ++i) + { + mSpineControllers[i] = NULL; - float pitch = xrot * mPitchFactor; - Ogre::Node *node; + std::map >::const_iterator found = nodes.find(i == 0 ? "bip01 spine1" : "bip01 spine2"); + if (found != nodes.end()) + { + osg::Node* node = found->second; + mSpineControllers[i] = new RotateController(objectRoot); + node->addUpdateCallback(mSpineControllers[i]); + map.insert(std::make_pair(node, mSpineControllers[i])); + } + } +} - // In spherearcher.nif, we have spine, not Spine. Not sure if all bone names should be case insensitive? - if (skel->hasBone("Bip01 spine2")) - node = skel->getBone("Bip01 spine2"); - else - node = skel->getBone("Bip01 Spine2"); - node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); +void WeaponAnimation::deleteControllers() +{ + for (int i=0; i<2; ++i) + mSpineControllers[i] = NULL; +} - if (skel->hasBone("Bip01 spine1")) // in spherearcher.nif - node = skel->getBone("Bip01 spine1"); - else - node = skel->getBone("Bip01 Spine1"); - node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); +void WeaponAnimation::configureControllers(float characterPitchRadians) +{ + if (!mSpineControllers[0]) + return; + + if (mPitchFactor == 0.f || characterPitchRadians == 0.f) + { + for (int i=0; i<2; ++i) + mSpineControllers[i]->setEnabled(false); + return; + } + + float pitch = characterPitchRadians * mPitchFactor; + osg::Quat rotate (pitch/2, osg::Vec3f(-1,0,0)); + for (int i=0; i<2; ++i) + { + mSpineControllers[i]->setRotate(rotate); + mSpineControllers[i]->setEnabled(true); + } } -*/ } diff --git a/apps/openmw/mwrender/weaponanimation.hpp b/apps/openmw/mwrender/weaponanimation.hpp index f46638ac8..9336afd4c 100644 --- a/apps/openmw/mwrender/weaponanimation.hpp +++ b/apps/openmw/mwrender/weaponanimation.hpp @@ -9,6 +9,8 @@ namespace MWRender { + class RotateController; + class WeaponAnimationTime : public SceneUtil::ControllerSource { private: @@ -28,7 +30,7 @@ namespace MWRender { public: WeaponAnimation(); - virtual ~WeaponAnimation() {} + virtual ~WeaponAnimation(); /// @note If no weapon (or an invalid weapon) is equipped, this function is a no-op. void attachArrow(MWWorld::Ptr actor); @@ -36,9 +38,20 @@ namespace MWRender /// @note If no weapon (or an invalid weapon) is equipped, this function is a no-op. void releaseArrow(MWWorld::Ptr actor); + /// Add WeaponAnimation-related controllers to \a nodes and store the added controllers in \a map. + void addControllers(const std::map >& nodes, + std::multimap, osg::ref_ptr >& map, osg::Node* objectRoot); + + void deleteControllers(); + + /// Configure controllers, should be called every animation frame. + void configureControllers(float characterPitchRadians); + protected: PartHolderPtr mAmmunition; + osg::ref_ptr mSpineControllers[2]; + virtual osg::Group* getArrowBone() = 0; virtual osg::Node* getWeaponNode() = 0; virtual Resource::ResourceSystem* getResourceSystem() = 0;