/* ---------------------------- S p i n 3 . c ------------------------------ */
/*  This is a simple routine that creates some spinning cubes and transforms */
/*  them into op-art.  It is a good example of how to take total control of  */
/*  of the screen for graphics and create a double buffered display (with    */
/*  color tables that can be changed!)                                       */
/*  I place it in the public domain so long as no one uses it for profit and */
/*  these comments remain with the source code.                              */
/*      Ronald Peterson     PeopleLink:  OPS448   BIX: rpeterson             */
/*---------------------------------------------------------------------------*/

/*--- INCLUDE's ------------------------------------------------------------ */

#include "stdio.h"
#include "math.h"
#include "limits.h"
#include "exec/types.h"
#include "graphics/gfx.h"
#include "graphics/gfxmacros.h"
#include "graphics/copper.h"
#include "graphics/view.h"
#include "graphics/regions.h"
#include "graphics/clip.h"
#include "exec/exec.h"
#include "graphics/gfxbase.h"
#include "graphics/rastport.h"
#include "hardware/custom.h"
#include "hardware/dmabits.h"

/*--- DEFINE's --------------------------------------------------------------*/

#define DEPTH 4
#define COLORS 16    /* COLORS = 2**DEPTH */
#define WIDTH 320
#define HEIGHT 200
#define NOT_ENOUGH_MEMORY -1000

/*--- GLOBAL variable definitions -------------------------------------------*/

struct View v;
struct ViewPort vp;
struct ColorMap *cm;  /* pointer to colormap structure, dynamic alloc */
struct RasInfo ri;
struct BitMap b;  /* note:  Due to the static allocation of a structure
                   * accessed directly by the custom chips, this program
                   * will only work if it resides entirely within the lower
                   * 512k bytes of memory (CHIP memory)
                   */
struct BitMap b2;  /* second bitmap for double buffering */
struct cprlist *lof1;  /* copper list pointers for double buffer */
struct cprlist *lof2;
struct cprlist *shf1;
struct cprlist *shf2;
struct CopList *Disp1, *Disp2;
extern int Enable_Abort;

/*--- RastPort variables ----------------- */

PLANEPTR rastpoint, rastpoint2;

struct RastPort rp;
struct RastPort rp2;

WORD areabuffer[250];
struct TmpRas tmpras;
struct AreaInfo myAreaInfo;

WORD areabuffer2[250];
struct TmpRas tmpras2;
struct AreaInfo myAreaInfo2;

/*--- My global variables */

extern struct Custom custom;  /* so that graphics macros work.  OFF_SPRITE */

extern struct ColorMap *GetColorMap();
struct GfxBase *GfxBase;
struct View *oldview;  /* save pointer to old view so can restore */

USHORT colortable[COLORS] = {0x0000, 0x0222,
      0x0444, 0x0555, 0x0666, 0x0777, 0x0888, 0x0999, 0x0AAA, 0x0BBB, 0x0CCC,
      0x0DDD, 0x0EEE, 0x0FFF, 0x0005, 0x000A};
     /* set colors shades of white, Light blue, Medium blue */
     /*                  0-13          14            15   */
USHORT j, tmp;
UBYTE *displaymem;
UWORD *colorpalette;

/*--- Start of main routine -------------------------------------------------*/

main()
{
SHORT bx[30], by[30], bz[30], dx[30], dy[30], dz[30];
SHORT size[30], xminus, xplus, yminus, yplus;
SHORT cx, cy, cz, half, nobj;
LONG i;
SHORT k, icol[30], icol2, xhalf, dxhalf, ncol;
static SHORT icl[8] = {256, 16, 1, 4096, -256, -16, -1 ,-4096};
static float scl[7] = {1.0, 1.138, 1.276, 1.414, 1.276, 1.138, 1.0};

/*--- Open graphics and Intuition libraries */

   GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0);
   if(GfxBase == NULL)exit(1);
   oldview = GfxBase->ActiView;         /* Save current view to restore later */
                                        /* Steals screen from Intuition if    */
                                        /* started from WorkBench.            */

/*--- Initialize graphics structures and create double buffered display. */

   OFF_SPRITE;        /* turn off pointer (sprites off) */
   InitView(&v);      /* initialize view */
   InitVPort(&vp);    /* initialize viewport */
   v.ViewPort = &vp;  /* link view into viewport */

   InitBitMap(&b,DEPTH,WIDTH,HEIGHT);  /* Init two bitmaps for double        */
   InitBitMap(&b2,DEPTH,WIDTH,HEIGHT); /* buffering (for RasInfo & RastPort) */

   ri.BitMap = &b;         /* Init RasInfo structure */
   ri.RxOffset = 0;
   ri.RyOffset = 0;
   ri.Next = NULL;

   vp.DWidth = WIDTH;      /* Specify ViewPort characteristics. */
   vp.DHeight = HEIGHT;
   vp.RasInfo = &ri;

   cm = GetColorMap(COLORS); /* Init color table (16 slots since 4 planes deep */
   vp.ColorMap = cm;       /* Set ViewPort pointer to my colors */
   LoadRGB4(&vp,colortable,COLORS);  /* Set viewport colors */

   for(i=0; i<DEPTH; i++)  /* Allocate RAM for bitmaps. */
   {
      b.Planes[i] = (PLANEPTR)AllocRaster(WIDTH,HEIGHT);
      if(b.Planes[i] == NULL)exit(NOT_ENOUGH_MEMORY);
      b2.Planes[i] = (PLANEPTR)AllocRaster(WIDTH,HEIGHT);
      if(b2.Planes[i] == NULL)exit(NOT_ENOUGH_MEMORY);
   }

   ri.BitMap = &b;
   MakeVPort(&v,&vp);      /* construct copper instr (prelim) list     */
   MrgCop(&v);             /* merge prelim lists together into a real  */
                           /* copper list in the view structure        */
   lof1 = v.LOFCprList;    /* store pointers to copper lists for bitmap 1 */
   shf1 = v.SHFCprList;
   Disp1 = v.ViewPort->DspIns; /* To get color map to change properly */

   v.LOFCprList = 0;
   v.SHFCprList = 0;
   v.ViewPort->DspIns = 0;

   ri.BitMap = &b2;
   MakeVPort(&v,&vp);
   MrgCop(&v);
   lof2 = v.LOFCprList;    /* store pointers to copper lists for bitmap 2 */
   shf2 = v.SHFCprList;
   Disp2 = v.ViewPort->DspIns;

   LoadView(&v);           /* tell copper to use list for display */

   InitRastPort(&rp);      /* Initialize first RastPort   */
   rp.BitMap = &b;         /* link rastport to bitmap 1   */
   SetDrMd(&rp,JAM1);      /* set draw mode to JAM1       */

   InitArea(&myAreaInfo, areabuffer, 100);   /* Vertice buffer for Fill */
   rp.AreaInfo = &myAreaInfo;

   rastpoint = (PLANEPTR)AllocRaster(WIDTH,HEIGHT);
   InitTmpRas(&tmpras, rastpoint, RASSIZE(WIDTH,HEIGHT));
   rp.TmpRas = &tmpras;

   InitRastPort(&rp2);   /* Do same for second RastPort */
   rp2.BitMap = &b2;     /* link rastport to bitmap     */
   SetDrMd(&rp2,JAM1);   /* set draw mode to JAM1       */

   InitArea(&myAreaInfo2, areabuffer2, 100);  /* Vertice buffer for Fill */
   rp2.AreaInfo = &myAreaInfo2;

   rastpoint2 = (PLANEPTR)AllocRaster(WIDTH,HEIGHT);
   InitTmpRas(&tmpras2, rastpoint2, RASSIZE(WIDTH,HEIGHT));
   rp2.TmpRas = &tmpras2;

/*--- Set outline colors and clear bitmaps. */

   SetOPen(&rp,0);       /* set outline color */
   SetOPen(&rp2,0);      /* set outline color */
   Enable_Abort = 0;     /* disable automatic CTRL-C abort */
   SetRast(&rp2,0);      /* clear bitmap 2 */
   SetRast(&rp,0);       /* clear bitmap 1 */

/*--- Set up positions, sizes, and directions for balls. */

   for(i=0; i<30; i++)
   {
      bx[i] = RangeRand(239) + 40;  /* pick an initial location inside */
      by[i] = RangeRand(119) + 40;  /* the clipping range.             */
      bz[i] = RangeRand(239) + 40;
      dx[i] = RangeRand(30) - 15;
      dy[i] = RangeRand(30) - 15;
      dz[i] = RangeRand(30) - 15;
      if(dx[i] == 0)dx[i] = 10;     /* zero dx not allowed */
      if(dy[i] == 0)dy[i] = 10;
      if(dz[i] == 0)dz[i] = 10;
      size[i] =  (319-bz[i])/12+ 1;      /* ball size is a function of z */
   }

   for(k=0; k<30; k++)    /* Initialize cube parameters */
   {
      icol[k] = RangeRand(6) + 7;
   }

/*--- Swap buffers, alternately drawing into each */

   ncol = 0;
   nobj = 3;
   for(i=0; i<100000; i++)
   {
      if(0 != Chk_Abort())   /* check for CTRL-C abort */
      {
         goto dats_all;
      }

      v.LOFCprList = lof1;   /* set pointers to copper lists and */
      v.SHFCprList = shf1;   /* set RastPort bitmap pointer      */
      v.ViewPort->DspIns = Disp1;
      WaitBOVP(&vp);         /* wait till in vertical blank area */
      LoadView(&v);          /* display bitmap 1 */
      if(i < 100)SetRast(&rp2,0);       /* clear bitmap 2 */

      if(i > 250)
      {
         tmp = colortable[0];   /* rotate colors */
         for(j=0; j<15; j++)
         {
            colortable[j] = colortable[j+1];
         }
         colortable[15] = tmp;
      }
      if(i > 200)ncol = RangeRand(15);
      colortable[ncol] = (colortable[ncol] + icl[RangeRand(8)]) & 0x0FFF;
      if(i > 150)LoadRGB4(&vp,colortable,COLORS);  /* Set viewport colors */

      if(i < 200)
      {
         SetAPen(&rp2,14);       /* Set wall color */
         AreaMove(&rp2, 0, 0);
         AreaDraw(&rp2, 0, 199);
         AreaDraw(&rp2, 80, 150);
         AreaDraw(&rp2, 80, 50);
         AreaEnd(&rp2);

         SetAPen(&rp2,11);       /* Set wall color */
         AreaMove(&rp2, 319, 0);
         AreaDraw(&rp2, 319, 199);
         AreaDraw(&rp2, 240, 150);
         AreaDraw(&rp2, 240, 50);
         AreaEnd(&rp2);

         SetAPen(&rp2,15);       /* Set floor/ceiling color */
         AreaMove(&rp2, 0, 0);
         AreaDraw(&rp2, 80, 50);
         AreaDraw(&rp2, 240, 50);
         AreaDraw(&rp2, 319, 0);

         AreaMove(&rp2, 0, 199);
         AreaDraw(&rp2, 80, 150);
         AreaDraw(&rp2, 240, 150);
         AreaDraw(&rp2, 319, 199);
         AreaEnd(&rp2);
      }

/*      if(i < 150)     /* start with one object, then go to three */
         nobj = 3;
      else
         nobj = 10;
*/
      for(k=0; k<nobj; k++)     /* move and draw balls */
      {
         bx[k] = bx[k] + dx[k];   /* increment positions. */
         by[k] = by[k] + dy[k];
         bz[k] = bz[k] + dz[k];
         size[k] = (319-bz[k])/12 + 1;

	 if(bx[k]+size[k] > 319) /* clip & change direction. */
         {
            bx[k] = 319 - size[k];
            dx[k] = RangeRand(10) - 11;  /* set negative dx */
         }
	 if(by[k]+size[k] > 199)
         {
            by[k] = 199 - size[k];
            dy[k] = RangeRand(10) - 11;
         }
	 if(bz[k] > 319)
         {
            bz[k] = 319;
            dz[k] = RangeRand(10) - 11;
         }
	 if(bx[k]-size[k] < 0)
         {
            bx[k] = size[k];
            dx[k] = RangeRand(10) + 1;   /* set positive dx */
         }
	 if(by[k]-size[k] < 0)
         {
            by[k] = size[k];
            dy[k] = RangeRand(10) + 1;
         }
	 if(bz[k] < 20)
         {
            bz[k] = 20;
            dz[k] = RangeRand(10) + 1;
         }

         cx = bx[k];
         cy = by[k];
         cz = bz[k];

         perspective(&cx, &cy, &cz);  /* do perspective xform */

         half = size[k]/2;
         icol[k] = icol[k] - 1;
         if(icol[k] < 7)icol[k] = 13;
         icol2 = icol[k] - 7;
         dxhalf = scl[icol2]*half;
         xhalf = cx - dxhalf + icol2*dxhalf/3;
         xminus = cx-dxhalf;
         xplus = cx+dxhalf;
         yminus = cy-half;
         yplus = cy+half;

         SetAPen(&rp2,icol2);         /* Set color to show rotation */
         AreaMove(&rp2, xminus, yminus);
         AreaDraw(&rp2, xhalf, yminus);
         AreaDraw(&rp2, xhalf, yplus);
         AreaDraw(&rp2, xminus, yplus);
         AreaEnd(&rp2);

         SetAPen(&rp2,icol[k]);         /* Set color to show rotation */
         AreaMove(&rp2, xhalf, yminus);
         AreaDraw(&rp2, xplus, yminus);
         AreaDraw(&rp2, xplus, yplus);
         AreaDraw(&rp2, xhalf, yplus);
         AreaEnd(&rp2);
      }

      v.LOFCprList = lof2;   /* set pointers to copper lists and */
      v.SHFCprList = shf2;   /* set RastPort bitmap pointer      */
      v.ViewPort->DspIns = Disp2;
      WaitBOVP(&vp);         /* wait till in vertical blank area */
      LoadView(&v);          /* display bitmap 2 */
      if(i < 100)SetRast(&rp,0);        /* clear bitmap 1 */

      if(i > 150)LoadRGB4(&vp,colortable,COLORS);  /* Set viewport colors */

      if(i < 200)
      {
         SetAPen(&rp,14);       /* Set wall color */
         AreaMove(&rp, 0, 0);
         AreaDraw(&rp, 0, 199);
         AreaDraw(&rp, 80, 150);
         AreaDraw(&rp, 80, 50);
         AreaEnd(&rp);

         SetAPen(&rp,11);       /* Set wall color */
         AreaMove(&rp, 319, 0);
         AreaDraw(&rp, 319, 199);
         AreaDraw(&rp, 240, 150);
         AreaDraw(&rp, 240, 50);
         AreaEnd(&rp);

         SetAPen(&rp,15);       /* Set floor color */
         AreaMove(&rp, 0, 0);
         AreaDraw(&rp, 80, 50);
         AreaDraw(&rp, 240, 50);
         AreaDraw(&rp, 319, 0);

         AreaMove(&rp, 0, 199);
         AreaDraw(&rp, 80, 150);
         AreaDraw(&rp, 240, 150);
         AreaDraw(&rp, 319, 199);
         AreaEnd(&rp);
      }

      for(k=0; k<nobj; k++)       /* draw a boxes spinning and others */
      {
         bx[k] = bx[k] + dx[k];   /* increment positions. */
         by[k] = by[k] + dy[k];
         bz[k] = bz[k] + dz[k];
         size[k] = (319-bz[k])/12 + 1;

	 if(bx[k]+size[k] > 319) /* clip & change direction. */
         {
            bx[k] = 319 - size[k];
            dx[k] = RangeRand(10) - 11;  /* set negative dx */
         }
	 if(by[k]+size[k] > 199)
         {
            by[k] = 199 - size[k];
            dy[k] = RangeRand(10) - 11;
         }
	 if(bz[k] > 319)
         {
            bz[k] = 319;
            dz[k] = RangeRand(10) - 11;
         }
	 if(bx[k]-size[k] < 0)
         {
            bx[k] = size[k];
            dx[k] = RangeRand(10) + 1;   /* set positive dx */
         }
	 if(by[k]-size[k] < 0)
         {
            by[k] = size[k];
            dy[k] = RangeRand(10) + 1;
         }
	 if(bz[k] < 20)
         {
            bz[k] = 20;
            dz[k] = RangeRand(10) + 1;
         }

         cx = bx[k];
         cy = by[k];
         cz = bz[k];

         perspective(&cx, &cy, &cz);  /* do perspective xform */

         half = size[k]/2;
         icol[k] = icol[k] - 1;
         if(icol[k] < 7)icol[k] = 13;
         icol2 = icol[k] - 7;
         dxhalf = scl[icol2]*half;
         xhalf = cx - dxhalf + icol2*dxhalf/3;
         xminus = cx-dxhalf;
         xplus = cx+dxhalf;
         yminus = cy-half;
         yplus = cy+half;

         SetAPen(&rp,icol2);         /* Set color to show rotation */
         AreaMove(&rp, xminus, yminus);
         AreaDraw(&rp, xhalf, yminus);
         AreaDraw(&rp, xhalf, yplus);
         AreaDraw(&rp, xminus, yplus);
         AreaEnd(&rp);

         SetAPen(&rp,icol[k]);         /* Set color to show rotation */
         AreaMove(&rp, xhalf, yminus);
         AreaDraw(&rp, xplus, yminus);
         AreaDraw(&rp, xplus, yplus);
         AreaDraw(&rp, xhalf, yplus);
         AreaEnd(&rp);
      }

   }

