]> git.tdb.fi Git - libs/gl.git/blobdiff - source/animation.cpp
Fix a stupid error with PixelStore parameter mask
[libs/gl.git] / source / animation.cpp
index eb22ab29f0462fa5c686b2542b9b21ba155f5a6e..c7e23b8e3b26f8bda1332cafcf3efb3d3680845b 100644 (file)
 #include <cmath>
+#include <msp/core/maputils.h>
 #include <msp/datafile/collection.h>
 #include <msp/time/units.h>
 #include "animation.h"
-#include "keyframe.h"
+#include "animationeventobserver.h"
+#include "armature.h"
+#include "error.h"
+#include "pose.h"
 
 using namespace std;
 
-#include <msp/io/print.h>
-
 namespace Msp {
 namespace GL {
 
 Animation::Animation():
+       armature(0),
        looping(false)
 { }
 
+// Avoid synthesizing ~RefPtr in files including animation.h
+Animation::~Animation()
+{ }
+
+void Animation::set_armature(const Armature &a)
+{
+       armature = &a;
+}
+
+unsigned Animation::get_slot_for_uniform(const string &n) const
+{
+       for(unsigned i=0; i<uniforms.size(); ++i)
+               if(uniforms[i].name==n)
+                       return i;
+       throw key_error(n);
+}
+
+const string &Animation::get_uniform_name(unsigned i) const
+{
+       if(i>=uniforms.size())
+               throw out_of_range("Animation::get_uniform_name");
+       return uniforms[i].name;
+}
+
 void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
+{
+       RefPtr<const KeyFrame> kfr(&kf);
+       kfr.keep();
+       add_keyframe(t, kfr);
+}
+
+void Animation::add_keyframe(const Time::TimeDelta &t, const RefPtr<const KeyFrame> &kf)
 {
        if(!keyframes.empty() && t<keyframes.back().time)
                throw invalid_argument("Animation::add_keyframe");
 
-       TimedKeyFrame tkf;
+       bool realloc = (keyframes.size()>=keyframes.capacity());
+
+       keyframes.push_back(TimedKeyFrame());
+       TimedKeyFrame &tkf = keyframes.back();
        tkf.time = t;
-       tkf.keyframe = &kf;
-       tkf.keyframe.keep();
+       tkf.keyframe = kf;
+
+       if(realloc)
+       {
+               for(unsigned i=1; i<keyframes.size(); ++i)
+                       keyframes[i].prev = &keyframes[i-1];
+       }
+       else if(keyframes.size()>1)
+               tkf.prev = &tkf-1;
+
        prepare_keyframe(tkf);
-       keyframes.push_back(tkf);
 }
 
 void Animation::prepare_keyframe(TimedKeyFrame &tkf)
 {
-       tkf.prev = (keyframes.empty() ? 0 : &keyframes.back());
-       if(!tkf.prev)
-               return;
+       const KeyFrame::UniformMap &kf_uniforms = tkf.keyframe->get_uniforms();
+       for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
+       {
+               bool found = false;
+               for(unsigned j=0; (!found && j<uniforms.size()); ++j)
+                       if(uniforms[j].name==i->first)
+                       {
+                               if(uniforms[j].size!=i->second.size)
+                                       throw invalid_operation("Animation::prepare_keyframe");
+                               found = true;
+                       }
+
+               if(!found)
+                       uniforms.push_back(UniformInfo(i->first, i->second.size));
+       }
 
-       tkf.delta_t = tkf.time-tkf.prev->time;
+       tkf.prepare(*this);
+}
+
+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);
+}
 
-       const double *m1_data = tkf.prev->keyframe->get_matrix().data();
-       const double *m2_data = tkf.keyframe->get_matrix().data();
+void Animation::set_looping(bool l)
+{
+       looping = l;
+}
+
+
+Animation::AxisInterpolation::AxisInterpolation():
+       slope(0),
+       scale(0)
+{ }
+
+Animation::AxisInterpolation::AxisInterpolation(const float *axis1, const float *axis2)
+{
+       // Compute a normalized vector halfway between the two endpoints
+       float a1_len = 0;
+       float h_len = 0;
+       float cos_half = 0;
        for(unsigned i=0; i<3; ++i)
        {
-               const double *m1_col = m1_data+i*4;
-               const double *m2_col = m2_data+i*4;
-
-               // Compute a normalized vector halfway between the two endpoints
-               double half[3];
-               double len = 0;
-               for(unsigned j=0; j<3; ++j)
-               {
-                       half[j] = (m1_col[j]+m2_col[j])/2;
-                       len += half[j]*half[j];
-               }
-               len = sqrt(len);
-               for(unsigned j=0; j<3; ++j)
-                       half[j] /= len;
-
-               // Compute correction factors for smooth interpolation
-               double cos_half = m1_col[0]*half[0]+m1_col[1]*half[1]+m1_col[2]*half[2];
-               double angle = acos(cos_half);
-               tkf.axes[i].slope = (angle ? angle/tan(angle) : 1);
-               tkf.axes[i].scale = cos_half;
+               float half_i = (axis1[i]+axis2[i])/2;
+               cos_half += axis1[i]*half_i;
+               a1_len += axis1[i]*axis1[i];
+               h_len += half_i*half_i;
        }
+
+       // Compute correction factors for smooth interpolation
+       cos_half = min(max(cos_half/sqrt(a1_len*h_len), -1.0f), 1.0f);
+       float angle = acos(cos_half);
+       slope = (angle ? angle/tan(angle) : 1);
+       scale = cos_half;
 }
 
