1 /*
2  *    Example program for the Allegro library, by Shawn Hargreaves.
3  *
4  *    This program demonstrates how to use the 3d matrix functions.
5  *    It isn't a very elegant or efficient piece of code, but it
6  *    does show the stuff in action. It is left to the reader as
7  *    an exercise to design a proper model structure and rendering
8  *    pipeline: after all, the best way to do that sort of stuff
9  *    varies hugely from one game to another.
10  *
11  *    The example first shows a screen resolution selection dialog.
12  *    Then, a number of bouncing 3d cubes are animated. Pressing
13  *    a key modifies the rendering of the cubes, which can be
14  *    wireframe, the more complex transparent perspective correct
15  *    texture mapped version, and many other.
16  */
17 
18 
19 #include <allegro.h>
20 
21 
22 
23 #define NUM_SHAPES         8     /* number of bouncing cubes */
24 
25 #define NUM_VERTICES       8     /* a cube has eight corners */
26 #define NUM_FACES          6     /* a cube has six faces */
27 
28 
29 typedef struct VTX
30 {
31    fixed x, y, z;
32 } VTX;
33 
34 
35 typedef struct QUAD              /* four vertices makes a quad */
36 {
37    VTX *vtxlist;
38    int v1, v2, v3, v4;
39 } QUAD;
40 
41 
42 typedef struct SHAPE             /* store position of a shape */
43 {
44    fixed x, y, z;                /* x, y, z position */
45    fixed rx, ry, rz;             /* rotations */
46    fixed dz;                     /* speed of movement */
47    fixed drx, dry, drz;          /* speed of rotation */
48 } SHAPE;
49 
50 
51 VTX points[] =                   /* a cube, centered on the origin */
52 {
53    /* vertices of the cube */
54    { -32 << 16, -32 << 16, -32 << 16 },
55    { -32 << 16,  32 << 16, -32 << 16 },
56    {  32 << 16,  32 << 16, -32 << 16 },
57    {  32 << 16, -32 << 16, -32 << 16 },
58    { -32 << 16, -32 << 16,  32 << 16 },
59    { -32 << 16,  32 << 16,  32 << 16 },
60    {  32 << 16,  32 << 16,  32 << 16 },
61    {  32 << 16, -32 << 16,  32 << 16 },
62 };
63 
64 
65 QUAD faces[] =                   /* group the vertices into polygons */
66 {
67    { points, 0, 3, 2, 1 },
68    { points, 4, 5, 6, 7 },
69    { points, 0, 1, 5, 4 },
70    { points, 2, 3, 7, 6 },
71    { points, 0, 4, 7, 3 },
72    { points, 1, 2, 6, 5 }
73 };
74 
75 
76 SHAPE shapes[NUM_SHAPES];        /* a list of shapes */
77 
78 
79 /* somewhere to put translated vertices */
80 VTX output_points[NUM_VERTICES * NUM_SHAPES];
81 QUAD output_faces[NUM_FACES * NUM_SHAPES];
82 
83 
84 enum {
85    wireframe,
86    flat,
87    gcol,
88    grgb,
89    atex,
90    ptex,
91    atex_mask,
92    ptex_mask,
93    atex_lit,
94    ptex_lit,
95    atex_mask_lit,
96    ptex_mask_lit,
97    atex_trans,
98    ptex_trans,
99    atex_mask_trans,
100    ptex_mask_trans,
101    last_mode
102 } render_mode = wireframe;
103 
104 
105 int render_type[] =
106 {
107    0,
108    POLYTYPE_FLAT,
109    POLYTYPE_GCOL,
110    POLYTYPE_GRGB,
111    POLYTYPE_ATEX,
112    POLYTYPE_PTEX,
113    POLYTYPE_ATEX_MASK,
114    POLYTYPE_PTEX_MASK,
115    POLYTYPE_ATEX_LIT,
116    POLYTYPE_PTEX_LIT,
117    POLYTYPE_ATEX_MASK_LIT,
118    POLYTYPE_PTEX_MASK_LIT,
119    POLYTYPE_ATEX_TRANS,
120    POLYTYPE_PTEX_TRANS,
121    POLYTYPE_ATEX_MASK_TRANS,
122    POLYTYPE_PTEX_MASK_TRANS
123 };
124 
125 
126 char *mode_desc[] =
127 {
128    "Wireframe",
129    "Flat shaded",
130    "Single color Gouraud shaded",
131    "Gouraud shaded",
132    "Texture mapped",
133    "Perspective correct texture mapped",
134    "Masked texture mapped",
135    "Masked persp. correct texture mapped",
136    "Lit texture map",
137    "Lit persp. correct texture map",
138    "Masked lit texture map",
139    "Masked lit persp. correct texture map",
140    "Transparent texture mapped",
141    "Transparent perspective correct texture mapped",
142    "Transparent masked texture mapped",
143    "Transparent masked persp. correct texture mapped",
144 };
145 
146 
147 BITMAP *texture;
148 
149 
150 
151 /* initialise shape positions */
init_shapes(void)152 void init_shapes(void)
153 {
154    int c;
155 
156    for (c=0; c<NUM_SHAPES; c++) {
157       shapes[c].x = ((AL_RAND() & 255) - 128) << 16;
158       shapes[c].y = ((AL_RAND() & 255) - 128) << 16;
159       shapes[c].z = 768 << 16;
160       shapes[c].rx = 0;
161       shapes[c].ry = 0;
162       shapes[c].rz = 0;
163       shapes[c].dz =  ((AL_RAND() & 255) - 8) << 12;
164       shapes[c].drx = ((AL_RAND() & 31) - 16) << 12;
165       shapes[c].dry = ((AL_RAND() & 31) - 16) << 12;
166       shapes[c].drz = ((AL_RAND() & 31) - 16) << 12;
167    }
168 }
169 
170 
171 
172 /* update shape positions */
animate_shapes(void)173 void animate_shapes(void)
174 {
175    int c;
176 
177    for (c=0; c<NUM_SHAPES; c++) {
178       shapes[c].z += shapes[c].dz;
179 
180       if ((shapes[c].z > itofix(1024)) ||
181 	  (shapes[c].z < itofix(192)))
182 	 shapes[c].dz = -shapes[c].dz;
183 
184       shapes[c].rx += shapes[c].drx;
185       shapes[c].ry += shapes[c].dry;
186       shapes[c].rz += shapes[c].drz;
187    }
188 }
189 
190 
191 
192 /* translate shapes from 3d world space to 2d screen space */
translate_shapes(void)193 void translate_shapes(void)
194 {
195    int c, d;
196    MATRIX matrix;
197    VTX *outpoint = output_points;
198    QUAD *outface = output_faces;
199 
200    for (c=0; c<NUM_SHAPES; c++) {
201       /* build a transformation matrix */
202       get_transformation_matrix(&matrix, itofix(1),
203 				shapes[c].rx, shapes[c].ry, shapes[c].rz,
204 				shapes[c].x, shapes[c].y, shapes[c].z);
205 
206       /* output the vertices */
207       for (d=0; d<NUM_VERTICES; d++) {
208 	 apply_matrix(&matrix, points[d].x, points[d].y, points[d].z,
209 		      &outpoint[d].x, &outpoint[d].y, &outpoint[d].z);
210 	 persp_project(outpoint[d].x, outpoint[d].y, outpoint[d].z,
211 		       &outpoint[d].x, &outpoint[d].y);
212       }
213 
214       /* output the faces */
215       for (d=0; d<NUM_FACES; d++) {
216 	 outface[d] = faces[d];
217 	 outface[d].vtxlist = outpoint;
218       }
219 
220       outpoint += NUM_VERTICES;
221       outface += NUM_FACES;
222    }
223 }
224 
225 
226 
227 /* draw a line (for wireframe display) */
wire(BITMAP * b,VTX * v1,VTX * v2)228 void wire(BITMAP *b, VTX *v1, VTX *v2)
229 {
230    int col = CLAMP(128, 255 - fixtoi(v1->z+v2->z) / 16, 255);
231    line(b, fixtoi(v1->x), fixtoi(v1->y), fixtoi(v2->x), fixtoi(v2->y),
232 	palette_color[col]);
233 }
234 
235 
236 
237 /* draw a quad */
draw_quad(BITMAP * b,VTX * v1,VTX * v2,VTX * v3,VTX * v4,int mode)238 void draw_quad(BITMAP *b, VTX *v1, VTX *v2, VTX *v3, VTX *v4, int mode)
239 {
240    int col;
241 
242    /* four vertices */
243    V3D vtx1 = { 0, 0, 0, 0,      0,      0 };
244    V3D vtx2 = { 0, 0, 0, 32<<16, 0,      0 };
245    V3D vtx3 = { 0, 0, 0, 32<<16, 32<<16, 0 };
246    V3D vtx4 = { 0, 0, 0, 0,      32<<16, 0 };
247 
248    vtx1.x = v1->x;   vtx1.y = v1->y;   vtx1.z = v1->z;
249    vtx2.x = v2->x;   vtx2.y = v2->y;   vtx2.z = v2->z;
250    vtx3.x = v3->x;   vtx3.y = v3->y;   vtx3.z = v3->z;
251    vtx4.x = v4->x;   vtx4.y = v4->y;   vtx4.z = v4->z;
252 
253    /* cull backfaces */
254    if ((mode != POLYTYPE_ATEX_MASK) && (mode != POLYTYPE_PTEX_MASK) &&
255        (mode != POLYTYPE_ATEX_MASK_LIT) && (mode != POLYTYPE_PTEX_MASK_LIT) &&
256        (polygon_z_normal(&vtx1, &vtx2, &vtx3) < 0))
257       return;
258 
259    /* set up the vertex color, differently for each rendering mode */
260    switch (mode) {
261 
262       case POLYTYPE_FLAT:
263 	 col = CLAMP(128, 255 - fixtoi(v1->z+v2->z) / 16, 255);
264 	 vtx1.c = vtx2.c = vtx3.c = vtx4.c = palette_color[col];
265 	 break;
266 
267       case POLYTYPE_GCOL:
268 	 vtx1.c = palette_color[0xD0];
269 	 vtx2.c = palette_color[0x80];
270 	 vtx3.c = palette_color[0xB0];
271 	 vtx4.c = palette_color[0xFF];
272 	 break;
273 
274       case POLYTYPE_GRGB:
275 	 vtx1.c = 0x000000;
276 	 vtx2.c = 0x7F0000;
277 	 vtx3.c = 0xFF0000;
278 	 vtx4.c = 0x7F0000;
279 	 break;
280 
281       case POLYTYPE_ATEX_LIT:
282       case POLYTYPE_PTEX_LIT:
283       case POLYTYPE_ATEX_MASK_LIT:
284       case POLYTYPE_PTEX_MASK_LIT:
285 	 vtx1.c = CLAMP(0, 255 - fixtoi(v1->z) / 4, 255);
286 	 vtx2.c = CLAMP(0, 255 - fixtoi(v2->z) / 4, 255);
287 	 vtx3.c = CLAMP(0, 255 - fixtoi(v3->z) / 4, 255);
288 	 vtx4.c = CLAMP(0, 255 - fixtoi(v4->z) / 4, 255);
289 	 break;
290    }
291 
292    /* draw the quad */
293    quad3d(b, mode, texture, &vtx1, &vtx2, &vtx3, &vtx4);
294 }
295 
296 
297 
298 /* callback for qsort() */
quad_cmp(const void * e1,const void * e2)299 int quad_cmp(const void *e1, const void *e2)
300 {
301    QUAD *q1 = (QUAD *)e1;
302    QUAD *q2 = (QUAD *)e2;
303 
304    fixed d1 = q1->vtxlist[q1->v1].z + q1->vtxlist[q1->v2].z +
305 	      q1->vtxlist[q1->v3].z + q1->vtxlist[q1->v4].z;
306 
307    fixed d2 = q2->vtxlist[q2->v1].z + q2->vtxlist[q2->v2].z +
308 	      q2->vtxlist[q2->v3].z + q2->vtxlist[q2->v4].z;
309 
310    return d2 - d1;
311 }
312 
313 
314 
315 /* draw the shapes calculated by translate_shapes() */
draw_shapes(BITMAP * b)316 void draw_shapes(BITMAP *b)
317 {
318    int c;
319    QUAD *face = output_faces;
320    VTX *v1, *v2, *v3, *v4;
321 
322    /* depth sort */
323    qsort(output_faces, NUM_FACES * NUM_SHAPES, sizeof(QUAD), quad_cmp);
324 
325    for (c=0; c < NUM_FACES * NUM_SHAPES; c++) {
326       /* find the vertices used by the face */
327       v1 = face->vtxlist + face->v1;
328       v2 = face->vtxlist + face->v2;
329       v3 = face->vtxlist + face->v3;
330       v4 = face->vtxlist + face->v4;
331 
332       /* draw the face */
333       if (render_mode == wireframe) {
334 	 wire(b, v1, v2);
335 	 wire(b, v2, v3);
336 	 wire(b, v3, v4);
337 	 wire(b, v4, v1);
338       }
339       else {
340 	 draw_quad(b, v1, v2, v3, v4, render_type[render_mode]);
341       }
342 
343       face++;
344    }
345 }
346 
347 
348 
349 /* RGB -> color mapping table. Not needed, but speeds things up */
350 RGB_MAP rgb_table;
351 
352 
353 /* lighting color mapping table */
354 COLOR_MAP light_table;
355 
356 /* transparency color mapping table */
357 COLOR_MAP trans_table;
358 
359 
360 
main(void)361 int main(void)
362 {
363    BITMAP *buffer;
364    PALETTE pal;
365    int c, w, h, bpp;
366    int last_retrace_count;
367 
368    if (allegro_init() != 0)
369       return 1;
370    install_keyboard();
371    install_mouse();
372    install_timer();
373 
374    /* color 0 = black */
375    pal[0].r = pal[0].g = pal[0].b = 0;
376 
377    /* copy the desktop palette */
378    for (c=1; c<64; c++)
379       pal[c] = desktop_palette[c];
380 
381    /* make a red gradient */
382    for (c=64; c<96; c++) {
383       pal[c].r = (c-64)*2;
384       pal[c].g = pal[c].b = 0;
385    }
386 
387    /* make a green gradient */
388    for (c=96; c<128; c++) {
389       pal[c].g = (c-96)*2;
390       pal[c].r = pal[c].b = 0;
391    }
392 
393    /* set up a greyscale in the top half of the palette */
394    for (c=128; c<256; c++)
395       pal[c].r = pal[c].g = pal[c].b = (c-128)/2;
396 
397    /* build rgb_map table */
398    create_rgb_table(&rgb_table, pal, NULL);
399    rgb_map = &rgb_table;
400 
401    /* build a lighting table */
402    create_light_table(&light_table, pal, 0, 0, 0, NULL);
403    color_map = &light_table;
404 
405    /* build a transparency table */
406    /* textures are 25% transparent (75% opaque) */
407    create_trans_table(&trans_table, pal, 192, 192, 192, NULL);
408 
409    /* set up the truecolor blending functions */
410    /* textures are 25% transparent (75% opaque) */
411    set_trans_blender(0, 0, 0, 192);
412 
413    /* set the graphics mode */
414    if (set_gfx_mode(GFX_SAFE, 320, 200, 0, 0) != 0) {
415       set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
416       allegro_message("Unable to set any graphic mode\n%s\n", allegro_error);
417       return 1;
418    }
419    set_palette(desktop_palette);
420 
421    c = GFX_AUTODETECT;
422    w = SCREEN_W;
423    h = SCREEN_H;
424    bpp = bitmap_color_depth(screen);
425    if (!gfx_mode_select_ex(&c, &w, &h, &bpp)) {
426       allegro_exit();
427       return 1;
428    }
429 
430    set_color_depth(bpp);
431 
432    if (set_gfx_mode(c, w, h, 0, 0) != 0) {
433       set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
434       allegro_message("Error setting graphics mode\n%s\n", allegro_error);
435       return 1;
436    }
437 
438    set_palette(pal);
439 
440    /* make a bitmap for use as a texture map */
441    texture = create_bitmap(32, 32);
442    clear_to_color(texture, bitmap_mask_color(texture));
443    line(texture, 0, 0, 31, 31, palette_color[1]);
444    line(texture, 0, 31, 31, 0, palette_color[1]);
445    rect(texture, 0, 0, 31, 31, palette_color[1]);
446    textout_ex(texture, font, "dead", 0, 0, palette_color[2], -1);
447    textout_ex(texture, font, "pigs", 0, 8, palette_color[2], -1);
448    textout_ex(texture, font, "cant", 0, 16, palette_color[2], -1);
449    textout_ex(texture, font, "fly.", 0, 24, palette_color[2], -1);
450 
451    /* double buffer the animation */
452    buffer = create_bitmap(SCREEN_W, SCREEN_H);
453 
454    /* set up the viewport for the perspective projection */
455    set_projection_viewport(0, 0, SCREEN_W, SCREEN_H);
456 
457    /* initialise the bouncing shapes */
458    init_shapes();
459 
460    last_retrace_count = retrace_count;
461 
462    for (;;) {
463       clear_bitmap(buffer);
464 
465       while (last_retrace_count < retrace_count) {
466 	 animate_shapes();
467 	 last_retrace_count++;
468       }
469 
470       translate_shapes();
471       draw_shapes(buffer);
472 
473       textprintf_ex(buffer, font, 0, 0, palette_color[192], -1, "%s, %d bpp",
474 		    mode_desc[render_mode], bitmap_color_depth(screen));
475       textout_ex(buffer, font, "Press a key to change", 0, 12,
476 		 palette_color[192], -1);
477 
478       vsync();
479       blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
480 
481       if (keypressed()) {
482 	 if ((readkey() & 0xFF) == 27)
483 	    break;
484 	 else {
485 	    render_mode++;
486 	    if (render_mode >= last_mode) {
487 	       render_mode = wireframe;
488 	       color_map = &light_table;
489 	    }
490 	    if (render_type[render_mode] >= POLYTYPE_ATEX_TRANS)
491 	       color_map = &trans_table;
492 	 }
493       }
494    }
495 
496    destroy_bitmap(buffer);
497    destroy_bitmap(texture);
498 
499    return 0;
500 }
501 
502 END_OF_MAIN()
503