X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2FMoof%2FAnimation.cc;fp=src%2FMoof%2FAnimation.cc;h=3c418f7052027b17f32bb3305e821563be151667;hp=0000000000000000000000000000000000000000;hb=c2321281bf12a7efaedde930422c7ddbc92080d4;hpb=87bc17e55b0c1dc73ecc66df856d3f08fd7a7724 diff --git a/src/Moof/Animation.cc b/src/Moof/Animation.cc new file mode 100644 index 0000000..3c418f7 --- /dev/null +++ b/src/Moof/Animation.cc @@ -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 +#include + +#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 + { + /** + * 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 rootObj; + + if (root->get(rootObj)) + { + std::map::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 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 rootObj; + + if (root->get(rootObj)) + { + std::map::iterator it; + for (it = rootObj.begin(); it != rootObj.end(); it++) + { + std::string key = (*it).first; + + if (key == "frames") + { + std::vector framesObj; + + if ((*it).second->get(framesObj)) + { + std::vector::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 rootObj; + + if (root->get(rootObj)) + { + std::map::iterator it; + + for (it = rootObj.begin(); it != rootObj.end(); it++) + { + sequences.insert(std::pair((*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(name) + { + loadFromFile(); + } + + std::map 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::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 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: *************************************************/ +