38ffa6c9c5e5c8ca9fdb83806e46f731d9bd274b
[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 AnimationImpl classes which is allocated and initialized with
43 * the interface object. This class contains the specific fields which are
44 * required to run a single instance of an animation. The sequence data is
45 * loaded in a difference class which can be shared amongst multiple animation
46 * implementation instances.
47 */
48
49 struct Animation::AnimationImpl
50 {
51
52 /**
53 * Contains "global" animation data for the various animations which get
54 * loaded. This is a mippleton, so it will be shared amongst any animation
55 * which wants to use these loaded sequences.
56 */
57
58 struct AnimationData : public Mippleton<AnimationData>
59 {
60 /**
61 * A frame of an animation sequence. A frame is merely an index which
62 * presumably represents a "slide" or tile which should be displayed,
63 * and the duration that is how long the slide will be shown.
64 */
65
66 struct Frame
67 {
68 unsigned index; ///< Frame index.
69 Scalar duration; ///< Frame duration.
70
71 /**
72 * Construction is initialization. The frame data is loaded from a
73 * frame map which is probably loaded within an animation file.
74 */
75
76 Frame(SerializablePtr root) :
77 index(0),
78 duration(1.0)
79 {
80 std::map<std::string,SerializablePtr> rootObj;
81
82 if (root->get(rootObj))
83 {
84 std::map<std::string,SerializablePtr>::iterator it;
85
86 for (it = rootObj.begin(); it != rootObj.end(); ++it)
87 {
88 std::string key = (*it).first;
89 if (key == "index")
90 {
91 long value = 0;
92 (*it).second->get(value);
93 index = unsigned(value);
94 }
95 else if (key == "duration")
96 {
97 double value = 0.0;
98 (*it).second->getNumber(value);
99 duration = Scalar(value);
100 }
101 }
102 }
103 }
104 };
105
106
107 /**
108 * A sequence is just a few attributes and a list of frames in the order
109 * that they should be played.
110 */
111
112 struct Sequence
113 {
114 std::vector<Frame> frames; ///< List of frames.
115 Scalar delay; ///< Scale frame durations.
116 bool loop; ///< Does the sequence repeat?
117 std::string next; ///< Next sequence name.
118
119 /**
120 * Construction is initialization. The constructor loads sequence
121 * data from the sequence map, presumably loaded from an animation
122 * file. The rest of the loading takes place in the frame's
123 * constructor which loads each individual frame.
124 */
125
126 Sequence(SerializablePtr root) :
127 delay(0.0),
128 loop(true)
129 {
130 std::map<std::string,SerializablePtr> rootObj;
131
132 if (root->get(rootObj))
133 {
134 std::map<std::string,SerializablePtr>::iterator it;
135 for (it = rootObj.begin(); it != rootObj.end(); ++it)
136 {
137 std::string key = (*it).first;
138
139 if (key == "frames")
140 {
141 std::vector<SerializablePtr> framesObj;
142
143 if ((*it).second->get(framesObj))
144 {
145 std::vector<SerializablePtr>::iterator jt;
146
147 for (jt = framesObj.begin();
148 jt != framesObj.end(); ++jt)
149 {
150 if (*jt)
151 {
152 frames.push_back(Frame(*jt));
153 }
154 }
155 }
156 }
157 else if (key == "delay")
158 {
159 double value;
160 (*it).second->getNumber(value);
161 delay = Scalar(value);
162 }
163 else if (key == "loop")
164 {
165 (*it).second->get(loop);
166 }
167 else if (key == "next")
168 {
169 (*it).second->get(next);
170 }
171 }
172 }
173 }
174 };
175
176
177 /**
178 * Starts loading a file with animation data. Such a file is formatted
179 * as a map of named sequences. The sequence constructor loads each
180 * individual sequence.
181 */
182
183 void loadFromFile()
184 {
185 std::string filePath = Animation::getPathToResource(getName());
186
187 Deserializer deserializer(filePath);
188
189 SerializablePtr root = deserializer.deserialize();
190
191 if (root)
192 {
193 std::map<std::string,SerializablePtr> rootObj;
194
195 if (root->get(rootObj))
196 {
197 std::map<std::string,SerializablePtr>::iterator it;
198
199 for (it = rootObj.begin(); it != rootObj.end(); ++it)
200 {
201 sequences.insert(std::pair<std::string,Sequence>((*it).first,
202 Sequence((*it).second)));
203 }
204 }
205 }
206 }
207
208 /**
209 * Construction is initialization. The animation class data container
210 * registers itself as a mippleton and then loads the animation data.
211 */
212
213 explicit AnimationData(const std::string& name) :
214 Mippleton<AnimationData>(name)
215 {
216 loadFromFile();
217 }
218
219 std::map<std::string,Sequence> sequences; ///< List of sequences.
220 };
221
222
223 /**
224 * Construction is intialization.
225 */
226
227 AnimationImpl(const std::string& name) :
228 data(AnimationData::retain(name), &AnimationData::release),
229 currentSequence(0),
230 frameCounter(0),
231 frameIndex(0),
232 timeAccum(0),
233 frameDuration(0) {}
234
235
236 /**
237 * Sets up the animation classes to "play" a named sequence. If another
238 * sequence was active, it will be replaced as the current sequence. Future
239 * updates will progress the new sequence.
240 */
241
242 void startSequence(const std::string& name)
243 {
244 std::map<std::string,AnimationData::Sequence>::iterator it;
245
246 it = data->sequences.find(name);
247
248 if (it != data->sequences.end())
249 {
250 currentSequence = &(*it).second;
251 frameCounter = 0;
252 frameIndex = currentSequence->frames[0].index;
253 timeAccum = 0.0;
254 frameDuration = currentSequence->delay *
255 currentSequence->frames[0].duration;
256 }
257 }
258
259 /**
260 * Updates or progresses the animation sequence. If the time interval
261 * surpasses the duration of the current frame, a new frame becomes the
262 * current frame. If the last frame of a sequence expires, the active
263 * sequence will switch automatically to the designated "next" sequence, or
264 * if none is specified but the sequence is set to loop, the first frame of
265 * the sequence will become the current frame, and the animation essentially
266 * starts over again.
267 */
268
269 void update(Scalar t, Scalar dt)
270 {
271 if (currentSequence)
272 {
273 timeAccum += dt;
274
275 if (timeAccum >= frameDuration)
276 {
277 if (++frameCounter >= currentSequence->frames.size())
278 {
279 if (!currentSequence->next.empty())
280 {
281 startSequence(currentSequence->next);
282 }
283 else if (currentSequence->loop)
284 {
285 frameCounter = 0;
286 }
287 else
288 {
289 frameCounter--;
290 currentSequence = 0;
291 }
292 }
293
294 frameIndex = currentSequence->frames[frameCounter].index;
295 timeAccum = frameDuration - timeAccum;
296 frameDuration = currentSequence->delay *
297 currentSequence->frames[frameCounter].duration;
298 }
299 }
300 }
301
302 boost::shared_ptr<AnimationData> data; ///< Internal data.
303
304 AnimationData::Sequence* currentSequence; ///< Active sequence.
305 unsigned frameCounter; ///< Current frame.
306 unsigned frameIndex; ///< Index of current frame.
307 Scalar timeAccum; ///< Time accumulation.
308 Scalar frameDuration; ///< Scaled frame duration.
309 };
310
311
312 Animation::Animation(const std::string& name) :
313 // pass through
314 impl_(new Animation::AnimationImpl(name)) {}
315
316
317 void Animation::startSequence(const std::string& name)
318 {
319 // pass through
320 impl_->startSequence(name);
321 }
322
323 void Animation::update(Scalar t, Scalar dt)
324 {
325 // pass through
326 impl_->update(t, dt);
327 }
328
329
330 /**
331 * Gets the index for the current frame. This is presumably called by some
332 * drawing code which will draw the correct current frame.
333 */
334
335 unsigned Animation::getFrame() const
336 {
337 return impl_->frameIndex;
338 }
339
340
341 /**
342 * Specialized search location for animation files. They can be found in the
343 * "animations" subdirectory of any of the searched directories.
344 */
345
346 std::string Animation::getPathToResource(const std::string& name)
347 {
348 return Resource::getPathToResource("animations/" + name + ".json");
349 }
350
351
352 } // namespace Mf
353
354 /** vim: set ts=4 sw=4 tw=80: *************************************************/
355
This page took 0.051835 seconds and 3 git commands to generate.