X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fanimation.cc;h=c03a49d7195a08e4f3d0252f1581eb18f3b00855;hp=745f0e4493eddcb486c5334f6b841d42da9575fe;hb=838bc00015eb7f583c7cf4b3b1007697bf047da1;hpb=6dfcfbd4a612230f2037cf891dd98520cb80c997 diff --git a/src/animation.cc b/src/animation.cc index 745f0e4..c03a49d 100644 --- a/src/animation.cc +++ b/src/animation.cc @@ -26,25 +26,329 @@ *******************************************************************************/ +#include +#include + +#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 { + /** + * 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 rootObj; + + if (root->get(rootObj)) + { + std::map::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 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 rootObj; + + if (root->get(rootObj)) + { + std::map::iterator i; + for (i = rootObj.begin(); i != rootObj.end(); i++) + { + std::string key = (*i).first; + + if (key == "frames") + { + std::vector framesObj; + + if ((*i).second->get(framesObj)) + { + std::vector::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 rootObj; + + if (root->get(rootObj)) + { + std::map::iterator i; + + for (i = rootObj.begin(); i != rootObj.end(); i++) + { + sequences.insert(std::pair((*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(name) + { + loadFromFile(); + } + + std::map sequences; ///< List of sequences. }; - std::map 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::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 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: *************************************************/ +