


/* geltools.c */

/* ===========================================================

GELTOOLS.C - 

   A FILE CONTAINING USEFUL SETUP TOOLS FOR THE ANIMATION SYSTEM


   author:  Rob Peck, incorporating valuable comments and changes from
       Barry Whitebook and David Lucas.

============================================================== */
#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <graphics/gels.h>
#include <graphics/clip.h>
#include <graphics/rastport.h>
#include <graphics/view.h>
#include <graphics/gfxbase.h>


/*************************************************************
 * This file is a collection of tools which are used with the vsprite
 * and bob software.  It contains the following:
 *
 * ReadyGels( *gelsinfo, *rastport );
 * PurgeGels( *gelsinfo );
 *
 * struct VSprite *MakeVSprite(lineheight,*image,*colorset,x,y,
 *    wordwidth,imagedepth,flags);       
 * DeleteVSprite( &VSprite );
 *
 * struct Bob *MakeBob(bitwidth,lineheight,imagedepth,*image,
 *       planePick,planeOnOff,x,y)
 * DeleteBob( &Bob );
 *
 * ReadyGels sets up the defaults of the gel system by initializing the
 * GelsInfo structure you provide.  First it allocates room for and
 * links in lastcolor and nextline. It then uses information in your
 * RastPort structure to establish boundary collision defaults at
 * the outer edges of the raster.  It then links together the GelsInfo
 * and the RastPort which you provide. Next it allocates space for two
 * dummy virtual sprite structures, calls InitGels and SetCollision.
 ! You must already have run LoadView before ReadyGels is called.
 *
 * PurgeGels deallocates all memory which ReadyGels and NewGelList have
 * allocated.  The system will crash if you have not used these
 * routines to allocate the space (you cant deallocate something
 * which you havent allocated in the first place).
 *
 * MakeVSprite allocates enough space for and inits a normal vsprite.
 * DeleteVSprite deallocates the memory it used.
 *
 * MakeBob initializes a standard bob and allocates as much memory as is
 * needed for a normal bob and its vsprite structure, links them together.
 * To find the associated vsprite, look at the back-pointer (see the
 * routine doc itself).
 * DeleteBob deallocates the memory it used.
 *
 * Written by Rob Peck, with thanks to Barry Whitebrook and David Lucas.
 *
 ***************************************************************/


void border_dummy()
{ 
   return; 
}

/* Caller passes a pointer to his
 * GelsInfo structure which he wants to init, 
 * along with a pointer to his IVPArgs.
 * Default init places the topmost
 * bottommost etc at the outermost boundaries
 * of callers rastport parameters.
 * Caller can change all this stuff after this routine returns.
 */

extern struct RastPort *myRast;

struct VSprite *SpriteHead = NULL;
struct VSprite *SpriteTail = NULL;

/***************************************************************
 * This routine cannot be run until the first LoadView(&view) has been
 * executed.  InitGels works with an already active View, so LoadView
 * must have been run first.
 */

