[comp.sys.mac.programmer] Animation Questions

leue@galen.crd.ge.com (Bill Leue) (10/12/89)

I've got a couple of questions regarding animation on the Mac.  I know
that this material has been discussed before in this group, but I didn't 
get all of it the first time.  Please, no flames if you can restrain
yourselves :-).

I'm moving some fairly small (app. 100 pixels wide x 20 pixels high)
sprites around using what I think is the standard method:  copying the
background to an offscreen bitmap, drawing the new position of the object
in the offscreen bitmap, and CopyBits-ing the whole thing back to the
screen.  It works pretty well, and eliminates the obnoxious flickering
that characterized my first, naive efforts to do animation using XOR
drawing.

However, there are still two glitches.  First, the objects being animated
are sometimes "sheared" horizontally, so that they are torn into two
pieces which don't quite track each other as the object moves (say)
horizontally.  I'm tenatively diagnosing this problem as a lack of
synchronization with the screen refresh.  Am I right?  If so, I suspect
there's a mechanism to synchronize drawing with vertical blanking.  Does
anyone have any lore (or posssibly code) they feel like sharing on this
subject?

Second, I am using "Classic" QuickDraw to perform limited color drawing.
I use ForeColor() and BackColor() calls along with a 50% grey pattern fill
to get a useful range of colors using dithering.  In this way I can
get color objects on a color system, but don't have to make special code
for  monochrome systems.  It works fine, except that the objects which
are animated using CopyBits only display in black and white.  I guess I
thought that since I was using the same QD calls to write to the offscreen
bitmap that I used to write the "static" portions of the screen, the
objects would also show up in color.  Wrong!  Anyone have an idea what I'm
doing wrong?  Remember, I am NOT using Color QD at all -- just the 8
colors in normal QD.

Thanks!
-Bill Leue
leue@crd.ge.com

brendan@claris.com (Brendan McCarthy) (10/13/89)

>In Article 10142 in comp.sys.mac.programmer:
>leue@galen.crd.ge.com (Bill Leue) writes:

>First, the objects being animated are sometimes "sheared" horizontally, so 
>that they are torn into two pieces which don't quite track each other as the 
>object moves (say)horizontally.  
>I'm tenatively diagnosing this problem as a lack of synchronization with the 
>screen refresh.  Am I right?  

Yes, you are right.  There's a cheap way to synchronize with the VBL.  It works
well for pretty simple drawing tasks (like CopyBits).  
Here's a code fragment:

    VAR theTicks: LONGINT;

    BEGIN  { your screen drawing routine }
       ...
       { sync with VBL task }
       theTicks := TickCount;
       REPEAT UNTIL theTicks <> TickCount;
       { do your drawing now }
       ...
    END;  { your drawing routine }


>Second, I am using "Classic" QuickDraw to perform limited color drawing.
>I use ForeColor() and BackColor() calls along with a 50% grey pattern fill
>to get a useful range of colors using dithering.  In this way I can
>get color objects on a color system, but don't have to make special code
>for  monochrome systems.  It works fine, except that the objects which
>are animated using CopyBits only display in black and white.

