Interceptor Demo (Disk 86) (Apr 1988) : planet / planet.c

#include "planet.h"

main(argc,argv)
int argc;
char *argv[];
{
   struct IntuiMessage *mess;
   unsigned int mask,open_things();
   int i,j,rad,ang;
   long x,y,lx,ly,lz,vx,vy,vz,px,py,pz,v,pz2,r2;
   float size,source,calp,lon,lat,light[3][3],trans[3][3];
   char *c;

/* default values */

   mapfile = (UBYTE *)"planet.map\0         ";
   rad = 180;
   dmax = 6;
   size = 1.0;

/* setup identity matrices for light source and planet rotation */

   for(i = 0; i < 3; i++)
   {
      for(j = 0; j < 3; j++)
      {
         if(i == j) light[i][j] = trans[i][j] = 1;
         else light[i][j] = trans[i][j] = 0;
      }
   }

/* read command line argument values */

   for(i = 1; i < argc; i++)
   {
      c = argv[i];
      if(c[0] == '?') help();
      else if(c[0] == '-')
      {
         switch(c[1])
         {
            case 'r' : if(argc > ++i) sscanf(argv[i],"%d",&rad);
                       break;
            case 'h' : if(argc > ++i) sscanf(argv[i],"%d",&dmax);
                       break;
            case 's' : if(argc > ++i) sscanf(argv[i],"%f",&size);
                       break;
            case 'l' : if(argc > ++i) sscanf(argv[i],"%d",&ang);
                       rotate(c[2],ang,light);
                       break;
            case 'p' : if(argc > ++i) sscanf(argv[i],"%d",&ang);
                       rotate(c[2],-ang,trans);
                       break;
            case 'f' : CMFF = TRUE;
                       break;
            default  : printf("Error in arguments\n");
                       help();
                       break;
         }
      }
      else sscanf(argv[i],"%s",mapfile);
   }

/* compute light source vector */

   lx = rad*light[0][2];
   ly = rad*light[1][2];
   lz = rad*light[2][2];
   r2 = (long)(rad*rad);

/* compute light source size effect */

   source = 2*atan((double)(size - 1.0))/PI;

   mask = open_things();

/* loop on destination display coordinates */

   for (y = 0L; y < 400L; ++y)
   {
      py = 200L - y;
      ro = go = bo = 0;

      for (x = 0L; x < 320L; ++x)
      {
         if(mess = (struct IntuiMessage *)GetMsg(w->UserPort))
            CheckMess(mess,mask);
         px = 25L*(x - 160L)/14L;

         /* calculate for coordinates on planet surface only */

         if((pz2 = r2 - (px*px + py*py)) > 0L)
         {
            pz = sqrt((double)pz2);

            /* compute cosine of angle between normal and light source */

            vx = px*trans[0][0] + py*trans[0][1] + pz*trans[0][2];
            vy = px*trans[1][0] + py*trans[1][1] + pz*trans[1][2];
            vz = px*trans[2][0] + py*trans[2][1] + pz*trans[2][2];
            calp = (vx*lx + vy*ly + vz*lz)/(float)r2;
            calp = (calp + source)/(1 + source);

            /* dont bother with black part of planet */

            if(calp > 0.0)
            {
               if(calp > 1.0) calp = 1.0;

               /* compute longitude and latitude */

               if((v = sqrt((double)(vy*vy + vz*vz))) == 0) v = 1;
               lon = atan((double)vx/v);
               if((v = sqrt((double)(vx*vx + vz*vz))) == 0) v = 1;
               lat = atan((double)vy/v);

               /* get color from planet map */

               map(lon,lat);
               PutPixel(x,y,calp);
            }
         }
      }
   }

   close_things((unsigned int)ILBM);
   mask ^= ILBM;

   for(;;)
   {
      Wait(1L << w->UserPort->mp_SigBit);
      if(mess = (struct IntuiMessage *)GetMsg(w->UserPort))
         CheckMess(mess,mask);
   }
} /* end main */

/* compute transformation and concatenate with previous transformation(s) */

