]> git.tdb.fi Git - libs/gl.git/commitdiff
Implement an event system for animations
authorMikko Rasa <tdb@tdb.fi>
Wed, 2 May 2018 21:49:15 +0000 (00:49 +0300)
committerMikko Rasa <tdb@tdb.fi>
Wed, 2 May 2018 22:07:15 +0000 (01:07 +0300)
It can be used to sync other actions to certain points of the animation.

source/animation.cpp
source/animation.h
source/animationeventobserver.h [new file with mode: 0644]
source/animationplayer.cpp
source/animationplayer.h

index 2f78d1eafad574b4d1c060a8209d0527309cc393..744262d84781ae40e0b2c42b604b1b2badb516cf 100644 (file)
@@ -3,6 +3,7 @@
 #include <msp/datafile/collection.h>
 #include <msp/time/units.h>
 #include "animation.h"
+#include "animationeventobserver.h"
 #include "armature.h"
 #include "error.h"
 #include "pose.h"
@@ -71,6 +72,15 @@ void Animation::add_keyframe(const Time::TimeDelta &t, const RefPtr<const KeyFra
        prepare_keyframe(tkf);
 }
 
+void Animation::add_event(const Time::TimeDelta &t, const string &n, const Variant &v)
+{
+       Event event;
+       event.time = t;
+       event.name = n;
+       event.value = v;
+       events.push_back(event);
+}
+
 void Animation::set_looping(bool l)
 {
        looping = l;
@@ -234,6 +244,7 @@ Animation::UniformInfo::UniformInfo(const string &n, unsigned s):
 Animation::Iterator::Iterator(const Animation &a):
        animation(&a),
        iter(animation->keyframes.begin()),
+       event_iter(animation->events.begin()),
        end(false)
 { }
 
@@ -263,6 +274,24 @@ Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
        return *this;
 }
 
