--- /dev/null
+#include "armature.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Armature::Link &Armature::add_link()
+{
+ links.push_back(Link(string(), links.size()));
+ return links.back();
+}
+
+const Armature::Link &Armature::get_link(unsigned index) const
+{
+ for(list<Link>::const_iterator i=links.begin(); i!=links.end(); ++i)
+ if(i->get_index()==index)
+ return *i;
+ throw invalid_argument("Armature::get_link");
+}
+
+const Armature::Link &Armature::get_link(const string &name) const
+{
+ for(list<Link>::const_iterator i=links.begin(); i!=links.end(); ++i)
+ if(i->get_name()==name)
+ return *i;
+ throw invalid_argument("Armature::get_link");
+}
+
+unsigned Armature::get_max_link_index() const
+{
+ unsigned max_index = 0;
+ for(list<Link>::const_iterator i=links.begin(); i!=links.end(); ++i)
+ max_index = max(max_index, i->get_index());
+ return max_index;
+}
+
+
+Armature::Link::Link(const string &n, unsigned i):
+ name(n),
+ index(i),
+ parent(0)
+{ }
+
+void Armature::Link::set_parent(const Link *p)
+{
+ parent = p;
+}
+
+void Armature::Link::set_base(const Vector3 &b)
+{
+ base = b;
+}
+
+
+Armature::Loader::Loader(Armature &a):
+ DataFile::ObjectLoader<Armature>(a)
+{
+ add("link", &Loader::link);
+}
+
+void Armature::Loader::link(const string &n)
+{
+ Link lnk(n, obj.links.size());
+ load_sub(lnk, obj);
+ obj.links.push_back(lnk);
+}
+
+
+Armature::Link::Loader::Loader(Link &l, const Armature &a):
+ DataFile::ObjectLoader<Link>(l),
+ armature(a)
+{
+ add("base", &Loader::base);
+ add("index", &Link::index);
+ add("parent", &Loader::parent);
+}
+
+void Armature::Link::Loader::base(float x, float y, float z)
+{
+ obj.base = Vector3(x, y, z);
+}
+
+void Armature::Link::Loader::parent(const string &n)
+{
+ obj.parent = &armature.get_link(n);
+}
+
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#ifndef ARMATURE_H_
+#define ARMATURE_H_
+
+#include <list>
+#include <string>
+#include <msp/datafile/objectloader.h>
+#include "pose.h"
+#include "vector.h"
+
+namespace Msp {
+namespace GL {
+
+class Armature
+{
+public:
+ class Loader: public DataFile::ObjectLoader<Armature>
+ {
+ public:
+ Loader(Armature &);
+ private:
+ void link(const std::string &);
+ };
+
+ class Link
+ {
+ public:
+ class Loader: public DataFile::ObjectLoader<Link>
+ {
+ private:
+ const Armature &armature;
+
+ public:
+ Loader(Link &, const Armature &);
+ private:
+ void base(float, float, float);
+ void parent(const std::string &);
+ };
+
+ private:
+ std::string name;
+ unsigned index;
+ const Link *parent;
+ Vector3 base;
+
+ public:
+ Link(const std::string &, unsigned);
+
+ void set_parent(const Link *);
+ void set_base(const Vector3 &);
+
+ const std::string &get_name() const { return name; }
+ unsigned get_index() const { return index; }
+ const Link *get_parent() const { return parent; }
+ const Vector3 &get_base() const { return base; }
+ };
+
+private:
+ std::list<Link> links;
+
+public:
+ Link &add_link();
+
+ const Link &get_link(unsigned) const;
+ const Link &get_link(const std::string &) const;
+ unsigned get_max_link_index() const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <msp/datafile/collection.h>
+#include "armature.h"
+#include "error.h"
+#include "pose.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Pose::Pose():
+ armature(0)
+{ }
+
+Pose::Pose(const Armature &a):
+ armature(0)
+{
+ set_armature(a);
+}
+
+void Pose::set_armature(const Armature &a)
+{
+ if(armature)
+ throw invalid_operation("Pose::set_armature");
+ armature = &a;
+ links.resize(armature->get_max_link_index()+1);
+}
+
+void Pose::rotate_link(unsigned i, float angle, const Vector3 &axis)
+{
+ if(i>=links.size())
+ throw out_of_range("Pose::rotate_link");
+
+ const Armature::Link &arm_link = armature->get_link(i);
+ links[i].local_matrix.rotate(angle, axis);
+
+ // Keep the base point stationary
+ Vector3 base = arm_link.get_base();
+ Vector3 new_base = links[i].local_matrix*base;
+ links[i].local_matrix.translate(base.x-new_base.x, base.y-new_base.y, base.z-new_base.z);
+
+ if(const Armature::Link *parent = arm_link.get_parent())
+ links[i].matrix = links[parent->get_index()].matrix*links[i].local_matrix;
+
+ // XXX apply matrix to descendants of the link
+}
+
+const Matrix &Pose::get_link_matrix(unsigned i) const
+{
+ if(i>=links.size())
+ throw out_of_range("Pose::get_link_matrix");
+ return links[i].matrix;
+}
+
+
+Pose::Loader::Loader(Pose &p, Collection &c):
+ DataFile::CollectionObjectLoader<Pose>(p, &c)
+{
+ add("armature", &Pose::armature);
+ add("link", &Loader::link);
+}
+
+void Pose::Loader::link(const string &n)
+{
+ if(!obj.armature)
+ error("Armature must be specified first");
+ LinkLoader ldr(obj, obj.armature->get_link(n).get_index());
+ load_sub_with(ldr);
+}
+
+
+Pose::LinkLoader::LinkLoader(Pose &p, unsigned l):
+ DataFile::ObjectLoader<Pose>(p),
+ link_index(l)
+{
+ add("rotation", &LinkLoader::rotation);
+}
+
+void Pose::LinkLoader::rotation(float a, float x, float y, float z)
+{
+ obj.rotate_link(link_index, a, Vector3(x, y, z));
+}
+
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GL_POSE_H_
+#define MSP_GL_POSE_H_
+
+#include <map>
+#include <msp/datafile/objectloader.h>
+#include "matrix.h"
+
+namespace Msp {
+namespace GL {
+
+class Armature;
+
+class Pose
+{
+public:
+ class Loader: public DataFile::CollectionObjectLoader<Pose>
+ {
+ public:
+ Loader(Pose &, Collection &);
+ private:
+ void link(const std::string &);
+ };
+
+private:
+ struct Link
+ {
+ Matrix matrix;
+ Matrix local_matrix;
+ };
+
+ class LinkLoader: public DataFile::ObjectLoader<Pose>
+ {
+ private:
+ unsigned link_index;
+
+ public:
+ LinkLoader(Pose &, unsigned);
+ private:
+ void rotation(float, float, float, float);
+ };
+
+ const Armature *armature;
+ std::vector<Link> links;
+
+public:
+ Pose();
+ Pose(const Armature &);
+
+ void set_armature(const Armature &);
+ void rotate_link(unsigned, float, const Vector3 &);
+ const Matrix &get_link_matrix(unsigned) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif