]> icculus.org git repositories - taylor/freespace2.git/blob - src/anim/animplay.cpp
sync up talking head fixes from FS2_Open code base (still not 100%)
[taylor/freespace2.git] / src / anim / animplay.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Anim/AnimPlay.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C module for playing back anim files
16  *
17  * $Log$
18  * Revision 1.6  2005/08/12 08:55:13  taylor
19  * sync up talking head fixes from FS2_Open code base (still not 100%)
20  *
21  * Revision 1.5  2004/09/20 01:31:44  theoddone33
22  * GCC 3.4 fixes.
23  *
24  * Revision 1.4  2004/06/11 00:27:06  tigital
25  * byte-swapping changes for bigendian systems
26  *
27  * Revision 1.3  2002/06/09 04:41:15  relnev
28  * added copyright header
29  *
30  * Revision 1.2  2002/05/07 03:16:43  theoddone33
31  * The Great Newline Fix
32  *
33  * Revision 1.1.1.1  2002/05/03 03:28:08  root
34  * Initial import.
35  *
36  * 
37  * 9     9/13/99 11:26p Andsager
38  * Add debug code to check for poorly sized anis
39  * 
40  * 8     8/26/99 9:45a Dave
41  * First pass at easter eggs and cheats.
42  * 
43  * 7     7/16/99 1:49p Dave
44  * 8 bit aabitmaps. yay.
45  * 
46  * 6     7/13/99 1:15p Dave
47  * 32 bit support. Whee!
48  * 
49  * 5     6/10/99 10:34a Dave
50  * Removed unnecessary assert.
51  * 
52  * 4     11/30/98 1:07p Dave
53  * 16 bit conversion, first run.
54  * 
55  * 3     10/22/98 6:14p Dave
56  * Optimized some #includes in Anim folder. Put in the beginnings of
57  * parse/localization support for externalized strings and tstrings.tbl
58  * 
59  * 2     10/07/98 10:52a Dave
60  * Initial checkin.
61  * 
62  * 1     10/07/98 10:48a Dave
63  * 
64  * 38    6/23/98 4:18p Hoffoss
65  * Fixed some bugs with AC release build.
66  * 
67  * 37    5/18/98 5:59p Hoffoss
68  * Made command briefing advanced now once the speech stops and animation
69  * has fully played once, whichever is longer.
70  * 
71  * 36    5/14/98 6:29p Hoffoss
72  * Fixed some warnings a release rebuild all turned up.
73  * 
74  * 35    5/07/98 3:11a Lawrance
75  * Implement custom streaming code
76  * 
77  * 34    4/27/98 3:36p Dave
78  * 
79  * 33    3/25/98 8:43p Hoffoss
80  * Changed anim_play() to not be so damn complex when you try and call it.
81  * 
82  * 32    2/05/98 9:21p John
83  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
84  * game.
85  * 
86  * 31    1/19/98 11:37p Lawrance
87  * Fixing Optimization build warnings
88  * 
89  * 30    1/19/98 3:09p Hoffoss
90  * Only free an anim instance if it is actually playing.
91  * 
92  * 29    1/14/98 6:43p Lawrance
93  * Add ref_count to anim struct, so we don't free multiple times
94  * 
95  * 28    12/30/97 6:44p John
96  * Made g3_Draw_bitmap functions account for aspect of bitmap.
97  * 
98  * 27    12/27/97 2:35p John
99  * Restructed some code so that if the memory-mapped file open fails, it
100  * will still read it the old-fashioned, non-memory mapped way.
101  * 
102  * 26    12/24/97 9:10p Lawrance
103  * take out some debugging statements
104  * 
105  * 25    12/24/97 8:57p Lawrance
106  * Added anim_ignore_next_frametime()
107  * 
108  * 24    12/07/97 2:06p Dave
109  * Removed some debug frame-checking code.
110  * 
111  * 23    12/06/97 2:55p Dave
112  * 
113  * 22    11/29/97 2:05p John
114  * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2,
115  * like they used to incorrectly assume.   Added code to model to read in
116  * thruster radius's.
117  * 
118  * 21    11/20/97 5:36p Dave
119  * Hooked in a bunch of main hall changes (including sound). Made it
120  * possible to reposition (rewind/ffwd) 
121  * sound buffer pointers. Fixed animation direction change framerate
122  * problem.
123  * 
124  * 20    11/20/97 4:33p Sandeep
125  * ALAN: ensure instance->paused gets initialized
126  * 
127  * 19    11/20/97 4:12p Lawrance
128  * when paused, don't increment time_elapsed
129  * 
130  * 18    11/19/97 8:28p Dave
131  * Hooked in Main Hall screen. Put in Anim support for ping ponging
132  * animations as well as general reversal of anim direction.
133  * 
134  * 17    9/11/97 4:17p Allender
135  * use _MAX_PATH instead of MAX_FILENAME_LENGTH for anim_play since it is
136  * called from MoviePlayer with full path
137  * 
138  * 16    9/11/97 10:49a Lawrance
139  * improve anim_show_next_frame()
140  * 
141  * 15    9/10/97 4:59p Lawrance
142  * improve comments to anim_play() function
143  * 
144  * 14    9/03/97 4:19p John
145  * changed bmpman to only accept ani and pcx's.  made passing .pcx or .ani
146  * to bm_load functions not needed.   Made bmpman keep track of palettes
147  * for bitmaps not mapped into game palettes.
148  * 
149  * 13    8/30/97 2:11p Lawrance
150  * allow animations to loop
151  * 
152  * 12    8/25/97 11:13p Lawrance
153  * support framerate independent playback with the option of now advancing
154  * more than one frame at a time
155  * 
156  * 11    8/22/97 8:20a Lawrance
157  * display scrolling text properly, with indicator that text exitsts to be
158  * scrolled
159  * 
160  * 10    8/21/97 5:11p Lawrance
161  * frame numbering for ANI's now is from 0 -> total_frames-1.
162  * 
163  * 9     8/19/97 9:30a Lawrance
164  * print out reason if file doesn't open right
165  * 
166  * 8     7/28/97 10:42p Lawrance
167  * re-did interface to unpack_frame() to make more general
168  * 
169  * 7     7/21/97 11:41a Lawrance
170  * make playback time of .ani files keyed of frametime
171  * 
172  * 6     7/20/97 6:57p Lawrance
173  * supporting new RLE format
174  * 
175  * 5     7/11/97 11:54a John
176  * added rotated 3d bitmaps.
177  * 
178  * 4     6/27/97 4:36p Lawrance
179  * update pal translation table when gr_screen.signature changes
180  * 
181  * 3     6/26/97 3:00p Lawrance
182  * fix bug with playing 3d anims
183  * 
184  * 2     6/26/97 12:12a Lawrance
185  * supporting anti-aliased bitmap animations
186  * 
187  * 1     6/23/97 5:09p Lawrance
188  * 
189  * 24    6/03/97 5:53p Lawrance
190  * don't unload bitmap after bm_create
191  * 
192  * $NoKeywords: $
193  */
194
195 #include "animplay.h"
196 #include "linklist.h"
197 #include "timer.h"
198 #include "bmpman.h"
199 #include "2d.h"
200 #include "3d.h"
201 #include "grinternal.h"
202 #include "pcxutils.h"
203 #include "packunpack.h"
204 #include "cfile.h"
205
206 static color Color_xparent;
207
208 anim *first_anim = NULL;
209 anim_instance anim_free_list;
210 anim_instance anim_render_list;
211
212 #define MAX_ANIM_INSTANCES 25
213 anim_instance anim_render_instance[MAX_ANIM_INSTANCES];
214
215 int Anim_paused;        // global variable to pause the playing back of anims
216 int Anim_inited = FALSE;
217
218 fix t1,t2;
219
220 int Anim_ignore_frametime=0;    // flag used to ignore frametime... useful when need to avoid saturated frametimes
221
222 // -------------------------------------------------------------------------------------------------
223 // anim_init() will queue all the anim_render_instance[] elements onto the anim_free_list
224 //
225 void anim_init()
226 {
227         int i;
228
229         if ( Anim_inited == TRUE )
230                 return;
231
232         list_init( &anim_free_list );
233         list_init( &anim_render_list );
234
235         // Link all anim render slots into the free list
236         for (i=1; i < MAX_ANIM_INSTANCES; i++)  {
237                 list_append(&anim_free_list, &anim_render_instance[i]);
238         }
239         
240         Anim_paused = 0;
241         Anim_inited = TRUE;
242 }
243
244 // -------------------------------------------------------------------------------------------------
245 // anim_render_all() will display the frames for the currently playing anims
246 //
247 void anim_render_all(int screen_id, float frametime)
248 {
249         anim_instance* A;
250         anim_instance* temp;
251
252         A = GET_FIRST(&anim_render_list);
253         while( A !=END_OF_LIST(&anim_render_list) )     {
254                 temp = GET_NEXT(A);
255                 if ( A->screen_id == screen_id ) {
256                         if ( Anim_ignore_frametime ) {
257                                 frametime = 0.0f;
258                                 Anim_ignore_frametime=0;
259                         }
260                         if ( anim_show_next_frame(A, frametime) == -1 ) {
261                                 A->data = NULL;
262                                 anim_release_render_instance(A);        
263                         }
264                 }
265                 A = temp;
266         }
267 }
268
269 // -------------------------------------------------------------------------------------------------
270 // anim_render_one() will display the frames for the passed animation, it will ignore animations which
271 // do not have the same id as the passed screen_id
272 //
273 void anim_render_one(int screen_id, anim_instance *ani, float frametime)
274 {
275         // make sure this guy's screen id matches the passed one
276         if(screen_id != ani->screen_id){
277                 return;
278         }
279
280         // otherwise render it  
281         if ( Anim_ignore_frametime ) {
282                 frametime = 0.0f;
283                 Anim_ignore_frametime=0;
284         }
285         if ( anim_show_next_frame(ani, frametime) == -1 ) {
286                 ani->data = NULL;
287                 anim_release_render_instance(ani);      
288         }       
289 }
290
291 MONITOR(NumANIPlayed);
292
293 // Setup an anim_play_struct for passing into anim_play().  Will fill in default values, which you
294 // can then change before calling anim_play().
295 //
296 void anim_play_init(anim_play_struct *aps, anim *a_info, int x, int y)
297 {
298         aps->anim_info = a_info;
299         aps->x = x;
300         aps->y = y;
301         aps->start_at = 0;
302         aps->stop_at = a_info->total_frames - 1;
303         aps->screen_id = 0;
304         aps->world_pos = NULL;
305         aps->radius = 0.0f;
306         aps->framerate_independent = 0;
307         aps->color = NULL;
308         aps->skip_frames = 1;
309         aps->looped = 0;
310         aps->ping_pong = 0;
311 }
312
313 // -------------------------------------------------------------------------------------------------
314 // anim_play() will add an anim instance to the anim_render_list.  This will cause the
315 // anim to be played at the x,y position specified in the parameter list.
316 //
317 // input:
318 //
319 //              anim_info       =>      the compressed animation that we should make an instance from
320 //              x                               =>      x position of animation to play at (top left corner)
321 //              y                               =>      y position of animation to play at ( top left corner)
322 //              start_at                =>      frame number to start at (note: numbering is from 0->num_frames-1)
323 //              stop_at         =>      frame number to stop at (note: numbering is from 0->num_frames-1)
324 //              screen_id       =>      OPTIONAL (default value 0): screen signature so animation only plays when
325 //                                                      anim_render_all() called with that same signature
326 //              world_pos       =>      OPTIONAL (default value NULL): only give a world pos when you want to
327 //                                                      play the animation at a 3D location.  You must specify radius when
328 //                                                      this is non-null.
329 //              radius          =>      OPTIONAL (default value 0): only needed when the animation is playing
330 //                                                      as a 3D animation (this is only when world_pos in not NULL).
331 //              fi                              =>      OPTIONAL (default value 0): framerate indepentdent flag, when set TRUE
332 //                                                      the animation will skip frames if necessary to maintain the fps value
333 //                                                      associated with the animation
334 //              color                   =>      OPTIONAL (default value NULL) address of an alpha color struct.  Only
335 //                                                      required when the animation should be drawn with an alpha color.
336 //              skip_frames     => OPTIONAL (default value 1) should anim skip frames when doing framerate
337 //                   independent playback
338 //              looped          =>      OPTIONAL (default value 0) should anim play looped (ie forever)
339 //
340 // returns:
341 //
342 //              pointer to instance     => success
343 //              NULL                                            => if anim anim could not be played
344 //
345 anim_instance *anim_play(anim_play_struct *aps)
346 {
347         Assert( aps->anim_info != NULL );
348         Assert( aps->start_at >= 0 );
349         Assert( aps->stop_at < aps->anim_info->total_frames );
350         // Assert( aps->stop_at >= aps->start_at );
351         Assert( !(aps->looped && aps->ping_pong) );  // shouldn't have these both set at once
352
353         MONITOR_INC(NumANIPlayed, 1);
354         
355         // if (aps->ping_pong && !(aps->anim_info->flags & ANF_ALL_KEYFRAMES));
356
357         anim_instance *instance;
358
359         // Find next free anim instance slot on queue
360         instance = GET_FIRST(&anim_free_list);
361         Assert( instance != &anim_free_list );  // shouldn't have the dummy element
362
363         // remove instance from the free list
364         list_remove( &anim_free_list, instance );
365
366         // insert instance onto the end of anim_render_list
367         list_append( &anim_render_list, instance );
368
369         aps->anim_info->instance_count++;
370         instance->frame_num = -1;
371         instance->last_frame_num = -99;
372         instance->parent = aps->anim_info;
373         instance->data = aps->anim_info->data;
374         if ( anim_instance_is_streamed(instance) ) {
375                 instance->file_offset = instance->parent->file_offset;
376         }
377         instance->frame = (ubyte *) malloc(instance->parent->width * instance->parent->height * 2);
378         Assert( instance->frame != NULL );
379         instance->time_elapsed = 0.0f;
380         instance->stop_at = aps->stop_at;
381         instance->x = aps->x;
382         instance->y = aps->y;
383         instance->world_pos = aps->world_pos;
384         instance->radius = aps->radius;
385         instance->framerate_independent = aps->framerate_independent;
386         instance->last_bitmap = -1;
387         instance->stop_now = FALSE;
388         instance->screen_id = aps->screen_id;
389         instance->aa_color = aps->color;
390         instance->skip_frames = aps->skip_frames;
391         instance->looped = aps->looped;
392         instance->ping_pong = aps->ping_pong;
393         instance->direction = ANIM_DIRECT_FORWARD;
394         instance->paused = 0;
395         instance->loop_count = 0;
396         if ( aps->color == NULL ){
397                 instance->xlate_pal = 1;
398         } else {
399                 instance->xlate_pal = 0;
400         }
401
402         // determining the start_at frame is more complicated, since it must be a key-frame.
403         // Futhermore, need to subtract 1 from key-frame number, since frame number is always
404         // incremented the first time anim_show_next_frame() is called
405
406         instance->start_at = aps->start_at;
407
408         if ( aps->start_at > 0 ) {
409                 key_frame *keyp;
410                 int idx;
411                 int key = 0;
412                 int offset = 0;
413                 int frame_num = aps->start_at;
414
415                 keyp = instance->parent->keys;
416                 idx = 0;
417                 while (idx < instance->parent->num_keys) {
418                         if (key == frame_num)
419                                 break;
420
421                         key = keyp[idx].frame_num - 1;
422                         offset = keyp[idx].offset;
423
424                         idx++;
425                 }
426                 /*while (keyp) {
427                         if (( (keyp->frame_num-1) <= frame_num) && ( (keyp->frame_num-1) > key)) {  // find closest key
428                                 key = keyp->frame_num-1;
429                                 offset = keyp->offset;
430                                 if ( key == frame_num )
431                                         break;
432                         }
433
434                         keyp = keyp->next;
435                 }*/
436
437                 if (key > instance->frame_num) {  // best key is closer than current position
438                         instance->frame_num = key;
439                         if ( anim_instance_is_streamed(instance) ) {
440                                 instance->file_offset = instance->parent->file_offset + offset;
441                         } else {
442                                 instance->data = instance->parent->data + offset;
443                         }
444
445                 }
446
447                 instance->frame_num--;  // required
448         }
449
450         return instance;
451 }
452
453 // -----------------------------------------------------------------------------
454 //      anim_show_next_frame()
455 //
456 //      This function is called to blit the next frame of an anim instance to the 
457 // screen.  This is normally called by the anim_render_all() function.
458 //
459 //      input:  instance                =>              pointer to animation instance
460 //                              frametime       =>              time elapsed since last call, in seconds
461 //
462 int anim_show_next_frame(anim_instance *instance, float frametime)
463 {
464         int             bitmap_id, bitmap_flags=0, new_frame_num, frame_diff=0, i, n_frames=0,frame_save;
465         float           percent_through, decompress_time, render_time, time;
466         vertex  image_vertex;
467         int aabitmap = 0;
468         int bpp = 16;
469
470         Assert( instance != NULL );
471
472         instance->time_elapsed += frametime;
473
474         // Advance to the next frame, if we determine enough time has elapsed.
475         if(instance->direction == ANIM_DIRECT_FORWARD)
476                 n_frames = instance->stop_at - instance->start_at + 1;
477         else if(instance->direction == ANIM_DIRECT_REVERSE)
478                 n_frames = instance->start_at - instance->stop_at + 1;
479         time = n_frames / i2fl(instance->parent->fps);
480
481         percent_through = instance->time_elapsed / time;
482
483         if(instance->direction == ANIM_DIRECT_FORWARD)
484                 new_frame_num = instance->start_at - 1 + fl2i(percent_through * n_frames + 0.5f);
485         else
486                 new_frame_num = instance->start_at - 1 - fl2i(percent_through * n_frames + 0.5f);
487
488         frame_save = instance->frame_num;
489
490         // If framerate independent, use the new_frame_num... unless instance->skip_frames is
491         // FALSE, then only advance a maximum of one frame (this is needed since some big animations
492         // should just play slower rather than taking the hit of decompressing multiple frames and
493         // creating an even greater slowdown    
494         if (instance->framerate_independent) {
495                 if(instance->direction == ANIM_DIRECT_FORWARD){
496                         if ( new_frame_num > instance->last_frame_num) {
497                                 if ( instance->skip_frames )
498                                         instance->frame_num = new_frame_num;
499                                 else
500                                         instance->frame_num++;
501                         }
502                 } else if(instance->direction == ANIM_DIRECT_REVERSE){
503                         if( new_frame_num < instance->last_frame_num) {
504                                 if ( instance->skip_frames )
505                                         instance->frame_num = new_frame_num;
506                                 else
507                                         instance->frame_num--;
508                         }
509                 }
510         }
511         else {                  
512                 if(instance->direction == ANIM_DIRECT_FORWARD){
513                         if ( new_frame_num > instance->last_frame_num) {
514                                 instance->frame_num++;
515                         }
516                 } else if(instance->direction == ANIM_DIRECT_REVERSE){
517                         if ( new_frame_num < instance->last_frame_num) {
518                                 instance->frame_num--;
519                         }
520                 }                       
521         }
522
523         if(instance->direction == ANIM_DIRECT_FORWARD){
524                 if ( instance->frame_num < instance->start_at ) {
525                         instance->frame_num = instance->start_at;
526                 }       
527         } else if(instance->direction == ANIM_DIRECT_REVERSE){
528                 if ( instance->frame_num > instance->start_at ) {
529                         instance->frame_num = instance->start_at;
530                 }       
531         }
532
533         if ( instance->stop_now == TRUE ) {
534                 return -1;
535         }
536
537         // If past the last frame, clamp to the last frame and then set the stop_now flag in the
538         // anim instance.  The next iteration, the animation will stop.
539         if(instance->direction == ANIM_DIRECT_FORWARD){
540                 if (instance->frame_num >= instance->stop_at ) {
541                         if (instance->looped) {                                                                         // looped animations
542                                 instance->frame_num = instance->stop_at;
543                                 instance->time_elapsed = 0.0f;
544                         } else if(instance->ping_pong) {                                                        // pingponged animations
545                                 instance->frame_num = instance->stop_at;
546                                 // instance->time_elapsed = 0.0f;
547                                 anim_reverse_direction(instance);
548                         } else {                                                                                                                        // one-shot animations
549                                 instance->frame_num = instance->stop_at;
550                                 instance->last_frame_num = instance->frame_num;
551                                 instance->stop_now = TRUE;
552                         }
553                 }
554         } else if(instance->direction == ANIM_DIRECT_REVERSE){
555                 if (instance->frame_num <= instance->stop_at ) {
556                         if (instance->looped) {                                                                         // looped animations
557                                 instance->frame_num = instance->stop_at;
558                                 instance->time_elapsed = 0.0f;
559                         } else if(instance->ping_pong) {                                                        // pingponged animations
560                                 instance->frame_num = instance->stop_at;
561                                 // instance->time_elapsed = 0.0f;
562                                 anim_reverse_direction(instance);
563                         } else {                                                                                                                        // one-shot animations
564                                 instance->frame_num = instance->stop_at+1;
565                                 instance->last_frame_num = instance->frame_num;
566                                 instance->stop_now = TRUE;
567                         }
568                 }
569         }
570
571         if(instance->direction == ANIM_DIRECT_FORWARD){
572                 if( instance->last_frame_num >= instance->start_at ) {
573                         frame_diff = instance->frame_num - instance->last_frame_num;            
574                 } else {
575                         frame_diff = 1;
576                 }
577         } else if(instance->direction == ANIM_DIRECT_REVERSE){
578                 if( instance->last_frame_num <= instance->start_at ) {
579                         frame_diff = instance->last_frame_num - instance->frame_num;
580                 } else {
581                         frame_diff = 1;
582                 }
583         }               
584         Assert(frame_diff >= 0);
585         //      nprintf(("Alan","FRAME DIFF: %d\n",frame_diff));
586         Assert( instance->frame_num >= 0 && instance->frame_num < instance->parent->total_frames );
587
588         // if the anim is paused, ignore all the above changes and still display this frame
589         if(instance->paused || Anim_paused){
590                 instance->frame_num = frame_save;
591                 instance->time_elapsed -= frametime;
592                 frame_diff = 0;
593         }
594
595         if (instance->parent->flags & ANF_XPARENT){
596                 // bitmap_flags = BMP_XPARENT;
597                 bitmap_flags = 0;
598         } 
599         bpp = 16;
600         if(instance->aa_color != NULL){
601                 bitmap_flags |= BMP_AABITMAP;
602                 aabitmap = 1;
603                 bpp = 8;
604         }       
605
606         if ( frame_diff > 0 ) {
607                 instance->last_frame_num = instance->frame_num;         
608
609                 t1 = timer_get_fixed_seconds();
610                 for ( i = 0; i < frame_diff; i++ ) {
611                         anim_check_for_palette_change(instance);                        
612
613                         // if we're playing backwards, every frame must be a keyframe and we set the data ptr here
614                         if(instance->direction == ANIM_DIRECT_REVERSE){
615                                 if ( anim_instance_is_streamed(instance) ) {
616                                         instance->file_offset = instance->parent->file_offset + instance->parent->keys[instance->frame_num-1].offset;
617                                 } else {
618                                         instance->data = instance->parent->data + instance->parent->keys[instance->frame_num-1].offset;
619                                 }
620                         }
621
622                         ubyte *temp = NULL;
623                         int temp_file_offset = 0;                       
624
625                         // if we're using bitmap polys
626                         if(Gr_bitmap_poly){
627                                 BM_SELECT_TEX_FORMAT();
628                         }
629
630                         if ( anim_instance_is_streamed(instance) ) {
631                                 if ( instance->xlate_pal ){
632                                         temp_file_offset = unpack_frame_from_file(instance, instance->frame, instance->parent->width*instance->parent->height, instance->parent->palette_translation, aabitmap, bpp);
633                                 } else {
634                                         temp_file_offset = unpack_frame_from_file(instance, instance->frame, instance->parent->width*instance->parent->height, NULL, aabitmap, bpp);
635                                 }
636                         } else {
637                                 if ( instance->xlate_pal ){
638                                         temp = unpack_frame(instance, instance->data, instance->frame, instance->parent->width*instance->parent->height, instance->parent->palette_translation, aabitmap, bpp);
639                                 } else {
640                                         temp = unpack_frame(instance, instance->data, instance->frame, instance->parent->width*instance->parent->height, NULL, aabitmap, bpp);
641                                 }
642                         }
643
644                         // always go back to screen format
645                         BM_SELECT_SCREEN_FORMAT();
646
647                         if(instance->direction == ANIM_DIRECT_FORWARD){
648                                 if ( anim_instance_is_streamed(instance) ) {
649                                         instance->file_offset = temp_file_offset;
650                                 } else {
651                                         instance->data = temp;
652                                 }
653                         }                       
654                 }
655                 t2 = timer_get_fixed_seconds();
656         }
657         else {
658                 t2=t1=0;
659         }
660
661         // this only happens when the anim is being looped, we need to reset the last_frame_num
662         if ( (instance->time_elapsed == 0) && (instance->looped) ) {
663                 instance->last_frame_num = -1;
664                 instance->frame_num = -1;
665                 instance->data = instance->parent->data;
666                 instance->file_offset = instance->parent->file_offset;
667                 instance->loop_count++;
668         }
669                 
670         decompress_time = f2fl(t2-t1);
671
672         t1 = timer_get_fixed_seconds();
673         if ( frame_diff == 0 && instance->last_bitmap != -1 ) {
674                 bitmap_id = instance->last_bitmap;
675         }
676         else {
677                 if ( instance->last_bitmap != -1 ){
678                         bm_release(instance->last_bitmap);
679                 }
680                 bitmap_id = bm_create(16, instance->parent->width, instance->parent->height, instance->frame, bitmap_flags);
681         }
682         
683         if ( bitmap_id == -1 ) {
684                 // anim has finsished playing, free the instance frame data
685                 anim_release_render_instance(instance); 
686                 return -1;
687
688                 // NOTE: there is no need to free the instance, since it was pre-allocated as 
689                 //       part of the anim_free_list
690         }
691         else {
692                 gr_set_bitmap(bitmap_id, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
693                 
694                 // determine x,y to display the bitmap at
695                 if ( instance->world_pos == NULL ) {
696                         if ( instance->aa_color == NULL ) {
697                                 gr_bitmap(instance->x, instance->y);
698                         }
699                         else {
700                                 gr_set_color_fast( (color*)instance->aa_color );
701                                 gr_aabitmap(instance->x, instance->y);
702                         }
703                 }
704                 else {
705                         g3_rotate_vertex(&image_vertex,instance->world_pos);
706                         Assert(instance->radius != 0.0f);
707                         g3_draw_bitmap(&image_vertex, 0, instance->radius*1.5f, TMAP_FLAG_TEXTURED );
708                 }
709
710                 //bm_release(bitmap_id);
711                 instance->last_bitmap = bitmap_id;
712         }
713
714         t2 = timer_get_fixed_seconds();
715         render_time = f2fl(t2-t1);
716
717 //      nprintf(("Alan","DECOMPRESS: %.3fms  RENDER: %.3fms\n", decompress_time*1000, render_time*1000));
718
719         return 0;
720 }
721
722 // -----------------------------------------------------------------------------
723 //      anim_stop_playing()
724 //
725 //      Stop an anim instance that is on the anim_render_list from playing
726 //
727 int anim_stop_playing(anim_instance* instance)
728 {
729         Assert(instance != NULL);
730
731         if ( anim_playing(instance) ) {
732                 anim_release_render_instance(instance);
733         }
734         return 0;
735 }
736
737 // -----------------------------------------------------------------------------
738 //      anim_release_render_instance()
739 //
740 //      Free a particular animation instance that is on the anim_render_list.  Do
741 // not call this function to free an animation instance in general (use 
742 // free_anim_instance() for that), only when you want to free an instance
743 // that is on the anim_render_list
744 //
745 void anim_release_render_instance(anim_instance* instance)
746 {
747         Assert( instance != NULL );
748         Assert(instance->frame);
749         free(instance->frame);
750         instance->frame = NULL;
751         instance->parent->instance_count--;
752
753         if ( instance->last_bitmap != -1 ) {
754                 bm_release(instance->last_bitmap); 
755                 instance->last_bitmap = -1;
756         }
757
758         // remove instance from anim_render_list
759         list_remove( &anim_render_list, instance );
760
761         // insert instance into the anim_free_list
762         list_append( &anim_free_list, instance );
763 }
764
765 // -----------------------------------------------------------------------------
766 //      anim_release_all_instances()
767 //
768 //      Free all anim instances that are on the anim_render_list.
769 //
770 //      input:  screen_id       =>              optional parameter that lets you only free a subset
771 //                                                                              of the anim instances.  A screen_id of 0 is the default
772 //                                                                              value, and this is used for animations that always play
773 //                                                                              when they are placed on the aim_render_list.
774 //
775 void anim_release_all_instances(int screen_id)
776 {
777         anim_instance* A;
778         anim_instance* temp;
779
780         if ( Anim_inited == FALSE )
781                 return;
782
783         A = GET_FIRST(&anim_render_list);
784         while( A !=END_OF_LIST(&anim_render_list) )     {
785                 temp = GET_NEXT(A);
786                 if ( A->screen_id == screen_id || screen_id == 0 ) {
787                         anim_release_render_instance(A);
788                 }
789                 A = temp;
790         }
791 }
792
793 // -----------------------------------------------------------------------------
794 //      anim_read_header()
795 //
796 // Read the header of a .ani file.  Below is the format of a .ani header
797 //
798 //      #bytes  |       description
799 //      2                       |       obsolete, kept for compatibility with old versions
800 //      2                       |       version number
801 //      2                       |       fps
802 //      1                       |       transparent red value
803 //      1                       |       transparent green value
804 //      1                       |       transparent blue value
805 //      2                       |       width
806 //      2                       |       height
807 //      2                       |       number of frames
808 //      2                       |       packer code
809 //      763                     |       palette
810 //      2                       |       number of key frames
811 //      2                       |       key frame number        }               repeats
812 //      4                       |       key frame offset        }               repeats
813 //      4                       |       compressed data length
814 //
815 void anim_read_header(anim *ptr, CFILE *fp)
816 {
817         ptr->width = cfread_short(fp);
818         // If first 2 bytes are zero, this means we are using a new format, which includes
819         // a version, and fps values.  This is only done since a version number was not included
820         // in the original header.
821
822         // default
823         Color_xparent.red = 0;
824         Color_xparent.green = 255;
825         Color_xparent.blue = 0;
826
827         if ( ptr->width == 0 ) {
828                 ptr->version = cfread_short(fp);
829                 ptr->fps = cfread_short(fp);
830
831                 // version 2 added a custom transparency color
832                 if ( ptr->version >= 2 ) {
833                         cfread(&Color_xparent.red, 1, 1, fp);
834                         cfread(&Color_xparent.green, 1, 1, fp);
835                         cfread(&Color_xparent.blue, 1, 1, fp);
836                 }
837
838                 ptr->width = cfread_short(fp);
839         }
840         else {
841                 ptr->version = 0;
842                 ptr->fps = 30;
843         }
844         
845         ptr->height = cfread_short(fp);
846
847 #ifndef NDEBUG
848         // get size of ani compared to power of 2
849         int r, floor_pow;
850         r = ptr->height;
851         floor_pow = 0;
852         
853         while(r >= 2) {
854                 r /= 2;
855                 floor_pow++;
856         }
857
858         int floor_size = (int) pow(2, floor_pow);
859         int diff = ptr->height - floor_size;
860         float waste = 100.0f * float((floor_size - diff))/(2.0f *(float)floor_size);
861
862         if (diff != 0) {
863                 if (ptr->height > 16) {
864                         mprintf(("ANI %s with size %dx%d (%.1f%% wasted)\n", ptr->name, ptr->height, ptr->height, waste));
865                 }
866         }
867 #endif
868
869         ptr->total_frames = cfread_short(fp);
870         cfread(&ptr->packer_code, 1, 1, fp);
871         ptr->packer_code = INTEL_SHORT(ptr->packer_code);
872         cfread(&ptr->palette, 256, 3, fp);
873         ptr->num_keys = cfread_short(fp);
874
875         // store xparent colors
876         ptr->xparent_r = Color_xparent.red;
877         ptr->xparent_g = Color_xparent.green;
878         ptr->xparent_b = Color_xparent.blue;
879
880         if(ptr->total_frames == ptr->num_keys){
881                 ptr->flags |= ANF_ALL_KEYFRAMES;
882         }
883 }
884
885 // -----------------------------------------------------------------------------
886 //      anim_load()
887 //
888 // Load an animation.  This stores the compressed data, which instances
889 // of the animation can reference.  Must be free'ed later with anim_free()
890 //
891 // input:       name                            =>              filename of animation
892 //                              file_mapped             =>              boolean, whether to use memory-mapped file or not.
893 //                                                                                      Memory-mapped files will page in the animation from disk
894 //                                                                                      as it is needed, but performance is not as good
895 //
896 //      returns:        pointer to anim that is loaded  => sucess
897 //                              NULL                                                                            =>      failure
898 //
899 anim *anim_load(char *real_filename, int file_mapped)
900 {
901         anim                    *ptr;
902         CFILE                   *fp;
903         int                     count,idx;
904         char name[_MAX_PATH];
905
906 //      file_mapped = 0;
907
908         Assert ( real_filename != NULL );
909
910         strcpy( name, real_filename );
911         char *p = strchr( name, '.' );
912         if ( p ) {
913                 *p = 0;
914         }
915         strcat( name, ".ani" );
916
917         ptr = first_anim;
918         while (ptr) {
919                 if (!stricmp(name, ptr->name))
920                         break;
921
922                 ptr = ptr->next;
923         }
924
925         if (!ptr) {
926                 fp = cfopen(name, "rb");
927                 if ( !fp )
928                         return NULL;
929
930                 ptr = (anim *) malloc(sizeof(anim));
931                 Assert(ptr);
932
933                 ptr->flags = 0;
934                 ptr->next = first_anim;
935                 first_anim = ptr;
936                 Assert(strlen(name) < _MAX_PATH - 1);
937                 strcpy(ptr->name, name);
938                 ptr->instance_count = 0;
939                 ptr->width = 0;
940                 ptr->height = 0;
941                 ptr->total_frames = 0;
942                 ptr->keys = NULL;
943                 ptr->ref_count=0;
944
945                 anim_read_header(ptr, fp);
946
947                 if(ptr->num_keys > 0){
948                         ptr->keys = (key_frame*)malloc(sizeof(key_frame) * ptr->num_keys);
949                         Assert(ptr->keys != NULL);
950                 }                       
951
952                 // store how long the anim should take on playback (in seconds)
953                 ptr->time = i2fl(ptr->total_frames)/ptr->fps;
954
955                 for(idx=0;idx<ptr->num_keys;idx++){
956                         ptr->keys[idx].frame_num = 0;
957                         cfread(&ptr->keys[idx].frame_num, 2, 1, fp);
958                         cfread(&ptr->keys[idx].offset, 4, 1, fp);
959             ptr->keys[idx].frame_num = INTEL_INT( ptr->keys[idx].frame_num );
960             ptr->keys[idx].offset = INTEL_INT( ptr->keys[idx].offset );
961                 }
962
963                 /*prev_keyp = &ptr->keys;
964                 count = ptr->num_keys;
965                 while (count--) {
966                         keyp = (key_frame *) malloc(sizeof(key_frame));
967                         keyp->next = *prev_keyp;
968                         *prev_keyp = keyp;
969                         prev_keyp = &keyp->next;
970
971                         keyp->frame_num = 0;
972                         cfread(&keyp->frame_num, 2, 1, fp);
973                         cfread(&keyp->offset, 4, 1, fp);
974                 }*/
975                 cfread(&count, 4, 1, fp);       // size of compressed data
976         count = INTEL_INT( count );
977
978                 ptr->cfile_ptr = NULL;
979
980                 if ( file_mapped ) {
981                         // Try mapping the file to memory 
982                         ptr->flags |= ANF_MEM_MAPPED;
983                         ptr->cfile_ptr = cfopen(name, "rb", CFILE_MEMORY_MAPPED);
984                 }
985
986                 // couldn't memory-map file... must be in a packfile, so stream manually
987                 if ( file_mapped && !ptr->cfile_ptr ) {
988                         ptr->flags &= ~ANF_MEM_MAPPED;
989                         ptr->flags |= ANF_STREAMED;
990                         ptr->cfile_ptr = cfopen(name, "rb");
991                 }
992
993                 ptr->cache = NULL;
994
995                 // If it opened properly as mem-mapped (or streamed)
996                 if (ptr->cfile_ptr != NULL)     {
997                         // VERY IMPORTANT STEP
998                         // Set the data pointer to the compressed data (which is not at the start of the
999                         // file).  Use ftell() to find out how far we've already parsed into the file
1000                         //
1001                         int offset;
1002                         offset = cftell(fp);
1003                         ptr->file_offset = offset;
1004                         if ( ptr->flags & ANF_STREAMED ) {
1005                                 ptr->data = NULL;
1006                                 ptr->cache_file_offset = ptr->file_offset;
1007                                 ptr->cache = (ubyte*)malloc(ANI_STREAM_CACHE_SIZE+2);
1008                                 Assert(ptr->cache);
1009                                 cfseek(ptr->cfile_ptr, offset, CF_SEEK_SET);
1010                                 cfread(ptr->cache, ANI_STREAM_CACHE_SIZE, 1, ptr->cfile_ptr);
1011                         } else {
1012                                 ptr->data = (ubyte*)cf_returndata(ptr->cfile_ptr) + offset;
1013                         }
1014                 } else {
1015                         // Not a memory mapped file (or streamed)
1016                         ptr->flags &= ~ANF_MEM_MAPPED;
1017                         ptr->flags &= ~ANF_STREAMED;
1018                         ptr->data = (ubyte *) malloc(count);
1019                         ptr->file_offset = -1;
1020                         cfread(ptr->data, count, 1, fp);
1021                 }
1022
1023                 cfclose(fp);
1024
1025                 // store screen signature, so we can tell if palette changes
1026                 ptr->screen_sig = gr_screen.signature;
1027
1028                 anim_set_palette(ptr);
1029         }
1030
1031         ptr->ref_count++;
1032         return ptr;
1033 }
1034
1035 // ---------------------------------------------------
1036 //      anim_free()
1037 //
1038 // Free an animation that was loaded with anim_load().  All instances
1039 // referencing this animation must be free'ed or get an assert.
1040 //
1041 int anim_free(anim *ptr)
1042 {
1043         Assert ( ptr != NULL );
1044         anim *list, **prev_anim;
1045
1046         list = first_anim;
1047         prev_anim = &first_anim;
1048         while (list && (list != ptr)) {
1049                 prev_anim = &list->next;
1050                 list = list->next;
1051         }
1052
1053         if ( !list )
1054                 return -2;
1055
1056         // only free when ref_count is 0
1057         ptr->ref_count--;
1058         if ( ptr->ref_count > 0 ) 
1059                 return -1;
1060
1061         // only free if there are no playing instances
1062         if ( ptr->instance_count > 0 )
1063                 return -1;
1064
1065         if(ptr->keys != NULL){
1066                 free(ptr->keys);
1067                 ptr->keys = NULL;
1068         }
1069
1070         if ( ptr->flags & (ANF_MEM_MAPPED|ANF_STREAMED) ) {
1071                 cfclose(ptr->cfile_ptr);
1072                 if ( ptr->cache ) {
1073                         free(ptr->cache);
1074                 }
1075         }
1076         else {
1077                 Assert(ptr->data);
1078                 free(ptr->data);
1079         }
1080
1081         *prev_anim = ptr->next;
1082         free(ptr);
1083         return 0;
1084 }
1085
1086
1087 // ---------------------------------------------------------------------
1088 // anim_playing()
1089 //
1090 // Return if an anim is playing or not.  
1091 //
1092 int anim_playing(anim_instance *ai)
1093 {
1094         Assert(ai != NULL);
1095         if ( ai->frame == NULL )
1096                 return 0;
1097         else 
1098                 return 1;
1099 }
1100
1101
1102 // ---------------------------------------------------------------------
1103 // anim_level_init()
1104 //
1105 // Called at the beginning of a mission to initialize any mission dependent
1106 // anim data.
1107 //
1108 void anim_level_init()
1109 {
1110 }
1111
1112 // ---------------------------------------------------------------------
1113 // anim_level_close()
1114 //
1115 // Called after the end of a mission to clean up any mission dependent 
1116 // anim data. 
1117 //
1118 void anim_level_close()
1119 {
1120         anim_release_all_instances();
1121 }
1122
1123 // ---------------------------------------------------
1124 //      anim_write_frames_out()
1125 //
1126 //      Write the frames of a .ani file out to disk as .pcx files.
1127 // Use naming convention: filename0000.pcx, filename0001.pcx etc.
1128 //
1129 // return:              0       =>              success
1130 //                                      -1      =>              failed
1131 //
1132 int anim_write_frames_out(char *filename)
1133 {
1134         anim                            *source_anim;
1135         anim_instance   *ai;
1136         char                            root_name[256], pcxname[256];
1137         char                            buf[64];
1138         int                             i,j;
1139         ubyte                           **row_data;
1140
1141         strcpy(root_name, filename);
1142         root_name[strlen(filename)-4] = 0;
1143
1144         source_anim = anim_load(filename);
1145         if ( source_anim == NULL ) 
1146                 return -1;
1147
1148         ai = init_anim_instance(source_anim, 16);
1149
1150         row_data = (ubyte**)malloc((source_anim->height+1) * 4);
1151
1152         for ( i = 0; i < source_anim->total_frames; i++ ) {
1153                 anim_get_next_raw_buffer(ai, 0, 0, 16);
1154                 strcpy(pcxname, root_name);
1155                 sprintf(buf,"%04d",i);
1156                 strcat(pcxname, buf);
1157
1158                 for ( j = 0; j < source_anim->height; j++ ) {
1159                         row_data[j] = &ai->frame[j*source_anim->width];
1160                 }
1161
1162
1163                 pcx_write_bitmap( pcxname,
1164                                                                 source_anim->width,
1165                                                                 source_anim->height,
1166                                                                 row_data,
1167                                                                 source_anim->palette);
1168
1169                 printf(".");
1170
1171         }
1172         printf("\n");
1173         free(row_data);
1174         return 0;
1175 }
1176
1177 // ---------------------------------------------------
1178 //      anim_display_info()
1179 //
1180 //      Display information and statistics about a .ani file.
1181 //      This is called when -i switch is on when running ac.exe
1182 //
1183 void anim_display_info(char *real_filename)
1184 {
1185         CFILE                           *fp;
1186         anim                            A;
1187         float                           percent;
1188         int                             i, uncompressed, compressed, *key_frame_nums=NULL, tmp;
1189         char filename[MAX_FILENAME_LEN];
1190
1191         strcpy( filename, real_filename );
1192         char *p = strchr( filename, '.' );
1193         if ( p ) {
1194                 *p = 0;
1195         }
1196         strcat( filename, ".ani" );
1197
1198         fp = cfopen(filename, "rb");
1199         if ( !fp ) {
1200                 printf("Fatal error opening %s", filename);
1201                 return;
1202         }
1203
1204         anim_read_header(&A, fp);
1205         // read the keyframe frame nums and offsets
1206         key_frame_nums = (int*)malloc(sizeof(int)*A.num_keys);
1207         Assert(key_frame_nums != NULL);
1208         for ( i = 0; i < A.num_keys; i++ ) {
1209                 key_frame_nums[i] = 0;
1210                 cfread(&key_frame_nums[i], 2, 1, fp);
1211                 cfread(&tmp, 4, 1, fp);
1212 //printf("key frame num: %d,%d\n", key_frame_nums[i],tmp);
1213         }
1214
1215         cfread(&compressed, 4, 1, fp);
1216
1217         uncompressed = A.width * A.height * A.total_frames;     // 8 bits per pixel
1218         percent = i2fl(compressed) / uncompressed * 100.0f;
1219
1220         printf("%% of uncompressed size:    %.0f%%\n", percent);
1221         printf("Width:                     %d\n", A.width);
1222         printf("Height:                    %d\n", A.height);
1223         printf("Total Frames:              %d\n", A.total_frames);
1224
1225 #ifndef NDEBUG
1226         printf("Key Frames:                %d\n", A.num_keys);
1227         if ( A.num_keys > 1 && (A.total_frames != A.num_keys) ) {
1228                 printf("key list: (");
1229                 for ( i = 0; i < A.num_keys; i++ ) {
1230                         if ( i < A.num_keys-1 ) 
1231                                 printf("%d, ", key_frame_nums[i]);
1232                         else
1233                                 printf("%d)\n", key_frame_nums[i]);
1234                 }
1235         }
1236 #endif
1237
1238         printf("FPS:                       %d\n", A.fps);
1239
1240 #ifndef NDEBUG
1241         printf("Transparent RGB:           (%d,%d,%d)\n", A.xparent_r, A.xparent_g, A.xparent_b); 
1242 #endif
1243
1244         printf("ac version:                %d\n", A.version);
1245
1246         if ( key_frame_nums != NULL ) {
1247                 free(key_frame_nums);
1248         }
1249
1250         cfclose(fp);
1251 }
1252
1253 void anim_reverse_direction(anim_instance *ai)
1254 {
1255         int temp;
1256
1257         if(!(ai->parent->flags & ANF_ALL_KEYFRAMES)){
1258                  // you're not allowed to call anim_reverse_direction(...) unless every frame is a keyframe!!!!
1259                  // The God of Delta-RLE demands it be thus.
1260                 Int3();
1261         }
1262                 
1263         // flip the animation direction
1264         if(ai->direction == ANIM_DIRECT_FORWARD){
1265                 ai->direction = ANIM_DIRECT_REVERSE;                            
1266         } else if(ai->direction == ANIM_DIRECT_REVERSE){
1267                 ai->direction = ANIM_DIRECT_FORWARD;
1268         }
1269
1270         // flip frame_num and last_frame_num
1271         temp = ai->frame_num;
1272         ai->frame_num = ai->last_frame_num;
1273         ai->last_frame_num = temp;
1274
1275         // flip the start and stop at frames
1276         temp = ai->stop_at;
1277         ai->stop_at = ai->start_at;
1278         ai->start_at = temp;
1279
1280         // make sure to sync up the time correctly 
1281         if(ai->direction == ANIM_DIRECT_FORWARD){
1282                 ai->time_elapsed = ((float)ai->frame_num - (float)ai->start_at - 1.0f) / (float)ai->parent->fps;        
1283         } else if(ai->direction == ANIM_DIRECT_REVERSE) {
1284                 ai->time_elapsed = ((float)ai->start_at - (float)ai->frame_num - 1.0f) / (float)ai->parent->fps;        
1285         }       
1286 }
1287
1288 void anim_pause(anim_instance *ai)
1289 {
1290         ai->paused = 1;
1291 }
1292
1293 void anim_unpause(anim_instance *ai)
1294 {
1295         ai->paused = 0;
1296 }
1297
1298 void anim_ignore_next_frametime()
1299 {
1300         Anim_ignore_frametime=1;
1301 }
1302
1303 int anim_instance_is_streamed(anim_instance *ai)
1304 {
1305         Assert(ai);
1306         return ( ai->parent->flags & ANF_STREAMED );
1307 }
1308
1309 unsigned char anim_instance_get_byte(anim_instance *ai, int offset)
1310 {
1311         int absolute_offset;
1312         anim *parent;
1313         
1314         Assert(ai);
1315         Assert(ai->parent->cfile_ptr);
1316         Assert(ai->parent->flags & ANF_STREAMED);
1317
1318         parent = ai->parent;
1319         absolute_offset = ai->file_offset + offset;
1320
1321         // maybe in cache?
1322         int cache_offset;
1323         cache_offset = absolute_offset - parent->cache_file_offset;
1324         if ( (cache_offset >= 0) && (cache_offset < ANI_STREAM_CACHE_SIZE) ) {
1325                 return parent->cache[cache_offset];
1326         } else {
1327                 // fill cache
1328                 cfseek(parent->cfile_ptr, absolute_offset, CF_SEEK_SET);
1329                 cfread(parent->cache, ANI_STREAM_CACHE_SIZE, 1, parent->cfile_ptr);
1330                 parent->cache_file_offset = absolute_offset;
1331                 return parent->cache[0];
1332         }
1333 }
1334