[comp.graphics] How do you do simple animation on a bitmap display?

cosell@bbn.com (Bernie Cosell) (02/16/89)

I confess that I'm a creature left over from the days of the point-plotting
scopes.  One of the neat things about doing animation on those scopes was
that it mostly came for free (by contrast: doing a STABLE single frame was a
pain, 'cause you had to keep regenerating the display!).  Can anyone give me
any tricks or tips for doing simple animation in the modern world of
bitmapped displays?  I've checked Foley&VanDam and Newman&Sproull and they
seem only to mention screen-flipping.  Is there nothing simpler?   

For example, I was writing a little program to explore the properties of
compound pendulums.  That seemed simple enough, but then I realized: on a
point-plotting scope it would all work more-or-less like magic.  I'd just
plot the position of the bob and the fading phosphors would give me enough of
a "tail" so that I could see the pattern of where it was and where it had
come from.  In fact, the impression of depth you could get essentially for
free was pretty spectacular.  The best I was able to come up with for THIS
guy (although I don't have it working yet) is to use my system's color table.
What I'll do is have a "current color" and I'll go and pump points into the
bitmap with THAT color until a little timer goes off.  THEN: I bump the
"current color". *and* diddle the color mapping table so that "current" is
now the brightest color, "current-1" (the previous current color) is just a
bit more faded, and so on around the palette.  Seems like a kludge, but it
might work!

Am I missing some set of simple tricks?  Are there any reference books that
talk about doing this sort of thing?  Thanks!

   __
  /  )                              Bernie Cosell
 /--<  _  __  __   o _              BBN Sys & Tech, Cambridge, MA 02238
/___/_(<_/ (_/) )_(_(<_             cosell@bbn.com

sfisher@abingdon.SGI.COM (Scott Fisher) (02/17/89)

In article <36049@bbn.COM>, cosell@bbn.com (Bernie Cosell) writes:
> I confess that I'm a creature left over from the days of the point-plotting
> scopes.  One of the neat things about doing animation on those scopes was
> that it mostly came for free (by contrast: doing a STABLE single frame was a
> pain, 'cause you had to keep regenerating the display!).  Can anyone give me
> any tricks or tips for doing simple animation in the modern world of
> bitmapped displays?  I've checked Foley&VanDam and Newman&Sproull and they
> seem only to mention screen-flipping.  Is there nothing simpler?   
> 
> For example, I was writing a little program to explore the properties of
> compound pendulums.  That seemed simple enough, but then I realized: on a
> point-plotting scope it would all work more-or-less like magic.  I'd just
> plot the position of the bob and the fading phosphors would give me enough of
> a "tail" so that I could see the pattern of where it was and where it had
> come from.  In fact, the impression of depth you could get essentially for
> free was pretty spectacular.  The best I was able to come up with for THIS
> guy (although I don't have it working yet) is to use my system's color table.
> What I'll do is have a "current color" and I'll go and pump points into the
> bitmap with THAT color until a little timer goes off.  THEN: I bump the
> "current color". *and* diddle the color mapping table so that "current" is
> now the brightest color, "current-1" (the previous current color) is just a
> bit more faded, and so on around the palette.  Seems like a kludge, but it
> might work!
> 
> Am I missing some set of simple tricks?  Are there any reference books that
> talk about doing this sort of thing?  Thanks!

Color map animation is one way to give the illusion of movement, but
it's complicated.  It is faster, as long as you can remember to 
reload your color maps between retrace--most systems give you a
way to specify this.  If you've ever seen a demo of a program that
swapped color maps in the middle of a retrace instead of between
them, you know why this is a good idea.

The modern approach to animation is, as you say, to clear the screen,
draw an image, clear the screen, draw the new image, etc.  There are
a couple of alternative "tricks," but not necessarily simple, and 
limited in application. 

One thing you might consider for your pendulum application is
whether your system does transformations faster than it fills
the framebuffer, or vice-versa.  If you're animating over a
solid-colored background, you might find it faster to do
something like this.  (Note: this does the simplest possible
thing to manage an x translation so that you can see what
is going on.  It's also limited to the 2D case for the sake of
simplicity, and of course it's a fragment, not a whole program.)

    .
    .
    .
    /* first clear the screen to black */

    color(BLACK);
    clear();

    centerpoint = 1200;

    /* now draw a circle moving R-L; circf draws a filled
       circle at x, y, radius */

    while (centerpoint > 100)
        {
        color (WHITE);     /* next thing drawn is WHITE */
                circf (centerpoint, 130.0, 65.0);
        color (BLACK);     /* effectively erases circle */
                circf (centerpoint, 130.0, 65.0);
        centerpoint = centerpoint - 1;
        }
    .
    .
    .

The advantage to this approach is that what you see is a white
circle moving across a black background, one pixel farther to
the left with each iteration of the while loop.  The idea here 
is that you blank out ONLY the pixels that you have
drawn in the preceding image. Obviously, for your pendulum,
you'd need to calculate x and y coordinates for each iteration
as well as simulating the rate you want, but you know that...

The problem with this is that it shortcuts such niceties as
double-buffering, complex image backgounds, texture-mapped
images behind the thing you're animating, etc.  But this one
does fill the fewest pixels on any given scene.  

Obviously in such a simple example, there is no real performance
benefit to be gained by erasing the object from the preceding 
scene before moving it.  But if you're working in a system that
is fill-rate limited and you have a number of polygons to draw
over a solid-colored background, sometimes you can save a few
cycles by drawing only the pixels that interest you and leaving
the rest of the screen alone.  If you're doing a fairly complex
image transformation on the object you're drawing (rather than 
this ultra-simplistic way to do an x translate), the time you save
in drawing pixels might get lost in the time it takes to 
calculate the transformation matrix.  On the other hand, it
might be the difference between realistic "tactile" response
and a clunky video-game feel.  Only your CPU knows for sure...

BTW, the "correct" way to do this is more like this:

    .
    .
    .
    /* be sure to enable double-buffering in your init. */
    /* by convention, all drawing is done in the "back" */
    /* buffer, the "front" is visible, and swapbuffers  */
    /* exchanges the position (in our GL, anyway)       */

    /* first clear the screen to black */

    color(BLACK);
    clear();

    centerpoint = 1200;

    /* now draw a circle moving R-L; circf draws a filled
       circle at x, y, radius stored in array 'coords'  */

    while (centerpoint > 100)
        {
        color (BLACK);
        clear();           /* erase the screen before drawing */
        color (WHITE);     /* next thing drawn is WHITE       */
        circf (centerpoint, 130.0, 65.0);
	centerpoint = centerpoint - 1;
        swapbuffers();     /* after you've drawn the scene    */
        }
    .
    .
    .

This does draw the entire screen (well, window) each time
you do the while loop, but it does the drawing in the back
buffer while the front buffer is visible (etc.).  

Note also that the examples here are gedankencode--that is,
they're taken from an example that works but modified to demonstrate
the point here, and I haven't compiled (much less run, much less
benchmarked) either one of them (though I have run the program
on which the second example is based & it looks very smooth).
If you yank them and stuff them into your code, don't sue us 
if they don't work the way you expected!:-)