Add .kf loader

pull/638/head
scrawl 10 years ago
parent 9242e6d256
commit 6219a7bbfc

@ -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 &copy, const osg::CopyOp &copyop)
: 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);
}
}

@ -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

@ -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<std::string, const Nif::NiKeyframeController*> map, int sourceIndex)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mMap(map)
, mSourceIndex(sourceIndex)
{
}
void apply(osg::Node &node)
{
std::map<std::string, const Nif::NiKeyframeController*>::const_iterator found = mMap.find(node.getName());
if (found != mMap.end())
{
const Nif::NiKeyframeController* keyframectrl = found->second;
osg::ref_ptr<NifOsg::SourcedKeyframeController> callback(new NifOsg::SourcedKeyframeController(keyframectrl->data.getPtr(), mSourceIndex));
callback->mFunction = boost::shared_ptr<NifOsg::ControllerFunction>(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<osg::NodeCallback> old = node.getUpdateCallback();
node.setUpdateCallback(callback);
callback->setNestedCallback(old);
}
traverse(node);
}
private:
std::map<std::string, const Nif::NiKeyframeController*> mMap;
int mSourceIndex;
};
osg::ref_ptr<osg::Geometry> handleMorphGeometry(const Nif::NiGeomMorpherController* morpher)
{
osg::ref_ptr<osgAnimation::MorphGeometry> 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<const Nif::NiSequenceStreamHelper*>(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<const Nif::NiTextKeyExtraData*>(extra.getPtr()), textKeys);
std::map<std::string, const Nif::NiKeyframeController*> 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<const Nif::NiStringExtraData*>(extra.getPtr());
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(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<const Nif::Node*>(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<int, int>(), 0, 0);
osg::Node* created = handleNode(nifNode, parentNode, false, std::map<int, int>(), 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<const Nif::Node*>(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<osgAnimation::Skeleton> 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<int, int>(), 0, 0);
handleNode(nifNode, mSkeleton, true, std::map<int, int>(), 0, 0);
return skel;
}
void Loader::applyNodeProperties(const Nif::Node *nifNode, osg::Node *applyTo, std::map<int, int>& boundTextures, int animflags)
@ -342,7 +436,7 @@ namespace NifOsg
toSetup->mFunction = boost::shared_ptr<ControllerFunction>(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<int, int> boundTextures, int animflags, int particleflags, bool collisionNode)
{
osg::ref_ptr<osg::MatrixTransform> 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<int, int> &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)

@ -30,21 +30,32 @@ namespace NifOsg
{
class Controller;
typedef std::multimap<float,std::string> 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<int, int> boundTextures, int animflags, int particleflags, bool collisionNode=false);
void handleMeshControllers(const Nif::Node* nifNode, osg::MatrixTransform* transformNode, const std::map<int, int>& boundTextures, int animflags);
@ -81,7 +92,6 @@ namespace NifOsg
Nif::NIFFilePtr mNif;
osg::Group* mRootNode;
osg::Group* mSkeleton;
};
}

Loading…
Cancel
Save