dats_all:

   ON_SPRITE;                /* turn on pointer (sprites on) */
   LoadView(oldview);        /* put back the old view */
   FreeMemory();             /* exit gracefully */
   CloseLibrary(GfxBase);    /* since opened library, close it */
}

/* End of main ----------------------------------------------------------*/

/* return user and system-allocated memory to sys manager */
FreeMemory()
{
   int i;

   FreeRaster(rastpoint,WIDTH,HEIGHT);   /* Free drawing areas */
   FreeRaster(rastpoint2,WIDTH,HEIGHT);
   for(i=0; i<DEPTH; i++)                /* Free bitmap RAM */
   {
      FreeRaster(b.Planes[i],WIDTH,HEIGHT);
      FreeRaster(b2.Planes[i],WIDTH,HEIGHT);
   }
   FreeColorMap(cm);                      /* Free colormap */
   v.LOFCprList = lof1;                   /* Free Copper structures */
   v.SHFCprList = shf1;
   FreeVPortCopLists(&vp);
   FreeCprList(v.LOFCprList);
   FreeCprList(v.SHFCprList);             /* Free interlace copper lists */
   v.LOFCprList = lof2;
   v.SHFCprList = shf2;
   FreeVPortCopLists(&vp);
   FreeCprList(v.LOFCprList);
   FreeCprList(v.SHFCprList);
   return(0);
}

int perspective(x, y, z)
SHORT *x, *y, *z;
{
   static float eye_x = 160., eye_y = 100., eye_z = -320.;  /* set viewpoint */
   static float range, sclx = 319., scly = 319.;       /* set scale factor */

   range = *z - eye_z;
   if(range != 0)
   {
      *x = (SHORT)(eye_x + sclx * ((float)*x-eye_x)/range);
      *y = (SHORT)(eye_y + scly * ((float)*y-eye_y)/range);
/*      *z = (SHORT)(1. * (float)*z);  */
   }
}
