--- /dev/null
+
+/*******************************************************************************
+
+ Copyright (c) 2009, Charles McGarvey
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*******************************************************************************/
+
+#include <map>
+#include <vector>
+
+#include "Animation.hh"
+#include "Mippleton.hh"
+#include "Serializable.hh"
+
+
+namespace Mf {
+
+
+/**
+ * The collection of nested animation classes. The animation implementation
+ * consists of an AnimationImpl 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::AnimationImpl
+{
+
+ /**
+ * 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 AnimationData : public Mippleton<AnimationData>
+ {
+ /**
+ * 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(SerializablePtr root) :
+ index(0),
+ duration(1.0)
+ {
+ std::map<std::string,SerializablePtr> rootObj;
+
+ if (root->get(rootObj))
+ {
+ std::map<std::string,SerializablePtr>::iterator it;
+
+ for (it = rootObj.begin(); it != rootObj.end(); it++)
+ {
+ std::string key = (*it).first;
+ if (key == "index")
+ {
+ long value = 0;
+ (*it).second->get(value);
+ index = unsigned(value);
+ }
+ else if (key == "duration")
+ {
+ double value = 0.0;
+ (*it).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(SerializablePtr root) :
+ delay(0.0),
+ loop(true)
+ {
+ std::map<std::string,SerializablePtr> rootObj;
+
+ if (root->get(rootObj))
+ {
+ std::map<std::string,SerializablePtr>::iterator it;
+ for (it = rootObj.begin(); it != rootObj.end(); it++)
+ {
+ std::string key = (*it).first;
+
+ if (key == "frames")
+ {
+ std::vector<SerializablePtr> framesObj;
+
+ if ((*it).second->get(framesObj))
+ {
+ std::vector<SerializablePtr>::iterator jt;
+
+ for (jt = framesObj.begin();
+ jt != framesObj.end(); jt++)
+ {
+ if (*jt)
+ {
+ frames.push_back(Frame(*jt));
+ }
+ }
+ }
+ }
+ else if (key == "delay")
+ {
+ double value;
+ (*it).second->getNumber(value);
+ delay = Scalar(value);
+ }
+ else if (key == "loop")
+ {
+ (*it).second->get(loop);
+ }
+ else if (key == "next")
+ {
+ (*it).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 deserializer(filePath);
+
+ SerializablePtr root = deserializer.deserialize();
+
+ if (root)
+ {
+ std::map<std::string,SerializablePtr> rootObj;
+
+ if (root->get(rootObj))
+ {
+ std::map<std::string,SerializablePtr>::iterator it;
+
+ for (it = rootObj.begin(); it != rootObj.end(); it++)
+ {
+ sequences.insert(std::pair<std::string,Sequence>((*it).first,
+ Sequence((*it).second)));
+ }
+ }
+ }
+ }
+
+ /**
+ * Construction is initialization. The animation class data container
+ * registers itself as a mippleton and then loads the animation data.
+ */
+
+ explicit AnimationData(const std::string& name) :
+ Mippleton<AnimationData>(name)
+ {
+ loadFromFile();
+ }
+
+ std::map<std::string,Sequence> sequences; ///< List of sequences.
+ };
+
+
+ /**
+ * Construction is intialization.
+ */
+
+ AnimationImpl(const std::string& name) :
+ data(AnimationData::retain(name), &AnimationData::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& name)
+ {
+ std::map<std::string,AnimationData::Sequence>::iterator it;
+
+ it = data->sequences.find(name);
+
+ if (it != data->sequences.end())
+ {
+ currentSequence = &(*it).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<AnimationData> data; ///< Internal data.
+
+ AnimationData::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::AnimationImpl(name)) {}
+
+
+void Animation::startSequence(const std::string& name)
+{
+ // pass through
+ impl_->startSequence(name);
+}
+
+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 Mf
+
+/** vim: set ts=4 sw=4 tw=80: *************************************************/
+