]> Dogcows Code - chaz/yoink/blob - src/Moof/Animation.cc
converted json sequences to lua
[chaz/yoink] / src / Moof / Animation.cc
1
2 /*******************************************************************************
3
4 Copyright (c) 2009, Charles McGarvey
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 * Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 *******************************************************************************/
28
29 #include <map>
30 #include <vector>
31
32 #include "Animation.hh"
33 #include "Mippleton.hh"
34 #include "Serializable.hh"
35
36
37 namespace Mf {
38
39
40 /**
41 * The collection of nested animation classes. The animation implementation
42 * consists of an Impl class which is allocated and initialized with the
43 * interface object. This class contains the specific fields which are required
44 * to run a single instance of an animation. The sequence data is loaded in a
45 * different class which can be shared amongst multiple animation implementation
46 * instances.
47 */
48
49 class Animation::Impl
50 {
51 friend class Animation;
52
53 /**
54 * Contains "global" animation data for the various animations which get
55 * loaded. This is a mippleton, so it will be shared amongst any animation
56 * which wants to use these loaded sequences.
57 */
58
59 class Data : public Mippleton<Data>
60 {
61 friend class Impl;
62 friend class Mippleton<Data>;
63
64 /**
65 * A frame of an animation sequence. A frame is merely an index which
66 * presumably represents a "slide" or tile which should be displayed,
67 * and the duration that is how long the slide will be shown.
68 */
69
70 struct Frame
71 {
72 unsigned index; ///< Frame index.
73 Scalar duration; ///< Frame duration.
74
75 /**
76 * Construction is initialization. The frame data is loaded from a
77 * frame map which is probably loaded within an animation file.
78 */
79
80 Frame(SerializableP root) :
81 index(0),
82 duration(1.0)
83 {
84 Serializable::Map rootObj;
85
86 if (root->get(rootObj))
87 {
88 Serializable::Map::iterator it;
89
90 for (it = rootObj.begin(); it != rootObj.end(); ++it)
91 {
92 std::string key = (*it).first;
93 if (key == "index")
94 {
95 long value = 0;
96 (*it).second->get(value);
97 index = unsigned(value);
98 }
99 else if (key == "duration")
100 {
101 double value = 0.0;
102 (*it).second->getNumber(value);
103 duration = Scalar(value);
104 }
105 }
106 }
107 }
108 };
109
110
111 /**
112 * A sequence is just a few attributes and a list of frames in the order
113 * that they should be played.
114 */
115
116 struct Sequence
117 {
118 std::vector<Frame> frames; ///< List of frames.
119 Scalar delay; ///< Scale frame durations.
120 bool loop; ///< Does the sequence repeat?
121 std::string next; ///< Next sequence name.
122
123 /**
124 * Construction is initialization. The constructor loads sequence
125 * data from the sequence map, presumably loaded from an animation
126 * file. The rest of the loading takes place in the frame's
127 * constructor which loads each individual frame.
128 */
129
130 Sequence(SerializableP root) :
131 delay(0.0),
132 loop(true)
133 {
134 Serializable::Map rootObj;
135
136 if (root->get(rootObj))
137 {
138 Serializable::Map::iterator it;
139 for (it = rootObj.begin(); it != rootObj.end(); ++it)
140 {
141 std::string key = (*it).first;
142
143 if (key == "frames")
144 {
145 Serializable::Array framesObj;
146
147 if ((*it).second->get(framesObj))
148 {
149 Serializable::Array::iterator jt;
150
151 for (jt = framesObj.begin();
152 jt != framesObj.end(); ++jt)
153 {
154 if (*jt)
155 {
156 frames.push_back(Frame(*jt));
157 }
158 }
159 }
160 }
161 else if (key == "delay")
162 {
163 double value;
164 (*it).second->getNumber(value);
165 delay = Scalar(value);
166 }
167 else if (key == "loop")
168 {
169 (*it).second->get(loop);
170 }
171 else if (key == "next")
172 {
173 (*it).second->get(next);
174 }
175 }
176 }
177 }
178 };
179
180
181 /**
182 * Starts loading a file with animation data. Such a file is formatted
183 * as a map of named sequences. The sequence constructor loads each
184 * individual sequence.
185 */
186
187 void loadFromFile()
188 {
189 std::string filePath = Animation::getPath(getName());
190
191 Deserializer deserializer(filePath);
192
193 SerializableP root = deserializer.deserialize();
194
195 if (root)
196 {
197 Serializable::Map rootObj;
198
199 if (root->get(rootObj))
200 {
201 Serializable::Map::iterator it;
202
203 for (it = rootObj.begin(); it != rootObj.end(); ++it)
204 {
205 sequences.insert(std::pair<std::string,Sequence>((*it).first,
206 Sequence((*it).second)));
207 }
208 }
209 }
210 }
211
212 /**
213 * Construction is initialization. The animation class data container
214 * registers itself as a mippleton and then loads the animation data.
215 */
216
217 explicit Data(const std::string& name) :
218 Mippleton<Data>(name)
219 {
220 loadFromFile();
221 }
222
223 std::map<std::string,Sequence> sequences; ///< List of sequences.
224 };
225
226
227 /**
228 * Construction is intialization.
229 */
230
231 Impl(const std::string& name) :
232 data(Data::getInstance(name)),
233 currentSequence(0),
234 frameCounter(0),
235 frameIndex(0),
236 timeAccum(0),
237 frameDuration(0) {}
238
239
240 /**
241 * Sets up the animation classes to "play" a named sequence. If another
242 * sequence was active, it will be replaced as the current sequence. Future
243 * updates will progress the new sequence.
244 */
245
246 void startSequence(const std::string& name)
247 {
248 std::map<std::string,Data::Sequence>::iterator it;
249
250 it = data->sequences.find(name);
251
252 if (it != data->sequences.end())
253 {
254 currentSequence = &(*it).second;
255 frameCounter = 0;
256 frameIndex = currentSequence->frames[0].index;
257 timeAccum = 0.0;
258 frameDuration = currentSequence->delay *
259 currentSequence->frames[0].duration;
260 }
261 }
262
263 /**
264 * Updates or progresses the animation sequence. If the time interval
265 * surpasses the duration of the current frame, a new frame becomes the
266 * current frame. If the last frame of a sequence expires, the active
267 * sequence will switch automatically to the designated "next" sequence, or
268 * if none is specified but the sequence is set to loop, the first frame of
269 * the sequence will become the current frame, and the animation essentially
270 * starts over again.
271 */
272
273 void update(Scalar t, Scalar dt)
274 {
275 if (currentSequence)
276 {
277 timeAccum += dt;
278
279 if (timeAccum >= frameDuration)
280 {
281 if (++frameCounter >= currentSequence->frames.size())
282 {
283 if (!currentSequence->next.empty())
284 {
285 startSequence(currentSequence->next);
286 }
287 else if (currentSequence->loop)
288 {
289 frameCounter = 0;
290 }
291 else
292 {
293 frameCounter--;
294 currentSequence = 0;
295 }
296 }
297
298 frameIndex = currentSequence->frames[frameCounter].index;
299 timeAccum = frameDuration - timeAccum;
300 frameDuration = currentSequence->delay *
301 currentSequence->frames[frameCounter].duration;
302 }
303 }
304 }
305
306 boost::shared_ptr<Data> data; ///< Internal data.
307
308 Data::Sequence* currentSequence; ///< Active sequence.
309 unsigned frameCounter; ///< Current frame.
310 unsigned frameIndex; ///< Index of current frame.
311 Scalar timeAccum; ///< Time accumulation.
312 Scalar frameDuration; ///< Scaled frame duration.
313 };
314
315
316 Animation::Animation(const std::string& name) :
317 // pass through
318 impl_(new Animation::Impl(name)) {}
319
320
321 void Animation::startSequence(const std::string& name)
322 {
323 // pass through
324 impl_->startSequence(name);
325 }
326
327 void Animation::update(Scalar t, Scalar dt)
328 {
329 // pass through
330 impl_->update(t, dt);
331 }
332
333
334 /**
335 * Gets the index for the current frame. This is presumably called by some
336 * drawing code which will draw the correct current frame.
337 */
338
339 unsigned Animation::getFrame() const
340 {
341 return impl_->frameIndex;
342 }
343
344
345 /**
346 * Specialized search location for animation files. They can be found in the
347 * "animations" subdirectory of any of the searched directories.
348 */
349
350 std::string Animation::getPath(const std::string& name)
351 {
352 return Resource::getPath("animations/" + name + ".json");
353 }
354
355
356 } // namespace Mf
357
358 /** vim: set ts=4 sw=4 tw=80: *************************************************/
359
This page took 0.048314 seconds and 4 git commands to generate.