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