/*-----------------------------------------------------------------------------
 The simple sphere ray-tracer.
 
 designed and written by Brad Grantham, completed   __ /__ /__ 
-----------------------------------------------------------------------------*/


/*--------------------------------#include's---------------------------------*/

 
#include "stdio.h"                     /* Standard I/O include file */
#define _STDIO_H                       /* This flag set to keep stdio.h from */
                                      /*  being opened again, like the rest.*/
#include "fcntl.h"                     /* File Control include file */
#define _FCNTL_H

#include "errno.h"                     /* System error include file, for file*/
#define _ERRNO_H                       /* errors, etc... */

#include "ctype.h"                     /* Char and string processing */
#define _CTYPE_H

#include "math.h"                      /* Floating point math */
#define _MATH_H

#include "exec/types.h"                /* Types & flags used by the Amiga /*
#define EXEC_TYPES_H                   /*  Exec system */

#include "exec/memory.h"               /* Memory allocation/deallocation */
#define EXEC_MEMORY_H                  /*  routines. */

#include "exec/ports.h"                /* Message ports include file, for use*/
#define EXEC_PORTS_H                   /*  with window IDCMP messages. */

#include "personal/myio.h"             /* I/O routines I created for all- */
#define PERSONAL_MYIO_H                /*  purpose use. */

#include "personal/hamgraphics.h"      /* HAM graphics routines I created */
#define PERSONAL_HAMGFX_H              /*  because there was no other way to */
                                       /*  efficiently display HAM grfx. */


/*--------------------------------#define's----------------------------------*/


                             /* These two are for OpenLibrary(). */
#define _INT_REV 0L                    /* Version of Intuition */
#define _GFX_REV 0L                    /* Version of Graphics library */

#define _height 0.72549                /* Ratio of screen height to width */
#define RADS(d) d/180*3.141597         /* Radian conversion macro */
#define NO_t -1.0                      /* No usable t value for parametrics. */

#define NONE 0                         /* No surface/source intersected */
#define SRC 2                          /* Light source intersected */

                             /* These flags specify how TraceImage returns. */
#define DONE 0                         /* Traceimage() finished naturally */
#define WINDCLS 1                      /* Close gadget on Window clicked */


/*--------------------------structure definitions----------------------------*/


struct bcol {                          /* Color struct from hamgraphics.h */
   struct stcolr c;                    /*  with brightness percentage. */
   int brt;
};

struct point {                         /* Point in three dimensions... */
   double X,Y,Z;
};

struct source {                        /* Light source (sphere) structure. */
   struct point C;                     /* Center of sphere */
   struct bcol Col;
   double R;                           /* (radius) */
};

struct vpoint {                        /* Viewpoint specification. */
   struct point V;                     /* Origin of rays */
   double Yaw,Pit,Roll,                /* Also known as Heading, Pitch, and */
                                       /*  Bank. */
    VAng;                              /* Entire View angle, left to right. */
};

struct world {                         /* World specification */
   char *Title;
   struct source *Src;                 /* Pointer to first source */
   struct bcol BC;                     /* Background color */
   struct vpoint VP;                   /* Viewpoint */
   int NSrcs,                          /* Number of polygons and sources, */
    XS,YS,                             /* Yaw and Pitch divisions per pixel */
    df,lnth,wdth,lace;
};

struct ray {                           /* Ray in 3D, parametric parameters. */
   struct point O;                     /* Origin of the ray */
   double dx,dy,dz;                     /* dx, dy, and dz with respect to 't' */
};


/*----------------------------global variables-------------------------------*/


struct IntuitionBase *IntuitionBase;   /* Intuition Library base */
struct GfxBase *GfxBase;               /* Graphics Library base */


