*******************************************************************************/
+#include <map>
+#include <vector>
+
+#include "serializable.hh"
+#include "mippleton.hh"
#include "animation.hh"
namespace dc {
-class animation_impl
+/**
+ * The collection of nested animation classes. The animation implementation
+ * consists of an animation_impl classes which is allocated and initialized with
+ * the interface object. This class contains the specific fields which are
+ * required to run a single instance of an animation. The sequence data is
+ * loaded in a difference class which can be shared amongst multiple animation
+ * implementation instances.
+ */
+
+struct animation::animation_impl
{
-public:
- class sequence
+ /**
+ * Contains "global" animation data for the various animations which get
+ * loaded. This is a mippleton, so it will be shared amongst any animation
+ * which wants to use these loaded sequences.
+ */
+
+ struct animation_data : public mippleton<animation_data>
{
+ /**
+ * A frame of an animation sequence. A frame is merely an index which
+ * presumably represents a "slide" or tile which should be displayed,
+ * and the duration that is how long the slide will be shown.
+ */
+
+ struct frame
+ {
+ unsigned index; ///< Frame index.
+ scalar duration; ///< Frame duration.
+
+ /**
+ * Construction is initialization. The frame data is loaded from a
+ * frame map which is probably loaded within an animation file.
+ */
+
+ frame(serializable_ptr root) :
+ index(0),
+ duration(1.0)
+ {
+ std::map<std::string,serializable_ptr> rootObj;
+
+ if (root->get(rootObj))
+ {
+ std::map<std::string,serializable_ptr>::iterator i;
+ for (i = rootObj.begin(); i != rootObj.end(); i++)
+ {
+ std::string key = (*i).first;
+ if (key == "index")
+ {
+ long value = 0;
+ (*i).second->get(value);
+ index = unsigned(value);
+ }
+ else if (key == "duration")
+ {
+ double value = 0.0;
+ (*i).second->getNumber(value);
+ duration = scalar(value);
+ }
+ }
+ }
+ }
+ };
+
+
+ /**
+ * A sequence is just a few attributes and a list of frames in the order
+ * that they should be played.
+ */
+
+ struct sequence
+ {
+ std::vector<frame> frames; ///< List of frames.
+ scalar delay; ///< Scale frame durations.
+ bool loop; ///< Does the sequence repeat?
+ std::string next; ///< Next sequence name.
+
+ /**
+ * Construction is initialization. The constructor loads sequence
+ * data from the sequence map, presumably loaded from an animation
+ * file. The rest of the loading takes place in the frame's
+ * constructor which loads each individual frame.
+ */
+
+ sequence(serializable_ptr root) :
+ delay(0.0),
+ loop(true)
+ {
+ std::map<std::string,serializable_ptr> rootObj;
+
+ if (root->get(rootObj))
+ {
+ std::map<std::string,serializable_ptr>::iterator i;
+ for (i = rootObj.begin(); i != rootObj.end(); i++)
+ {
+ std::string key = (*i).first;
+
+ if (key == "frames")
+ {
+ std::vector<serializable_ptr> framesObj;
+
+ if ((*i).second->get(framesObj))
+ {
+ std::vector<serializable_ptr>::iterator j;
+
+ for (j = framesObj.begin();
+ j != framesObj.end(); j++)
+ {
+ if (*j)
+ {
+ frames.push_back(frame(*j));
+ }
+ }
+ }
+ }
+ else if (key == "delay")
+ {
+ double value;
+ (*i).second->getNumber(value);
+ delay = scalar(value);
+ }
+ else if (key == "loop")
+ {
+ (*i).second->get(loop);
+ }
+ else if (key == "next")
+ {
+ (*i).second->get(next);
+ }
+ }
+ }
+ }
+ };
+
+
+ /**
+ * Starts loading a file with animation data. Such a file is formatted
+ * as a map of named sequences. The sequence constructor loads each
+ * individual sequence.
+ */
+
+ void loadFromFile()
+ {
+ std::string filePath = animation::getPathToResource(getName());
+
+ deserializer in(filePath);
+
+ serializable_ptr root = in.deserialize();
+
+ if (root)
+ {
+ std::map<std::string,serializable_ptr> rootObj;
+
+ if (root->get(rootObj))
+ {
+ std::map<std::string,serializable_ptr>::iterator i;
+
+ for (i = rootObj.begin(); i != rootObj.end(); i++)
+ {
+ sequences.insert(std::pair<std::string,sequence>((*i).first,
+ sequence((*i).second)));
+ }
+ }
+ }
+ }
+
+ /**
+ * Construction is initialization. The animation class data container
+ * registers itself as a mippleton and then loads the animation data.
+ */
+
+ explicit animation_data(const std::string& name) :
+ mippleton<animation_data>(name)
+ {
+ loadFromFile();
+ }
+
+ std::map<std::string,sequence> sequences; ///< List of sequences.
};
- std::map<std::string,sequence> sequences;
+
+ /**
+ * Construction is intialization.
+ */
+
+ animation_impl(const std::string& name) :
+ data(animation_data::retain(name), &animation_data::release),
+ currentSequence(0),
+ frameCounter(0),
+ frameIndex(0),
+ timeAccum(0),
+ frameDuration(0) {}
+
+
+ /**
+ * Sets up the animation classes to "play" a named sequence. If another
+ * sequence was active, it will be replaced as the current sequence. Future
+ * updates will progress the new sequence.
+ */
+
+ void startSequence(const std::string& sequenceName)
+ {
+ std::map<std::string,animation_data::sequence>::iterator i;
+
+ i = data->sequences.find(sequenceName);
+
+ if (i != data->sequences.end())
+ {
+ currentSequence = &(*i).second;
+ frameCounter = 0;
+ frameIndex = currentSequence->frames[0].index;
+ timeAccum = 0.0;
+ frameDuration = currentSequence->delay *
+ currentSequence->frames[0].duration;
+ }
+ }
+
+ /**
+ * Updates or progresses the animation sequence. If the time interval
+ * surpasses the duration of the current frame, a new frame becomes the
+ * current frame. If the last frame of a sequence expires, the active
+ * sequence will switch automatically to the designated "next" sequence, or
+ * if none is specified but the sequence is set to loop, the first frame of
+ * the sequence will become the current frame, and the animation essentially
+ * starts over again.
+ */
+
+ void update(scalar t, scalar dt)
+ {
+ if (currentSequence)
+ {
+ timeAccum += dt;
+
+ if (timeAccum >= frameDuration)
+ {
+ if (++frameCounter >= currentSequence->frames.size())
+ {
+ if (!currentSequence->next.empty())
+ {
+ startSequence(currentSequence->next);
+ }
+ else if (currentSequence->loop)
+ {
+ frameCounter = 0;
+ }
+ else
+ {
+ frameCounter--;
+ currentSequence = 0;
+ }
+ }
+
+ frameIndex = currentSequence->frames[frameCounter].index;
+ timeAccum = frameDuration - timeAccum;
+ frameDuration = currentSequence->delay *
+ currentSequence->frames[frameCounter].duration;
+ }
+ }
+ }
+
+ boost::shared_ptr<animation_data> data; ///< Internal data.
+
+ animation_data::sequence* currentSequence; ///< Active sequence.
+ unsigned frameCounter; ///< Current frame.
+ unsigned frameIndex; ///< Index of current frame.
+ scalar timeAccum; ///< Time accumulation.
+ scalar frameDuration; ///< Scaled frame duration.
};
+animation::animation(const std::string& name) :
+ // pass through
+ impl(new animation::animation_impl(name)) {}
+
+
+void animation::startSequence(const std::string& sequenceName)
+{
+ // pass through
+ impl->startSequence(sequenceName);
+}
+
+void animation::update(scalar t, scalar dt)
+{
+ // pass through
+ impl->update(t, dt);
+}
+
+
+/**
+ * Gets the index for the current frame. This is presumably called by some
+ * drawing code which will draw the correct current frame.
+ */
+
+unsigned animation::getFrame() const
+{
+ return impl->frameIndex;
+}
+
+
+/**
+ * Specialized search location for animation files. They can be found in the
+ * "animations" subdirectory of any of the searched directories.
+ */
+
+std::string animation::getPathToResource(const std::string& name)
+{
+ return resource::getPathToResource("animations/" + name + ".json");
+}
} // namespace dc
+/** vim: set ts=4 sw=4 tw=80: *************************************************/
+