From 2db5df77f030ef053a008ea682be6e950a106474 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 22 Mar 2015 22:52:44 +0100 Subject: [PATCH] Change rotation/scale workaround in preparation for loading .kf controllers --- components/CMakeLists.txt | 2 +- components/nif/niftypes.hpp | 2 +- components/nifosg/controller.cpp | 40 +++++++++++++++++++------- components/nifosg/controller.hpp | 7 ++--- components/nifosg/nifloader.cpp | 17 +++++++---- components/nifosg/particle.cpp | 24 ++++++++++++++++ components/nifosg/particle.hpp | 33 +++------------------ components/nifosg/userdata.hpp | 49 ++++++++++++++++++++++++++++++++ 8 files changed, 121 insertions(+), 53 deletions(-) create mode 100644 components/nifosg/userdata.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index a78f164a6..e0ddeb254 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -43,7 +43,7 @@ add_component_dir (nif ) add_component_dir (nifosg - nifloader controller particle + nifloader controller particle userdata ) #add_component_dir (nifcache diff --git a/components/nif/niftypes.hpp b/components/nif/niftypes.hpp index 4f6998200..7fefb92a2 100644 --- a/components/nif/niftypes.hpp +++ b/components/nif/niftypes.hpp @@ -46,7 +46,7 @@ struct Matrix3 struct Transformation { osg::Vec3f pos; - Matrix3 rotation; + Matrix3 rotation; // this can contain scale components too, including negative and nonuniform scales float scale; static const Transformation& getIdentity() diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 01671dbd8..d61a8937a 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -4,15 +4,17 @@ #include #include #include +#include +#include #include #include -#include - #include +#include "userdata.hpp" + namespace NifOsg { @@ -76,13 +78,10 @@ KeyframeController::KeyframeController(const KeyframeController ©, const osg , mTranslations(copy.mTranslations) , mScales(copy.mScales) , mNif(copy.mNif) - , mInitialQuat(copy.mInitialQuat) - , mInitialScale(copy.mInitialScale) { } -KeyframeController::KeyframeController(const Nif::NIFFilePtr &nif, const Nif::NiKeyframeData *data, - osg::Quat initialQuat, float initialScale) +KeyframeController::KeyframeController(const Nif::NIFFilePtr &nif, const Nif::NiKeyframeData *data) : mRotations(&data->mRotations) , mXRotations(&data->mXRotations) , mYRotations(&data->mYRotations) @@ -90,9 +89,8 @@ KeyframeController::KeyframeController(const Nif::NIFFilePtr &nif, const Nif::Ni , mTranslations(&data->mTranslations) , mScales(&data->mScales) , mNif(nif) - , mInitialQuat(initialQuat) - , mInitialScale(initialScale) -{ } +{ +} osg::Quat KeyframeController::interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time) { @@ -154,15 +152,35 @@ void KeyframeController::operator() (osg::Node* node, osg::NodeVisitor* nv) float time = getInputValue(nv); + NodeUserData* userdata = static_cast(trans->getUserDataContainer()->getUserObject(0)); + Nif::Matrix3& rot = userdata->mRotationScale; + + bool setRot = false; if(mRotations->mKeys.size() > 0) + { mat.setRotate(interpKey(mRotations->mKeys, time)); + setRot = true; + } else if (!mXRotations->mKeys.empty() || !mYRotations->mKeys.empty() || !mZRotations->mKeys.empty()) + { mat.setRotate(getXYZRotation(time)); + setRot = true; + } else - mat.setRotate(mInitialQuat); + { + // no rotation specified, use the previous value from the UserData + for (int i=0;i<3;++i) + for (int j=0;j<3;++j) + mat(j,i) = rot.mValues[i][j]; // NB column/row major difference + } + + if (setRot) // copy the new values back to the UserData + for (int i=0;i<3;++i) + for (int j=0;j<3;++j) + rot.mValues[i][j] = mat(j,i); // NB column/row major difference // let's hope no one's using multiple KeyframeControllers on the same node (not that would make any sense...) - float scale = mInitialScale; + float& scale = userdata->mScale; if(mScales->mKeys.size() > 0) scale = interpKey(mScales->mKeys, time); diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 5a9af6324..fb24fb518 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -147,11 +147,9 @@ namespace NifOsg const Nif::FloatKeyMap* mZRotations; const Nif::Vector3KeyMap* mTranslations; const Nif::FloatKeyMap* mScales; + // TODO: we don't need to keep the whole NIF around, just change the key maps to Referenced Nif::NIFFilePtr mNif; // Hold a SharedPtr to make sure key lists stay valid - osg::Quat mInitialQuat; - float mInitialScale; - using ValueInterpolator::interpKey; @@ -161,8 +159,7 @@ namespace NifOsg public: /// @note The NiKeyFrameData must be valid as long as this KeyframeController exists. - KeyframeController(const Nif::NIFFilePtr& nif, const Nif::NiKeyframeData *data, - osg::Quat initialQuat, float initialScale); + KeyframeController(const Nif::NIFFilePtr& nif, const Nif::NiKeyframeData *data); KeyframeController(); KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 27cce16fd..edc011c54 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -42,6 +42,7 @@ #include #include "particle.hpp" +#include "userdata.hpp" namespace { @@ -191,12 +192,13 @@ namespace { if (_referenceFrame==RELATIVE_RF) { + const NifOsg::NodeUserData* userdata = static_cast(getUserDataContainer()->getUserObject(0)); + matrix.preMult(_matrix); - osg::Vec3 scale = matrix.getScale(); matrix.setRotate(osg::Quat()); - matrix(0,0) = scale.x(); - matrix(1,1) = scale.y(); - matrix(2,2) = scale.z(); + matrix(0,0) = userdata->mScale; + matrix(1,1) = userdata->mScale; + matrix(2,2) = userdata->mScale; } else // absolute { @@ -365,7 +367,10 @@ namespace NifOsg // - finding the correct emitter node for a particle system // - establishing connections to the animated collision shapes, which are handled in a separate loader // - finding a random child NiNode in NiBspArrayController - transformNode->setUserData(new RecIndexHolder(nifNode->recIndex)); + // - storing the previous 3x3 rotation and scale values for when a KeyframeController wants to + // change only certain elements of the 4x4 transform + transformNode->getOrCreateUserDataContainer()->addUserObject( + new NodeUserData(nifNode->recIndex, nifNode->trafo.scale, nifNode->trafo.rotation)); if (nifNode->recType == Nif::RC_NiBSAnimationNode) animflags |= nifNode->flags; @@ -471,7 +476,7 @@ namespace NifOsg for (int j=0;j<3;++j) mat(j,i) = nifNode->trafo.rotation.mValues[i][j]; - osg::ref_ptr callback(new KeyframeController(mNif, key->data.getPtr(), mat.getRotate(), nifNode->trafo.scale)); + osg::ref_ptr callback(new KeyframeController(mNif, key->data.getPtr())); setupController(key, callback, animflags); transformNode->addUpdateCallback(callback); diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 934ef51eb..16c2b692c 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -8,6 +8,8 @@ #include +#include "userdata.hpp" + namespace NifOsg { @@ -279,4 +281,26 @@ void Emitter::emitParticles(double dt) } } +FindRecIndexVisitor::FindRecIndexVisitor(int recIndex) + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mFound(NULL) + , mRecIndex(recIndex) +{ +} + +void FindRecIndexVisitor::apply(osg::Node &searchNode) +{ + if (searchNode.getUserDataContainer() && searchNode.getUserDataContainer()->getNumUserObjects()) + { + NodeUserData* holder = dynamic_cast(searchNode.getUserDataContainer()->getUserObject(0)); + if (holder && holder->mIndex == mRecIndex) + { + mFound = static_cast(&searchNode); + mFoundPath = getNodePath(); + return; + } + } + traverse(searchNode); +} + } diff --git a/components/nifosg/particle.hpp b/components/nifosg/particle.hpp index 1c8174e8a..01e06f471 100644 --- a/components/nifosg/particle.hpp +++ b/components/nifosg/particle.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -152,39 +153,13 @@ namespace NifOsg osg::Vec3f mCachedWorldPositionDirection; }; - - class RecIndexHolder : public osg::Referenced - { - public: - RecIndexHolder(int index) : mIndex(index) {} - int mIndex; - }; - - // NodeVisitor to find a child node with the given record index, stored in the node's user data. + // NodeVisitor to find a child node with the given record index, stored in the node's user data container. class FindRecIndexVisitor : public osg::NodeVisitor { public: - FindRecIndexVisitor(int recIndex) - : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) - , mFound(NULL) - , mRecIndex(recIndex) - { - } + FindRecIndexVisitor(int recIndex); - virtual void apply(osg::Node &searchNode) - { - if (searchNode.getUserData()) - { - RecIndexHolder* holder = static_cast(searchNode.getUserData()); - if (holder->mIndex == mRecIndex) - { - mFound = static_cast(&searchNode); - mFoundPath = getNodePath(); - return; - } - } - traverse(searchNode); - } + virtual void apply(osg::Node &searchNode); osg::Group* mFound; osg::NodePath mFoundPath; diff --git a/components/nifosg/userdata.hpp b/components/nifosg/userdata.hpp new file mode 100644 index 000000000..9770890b0 --- /dev/null +++ b/components/nifosg/userdata.hpp @@ -0,0 +1,49 @@ +#ifndef OPENMW_COMPONENTS_NIFOSG_USERDATA_H +#define OPENMW_COMPONENTS_NIFOSG_USERDATA_H + +#include + +#include + +namespace NifOsg +{ + + // Note if you are copying a scene graph with this user data you should use the DEEP_COPY_USERDATA copyop. + class NodeUserData : public osg::Object + { + public: + NodeUserData(int index, float scale, const Nif::Matrix3& rotationScale) + : mIndex(index), mScale(scale), mRotationScale(rotationScale) + { + } + NodeUserData() + : mIndex(0), mScale(0) + { + } + NodeUserData(const NodeUserData& copy, const osg::CopyOp& copyop) + : Object(copy, copyop) + , mIndex(copy.mIndex) + , mScale(copy.mScale) + , mRotationScale(copy.mRotationScale) + { + } + + META_Object(NifOsg, NodeUserData) + + // NIF record index + int mIndex; + + // Hack: account for Transform differences between OSG and NIFs. + // OSG uses a 4x4 matrix, NIF's use a 3x3 rotationScale, float scale, and vec3 position. + // Decomposing the original components from the 4x4 matrix isn't possible, which causes + // problems when a KeyframeController wants to change only one of these components. So + // we store the scale and rotation components separately here. + // Note for a cleaner solution it would be possible to write a custom Transform node, + // but then we have to fork osgAnimation :/ + float mScale; + Nif::Matrix3 mRotationScale; + }; + +} + +#endif