ReadyGels(g, r)
struct RastPort *r;
struct GelsInfo *g;
{
   /* Allocate head and tail of list. */
   if ((SpriteHead = (struct VSprite *)AllocMem(sizeof
       (struct VSprite), MEMF_PUBLIC | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      kprintf("ReadyGels: No memory for sprite head.\n");
#endif
      return(-1);
   }

   if ((SpriteTail = (struct VSprite *)AllocMem(sizeof
       (struct VSprite), MEMF_PUBLIC | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      kprintf("ReadyGels: No memory for sprite tail.\n");
#endif
      return(-1);
   }

   /* By setting all bits here, it means that there are NO
      * reserved sprites.  The system can freely use all of the 
      * hardware sprites for its own purposes.  The caller will not be
      * trying to independently use any hardware sprites!
      */
   g->sprRsrvd = -1;

   /* The nextline array is used to hold system information about
      * "at which line number on the screen is this hardware sprite
      * again going to become available to be given a new vsprite to
      * display".
      */
   if ((g->nextLine = (WORD *)AllocMem(sizeof(WORD) * 8,
   MEMF_PUBLIC | MEMF_CLEAR)) == NULL) {
#ifdef DEBUG
      kprintf("ReadyGels: No memory for nextline.\n");
#endif
      return(-1);
   }

   /* In the lastcolor pointer array, the system will store
      * a pointer to the color definitions most recently used
      * by the system. .... as a reminder, virtual sprites can
      * be assigned to any of the real hardware sprites which
      * may be available at the time.  The vsprite colors will
      * be written into the hardware sprite register set for
      * the hardware sprite to which that vsprite is assigned.
      * This pointer array contains one pointer to the last
      * set of three colors (from the vsprite structure *sprColors)
      * for each hardware sprite.  
      *
   * As the system is scanning to determine which hardware 
      * sprite should next be used to represent a vsprite, it
      * checks the contents of this array.  If a hardware sprite
      * is available and already has been assigned this set of
      * colors, no color assignment is needed, and therefore
      * no color change instructions will be generated for the
      * copper list.
      *
   * If all vsprites use a different set of sprColors, (pointers
      * to sprColors are different for all vsprites), then there
      * is a limit of 4 vsprites on a horizontal line.  If, on
      * the other hand, you define, lets say 8 vsprites, with
      * 1 and 2 having the same sprColors, 3 and 4 the same as
      * each other, 5 and 6 the same as each other, and 7 and 8
      * also having the same vsprite colors, then you will be
      * able to have all 8 vsprites on the same horizontal line.
      *
   * In this case, you will be able to put all 8 vsprites on
      * the same horizontal line.  The reason this helps is that
      * the system hardware shares the color registers between pairs
      * of hardware sprites.  The system thus has enough resources
      * to assign all vsprites to hardware sprites in that there
      * are 4 color-sets for 8 vsprites, exactly matching the 
      * hardware maximum capabilities.
      *
   * Note that lastcolor will not be used for bobs. Just sprites.
      */
   if ((g->lastColor = (WORD **)AllocMem(sizeof(LONG) * 8,
   MEMF_PUBLIC | MEMF_CLEAR)) == NULL) {
#ifdef DEBUG
      kprintf("ReadyGels: No memory for lastcolor.\n");
#endif
      return(-1);
   }

   /* This is a table of pointers to the routines which should
      * be performed when DoCollision senses a collision.  This
      * declaration may not be necessary for a basic vsprite with
      * no collision detection implemented, but then it makes for
      * a complete example.
      */
   if ((g->collHandler = (struct collTable *)AllocMem(sizeof(struct
       collTable), MEMF_PUBLIC | MEMF_CLEAR)) == NULL) {
#ifdef DEBUG
      kprintf("ReadyGels: No memory for collHandler.\n");
#endif
      return(-1);
   }

   /* When any part of the object touches or passes across
      * this boundary, it will cause the boundary collision
      * routine to be called.  This is at smash[0] in the
      * collision handler table and is called only if
      * DoCollision is called.
      */
   g->leftmost = 0;
   g->rightmost = r->BitMap->BytesPerRow * 8 - 1;
   g->topmost = 0;
   g->bottommost = r->BitMap->Rows - 1;

   r->GelsInfo = g;  /* Link together the two structures */

   InitGels(SpriteHead, SpriteTail, g );

   /* Pointers initialized to the dummy sprites which will be
      * used by the system to keep track of the animation system.
      */
   SetCollision(0, border_dummy, g);
   WaitTOF();
   return(0);
}

/*****************************************************************
 * Use this to get rid of the gels stuff when it is not needed any more.
 * more.  You must have allocated the gels info stuff
 * (use the ReadyGels routine).
 */

PurgeGels(g)
struct GelsInfo *g;
{
   if (g->collHandler != NULL)
      FreeMem(g->collHandler, sizeof(struct collTable));
   if (g->lastColor != NULL)
      FreeMem(g->lastColor, sizeof(LONG) * 8);
   if (g->nextLine != NULL)
      FreeMem(g->nextLine, sizeof(WORD) * 8);
   if (g->gelHead != NULL)
      FreeMem(g->gelHead, sizeof(struct VSprite));
   if (g->gelTail != NULL)
      FreeMem(g->gelTail, sizeof(struct VSprite));
}

/**************************************************************
 * Because MakeVSprite is called by MakeBob,
 * MakeVSprite only creates the
 * VSprite,it doesn't add it to the system list.
 * The calling routine must
 * do an AddVSprite after it is created.
 */

struct VSprite *MakeVSprite(lineheight, image, x, y,
wordwidth, imagedepth, flags)
SHORT lineheight;   /* How tall is this vsprite? */
WORD *image;      /* Where is the vsprite image data, should be
            twice as many words as the value of lineheight */
SHORT x, y;      /* What is its initial onscreen position? */
SHORT wordwidth, imagedepth, flags;
{
   struct VSprite *v;   /* Make a pointer to the vsprite structure which
               this routine dynamically allocates */

   if ((v = (struct VSprite *)AllocMem(sizeof(struct VSprite),
   MEMF_PUBLIC | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      printf("MakeVSprite: Couldn't allocate VSprite.\n");
#endif
      return(0);
   }

   v->Flags = flags;   /* Is this a vsprite, not a bob? */

   v->Y = y;      /* Establish initial position relative to */
   v->X = x;      /* the Display coordinates. */

   v->Height = lineheight;   /* The Caller says how high it is. */
   v->Width = wordwidth; /* A vsprite is always 1 word (16 bits) wide.*/

   /* There are two kinds of depth... the depth of the image itself,
      and the depth of the playfield into which it will be drawn.
      The image depth says how much data space will be needed
      to store an image if it's dynamically allocated.
      The playfield depth establishes how much space
      will be needed to save and restore the background when
      a bob is drawn. A vsprite is always 2 planes deep,
      but if it's being used to make a bob, it may be deeper...
      */

   v->Depth = imagedepth;

   /* Assume that the caller at least has a default boundary
      collision routine...bit 1 of this mask is reserved for
      boundary collision detect during DoCollision(). The only
      collisions reported will be with the borders.
      The caller can change all this later.
      */

   v->MeMask = 1;
   v->HitMask = 1;

   v->ImageData = image;   /* Caller says where to find the image. */

   /* Show system where to find a mask which is a squished down
      * version of the vsprite (allows for fast horizontal border
      * collision detect).
      */

   if ((v->BorderLine = (WORD *)AllocMem((sizeof(WORD)*wordwidth),
   MEMF_PUBLIC | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      kprintf("MakeVSprite: Couldn't allocate BorderLine.\n");
#endif
      return(0);
   }

   /* Show system where to find the mask which contains a 1 bit for any
      * position in the object in any plane where there
      * is a 1 bit (all planes OR'ed together).
      */

   if ((v->CollMask = (WORD *)AllocMem(sizeof(WORD)*lineheight*wordwidth,
   MEMF_CHIP | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      kprintf("MakeVSprite: Couldn't allocate CollMask.\n");
#endif
      return(0);
   }


   /* These aren't used for a VSprite, and MakeBob'll do set up for Bob. */
   v->PlanePick = 0x00;
   v->PlaneOnOff = 0x00;

   InitMasks(v);   /* Create the collMask and borderLine */

   return(v);
}

struct Bob *MakeBob(bitwidth,lineheight,imagedepth,image,
planePick,planeOnOff, x,y, flags)
SHORT bitwidth,lineheight,imagedepth,planePick,planeOnOff,x,y,flags;
WORD *image;
{
   struct Bob *b;
   struct VSprite *v;
   SHORT wordwidth;

   wordwidth = (bitwidth+15)/16;

   /* Create a vsprite for this bob, it will need to be deallocated
      * later (freed) when this bob gets deleted.
      * Note: No color set for bobs.
      */
   if ((v = MakeVSprite(lineheight, image, NULL, x, y, wordwidth,
   imagedepth, flags)) == 0) {
#ifdef DEBUG
      kprintf("MakeBob: MakeVSprite failed.\n");
#endif
      return(0);
   }

   /* Caller selects which bit planes into which the image is drawn. */
   v->PlanePick = planePick;

   /* What happens to the bit planes into which the image is not drawn. */
   v->PlaneOnOff = planeOnOff;

   if ((b = (struct Bob *)AllocMem(sizeof(struct Bob),
   MEMF_PUBLIC | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      kprintf("MakeBob: Couldn't allocate bob.\n");
#endif
      return(0);
   }

   v->VSBob = b; /* Link together the bob and its vsprite structures */

   b->Flags = 0; /* Not part of an animation (BOBISCOMP) and don't keep the
            image present after bob is removed (SAVEBOB) */


   /* Tell where to save background. Must have enough space for as many
      * bitplanes deep as the display into which everything is
         being drawn.      */

   if ((b->SaveBuffer = (WORD *)AllocMem(sizeof(SHORT) * wordwidth
       * lineheight * imagedepth, MEMF_CHIP | MEMF_CLEAR)) == 0) {
#ifdef DEBUG
      kprintf("MakeBob: Couldn't allocate save buffer.\n");
#endif
      return(0);
   }

   b->ImageShadow = v->CollMask;

   /* Interbob priorities are set such that the earliest defined bobs
      * have the lowest priority, last bob defined is on top.
      */

   b->Before = NULL;   /* Let the caller worry about priority later. */
   b->After = NULL;

   b->BobVSprite = v;

   /* InitMasks does not preset the imageShadow ... caller may elect to
      * use the collMask or to create his own version of a shadow,
      * although it is usually the same.
      */

   b->BobComp = NULL;   /* this is not part of an animation */
   b->DBuffer = NULL;   /* this is not double buffered */

   /* Return a pointer to this newly created bob for additional caller
      * interaction or for AddBob(b);
      */
   return(b);
}

/* Deallocate memory which has been allocated by the routines Makexxx. */
/* Assumes images and imageshadow deallocated elsewhere. */
DeleteGel(v)
struct VSprite *v;
{
   if (v != NULL) {
      if (v->VSBob != NULL) {
         if (v->VSBob->SaveBuffer != NULL) {
            FreeMem(v->VSBob->SaveBuffer, sizeof(SHORT) * v->Width
                * v->Height * v->Depth);
         }
         if (v->VSBob->DBuffer != NULL) {
            if (v->VSBob->DBuffer->BufBuffer != 0) {
               FreeMem(v->VSBob->DBuffer->BufBuffer,
               sizeof(SHORT) * v->Width * v->Height * v->Depth);
            }
            FreeMem(v->VSBob->DBuffer, sizeof(struct DBufPacket));
         }
         FreeMem( v->VSBob, sizeof(struct Bob));
      }
      if (v->CollMask != NULL) {
         FreeMem(v->CollMask, sizeof(WORD) * v->Height * v->Width);
      }
      if (v->BorderLine != NULL) {
         FreeMem(v->BorderLine, sizeof(WORD) * v->Width);
      }
      FreeMem(v, sizeof(struct VSprite));
   }
}