Right.  The reason you're getting black & white is that CopyBits only moves the
bits (that's its name).  Classic QD doesn't store colour information anywhere.
It's part of the port's drawing mode. 
In order to get colour from CopyBits using old QD, you'll have to SetForeColor 
on the CopyBits call that draws from your offscreen bitmap to the screen.  

I'm assuming that you currently draw the different coloured portions of your
image into one offscreen bitmap, and then CopyBits that to the screen.  To get
the effect you want, you'll need to create an offscreen bitmap for each colour
of your image, and then do the ForeColor/CopyBits thing on to the screen.

Brendan
        :
 CLARIS :
........:....................................................................
        :
        :        Brendan McCarthy 
        :        Software Architect
        :        Planning Applications
        :        UUCP:      brendan@claris.com
        :        InterNet:  {ames,apple,portal,sun,voder}!claris!brendan
        :

mnkonar@gorby.SRC.Honeywell.COM (Murat N. Konar) (10/13/89)

In article <10611@claris.com= brendan@claris.com (Brendan McCarthy) writes:
==In Article 10142 in comp.sys.mac.programmer:
==leue@galen.crd.ge.com (Bill Leue) writes:
=
==First, the objects being animated are sometimes "sheared" horizontally, so 
==that they are torn into two pieces which don't quite track each other as the 
==object moves (say)horizontally.  
==I'm tenatively diagnosing this problem as a lack of synchronization with the 
==screen refresh.  Am I right?  
=
=Yes, you are right.  There's a cheap way to synchronize with the VBL.  It works
=well for pretty simple drawing tasks (like CopyBits).  
=Here's a code fragment:
=
=    VAR theTicks: LONGINT;
=
=    BEGIN  { your screen drawing routine }
=       ...
=       { sync with VBL task }
=       theTicks := TickCount;
=       REPEAT UNTIL theTicks <= TickCount;
=       { do your drawing now }
=       ...
=    END;  { your drawing routine }

I beleive that the above code fragment will only eliminate flicker on Mac Pluses
SEs and  maybe SE30s.  MacII variants may have monitors that have a vertical 
blanking interval that is different than the normal 1/60 sec.  So if you synch
up with the Tick counter, you are still out of synch with the Monitor's vertical
retrace.  The solution appears to be to figure out which monitor you are drawing on
(easy, see the chapter on GDevices in IM vol V), figure out what slot that monitor's
card is in (don't know how to do that), and then synch up with that slots VBL queue
(info for that puzzle is buried in IM vol V also, but not in digestible form).

I too am trying to figure out how to do smooth animation on Mac IIs so if anyone
out there has got the details figured out, let me know.

thanks.
____________________________________________________________________
Have a day. :^|
Murat N. Konar        Honeywell Systems & Research Center, Camden, MN
mnkonar@SRC.honeywell.com (internet) {umn-cs,ems,bthpyd}!srcsip!mnkonar(UUCP)

jackiw@cs.swarthmore.edu (Nick Jackiw) (10/13/89)

In article <3123@crdgw1.crd.ge.com> leue@galen.crd.ge.com (Bill Leue) writes:
>  [Discussion of offscreen animating]
> However, there are still two glitches.  First, the objects being animated
> are sometimes "sheared" horizontally, so that they are torn into two
> pieces which don't quite track each other as the object moves (say)
> horizontally.  I'm tenatively diagnosing this problem as a lack of
> synchronization with the screen refresh.  Am I right?

Yes.

>  If so, I suspect
> there's a mechanism to synchronize drawing with vertical blanking.  Does
> anyone have any lore (or posssibly code) they feel like sharing on this
> subject?

There are two cases.  On the MAC 128, 128K/E, Plus, and SE, it's simple.
The "tick counter" is installed as a VBL task in the screen's retrace
interval.  Consequently, waiting for the tickCount to change guarantees
you're in the retrace interval.  Code would look like this:

var junk:longint;
...
	Delay(1,x);	{Wait for next tick-count}
	CopyBits(...); 	{Start drawing as fast as you can}

If you are drawing multiple objects per screen refresh, draw the ones 
closer to the top of the screen first. This helps keep your drawing 
"in front of" the electron gun.

On the case of Macs with external video cards, the tick-count is not
synchronized to your video redraw.  Instead you'll have to make your
own VBL task (which merely sets a flag visible to your application meaning
"Draw now!") and install it in the VBL queue of the appropriate video
card.  E-mail for more details on this.

> 
> Second, I am using "Classic" QuickDraw to perform limited color drawing.
> I use ForeColor() and BackColor() calls along with a 50% grey pattern fill
> to get a useful range of colors using dithering.  In this way I can
> get color objects on a color system, but don't have to make special code
> for  monochrome systems.  It works fine, except that the objects which
> are animated using CopyBits only display in black and white.  I guess I
> thought that since I was using the same QD calls to write to the offscreen
> bitmap that I used to write the "static" portions of the screen, the
> objects would also show up in color.  Wrong!  Anyone have an idea what I'm
> doing wrong?  Remember, I am NOT using Color QD at all -- just the 8
> colors in normal QD.

You aren't doing anything wrong; you've come across a limitation of the
(incredibly farseeing IMHO) original QD coloring scheme.  While computers
with CQD are capable of *drawing* any color they choose into your on-screen
window, the bitmap in which the image is saved (i. e. yourPort^.portBits)
is still only one-bit-deep (i. e. capable of expressing at most two colors).
Consequently, any CopyBits(source->dest) call in which source is a 1-bit
(old style) grafport can only interpret that image's data in terms of
forecolor (bit=0) or backcolor (bit=1), regardless of how many different
forecolors and backcolors were used in composing it.

To overcome this, you'll need to go to a deeper bitmap, and get into true
ColorQuickdraw.  Alternately, you could create *separate* off-screen
bitmaps for each "color separation" of your final object, and copyBits them
(with a mask) on top of each other (changing forecolor+backcolor appropriately
between calls) in your destination window.

> Thanks!
Hope it works. Is it, by chance, a *game?*  God knows Macs could use more
good animation games.

> -Bill Leue
> leue@crd.ge.com


-- 
     _  _|\____    Nick Jackiw | Visual Geometry Project | Math Department
   / /_/   O>  \   ------------+-------------------------+ Swarthmore College
   |       O>   |  215-328-8225| jackiw@cs.swarthmore.edu| Swarthmore PA 19081
    \_Guernica_/   ------------+-------------------------+                 USA

ogden@nmsu.edu (Bill Ogden) (10/14/89)

In article <34698@srcsip.UUCP> mnkonar@gorby.SRC.Honeywell.COM (Murat N. Konar) writes:


>I beleive that the above code fragment will only eliminate flicker on Mac Pluses
>SEs and  maybe SE30s.  MacII variants may have monitors that have a vertical 
>blanking interval that is different than the normal 1/60 sec.  So if you synch
>up with the Tick counter, you are still out of synch with the Monitor's vertical
>retrace.  The solution appears to be to figure out which monitor you are drawing on
>(easy, see the chapter on GDevices in IM vol V), figure out what slot that monitor's
>card is in (don't know how to do that), and then synch up with that slots VBL queue
>(info for that puzzle is buried in IM vol V also, but not in digestible form).

I use GetVideoDefault to find the slot (but may NOT be the device
currently being written to!) and then set up a dummy (do nothing) 
VBL task which decrements the vblCount every vertical blank period. It
seems to work.
Here's how I do it:

VBLQElPtr 		vtrace;	/* global vertical blank task pointer */
int			vSlot;	/* global for video slot */

InstallRetracer()	/* installs the dummy vbl task in default slot */
{
	DefVideoRec	defaultScreen;

	GetVideoDefault(&defaultScreen);
	vSlot = defaultScreen.sdSlot;
  	vtrace =(VBLQElPtr) NewPtr(sizeof(VBLTask)); 
	vtrace->qType = vType;
	vtrace->vblAddr = DummyVBLTask;
	vtrace->vblCount = 3000;    /* count can be anything */
	vtrace->vblPhase = 0;
	SlotVInstall(vtrace,vSlot);
}

waitForSync()  /* call this to wait for the next vertical blank peroid */

{	int		wait;

	wait = vtrace->vblCount;
	while (vtrace->vblCount == wait); 
}

Be sure and do a SlotVRemove call when you done !!
This will only work on MacII's or later.


Bill Ogden  ogden@nmsu.edu
Computing Research Lab / Psychology Dept
New Mexico State University

fry@brauer.harvard.edu (D. Spotts Fry) (10/14/89)

To do synched drawing on a Mac II, you want to use
a technique analagous to that of watching TickCount()
on the MacPlus, but you have to make your own VBL
counter.  This should be done with VBLTask tied
to the video slot so you really get video synching.

To illustrate this, here is some code (in Think C):

                Dave
 
/*  my globals  */
extern long     VBLcounter;
extern short    videoSlot;
extern VBLTask  drawtask;

/****************************************************************

    Before we do anything, we have to know which slot the
    card is in.  Use this routine.  (See Start Manager in
    IM V.)

*/

FindVideoSlot()
{
    DefVideoRec defvidrec;

    GetVideoDefault(&defvidrec);
    videoSlot = defvidrec.sdSlot;
}

/*******************************************************************

    Then we have to install a VBL Task.  This is the one
    I'll use.  To use global variables you must setup the
    A5 register.  To do this in MultiFinder requires
    a special trick, which is demonstrated with the
    CacheA5 function.

*/

pascal void ScreenDrawTask()
{
/*
*   WARNING! - this is self-modifying code, but it is
*   necessary to run under MultiFinder...see Tech Note 180
*/
    long    oldA5;

    asm {
        move.l  a5,oldA5
        move.l  #0,a5   ; this will be replaced with
                        ; CurrentA5 by the CacheA5 function at runtime
    }
    VBLcounter += 1;
    drawtask.vblCount = 1;
    asm {
        move.l  oldA5,a5
    }
}

pascal void CacheA5(addr)
long    addr;
/*
*   This function will place CurrentA5 into the beginning of task
*   functions which start as:
*   pascal void FooTask()
*   {
*       long    junk;
*
*       asm {
*           move.l  a5,junk
*           move.l  #0,a5   ;this will be replaced by CurrentA5
*           ....
*
*   See Tech Note #180
*/
{
        asm {
            move.l  addr,a0
            add.w   #2,a0   ; move past JMP instruction
            move.l  (a0),a0
            add.w   #0xa,a0 ; move past first part of function
            move.l  CurrentA5,(a0)
                            ; puts CurrentA5 in (*addr)() dc.l location
    }
}


/****************************************************************

    This function actually sets up the VBL Task.

*/

StartCounter()
{
    CacheA5(ScreenDrawTask);
    VBLcounter = 0;
    drawtask.vblAddr = ScreenDrawTask;
    drawtask.qType = vType;
    drawtask.vblCount = 1;
    drawtask.vblPhase = 0;
    SlotVInstall(&drawtask,videoSlot);
}


/*********************************************************************

    This function removes the VBL Task.

*/

RemoveCounter()
{
    SlotVRemove(&drawtask,videoSlot);
}


/*********************************************************************

    This function holds everything until it's
    time to draw. Note that it could be replaced by
    a #define macro (make VBLjunk a global).

*/

WAIT_FOR_SCREEN()
{
    long VBLjunk;

    VBLjunk = VBLcounter;
    do {} while ( VBLcounter == VBLjunk );
    /* this is used to synch drawing to screen */
}

/********************************************************************

    Now, putting this altogether into a program

*/


main()
{

    /* do your init stuff */

    FindVideoSlot();
    StartCounter();

    /* now, whenever you want to draw something, do this... */
    WAIT_FOR_SCREEN();
    DrawMyThing();

    /* when you're done...*/

    RemoveCounter();

}