rotate(axis,ang,m)
int axis,ang;
float (*m)[3];
{
   int i,j,k;
   float n[3][3],p[3][3];

   /* setup tranformation matrix based on selected axis */

   switch(axis)
   {
      case 'x': i = 0; j = 1; k = 2; break;
      case 'y': i = 1; j = 2; k = 0; break;
      case 'z': i = 2; j = 0; k = 1; break;
      default : i = 0; j = 1; k = 2; break;
   }

   n[i][i] = 1.0;
   n[j][j] = n[k][k] = cos((double)ang*PI/180);
   n[i][j] = n[i][k] = n[j][i] = n[k][i] = 0.0;
   n[k][j] = sin((double)ang*PI/180);
   n[j][k] = -n[k][j];

   /* concatenate with previously computed transformation matrix */

   for(i = 0; i < 3; i++)
   {
      for(j = 0; j < 3; j++)
      {
         p[i][j] = 0.0;
         for(k = 0; k < 3; k++) p[i][j] = p[i][j] + n[i][k]*m[k][j];
      }
   }

   for(j = 0; j < 3; j++)
   {
      for(i = 0; i < 3; i++) m[i][j] = p[i][j];
   }
} /* end rotate */

/* self explanitory */

help()
{
   printf
   ("usage: planet [1] -r [2] -h [3] -s [4] -l[x,y,z 5] -p[x,y,z 6] -f [7]\n");
   printf
   ("    1: map file name (IFF);                     any ILBM [planet.map]\n");
   printf
   ("    2: radius of planet (pixels);               10   -> 180  [180]\n");
   printf
   ("    3: ham tweek factor (color gradients);      0    -> 45   [6]\n");
   printf
   ("    4: radius of light source (planet radii);   0.0  -> 10.0 [1.0]\n");
   printf
   ("    5: light source angle about axis (degrees); 0    -> 360  [x 0]\n");
   printf
   ("    6: planet rotation about axis (degrees);    0    -> 360  [x 0]\n");
   printf
   ("    7: color map from file;                     no argument\n");
   exit(0);
} /* end help */

/* get color from map at specified longitude and latitude */

map(lon,lat)
float lon,lat;
{
   int x,y,i,id;

   x = (lon/PI + .5)*(Width-1);
   y = (.5 - lat/PI)*(Height-1);

   /* if map is HAM mode then full scan line must be processed */

   if(ViewModes & HAM) {
      rc = gc = bc = 0;
      for(i = 0; i <= x; i++) {
         id = ReadPixel(&maprp,(long)i,(long)y);
         if((id & 48) == 32) rc = (id & 15);
         else if((id & 48) == 48) gc = (id & 15);
         else if((id & 48) == 16) bc = (id & 15);
         else {
            rc = rm[id];
            gc = gm[id];
            bc = bm[id];
         }
      }
   }

   /* else just read color value from map */

   else {
      id = ReadPixel(&maprp,(long)x,(long)y);
      rc = rm[id];
      gc = gm[id];
      bc = bm[id];
   }
} /* end map */

/* compute planet color value with shade applied */

PutPixel(x,y,ca)
long x,y;
float ca;
{
   int r,g,b,dr,dg,db,val,d,diff,n;

   /* multiply each color component with shade value */

   r = ca*rc;
   g = ca*gc;
   b = ca*bc;

   /* compute closest HAM color to desired color */

   dr = ABS(r - ro);
   dg = ABS(g - go);
   db = ABS(b - bo);

   if(dr > dg) {
      if(dr > db) { ro = r; dr = 0; val = r + 32; }
      else        { bo = b; db = 0; val = b + 16; }
   }
   else {
      if(dg > db) { go = g; dg = 0; val = g + 48; }
      else        { bo = b; db = 0; val = b + 16; }
   }

   diff = dr + dg + db;

   /* check to see if any existing color comes closer */

   for(n = nc; n >= 0; n--)
   {
      dr = ABS(r - rn[n]);
      dg = ABS(g - gn[n]);
      db = ABS(b - bn[n]);
      d = dr + dg + db;

      if(d <= diff) {
         diff = d;
         val = n;
         ro = rn[n];
         go = gn[n];
         bo = bn[n];
      }
   }

   /* if the difference between obtainable color and desired color exceeds
      maximum difference (HAM tweek factor) then place desired color into
      palatte (if there is room) and update color bar */

   if((diff > dmax) && (nc < NCOL)) {
      val = ++nc;
      ro = rn[nc] = r;
      go = gn[nc] = g;
      bo = bn[nc] = b;
      SetRGB4(vp,(long)nc,(long)r,(long)g,(long)b);
      if(showtitle) DrawImage(trp,&square[15],0L,0L);
   }

   /* set color on displayed planet */

   SetAPen(rp,(long)val);
   WritePixel(rp,x,y);

} /* end PutPixel */

