1 /*
2 * Copyright (C) 2012 Daniel Manjarres
3 * Copyright (C) 2013-2015 Kim Woelders
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies of the Software, its documentation and marketing & publicity
14 * materials, and acknowledgment shall be given in the documentation, materials
15 * and software packages that this Software was used.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24 #include "config.h"
25
26 #include <math.h>
27
28 #include "E.h"
29 #include "animation.h"
30 #include "eobj.h"
31 #include "timers.h"
32 #include "util.h"
33
34 #define ENABLE_DEBUG 1
35 #if ENABLE_DEBUG
36 #define Dprintf(fmt...) if(EDebug(EDBUG_TYPE_ANIM))Eprintf(fmt)
37 #define D2printf(fmt...) if(EDebug(EDBUG_TYPE_ANIM)>1)Eprintf(fmt)
38 #define D3printf(fmt...)
39 #define EOW(eo) (eo ? EobjGetXwin(eo) : NoXID)
40 #else
41 #define Dprintf(fmt...)
42 #define D2printf(fmt...)
43 #define D3printf(fmt...)
44 #endif /* ENABLE_DEBUG */
45
46 #define FPS Mode.screen.fps
47
48 static int timing_engine(void);
49
50 /*
51 * State
52 */
53 static struct {
54 Timer *timer;
55 Idler *idler;
56 unsigned int time_ms; /* Just use Mode.events.time_ms? */
57 unsigned int seqn;
58 } Mode_anim = {
59 NULL, NULL, 0, 0
60 };
61
62 static int
_AnimatorsTimer(void * timer_call)63 _AnimatorsTimer(void *timer_call)
64 {
65 int frame_skip;
66 int dt;
67
68 /* Remember current event run sequence number */
69 Mode_anim.seqn = Mode.events.seqn;
70 /* Remember current event time */
71 Mode_anim.time_ms = Mode.events.time_ms;
72
73 frame_skip = timing_engine();
74
75 /* time = partial_frames * usecs_per_partial_frame */
76 if (frame_skip < 1000000)
77 dt = (1000 * (frame_skip + 1)) / FPS;
78 else
79 dt = 1000000000; /* Some arbitrary high value */
80
81 if (timer_call)
82 {
83 TimerSetInterval(Mode_anim.timer, dt);
84 }
85 else
86 {
87 TIMER_DEL(Mode_anim.timer);
88 TIMER_ADD(Mode_anim.timer, dt, _AnimatorsTimer, (void *)1L);
89 }
90
91 D2printf("%s (%s) frame_skip=%d dt=%d\n", __func__,
92 timer_call ? "TIMER" : "IDLER", frame_skip, dt);
93
94 return 1;
95 }
96
97 static void
_AnimatorsIdler(void * data)98 _AnimatorsIdler(void *data)
99 {
100 /* Don't run idler if we have just run timer */
101 if (Mode_anim.seqn == Mode.events.seqn)
102 return;
103
104 _AnimatorsTimer(data);
105 }
106
107 static void
_AnimatorsInit(void)108 _AnimatorsInit(void)
109 {
110 TIMER_ADD(Mode_anim.timer, 100, _AnimatorsTimer, (void *)1L);
111 Mode_anim.idler = IdlerAdd(_AnimatorsIdler, (void *)0L);
112 }
113
114 /*
115 * The animation engine
116 */
117
118 #define LATER(frame, than) ((int)(frame - than) > 0)
119 #define LATER_EQ(frame, than) ((int)(frame - than) >= 0)
120
121 struct _animator {
122 struct _animator *next;
123 AnimCbFunc *func;
124 AnimDoneFunc *done;
125 animation_category category;
126 int duration;
127 esound_e start_sound;
128 esound_e end_sound;
129 char initialized;
130 char serialize;
131 char cancelled;
132 unsigned int start_frame;
133 unsigned int end_frame;
134 unsigned int next_frame;
135 unsigned int last_tms;
136 EObj *eo;
137 };
138
139 static Animator *global_animators;
140
141 /* This is the frame we THINk we are currently displaying.
142 * The next frame to render is this + 1. */
143 static unsigned int current_frame_num = 1;
144
145 /* This is the number of the next frame we need to render for a pending
146 * animation */
147 static unsigned int skip_to_frame_num = 0;
148
149 static char anim_recheck = 0;
150
151 Animator *
AnimatorAdd(EObj * eo,animation_category category,AnimCbFunc * func,int duration,int serialize,size_t data_size,void * data)152 AnimatorAdd(EObj * eo, animation_category category, AnimCbFunc * func,
153 int duration, int serialize, size_t data_size, void *data)
154 {
155 Animator *an, **insert;
156
157 an = (Animator *) calloc(1, sizeof(Animator) + data_size);
158 if (!an)
159 return NULL;
160
161 Dprintf("%s: %u/%u: %#x %p C%d\n", __func__,
162 current_frame_num, skip_to_frame_num, EOW(eo), an, category);
163
164 if (!Mode_anim.timer)
165 _AnimatorsInit();
166
167 insert = eo ? &eo->animations : &global_animators;
168 while (*insert)
169 insert = &((*insert)->next);
170 *insert = an;
171
172 an->func = func;
173 if (duration >= 0)
174 {
175 an->duration = (duration * FPS) / 1000; /* ms -> frames */
176 if (an->duration == 0)
177 an->duration = 1; /* At least one frame */
178 }
179 else
180 an->duration = -1; /* Forever */
181 an->category = category;
182 an->serialize = serialize;
183
184 an->eo = eo;
185 an->start_sound = an->end_sound = SOUND_NONE;
186
187 if (data_size)
188 memcpy(an + 1, data, data_size);
189
190 anim_recheck = 1;
191
192 return an;
193 }
194
195 void
AnimatorSetSound(Animator * an,esound_e start_sound,esound_e end_sound)196 AnimatorSetSound(Animator * an, esound_e start_sound, esound_e end_sound)
197 {
198 if (!an)
199 return;
200 an->start_sound = start_sound;
201 an->end_sound = end_sound;
202 }
203
204 void
AnimatorSetDoneFunc(Animator * an,AnimDoneFunc * done)205 AnimatorSetDoneFunc(Animator * an, AnimDoneFunc * done)
206 {
207 an->done = done;
208 }
209
210 static void
_AnimatorDel(Animator * an)211 _AnimatorDel(Animator * an)
212 {
213 Dprintf("%s: %u/%u: %#x %p C%d\n", __func__,
214 current_frame_num, skip_to_frame_num, EOW(an->eo), an, an->category);
215 Efree(an);
216 }
217
218 void
AnimatorsFree(EObj * eo)219 AnimatorsFree(EObj * eo)
220 {
221 Animator *an, *next;
222
223 for (an = eo->animations; an; an = next)
224 {
225 next = an->next;
226 _AnimatorDel(an);
227 }
228 }
229
230 void *
AnimatorGetData(Animator * an)231 AnimatorGetData(Animator * an)
232 {
233 return (an) ? an + 1 : NULL;
234 }
235
236 /* Quarter period sinusoidal used in time limited animations */
237 #define M_PI_F ((float)(M_PI))
238 #define REMAINING(elapsed, duration) \
239 (int)(1024 * (1.f - cosf(((M_PI_F / 2 * (elapsed)) / (duration)))))
240
241 static unsigned int
_AnimatorsRun(Animator ** head,unsigned int frame_num,unsigned int next_frame)242 _AnimatorsRun(Animator ** head, unsigned int frame_num, unsigned int next_frame)
243 {
244 Animator *an, *next, **pprev;
245 int res;
246 int first;
247 int remaining;
248 int delta_t;
249
250 for (first = 1, pprev = head, an = *head; an; an = next)
251 {
252 D3printf("%s: %#x %p\n", __func__, EOW(an->eo), an);
253 next = an->next;
254
255 if (an->cancelled)
256 {
257 res = ANIM_RET_CANCEL_ANIM;
258 goto check_res;
259 }
260
261 if (!an->initialized)
262 {
263 /* Just added - calculate first/last frame */
264 /* NB! New animations start one frame into the future */
265 if (an->serialize)
266 {
267 /* Start when other non-forever animations have run */
268 if (!first)
269 goto do_next;
270 Dprintf("%s: %#x %p C%d: De-serialize\n", __func__,
271 EOW(an->eo), an, an->category);
272 }
273 an->initialized = 1;
274 an->start_frame = frame_num + 1;
275 an->end_frame = an->start_frame + an->duration - 1;
276 an->next_frame = an->start_frame;
277 an->last_tms = Mode_anim.time_ms;
278 }
279
280 /* Don't serialize animations that follow an inf loop with the inf loop */
281 if (an->duration > 0)
282 first = 0;
283
284 if (an->category >= 0 && LATER(an->next_frame, frame_num))
285 goto check_next_frame;
286
287 /*{ start of old _AnimatorRun() */
288
289 if (an->start_sound)
290 {
291 SoundPlay(an->start_sound);
292 an->start_sound = SOUND_NONE;
293 }
294
295 delta_t = Mode_anim.time_ms - an->last_tms;
296 an->last_tms = Mode_anim.time_ms;
297
298 if (an->duration > 0)
299 {
300 remaining = 0;
301 if (frame_num < an->end_frame)
302 remaining = REMAINING(an->end_frame - frame_num, an->duration);
303 }
304 else
305 {
306 remaining = delta_t;
307 }
308
309 D2printf("%s: eo=%p an=%p cat=%d rem=%4d dur=%4d dt=%4d\n", __func__,
310 an->eo, an, an->category, remaining, an->duration, delta_t);
311 res = an->func(an->eo, remaining, an + 1);
312 Dprintf("%s: res=%4d num=%u end=%u\n", __func__, res, frame_num,
313 an->end_frame);
314
315 if (res >= 0)
316 {
317 if (an->duration > 0 && remaining <= 0)
318 {
319 Dprintf("%s: %#x %p C%d: autocancelling\n", __func__,
320 EOW(an->eo), an, an->category);
321 res = ANIM_RET_CANCEL_ANIM;
322 }
323 }
324 else
325 {
326 Dprintf("%s: %#x %p C%d: self cancelling\n", __func__,
327 EOW(an->eo), an, an->category);
328 }
329
330 /*} end of old _AnimatorRun() */
331
332 check_res:
333 if (res >= 0)
334 {
335 /* animator will run again */
336 an->next_frame = frame_num + 1 + res;
337 }
338 else
339 {
340 if (an->done)
341 an->done(an->eo, an + 1);
342
343 if (an->end_sound)
344 SoundPlay(an->end_sound);
345
346 _AnimatorDel(an);
347 *pprev = next;
348 continue; /* Skip pprev update */
349 }
350
351 check_next_frame:
352 if (an->category >= 0 && LATER(next_frame, an->next_frame))
353 next_frame = an->next_frame;
354
355 do_next:
356 pprev = &an->next;
357 }
358
359 return next_frame;
360 }
361
362 static unsigned int
_AnimatorsRunAll(unsigned int frame_num)363 _AnimatorsRunAll(unsigned int frame_num)
364 {
365 EObj **lst;
366 EObj *const *lst2;
367 unsigned int next_frame;
368 int num, i;
369
370 lst2 = EobjListStackGet(&num);
371 lst = EMALLOC(EObj *, num);
372 memcpy(lst, lst2, num * sizeof(EObj *));
373
374 next_frame = frame_num + 0x7fffffff;
375
376 D3printf("%s: %u/%u\n", __func__, current_frame_num, skip_to_frame_num);
377
378 for (i = 0; i < num; i++)
379 next_frame = _AnimatorsRun(&lst[i]->animations, frame_num, next_frame);
380
381 Efree(lst);
382
383 next_frame = _AnimatorsRun(&global_animators, frame_num, next_frame);
384
385 return next_frame;
386 }
387
388 int
AnimatorDel(EObj * eo,Animator * anx)389 AnimatorDel(EObj * eo, Animator * anx)
390 {
391 Animator *an;
392
393 for (an = (eo) ? eo->animations : global_animators; an; an = an->next)
394 {
395 if (an != anx)
396 continue;
397 Dprintf("%s: %u/%u: %#x %p C%d\n", __func__,
398 current_frame_num, skip_to_frame_num, EOW(an->eo), an,
399 an->category);
400 an->cancelled = 1;
401 return 1;
402 }
403 return 0;
404 }
405
406 static unsigned int
_FrameNum(void)407 _FrameNum(void)
408 {
409 static char init = 0;
410 static unsigned int tp = 0;
411 static unsigned int fp = 0;
412 unsigned int t, frame, dx;
413
414 t = GetTimeMs();
415
416 if (!init)
417 {
418 init = 1;
419 tp = t;
420 }
421
422 dx = t - tp;
423 frame = fp + (dx * FPS) / 1000;
424
425 if (dx > 1000000)
426 {
427 dx /= 1000;
428 tp += dx * 1000;
429 fp += dx * FPS;
430 }
431
432 return frame;
433 }
434
435 static unsigned int
get_check_frame_count(unsigned int last_frame __UNUSED__,unsigned int skip_to_frame,unsigned int * good_framesp,unsigned int * last_skipped_framep,const char * msg)436 get_check_frame_count(unsigned int last_frame __UNUSED__,
437 unsigned int skip_to_frame,
438 unsigned int *good_framesp,
439 unsigned int *last_skipped_framep, const char *msg)
440 {
441 unsigned int frame_num;
442
443 frame_num = _FrameNum();
444
445 if (frame_num > skip_to_frame)
446 {
447 if (EDebug(1))
448 Eprintf("@%u %s missed %u frames after %u [%u] good frames\n",
449 frame_num, msg, frame_num - skip_to_frame, *good_framesp,
450 skip_to_frame - 1 - *last_skipped_framep);
451 *good_framesp = 0;
452 *last_skipped_framep = frame_num - 1;
453 }
454
455 return frame_num;
456 }
457
458 static int
timing_engine(void)459 timing_engine(void)
460 {
461 static unsigned int last_frame_num;
462 static unsigned int good_frames;
463 static unsigned int last_skipped_frame;
464 int frameskip;
465
466 current_frame_num = get_check_frame_count(last_frame_num, skip_to_frame_num,
467 &good_frames, &last_skipped_frame,
468 "before render");
469
470 D2printf("%s: cur/last=%u/%u next=%u good=%u last-skipped=%u\n",
471 __func__, current_frame_num, last_frame_num, skip_to_frame_num,
472 good_frames, last_skipped_frame);
473
474 if (current_frame_num == last_frame_num && !anim_recheck)
475 goto done;
476
477 last_frame_num = current_frame_num;
478 anim_recheck = 0;
479
480 skip_to_frame_num = _AnimatorsRunAll(current_frame_num);
481
482 done:
483 frameskip = skip_to_frame_num - current_frame_num - 1;
484
485 return frameskip;
486 }
487