]> Dogcows Code - chaz/yoink/blob - src/Animation.cc
moving some non-portable code to stlplus
[chaz/yoink] / src / Animation.cc
1
2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
4 *
5 * vi:ts=4 sw=4 tw=75
6 *
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
9 *
10 **************************************************************************/
11
12 #include <map>
13 #include <stdexcept>
14 #include <vector>
15
16 #include <moof/manager.hh>
17 #include <moof/log.hh>
18 #include <moof/script.hh>
19
20 #include "Animation.hh"
21
22
23 /**
24 * The collection of nested animation classes. The animation
25 * implementation consists of an impl class which is allocated and
26 * initialized with the interface object. This class contains the specific
27 * fields which are required to run a single instance of an animation. The
28 * sequence data is loaded in a different class which can be shared amongst
29 * multiple animation implementation instances.
30 */
31
32 class Animation::impl
33 {
34 public:
35
36 /**
37 * Contains "global" animation data for the various animations which
38 * get loaded. This is a mippleton, so it will be shared amongst any
39 * animation which wants to use these loaded sequences.
40 */
41
42 class Data : public moof::manager<Data>
43 {
44 public:
45
46 /**
47 * A frame of an animation sequence. A frame is merely an index
48 * which presumably represents a "slide" or tile which should be
49 * displayed, and the duration that is how long the slide will be
50 * shown.
51 */
52
53 class Frame
54 {
55 public:
56
57 unsigned mIndex; ///< Frame index.
58 moof::scalar mDuration; ///< Frame duration.
59
60 /**
61 * Construction is initialization. The frame data is loaded
62 * from a frame map which is probably loaded within an
63 * animation file.
64 */
65
66 Frame(const moof::script::slot& table) :
67 mIndex(0),
68 mDuration(1.0)
69 {
70 table.get(mIndex, "index");
71 table.get(mDuration, "duration");
72 }
73 };
74
75
76 /**
77 * A sequence is just a few attributes and a list of frames in the
78 * order that they should be played.
79 */
80
81 class Sequence
82 {
83 public:
84
85 std::vector<Frame> mFrames; ///< List of frames.
86 moof::scalar mDelay; ///< Scale frame durations.
87 bool mLoop; ///< Does the sequence repeat?
88 std::string mNext; ///< Next sequence name.
89
90 /**
91 * Construction is initialization. The constructor loads
92 * sequence data from the sequence map, presumably loaded from
93 * an animation file. The rest of the loading takes place in
94 * the frame's constructor which loads each individual frame.
95 */
96
97 Sequence(const moof::script::slot& table) :
98 mDelay(0.0),
99 mLoop(true)
100 {
101 table.get(mDelay, "delay");
102 table.get(mLoop, "loop");
103 table.get(mNext, "next");
104
105 // TODO - sequence class/type not yet implemented
106
107 moof::script::slot frameTable = table.push_field("frames");
108 if (frameTable.is_table())
109 {
110 int max = frameTable.length();
111 for (int index = 1; index <= max; ++index)
112 {
113 moof::script::slot top = frameTable.push_field(index);
114
115 if (top.is_table())
116 {
117 mFrames.push_back(Frame(top));
118 }
119 else
120 {
121 moof::log_warning << "invalid frame at index "
122 << index << std::endl;
123 }
124 }
125 }
126 frameTable.remove();
127 }
128 };
129
130
131 /**
132 * Starts loading a file with animation data. Such a file is
133 * formatted as a map of named sequences. The sequence
134 * constructor loads each individual sequence.
135 */
136
137 void init(const std::string& name)
138 {
139 moof::script script;
140 std::string path(name);
141
142 if (!resource::find(path))
143 {
144 throw std::runtime_error("cannot find resource " + name);
145 }
146
147 script.import_base_library();
148 moof::log::import(script);
149 importAnimationBindings(script);
150
151 if (script.do_file(path) != moof::script::success)
152 {
153 std::string str;
154 script[-1].get(str);
155 moof::log_warning(str);
156 }
157 }
158
159 int defineSequence(moof::script& script)
160 {
161 moof::script::slot name = script[1].require_string();
162 moof::script::slot table = script[2].require_table();
163
164 std::string nameStr;
165 name.get(nameStr);
166
167 mSequences.insert(std::make_pair(nameStr, Sequence(table)));
168
169 return 0;
170 }
171
172
173 void importAnimationBindings(moof::script& script)
174 {
175 script.import_function("DefineSequence",
176 boost::bind(&Data::defineSequence,
177 this, _1));
178
179 script.globals().set_field("ATTACK", 1);
180 script.globals().set_field("CHARGE", 2);
181 script.globals().set_field("FLY", 3);
182 script.globals().set_field("HIT", 4);
183 script.globals().set_field("JUMP", 5);
184 script.globals().set_field("RUN", 6);
185 script.globals().set_field("STAND", 7);
186 }
187
188
189 std::map<std::string,Sequence> mSequences; ///< List of sequences.
190 };
191
192
193 /**
194 * Construction is intialization.
195 */
196
197 impl(const std::string& name) :
198 mData(Data::instance(name)),
199 mCurrentSequence(0),
200 mFrameCounter(0),
201 mFrameIndex(0),
202 mTimeAccum(0),
203 mFrameDuration(0) {}
204
205
206 /**
207 * Sets up the animation classes to "play" a named sequence. If
208 * another sequence was active, it will be replaced. Future updates
209 * will progress the new sequence.
210 */
211
212 void startSequence(const std::string& name)
213 {
214 std::map<std::string,Data::Sequence>::iterator it;
215
216 it = mData->mSequences.find(name);
217
218 if (it != mData->mSequences.end())
219 {
220 mCurrentSequence = &(*it).second;
221 mFrameCounter = 0;
222 mFrameIndex = mCurrentSequence->mFrames[0].mIndex;
223 mTimeAccum = 0.0;
224 mFrameDuration = mCurrentSequence->mDelay *
225 mCurrentSequence->mFrames[0].mDuration;
226 }
227 }
228
229 /**
230 * Updates or progresses the animation sequence. If the time interval
231 * surpasses the duration of the current frame, a new frame becomes the
232 * current frame. If the last frame of a sequence expires, the active
233 * sequence will switch automatically to the designated "next"
234 * sequence, or if none is specified but the sequence is set to loop,
235 * the first frame of the sequence will become the current frame, and
236 * the animation essentially starts over again.
237 */
238
239 void update(moof::scalar t, moof::scalar dt)
240 {
241 if (!mCurrentSequence) return;
242
243 mTimeAccum += dt;
244
245 if (mTimeAccum >= mFrameDuration)
246 {
247 if (++mFrameCounter >= mCurrentSequence->mFrames.size())
248 {
249 if (!mCurrentSequence->mNext.empty())
250 {
251 startSequence(mCurrentSequence->mNext);
252 }
253 else if (mCurrentSequence->mLoop)
254 {
255 mFrameCounter = 0;
256 }
257 else
258 {
259 mFrameCounter--;
260 mCurrentSequence = 0;
261 }
262 }
263
264 mFrameIndex = mCurrentSequence->mFrames[mFrameCounter].mIndex;
265 mTimeAccum = mFrameDuration - mTimeAccum;
266 mFrameDuration = mCurrentSequence->mDelay *
267 mCurrentSequence->mFrames[mFrameCounter].mDuration;
268 }
269 }
270
271 boost::shared_ptr<Data> mData; ///< Internal data.
272
273 Data::Sequence* mCurrentSequence; ///< Active sequence.
274 unsigned mFrameCounter; ///< Current frame.
275 unsigned mFrameIndex; ///< Index of current frame.
276 moof::scalar mTimeAccum; ///< Time accumulation.
277 moof::scalar mFrameDuration; ///< Scaled frame duration.
278 };
279
280
281 Animation::Animation(const std::string& name) :
282 // pass through
283 impl_(new Animation::impl(name)) {}
284
285
286 void Animation::startSequence(const std::string& name)
287 {
288 // pass through
289 impl_->startSequence(name);
290 }
291
292 void Animation::update(moof::scalar t, moof::scalar dt)
293 {
294 // pass through
295 impl_->update(t, dt);
296 }
297
298
299 /**
300 * Gets the index for the current frame. This is presumably called by some
301 * drawing code which will draw the correct current frame.
302 */
303
304 unsigned Animation::getFrame() const
305 {
306 return impl_->mFrameIndex;
307 }
308
This page took 0.042579 seconds and 4 git commands to generate.