-Matrix Animation::compute_matrix(const TimedKeyFrame &tkf, const Time::TimeDelta &dt) const
+
+Animation::MatrixInterpolation::MatrixInterpolation():
+       matrix1(0),
+       matrix2(0)
+{ }
+
+Animation::MatrixInterpolation::MatrixInterpolation(const Matrix &m1, const Matrix &m2):
+       matrix1(&m1),
+       matrix2(&m2)
 {
-       if(!dt)
-               return tkf.keyframe->get_matrix();
-       if(!tkf.prev)
-               throw invalid_argument("Animation::compute_matrix");
-       const TimedKeyFrame &prev = *tkf.prev;
+       const float *m1_data = matrix1->data();
+       const float *m2_data = matrix2->data();
+       for(unsigned i=0; i<3; ++i)
+               axes[i] = AxisInterpolation(m1_data+i*4, m2_data+i*4);
+}
 
-       float t = dt/tkf.delta_t;
+Matrix Animation::MatrixInterpolation::get(float t) const
+{
        float u = t*2.0f-1.0f;
 
-       double matrix[16];
-       const double *m1_data = prev.keyframe->get_matrix().data();
-       const double *m2_data = tkf.keyframe->get_matrix().data();
+       float matrix[16];
        for(unsigned i=0; i<4; ++i)
        {
-               const double *m1_col = m1_data+i*4;
-               const double *m2_col = m2_data+i*4;
-               double *out_col = matrix+i*4;
+               const float *m1_col = matrix1->data()+i*4;
+               const float *m2_col = matrix2->data()+i*4;
+               float *out_col = matrix+i*4;
 
                if(i<3)
                {
@@ -91,13 +169,13 @@ Matrix Animation::compute_matrix(const TimedKeyFrame &tkf, const Time::TimeDelta
                        around the halfway point and computing its tangent.  This is
                        approximated by a third degree polynomial, scaled so that the result
                        will be in the range [-1, 1]. */
-                       float w = (tkf.axes[i].slope+(1-tkf.axes[i].slope)*u*u)*u*0.5f+0.5f;
+                       float w = (axes[i].slope+(1-axes[i].slope)*u*u)*u*0.5f+0.5f;
 
-                       /* The interpolate vectors will also be shorter than unit length.  At
+                       /* The interpolated vectors will also be shorter than unit length.  At
                        the halfway point the length will be equal to the cosine of half the
                        angle, which was computed earlier.  Use a second degree polynomial to
                        approximate. */
-                       float n = (tkf.axes[i].scale+(1-tkf.axes[i].scale)*u*u);
+                       float n = (axes[i].scale+(1-axes[i].scale)*u*u);
 
                        for(unsigned j=0; j<3; ++j)
                                out_col[j] = ((1-w)*m1_col[j]+w*m2_col[j])/n;
@@ -118,15 +196,56 @@ Matrix Animation::compute_matrix(const TimedKeyFrame &tkf, const Time::TimeDelta
 }
 
 
-Animation::AxisInterpolation::AxisInterpolation():
-       slope(0),
-       scale(0)
+Animation::TimedKeyFrame::TimedKeyFrame():
+       prev(0)
+{ }
+
+void Animation::TimedKeyFrame::prepare(const Animation &animation)
+{
+       const KeyFrame::UniformMap &kf_uniforms = keyframe->get_uniforms();
+       for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
+       {
+               unsigned j = animation.get_slot_for_uniform(i->first);
+               uniforms.reserve(j+1);
+               for(unsigned k=uniforms.size(); k<=j; ++k)
+                       uniforms.push_back(KeyFrame::AnimatedUniform(animation.uniforms[k].size, 0.0f));
+
+               uniforms[j] = i->second;
+       }
+
+       if(!prev)
+               return;
+
+       delta_t = time-prev->time;
+       matrix = MatrixInterpolation(prev->keyframe->get_matrix(), keyframe->get_matrix());
+
+       if(animation.armature)
+       {
+               unsigned max_index = animation.armature->get_max_link_index();
+               pose_matrices.resize(max_index+1);
+               const Pose *pose1 = prev->keyframe->get_pose();
+               const Pose *pose2 = keyframe->get_pose();
+               static Matrix identity;
+               for(unsigned i=0; i<=max_index; ++i)
+               {
+                       const Matrix &matrix1 = (pose1 ? pose1->get_link_matrix(i) : identity);
+                       const Matrix &matrix2 = (pose2 ? pose2->get_link_matrix(i) : identity);
+                       pose_matrices[i] = MatrixInterpolation(matrix1, matrix2);
+               }
+       }
+}
+
+
+Animation::UniformInfo::UniformInfo(const string &n, unsigned s):
+       name(n),
+       size(s)
 { }
 
 
 Animation::Iterator::Iterator(const Animation &a):
-       animation(a),
-       iter(animation.keyframes.begin()),
+       animation(&a),
+       iter(animation->keyframes.begin()),
+       event_iter(animation->events.begin()),
        end(false)
 { }
 
@@ -135,12 +254,12 @@ Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
        time_since_keyframe += t;
        while(time_since_keyframe>iter->delta_t)
        {
-               KeyFrameList::const_iterator next = iter;
+               vector<TimedKeyFrame>::const_iterator next = iter;
                ++next;
-               if(next==animation.keyframes.end())
+               if(next==animation->keyframes.end())
                {
-                       if(animation.looping)
-                               next = animation.keyframes.begin();
+                       if(animation->looping)
+                               next = animation->keyframes.begin();
                        else
                        {
                                end = true;
@@ -156,9 +275,72 @@ 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
 {
-       return animation.compute_matrix(*iter, time_since_keyframe);
+       if(!iter->prev)
+               return iter->keyframe->get_matrix();
+
+       return iter->matrix.get(time_since_keyframe/iter->delta_t);
+}
+
+KeyFrame::AnimatedUniform Animation::Iterator::get_uniform(unsigned i) const
+{
+       if(!iter->prev)
+       {
+               if(iter->uniforms.size()>i)
+                       return iter->uniforms[i];
+               else
+                       return KeyFrame::AnimatedUniform(animation->uniforms[i].size, 0.0f);
+       }
+
+       unsigned size = animation->uniforms[i].size;
+       float t = time_since_keyframe/iter->delta_t;
+       KeyFrame::AnimatedUniform result(size, 0.0f);
+       for(unsigned j=0; j<size; ++j)
+               result.values[j] = iter->prev->uniforms[i].values[j]*(1-t)+iter->uniforms[i].values[j]*t;
+       return result;
+}
+
+Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
+{
+       if(!animation->armature)
+               throw invalid_operation("Animation::Iterator::get_pose_matrix");
+       if(link>animation->armature->get_max_link_index())
+               throw out_of_range("Animation::Iterator::get_pose_matrix");
+
+       if(!iter->prev)
+       {
+               if(const Pose *pose = iter->keyframe->get_pose())
+                       return pose->get_link_matrix(link);
+               else
+                       return Matrix();
+       }
+
+       // We must redo the base point correction since interpolation throws it off
+       // XXX This should probably be done on local matrices
+       Matrix result = iter->pose_matrices[link].get(time_since_keyframe/iter->delta_t);
+       const Vector3 &base = animation->armature->get_link(link).get_base();
+       Vector3 new_base = result*base;
+       result = Matrix::translation(base-new_base)*result;
+       return result;
 }
 
 
@@ -176,27 +358,47 @@ 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::keyframe(const string &n)
+void Animation::Loader::event(const string &n)
 {
-       obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
+       obj.add_event(current_time, n);
 }
 
-void Animation::Loader::keyframe_inline()
+void Animation::Loader::event1i(const string &n, int v)
 {
-       RefPtr<KeyFrame> kf = new KeyFrame;
-       load_sub(*kf);
+       obj.add_event(current_time, n, v);
+}
 
-       TimedKeyFrame tkf;
-       tkf.time = current_time;
-       tkf.keyframe = kf;
-       obj.prepare_keyframe(tkf);
-       obj.keyframes.push_back(tkf);
+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::interval(float t)
@@ -204,5 +406,21 @@ void Animation::Loader::interval(float t)
        current_time += t*Time::sec;
 }
 
+void Animation::Loader::keyframe(const string &n)
+{
+       obj.add_keyframe(current_time, get_collection().get<KeyFrame>(n));
+}
+
+void Animation::Loader::keyframe_inline()
+{
+       RefPtr<KeyFrame> kf = new KeyFrame;
+       if(coll)
+               load_sub(*kf, get_collection());
+       else
+               load_sub(*kf);
+
+       obj.add_keyframe(current_time, kf);
+}
+
 } // namespace GL
 } // namespace Msp