diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 86d657792e..4627ea2f09 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -59,7 +59,7 @@ add_component_dir (nif ) add_component_dir (nifosg - nifloader controller particle userdata + nifloader controller particle matrixtransform ) add_component_dir (nifbullet diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 3c95394a68..203951eddc 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -4,14 +4,13 @@ #include #include #include -#include #include #include #include -#include "userdata.hpp" +#include "matrixtransform.hpp" namespace NifOsg { @@ -119,50 +118,24 @@ void KeyframeController::operator() (osg::Node* node, osg::NodeVisitor* nv) { if (hasInput()) { - osg::MatrixTransform* trans = static_cast(node); - osg::Matrix mat = trans->getMatrix(); + NifOsg::MatrixTransform* trans = static_cast(node); float time = getInputValue(nv); - NodeUserData* userdata = static_cast(trans->getUserDataContainer()->getUserObject(0)); - Nif::Matrix3& rot = userdata->mRotationScale; - - bool setRot = false; - if(!mRotations.empty()) - { - mat.setRotate(mRotations.interpKey(time)); - setRot = true; - } + if (!mRotations.empty()) + trans->updateRotation(mRotations.interpKey(time)); else if (!mXRotations.empty() || !mYRotations.empty() || !mZRotations.empty()) - { - mat.setRotate(getXYZRotation(time)); - setRot = true; - } - else - { - // 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 - } + trans->updateRotation(getXYZRotation(time)); + else // no rotation specified, use the previous value + trans->applyCurrentRotation(); - 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 + if (!mScales.empty()) + trans->updateScale(mScales.interpKey(time)); + else // no scale specified, use the previous value + trans->applyCurrentScale(); - float& scale = userdata->mScale; - if(!mScales.empty()) - scale = mScales.interpKey(time); - - for (int i=0;i<3;++i) - for (int j=0;j<3;++j) - mat(i,j) *= scale; - - if(!mTranslations.empty()) - mat.setTrans(mTranslations.interpKey(time)); - - trans->setMatrix(mat); + if (!mTranslations.empty()) + trans->setTranslation(mTranslations.interpKey(time)); } traverse(node, nv); diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index c81f97a714..df1086f56f 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -9,11 +9,9 @@ #include #include -#include //UVController +#include -// FlipController #include -#include #include #include @@ -22,8 +20,6 @@ namespace osg { - class Node; - class StateSet; class Material; } diff --git a/components/nifosg/matrixtransform.cpp b/components/nifosg/matrixtransform.cpp new file mode 100644 index 0000000000..72e12ecf8e --- /dev/null +++ b/components/nifosg/matrixtransform.cpp @@ -0,0 +1,56 @@ +#include "matrixtransform.hpp" + +namespace NifOsg +{ + MatrixTransform::MatrixTransform() + : osg::MatrixTransform() + { + } + + MatrixTransform::MatrixTransform(const Nif::Transformation &trafo) + : osg::MatrixTransform(trafo.toMatrix()) + , mScale(trafo.scale) + , mRotationScale(trafo.rotation) + { + } + + MatrixTransform::MatrixTransform(const MatrixTransform ©, const osg::CopyOp ©op) + : osg::MatrixTransform(copy, copyop) + , mScale(copy.mScale) + , mRotationScale(copy.mRotationScale) + { + } + + void MatrixTransform::applyCurrentRotation() + { + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + _matrix(j,i) = mRotationScale.mValues[i][j]; // NB column/row major difference + } + + void MatrixTransform::applyCurrentScale() + { + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + _matrix(i,j) *= mScale; + } + + void MatrixTransform::updateRotation(const osg::Quat& rotation) + { + _matrix.setRotate(rotation); + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + mRotationScale.mValues[i][j] = _matrix(j,i); // NB column/row major difference + } + + void MatrixTransform::updateScale(const float scale) + { + mScale = scale; + applyCurrentScale(); + } + + void MatrixTransform::setTranslation(const osg::Vec3f& translation) + { + _matrix.setTrans(translation); + } +} diff --git a/components/nifosg/matrixtransform.hpp b/components/nifosg/matrixtransform.hpp new file mode 100644 index 0000000000..ac2fbb57a6 --- /dev/null +++ b/components/nifosg/matrixtransform.hpp @@ -0,0 +1,44 @@ +#ifndef OPENMW_COMPONENTS_NIFOSG_MATRIXTRANSFORM_H +#define OPENMW_COMPONENTS_NIFOSG_MATRIXTRANSFORM_H + +#include + +#include + +namespace NifOsg +{ + + class MatrixTransform : public osg::MatrixTransform + { + public: + MatrixTransform(); + MatrixTransform(const Nif::Transformation &trafo); + MatrixTransform(const MatrixTransform ©, const osg::CopyOp ©op); + + META_Node(NifOsg, MatrixTransform) + + // Apply the current NIF rotation or scale to OSG matrix. + void applyCurrentRotation(); + void applyCurrentScale(); + + // Apply the given rotation to OSG matrix directly and update NIF rotation matrix. + void updateRotation(const osg::Quat& rotation); + // Update current NIF scale and apply it to OSG matrix. + void updateScale(const float scale); + + // Apply the given translation to OSG matrix. + void setTranslation(const osg::Vec3f& translation); + + private: + // 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. + float mScale{0.f}; + Nif::Matrix3 mRotationScale; + }; + +} + +#endif diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 8266b5e016..f88800e364 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -43,8 +42,9 @@ #include #include +#include "matrixtransform.hpp" +#include "nodeindexholder.hpp" #include "particle.hpp" -#include "userdata.hpp" namespace { @@ -170,31 +170,6 @@ namespace namespace NifOsg { - class CollisionSwitch : public osg::MatrixTransform - { - public: - CollisionSwitch() : osg::MatrixTransform() - { - } - - CollisionSwitch(const CollisionSwitch& copy, const osg::CopyOp& copyop) - : osg::MatrixTransform(copy, copyop) - { - } - - META_Node(NifOsg, CollisionSwitch) - - CollisionSwitch(const osg::Matrixf& transformations, bool enabled) : osg::MatrixTransform(transformations) - { - setEnabled(enabled); - } - - void setEnabled(bool enabled) - { - setNodeMask(enabled ? ~0 : Loader::getIntersectionDisabledNodeMask()); - } - }; - bool Loader::sShowMarkers = false; void Loader::setShowMarkers(bool show) @@ -501,14 +476,6 @@ namespace NifOsg case Nif::RC_NiBillboardNode: dataVariance = osg::Object::DYNAMIC; break; - case Nif::RC_NiCollisionSwitch: - { - bool enabled = nifNode->flags & Nif::NiNode::Flag_ActiveCollision; - node = new CollisionSwitch(nifNode->trafo.toMatrix(), enabled); - // This matrix transform must not be combined with another matrix transform. - dataVariance = osg::Object::DYNAMIC; - break; - } default: // The Root node can be created as a Group if no transformation is required. // This takes advantage of the fact root nodes can't have additional controllers @@ -521,7 +488,14 @@ namespace NifOsg break; } if (!node) - node = new osg::MatrixTransform(nifNode->trafo.toMatrix()); + node = new NifOsg::MatrixTransform(nifNode->trafo); + + if (nifNode->recType == Nif::RC_NiCollisionSwitch && !(nifNode->flags & Nif::NiNode::Flag_ActiveCollision)) + { + node->setNodeMask(Loader::getIntersectionDisabledNodeMask()); + // This node must not be combined with another node. + dataVariance = osg::Object::DYNAMIC; + } node->setDataVariance(dataVariance); @@ -549,14 +523,11 @@ namespace NifOsg if (!rootNode) rootNode = node; - // UserData used for a variety of features: + // The original NIF record index is used for a variety of features: // - 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 - // - storing the previous 3x3 rotation and scale values for when a KeyframeController wants to - // change only certain elements of the 4x4 transform - node->getOrCreateUserDataContainer()->addUserObject( - new NodeUserData(nifNode->recIndex, nifNode->trafo.scale, nifNode->trafo.rotation)); + node->getOrCreateUserDataContainer()->addUserObject(new NodeIndexHolder(nifNode->recIndex)); for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->next) { diff --git a/components/nifosg/nodeindexholder.hpp b/components/nifosg/nodeindexholder.hpp new file mode 100644 index 0000000000..e7d4f0db31 --- /dev/null +++ b/components/nifosg/nodeindexholder.hpp @@ -0,0 +1,35 @@ +#ifndef OPENMW_COMPONENTS_NIFOSG_NODEINDEXHOLDER_H +#define OPENMW_COMPONENTS_NIFOSG_NODEINDEXHOLDER_H + +#include + +namespace NifOsg +{ + + class NodeIndexHolder : public osg::Object + { + public: + NodeIndexHolder() = default; + NodeIndexHolder(int index) + : mIndex(index) + { + } + NodeIndexHolder(const NodeIndexHolder& copy, const osg::CopyOp& copyop) + : Object(copy, copyop) + , mIndex(copy.mIndex) + { + } + + META_Object(NifOsg, NodeIndexHolder) + + int getIndex() const { return mIndex; } + + private: + + // NIF record index + int mIndex{0}; + }; + +} + +#endif diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index f71dcdd967..0cbc3f22b5 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -11,7 +11,7 @@ #include #include -#include "userdata.hpp" +#include "nodeindexholder.hpp" namespace NifOsg { @@ -383,8 +383,8 @@ void FindGroupByRecIndex::applyNode(osg::Node &searchNode) { if (searchNode.getUserDataContainer() && searchNode.getUserDataContainer()->getNumUserObjects()) { - NodeUserData* holder = dynamic_cast(searchNode.getUserDataContainer()->getUserObject(0)); - if (holder && holder->mIndex == mRecIndex) + NodeIndexHolder* holder = dynamic_cast(searchNode.getUserDataContainer()->getUserObject(0)); + if (holder && holder->getIndex() == mRecIndex) { osg::Group* group = searchNode.asGroup(); if (!group) diff --git a/components/nifosg/userdata.hpp b/components/nifosg/userdata.hpp deleted file mode 100644 index 42fcaff471..0000000000 --- a/components/nifosg/userdata.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#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 - float mScale; - Nif::Matrix3 mRotationScale; - }; - -} - -#endif diff --git a/components/sceneutil/attach.cpp b/components/sceneutil/attach.cpp index c438e705d8..2a6ec84e5b 100644 --- a/components/sceneutil/attach.cpp +++ b/components/sceneutil/attach.cpp @@ -64,7 +64,7 @@ namespace SceneUtil for (const osg::ref_ptr& node : mToCopy) { if (node->getNumParents() > 1) - Log(Debug::Error) << "Error CopyRigVisitor: node has multiple parents"; + Log(Debug::Error) << "Error CopyRigVisitor: node has " << node->getNumParents() << " parents"; while (node->getNumParents()) node->getParent(0)->removeChild(node); diff --git a/components/sceneutil/clone.cpp b/components/sceneutil/clone.cpp index c3261515d6..1de7bfd91e 100644 --- a/components/sceneutil/clone.cpp +++ b/components/sceneutil/clone.cpp @@ -6,8 +6,6 @@ #include #include -#include - #include #include @@ -22,15 +20,6 @@ namespace SceneUtil | osg::CopyOp::DEEP_COPY_USERDATA); } - osg::Object* CopyOp::operator ()(const osg::Object* node) const - { - // We should copy node transformations when we copy node - if (dynamic_cast(node)) - return static_cast(node->clone(*this)); - - return osg::CopyOp::operator()(node); - } - osg::Node* CopyOp::operator ()(const osg::Node* node) const { if (const osgParticle::ParticleProcessor* processor = dynamic_cast(node)) diff --git a/components/sceneutil/clone.hpp b/components/sceneutil/clone.hpp index cf6d79e683..8c5fbd3510 100644 --- a/components/sceneutil/clone.hpp +++ b/components/sceneutil/clone.hpp @@ -30,8 +30,6 @@ namespace SceneUtil virtual osg::Node* operator() (const osg::Node* node) const; virtual osg::Drawable* operator() (const osg::Drawable* drawable) const; - virtual osg::Object* operator ()(const osg::Object* node) const; - private: // maps new pointers to their old pointers // a little messy, but I think this should be the most efficient way diff --git a/components/sceneutil/serialize.cpp b/components/sceneutil/serialize.cpp index 60f096a724..62325186c0 100644 --- a/components/sceneutil/serialize.cpp +++ b/components/sceneutil/serialize.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include #include #include @@ -74,6 +76,15 @@ public: } }; +class MatrixTransformSerializer : public osgDB::ObjectWrapper +{ +public: + MatrixTransformSerializer() + : osgDB::ObjectWrapper(createInstanceFunc, "NifOsg::MatrixTransform", "osg::Object osg::Node osg::Transform osg::MatrixTransform NifOsg::MatrixTransform") + { + } +}; + osgDB::ObjectWrapper* makeDummySerializer(const std::string& classname) { return new osgDB::ObjectWrapper(createInstanceFunc, classname, "osg::Object"); @@ -100,6 +111,7 @@ void registerSerializers() mgr->addWrapper(new MorphGeometrySerializer); mgr->addWrapper(new LightManagerSerializer); mgr->addWrapper(new CameraRelativeTransformSerializer); + mgr->addWrapper(new MatrixTransformSerializer); // Don't serialize Geometry data as we are more interested in the overall structure rather than tons of vertex data that would make the file large and hard to read. mgr->removeWrapper(mgr->findWrapper("osg::Geometry")); @@ -118,7 +130,6 @@ void registerSerializers() "SceneUtil::StateSetUpdater", "SceneUtil::DisableLight", "SceneUtil::MWShadowTechnique", - "NifOsg::NodeUserData", "NifOsg::FlipController", "NifOsg::KeyframeController", "NifOsg::TextKeyMapHolder", @@ -131,7 +142,8 @@ void registerSerializers() "NifOsg::StaticBoundingBoxCallback", "NifOsg::GeomMorpherController", "NifOsg::UpdateMorphGeometry", - "NifOsg::CollisionSwitch", + "NifOsg::UVController", + "NifOsg::NodeIndexHolder", "osgMyGUI::Drawable", "osg::DrawCallback", "osgOQ::ClearQueriesCallback",