+void Animation::Iterator::dispatch_events(AnimationEventObserver &observer)
+{
+       vector<Event>::const_iterator events_end = animation->events.end();
+       if(end)
+       {
+               for(; event_iter!=events_end; ++event_iter)
+                       observer.animation_event(0, event_iter->name, event_iter->value);
+       }
+       else if(event_iter!=events_end)
+       {
+               Time::TimeDelta t = time_since_keyframe;
+               if(iter->prev)
+                       t += iter->prev->time;
+               for(; (event_iter!=events_end && event_iter->time<=t); ++event_iter)
+                       observer.animation_event(0, event_iter->name, event_iter->value);
+       }
+}
+
 Matrix Animation::Iterator::get_matrix() const
 {
        if(!iter->prev)
@@ -329,12 +358,48 @@ Animation::Loader::Loader(Animation &a, Collection &c):
 void Animation::Loader::init()
 {
        add("armature", &Animation::armature);
+       add("event", &Loader::event);
+       add("event", &Loader::event1i);
+       add("event", &Loader::event1f);
+       add("event", &Loader::event2f);
+       add("event", &Loader::event3f);
+       add("event", &Loader::event4f);
        add("interval", &Loader::interval);
        add("keyframe", &Loader::keyframe);
        add("keyframe", &Loader::keyframe_inline);
        add("looping", &Animation::looping);
 }
 
+void Animation::Loader::event(const string &n)
+{
+       obj.add_event(current_time, n);
+}
+
+void Animation::Loader::event1i(const string &n, int v)
+{
+       obj.add_event(current_time, n, v);
+}
+
+void Animation::Loader::event1f(const string &n, float v)
+{
+       obj.add_event(current_time, n, v);
+}
+
+void Animation::Loader::event2f(const string &n, float v0, float v1)
+{
+       obj.add_event(current_time, n, LinAl::Vector<float, 2>(v0, v1));
+}
+
+void Animation::Loader::event3f(const string &n, float v0, float v1, float v2)
+{
+       obj.add_event(current_time, n, Vector3(v0, v1, v2));
+}
+
+void Animation::Loader::event4f(const string &n, float v0, float v1, float v2, float v3)
+{
+       obj.add_event(current_time, n, Vector4(v0, v1, v2, v3));
+}
+
 void Animation::Loader::keyframe(const string &n)
 {
        obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
index dd3cc688c41290316450858a7d540a79e93dcfb8..c190bdbfd1cb11048020ad7ba06a44fff81d1a24 100644 (file)
@@ -9,6 +9,7 @@
 namespace Msp {
 namespace GL {
 
+class AnimationEventObserver;
 class Armature;
 class Matrix;
 class Pose;
@@ -31,6 +32,12 @@ public:
        private:
                void init();
 
+               void event(const std::string &);
+               void event1i(const std::string &, int);
+               void event1f(const std::string &, float);
+               void event2f(const std::string &, float, float);
+               void event3f(const std::string &, float, float, float);
+               void event4f(const std::string &, float, float, float, float);
                void keyframe(const std::string &);
                void keyframe_inline();
                void interval(float);
@@ -72,6 +79,13 @@ private:
                void prepare(const Animation &);
        };
 
+       struct Event
+       {
+               Time::TimeDelta time;
+               std::string name;
+               Variant value;
+       };
+
        struct UniformInfo
        {
                std::string name;
@@ -86,6 +100,7 @@ public:
        private:
                const Animation *animation;
                std::vector<TimedKeyFrame>::const_iterator iter;
+               std::vector<Event>::const_iterator event_iter;
                Time::TimeDelta time_since_keyframe;
                bool end;
 
@@ -93,6 +108,7 @@ public:
                Iterator(const Animation &);
 
                Iterator &operator+=(const Time::TimeDelta &);
+               void dispatch_events(AnimationEventObserver &);
 
                bool is_end() const { return end; }
                Matrix get_matrix() const;
@@ -103,6 +119,7 @@ public:
 private:
        const Armature *armature;
        std::vector<TimedKeyFrame> keyframes;
+       std::vector<Event> events;
        bool looping;
        std::vector<UniformInfo> uniforms;
 
@@ -121,8 +138,9 @@ public:
 private:
        void add_keyframe(const Time::TimeDelta &, const RefPtr<const KeyFrame> &);
        void prepare_keyframe(TimedKeyFrame &);
-
 public:
+       void add_event(const Time::TimeDelta &, const std::string &, const Variant & = Variant());
+
        void set_looping(bool);
 };
 
diff --git a/source/animationeventobserver.h b/source/animationeventobserver.h
new file mode 100644 (file)
index 0000000..167b966
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef MSP_GL_ANIMATIONEVENTOBSERVER_H_
+#define MSP_GL_ANIMATIONEVENTOBSERVER_H_
+
+#include <string>
+#include <msp/core/variant.h>
+
+namespace Msp {
+namespace GL {
+
+class AnimatedObject;
+
+class AnimationEventObserver
+{
+protected:
+       AnimationEventObserver() { }
+public:
+       virtual ~AnimationEventObserver() { }
+
+       virtual void animation_event(AnimatedObject *, const std::string &, const Variant &) { }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
index cd71679800fffea672ebea6c4ef84ed796622104..6b5a481f6a74f8da98b138067d803d8aacfbe785 100644 (file)
@@ -1,3 +1,4 @@
+#include <msp/core/algorithm.h>
 #include "animatedobject.h"
 #include "animationplayer.h"
 #include "armature.h"
@@ -44,6 +45,34 @@ unsigned AnimationPlayer::get_n_active_animations(const AnimatedObject &obj) con
        return (i!=objects.end() ? i->second.animations.size() : 0);
 }
 
+void AnimationPlayer::observe_events(AnimatedObject &obj, AnimationEventObserver &observer)
+{
+       Target &target = get_slot(obj);
+       if(find(target.event_observers, &observer)==target.event_observers.end())
+               target.event_observers.push_back(&observer);
+}
+
+void AnimationPlayer::unobserve_events(AnimatedObject &obj, AnimationEventObserver &observer)
+{
+       ObjectMap::iterator i = objects.find(&obj);
+       if(i==objects.end())
+               return;
+
+       vector<AnimationEventObserver *>::iterator j = find(i->second.event_observers, &observer);
+       if(j!=i->second.event_observers.end())
+               i->second.event_observers.erase(j);
+}
+
+void AnimationPlayer::unobserve_events(AnimationEventObserver &observer)
+{
+       for(ObjectMap::iterator i=objects.begin(); i!=objects.end(); ++i)
+       {
+               vector<AnimationEventObserver *>::iterator j = find(i->second.event_observers, &observer);
+               if(j!=i->second.event_observers.end())
+                       i->second.event_observers.erase(j);
+       }
+}
+
 void AnimationPlayer::stop(AnimatedObject &obj)
 {
        objects.erase(&obj);
@@ -72,10 +101,10 @@ void AnimationPlayer::tick(const Time::TimeDelta &dt)
        {
                if(i->second.stacked)
                        tick_stacked(i->second, dt);
-               else
+               else if(!i->second.animations.empty())
                        tick_single(i->second, dt);
 
-               if(i->second.animations.empty())
+               if(i->second.animations.empty() && i->second.event_observers.empty())
                        objects.erase(i++);
                else
                        ++i;
@@ -99,6 +128,8 @@ void AnimationPlayer::tick_single(Target &target, const Time::TimeDelta &dt)
                        target.object.set_pose_matrix(i, anim.iterator.get_pose_matrix(i));
        }
 
+       anim.iterator.dispatch_events(target);
+
        if(!anim.iterator.is_end())
                target.animations.clear();
 }
@@ -134,6 +165,8 @@ void AnimationPlayer::tick_stacked(Target &target, const Time::TimeDelta &dt)
 
        for(vector<PlayingAnimation>::iterator i=target.animations.begin(); i!=target.animations.end(); )
        {
+               i->iterator.dispatch_events(target);
+
                if(i->iterator.is_end())
                        i = target.animations.erase(i);
                else
@@ -165,6 +198,12 @@ AnimationPlayer::Target::Target(AnimatedObject &o):
        stacked(false)
 { }
 
+void AnimationPlayer::Target::animation_event(AnimatedObject *, const string &name, const Variant &value)
+{
+       for(vector<AnimationEventObserver *>::const_iterator i=event_observers.begin(); i!=event_observers.end(); ++i)
+               (*i)->animation_event(&object, name, value);
+}
+
 
 AnimationPlayer::PlayingAnimation::PlayingAnimation(const Animation &a):
        animation(&a),
index 5d5f4cb85a260f88f9d5a109004b71b982c05564..4c6e9ceca50bbc2fc0c7beeb5f256658a81c7150 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <msp/time/timedelta.h>
 #include "animation.h"
+#include "animationeventobserver.h"
 #include "matrix.h"
 
 namespace Msp {
@@ -32,8 +33,11 @@ private:
                const Armature *armature;
                std::vector<PlayingAnimation> animations;
                bool stacked;
+               std::vector<AnimationEventObserver *> event_observers;
 
                Target(AnimatedObject &);
+
+               virtual void animation_event(AnimatedObject *, const std::string &, const Variant &);
        };
 
        typedef std::map<const AnimatedObject *, Target> ObjectMap;
@@ -54,6 +58,17 @@ public:
        /// Returns the number of animations currently affecting an object.
        unsigned get_n_active_animations(const AnimatedObject &) const;
 
+       /** Request delivery of animation events for the given object.  Events will
+       be delivered from all current and future animations until the observer is
+       removed. */
+       void observe_events(AnimatedObject &, AnimationEventObserver &);
+
+       /// Remove an event observer from one object.
+       void unobserve_events(AnimatedObject &, AnimationEventObserver &);
+
+       /// Remove an event observer from all objects.
+       void unobserve_events(AnimationEventObserver &);
+
        /// Stops all animations affecting an object.
        void stop(AnimatedObject &);