Previous | Table of Contents | Next

### Animation

Gosh. There’s just no way I can discuss high-level animation fundamentals in any detail here; I could spend an entire (and entirely separate) book on animation techniques alone. You might want to have a look at Chapters 43 through 46 before attacking the code in this chapter; that will have to do us for the present volume. (I will return to 3-D animation in the next chapter.)

Basically, I’m going to perform page flipped animation, in which one page (that is, a bitmap large enough to hold a full screen) of display memory is displayed while another page is drawn to. When the drawing is finished, the newly modified page is displayed, and the other—now invisible—page is drawn to. The process repeats ad infinitum. For further information, some good places to start are Computer Graphics, by Foley and van Dam (Addison-Wesley); Principles of Interactive Computer Graphics, by Newman and Sproull (McGraw Hill); and “Real-Time Animation” by Rahner James (January 1990, Dr. Dobb’s Journal).

Some of the code in this chapter was adapted for Mode X from the code in Chapter 44—yet another reason to read that chapter before finishing this one.

### Mode X Animation in Action

Listing 49.5 ties together everything I’ve discussed about Mode X so far in a compact but surprisingly powerful animation package. Listing 49.5 first uses solid and patterned fills and system-memory-to-screen-memory masked copying to draw a static background containing a mountain, a sun, a plain, water, and a house with puffs of smoke coming out of the chimney, and sets up the four alignments of a masked kite image. The background is transferred to both display pages, and drawing of 20 kite images in the nondisplayed page using fast masked copying begins. After all images have been drawn, the page is flipped to show the newly updated screen, and the kites are moved and drawn in the other page, which is no longer displayed. Kites are erased at their old positions in the nondisplayed page by block copying from the background page. (See the discussion in the previous chapter for the display memory organization used by Listing 49.5.) So far as the displayed image is concerned, there is never any hint of flicker or disturbance of the background. This continues at a rate of up to 60 times a second until Esc is pressed to exit the program. See Figure 49.1 for a screen shot of the resulting image—add the animation in your imagination.

Figure 49.1 An animated Mode X screen.

