]> git.tdb.fi Git - libs/gl.git/commitdiff
Add support for playing multiple stacked animations on an object
authorMikko Rasa <tdb@tdb.fi>
Thu, 11 Apr 2013 20:21:50 +0000 (23:21 +0300)
committerMikko Rasa <tdb@tdb.fi>
Thu, 11 Apr 2013 20:21:50 +0000 (23:21 +0300)
source/animationplayer.cpp
source/animationplayer.h

index 9bcb5e5720872d006b28ac6f8a9c1dec7421c26b..eb18f0f94b42e43cf49ad8dfa6bbe95232b3a200 100644 (file)
@@ -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<Slot>::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)
 { }
index fe53a359d800ce588c8bbbce300c9e3ae94ebfa6..cc34e16a513f411f1c427364f91eee18bda10b11 100644 (file)
@@ -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<AnimationSlot> AnimationList;
+
+       struct ObjectSlot
+       {
+               AnimatedObject &object;
+               Matrix base_matrix;
+               const Armature *armature;
+               AnimationList animations;
+               bool stacked;
+
+               ObjectSlot(AnimatedObject &);
        };
 
-       std::list<Slot> slots;
+       typedef std::map<AnimatedObject *, ObjectSlot> 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