/*] Copyright (c) 2009-2010, Charles McGarvey [************************** **] All rights reserved. * * vi:ts=4 sw=4 tw=75 * * Distributable under the terms and conditions of the 2-clause BSD license; * see the file COPYING for a complete text of the license. * **************************************************************************/ #include #include #include #include #include #include #include "Animation.hh" /** * The collection of nested animation classes. The animation * implementation consists of an impl class 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 different class which can be shared amongst * multiple animation implementation instances. */ class Animation::impl { public: /** * 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. */ class Data : public moof::manager { public: /** * 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. */ class Frame { public: unsigned mIndex; ///< Frame index. moof::scalar mDuration; ///< Frame duration. /** * Construction is initialization. The frame data is loaded * from a frame map which is probably loaded within an * animation file. */ Frame(const moof::script::slot& table) : mIndex(0), mDuration(1.0) { table.get(mIndex, "index"); table.get(mDuration, "duration"); } }; /** * A sequence is just a few attributes and a list of frames in the * order that they should be played. */ class Sequence { public: std::vector mFrames; ///< List of frames. moof::scalar mDelay; ///< Scale frame durations. bool mLoop; ///< Does the sequence repeat? std::string mNext; ///< 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(const moof::script::slot& table) : mDelay(0.0), mLoop(true) { table.get(mDelay, "delay"); table.get(mLoop, "loop"); table.get(mNext, "next"); // TODO - sequence class/type not yet implemented moof::script::slot frameTable = table.push_field("frames"); if (frameTable.is_table()) { int max = frameTable.length(); for (int index = 1; index <= max; ++index) { moof::script::slot top = frameTable.push_field(index); if (top.is_table()) { mFrames.push_back(Frame(top)); } else { moof::log_warning << "invalid frame at index " << index << std::endl; } } } frameTable.remove(); } }; /** * 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 init(const std::string& name) { moof::script script; std::string path(name); if (!resource::find(path)) { throw std::runtime_error("cannot find resource " + name); } script.import_base_library(); moof::log::import(script); importAnimationBindings(script); if (script.do_file(path) != moof::script::success) { std::string str; script[-1].get(str); moof::log_warning(str); } } int defineSequence(moof::script& script) { moof::script::slot name = script[1].require_string(); moof::script::slot table = script[2].require_table(); std::string nameStr; name.get(nameStr); mSequences.insert(std::make_pair(nameStr, Sequence(table))); return 0; } void importAnimationBindings(moof::script& script) { script.import_function("DefineSequence", boost::bind(&Data::defineSequence, this, _1)); script.globals().set_field("ATTACK", 1); script.globals().set_field("CHARGE", 2); script.globals().set_field("FLY", 3); script.globals().set_field("HIT", 4); script.globals().set_field("JUMP", 5); script.globals().set_field("RUN", 6); script.globals().set_field("STAND", 7); } std::map mSequences; ///< List of sequences. }; /** * Construction is intialization. */ impl(const std::string& name) : mData(Data::instance(name)), mCurrentSequence(0), mFrameCounter(0), mFrameIndex(0), mTimeAccum(0), mFrameDuration(0) {} /** * Sets up the animation classes to "play" a named sequence. If * another sequence was active, it will be replaced. Future updates * will progress the new sequence. */ void startSequence(const std::string& name) { std::map::iterator it; it = mData->mSequences.find(name); if (it != mData->mSequences.end()) { mCurrentSequence = &(*it).second; mFrameCounter = 0; mFrameIndex = mCurrentSequence->mFrames[0].mIndex; mTimeAccum = 0.0; mFrameDuration = mCurrentSequence->mDelay * mCurrentSequence->mFrames[0].mDuration; } } /** * 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(moof::scalar t, moof::scalar dt) { if (!mCurrentSequence) return; mTimeAccum += dt; if (mTimeAccum >= mFrameDuration) { if (++mFrameCounter >= mCurrentSequence->mFrames.size()) { if (!mCurrentSequence->mNext.empty()) { startSequence(mCurrentSequence->mNext); } else if (mCurrentSequence->mLoop) { mFrameCounter = 0; } else { mFrameCounter--; mCurrentSequence = 0; } } mFrameIndex = mCurrentSequence->mFrames[mFrameCounter].mIndex; mTimeAccum = mFrameDuration - mTimeAccum; mFrameDuration = mCurrentSequence->mDelay * mCurrentSequence->mFrames[mFrameCounter].mDuration; } } boost::shared_ptr mData; ///< Internal data. Data::Sequence* mCurrentSequence; ///< Active sequence. unsigned mFrameCounter; ///< Current frame. unsigned mFrameIndex; ///< Index of current frame. moof::scalar mTimeAccum; ///< Time accumulation. moof::scalar mFrameDuration; ///< Scaled frame duration. }; Animation::Animation(const std::string& name) : // pass through impl_(new Animation::impl(name)) {} void Animation::startSequence(const std::string& name) { // pass through impl_->startSequence(name); } void Animation::update(moof::scalar t, moof::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_->mFrameIndex; }