]> Dogcows Code - chaz/yoink/blobdiff - src/Moof/Animation.cc
extreme refactoring
[chaz/yoink] / src / Moof / Animation.cc
diff --git a/src/Moof/Animation.cc b/src/Moof/Animation.cc
new file mode 100644 (file)
index 0000000..3c418f7
--- /dev/null
@@ -0,0 +1,355 @@
+
+/*******************************************************************************
+
+ 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: *************************************************/
+
This page took 0.024675 seconds and 4 git commands to generate.