/* check to see what the user wants */

CheckMess(message,mask)
struct IntuiMessage *message;
unsigned int mask;
{
   ULONG class;
   unsigned int code,menunum,itemnum;

   class = message->Class;
   code = message->Code;
   ReplyMsg(message);
   if(class == MENUPICK)
   {
      menunum = MENUNUM(code);
      itemnum = ITEMNUM(code);
      switch(menunum)
      {
         case 0:
         switch(itemnum)
         {
            case 0:
            TitleShow();
            break;

            case 1:
            close_things(mask);
	    exit(0);
         }
      }
      if(showtitle) DrawImage(trp,&square[15],0L,0L);      
   }
} /* end CheckMess */

/* open everything */

unsigned int open_things()
{
   unsigned int mask;
   int i;

   struct Library *OpenLibrary();
   struct Screen *OpenScreen();
   struct Window *OpenWindow();
   struct ViewPort *ViewPortAddress();

   mask = 0;

   if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L)))
   {
      printf("no graphics library!!!\n");
      exit(1);
   }
   mask |= GRAPHICS;

   if(!(IntuitionBase = (struct IntuitionBase *)
        OpenLibrary("intuition.library",0L)))
   {
      printf("no intuition library!!!\n");
      close_things(mask);
      exit(2);
   }
   mask |= INTUITION;

   ns.DefaultTitle = mapfile;

   if (!(s = OpenScreen(&ns)))
   {
      printf("could not open the screen\n");
      close_things(mask);
      exit(3);
   }
   mask |= SCREEN;

   nw.Screen = s;

   if (!(w = OpenWindow(&nw)))
   {
      printf("could not open the window\n");
      close_things(mask);
      exit(4);
   }
   mask |= WINDOW;

   rp = w->RPort;
   vp = ViewPortAddress(w);

   SetMenuStrip(w,&ScrnMenu);
   showtitle = TRUE;
   TitleShow();

   if (!ReadILBM(mapfile))
   {
      printf("could not get ILBM\n");
      close_things(mask);
      exit(5);
   }
   mask |= ILBM;

   /* setup color bar */

   for(i=0;i<16;i++) {
      SetRGB4(vp,(long)i,(long)rn[i],(long)gn[i],(long)bn[i]);
      square[i].LeftEdge = 158 + 8*i;
      square[i].TopEdge = 0;
      square[i].Width = 8;
      square[i].Height = 10;
      square[i].Depth = 0;
      square[i].ImageData = NULL;
      square[i].PlanePick = 0;
      square[i].PlaneOnOff = i;
      if(i != 0) square[i].NextImage = &square[i-1];
   }
   square[0].NextImage = NULL;
   trp = s->BarLayer->rp;

   return(mask);
} /* end open_things */

/* close things indicated by mask */

close_things(mask)
unsigned int mask;
{
   int i;

   if(mask & ILBM)
   {
      FreeMem(bufstart,(long)header.ckSize);
      for(i=0;i<Depth;i++)
         FreeMem(bmap.Planes[i],PlaneSize);
   }
   if(mask & WINDOW)
   {
      ClearMenuStrip(w);
      CloseWindow(w);
   }
   if(mask & SCREEN)    CloseScreen(s);
   if(mask & GRAPHICS)  CloseLibrary(GfxBase);
   if(mask & INTUITION) CloseLibrary(IntuitionBase);
} /* end close_things */

/* toggle title bar */

TitleShow()
{
   ClearMenuStrip(w);
   if(showtitle)
   {
      showtitle = FALSE;
      ScrnText1.IText = (UBYTE *)"Show Title Bar";
      ShowTitle(s,FALSE);
   }
   else
   {
      showtitle = TRUE;
      ScrnText1.IText = (UBYTE *)"Hide Title Bar";
      ShowTitle(s,TRUE);
      DrawImage(trp,&square[15],0L,0L);
   }
   SetMenuStrip(w,&ScrnMenu);
} /* end TitleShow */

/**************************************************************************
*                                                                         *
*    Routine name(s) : ReadILBM()                                         *
*    Author          : D. John Hodgson (modified by Russell Leighton)     *
*    Environment     : Aztec "C", default                                 *
*                                                                         *
*    ReadILBM attempts to read an IFF file. If successful then contents   *
*    are stored in bitmap, bmap and a rastport, maprp is initialized. The *
*    colormap is stored (the RGB components are kept separate) in the     *
*    array, mapcolors.                                                    *
*                                                                         *
*    LIMITATIONS : no masking, CATS/LISTS/PROPS. CAMG chunks supported.   *
**************************************************************************/