struct NewWindow MyNewWindow= {        /* My window for close option. */
   20,50,170,11,                       /* I use this window so I can stop the*/
   -1,-1,                              /*  program in the middle of tracing a*/
   CLOSEWINDOW,                        /*  big picture without crashing. */
   WINDOWDEPTH|WINDOWCLOSE|WINDOWDRAG|SIMPLE_REFRESH,
   NULL,NULL,
   "3-D Portal",
   NULL,NULL,
   89,11,91,11,
   WBENCHSCREEN
};

struct Window *MyWindow;               /* The window as it will exist */

struct IntuiMessage *MyMessg;          /* Message that contains Close info. */


/*--------------------------program and functions----------------------------*/




main(argc,argv)
int argc;
char *argv[];
{  struct Screen *Screen;              /* The Screen I will create */
   struct world *World,*GetWorld();    /* the World and GetWorld to get it. */
   int cause,TraceImage();             /* Cause of TraceImage's return */

   printf("\nThree-D Portal, v0.0.\n");
   printf("Designed and Written by Brad Grantham.\n\n");
   if(argc<2) {
      printf("Try '%s HELP'\n",argv[0]);
      exit(0);
   };
   if((!strcmp(argv[1],"help"))||(!strcmp(argv[1],"HELP"))) {
      SendHelp();
      exit(0);
   };
   if((IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",
    _INT_REV))==NULL) {
      printf("Cannot open Intuition library.\n");
      exit(0);
   };
   if((GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",_GFX_REV))
    ==NULL) {
      printf("Cannot open Graphics library.\n");
      goto s4;
   };
   if((World=(struct world *)GetWorld(argv[1]))==NULL) {
      printf("Cannot set up world.\n");
      goto s3;
   };
   if((Screen=(struct Screen *)makescreen(World->Title,World->lace))==NULL) {
      printf("Cannot open screen, error#%d.\n",errno);
      goto s0;
   };
   if((MyWindow=(struct Window *)OpenWindow(&MyNewWindow))==NULL) {
      printf("Cannot open control window.\n");
      goto s1;
   };
   cause=TraceImage(Screen,World);
   if(cause==DONE) getchar();
s1:   CloseWindow(MyWindow);           /* These must be closed in reverse */
   CloseScreen(Screen);                /*  order that they were opened. */
s0:   FreeWorld(World);
s3:   CloseLibrary(IntuitionBase);
s4:   CloseLibrary(GfxBase);
   exit(0);
}