LISTING 49.5 L49-5.C

    /* Sample mode X VGA animation program. Portions of this code first appeared
       in PC Techniques. Compiled with Borland C++ 2.0 in C compilation mode. */

    #include <stdio.h>
    #include <conio.h>
    #include <dos.h>
    #include <math.h>
    #include “maskim.h”

    #define SCREEN_SEG         0xA000
    #define SCREEN_WIDTH       320
    #define SCREEN_HEIGHT      240
    #define PAGE0_START_OFFSET 0
    #define PAGE1_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH)/4)
    #define BG_START_OFFSET    (((long)SCREEN_HEIGHT*SCREEN_WIDTH*2)/4)
    #define DOWNLOAD_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH*3)/4)

    static unsigned int PageStartOffsets[2] = {PAGE0_START_OFFSET,PAGE1_START_OFFSET};
    static char GreenAndBrownPattern[] = {2,6,2,6, 6,2,6,2, 2,6,2,6, 6,2,6,2};
    static char PineTreePattern[] = {2,2,2,2, 2,6,2,6, 2,2,6,2, 2,2,2,2};
    static char BrickPattern[] = {6,6,7,6, 7,7,7,7, 7,6,6,6, 7,7,7,7,};
    static char RoofPattern[] = {8,8,8,7, 7,7,7,7, 8,8,8,7, 8,8,8,7};

    #define SMOKE_WIDTH  7
    #define SMOKE_HEIGHT 7
    static char SmokePixels[] = {
       0, 0,15,15,15, 0, 0,
       0, 7, 7,15,15,15, 0,
       8, 7, 7, 7,15,15,15,
       8, 7, 7, 7, 7,15,15,
       0, 8, 7, 7, 7, 7,15,
       0, 0, 8, 7, 7, 7, 0,
       0, 0, 0, 8, 8, 0, 0};
    static char SmokeMask[] = {
       0, 0, 1, 1, 1, 0, 0,
       0, 1, 1, 1, 1, 1, 0,
       1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1,
       0, 1, 1, 1, 1, 1, 0,
       0, 0, 1, 1, 1, 0, 0};
    #define KITE_WIDTH  10
    #define KITE_HEIGHT 16
    static char KitePixels[] = {
       0, 0, 0, 0,45, 0, 0, 0, 0, 0,
       0, 0, 0,46,46,46, 0, 0, 0, 0,
       0, 0,47,47,47,47,47, 0, 0, 0,
       0,48,48,48,48,48,48,48, 0, 0,
      49,49,49,49,49,49,49,49,49, 0,
       0,50,50,50,50,50,50,50, 0, 0,
       0,51,51,51,51,51,51,51, 0, 0,
       0, 0,52,52,52,52,52, 0, 0, 0,
       0, 0,53,53,53,53,53, 0, 0, 0,
       0, 0, 0,54,54,54, 0, 0, 0, 0,
       0, 0, 0,55,55,55, 0, 0, 0, 0,
       0, 0, 0, 0,58, 0, 0, 0, 0, 0,
       0, 0, 0, 0,59, 0, 0, 0, 0,66,
       0, 0, 0, 0,60, 0, 0,64, 0,65,
       0, 0, 0, 0, 0,61, 0, 0,64, 0,
       0, 0, 0, 0, 0, 0,62,63, 0,64};
    static char KiteMask[] = {
       0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
       0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
       0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
       0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
       0, 1, 1, 1, 1, 1, 1, 1, 0, 0,
       0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
       0, 0, 1, 1, 1, 1, 1, 0, 0, 0,
       0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
       0, 0, 0, 1, 1, 1, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
       0, 0, 0, 0, 1, 0, 0, 1, 0, 1,
       0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
       0, 0, 0, 0, 0, 0, 1, 1, 0, 1};
    static MaskedImage KiteImage;

    #define NUM_OBJECTS  20
    typedef struct {
       int X,Y,Width,Height,XDir,YDir,XOtherPage,YOtherPage;
       MaskedImage *Image;
    } AnimatedObject;
    AnimatedObject AnimatedObjects[] = {
       {  0,  0,KITE_WIDTH,KITE_HEIGHT, 1, 1,  0,  0,&KiteImage},
       { 10, 10,KITE_WIDTH,KITE_HEIGHT, 0, 1, 10, 10,&KiteImage},
       { 20, 20,KITE_WIDTH,KITE_HEIGHT,-1, 1, 20, 20,&KiteImage},
       { 30, 30,KITE_WIDTH,KITE_HEIGHT,-1,-1, 30, 30,&KiteImage},
       { 40, 40,KITE_WIDTH,KITE_HEIGHT, 1,-1, 40, 40,&KiteImage},
       { 50, 50,KITE_WIDTH,KITE_HEIGHT, 0,-1, 50, 50,&KiteImage},
       { 60, 60,KITE_WIDTH,KITE_HEIGHT, 1, 0, 60, 60,&KiteImage},
       { 70, 70,KITE_WIDTH,KITE_HEIGHT,-1, 0, 70, 70,&KiteImage},
       { 80, 80,KITE_WIDTH,KITE_HEIGHT, 1, 2, 80, 80,&KiteImage},
       { 90, 90,KITE_WIDTH,KITE_HEIGHT, 0, 2, 90, 90,&KiteImage},
       {100,100,KITE_WIDTH,KITE_HEIGHT,-1, 2,100,100,&KiteImage},
       {110,110,KITE_WIDTH,KITE_HEIGHT,-1,-2,110,110,&KiteImage},
       {120,120,KITE_WIDTH,KITE_HEIGHT, 1,-2,120,120,&KiteImage},
       {130,130,KITE_WIDTH,KITE_HEIGHT, 0,-2,130,130,&KiteImage},
       {140,140,KITE_WIDTH,KITE_HEIGHT, 2, 0,140,140,&KiteImage},
       {150,150,KITE_WIDTH,KITE_HEIGHT,-2, 0,150,150,&KiteImage},
       {160,160,KITE_WIDTH,KITE_HEIGHT, 2, 2,160,160,&KiteImage},
       {170,170,KITE_WIDTH,KITE_HEIGHT,-2, 2,170,170,&KiteImage},
       {180,180,KITE_WIDTH,KITE_HEIGHT,-2,-2,180,180,&KiteImage},
       {190,190,KITE_WIDTH,KITE_HEIGHT, 2,-2,190,190,&KiteImage},
    };
    void main(void);
    void DrawBackground(unsigned int);
    void MoveObject(AnimatedObject *);
    extern void Set320x240Mode(void);
    extern void FillRectangleX(int, int, int, int, unsigned int, int);
    extern void FillPatternX(int, int, int, int, unsigned int, char*);
    extern void CopySystemToScreenMaskedX(int, int, int, int, int, int,
       char *, unsigned int, int, int, char *);
    extern void CopyScreenToScreenX(int, int, int, int, int, int,
       unsigned int, unsigned int, int, int);
    extern unsigned int CreateAlignedMaskedImage(MaskedImage *,
       unsigned int, char *, int, int, char *);
    extern void CopyScreenToScreenMaskedX(int, int, int, int, int, int,
       MaskedImage *, unsigned int, int);
    extern void ShowPage(unsigned int);

    void main()
    {
       int DisplayedPage, NonDisplayedPage, Done, i;
       union REGS regset;
       Set320x240Mode();
       /* Download the kite image for fast copying later. */
       if (CreateAlignedMaskedImage(&KiteImage, DOWNLOAD_START_OFFSET,
             KitePixels, KITE_WIDTH, KITE_HEIGHT, KiteMask) == 0) {
          regset.x.ax = 0x0003; int86(0x10, &regset, &regset);
          printf(“Couldn’t get memory\n”); exit();
       }
       /* Draw the background to the background page. */
       DrawBackground(BG_START_OFFSET);
       /* Copy the background to both displayable pages. */
       CopyScreenToScreenX(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0,
             BG_START_OFFSET, PAGE0_START_OFFSET, SCREEN_WIDTH, SCREEN_WIDTH);
       CopyScreenToScreenX(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0,
             BG_START_OFFSET, PAGE1_START_OFFSET, SCREEN_WIDTH, SCREEN_WIDTH);
       /* Move the objects and update their images in the nondisplayed
          page, then flip the page, until Esc is pressed. */
       Done = DisplayedPage = 0;
       do {
          NonDisplayedPage = DisplayedPage ^ 1;
          /* Erase each object in nondisplayed page by copying block from
                background page at last location in that page. */
          for (i=0; i<NUM_OBJECTS; i++) {
             CopyScreenToScreenX(AnimatedObjects[i].XOtherPage,
                   AnimatedObjects[i].YOtherPage,
                   AnimatedObjects[i].XOtherPage +
                   AnimatedObjects[i].Width,
                   AnimatedObjects[i].YOtherPage +
                   AnimatedObjects[i].Height,
                   AnimatedObjects[i].XOtherPage,
                   AnimatedObjects[i].YOtherPage, BG_START_OFFSET,
                   PageStartOffsets[NonDisplayedPage], SCREEN_WIDTH, SCREEN_WIDTH);
          }
          /* Move and draw each object in the nondisplayed page. */
          for (i=0; i<NUM_OBJECTS; i++) {
             MoveObject(&AnimatedObjects[i]);
             /* Draw object into nondisplayed page at new location */
             CopyScreenToScreenMaskedX(0, 0, AnimatedObjects[i].Width,
                   AnimatedObjects[i].Height, AnimatedObjects[i].X,
                   AnimatedObjects[i].Y, AnimatedObjects[i].Image,
                   PageStartOffsets[NonDisplayedPage], SCREEN_WIDTH);
          }
          /* Flip to the page into which we just drew. */
          ShowPage(PageStartOffsets[DisplayedPage = NonDisplayedPage]);
          /* See if it’s time to end. */
          if (kbhit()) {
             if (getch() == 0x1B) Done = 1;   /* Esc to end */
          }
       } while (!Done);
       /* Restore text mode and done. */
       regset.x.ax = 0x0003; int86(0x10, &regset, &regset);
    }
    void DrawBackground(unsigned int PageStart)
    {
       int i,j,Temp;
       /* Fill the screen with cyan. */
       FillRectangleX(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, PageStart, 11);
       /* Draw a green and brown rectangle to create a flat plain. */
       FillPatternX(0, 160, SCREEN_WIDTH, SCREEN_HEIGHT, PageStart,
                    GreenAndBrownPattern);
       /* Draw blue water at the bottom of the screen. */
       FillRectangleX(0, SCREEN_HEIGHT-30, SCREEN_WIDTH, SCREEN_HEIGHT,
                      PageStart, 1);
       /* Draw a brown mountain rising out of the plain. */
       for (i=0; i<120; i++)
          FillRectangleX(SCREEN_WIDTH/2-30-i, 51+i, SCREEN_WIDTH/2-30+i+1,
                         51+i+1, PageStart, 6);
       /* Draw a yellow sun by overlapping rects of various shapes. */
       for (i=0; i<=20; i++) {
          Temp = (int)(sqrt(20.0*20.0 - (float)i*(float)i) + 0.5);
          FillRectangleX(SCREEN_WIDTH-25-i, 30-Temp, SCREEN_WIDTH-25+i+1,
                         30+Temp+1, PageStart, 14);
       }
       /* Draw green trees down the side of the mountain. */
       for (i=10; i<90; i += 15)
          for (j=0; j<20; j++)
           FillPatternX(SCREEN_WIDTH/2+i-j/3-15, i+j+51,SCREEN_WIDTH/2+i+j/3-15+1,
                                           i+j+51+1, PageStart, PineTreePattern);
       /* Draw a house on the plain. */
       FillPatternX(265, 150, 295, 170, PageStart, BrickPattern);
       FillPatternX(265, 130, 270, 150, PageStart, BrickPattern);
       for (i=0; i<12; i++)
          FillPatternX(280-i*2, 138+i, 280+i*2+1, 138+i+1, PageStart, RoofPattern);
       /* Finally, draw puffs of smoke rising from the chimney. */
       for (i=0; i<4; i++)
          CopySystemToScreenMaskedX(0, 0, SMOKE_WIDTH, SMOKE_HEIGHT, 264,
            110-i*20, SmokePixels, PageStart, SMOKE_WIDTH,SCREEN_WIDTH, SmokeMask);
    }
    /* Move the specified object, bouncing at the edges of the screen and
       remembering where the object was before the move for erasing next time. */
    void MoveObject(AnimatedObject * ObjectToMove) {
       int X, Y;
       X = ObjectToMove->X + ObjectToMove->XDir;
       Y = ObjectToMove->Y + ObjectToMove->YDir;
       if ((X < 0) || (X > (SCREEN_WIDTH - ObjectToMove->Width))) {
          ObjectToMove->XDir = -ObjectToMove->XDir;
          X = ObjectToMove->X + ObjectToMove->XDir;
       }
       if ((Y < 0) || (Y > (SCREEN_HEIGHT - ObjectToMove->Height))) {
          ObjectToMove->YDir = -ObjectToMove->YDir;
          Y = ObjectToMove->Y + ObjectToMove->YDir;
       }
       /* Remember previous location for erasing purposes. */
       ObjectToMove->XOtherPage = ObjectToMove->X;
       ObjectToMove->YOtherPage = ObjectToMove->Y;
       ObjectToMove->X = X; /* set new location */
       ObjectToMove->Y = Y;
    }

Previous | Table of Contents | Next

Graphics Programming Black Book © 2001 Michael Abrash