1 #include "globalincs/pstypes.h"
2 #include "globalincs/globals.h"
3 #include "bmpman/bmpman.h"
4 #include "graphics/generic.h"
5 #include "graphics/2d.h"
6 #include "anim/animplay.h"
7 #include "anim/packunpack.h"
8 #define BMPMAN_INTERNAL
9 #include "bmpman/bm_internal.h"
10 #ifdef _WIN32
11 #include <windows.h> // for MAX_PATH
12 #else
13 #define MAX_PATH 255
14 #endif
15 //#define TIMER
16 #ifdef TIMER
17 #include "io/timer.h"
18 #endif
19
20 //we check background type to avoid messed up colours for ANI
21 #define ANI_BPP_CHECK (ga->ani.bg_type == BM_TYPE_PCX) ? 16 : 32
22
23 // Goober5000
generic_anim_init_and_stream(generic_anim * anim,const char * anim_filename,ubyte bg_type,bool attempt_hi_res)24 int generic_anim_init_and_stream(generic_anim *anim, const char *anim_filename, ubyte bg_type, bool attempt_hi_res)
25 {
26 int stream_result = -1;
27 char filename[NAME_LENGTH];
28 char *p;
29
30 Assert(anim != NULL);
31 Assert(anim_filename != NULL);
32
33 // hi-res support
34 if (attempt_hi_res && (gr_screen.res == GR_1024)) {
35 // attempt to load a hi-res animation
36 memset(filename, 0, NAME_LENGTH);
37 strcpy_s(filename, "2_");
38 strncat(filename, anim_filename, NAME_LENGTH - 3);
39
40 // remove extension
41 p = strchr(filename, '.');
42 if(p) {
43 *p = '\0';
44 }
45
46 // attempt to stream the hi-res ani
47 generic_anim_init(anim, filename);
48 anim->ani.bg_type = bg_type;
49 stream_result = generic_anim_stream(anim);
50 }
51
52 // we failed to stream hi-res, or we aren't running in hi-res, so try low-res
53 if (stream_result < 0) {
54 strcpy_s(filename, anim_filename);
55
56 // remove extension
57 p = strchr(filename, '.');
58 if(p) {
59 *p = '\0';
60 }
61
62 // attempt to stream the low-res ani
63 generic_anim_init(anim, filename);
64 anim->ani.bg_type = bg_type;
65 stream_result = generic_anim_stream(anim);
66 }
67
68 return stream_result;
69 }
70
71 // Goober5000
generic_anim_init(generic_anim * ga)72 void generic_anim_init(generic_anim *ga)
73 {
74 generic_anim_init(ga, NULL);
75 }
76
77 // Goober5000
generic_anim_init(generic_anim * ga,const char * filename)78 void generic_anim_init(generic_anim *ga, const char *filename)
79 {
80 if (filename != NULL)
81 strcpy_s(ga->filename, filename);
82 else
83 memset(ga->filename, 0, MAX_FILENAME_LEN);
84 ga->first_frame = -1;
85 ga->num_frames = 0;
86 ga->keyframe = 0;
87 ga->keyoffset = 0;
88 ga->current_frame = 0;
89 ga->previous_frame = -1;
90 ga->direction = GENERIC_ANIM_DIRECTION_FORWARDS;
91 ga->done_playing = 0;
92 ga->total_time = 0.0f;
93 ga->anim_time = 0.0f;
94
95 //we only care about the stuff below if we're streaming
96 ga->ani.animation = NULL;
97 ga->ani.instance = NULL;
98 ga->ani.bg_type = 0;
99 ga->type = BM_TYPE_NONE;
100 ga->streaming = 0;
101 ga->buffer = NULL;
102 ga->height = 0;
103 ga->width = 0;
104 ga->bitmap_id = -1;
105 ga->use_hud_color = false;
106 }
107
108 // CommanderDJ - same as generic_anim_init, just with an SCP_string
generic_anim_init(generic_anim * ga,const SCP_string & filename)109 void generic_anim_init(generic_anim *ga, const SCP_string& filename)
110 {
111 generic_anim_init(ga);
112 filename.copy(ga->filename, MAX_FILENAME_LEN - 1);
113 }
114
115 // Goober5000
generic_bitmap_init(generic_bitmap * gb,const char * filename)116 void generic_bitmap_init(generic_bitmap *gb, const char *filename)
117 {
118 if (filename == NULL) {
119 gb->filename[0] = '\0';
120 } else {
121 strncpy(gb->filename, filename, MAX_FILENAME_LEN - 1);
122 }
123
124 gb->bitmap_id = -1;
125 }
126
127 // Goober5000
128 // load a generic_anim
129 // return 0 is successful, otherwise return -1
generic_anim_load(generic_anim * ga)130 int generic_anim_load(generic_anim *ga)
131 {
132 int fps;
133
134 if ( !VALID_FNAME(ga->filename) )
135 return -1;
136
137 ga->first_frame = bm_load_animation(ga->filename, &ga->num_frames, &fps, &ga->keyframe);
138 //mprintf(("generic_anim_load: %s - keyframe = %d\n", ga->filename, ga->keyframe));
139
140 if (ga->first_frame < 0)
141 return -1;
142
143 Assert(fps != 0);
144 ga->total_time = ga->num_frames / (float)fps;
145 ga->done_playing = 0;
146 ga->anim_time = 0.0f;
147
148 return 0;
149 }
150
generic_anim_stream(generic_anim * ga)151 int generic_anim_stream(generic_anim *ga)
152 {
153 CFILE *img_cfp = NULL;
154 int anim_fps = 0;
155 char full_path[MAX_PATH];
156 int size = 0, offset = 0;
157 const int NUM_TYPES = 2;
158 const ubyte type_list[NUM_TYPES] = {BM_TYPE_EFF, BM_TYPE_ANI};
159 const char *ext_list[NUM_TYPES] = {".eff", ".ani"};
160 int rval = -1;
161 int bpp;
162
163 ga->type = BM_TYPE_NONE;
164
165 rval = cf_find_file_location_ext(ga->filename, NUM_TYPES, ext_list, CF_TYPE_ANY, sizeof(full_path) - 1, full_path, &size, &offset, 0);
166
167 // could not be found, or is invalid for some reason
168 if ( (rval < 0) || (rval >= NUM_TYPES) )
169 return -1;
170
171 //make sure we can open it
172 img_cfp = cfopen_special(full_path, "rb", size, offset, CF_TYPE_ANY);
173
174 if (img_cfp == NULL) {
175 return -1;
176 }
177
178 strcat_s(ga->filename, ext_list[rval]);
179 ga->type = type_list[rval];
180 //seek to the end
181 cfseek(img_cfp, 0, CF_SEEK_END);
182
183 cfclose(img_cfp);
184
185 //TODO: add streaming EFF
186 if(ga->type == BM_TYPE_ANI) {
187 bpp = ANI_BPP_CHECK;
188 if(ga->use_hud_color)
189 bpp = 8;
190 ga->ani.animation = anim_load(ga->filename, CF_TYPE_ANY, 0);
191 ga->ani.instance = init_anim_instance(ga->ani.animation, bpp);
192
193 #ifndef NDEBUG
194 // for debug of ANI sizes
195 strcpy_s(ga->ani.animation->name, ga->filename);
196 #endif
197
198 ga->num_frames = ga->ani.animation->total_frames;
199 anim_fps = ga->ani.animation->fps;
200 ga->height = ga->ani.animation->height;
201 ga->width = ga->ani.animation->width;
202 ga->buffer = ga->ani.instance->frame;
203 ga->bitmap_id = bm_create(bpp, ga->width, ga->height, ga->buffer, (bpp==8)?BMP_AABITMAP:0);
204 ga->ani.instance->last_bitmap = -1;
205
206 ga->ani.instance->file_offset = ga->ani.animation->file_offset;
207 ga->ani.instance->data = ga->ani.animation->data;
208
209 ga->previous_frame = -1;
210 }
211 else {
212 bpp = 32;
213 if(ga->use_hud_color)
214 bpp = 8;
215 bm_load_and_parse_eff(ga->filename, CF_TYPE_ANY, &ga->num_frames, &anim_fps, &ga->keyframe, 0);
216 char *p = strrchr( ga->filename, '.' );
217 if ( p )
218 *p = 0;
219 char frame_name[32];
220 snprintf(frame_name, 32, "%s_0000", ga->filename);
221 ga->bitmap_id = bm_load(frame_name);
222 if(ga->bitmap_id < 0) {
223 mprintf(("Cannot find first frame for eff streaming. eff Filename: %s", ga->filename));
224 return -1;
225 }
226 snprintf(frame_name, 32, "%s_0001", ga->filename);
227 ga->eff.next_frame = bm_load(frame_name);
228 bm_get_info(ga->bitmap_id, &ga->width, &ga->height);
229 ga->previous_frame = 0;
230 }
231
232 // keyframe info
233 if (ga->type == BM_TYPE_ANI) {
234 //we only care if there are 2 keyframes - first frame, other frame to jump to for ship/weapons
235 //mainhall door anis hav every frame as keyframe, so we don't care
236 //other anis only have the first frame
237 if(ga->ani.animation->num_keys == 2) {
238 int key1 = ga->ani.animation->keys[0].frame_num;
239 int key2 = ga->ani.animation->keys[1].frame_num;
240
241 if (key1 < 0 || key1 >= ga->num_frames) key1 = -1;
242 if (key2 < 0 || key2 >= ga->num_frames) key2 = -1;
243
244 // some retail anis have their keyframes reversed
245 // and some have their keyframes out of bounds
246 if (key1 >= 0 && key1 >= key2) {
247 ga->keyframe = ga->ani.animation->keys[0].frame_num;
248 ga->keyoffset = ga->ani.animation->keys[0].offset;
249 }
250 else if (key2 >= 0 && key2 >= key1) {
251 ga->keyframe = ga->ani.animation->keys[1].frame_num;
252 ga->keyoffset = ga->ani.animation->keys[1].offset;
253 }
254 }
255 }
256
257 ga->streaming = 1;
258
259 Assert(anim_fps != 0);
260 ga->total_time = ga->num_frames / (float) anim_fps;
261 ga->done_playing = 0;
262 ga->anim_time = 0.0f;
263
264 return 0;
265 }
266
generic_bitmap_load(generic_bitmap * gb)267 int generic_bitmap_load(generic_bitmap *gb)
268 {
269 if ( !VALID_FNAME(gb->filename) )
270 return -1;
271
272 gb->bitmap_id = bm_load(gb->filename);
273
274 if (gb->bitmap_id < 0)
275 return -1;
276
277 return 0;
278 }
279
generic_anim_unload(generic_anim * ga)280 void generic_anim_unload(generic_anim *ga)
281 {
282 if(ga->num_frames > 0) {
283 if(ga->streaming) {
284 if(ga->type == BM_TYPE_ANI) {
285 anim_free(ga->ani.animation);
286 free_anim_instance(ga->ani.instance);
287 }
288 if(ga->type == BM_TYPE_EFF) {
289 if(ga->eff.next_frame >= 0)
290 bm_release(ga->eff.next_frame);
291 if(ga->bitmap_id >= 0)
292 bm_release(ga->bitmap_id);
293 }
294 }
295 else {
296 //trying to release the first frame will release ALL frames
297 bm_release(ga->first_frame);
298 }
299 if(ga->buffer) {
300 bm_release(ga->bitmap_id);
301 }
302 }
303 generic_anim_init(ga, NULL);
304 }
305
306 //for timer debug, #define TIMER
generic_render_eff_stream(generic_anim * ga)307 void generic_render_eff_stream(generic_anim *ga)
308 {
309 if(ga->current_frame == ga->previous_frame)
310 return;
311 ubyte bpp = 32;
312 if(ga->use_hud_color)
313 bpp = 8;
314 #ifdef TIMER
315 int start_time = timer_get_fixed_seconds();
316 #endif
317
318 #ifdef TIMER
319 mprintf(("=========================\n"));
320 mprintf(("frame: %d\n", ga->current_frame));
321 #endif
322 char frame_name[32];
323 snprintf(frame_name, 32, "%s_%.4d", ga->filename, ga->current_frame);
324 if(bm_reload(ga->eff.next_frame, frame_name) == ga->eff.next_frame)
325 {
326 bitmap* next_frame_bmp = bm_lock(ga->eff.next_frame, bpp, (bpp==8)?BMP_AABITMAP:BMP_TEX_NONCOMP, true);
327 if(next_frame_bmp->data)
328 gr_update_texture(ga->bitmap_id, bpp, (ubyte*)next_frame_bmp->data, ga->width, ga->height);
329 bm_unlock(ga->eff.next_frame);
330 bm_unload(ga->eff.next_frame, 0, true);
331 }
332 #ifdef TIMER
333 mprintf(("end: %d\n", timer_get_fixed_seconds() - start_time));
334 mprintf(("=========================\n"));
335 #endif
336 }
337
generic_render_ani_stream(generic_anim * ga)338 void generic_render_ani_stream(generic_anim *ga)
339 {
340 int i;
341 int bpp = ANI_BPP_CHECK;
342 if(ga->use_hud_color)
343 bpp = 8;
344 #ifdef TIMER
345 int start_time = timer_get_fixed_seconds();
346 #endif
347
348 if(ga->current_frame == ga->previous_frame)
349 return;
350
351 #ifdef TIMER
352 mprintf(("=========================\n"));
353 mprintf(("frame: %d\n", ga->current_frame));
354 #endif
355
356 anim_check_for_palette_change(ga->ani.instance);
357 // if we're using bitmap polys
358 BM_SELECT_TEX_FORMAT();
359 if(ga->direction & GENERIC_ANIM_DIRECTION_BACKWARDS) {
360 //grab the keyframe - every frame is a keyframe for ANI
361 if(ga->ani.animation->flags & ANF_STREAMED) {
362 ga->ani.instance->file_offset = ga->ani.animation->file_offset + ga->ani.animation->keys[ga->current_frame].offset;
363 } else {
364 ga->ani.instance->data = ga->ani.animation->data + ga->ani.animation->keys[ga->current_frame].offset;
365 }
366 if(ga->ani.animation->flags & ANF_STREAMED) {
367 ga->ani.instance->file_offset = unpack_frame_from_file(ga->ani.instance, ga->buffer, ga->width * ga->height, (ga->ani.instance->xlate_pal) ? ga->ani.animation->palette_translation : NULL, (bpp==8)?1:0, bpp);
368 }
369 else {
370 ga->ani.instance->data = unpack_frame(ga->ani.instance, ga->ani.instance->data, ga->buffer, ga->width * ga->height, (ga->ani.instance->xlate_pal) ? ga->ani.animation->palette_translation : NULL, (bpp==8)?1:0, bpp);
371 }
372 }
373 else {
374 //looping back
375 if((ga->current_frame == 0) || (ga->current_frame < ga->previous_frame)) {
376 //go back to keyframe if there is one
377 if(ga->keyframe && (ga->current_frame > 0)) {
378 if(ga->ani.animation->flags & ANF_STREAMED) {
379 ga->ani.instance->file_offset = ga->ani.animation->file_offset + ga->keyoffset;
380 } else {
381 ga->ani.instance->data = ga->ani.animation->data + ga->keyoffset;
382 }
383 ga->previous_frame = ga->keyframe - 1;
384 }
385 //go back to the start
386 else {
387 ga->ani.instance->file_offset = ga->ani.animation->file_offset;
388 ga->ani.instance->data = ga->ani.animation->data;
389 ga->previous_frame = -1;
390 }
391 }
392 #ifdef TIMER
393 mprintf(("proc: %d\n", timer_get_fixed_seconds() - start_time));
394 mprintf(("previous frame: %d\n", ga->previous_frame));
395 #endif
396 for(i = ga->previous_frame + 1; i <= ga->current_frame; i++) {
397 if(ga->ani.animation->flags & ANF_STREAMED) {
398 ga->ani.instance->file_offset = unpack_frame_from_file(ga->ani.instance, ga->buffer, ga->width * ga->height, (ga->ani.instance->xlate_pal) ? ga->ani.animation->palette_translation : NULL, (bpp==8)?1:0, bpp);
399 }
400 else {
401 ga->ani.instance->data = unpack_frame(ga->ani.instance, ga->ani.instance->data, ga->buffer, ga->width * ga->height, (ga->ani.instance->xlate_pal) ? ga->ani.animation->palette_translation : NULL, (bpp==8)?1:0, bpp);
402 }
403 }
404 }
405 // always go back to screen format
406 BM_SELECT_SCREEN_FORMAT();
407 //we need to use this because performance is worse if we flush the gfx card buffer
408
409 gr_update_texture(ga->bitmap_id, bpp, ga->buffer, ga->width, ga->height);
410
411 //in case we want to check that the frame is actually changing
412 //mprintf(("frame crc = %08X\n", cf_add_chksum_long(0, ga->buffer, ga->width * ga->height * (bpp >> 3))));
413 ga->ani.instance->last_bitmap = ga->bitmap_id;
414
415 #ifdef TIMER
416 mprintf(("end: %d\n", timer_get_fixed_seconds() - start_time));
417 mprintf(("=========================\n"));
418 #endif
419 }
420
generic_anim_render(generic_anim * ga,float frametime,int x,int y,bool menu)421 void generic_anim_render(generic_anim *ga, float frametime, int x, int y, bool menu)
422 {
423 float keytime = 0.0;
424
425 if(ga->keyframe)
426 keytime = (ga->total_time * ((float)ga->keyframe / (float)ga->num_frames));
427 //don't mess with the frame time if we're paused
428 if((ga->direction & GENERIC_ANIM_DIRECTION_PAUSED) == 0) {
429 if(ga->direction & GENERIC_ANIM_DIRECTION_BACKWARDS) {
430 //keep going forwards if we're in a keyframe loop
431 if(ga->keyframe && (ga->anim_time >= keytime)) {
432 ga->anim_time += frametime;
433 if(ga->anim_time >= ga->total_time) {
434 ga->anim_time = keytime - 0.001f;
435 ga->done_playing = 0;
436 }
437 }
438 else {
439 //playing backwards
440 ga->anim_time -= frametime;
441 if((ga->direction & GENERIC_ANIM_DIRECTION_NOLOOP) && ga->anim_time <= 0.0) {
442 ga->anim_time = 0; //stop on first frame when playing in reverse
443 }
444 else {
445 while(ga->anim_time <= 0.0)
446 ga->anim_time += ga->total_time; //make sure we're always positive, so we can go back to the end
447 }
448 }
449 }
450 else {
451 ga->anim_time += frametime;
452 if(ga->anim_time >= ga->total_time) {
453 if(ga->direction & GENERIC_ANIM_DIRECTION_NOLOOP) {
454 ga->anim_time = ga->total_time - 0.001f; //stop on last frame when playing - if it's equal we jump to the first frame
455 }
456 if(!ga->done_playing){
457 //we've played this at least once
458 ga->done_playing = 1;
459 }
460 }
461 }
462 }
463 if(ga->num_frames > 0)
464 {
465 ga->current_frame = 0;
466 if(ga->done_playing && ga->keyframe) {
467 ga->anim_time = fmod(ga->anim_time - keytime, ga->total_time - keytime) + keytime;
468 }
469 else {
470 ga->anim_time = fmod(ga->anim_time, ga->total_time);
471 }
472 ga->current_frame += fl2i(ga->anim_time * ga->num_frames / ga->total_time);
473 //sanity check
474 CLAMP(ga->current_frame, 0, ga->num_frames - 1);
475 if(ga->streaming) {
476 //handle streaming - render one frame
477 //TODO: add EFF streaming
478 if(ga->type == BM_TYPE_ANI) {
479 generic_render_ani_stream(ga);
480 } else {
481 generic_render_eff_stream(ga);
482 }
483 gr_set_bitmap(ga->bitmap_id);
484 }
485 else {
486 gr_set_bitmap(ga->first_frame + ga->current_frame);
487 }
488 ga->previous_frame = ga->current_frame;
489 if(ga->use_hud_color)
490 gr_aabitmap(x, y, (menu ? GR_RESIZE_MENU : GR_RESIZE_FULL));
491 else
492 gr_bitmap(x, y, (menu ? GR_RESIZE_MENU : GR_RESIZE_FULL));
493 }
494 }
495