struct world *GetWorld(fname)          /* Get world and return pointer. */
char *fname;                           /* Data file. */
{  FILE *fp;                           /* Pointer to file control structure. */
   char *inpstr;                       /* What I'm reading from the file */
   int cntr,cnt2,wkd,getint();         /* Counters, Flag, and integer get */
                                       /*  routine. */
   struct world *Wd;                   /* The World. */
   struct source *tsrc;                /* Temporary light source pointer */
   double doubleget();                   /* Fetch double floating point */
   
   printf("Please wait, reading in data.\n\n"); 
   if((Wd=(struct world *)AllocMem(sizeof(*Wd),MEMF_PUBLIC))==NULL) {
      printf("Out of Mem; cannot allocate world structure.\n");
      return(NULL);   
   };
   if((fp=fopen(fname,"r"))==NULL) {
      printf("Cannot open file %s for input.\nE#%d\n",fname,errno);
      FreeMem(Wd,sizeof(*Wd)); return(NULL);
   };
   if((inpstr=getstr(fp))==NULL) {
      printf("Cannot read title.\n");
      fclose(fp);
      return(NULL);
   };
   if((Wd->Title=(char *)AllocMem(strlen(inpstr)+1,MEMF_PUBLIC))==NULL) {
      printf("No memory for world title.\n");
      goto nl2;
   };
   if(!strcmp(inpstr,".")) {
      FreeMem(Wd,sizeof(*Wd));
      Wd->Title=NULL;
   }
   else 
      strcpy(Wd->Title,inpstr);
   if(!getint(fp,&Wd->lace)) {
      printf("*!LACE\n");
      goto nl2;
   };
   if(!getint(fp,&Wd->wdth)) {
      printf("*!WDTH\n");
      goto nl2;
   };
   if(!getint(fp,&Wd->lnth)) {
      printf("*!LNTH\n");
      goto nl2;
   };
   if(!getint(fp,&Wd->XS)) {
      printf("*!XS\n");
      goto nl2;
   };
   if(!getint(fp,&Wd->YS)) {
      printf("*!YS\n");
      goto nl2;
   };
   if(!getint(fp,&Wd->BC.c.r)) {
      printf("*!BCr\n");
      goto nl2;
   };
   if(!getint(fp,&Wd->BC.c.g)) {
      printf("*!BCg\n");
      goto nl2;
   };
   if(!getint(fp,&Wd->BC.c.b)) {
      printf("*!BCb\m");
      goto nl2;
   };
   if(!getint(fp,&Wd->BC.brt)) {
      printf("*!BCb\m");
      goto nl2;
   };
   if(!getint(fp,&Wd->df)) {
      printf("*!DIFFUSION\n");
      goto nl2;
   };
   if(!getint(fp,&Wd->NSrcs)) {
      printf("*!#Sources\n");
      goto nl2;
   };
   if((Wd->Src=AllocMem(sizeof(struct source)*Wd->NSrcs,
      MEMF_PUBLIC))==NULL) {
      printf("No memory for light sources.\n");
      goto nl2;
   };
   tsrc=Wd->Src;
   for(cntr=0;cntr<Wd->NSrcs;cntr++) {
      wkd=(doubleget(fp,&tsrc->C.X)&&doubleget(fp,&tsrc->C.Y));
      wkd=(wkd&&doubleget(fp,&tsrc->C.Z)&&doubleget(fp,&tsrc->R));
      wkd=(wkd&&getint(fp,&tsrc->Col.c.r)&&getint(fp,&tsrc->Col.c.g));
      wkd=(wkd&&getint(fp,&tsrc->Col.c.b)&&getint(fp,&tsrc->Col.brt));
      if(!wkd) {
         printf("*!Source #%d\n",cntr);
         goto nl4;
      };
      ++tsrc;
   };
   wkd=(wkd&&doubleget(fp,&Wd->VP.V.X)&&doubleget(fp,&Wd->VP.V.Y));
   wkd=(wkd&&doubleget(fp,&Wd->VP.V.Z)&&doubleget(fp,&Wd->VP.Yaw));
   wkd=(wkd&&doubleget(fp,&Wd->VP.Pit)&&doubleget(fp,&Wd->VP.Roll));
   wkd=(wkd&&doubleget(fp,&Wd->VP.VAng));
   if(!wkd) {
      printf("*!Viewpoint\n",cntr);
      goto nl4;
   };
   fclose(fp);
   return(Wd);

nl4:  FreeMem(Wd->Src,sizeof(struct source)*Wd->NSrcs);
nl2:  if(Wd->Title!=NULL)
      FreeMem(Wd->Title,strlen(Wd->Title));
nl1:  fclose(fp);
nl0:  FreeMem(Wd,sizeof(*Wd));
   return(NULL);

}


TraceImage(Scn,Wd)                     /* This controls the actual ray- */
                                       /*  tracing. */
struct Screen *Scn;                    /* Screen to trace on */
struct world *Wd;                      /* World to trace from */
{  int xloop,yloop,cols,qkx,qky,rs;    /* Counters */
   struct bcol pcol,*Pixel();          /* Final pixel color and recursive */
                                       /*  trace-a-ray routine. */ 
   struct stcolr pc;                   /* Absolutely final pixel color */
   struct ray Ray;                     /* Ray that is being traced */
   struct vpoint *VP;                  /* Viewpoint to trace from */
   double RawYaw,RawPit,YawAng,PitAng,indgx,indgy,
    RCOS,RSIN,PitCOS;                  /* Raw calc Yaw and Pitch, final Yaw */
                                       /*  and Pitch, Yaw and Pitch increment*/
                                       /*  and temp variables to cut back */
                                       /*  unnecessary repetition. */
 
