Merge branch 'osgAnimation_basics' into 'master'

Collada animation support

See merge request OpenMW/openmw!421
pull/3029/head
psi29a 4 years ago
commit dc1bd8ec29

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

@ -936,7 +936,7 @@ void split(const std::string &s, char delim, std::vector<std::string> &elems) {
}
}
void CharacterController::handleTextKey(const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key, const NifOsg::TextKeyMap& map)
void CharacterController::handleTextKey(const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key, const SceneUtil::TextKeyMap& map)
{
const std::string &evt = key->second;

@ -241,7 +241,7 @@ public:
CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim);
virtual ~CharacterController();
void handleTextKey(const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key, const NifOsg::TextKeyMap& map) override;
void handleTextKey(const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key, const SceneUtil::TextKeyMap& map) override;
// Be careful when to call this, see comment in Actors
void updateContinuousVfx();

@ -20,8 +20,7 @@
#include <components/misc/constants.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/nifosg/nifloader.hpp> // KeyframeHolder
#include <components/nifosg/controller.hpp>
#include <components/sceneutil/keyframe.hpp>
#include <components/vfs/manager.hpp>
@ -148,7 +147,7 @@ namespace
}
};
float calcAnimVelocity(const NifOsg::TextKeyMap& keys, NifOsg::KeyframeController *nonaccumctrl,
float calcAnimVelocity(const SceneUtil::TextKeyMap& keys, SceneUtil::KeyframeController *nonaccumctrl,
const osg::Vec3f& accum, const std::string &groupname)
{
const std::string start = groupname+": start";
@ -530,13 +529,13 @@ namespace MWRender
struct Animation::AnimSource
{
osg::ref_ptr<const NifOsg::KeyframeHolder> mKeyframes;
osg::ref_ptr<const SceneUtil::KeyframeHolder> mKeyframes;
typedef std::map<std::string, osg::ref_ptr<NifOsg::KeyframeController> > ControllerMap;
typedef std::map<std::string, osg::ref_ptr<SceneUtil::KeyframeController> > ControllerMap;
ControllerMap mControllerMap[Animation::sNumBlendMasks];
const NifOsg::TextKeyMap& getTextKeys() const;
const SceneUtil::TextKeyMap& getTextKeys() const;
};
void UpdateVfxCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
@ -688,7 +687,7 @@ namespace MWRender
return 0;
}
const NifOsg::TextKeyMap &Animation::AnimSource::getTextKeys() const
const SceneUtil::TextKeyMap &Animation::AnimSource::getTextKeys() const
{
return mKeyframes->mTextKeys;
}
@ -729,8 +728,6 @@ namespace MWRender
if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0)
kfname.replace(kfname.size()-4, 4, ".kf");
else
return;
addSingleAnimSource(kfname, baseModel);
@ -753,7 +750,7 @@ namespace MWRender
const NodeMap& nodeMap = getNodeMap();
for (NifOsg::KeyframeHolder::KeyframeControllerMap::const_iterator it = animsrc->mKeyframes->mKeyframeControllers.begin();
for (SceneUtil::KeyframeHolder::KeyframeControllerMap::const_iterator it = animsrc->mKeyframes->mKeyframeControllers.begin();
it != animsrc->mKeyframes->mKeyframeControllers.end(); ++it)
{
std::string bonename = Misc::StringUtils::lowerCase(it->first);
@ -769,7 +766,7 @@ namespace MWRender
size_t blendMask = detectBlendMask(node);
// clone the controller, because each Animation needs its own ControllerSource
osg::ref_ptr<NifOsg::KeyframeController> cloned = new NifOsg::KeyframeController(*it->second, osg::CopyOp::SHALLOW_COPY);
osg::ref_ptr<SceneUtil::KeyframeController> cloned = osg::clone(it->second.get(), osg::CopyOp::SHALLOW_COPY);
cloned->setSource(mAnimationTimePtr[blendMask]);
animsrc->mControllerMap[blendMask].insert(std::make_pair(bonename, cloned));
@ -785,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;
@ -810,7 +809,7 @@ namespace MWRender
AnimSourceList::const_iterator iter(mAnimSources.begin());
for(;iter != mAnimSources.end();++iter)
{
const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys();
const SceneUtil::TextKeyMap &keys = (*iter)->getTextKeys();
if (keys.hasGroupStart(anim))
return true;
}
@ -822,7 +821,7 @@ namespace MWRender
{
for(AnimSourceList::const_reverse_iterator iter(mAnimSources.rbegin()); iter != mAnimSources.rend(); ++iter)
{
const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys();
const SceneUtil::TextKeyMap &keys = (*iter)->getTextKeys();
const auto found = keys.findGroupStart(groupname);
if(found != keys.end())
@ -835,7 +834,7 @@ namespace MWRender
{
for(AnimSourceList::const_reverse_iterator iter(mAnimSources.rbegin()); iter != mAnimSources.rend(); ++iter)
{
const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys();
const SceneUtil::TextKeyMap &keys = (*iter)->getTextKeys();
for(auto iterKey = keys.begin(); iterKey != keys.end(); ++iterKey)
{
@ -847,8 +846,8 @@ namespace MWRender
return -1.f;
}
void Animation::handleTextKey(AnimState &state, const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key,
const NifOsg::TextKeyMap& map)
void Animation::handleTextKey(AnimState &state, const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key,
const SceneUtil::TextKeyMap& map)
{
const std::string &evt = key->second;
@ -911,7 +910,7 @@ namespace MWRender
AnimSourceList::reverse_iterator iter(mAnimSources.rbegin());
for(;iter != mAnimSources.rend();++iter)
{
const NifOsg::TextKeyMap &textkeys = (*iter)->getTextKeys();
const SceneUtil::TextKeyMap &textkeys = (*iter)->getTextKeys();
if(reset(state, textkeys, groupname, start, stop, startpoint, loopfallback))
{
state.mSource = *iter;
@ -956,7 +955,7 @@ namespace MWRender
resetActiveGroups();
}
bool Animation::reset(AnimState &state, const NifOsg::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, bool loopfallback)
bool Animation::reset(AnimState &state, const SceneUtil::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, bool loopfallback)
{
// Look for text keys in reverse. This normally wouldn't matter, but for some reason undeadwolf_2.nif has two
// separate walkforward keys, and the last one is supposed to be used.
@ -1186,7 +1185,7 @@ namespace MWRender
AnimSourceList::const_reverse_iterator animsrc(mAnimSources.rbegin());
for(;animsrc != mAnimSources.rend();++animsrc)
{
const NifOsg::TextKeyMap &keys = (*animsrc)->getTextKeys();
const SceneUtil::TextKeyMap &keys = (*animsrc)->getTextKeys();
if (keys.hasGroupStart(groupname))
break;
}
@ -1194,7 +1193,7 @@ namespace MWRender
return 0.0f;
float velocity = 0.0f;
const NifOsg::TextKeyMap &keys = (*animsrc)->getTextKeys();
const SceneUtil::TextKeyMap &keys = (*animsrc)->getTextKeys();
const AnimSource::ControllerMap& ctrls = (*animsrc)->mControllerMap[0];
for (AnimSource::ControllerMap::const_iterator it = ctrls.begin(); it != ctrls.end(); ++it)
@ -1215,7 +1214,7 @@ namespace MWRender
while(!(velocity > 1.0f) && ++animiter != mAnimSources.rend())
{
const NifOsg::TextKeyMap &keys2 = (*animiter)->getTextKeys();
const SceneUtil::TextKeyMap &keys2 = (*animiter)->getTextKeys();
const AnimSource::ControllerMap& ctrls2 = (*animiter)->mControllerMap[0];
for (AnimSource::ControllerMap::const_iterator it = ctrls2.begin(); it != ctrls2.end(); ++it)
@ -1265,7 +1264,7 @@ namespace MWRender
continue;
}
const NifOsg::TextKeyMap &textkeys = state.mSource->getTextKeys();
const SceneUtil::TextKeyMap &textkeys = state.mSource->getTextKeys();
auto textkey = textkeys.upperBound(state.getTime());
float timepassed = duration * state.mSpeedMult;
@ -1839,7 +1838,7 @@ namespace MWRender
osg::Callback* cb = node->getUpdateCallback();
while (cb)
{
if (dynamic_cast<NifOsg::KeyframeController*>(cb))
if (dynamic_cast<SceneUtil::KeyframeController*>(cb))
{
foundKeyframeCtrl = true;
break;

@ -4,8 +4,8 @@
#include "../mwworld/ptr.hpp"
#include <components/sceneutil/controller.hpp>
#include <components/sceneutil/textkeymap.hpp>
#include <components/sceneutil/util.hpp>
#include <components/nifosg/textkeymap.hpp>
#include <vector>
@ -20,14 +20,10 @@ namespace Resource
class ResourceSystem;
}
namespace NifOsg
namespace SceneUtil
{
class KeyframeHolder;
class KeyframeController;
}
namespace SceneUtil
{
class LightSource;
class LightListCallback;
class Skeleton;
@ -150,8 +146,8 @@ public:
class TextKeyListener
{
public:
virtual void handleTextKey(const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key,
const NifOsg::TextKeyMap& map) = 0;
virtual void handleTextKey(const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key,
const SceneUtil::TextKeyMap& map) = 0;
virtual ~TextKeyListener() = default;
};
@ -242,7 +238,7 @@ protected:
osg::ref_ptr<osg::Node> mAccumRoot;
// The controller animating that node.
osg::ref_ptr<NifOsg::KeyframeController> mAccumCtrl;
osg::ref_ptr<SceneUtil::KeyframeController> mAccumCtrl;
// Used to reset the position of the accumulation root every frame - the movement should be applied to the physics system
osg::ref_ptr<ResetAccumRootCallback> mResetAccumRootCallback;
@ -306,12 +302,12 @@ protected:
* the marker is not found, or if the markers are the same, it returns
* false.
*/
bool reset(AnimState &state, const NifOsg::TextKeyMap &keys,
bool reset(AnimState &state, const SceneUtil::TextKeyMap &keys,
const std::string &groupname, const std::string &start, const std::string &stop,
float startpoint, bool loopfallback);
void handleTextKey(AnimState &state, const std::string &groupname, NifOsg::TextKeyMap::ConstIterator key,
const NifOsg::TextKeyMap& map);
void handleTextKey(AnimState &state, const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key,
const SceneUtil::TextKeyMap& map);
/** Sets the root model of the object.
*

@ -19,11 +19,10 @@
#include <components/sceneutil/attach.hpp>
#include <components/sceneutil/visitor.hpp>
#include <components/sceneutil/skeleton.hpp>
#include <components/sceneutil/keyframe.hpp>
#include <components/settings/settings.hpp>
#include <components/nifosg/nifloader.hpp> // TextKeyMapHolder
#include <components/vfs/manager.hpp>
#include "../mwworld/esmstore.hpp"
@ -864,7 +863,7 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g
for (unsigned int i=0; i<node->getUserDataContainer()->getNumUserObjects(); ++i)
{
osg::Object* obj = node->getUserDataContainer()->getUserObject(i);
if (NifOsg::TextKeyMapHolder* keys = dynamic_cast<NifOsg::TextKeyMapHolder*>(obj))
if (SceneUtil::TextKeyMapHolder* keys = dynamic_cast<SceneUtil::TextKeyMapHolder*>(obj))
{
for (const auto &key : keys->mTextKeys)
{

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

@ -71,8 +71,7 @@ KeyframeController::KeyframeController()
}
KeyframeController::KeyframeController(const KeyframeController &copy, const osg::CopyOp &copyop)
: osg::NodeCallback(copy, copyop)
, Controller(copy)
: SceneUtil::KeyframeController(copy, copyop)
, mRotations(copy.mRotations)
, mXRotations(copy.mXRotations)
, mYRotations(copy.mYRotations)

@ -6,7 +6,7 @@
#include <components/nif/controller.hpp>
#include <components/nif/data.hpp>
#include <components/sceneutil/controller.hpp>
#include <components/sceneutil/keyframe.hpp>
#include <components/sceneutil/statesetupdater.hpp>
#include <set>
@ -226,7 +226,7 @@ namespace NifOsg
std::vector<FloatInterpolator> mKeyFrames;
};
class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller
class KeyframeController : public SceneUtil::KeyframeController
{
public:
// This is used if there's no interpolator but there is data (Morrowind meshes).
@ -242,7 +242,7 @@ namespace NifOsg
META_Object(NifOsg, KeyframeController)
virtual osg::Vec3f getTranslation(float time) const;
osg::Vec3f getTranslation(float time) const override;
void operator() (osg::Node*, osg::NodeVisitor*) override;

@ -139,7 +139,7 @@ namespace
}
};
void extractTextKeys(const Nif::NiTextKeyExtraData *tk, NifOsg::TextKeyMap &textkeys)
void extractTextKeys(const Nif::NiTextKeyExtraData *tk, SceneUtil::TextKeyMap &textkeys)
{
for(size_t i = 0;i < tk->list.size();i++)
{
@ -234,7 +234,7 @@ namespace NifOsg
// This is used to queue emitters that weren't attached to their node yet.
std::vector<std::pair<size_t, osg::ref_ptr<Emitter>>> mEmitterQueue;
static void loadKf(Nif::NIFFilePtr nif, KeyframeHolder& target)
static void loadKf(Nif::NIFFilePtr nif, SceneUtil::KeyframeHolder& target)
{
const Nif::NiSequenceStreamHelper *seq = nullptr;
const size_t numRoots = nif->numRoots();
@ -284,7 +284,7 @@ namespace NifOsg
if (key->data.empty() && key->interpolator.empty())
continue;
osg::ref_ptr<NifOsg::KeyframeController> callback(handleKeyframeController(key));
osg::ref_ptr<SceneUtil::KeyframeController> callback(handleKeyframeController(key));
callback->setFunction(std::shared_ptr<NifOsg::ControllerFunction>(new NifOsg::ControllerFunction(key)));
if (!target.mKeyframeControllers.emplace(strdata->string, callback).second)
@ -305,7 +305,7 @@ namespace NifOsg
if (!nifNode)
nif->fail("Found no root nodes");
osg::ref_ptr<TextKeyMapHolder> textkeys (new TextKeyMapHolder);
osg::ref_ptr<SceneUtil::TextKeyMapHolder> textkeys (new SceneUtil::TextKeyMapHolder);
osg::ref_ptr<osg::Node> created = handleNode(nifNode, nullptr, imageManager, std::vector<unsigned int>(), 0, false, false, false, &textkeys->mTextKeys);
@ -353,10 +353,10 @@ namespace NifOsg
else if (props[i].getPtr()->recType == Nif::RC_NiTexturingProperty)
{
if (props[i].getPtr()->recIndex == mFirstRootTextureIndex)
applyTo->setUserValue("overrideFx", 1);
applyTo->setUserValue("overrideFx", 1);
}
handleProperty(props[i].getPtr(), applyTo, composite, imageManager, boundTextures, animflags);
}
}
}
}
@ -514,7 +514,7 @@ namespace NifOsg
}
osg::ref_ptr<osg::Node> handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager,
std::vector<unsigned int> boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool hasAnimatedParents, TextKeyMap* textKeys, osg::Node* rootNode=nullptr)
std::vector<unsigned int> boundTextures, int animflags, bool skipMeshes, bool hasMarkers, bool hasAnimatedParents, SceneUtil::TextKeyMap* textKeys, osg::Node* rootNode=nullptr)
{
if (rootNode != nullptr && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box"))
return nullptr;
@ -1197,7 +1197,7 @@ namespace NifOsg
for (const auto& strip : data->strips)
{
if (strip.size() >= 3)
geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(),
geometry->addPrimitiveSet(new osg::DrawElementsUShort(osg::PrimitiveSet::TRIANGLE_STRIP, strip.size(),
(unsigned short*)strip.data()));
}
}
@ -1929,7 +1929,7 @@ namespace NifOsg
return impl.load(file, imageManager);
}
void Loader::loadKf(Nif::NIFFilePtr kf, KeyframeHolder& target)
void Loader::loadKf(Nif::NIFFilePtr kf, SceneUtil::KeyframeHolder& target)
{
LoaderImpl impl(kf->getFilename(), kf->getVersion(), kf->getUserVersion(), kf->getBethVersion());
impl.loadKf(kf, target);

@ -2,12 +2,13 @@
#define OPENMW_COMPONENTS_NIFOSG_LOADER
#include <components/nif/niffile.hpp>
#include <components/sceneutil/keyframe.hpp>
#include <components/sceneutil/textkeymap.hpp>
#include <osg/ref_ptr>
#include <osg/Referenced>
#include "controller.hpp"
#include "textkeymap.hpp"
namespace osg
{
@ -21,39 +22,6 @@ namespace Resource
namespace NifOsg
{
struct TextKeyMapHolder : public osg::Object
{
public:
TextKeyMapHolder() {}
TextKeyMapHolder(const TextKeyMapHolder& copy, const osg::CopyOp& copyop)
: osg::Object(copy, copyop)
, mTextKeys(copy.mTextKeys)
{}
TextKeyMap mTextKeys;
META_Object(NifOsg, TextKeyMapHolder)
};
class KeyframeHolder : public osg::Object
{
public:
KeyframeHolder() {}
KeyframeHolder(const KeyframeHolder& copy, const osg::CopyOp& copyop)
: mTextKeys(copy.mTextKeys)
, mKeyframeControllers(copy.mKeyframeControllers)
{
}
TextKeyMap mTextKeys;
META_Object(OpenMW, KeyframeHolder)
typedef std::map<std::string, osg::ref_ptr<const KeyframeController> > KeyframeControllerMap;
KeyframeControllerMap mKeyframeControllers;
};
/// The main class responsible for loading NIF files into an OSG-Scenegraph.
/// @par This scene graph is self-contained and can be cloned using osg::clone if desired. Particle emitters
/// and programs hold a pointer to their ParticleSystem, which would need to be manually updated when cloning.
@ -64,7 +32,7 @@ namespace NifOsg
static osg::ref_ptr<osg::Node> load(Nif::NIFFilePtr file, Resource::ImageManager* imageManager);
/// Load keyframe controllers from the given kf file.
static void loadKf(Nif::NIFFilePtr kf, KeyframeHolder& target);
static void loadKf(Nif::NIFFilePtr kf, SceneUtil::KeyframeHolder& target);
/// Set whether or not nodes marked as "MRK" should be shown.
/// These should be hidden ingame, but visible in the editor.

@ -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,13 +2,88 @@
#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 Resource
{
KeyframeManager::KeyframeManager(const VFS::Manager* vfs)
RetrieveAnimationsVisitor::RetrieveAnimationsVisitor(SceneUtil::KeyframeHolder& target, osg::ref_ptr<osgAnimation::BasicAnimationManager> animationManager) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), mTarget(target), mAnimationManager(animationManager) {}
void RetrieveAnimationsVisitor::apply(osg::Node& node)
{
if (node.libraryName() == std::string("osgAnimation") && node.className() == std::string("Bone") && node.getName() == std::string("root"))
{
osg::ref_ptr<SceneUtil::OsgAnimationController> callback = new SceneUtil::OsgAnimationController();
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);
}
traverse(node);
}
}
namespace Resource
{
KeyframeManager::KeyframeManager(const VFS::Manager* vfs, SceneManager* sceneManager)
: ResourceManager(vfs)
, mSceneManager(sceneManager)
{
}
@ -16,19 +91,32 @@ namespace Resource
{
}
osg::ref_ptr<const NifOsg::KeyframeHolder> KeyframeManager::get(const std::string &name)
osg::ref_ptr<const SceneUtil::KeyframeHolder> KeyframeManager::get(const std::string &name)
{
std::string normalized = name;
mVFS->normalizeFilename(normalized);
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);
if (obj)
return osg::ref_ptr<const NifOsg::KeyframeHolder>(static_cast<NifOsg::KeyframeHolder*>(obj.get()));
return osg::ref_ptr<const SceneUtil::KeyframeHolder>(static_cast<SceneUtil::KeyframeHolder*>(obj.get()));
else
{
osg::ref_ptr<NifOsg::KeyframeHolder> loaded (new NifOsg::KeyframeHolder);
NifOsg::Loader::loadKf(Nif::NIFFilePtr(new Nif::NIFFile(mVFS->getNormalized(normalized), normalized)), *loaded.get());
osg::ref_ptr<SceneUtil::KeyframeHolder> loaded (new SceneUtil::KeyframeHolder);
std::string ext = Resource::getFileExtension(normalized);
if (ext == "kf")
{
NifOsg::Loader::loadKf(Nif::NIFFilePtr(new Nif::NIFFile(mVFS->getNormalized(normalized), normalized)), *loaded.get());
}
else
{
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());
if (bam)
{
Resource::RetrieveAnimationsVisitor rav(*loaded.get(), bam);
scene->accept(rav);
}
}
mCache->addEntryToObjectCache(normalized, loaded);
return loaded;
}

@ -2,28 +2,50 @@
#define OPENMW_COMPONENTS_KEYFRAMEMANAGER_H
#include <osg/ref_ptr>
#include <osgAnimation/BasicAnimationManager>
#include <string>
#include <components/nifosg/nifloader.hpp>
#include <components/sceneutil/keyframe.hpp>
#include "resourcemanager.hpp"
namespace Resource
{
/// @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) override;
private:
SceneUtil::KeyframeHolder& mTarget;
osg::ref_ptr<osgAnimation::BasicAnimationManager> mAnimationManager;
};
}
namespace Resource
{
class SceneManager;
/// @brief Managing of keyframe resources
/// @note May be used from any thread.
class KeyframeManager : public ResourceManager
{
public:
KeyframeManager(const VFS::Manager* vfs);
KeyframeManager(const VFS::Manager* vfs, SceneManager* sceneManager);
~KeyframeManager();
/// Retrieve a read-only keyframe resource by name (case-insensitive).
/// @note Throws an exception if the resource is not found.
osg::ref_ptr<const NifOsg::KeyframeHolder> get(const std::string& name);
osg::ref_ptr<const SceneUtil::KeyframeHolder> get(const std::string& name);
void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;
private:
SceneManager* mSceneManager;
};
}

@ -14,9 +14,9 @@ namespace Resource
: mVFS(vfs)
{
mNifFileManager.reset(new NifFileManager(vfs));
mKeyframeManager.reset(new KeyframeManager(vfs));
mImageManager.reset(new ImageManager(vfs));
mSceneManager.reset(new SceneManager(vfs, mImageManager.get(), mNifFileManager.get()));
mKeyframeManager.reset(new KeyframeManager(vfs, mSceneManager.get()));
addResourceManager(mNifFileManager.get());
addResourceManager(mKeyframeManager.get());

@ -338,17 +338,9 @@ namespace Resource
Resource::ImageManager* mImageManager;
};
std::string getFileExtension(const std::string& file)
{
size_t extPos = file.find_last_of('.');
if (extPos != std::string::npos && extPos+1 < file.size())
return file.substr(extPos+1);
return std::string();
}
osg::ref_ptr<osg::Node> load (Files::IStreamPtr file, const std::string& normalizedFilename, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager)
{
std::string ext = getFileExtension(normalizedFilename);
std::string ext = Resource::getFileExtension(normalizedFilename);
if (ext == "nif")
return NifOsg::Loader::load(nifFileManager->get(normalizedFilename), imageManager);
else
@ -492,7 +484,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)
{
@ -780,4 +772,11 @@ namespace Resource
return shaderVisitor;
}
std::string getFileExtension(const std::string& file)
{
size_t extPos = file.find_last_of('.');
if (extPos != std::string::npos && extPos+1 < file.size())
return file.substr(extPos+1);
return std::string();
}
}

@ -181,6 +181,7 @@ namespace Resource
void operator = (const SceneManager&);
};
std::string getFileExtension(const std::string& file);
}
#endif

@ -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));
}

@ -0,0 +1,68 @@
#ifndef OPENMW_COMPONENTS_SCENEUTIL_KEYFRAME_HPP
#define OPENMW_COMPONENTS_SCENEUTIL_KEYFRAME_HPP
#include <map>
#include <osg/Node>
#include <components/sceneutil/controller.hpp>
#include <components/sceneutil/textkeymap.hpp>
#include <components/resource/animation.hpp>
namespace SceneUtil
{
class KeyframeController : public osg::NodeCallback, public SceneUtil::Controller
{
public:
KeyframeController() {}
KeyframeController(const KeyframeController& copy, const osg::CopyOp& copyop)
: osg::NodeCallback(copy, copyop)
, SceneUtil::Controller(copy)
{}
META_Object(SceneUtil, KeyframeController)
virtual osg::Vec3f getTranslation(float time) const { return osg::Vec3f(); }
virtual void operator() (osg::Node* node, osg::NodeVisitor* nodeVisitor) override { traverse(node, nodeVisitor); }
};
/// Wrapper object containing an animation track as a ref-countable osg::Object.
struct TextKeyMapHolder : public osg::Object
{
public:
TextKeyMapHolder() {}
TextKeyMapHolder(const TextKeyMapHolder& copy, const osg::CopyOp& copyop)
: osg::Object(copy, copyop)
, mTextKeys(copy.mTextKeys)
{}
TextKeyMap mTextKeys;
META_Object(SceneUtil, TextKeyMapHolder)
};
/// Wrapper object containing the animation track and its KeyframeControllers.
class KeyframeHolder : public osg::Object
{
public:
KeyframeHolder() {}
KeyframeHolder(const KeyframeHolder& copy, const osg::CopyOp& copyop)
: mTextKeys(copy.mTextKeys)
, mKeyframeControllers(copy.mKeyframeControllers)
{
}
TextKeyMap mTextKeys;
META_Object(SceneUtil, KeyframeHolder)
/// Controllers mapped to node name.
typedef std::map<std::string, osg::ref_ptr<const KeyframeController> > KeyframeControllerMap;
KeyframeControllerMap mKeyframeControllers;
};
}
#endif

@ -0,0 +1,197 @@
#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 SceneUtil
{
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));
}
OsgAnimationController::OsgAnimationController(const OsgAnimationController &copy, const osg::CopyOp &copyop) : SceneUtil::KeyframeController(copy, copyop)
, mMergedAnimationTracks(copy.mMergedAnimationTracks)
, mEmulatedAnimations(copy.mEmulatedAnimations)
{
mLinker = nullptr;
}
osg::Vec3f OsgAnimationController::getTranslation(float time) const
{
osg::Vec3f translationValue;
std::string animationName;
float newTime = time;
//Find the correct animation based on time
for (const EmulatedAnimation& emulatedAnimation : mEmulatedAnimations)
{
if (time > emulatedAnimation.mStartTime && time < emulatedAnimation.mStopTime)
{
newTime = time - emulatedAnimation.mStartTime;
animationName = emulatedAnimation.mName;
}
}
//Find the root transform track in animation
for (const osg::ref_ptr<Resource::Animation> 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 OsgAnimationController::update(float time, std::string animationName)
{
for (const osg::ref_ptr<Resource::Animation> mergedAnimationTrack : mMergedAnimationTracks)
{
if (mergedAnimationTrack->getName() == animationName) mergedAnimationTrack->update(time);
}
}
void OsgAnimationController::operator() (osg::Node* node, osg::NodeVisitor* nv)
{
if (hasInput())
{
if (mNeedToLink)
{
for (const osg::ref_ptr<Resource::Animation> mergedAnimationTrack : mMergedAnimationTracks)
{
if (!mLinker.valid()) mLinker = new LinkVisitor();
mLinker->setAnimation(mergedAnimationTrack);
node->accept(*mLinker);
}
mNeedToLink = false;
}
float time = getInputValue(nv);
for (const EmulatedAnimation& emulatedAnimation : mEmulatedAnimations)
{
if (time > emulatedAnimation.mStartTime && time < emulatedAnimation.mStopTime)
{
update(time - emulatedAnimation.mStartTime, emulatedAnimation.mName);
}
}
}
traverse(node, nv);
}
void OsgAnimationController::setEmulatedAnimations(std::vector<EmulatedAnimation> emulatedAnimations)
{
mEmulatedAnimations = emulatedAnimations;
}
void OsgAnimationController::addMergedAnimationTrack(osg::ref_ptr<Resource::Animation> animationTrack)
{
mMergedAnimationTracks.emplace_back(animationTrack);
}
}

@ -0,0 +1,80 @@
#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 SceneUtil
{
struct EmulatedAnimation
{
float mStartTime;
float mStopTime;
std::string mName;
};
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) override;
virtual void apply(osg::Geode& node) override;
protected:
Resource::Animation* mAnimation;
};
class OsgAnimationController : public SceneUtil::KeyframeController
{
public:
/// @brief Handles the animation for osgAnimation formats
OsgAnimationController() {};
OsgAnimationController(const OsgAnimationController& copy, const osg::CopyOp& copyop);
META_Object(SceneUtil, OsgAnimationController)
/// @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<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;
std::vector<osg::ref_ptr<Resource::Animation>> mMergedAnimationTracks; // Used only by osgAnimation-based formats (e.g. dae)
std::vector<EmulatedAnimation> mEmulatedAnimations;
};
}
#endif

@ -1,12 +1,12 @@
#ifndef OPENMW_COMPONENTS_NIFOSG_TEXTKEYMAP
#define OPENMW_COMPONENTS_NIFOSG_TEXTKEYMAP
#ifndef OPENMW_COMPONENTS_SCENEUTIL_TEXTKEYMAP
#define OPENMW_COMPONENTS_SCENEUTIL_TEXTKEYMAP
#include <algorithm>
#include <map>
#include <set>
#include <string>
namespace NifOsg
namespace SceneUtil
{
class TextKeyMap
{
Loading…
Cancel
Save