#include <cmath>
+#include <msp/core/maputils.h>
#include <msp/datafile/collection.h>
#include <msp/time/units.h>
#include "animation.h"
#include "armature.h"
#include "error.h"
-#include "keyframe.h"
#include "pose.h"
using namespace std;
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(*this);
+ 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::set_looping(bool l)
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.prepare();
+ tkf.prepare(*this);
}
will be in the range [-1, 1]. */
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. */
}
-Animation::TimedKeyFrame::TimedKeyFrame(const Animation &a):
- animation(a),
+Animation::TimedKeyFrame::TimedKeyFrame():
prev(0)
{ }
-void Animation::TimedKeyFrame::prepare()
+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();
}
+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()),
end(false)
{ }
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;
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)
+ if(!animation->armature)
throw invalid_operation("Animation::Iterator::get_pose_matrix");
- if(link>animation.armature->get_max_link_index())
+ if(link>animation->armature->get_max_link_index())
throw out_of_range("Animation::Iterator::get_pose_matrix");
if(!iter->prev)
// We must redo the base point correction since interpolation throws if 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();
+ const Vector3 &base = animation->armature->get_link(link).get_base();
Vector3 new_base = result*base;
result = Matrix::translation(base-new_base)*result;
return result;
else
load_sub(*kf);
- TimedKeyFrame tkf(obj);
- tkf.time = current_time;
- tkf.keyframe = kf;
- obj.prepare_keyframe(tkf);
- obj.keyframes.push_back(tkf);
+ obj.add_keyframe(current_time, kf);
}
void Animation::Loader::interval(float t)