chiark / gitweb /
Initial revision
[newkind] / alg_gfx.c
1 /**
2  *
3  * Elite - The New Kind.
4  *
5  * Allegro version of Graphics routines.
6  *
7  * The code in this file has not been derived from the original Elite code.
8  * Written by C.J.Pinder 1999-2001.
9  * email: <christian@newkind.co.uk>
10  *
11  * Routines for drawing anti-aliased lines and circles by T.Harte.
12  *
13  **/
14
15 #include <string.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <math.h>
19 #include <ctype.h>
20
21 #include "allegro.h"
22
23 #include "config.h"
24 #include "gfx.h"
25 #include "alg_data.h"
26 #include "elite.h"
27
28 BITMAP *gfx_screen;
29 volatile int frame_count;
30 DATAFILE *datafile;
31 BITMAP *scanner_image;
32
33 #define MAX_POLYS       100
34
35 static int start_poly;
36 static int total_polys;
37
38 struct poly_data
39 {
40         int z;
41         int no_points;
42         int face_colour;
43         int point_list[16];
44         int next;
45 };
46
47 static struct poly_data poly_chain[MAX_POLYS];
48
49
50
51
52 void frame_timer (void)
53 {
54         frame_count++;
55 }
56 END_OF_FUNCTION(frame_timer);
57
58
59
60 int gfx_graphics_startup (void)
61 {
62         PALETTE the_palette;
63         int rv;
64
65 #ifdef ALLEGRO_WINDOWS  
66
67 #ifdef RES_512_512
68         rv = set_gfx_mode(GFX_DIRECTX_OVL, 512, 512, 0, 0);
69         
70         if (rv != 0)
71                 rv = set_gfx_mode(GFX_DIRECTX_WIN, 512, 512, 0, 0);
72
73         if (rv != 0)
74                 rv = set_gfx_mode(GFX_GDI, 512, 512, 0, 0);
75
76         if (rv == 0)
77                 set_display_switch_mode (SWITCH_BACKGROUND);
78 #else
79         rv = set_gfx_mode(GFX_DIRECTX, 800, 600, 0, 0);
80         
81         if (rv != 0)
82                 rv = set_gfx_mode(GFX_GDI, 800, 600, 0, 0);
83 #endif
84
85 #else
86         rv = set_gfx_mode(GFX_AUTODETECT, 800, 600, 0, 0);
87 #endif
88
89         if (rv != 0)
90         {
91                 set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
92         allegro_message("Unable to set graphics mode.");
93         return 1;
94         }
95         
96         datafile = load_datafile("elite.dat");
97         if (!datafile)
98         {
99                 set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
100         allegro_message("Error loading %s!\n", "elite.dat");
101         return 1;
102         }
103
104         scanner_image = load_bitmap(scanner_filename, the_palette);
105         if (!scanner_image)
106         {
107                 set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
108                 allegro_message("Error reading scanner bitmap file.\n");
109                 return 1;
110         }
111
112         /* select the scanner palette */
113         set_palette(the_palette);
114
115         /* Create the screen buffer bitmap */
116         gfx_screen = create_bitmap (SCREEN_W, SCREEN_H);
117
118         clear (gfx_screen);
119
120         blit (scanner_image, gfx_screen, 0, 0, GFX_X_OFFSET, 385+GFX_Y_OFFSET, scanner_image->w, scanner_image->h);
121         gfx_draw_line (0, 0, 0, 384);
122         gfx_draw_line (0, 0, 511, 0);
123         gfx_draw_line (511, 0, 511, 384);
124
125         /* Install a timer to regulate the speed of the game... */
126
127         LOCK_VARIABLE(frame_count);
128         LOCK_FUNCTION(frame_timer);
129         frame_count = 0;
130         install_int (frame_timer, speed_cap);
131         
132         return 0;
133 }
134
135
136 void gfx_graphics_shutdown (void)
137 {
138         destroy_bitmap(scanner_image);
139         destroy_bitmap(gfx_screen);
140         unload_datafile(datafile);
141 }
142
143
144 /*
145  * Blit the back buffer to the screen.
146  */
147
148 void gfx_update_screen (void)
149 {
150         while (frame_count < 1)
151                 rest (10);
152         frame_count = 0;
153         
154         acquire_screen();
155         blit (gfx_screen, screen, GFX_X_OFFSET, GFX_Y_OFFSET, GFX_X_OFFSET, GFX_Y_OFFSET, 512, 512);
156         release_screen();
157 }
158
159
160 void gfx_acquire_screen (void)
161 {
162         acquire_bitmap (gfx_screen);
163 }
164
165
166 void gfx_release_screen (void)
167 {
168         release_bitmap(gfx_screen);
169 }
170
171 void gfx_fast_plot_pixel (int x, int y, int col)
172 {
173 //      _putpixel(gfx_screen, x, y, col);
174         gfx_screen->line[y][x] = col;
175 }
176
177
178 void gfx_plot_pixel (int x, int y, int col)
179 {
180         putpixel (gfx_screen, x + GFX_X_OFFSET, y + GFX_Y_OFFSET, col);
181 }
182
183
184 void gfx_draw_filled_circle (int cx, int cy, int radius, int circle_colour)
185 {
186         circlefill (gfx_screen, cx + GFX_X_OFFSET, cy + GFX_Y_OFFSET, radius, circle_colour);
187 }
188
189
190 #define AA_BITS 3
191 #define AA_AND  7
192 #define AA_BASE 235
193
194 #define trunc(x) ((x) & ~65535)
195 #define frac(x) ((x) & 65535)
196 #define invfrac(x) (65535-frac(x))
197 #define plot(x,y,c) putpixel(gfx_screen, (x), (y), (c)+AA_BASE)
198
199 /*
200  * Draw anti-aliased wireframe circle.
201  * By T.Harte.
202  */
203
204 void gfx_draw_aa_circle(int cx, int cy, int radius)
205 {
206         int x,y;
207         int s;
208         int sx, sy;
209
210         cx += GFX_X_OFFSET;
211         cy += GFX_Y_OFFSET;
212
213         radius >>= (16 - AA_BITS);
214
215         x = radius;
216         s = -radius;
217         y = 0;
218
219         while (y <= x)
220         {
221                 //wide pixels
222                 sx = cx + (x >> AA_BITS); sy = cy + (y >> AA_BITS);
223
224                 plot(sx,        sy,     AA_AND - (x&AA_AND));
225                 plot(sx + 1,    sy,     x&AA_AND);
226
227                 sy = cy - (y >> AA_BITS);
228
229                 plot(sx,        sy,     AA_AND - (x&AA_AND));
230                 plot(sx + 1,    sy,     x&AA_AND);
231
232                 sx = cx - (x >> AA_BITS);
233
234                 plot(sx,        sy,     AA_AND - (x&AA_AND));
235                 plot(sx - 1,    sy,     x&AA_AND);
236
237                 sy = cy + (y >> AA_BITS);
238
239                 plot(sx,        sy,     AA_AND - (x&AA_AND));
240                 plot(sx - 1,    sy,     x&AA_AND);
241
242                 //tall pixels
243                 sx = cx + (y >> AA_BITS); sy = cy + (x >> AA_BITS);
244
245                 plot(sx,        sy,     AA_AND - (x&AA_AND));
246                 plot(sx,        sy + 1, x&AA_AND);
247
248                 sy = cy - (x >> AA_BITS);
249
250                 plot(sx,        sy,     AA_AND - (x&AA_AND));
251                 plot(sx,        sy - 1, x&AA_AND);
252
253                 sx = cx - (y >> AA_BITS);
254
255                 plot(sx,        sy,     AA_AND - (x&AA_AND));
256                 plot(sx,        sy - 1, x&AA_AND);
257
258                 sy = cy + (x >> AA_BITS);
259
260                 plot(sx,        sy,     AA_AND - (x&AA_AND));
261                 plot(sx,        sy + 1, x&AA_AND);
262
263                 s +=    AA_AND+1 + (y << (AA_BITS+1)) + ((1 << (AA_BITS+2))-2);
264                 y +=    AA_AND+1;
265
266                 while(s >= 0)
267                 {
268                         s -= (x << 1) + 2;
269                         x --;
270                 }
271         }
272 }
273
274
275 /*
276  * Draw anti-aliased line.
277  * By T.Harte.
278  */
279  
280 void gfx_draw_aa_line (int x1, int y1, int x2, int y2)
281 {
282         fixed grad, xd, yd;
283         fixed xgap, ygap, xend, yend, xf, yf;
284         fixed brightness1, brightness2, swap;
285
286         int x, y, ix1, ix2, iy1, iy2;
287
288         x1 += itofix(GFX_X_OFFSET);
289         x2 += itofix(GFX_X_OFFSET);
290         y1 += itofix(GFX_Y_OFFSET);
291         y2 += itofix(GFX_Y_OFFSET);
292
293         xd = x2 - x1;
294         yd = y2 - y1;
295
296         if (abs(xd) > abs(yd))
297         {
298                 if(x1 > x2)
299                 {
300                         swap = x1; x1 = x2; x2 = swap;
301                         swap = y1; y1 = y2; y2 = swap;
302                         xd   = -xd;
303                         yd   = -yd;
304                 }
305
306                 grad = fdiv(yd, xd);
307
308                 //end point 1
309
310                 xend = trunc(x1 + 32768);
311                 yend = y1 + fmul(grad, xend-x1);
312
313                 xgap = invfrac(x1+32768);
314
315                 ix1  = xend >> 16;
316                 iy1  = yend >> 16;
317
318                 brightness1 = fmul(invfrac(yend), xgap);
319                 brightness2 = fmul(frac(yend), xgap);
320
321                 plot(ix1, iy1, brightness1 >> (16-AA_BITS));
322                 plot(ix1, iy1+1, brightness2 >> (16-AA_BITS));
323
324                 yf = yend+grad;
325
326                 //end point 2;
327
328                 xend = trunc(x2 + 32768);
329                 yend = y2 + fmul(grad, xend-x2);
330
331                 xgap = invfrac(x2 - 32768);
332
333                 ix2 = xend >> 16;
334                 iy2 = yend >> 16;
335
336                 brightness1 = fmul(invfrac(yend), xgap);
337                 brightness2 = fmul(frac(yend), xgap);
338       
339                 plot(ix2, iy2, brightness1 >> (16-AA_BITS));
340                 plot(ix2, iy2+1, brightness2 >> (16-AA_BITS));
341
342                 for(x = ix1+1; x <= ix2-1; x++)
343                 {
344                         brightness1 = invfrac(yf);
345                         brightness2 = frac(yf);
346
347                         plot(x, (yf >> 16), brightness1 >> (16-AA_BITS));
348                         plot(x, 1+(yf >> 16), brightness2 >> (16-AA_BITS));
349
350                         yf += grad;
351                 }
352         }
353         else
354         {
355                 if(y1 > y2)
356                 {
357                         swap = x1; x1 = x2; x2 = swap;
358                         swap = y1; y1 = y2; y2 = swap;
359                         xd   = -xd;
360                         yd   = -yd;
361                 }
362
363                 grad = fdiv(xd, yd);
364
365                 //end point 1
366
367                 yend = trunc(y1 + 32768);
368                 xend = x1 + fmul(grad, yend-y1);
369
370                 ygap = invfrac(y1+32768);
371
372                 iy1  = yend >> 16;
373                 ix1  = xend >> 16;
374
375                 brightness1 = fmul(invfrac(xend), ygap);
376                 brightness2 = fmul(frac(xend), ygap);
377
378                 plot(ix1, iy1, brightness1 >> (16-AA_BITS));
379                 plot(ix1+1, iy1, brightness2 >> (16-AA_BITS));
380
381                 xf = xend+grad;
382
383                 //end point 2;
384
385                 yend = trunc(y2 + 32768);
386                 xend = x2 + fmul(grad, yend-y2);
387
388                 ygap = invfrac(y2 - 32768);
389
390                 ix2 = xend >> 16;
391                 iy2 = yend >> 16;
392
393                 brightness1 = fmul(invfrac(xend), ygap);
394                 brightness2 = fmul(frac(xend), ygap);
395       
396                 plot(ix2, iy2, brightness1 >> (16-AA_BITS));
397                 plot(ix2+1, iy2, brightness2 >> (16-AA_BITS));
398
399                 for(y = iy1+1; y <= iy2-1; y++)
400                 {
401                         brightness1 = invfrac(xf);
402                         brightness2 = frac(xf);
403
404                         plot((xf >> 16), y, brightness1 >> (16-AA_BITS));
405                         plot(1+(xf >> 16), y, brightness2 >> (16-AA_BITS));
406
407                         xf += grad;
408                 }
409         }
410 }
411
412 #undef trunc
413 #undef frac
414 #undef invfrac
415 #undef plot
416
417 #undef AA_BITS
418 #undef AA_AND
419 #undef AA_BASE
420
421
422
423 void gfx_draw_circle (int cx, int cy, int radius, int circle_colour)
424 {
425         if (anti_alias_gfx && (circle_colour == GFX_COL_WHITE))
426                 gfx_draw_aa_circle (cx, cy, itofix(radius));
427         else    
428                 circle (gfx_screen, cx + GFX_X_OFFSET, cy + GFX_Y_OFFSET, radius, circle_colour);
429 }
430
431
432
433 void gfx_draw_line (int x1, int y1, int x2, int y2)
434 {
435         if (y1 == y2)
436         {
437                 hline (gfx_screen, x1 + GFX_X_OFFSET, y1 + GFX_Y_OFFSET, x2 + GFX_X_OFFSET, GFX_COL_WHITE);
438                 return;
439         }
440
441         if (x1 == x2)
442         {
443                 vline (gfx_screen, x1 + GFX_X_OFFSET, y1 + GFX_Y_OFFSET, y2 + GFX_Y_OFFSET, GFX_COL_WHITE);
444                 return;
445         }
446
447         if (anti_alias_gfx)
448                 gfx_draw_aa_line (itofix(x1), itofix(y1), itofix(x2), itofix(y2));
449         else
450                 line (gfx_screen, x1 + GFX_X_OFFSET, y1 + GFX_Y_OFFSET, x2 + GFX_X_OFFSET, y2 + GFX_Y_OFFSET, GFX_COL_WHITE);
451 }
452
453
454
455 void gfx_draw_colour_line (int x1, int y1, int x2, int y2, int line_colour)
456 {
457         if (y1 == y2)
458         {
459                 hline (gfx_screen, x1 + GFX_X_OFFSET, y1 + GFX_Y_OFFSET, x2 + GFX_X_OFFSET, line_colour);
460                 return;
461         }
462
463         if (x1 == x2)
464         {
465                 vline (gfx_screen, x1 + GFX_X_OFFSET, y1 + GFX_Y_OFFSET, y2 + GFX_Y_OFFSET, line_colour);
466                 return;
467         }
468
469         if (anti_alias_gfx && (line_colour == GFX_COL_WHITE))
470                 gfx_draw_aa_line (itofix(x1), itofix(y1), itofix(x2), itofix(y2));
471         else
472                 line (gfx_screen, x1 + GFX_X_OFFSET, y1 + GFX_Y_OFFSET, x2 + GFX_X_OFFSET, y2 + GFX_Y_OFFSET, line_colour);
473 }
474
475
476
477 void gfx_draw_triangle (int x1, int y1, int x2, int y2, int x3, int y3, int col)
478 {
479         triangle (gfx_screen, x1 + GFX_X_OFFSET, y1 + GFX_Y_OFFSET, x2 + GFX_X_OFFSET, y2 + GFX_Y_OFFSET,
480                                    x3 + GFX_X_OFFSET, y3 + GFX_Y_OFFSET, col);
481 }
482
483
484
485 void gfx_display_text (int x, int y, char *txt)
486 {
487         text_mode (-1);
488         textout (gfx_screen, datafile[ELITE_1].dat, txt, (x / (2 / GFX_SCALE)) + GFX_X_OFFSET, (y / (2 / GFX_SCALE)) + GFX_Y_OFFSET, GFX_COL_WHITE);
489 }
490
491
492 void gfx_display_colour_text (int x, int y, char *txt, int col)
493 {
494         text_mode (-1);
495         textout (gfx_screen, datafile[ELITE_1].dat, txt, (x / (2 / GFX_SCALE)) + GFX_X_OFFSET, (y / (2 / GFX_SCALE)) + GFX_Y_OFFSET, col);
496 }
497
498
499
500 void gfx_display_centre_text (int y, char *str, int psize, int col)
501 {
502         int txt_size;
503         int txt_colour;
504         
505         if (psize == 140)
506         {
507                 txt_size = ELITE_2;
508                 txt_colour = -1;
509         }
510         else
511         {
512                 txt_size = ELITE_1;
513                 txt_colour = col;
514         }
515
516         text_mode (-1);
517         textout_centre (gfx_screen,  datafile[txt_size].dat, str, (128 * GFX_SCALE) + GFX_X_OFFSET, (y / (2 / GFX_SCALE)) + GFX_Y_OFFSET, txt_colour);
518 }
519
520
521 void gfx_clear_display (void)
522 {
523         rectfill (gfx_screen, GFX_X_OFFSET + 1, GFX_Y_OFFSET + 1, 510 + GFX_X_OFFSET, 383 + GFX_Y_OFFSET, GFX_COL_BLACK);
524 }
525
526 void gfx_clear_text_area (void)
527 {
528         rectfill (gfx_screen, GFX_X_OFFSET + 1, GFX_Y_OFFSET + 340, 510 + GFX_X_OFFSET, 383 + GFX_Y_OFFSET, GFX_COL_BLACK);
529 }
530
531
532 void gfx_clear_area (int tx, int ty, int bx, int by)
533 {
534         rectfill (gfx_screen, tx + GFX_X_OFFSET, ty + GFX_Y_OFFSET,
535                                    bx + GFX_X_OFFSET, by + GFX_Y_OFFSET, GFX_COL_BLACK);
536 }
537
538 void gfx_draw_rectangle (int tx, int ty, int bx, int by, int col)
539 {
540         rectfill (gfx_screen, tx + GFX_X_OFFSET, ty + GFX_Y_OFFSET,
541                                    bx + GFX_X_OFFSET, by + GFX_Y_OFFSET, col);
542 }
543
544
545 void gfx_display_pretty_text (int tx, int ty, int bx, int by, char *txt)
546 {
547         char strbuf[100];
548         char *str;
549         char *bptr;
550         int len;
551         int pos;
552         int maxlen;
553         
554         maxlen = (bx - tx) / 8;
555
556         str = txt;      
557         len = strlen(txt);
558         
559         while (len > 0)
560         {
561                 pos = maxlen;
562                 if (pos > len)
563                         pos = len;
564
565                 while ((str[pos] != ' ') && (str[pos] != ',') &&
566                            (str[pos] != '.') && (str[pos] != '\0'))
567                 {
568                         pos--;
569                 }
570
571                 len = len - pos - 1;
572         
573                 for (bptr = strbuf; pos >= 0; pos--)
574                         *bptr++ = *str++;
575
576                 *bptr = '\0';
577
578                 text_mode (-1);
579                 textout (gfx_screen, datafile[ELITE_1].dat, strbuf, tx + GFX_X_OFFSET, ty + GFX_Y_OFFSET, GFX_COL_WHITE);
580                 ty += (8 * GFX_SCALE);
581         }
582 }
583
584
585 void gfx_draw_scanner (void)
586 {
587         blit (scanner_image, gfx_screen, 0, 0, GFX_X_OFFSET, 385+GFX_Y_OFFSET, scanner_image->w, scanner_image->h);
588 }
589
590 void gfx_set_clip_region (int tx, int ty, int bx, int by)
591 {
592         set_clip (gfx_screen, tx + GFX_X_OFFSET, ty + GFX_Y_OFFSET, bx + GFX_X_OFFSET, by + GFX_Y_OFFSET);
593 }
594
595
596 void gfx_start_render (void)
597 {
598         start_poly = 0;
599         total_polys = 0;
600 }
601
602
603 void gfx_render_polygon (int num_points, int *point_list, int face_colour, int zavg)
604 {
605         int i;
606         int x;
607         int nx;
608         
609         if (total_polys == MAX_POLYS)
610                 return;
611
612         x = total_polys;
613         total_polys++;
614         
615         poly_chain[x].no_points = num_points;
616         poly_chain[x].face_colour = face_colour;
617         poly_chain[x].z = zavg;
618         poly_chain[x].next = -1;
619
620         for (i = 0; i < 16; i++)
621                 poly_chain[x].point_list[i] = point_list[i];                            
622
623         if (x == 0)
624                 return;
625
626         if (zavg > poly_chain[start_poly].z)
627         {
628                 poly_chain[x].next = start_poly;
629                 start_poly = x;
630                 return;
631         }       
632
633         for (i = start_poly; poly_chain[i].next != -1; i = poly_chain[i].next)
634         {
635                 nx = poly_chain[i].next;
636                 
637                 if (zavg > poly_chain[nx].z)
638                 {
639                         poly_chain[i].next = x;
640                         poly_chain[x].next = nx;
641                         return;
642                 }
643         }       
644         
645         poly_chain[i].next = x;
646 }
647
648
649 void gfx_render_line (int x1, int y1, int x2, int y2, int dist, int col)
650 {
651         int point_list[4];
652         
653         point_list[0] = x1;
654         point_list[1] = y1;
655         point_list[2] = x2;
656         point_list[3] = y2;
657         
658         gfx_render_polygon (2, point_list, col, dist);
659 }
660
661
662 void gfx_finish_render (void)
663 {
664         int num_points;
665         int *pl;
666         int i;
667         int col;
668         
669         if (total_polys == 0)
670                 return;
671                 
672         for (i = start_poly; i != -1; i = poly_chain[i].next)
673         {
674                 num_points = poly_chain[i].no_points;
675                 pl = poly_chain[i].point_list;
676                 col = poly_chain[i].face_colour;
677
678                 if (num_points == 2)
679                 {
680                         gfx_draw_colour_line (pl[0], pl[1], pl[2], pl[3], col);
681                         continue;
682                 }
683                 
684                 gfx_polygon (num_points, pl, col); 
685         };
686 }
687
688
689 void gfx_polygon (int num_points, int *poly_list, int face_colour)
690 {
691         int i;
692         int x,y;
693         
694         x = 0;
695         y = 1;
696         for (i = 0; i < num_points; i++)
697         {
698                 poly_list[x] += GFX_X_OFFSET;
699                 poly_list[y] += GFX_Y_OFFSET;
700                 x += 2;
701                 y += 2;
702         }
703         
704         polygon (gfx_screen, num_points, poly_list, face_colour);
705 }
706
707
708 void gfx_draw_sprite (int sprite_no, int x, int y)
709 {
710         BITMAP *sprite_bmp;
711         
712         switch (sprite_no)
713         {
714                 case IMG_GREEN_DOT:
715                         sprite_bmp = datafile[GRNDOT].dat;
716                         break;
717                 
718                 case IMG_RED_DOT:
719                         sprite_bmp = datafile[REDDOT].dat;
720                         break;
721                         
722                 case IMG_BIG_S:
723                         sprite_bmp = datafile[SAFE].dat;
724                         break;
725                 
726                 case IMG_ELITE_TXT:
727                         sprite_bmp = datafile[ELITETXT].dat;
728                         break;
729                         
730                 case IMG_BIG_E:
731                         sprite_bmp = datafile[ECM].dat;
732                         break;
733                         
734                 case IMG_BLAKE:
735                         sprite_bmp = datafile[BLAKE].dat;
736                         break;
737                 
738                 case IMG_MISSILE_GREEN:
739                         sprite_bmp = datafile[MISSILE_G].dat;
740                         break;
741
742                 case IMG_MISSILE_YELLOW:
743                         sprite_bmp = datafile[MISSILE_Y].dat;
744                         break;
745
746                 case IMG_MISSILE_RED:
747                         sprite_bmp = datafile[MISSILE_R].dat;
748                         break;
749
750                 default:
751                         return;
752         }
753
754         if (x == -1)
755                 x = ((256 * GFX_SCALE) - sprite_bmp->w) / 2;
756         
757         draw_sprite (gfx_screen, sprite_bmp, x + GFX_X_OFFSET, y + GFX_Y_OFFSET);
758 }
759
760
761 int gfx_request_file (char *title, char *path, char *ext)
762 {
763         int okay;
764
765         show_mouse (screen);
766         okay = file_select (title, path, ext);
767         show_mouse (NULL);
768
769         return okay;
770 }
771