From: Mikko Rasa Date: Thu, 11 Apr 2013 20:21:50 +0000 (+0300) Subject: Add support for playing multiple stacked animations on an object X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=5ba1446b314ddc914693cb9cbb9a7b54a4d30a45 Add support for playing multiple stacked animations on an object --- diff --git a/source/animationplayer.cpp b/source/animationplayer.cpp index 9bcb5e57..eb18f0f9 100644 --- a/source/animationplayer.cpp +++ b/source/animationplayer.cpp @@ -7,34 +7,119 @@ using namespace std; namespace Msp { namespace GL { +AnimationPlayer::ObjectSlot &AnimationPlayer::get_slot(AnimatedObject &obj) +{ + ObjectMap::iterator i = objects.find(&obj); + if(i!=objects.end()) + return i->second; + + return objects.insert(ObjectMap::value_type(&obj, ObjectSlot(obj))).first->second; +} + void AnimationPlayer::play(AnimatedObject &obj, const Animation &anim) { - slots.push_back(Slot(obj, anim)); + ObjectSlot &obj_slot = get_slot(obj); + obj_slot.animations.clear(); + obj_slot.base_matrix = Matrix(); + obj_slot.stacked = false; + obj_slot.armature = anim.get_armature(); + obj_slot.animations.push_back(AnimationSlot(anim)); +} + +void AnimationPlayer::play_stacked(AnimatedObject &obj, const Animation &anim) +{ + ObjectSlot &obj_slot = get_slot(obj); + if(obj_slot.animations.empty()) + obj_slot.base_matrix = *obj.get_matrix(); + // TODO check for incompatible armature + obj_slot.stacked = true; + obj_slot.armature = anim.get_armature(); + obj_slot.animations.push_back(AnimationSlot(anim)); +} + +void AnimationPlayer::stop(AnimatedObject &obj) +{ + objects.erase(&obj); } void AnimationPlayer::tick(const Time::TimeDelta &dt) { - for(list::iterator i=slots.begin(); i!=slots.end(); ) + for(ObjectMap::iterator i=objects.begin(); i!=objects.end(); ) + { + bool keep = false; + if(i->second.stacked) + keep = tick_stacked(i->second, dt); + else + keep = tick_single(i->second, dt); + + if(!keep) + objects.erase(i++); + else + ++i; + } +} + +bool AnimationPlayer::tick_single(ObjectSlot &slot, const Time::TimeDelta &dt) +{ + AnimatedObject &obj = slot.object; + AnimationSlot &anim = slot.animations.front(); + anim.iterator += dt; + obj.set_matrix(anim.iterator.get_matrix()); + if(slot.armature) + { + unsigned max_index = slot.armature->get_max_link_index(); + for(unsigned i=0; i<=max_index; ++i) + obj.set_pose_matrix(i, anim.iterator.get_pose_matrix(i)); + } + + return !anim.iterator.is_end(); +} + +bool AnimationPlayer::tick_stacked(ObjectSlot &slot, const Time::TimeDelta &dt) +{ + Matrix matrix = slot.base_matrix; + for(AnimationList::iterator i=slot.animations.begin(); i!=slot.animations.end(); ++i) { i->iterator += dt; - i->object.set_matrix(i->iterator.get_matrix()); - if(const Armature *armature = i->animation.get_armature()) + matrix *= i->iterator.get_matrix(); + } + slot.object.set_matrix(matrix); + + if(slot.armature) + { + unsigned max_index = slot.armature->get_max_link_index(); + for(unsigned i=0; i<=max_index; ++i) { - unsigned max_index = armature->get_max_link_index(); - for(unsigned j=0; j<=max_index; ++j) - i->object.set_pose_matrix(j, i->iterator.get_pose_matrix(j)); + matrix = Matrix(); + /* XXX This is in all likelihood incorrect. The stacking should be + performed on local matrices. */ + for(AnimationList::iterator j=slot.animations.begin(); j!=slot.animations.end(); ++j) + if(j->animation.get_armature()) + matrix *= j->iterator.get_pose_matrix(i); + slot.object.set_pose_matrix(i, matrix); } + } + for(AnimationList::iterator i=slot.animations.begin(); i!=slot.animations.end(); ) + { if(i->iterator.is_end()) - slots.erase(i++); + slot.animations.erase(i++); else ++i; } + + return !slot.animations.empty(); } -AnimationPlayer::Slot::Slot(AnimatedObject &o, const Animation &a): +AnimationPlayer::ObjectSlot::ObjectSlot(AnimatedObject &o): object(o), + armature(0), + stacked(false) +{ } + + +AnimationPlayer::AnimationSlot::AnimationSlot(const Animation &a): animation(a), iterator(animation) { } diff --git a/source/animationplayer.h b/source/animationplayer.h index fe53a359..cc34e16a 100644 --- a/source/animationplayer.h +++ b/source/animationplayer.h @@ -16,24 +16,52 @@ can handle an arbitrary number of animations simultaneously. class AnimationPlayer { private: - struct Slot + struct AnimationSlot { - AnimatedObject &object; const Animation &animation; Animation::Iterator iterator; - Slot(AnimatedObject &, const Animation &); + AnimationSlot(const Animation &); + }; + + typedef std::list AnimationList; + + struct ObjectSlot + { + AnimatedObject &object; + Matrix base_matrix; + const Armature *armature; + AnimationList animations; + bool stacked; + + ObjectSlot(AnimatedObject &); }; - std::list slots; + typedef std::map ObjectMap; + + ObjectMap objects; + +private: + ObjectSlot &get_slot(AnimatedObject &); public: - /** Plays an animation on an object. */ + /// Plays an animation on an object. Any previous animations are replaced. void play(AnimatedObject &, const Animation &); + /** Plays an animation, stacked with other animations. If no animations are + playing yet, the object's current matrix is used as the base. */ + void play_stacked(AnimatedObject &, const Animation &); + + /// Stops any animations affecting an object. + void stop(AnimatedObject &); + /** Advances all playing animations. Should be called in a regular manner, preferably just before rendering. */ void tick(const Time::TimeDelta &); + +private: + bool tick_single(ObjectSlot &, const Time::TimeDelta &); + bool tick_stacked(ObjectSlot &, const Time::TimeDelta &); }; } // namespace GL