int ReadILBM(fspec)
char *fspec; /* AmigaDOS filename */
{
  struct FileHandle *fp,*Open();
  UBYTE *sourcebuf,*destbuf,*AllocMem();
  UBYTE colormap[MAXCOLORS][3];
  short colorcount,plane,linelen,rowbytes,i,r,g,b;
  long id;
  char n;

  if ((fp=Open(fspec,MODE_OLDFILE))==NULL) return(0);

  SafeRead(fp,&header,(long)sizeof(header));
  if (header.ckID!=ID_FORM) { Close(fp); return(0); }

  SafeRead(fp,&id,(long)sizeof(id));
  if (id!=ID_ILBM) { Close(fp); return(0); }

  for (;;) {
    SafeRead(fp,&header,(long)sizeof(header));

    if (header.ckID==ID_BODY) break;

    switch(header.ckID) {
      case ID_BMHD: SafeRead(fp,&bmhd,(long)sizeof(bmhd));
                    break;

      case ID_CMAP: SafeRead(fp,&colormap[0][0],(long)header.ckSize);
                    colorcount=header.ckSize/3;
                    break;

      case ID_CAMG: SafeRead(fp,&ViewModes,(long)header.ckSize);
                    break;

      default:      Seek(fp,ROUNDODDUP(header.ckSize),OFFSET_CURRENT);
    }
  }

  /* Read planes into RAM for ease if decompression */
     
  sourcebuf=bufstart=AllocMem((long)header.ckSize,MEMF_PUBLIC | MEMF_CHIP);
  if (sourcebuf==NULL) return(0);

  SafeRead(fp,sourcebuf,(long)header.ckSize); Close(fp);

  Width=bmhd.w;
  Height=bmhd.h;
  Depth=bmhd.nPlanes;

  /* make some forced assumptions if CAMG chunk unavailable */

  if (ViewModes == 0L) {
    if (Width > MAXWIDTH) ViewModes |= HIRES;
    if (Height > MAXHEIGHT) ViewModes |= LACE;
  }

  linelen=Width/8;

  bmap.BytesPerRow = linelen;
  bmap.Rows = Height;
  bmap.Depth = (UBYTE)Depth;

  PlaneSize = (long)(linelen*Height);
  for(i=0;i<Depth;i++) { /* allocate space for bitmap */
    bmap.Planes[i] = AllocMem(PlaneSize,MEMF_PUBLIC | MEMF_CHIP);
    if(bmap.Planes[i] == NULL) return(0);
  }

  maprp.BitMap=&bmap;

  while (colorcount--) {
    rm[colorcount] = colormap[colorcount][0] >> 4L;
    gm[colorcount] = colormap[colorcount][1] >> 4L;
    bm[colorcount] = colormap[colorcount][2] >> 4L;
    if(CMFF && (colorcount < 16)) {
       rn[colorcount] = rm[colorcount];
       gn[colorcount] = gm[colorcount];
       bn[colorcount] = bm[colorcount];
    }
  }

  if(CMFF) nc = NCOL;

  for (i=0;i<Height;i++) /* process n lines/screen */
    for (plane=0;plane<Depth;plane++) { /* process n planes/line */
      destbuf=(UBYTE *)(bmap.Planes[plane])+linelen*i;

      if (bmhd.compression==cmpByteRun1) { /* compressed screen? */
        rowbytes=linelen;

        while (rowbytes) { /* unpack until 1 scan-line complete */
          n=*sourcebuf++; /* fetch block run marker */

          /* uncompressed block? copy n bytes verbatim */
          if (n>=0) {
            movmem(sourcebuf,destbuf,(unsigned int)++n); rowbytes-=n;
            destbuf+=n; sourcebuf+=n;
          }
          else { /* compressed block? expand n duplicate bytes */
            n=-n+1; rowbytes-=n;
            setmem(destbuf,(unsigned int)n,(unsigned int)*sourcebuf++);
            destbuf+=n;
          }

        } /* finish unpacking line */
      }
      else { /* uncompressed? just copy */
        movmem(sourcebuf,destbuf,(unsigned int)linelen);
        sourcebuf+=linelen; destbuf+=linelen;
      }
    } /* finish interleaved planes, lines */
  return(1);
} /* end ReadILBM */