   VP=&Wd->VP;
   Ray.O.X=VP->V.X;
   Ray.O.Y=VP->V.Y;
   Ray.O.Z=VP->V.Z;
   VP->Pit=RADS(VP->Pit);
   VP->Yaw=RADS(VP->Yaw);
   VP->Roll=RADS(VP->Roll);
   VP->VAng=RADS(VP->VAng);
   RCOS=cos(VP->Roll)*cos(VP->Roll);
   RSIN=sin(VP->Roll)*sin(VP->Roll);
   indgx=VP->VAng/(Wd->wdth*Wd->XS);
   indgy=VP->VAng/(Wd->lnth*Wd->YS);
   for(yloop=10;yloop<Wd->lnth;yloop++)
      for(xloop=0;xloop<(Wd->wdth-2);) {
         cols=0;
         for(qkx=0;qkx<Wd->XS;qkx++) {
            RawYaw=VP->VAng/2-(xloop*Wd->XS+qkx)*indgx;
            for(qky=0;qky<Wd->YS;qky++) {
               RawPit=VP->VAng/2-(yloop*Wd->YS+qky)*indgy;
               YawAng=VP->Yaw+RawYaw*RCOS+RawPit*RSIN;
               PitAng=VP->Pit+_height*(-RawYaw*RSIN+RawPit*RCOS);
               PitCOS=cos(PitAng);
               Ray.dx=sin(YawAng)*PitCOS;
               Ray.dy=sin(PitAng);
               Ray.dz=cos(YawAng)*PitCOS;
               ++cols; 
               Avgcol(&pcol,cols,Pixel(&Ray,Wd));
            };
         };
         pc.r=pcol.c.r*pcol.brt/100;
         pc.g=pcol.c.g*pcol.brt/100;
         pc.b=pcol.c.b*pcol.brt/100;
         xloop+=HAMPixel(&Scn->RastPort,xloop,yloop,&pc);
         if((MyMessg=(struct IntuiMessage *)GetMsg(MyWindow->UserPort))!=NULL) {
            if(MyMessg->Class==CLOSEWINDOW);
               return(WINDCLS);
            ReplyMsg(MyMessg);
         };
      };
   return(DONE);
}


static struct bcol btc;                /* Temporary color for recursion */


struct bcol *Pixel(Ray,Wd)        /* Performs actual trace */
struct ray *Ray;                       /* Ray to bounce through world. */
struct world *Wd;                      /* World to bounce ray through */
{  int cntr,theone,thenum;             /* Counters and markers */
   double tmpds,fnlds,srcint();
                                       /* Temp distance, final distance */
                                       /*  and Intersection routines which */
                                       /*  do return a 't' value */
   struct source *tsrc;

   theone=NONE;
   for(cntr=0;cntr<Wd->NSrcs;cntr++) {
      tmpds=srcint((Wd->Src+cntr),Ray);
      if(tmpds!=NO_t)
         if((theone==NONE)||((theone!=NONE)&&(tmpds<fnlds))) {
            theone=SRC;
            thenum=cntr;
            fnlds=tmpds;
         };
   };
   if(theone==NONE)
      return(&Wd->BC);
   else {
      tsrc=(Wd->Src+thenum);
      btc=tsrc->Col;
      btc.brt=(int)(btc.brt*(tsrc->C.Z-(Ray->O.Z+fnlds*Ray->dz))/tsrc->R+
       (RangeRand(Wd->df)-Wd->df/2));
      if(btc.brt<10)
         btc.brt=10;
      if(btc.brt>100)
         btc.brt=100;
      return(&btc);
   };
}


Avgcol(pcol,cols,avgc)                 /* Average successive colors, for */
                                       /*  redundancy option */
struct bcol *pcol,*avgc;               /* The colors so far and the new one. */
int cols;                              /* How many colors so far in pcol */
{  
   pcol->c.r=(pcol->c.r*(cols-1)+avgc->c.r)/cols;
   pcol->c.g=(pcol->c.g*(cols-1)+avgc->c.g)/cols;
   pcol->c.b=(pcol->c.b*(cols-1)+avgc->c.b)/cols;
   pcol->brt=(pcol->brt*(cols-1)+avgc->brt)/cols;
   return;
}



colmask(maskc,color)                   /* Mask color thru maskc, as if taking*/
                                       /*  backwards path of rays and adding */
                                       /*  colors up */
struct bcol *maskc,*color;             /* Color to restrict new color, and */
                                       /*  new color */
{

/*
   ************************
   ***UNDER CONSTRUCTION***
   ************************
*/
 
   return(NULL);
}


double srcint(src,ray)                 /* Does ray intersect source and where*/
struct source *src;                    /* What source? */
struct ray *ray;                       /* Which ray? */
{  double b,m,n,o,t1,t2,sqroot;        /* double floating point accumulators */
                                       /*  and such. */

   m=ray->O.X-src->C.X;
   n=ray->O.Y-src->C.Y;
   o=ray->O.Z-src->C.Z;
   b=2*(ray->dx*m+ray->dy*n+ray->dz*o);
   sqroot=b*b-4*(m*m+n*n+o*o-src->R*src->R);
   if(sqroot<0)
      return(NO_t);
   t1=(-b-sqrt(sqroot))/2;
   t2=(-b+sqrt(sqroot))/2;
   if((t1<0)&&(t2<0))
      return(NO_t);
   if(t1<0)
      return(t2);
   if((t2<0)||(t1<t2))
      return(t1);
   else
      return(t2);
}

FreeWorld(Wd)                          /* Free up memory used in World */
struct world *Wd;                      /* World to liberate */
{
   FreeMem(Wd->Src,sizeof(struct source)*Wd->NSrcs);
   FreeMem(Wd->Title,strlen(Wd->Title));
   FreeMem(Wd,sizeof(*Wd));
   return;
}


int getint(fp,num)                     /* Call myio routine getstr() and */
                                       /*  convert result to integer */
FILE *fp;                              /* File for input */
int *num;                              /* Number to get */
{  char *str;                          /* String receiving from file */
   
   if((str=getstr(fp))==NULL)
      return(0);
   *num=atoi(str);
   return(1);
}


double doubleget(fp,num)               /* Call getstr() and convert result to*/
                                       /*  double-floating point */
FILE *fp;                              /* Input */
double *num;                           /* Number */
{  char *str;                          /* Receipt from file */
   double ascflt();                    /* Convert asc to flt routine */
 
   if((str=getstr(fp))==NULL)
      return(0);
   *num=ascflt(str);
   return(1);
}


double ascflt(str)                     /* Convert ascii string to double- */
                                       /*  floating point number. (quite a */
                                       /*  clever method, isn't it?) */
char *str;                             /* String to convert */
{  double acc=0.0;                     /* Accumulator for converted value */
   int dec,dv,sign;                    /* Converters and the number's sign. */
 
   dec=dv=sign=1;
   if(*str=='-') { sign=-1; ++str; };
   while(isdigit(*str)||(*str=='.')) {
      if(*str=='.')
         dec=10;
      else
         acc=(acc*(10/dec))+(*str-'0')*(dv=dv/dec);
      ++str;
   };
   return(sign*acc);
}


SendHelp()                             /* User-friendly help routine */
{
   printf("Try typing the command, then the input file\n");
#ifdef SAVEIMAGE
   printf("then the ILBM destination file,");
#endif
   printf("then return.\n\n");
   return;
}


