From 6219a7bbfc6e9c4c611f5eac349dbcb8d684de63 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 23 Mar 2015 01:31:16 +0100 Subject: [PATCH] Add .kf loader --- components/nifosg/controller.cpp | 33 ++++++++ components/nifosg/controller.hpp | 37 +++++++-- components/nifosg/nifloader.cpp | 132 +++++++++++++++++++++++++++---- components/nifosg/nifloader.hpp | 20 +++-- 4 files changed, 193 insertions(+), 29 deletions(-) diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 88802f81e0..ef3157773a 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -469,4 +469,37 @@ void ParticleSystemController::operator() (osg::Node* node, osg::NodeVisitor* nv traverse(node, nv); } +SourcedKeyframeController::SourcedKeyframeController(const Nif::NiKeyframeData *data, int sourceIndex) + : KeyframeController(data) + , mSourceIndex(sourceIndex) + , mEnabled(false) +{ +} + +SourcedKeyframeController::SourcedKeyframeController() + : mSourceIndex(0) + , mEnabled(false) +{ +} + +SourcedKeyframeController::SourcedKeyframeController(const SourcedKeyframeController ©, const osg::CopyOp ©op) + : KeyframeController(copy, copyop) + , mSourceIndex(copy.mSourceIndex) + , mEnabled(copy.mEnabled) +{ +} + +void SourcedKeyframeController::setEnabled(bool enabled) +{ + mEnabled = enabled; +} + +void SourcedKeyframeController::operator ()(osg::Node* node, osg::NodeVisitor* nv) +{ + if (mEnabled) + KeyframeController::operator()(node, nv); // calls traverse + else + traverse(node, nv); +} + } diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index eb9b95a814..17bf3ae75b 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -140,6 +140,17 @@ namespace NifOsg class KeyframeController : public osg::NodeCallback, public Controller, public ValueInterpolator { + public: + KeyframeController(const Nif::NiKeyframeData *data); + KeyframeController(); + KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop); + + META_Object(NifOsg, KeyframeController) + + virtual osg::Vec3f getTranslation(float time) const; + + virtual void operator() (osg::Node*, osg::NodeVisitor*); + private: Nif::QuaternionKeyMapPtr mRotations; @@ -152,21 +163,33 @@ namespace NifOsg using ValueInterpolator::interpKey; - osg::Quat interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time); osg::Quat getXYZRotation(float time) const; + }; + // Specialization of KeyframeController that remembers a "source index" for the animation source + // it came from, and can be enabled/disabled. Used for multiple animation sources support, i.e. .kf files. + // A SourcedKeyframeController is disabled by default and should be manually enabled when playing an animation from + // the relevant animation source. + class SourcedKeyframeController : public KeyframeController + { public: - KeyframeController(const Nif::NiKeyframeData *data); - KeyframeController(); - KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop); - - META_Object(NifOsg, KeyframeController) + SourcedKeyframeController(const Nif::NiKeyframeData* data, int sourceIndex); + SourcedKeyframeController(); + SourcedKeyframeController(const SourcedKeyframeController& copy, const osg::CopyOp& copyop); - virtual osg::Vec3f getTranslation(float time) const; + META_Object(NifOsg, SourcedKeyframeController) virtual void operator() (osg::Node*, osg::NodeVisitor*); + + int getSourceIndex() const; + + void setEnabled(bool enabled); + + private: + int mSourceIndex; + bool mEnabled; }; // Note we're using NodeCallback instead of StateSet::Callback because the StateSet callback doesn't support nesting diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 1e69ee4f58..c73bcbc969 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -248,6 +248,43 @@ namespace } }; + + // NodeVisitor that adds keyframe controllers to an existing scene graph, used when loading .kf files + class LoadKfVisitor : public osg::NodeVisitor + { + public: + LoadKfVisitor(std::map map, int sourceIndex) + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mMap(map) + , mSourceIndex(sourceIndex) + { + } + + void apply(osg::Node &node) + { + std::map::const_iterator found = mMap.find(node.getName()); + if (found != mMap.end()) + { + const Nif::NiKeyframeController* keyframectrl = found->second; + + osg::ref_ptr callback(new NifOsg::SourcedKeyframeController(keyframectrl->data.getPtr(), mSourceIndex)); + callback->mFunction = boost::shared_ptr(new NifOsg::ControllerFunction(keyframectrl, false)); + + // Insert in front of the callback list, to make sure UpdateBone is last. + // The order of SourcedKeyframeControllers doesn't matter since only one of them should be enabled at a time. + osg::ref_ptr old = node.getUpdateCallback(); + node.setUpdateCallback(callback); + callback->setNestedCallback(old); + } + + traverse(node); + } + + private: + std::map mMap; + int mSourceIndex; + }; + osg::ref_ptr handleMorphGeometry(const Nif::NiGeomMorpherController* morpher) { osg::ref_ptr morphGeom = new osgAnimation::MorphGeometry; @@ -270,38 +307,94 @@ namespace namespace NifOsg { - void Loader::load(Nif::NIFFilePtr nif, osg::Group *parentNode) + void Loader::loadKf(Nif::NIFFilePtr nif, osg::Node *rootNode, int sourceIndex) + { + if(nif->numRoots() < 1) + { + nif->warn("Found no root nodes"); + return; + } + + const Nif::Record *r = nif->getRoot(0); + assert(r != NULL); + + if(r->recType != Nif::RC_NiSequenceStreamHelper) + { + nif->warn("First root was not a NiSequenceStreamHelper, but a "+ + r->recName+"."); + return; + } + const Nif::NiSequenceStreamHelper *seq = static_cast(r); + + Nif::ExtraPtr extra = seq->extra; + if(extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData) + { + nif->warn("First extra data was not a NiTextKeyExtraData, but a "+ + (extra.empty() ? std::string("nil") : extra->recName)+"."); + return; + } + + //extractTextKeys(static_cast(extra.getPtr()), textKeys); + + std::map controllerMap; + + extra = extra->extra; + Nif::ControllerPtr ctrl = seq->controller; + for(;!extra.empty() && !ctrl.empty();(extra=extra->extra),(ctrl=ctrl->next)) + { + if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) + { + nif->warn("Unexpected extra data "+extra->recName+" with controller "+ctrl->recName); + continue; + } + + if (!(ctrl->flags & Nif::NiNode::ControllerFlag_Active)) + continue; + + const Nif::NiStringExtraData *strdata = static_cast(extra.getPtr()); + const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); + + if(key->data.empty()) + continue; + + controllerMap[strdata->string] = key; + } + + LoadKfVisitor visitor(controllerMap, sourceIndex); + rootNode->accept(visitor); + } + + osg::Node* Loader::load(Nif::NIFFilePtr nif, osg::Group *parentNode) { mNif = nif; if (nif->numRoots() < 1) { - nif->warn("Found no root nodes"); - return; + nif->fail("Found no root nodes"); } const Nif::Record* r = nif->getRoot(0); - assert(r != NULL); const Nif::Node* nifNode = dynamic_cast(r); if (nifNode == NULL) { - nif->warn("First root was not a node, but a " + r->recName); - return; + nif->fail("First root was not a node, but a " + r->recName); } mRootNode = parentNode; - handleNode(nifNode, parentNode, false, std::map(), 0, 0); + + osg::Node* created = handleNode(nifNode, parentNode, false, std::map(), 0, 0); + return created; } - void Loader::loadAsSkeleton(Nif::NIFFilePtr nif, osg::Group *parentNode) + osg::Node* Loader::loadAsSkeleton(Nif::NIFFilePtr nif, osg::Group *parentNode) { mNif = nif; if (nif->numRoots() < 1) { - nif->warn("Found no root nodes"); - return; + //nif->warn("Found no root nodes"); + nif->fail("Found no root nodes"); } const Nif::Record* r = nif->getRoot(0); @@ -310,17 +403,18 @@ namespace NifOsg const Nif::Node* nifNode = dynamic_cast(r); if (nifNode == NULL) { - nif->warn("First root was not a node, but a " + r->recName); - return; + //nif->warn("First root was not a node, but a " + r->recName); + nif->fail("First root was not a node, but a " + r->recName); } + osg::ref_ptr skel = new osgAnimation::Skeleton; + parentNode->addChild(skel); + mRootNode = parentNode; - osgAnimation::Skeleton* skel = new osgAnimation::Skeleton; - mSkeleton = skel; - mRootNode->addChild(mSkeleton); + handleNode(nifNode, skel, true, std::map(), 0, 0); - handleNode(nifNode, mSkeleton, true, std::map(), 0, 0); + return skel; } void Loader::applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, std::map& boundTextures, int animflags) @@ -342,7 +436,7 @@ namespace NifOsg toSetup->mFunction = boost::shared_ptr(new ControllerFunction(ctrl, 1 /*autoPlay*/)); } - void Loader::handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton, + osg::Node* Loader::handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton, std::map boundTextures, int animflags, int particleflags, bool collisionNode) { osg::ref_ptr transformNode; @@ -362,6 +456,7 @@ namespace NifOsg { transformNode = new osg::MatrixTransform(toMatrix(nifNode->trafo)); } + // Ignoring name for non-bone nodes for now. We might need it later in isolated cases, e.g. AttachLight. // UserData used for a variety of features: // - finding the correct emitter node for a particle system @@ -428,6 +523,8 @@ namespace NifOsg handleNode(children[i].getPtr(), transformNode, createSkeleton, boundTextures, animflags, particleflags, collisionNode); } } + + return transformNode; } void Loader::handleMeshControllers(const Nif::Node *nifNode, osg::MatrixTransform *transformNode, const std::map &boundTextures, int animflags) @@ -645,6 +742,7 @@ namespace NifOsg // This seems to be true for all NIF files in the game that I've checked, suggesting that NIFs work similar to OSG with regards to update order. // If something ever violates this assumption, the worst that could happen is the culling being one frame late, which wouldn't be a disaster. + // Creating emitters will need to be changed when cloning a scenegraph is implemented, the particleSystem pointer would become invalid FindRecIndexVisitor find (partctrl->emitter->recIndex); mRootNode->accept(find); if (!find.mFound) diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index 5bd25f6ca7..18e3e7db8c 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -30,21 +30,32 @@ namespace NifOsg { class Controller; + typedef std::multimap TextKeyMap; + /// The main class responsible for loading NIF files into an OSG-Scenegraph. class Loader { public: - /// @param node The parent of the root node for the created NIF file. - void load(Nif::NIFFilePtr file, osg::Group* parentNode); + // TODO: add auto-detection for skinning. We will still need a "force skeleton" parameter + // though, when assembling from several files, i.e. equipment parts + /// Create a scene graph for the given NIF. Assumes no skinning is used. + /// @param node The parent of the new root node for the created scene graph. + osg::Node* load(Nif::NIFFilePtr file, osg::Group* parentNode); + + /// Create a scene graph for the given NIF. Assumes skinning will be used. + osg::Node* loadAsSkeleton(Nif::NIFFilePtr file, osg::Group* parentNode); - void loadAsSkeleton(Nif::NIFFilePtr file, osg::Group* parentNode); + /// Load keyframe controllers from the given kf file onto the given scene graph. + /// @param sourceIndex The source index for this animation source, used for identifying + /// which animation source a keyframe controller came from. + void loadKf(Nif::NIFFilePtr kf, osg::Node* rootNode, int sourceIndex); const VFS::Manager* resourceManager; private: /// @param createSkeleton If true, use an osgAnimation::Bone for NIF nodes, otherwise an osg::MatrixTransform. - void handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton, + osg::Node* handleNode(const Nif::Node* nifNode, osg::Group* parentNode, bool createSkeleton, std::map boundTextures, int animflags, int particleflags, bool collisionNode=false); void handleMeshControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode, const std::map& boundTextures, int animflags); @@ -81,7 +92,6 @@ namespace NifOsg Nif::NIFFilePtr mNif; osg::Group* mRootNode; - osg::Group* mSkeleton; }; }