mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-21 06:39:40 +00:00
Add .kf loader
This commit is contained in:
parent
9242e6d256
commit
6219a7bbfc
4 changed files with 196 additions and 32 deletions
|
@ -469,4 +469,37 @@ void ParticleSystemController::operator() (osg::Node* node, osg::NodeVisitor* nv
|
||||||
traverse(node, 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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,6 +140,17 @@ namespace NifOsg
|
||||||
|
|
||||||
class KeyframeController : public osg::NodeCallback, public Controller, public ValueInterpolator
|
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:
|
private:
|
||||||
Nif::QuaternionKeyMapPtr mRotations;
|
Nif::QuaternionKeyMapPtr mRotations;
|
||||||
|
|
||||||
|
@ -152,21 +163,33 @@ namespace NifOsg
|
||||||
|
|
||||||
using ValueInterpolator::interpKey;
|
using ValueInterpolator::interpKey;
|
||||||
|
|
||||||
|
|
||||||
osg::Quat interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time);
|
osg::Quat interpKey(const Nif::QuaternionKeyMap::MapType &keys, float time);
|
||||||
|
|
||||||
osg::Quat getXYZRotation(float time) const;
|
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:
|
public:
|
||||||
KeyframeController(const Nif::NiKeyframeData *data);
|
SourcedKeyframeController(const Nif::NiKeyframeData* data, int sourceIndex);
|
||||||
KeyframeController();
|
SourcedKeyframeController();
|
||||||
KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop);
|
SourcedKeyframeController(const SourcedKeyframeController& copy, const osg::CopyOp& copyop);
|
||||||
|
|
||||||
META_Object(NifOsg, KeyframeController)
|
META_Object(NifOsg, SourcedKeyframeController)
|
||||||
|
|
||||||
virtual osg::Vec3f getTranslation(float time) const;
|
|
||||||
|
|
||||||
virtual void operator() (osg::Node*, osg::NodeVisitor*);
|
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
|
// 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<osg::Geometry> handleMorphGeometry(const Nif::NiGeomMorpherController* morpher)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osgAnimation::MorphGeometry> morphGeom = new osgAnimation::MorphGeometry;
|
osg::ref_ptr<osgAnimation::MorphGeometry> morphGeom = new osgAnimation::MorphGeometry;
|
||||||
|
@ -270,38 +307,94 @@ namespace
|
||||||
namespace NifOsg
|
namespace NifOsg
|
||||||
{
|
{
|
||||||
|
|
||||||
void Loader::load(Nif::NIFFilePtr nif, osg::Group *parentNode)
|
void Loader::loadKf(Nif::NIFFilePtr nif, osg::Node *rootNode, int sourceIndex)
|
||||||
{
|
{
|
||||||
mNif = nif;
|
if(nif->numRoots() < 1)
|
||||||
|
|
||||||
if (nif->numRoots() < 1)
|
|
||||||
{
|
{
|
||||||
nif->warn("Found no root nodes");
|
nif->warn("Found no root nodes");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Nif::Record* r = nif->getRoot(0);
|
const Nif::Record *r = nif->getRoot(0);
|
||||||
assert(r != NULL);
|
assert(r != NULL);
|
||||||
|
|
||||||
const Nif::Node* nifNode = dynamic_cast<const Nif::Node*>(r);
|
if(r->recType != Nif::RC_NiSequenceStreamHelper)
|
||||||
if (nifNode == NULL)
|
|
||||||
{
|
{
|
||||||
nif->warn("First root was not a node, but a " + r->recName);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mRootNode = parentNode;
|
//extractTextKeys(static_cast<const Nif::NiTextKeyExtraData*>(extra.getPtr()), textKeys);
|
||||||
handleNode(nifNode, parentNode, false, std::map<int, int>(), 0, 0);
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::loadAsSkeleton(Nif::NIFFilePtr nif, osg::Group *parentNode)
|
osg::Node* Loader::load(Nif::NIFFilePtr nif, osg::Group *parentNode)
|
||||||
{
|
{
|
||||||
mNif = nif;
|
mNif = nif;
|
||||||
|
|
||||||
if (nif->numRoots() < 1)
|
if (nif->numRoots() < 1)
|
||||||
{
|
{
|
||||||
nif->warn("Found no root nodes");
|
nif->fail("Found no root nodes");
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
const Nif::Record* r = nif->getRoot(0);
|
||||||
|
|
||||||
|
const Nif::Node* nifNode = dynamic_cast<const Nif::Node*>(r);
|
||||||
|
if (nifNode == NULL)
|
||||||
|
{
|
||||||
|
nif->fail("First root was not a node, but a " + r->recName);
|
||||||
|
}
|
||||||
|
|
||||||
|
mRootNode = parentNode;
|
||||||
|
|
||||||
|
osg::Node* created = handleNode(nifNode, parentNode, false, std::map<int, int>(), 0, 0);
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::Node* Loader::loadAsSkeleton(Nif::NIFFilePtr nif, osg::Group *parentNode)
|
||||||
|
{
|
||||||
|
mNif = nif;
|
||||||
|
|
||||||
|
if (nif->numRoots() < 1)
|
||||||
|
{
|
||||||
|
//nif->warn("Found no root nodes");
|
||||||
|
nif->fail("Found no root nodes");
|
||||||
}
|
}
|
||||||
|
|
||||||
const Nif::Record* r = nif->getRoot(0);
|
const Nif::Record* r = nif->getRoot(0);
|
||||||
|
@ -310,17 +403,18 @@ namespace NifOsg
|
||||||
const Nif::Node* nifNode = dynamic_cast<const Nif::Node*>(r);
|
const Nif::Node* nifNode = dynamic_cast<const Nif::Node*>(r);
|
||||||
if (nifNode == NULL)
|
if (nifNode == NULL)
|
||||||
{
|
{
|
||||||
nif->warn("First root was not a node, but a " + r->recName);
|
//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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::ref_ptr<osgAnimation::Skeleton> skel = new osgAnimation::Skeleton;
|
||||||
|
parentNode->addChild(skel);
|
||||||
|
|
||||||
mRootNode = parentNode;
|
mRootNode = parentNode;
|
||||||
|
|
||||||
osgAnimation::Skeleton* skel = new osgAnimation::Skeleton;
|
handleNode(nifNode, skel, true, std::map<int, int>(), 0, 0);
|
||||||
mSkeleton = skel;
|
|
||||||
mRootNode->addChild(mSkeleton);
|
|
||||||
|
|
||||||
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)
|
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*/));
|
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)
|
std::map<int, int> boundTextures, int animflags, int particleflags, bool collisionNode)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::MatrixTransform> transformNode;
|
osg::ref_ptr<osg::MatrixTransform> transformNode;
|
||||||
|
@ -362,6 +456,7 @@ namespace NifOsg
|
||||||
{
|
{
|
||||||
transformNode = new osg::MatrixTransform(toMatrix(nifNode->trafo));
|
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:
|
// UserData used for a variety of features:
|
||||||
// - finding the correct emitter node for a particle system
|
// - 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);
|
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)
|
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.
|
// 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.
|
// 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);
|
FindRecIndexVisitor find (partctrl->emitter->recIndex);
|
||||||
mRootNode->accept(find);
|
mRootNode->accept(find);
|
||||||
if (!find.mFound)
|
if (!find.mFound)
|
||||||
|
|
|
@ -30,21 +30,32 @@ namespace NifOsg
|
||||||
{
|
{
|
||||||
class Controller;
|
class Controller;
|
||||||
|
|
||||||
|
typedef std::multimap<float,std::string> TextKeyMap;
|
||||||
|
|
||||||
/// The main class responsible for loading NIF files into an OSG-Scenegraph.
|
/// The main class responsible for loading NIF files into an OSG-Scenegraph.
|
||||||
class Loader
|
class Loader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// @param node The parent of the root node for the created NIF file.
|
// TODO: add auto-detection for skinning. We will still need a "force skeleton" parameter
|
||||||
void load(Nif::NIFFilePtr file, osg::Group* parentNode);
|
// 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);
|
||||||
|
|
||||||
void loadAsSkeleton(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);
|
||||||
|
|
||||||
|
/// 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;
|
const VFS::Manager* resourceManager;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// @param createSkeleton If true, use an osgAnimation::Bone for NIF nodes, otherwise an osg::MatrixTransform.
|
/// @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);
|
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);
|
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;
|
Nif::NIFFilePtr mNif;
|
||||||
|
|
||||||
osg::Group* mRootNode;
|
osg::Group* mRootNode;
|
||||||
osg::Group* mSkeleton;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue