#include <cmath>
+#include <msp/core/maputils.h>
#include <msp/datafile/collection.h>
#include <msp/time/units.h>
#include "animation.h"
-#include "keyframe.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)
{
if(!keyframes.empty() && t<keyframes.back().time)
keyframes.push_back(tkf);
}
+void Animation::set_looping(bool l)
+{
+ looping = l;
+}
+
void Animation::prepare_keyframe(TimedKeyFrame &tkf)
{
tkf.prev = (keyframes.empty() ? 0 : &keyframes.back());
- if(!tkf.prev)
- return;
- tkf.prepare();
+ 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();
}
scale(0)
{ }
-Animation::AxisInterpolation::AxisInterpolation(const double *axis1, const double *axis2)
+Animation::AxisInterpolation::AxisInterpolation(const float *axis1, const float *axis2)
{
// Compute a normalized vector halfway between the two endpoints
- double half[3];
- double len = 0;
+ float half[3];
+ float a1_len = 0;
+ float h_len = 0;
for(unsigned i=0; i<3; ++i)
{
half[i] = (axis1[i]+axis2[i])/2;
- len += half[i]*half[i];
+ a1_len += axis1[i]*axis1[i];
+ h_len += half[i]*half[i];
}
- len = sqrt(len);
- for(unsigned i=0; i<3; ++i)
- half[i] /= len;
// Compute correction factors for smooth interpolation
- double cos_half = axis1[0]*half[0]+axis1[1]*half[1]+axis1[2]*half[2];
- double angle = acos(cos_half);
+ float cos_half = (axis1[0]*half[0]+axis1[1]*half[1]+axis1[2]*half[2])/sqrt(a1_len*h_len);
+ float angle = acos(cos_half);
slope = (angle ? angle/tan(angle) : 1);
scale = cos_half;
}
matrix1(&m1),
matrix2(&m2)
{
- const double *m1_data = matrix1->data();
- const double *m2_data = matrix2->data();
+ 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 u = t*2.0f-1.0f;
- double matrix[16];
+ float matrix[16];
for(unsigned i=0; i<4; ++i)
{
- const double *m1_col = matrix1->data()+i*4;
- const double *m2_col = matrix2->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)
{
void Animation::TimedKeyFrame::prepare()
{
+ 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()),
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 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();
+ Vector3 new_base = result*base;
+ result = Matrix::translation(base-new_base)*result;
+ return result;
+}
+
Animation::Loader::Loader(Animation &a):
DataFile::CollectionObjectLoader<Animation>(a, 0)
void Animation::Loader::init()
{
+ add("armature", &Animation::armature);
add("interval", &Loader::interval);
add("keyframe", &Loader::keyframe);
add("keyframe", &Loader::keyframe_inline);
void Animation::Loader::keyframe_inline()
{
RefPtr<KeyFrame> kf = new KeyFrame;
- load_sub(*kf);
+ if(coll)
+ load_sub(*kf, get_collection());
+ else
+ load_sub(*kf);
TimedKeyFrame tkf(obj);
tkf.time = current_time;