OSG-Collada animation support

pull/3029/head
Nelsson Huotari 4 years ago
parent f78a5d795c
commit 6e77ad1f6a

@ -259,7 +259,7 @@ if(NOT HAVE_STDINT_H)
endif()
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX osgShadow)
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX osgShadow osgAnimation)
include_directories(SYSTEM ${OPENSCENEGRAPH_INCLUDE_DIRS})
set(USED_OSG_PLUGINS

@ -782,6 +782,8 @@ namespace MWRender
NodeMap::const_iterator found = nodeMap.find("bip01");
if (found == nodeMap.end())
found = nodeMap.find("root bone");
if (found == nodeMap.end())
found = nodeMap.find("root");
if (found != nodeMap.end())
mAccumRoot = found->second;

@ -41,7 +41,8 @@ add_component_dir (vfs
)
add_component_dir (resource
scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem resourcemanager stats
scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem
resourcemanager stats animation
)
add_component_dir (shader
@ -51,7 +52,7 @@ add_component_dir (shader
add_component_dir (sceneutil
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer
actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin
actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin osgacontroller
)
add_component_dir (nif

@ -0,0 +1,40 @@
#include <components/resource/animation.hpp>
#include <osg/ref_ptr>
#include <osgAnimation/Channel>
namespace Resource
{
Animation::Animation(const Animation& anim, const osg::CopyOp& copyop): osg::Object(anim, copyop),
mDuration(0.0f),
mStartTime(0.0f)
{
const osgAnimation::ChannelList& channels = anim.getChannels();
for (const osg::ref_ptr<osgAnimation::Channel> channel: channels)
addChannel(channel.get()->clone());
}
void Animation::addChannel(osg::ref_ptr<osgAnimation::Channel> pChannel)
{
mChannels.push_back(pChannel);
}
std::vector<osg::ref_ptr<osgAnimation::Channel>>& Animation::getChannels()
{
return mChannels;
}
const std::vector<osg::ref_ptr<osgAnimation::Channel>>& Animation::getChannels() const
{
return mChannels;
}
bool Animation::update (double time)
{
for (const osg::ref_ptr<osgAnimation::Channel> channel: mChannels)
{
channel->update(time, 1.0f, 0);
}
return true;
}
}

@ -0,0 +1,39 @@
#ifndef OPENMW_COMPONENTS_RESOURCE_ANIMATION_HPP
#define OPENMW_COMPONENTS_RESOURCE_ANIMATION_HPP
#include <vector>
#include <osg/Node>
#include <osg/Object>
#include <osgAnimation/Channel>
namespace Resource
{
/// Stripped down class of osgAnimation::Animation, only needed for OSG's plugin formats like dae
class Animation : public osg::Object
{
public:
META_Object(Resource, Animation)
Animation() :
mDuration(0.0), mStartTime(0) {}
Animation(const Animation&, const osg::CopyOp&);
~Animation() {}
void addChannel (osg::ref_ptr<osgAnimation::Channel> pChannel);
std::vector<osg::ref_ptr<osgAnimation::Channel>>& getChannels();
const std::vector<osg::ref_ptr<osgAnimation::Channel>>& getChannels() const;
bool update (double time);
protected:
double mDuration;
double mStartTime;
std::vector<osg::ref_ptr<osgAnimation::Channel>> mChannels;
};
}
#endif

@ -2,29 +2,86 @@
#include <components/vfs/manager.hpp>
#include <osgAnimation/Animation>
#include <osgAnimation/BasicAnimationManager>
#include <osgAnimation/Channel>
#include <components/nifosg/nifloader.hpp>
#include <components/sceneutil/keyframe.hpp>
#include <components/sceneutil/osgacontroller.hpp>
#include "animation.hpp"
#include "objectcache.hpp"
#include "scenemanager.hpp"
namespace
namespace OsgAOpenMW
{
class RetrieveAnimationsVisitor : public osg::NodeVisitor
{
public:
RetrieveAnimationsVisitor(SceneUtil::KeyframeHolder& target) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mTarget(target) {}
RetrieveAnimationsVisitor::RetrieveAnimationsVisitor(SceneUtil::KeyframeHolder& target, osg::ref_ptr<osgAnimation::BasicAnimationManager> animationManager) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mTarget(target), mAnimationManager(animationManager) {}
virtual void apply(osg::Node& node)
{
if (node.libraryName() == std::string("osgAnimation"))
std::cout << "found an " << node.className() << std::endl;
traverse(node);
}
void RetrieveAnimationsVisitor::apply(osg::Node& node)
{
if (node.libraryName() == std::string("osgAnimation") && node.className() == std::string("Bone") && node.getName() == std::string("root"))
{
if (!mAnimationManager)
{
traverse(node);
return;
}
osg::ref_ptr<OsgaController::KeyframeController> callback = new OsgaController::KeyframeController();
std::vector<SceneUtil::EmulatedAnimation> emulatedAnimations;
for (auto animation : mAnimationManager->getAnimationList())
{
if (animation)
{
if (animation->getName() == "Default") //"Default" is osg dae plugin's default naming scheme for unnamed animations
{
animation->setName(std::string("idle")); // animation naming scheme "idle: start" and "idle: stop" is the default idle animation that OpenMW seems to want to play
}
osg::ref_ptr<Resource::Animation> mergedAnimationTrack = new Resource::Animation;
std::string animationName = animation->getName();
std::string start = animationName + std::string(": start");
std::string stop = animationName + std::string(": stop");
std::string loopstart = animationName + std::string(": loop start");
std::string loopstop = animationName + std::string(": loop stop");
const osgAnimation::ChannelList& channels = animation->getChannels();
for (const osg::ref_ptr<osgAnimation::Channel> channel: channels)
{
mergedAnimationTrack->addChannel(channel.get()->clone()); // is ->clone needed?
}
mergedAnimationTrack->setName(animation->getName());
callback->addMergedAnimationTrack(mergedAnimationTrack);
float startTime = animation->getStartTime();
float stopTime = startTime + animation->getDuration();
// mTextKeys is a nif-thing, used by OpenMW's animation system
// Format is likely "AnimationName: [Keyword_optional] [Start OR Stop]"
// AnimationNames are keywords like idle2, idle3... AiPackages and various mechanics control which animations are played
// Keywords can be stuff like Loop, Equip, Unequip, Block, InventoryHandtoHand, InventoryWeaponOneHand, PickProbe, Slash, Thrust, Chop... even "Slash Small Follow"
mTarget.mTextKeys.emplace(startTime, std::move(start));
mTarget.mTextKeys.emplace(stopTime, std::move(stop));
mTarget.mTextKeys.emplace(startTime, std::move(loopstart));
mTarget.mTextKeys.emplace(stopTime, std::move(loopstop));
SceneUtil::EmulatedAnimation emulatedAnimation;
emulatedAnimation.mStartTime = startTime;
emulatedAnimation.mStopTime = stopTime;
emulatedAnimation.mName = animationName;
emulatedAnimations.emplace_back(emulatedAnimation);
}
}
callback->setEmulatedAnimations(emulatedAnimations);
mTarget.mKeyframeControllers.emplace(node.getName(), callback);
}
private:
SceneUtil::KeyframeHolder& mTarget;
};
traverse(node);
}
std::string getFileExtension(const std::string& file)
{
@ -59,16 +116,17 @@ namespace Resource
else
{
osg::ref_ptr<SceneUtil::KeyframeHolder> loaded (new SceneUtil::KeyframeHolder);
std::string ext = getFileExtension(normalized);
std::string ext = OsgAOpenMW::getFileExtension(normalized);
if (ext == "kf")
{
NifOsg::Loader::loadKf(Nif::NIFFilePtr(new Nif::NIFFile(mVFS->getNormalized(normalized), normalized)), *loaded.get());
}
else
{
osg::ref_ptr<const osg::Node> scene = mSceneManager->getTemplate(normalized);
RetrieveAnimationsVisitor rav(*loaded.get());
const_cast<osg::Node*>(scene.get())->accept(rav); // const_cast required because there is no const version of osg::NodeVisitor
osg::ref_ptr<osg::Node> scene = const_cast<osg::Node*> ( mSceneManager->getTemplate(normalized).get() );
osg::ref_ptr<osgAnimation::BasicAnimationManager> bam = dynamic_cast<osgAnimation::BasicAnimationManager*> (scene->getUpdateCallback());
OsgAOpenMW::RetrieveAnimationsVisitor rav(*loaded.get(), bam);
scene->accept(rav);
}
mCache->addEntryToObjectCache(normalized, loaded);
return loaded;

@ -2,12 +2,32 @@
#define OPENMW_COMPONENTS_KEYFRAMEMANAGER_H
#include <osg/ref_ptr>
#include <osgAnimation/BasicAnimationManager>
#include <string>
#include <components/sceneutil/keyframe.hpp>
#include "resourcemanager.hpp"
namespace OsgAOpenMW
{
/// @brief extract animations to OpenMW's animation system
class RetrieveAnimationsVisitor : public osg::NodeVisitor
{
public:
RetrieveAnimationsVisitor(SceneUtil::KeyframeHolder& target, osg::ref_ptr<osgAnimation::BasicAnimationManager> animationManager);
virtual void apply(osg::Node& node);
private:
SceneUtil::KeyframeHolder& mTarget;
osg::ref_ptr<osgAnimation::BasicAnimationManager> mAnimationManager;
};
std::string getFileExtension(const std::string& file);
}
namespace Resource
{

@ -492,7 +492,7 @@ namespace Resource
}
catch (std::exception& e)
{
static const char * const sMeshTypes[] = { "nif", "osg", "osgt", "osgb", "osgx", "osg2" };
static const char * const sMeshTypes[] = { "nif", "osg", "osgt", "osgb", "osgx", "osg2", "dae" };
for (unsigned int i=0; i<sizeof(sMeshTypes)/sizeof(sMeshTypes[0]); ++i)
{

@ -2,6 +2,11 @@
#include <osg/StateSet>
#include <osgAnimation/Bone>
#include <osgAnimation/Skeleton>
#include <osgAnimation/MorphGeometry>
#include <osgAnimation/RigGeometry>
#include <osgParticle/ParticleProcessor>
#include <osgParticle/ParticleSystemUpdater>
#include <osgParticle/Emitter>
@ -30,6 +35,11 @@ namespace SceneUtil
mUpdaterToOldPs[cloned] = updater->getParticleSystem(0);
return cloned;
}
if (dynamic_cast<const osgAnimation::Bone*>(node) || dynamic_cast<const osgAnimation::Skeleton*>(node))
{
return osg::clone(node, *this);
}
return osg::CopyOp::operator()(node);
}
@ -38,7 +48,7 @@ namespace SceneUtil
if (const osgParticle::ParticleSystem* partsys = dynamic_cast<const osgParticle::ParticleSystem*>(drawable))
return operator()(partsys);
if (dynamic_cast<const SceneUtil::RigGeometry*>(drawable) || dynamic_cast<const SceneUtil::MorphGeometry*>(drawable))
if (dynamic_cast<const SceneUtil::RigGeometry*>(drawable) || dynamic_cast<const SceneUtil::MorphGeometry*>(drawable) || dynamic_cast<const osgAnimation::RigGeometry*>(drawable) || dynamic_cast<const osgAnimation::MorphGeometry*>(drawable))
{
return static_cast<osg::Drawable*>(drawable->clone(*this));
}

@ -7,9 +7,16 @@
#include <components/sceneutil/controller.hpp>
#include <components/sceneutil/textkeymap.hpp>
#include <components/resource/animation.hpp>
namespace SceneUtil
{
struct EmulatedAnimation
{
float mStartTime;
float mStopTime;
std::string mName;
};
class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller
{
@ -19,12 +26,18 @@ namespace SceneUtil
KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop)
: osg::NodeCallback(copy, copyop)
, SceneUtil::Controller(copy)
, mMergedAnimationTracks(copy.mMergedAnimationTracks)
, mEmulatedAnimations(copy.mEmulatedAnimations)
{}
META_Object(SceneUtil, KeyframeController)
virtual osg::Vec3f getTranslation(float time) const { return osg::Vec3f(); }
virtual void operator() (osg::Node* node, osg::NodeVisitor* nodeVisitor) { traverse(node, nodeVisitor); }
protected:
std::vector<osg::ref_ptr<Resource::Animation>> mMergedAnimationTracks; // Used only by osgAnimation-based formats (e.g. dae)
std::vector<EmulatedAnimation> mEmulatedAnimations;
};
/// Wrapper object containing an animation track as a ref-countable osg::Object.

@ -0,0 +1,195 @@
#include <components/sceneutil/osgacontroller.hpp>
#include <osg/Geode>
#include <osg/Node>
#include <osg/NodeVisitor>
#include <osg/ref_ptr>
#include <osg/StateSet>
#include <osgAnimation/Animation>
#include <osgAnimation/AnimationUpdateCallback>
#include <osgAnimation/Channel>
#include <osgAnimation/BasicAnimationManager>
#include <osgAnimation/Bone>
#include <osgAnimation/Sampler>
#include <osgAnimation/Skeleton>
#include <osgAnimation/RigGeometry>
#include <osgAnimation/UpdateMatrixTransform>
#include <components/debug/debuglog.hpp>
#include <components/resource/animation.hpp>
#include <components/sceneutil/controller.hpp>
#include <components/sceneutil/keyframe.hpp>
namespace OsgaController
{
LinkVisitor::LinkVisitor() : osg::NodeVisitor( TRAVERSE_ALL_CHILDREN )
{
mAnimation = nullptr;
}
void LinkVisitor::link(osgAnimation::UpdateMatrixTransform* umt)
{
const osgAnimation::ChannelList& channels = mAnimation->getChannels();
for (const osg::ref_ptr<osgAnimation::Channel> channel: channels)
{
const std::string& channelName = channel->getName();
const std::string& channelTargetName = channel->getTargetName();
if (channelTargetName != umt->getName()) continue;
// check if we can link a StackedTransformElement to the current Channel
for (auto stackedTransform : umt->getStackedTransforms())
{
osgAnimation::StackedTransformElement* element = stackedTransform.get();
if (element && !element->getName().empty() && channelName == element->getName())
{
osgAnimation::Target* target = element->getOrCreateTarget();
if (target)
{
channel->setTarget(target);
}
}
}
}
}
void LinkVisitor::handle_stateset(osg::StateSet* stateset)
{
if (!stateset)
return;
const osg::StateSet::AttributeList& attributeList = stateset->getAttributeList();
for (auto attribute : attributeList)
{
osg::StateAttribute* sattr = attribute.second.first.get();
osgAnimation::UpdateMatrixTransform* umt = dynamic_cast<osgAnimation::UpdateMatrixTransform*>(sattr->getUpdateCallback()); //Can this even be in sa?
if (umt) link(umt);
}
}
void LinkVisitor::setAnimation(Resource::Animation* animation)
{
mAnimation = animation;
}
void LinkVisitor::apply(osg::Node& node)
{
osg::StateSet* st = node.getStateSet();
if (st)
handle_stateset(st);
osg::Callback* cb = node.getUpdateCallback();
while (cb)
{
osgAnimation::UpdateMatrixTransform* umt = dynamic_cast<osgAnimation::UpdateMatrixTransform*>(cb);
if (umt)
if (node.getName() != "root") link(umt);
cb = cb->getNestedCallback();
}
traverse( node );
}
void LinkVisitor::apply(osg::Geode& node)
{
for (unsigned int i = 0; i < node.getNumDrawables(); i++)
{
osg::Drawable* drawable = node.getDrawable(i);
if (drawable && drawable->getStateSet())
handle_stateset(drawable->getStateSet());
}
apply(static_cast<osg::Node&>(node));
}
KeyframeController::KeyframeController(const KeyframeController &copy, const osg::CopyOp &copyop) : SceneUtil::KeyframeController(copy, copyop)
{
mLinker = nullptr;
}
osg::Vec3f KeyframeController::getTranslation(float time) const
{
osg::Vec3f translationValue;
std::string animationName;
float newTime = time;
//Find the correct animation based on time
for (auto emulatedAnimation : mEmulatedAnimations)
{
if (time > emulatedAnimation.mStartTime && time < emulatedAnimation.mStopTime)
{
newTime = time - emulatedAnimation.mStartTime;
animationName = emulatedAnimation.mName;
}
}
//Find the root transform track in animation
for (auto mergedAnimationTrack : mMergedAnimationTracks)
{
if (mergedAnimationTrack->getName() != animationName) continue;
const osgAnimation::ChannelList& channels = mergedAnimationTrack->getChannels();
for (const osg::ref_ptr<osgAnimation::Channel> channel: channels)
{
if (channel->getTargetName() != "root" || channel->getName() != "transform") continue;
if ( osgAnimation::MatrixLinearSampler* templateSampler = dynamic_cast<osgAnimation::MatrixLinearSampler*> (channel->getSampler()) )
{
osg::Matrixf matrix;
templateSampler->getValueAt(newTime, matrix);
translationValue = matrix.getTrans();
return osg::Vec3f(translationValue[0], translationValue[1], translationValue[2]);
}
}
}
return osg::Vec3f();
}
void KeyframeController::update(float time, std::string animationName)
{
for (auto mergedAnimationTrack : mMergedAnimationTracks)
{
if (mergedAnimationTrack->getName() == animationName) mergedAnimationTrack->update(time);
}
}
void KeyframeController::operator() (osg::Node* node, osg::NodeVisitor* nv)
{
if (hasInput())
{
if (mNeedToLink)
{
for (auto mergedAnimationTrack : mMergedAnimationTracks)
{
if (!mLinker.valid()) mLinker = new LinkVisitor();
mLinker->setAnimation(mergedAnimationTrack);
node->accept(*mLinker);
}
mNeedToLink = false;
}
float time = getInputValue(nv);
for (auto emulatedAnimation : mEmulatedAnimations)
{
if (time > emulatedAnimation.mStartTime && time < emulatedAnimation.mStopTime)
{
update(time - emulatedAnimation.mStartTime, emulatedAnimation.mName);
}
}
}
traverse(node, nv);
}
void KeyframeController::setEmulatedAnimations(std::vector<SceneUtil::EmulatedAnimation> emulatedAnimations)
{
mEmulatedAnimations = emulatedAnimations;
}
void KeyframeController::addMergedAnimationTrack(osg::ref_ptr<Resource::Animation> animationTrack)
{
mMergedAnimationTracks.emplace_back(animationTrack);
}
}

@ -0,0 +1,70 @@
#ifndef OPENMW_COMPONENTS_SCENEUTIL_OSGACONTROLLER_HPP
#define OPENMW_COMPONENTS_SCENEUTIL_OSGACONTROLLER_HPP
#include <osg/Node>
#include <osg/NodeVisitor>
#include <osg/StateSet>
#include <osg/ref_ptr>
#include <osgAnimation/Animation>
#include <osgAnimation/AnimationUpdateCallback>
#include <osgAnimation/Channel>
#include <osgAnimation/BasicAnimationManager>
#include <osgAnimation/StackedTransform>
#include <osgAnimation/UpdateMatrixTransform>
#include <components/sceneutil/controller.hpp>
#include <components/sceneutil/keyframe.hpp>
#include <components/resource/animation.hpp>
namespace OsgaController
{
class LinkVisitor : public osg::NodeVisitor
{
public:
LinkVisitor();
virtual void link(osgAnimation::UpdateMatrixTransform* umt);
virtual void handle_stateset(osg::StateSet* stateset);
virtual void setAnimation(Resource::Animation* animation);
virtual void apply(osg::Node& node);
virtual void apply(osg::Geode& node);
protected:
Resource::Animation* mAnimation;
};
class KeyframeController : public SceneUtil::KeyframeController
{
public:
/// @brief Handles the animation for osgAnimation formats
KeyframeController() {};
KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop);
META_Object(OsgaController, KeyframeController)
/// @brief Handles the location of the instance
osg::Vec3f getTranslation(float time) const override;
/// @brief Calls animation track update()
void update(float time, std::string animationName);
/// @brief Called every frame for osgAnimation
void operator() (osg::Node*, osg::NodeVisitor*) override;
/// @brief Sets details of the animations
void setEmulatedAnimations(std::vector<SceneUtil::EmulatedAnimation> emulatedAnimations);
/// @brief Adds an animation track to a model
void addMergedAnimationTrack(osg::ref_ptr<Resource::Animation> animationTrack);
private:
bool mNeedToLink = true;
osg::ref_ptr<LinkVisitor> mLinker;
};
}
#endif
Loading…
Cancel
Save