/* main.c 
 *
 *****************************************************************
 *                                                               *
 *                      basic ray tracing:                       *
 *    spheres and a floor (reflection, refraction and diffuse)   *
 *		programmer: friedrich knauss			 *
 *                       7-4-86 to 7-21-86                       *
 *		ported to st: allen king 5/87, added:		 *
 *			limited color				 *
 *			low rez display first			 *
 *			.25 to 16 rays per pixel 		 *
 *			object definitions in file		 *
 *			multiple-frame movies			 *
 *			bouncing and gravity			 *
 *                                                               *
 *****************************************************************/

#include <stdio.h>
#include <math.h>
#include <osbind.h>
#include <gemdefs.h>
#include "rtd.h"
#define dprintf if (debug) printf

/* these definitions describe a window in the x-y plane
   that the whole thing is viewd through. */

int XMIN, YMIN, XMAX, YMAX;
float xmin, ymax;		/* floating pt versions */

wOnewSize(x,y, w,h)
int x,y, w,h;
{	XMIN = x;	YMIN = y;
	XMAX = x+w+2;	YMAX = y+h+2;
}

/* Nomenclature notes: 
 *	1. <a>P<b> stands for "the number of <a>'s PER <b>
 *	2. r stands for rays
 *	3. p stands for pixels
 *	4. s stands for either side of the screen
 *	5. the suffix "Max" denotes the max number compiled into the program
 *
 * Note: these are linear (not area) conversions. A value of 2 rPp (rays PER
 * pixel) is applied in both x and y directions to get an area (and more
 * true to life) conversion of 4 (square) rays through each (square) pixel.
 */
    float rPp;			/* rays PER pixel (on a (linear) side)*/
#   define rPpMax 4
				/* maximum rays PER pixel (ditto) */
    int pPs;			/* pixels PER side (of screen) */
    int rPs;			/* rays PER side (of screen) */
    int rPsMax;			/* Max rays PER side (of screen) */

#define INC  1
#define INCY  (1.25/rPpMax)
#define INCX  (1.0/rPpMax)

int colorInd;			/* color index (1->b/w, 2->2tones, 3->3tones)*/

#define colors(r,g,b) 1000*(2*(r)+1)/16, 1000*(2*(g)+1)/16, 1000*(2*(b)+1)/16,
int oldClut[16][3], trClut[][3] = 
{
#include "colors.h"
0};

struct 
{	int (*pal)[][3];
	int Navg;
} cConfig[] = {{trClut, 16}, {trClut+16, 32}, {trClut+32, 43}};

FILE *fp, *fopen();
char getline();
char outname[6] = "pearl";
int wHandle;
int pxy_array[4];

#define Nball 15
struct ball bl[Nball];
int     level,
        nob;
struct vector vp;
float gravity = 0.0, attraction = 0.0;
int step, Step = 1000, bounce = 1;;
int fr=0, frames=1;

int debug =0;

struct sphere   ls;

main ()
{   static float    xco,
                    yco, yup;
    struct ray  rr;
    int     h, i, j, k;
    int c, cr, cg, cb;
    int x0, y0, x1, y1, dx, dy, dmax, xMax, yMax;
    double red, green, blue;				/* was int */
    long ctr, BavgN;
    float tmp;

    char str[200], *buf;
    int Navg, *avg, *Bavg;

    rPp = 1.0;/* defaults: */
    colorInd = 1;

/*    graf_mouse(M_OFF, 0L);/**/
    printf("\033E");
    do
    {	printf("                RAYMOVI.PRG\n\n");
	printf("Enter scene descriptor\nfilename: ");
	if (getline(str, sizeof(str)) == '\003')
		exit(0);
	printf("\nreading file %s\n", str);
    } while(scene_inz(str) == 0);

    printf("\033E");

    wHandle = w_open(0, "raymovi", 0);	/* open window (typeless, menueless) */
    w_lClut(cConfig[colorInd-1].pal, oldClut);
    v_hide_c(wHandle);

    xmin = XMIN;	ymax = YMAX;
    dx = XMAX-XMIN;
    dy = YMAX-YMIN;
    if (rPp > 1.0001)
    {	BavgN = (long)dx * dy * colorInd;
	Bavg = Malloc(BavgN * sizeof(int));
	if (Bavg == 0)
	{   long l1;
	    printf("limited to 1 ray/pixel due to insufficient RAM\n");
	    if (rPp > 1.0)
		rPp = 1.0;
	    for (l1=0; l1<200000; l1++);
	}   
	for (tmp = 1.0; rPp >= 1.0001; tmp *= 2.0, rPp /= 2.0);
    }
    else
	for (tmp = 1.0; rPp < .5001; tmp /= 2.0, rPp *= 2.0);
    rPp = tmp;
    dmax = (dx>dy)? dx: dy;
    for (i=dmax-1,       pPs=1;     i>0   ; i>>=1, pPs<<=1);
    for (i=rPp*pPs-0.5,    rPs=1;   i>0   ; i>>=1, rPs<<=1);
    rPsMax = rPpMax * pPs;
    xMax = ((long)rPsMax*dx)/pPs;	yMax = ((long)rPsMax*dy)/pPs;

    for (; fr<=frames; fr++) 
    {  	int len, len1, p34, x, y, Elen, Ey;
redraw:	
	if (Bavg) for (avg = Bavg, ctr=0; ctr<BavgN; ctr++)
		*avg++ = 0;
	Navg = cConfig[colorInd-1].Navg;
	ctr = 0;
	for (len = rPsMax; len>= rPsMax/rPs; len /=2)
	{   p34 = 3;
	    if (len>=rPpMax)
	    {	len1 = len/rPpMax - 1;
		Elen = 0;
	    }else
	    {	len1 = 0;
	        Elen = rPpMax -1;
		Navg *= 4;
	    }
	    for (y=0; y<yMax; y += len)
	    {	p34 = (p34 | 1) ^ 2;
		if (Elen)
		    Ey = (((y + len)&Elen) == 0);
		y0 = y/rPpMax;
		y1 = y0 + YMIN;
		pxy_array[1] = y1;
		pxy_array[3] = y1 + len1;
		yco = ymax - INCY*y;

		for (x=0; x<xMax; x += len)
		{   if ((p34 ^=1) == 0 && ctr !=0)
			continue;
		    ctr++;
		    x0 = x/rPpMax;
		    x1 = x0 + XMIN;
		    pxy_array[0] = x1;
		    pxy_array[2] = x1 + len1;
		    xco = xmin + INCX*x;

	        /* define the ray through a pixel; find out value for that pixel */
	 	    mv (xco, yco, 0.0, &(rr.org));
		    sv (&(rr.dir), &(rr.org), &vp);
		    shade (&rr, &red, &green, &blue);   /* find out color */

		    cb = blue;
		    cr = red;
		    cg = green;
		    if (Bavg)		/* compute sum for all rays */
		    {   register int *array;
			array = Bavg + ((long)y0*dx+x0+1)*colorInd;
			switch(colorInd)
			{ case 3:
			    cg = *--array += cg;
			  case 2:
			    cr = *--array += cr;
			  case 1:
			    cb = *--array += cb;
		    }   }

		    if (Elen==0 || Ey && ((x+len)&Elen) == 0 )
		    {	switch(colorInd)
			{ case 1:			/* white (and black): */
			    c = (cb/Navg + 1) & 0xf;		/* white */
			    break;
			  case 2:			/* white and red: */
			    if (2*cr < 3*cb)
				c = cb/Navg+1 & 7; 		/* white */
			    else c = (cr/Navg & 7) + 8;		/* red */
			    break;
			  case 3:			/* white, red, blue: */
			    if (4*cr > 3*(cg+cb))
			    {	c = cr/Navg % 6;		/* red */
				if (c) c += 5; /* red */
				else c = 1;
			    }
			    else if (4*cb > 3*(cr+cg))
			    {	c = cb/Navg % 6;		/* blue */
				if (c) c += 10; /* red */
				else c = 1;
			    }
			    else c = (cg/Navg+1) % 6;	 	/* white */
			    break;
			}
			dprintf("%d,%d,%d -> %d\n", cr, cg, cb, c);
		    	vsf_color(wHandle, c);	/*draw colored box*/
			v_bar(wHandle, pxy_array);
		    }
		    if ((ctr&15)== 0 && kbd() == -1)
			goto redraw;
		}
		sprintf(str, "%s%03d:     RAYMOVI.PRG     rpp=%05.3f", 
			outname, fr, (float)ctr/((long)dx*dy));
		di_header(1, str);
	}   } 	
	sprintf(str, "%s%03d.pi1", outname, fr);
	f_write(str, colorInd-1);

	movie(&bl, nob, step, Step, gravity, bounce, attraction);
	write_ckpt("checkpt");
	}
exit:
    w_lClut(oldClut, (long)0);
    w_close();
}

kbd()
{	char key, fname[20];
	int found_first = 0, fr1, rval=0;

	if (Cconis() == 0)
		return(0);

	key = Cconin();
	while (1)
	{	sprintf(fname, "%s%03d.pi1", outname, fr);
		switch(key)
		{   case ('?'): case ('\0'):		/* help */
			printf("\033E\n\n       RayMovi\n\
			\nw - write \"%s\" from screen,\
			\nr - read \"%s\" to screen,\
			\np - play movie \"%sXXX.pi1\
			\ne - exit\n\n", fname, fname, outname);
			key = Cconin();
			printf("\033E");
			rval = -1;
			break;
		    case ('D'):
			debug = 1-debug;
			return (rval);
		    case ('w'):
			f_write(fname, colorInd-1);
			return (rval);
		    case ('r'):	case ('p'):
			fr1 = 1;
			while (key == 'p' || key == 'r')
			{	sprintf(fname,"%s%03d.pi1", outname, fr1++);
				if (f_read(fname, (long)0) <0)
				{	if (found_first)
					{	fr1 = 1;
						found_first = 0;
				}	}
				else
					found_first = 1;
				if (Cconis() != 0 || key == 'r' && found_first)
					key = Cconin();
			}
			rval = -1;
			break;
		    case ('e'): case('\003'):
			w_lClut(oldClut, (long)0);
			w_close();
			exit(0);
		    default:
			return(rval);
}	}	}

scene_inz(inz_file)
char *inz_file;
{   int h, i, j, k; char str[200], *buf;
    if ((fp=fopen(inz_file, "r"))==0)
    {	printf("can't open file '%s'\n", inz_file);
	return(0);
    }
    j=0, h=0;
    while (1)
    {	int linenum;
    	buf = str;
	for (k=0; (i = getc(fp)) != '\n' && i != EOF && ++k<sizeof(str)-1;)
		*buf++ = i;
	*buf = '\0';
	linenum++;
	printf("%d: %s\n", linenum, str);
	buf = str;
	if (i == EOF)
	    break;		
	switch (buf[0])
	{   case ('p'):		/* physical ball properties */
		if (j >= Nball)
			panic("**** ERROR: too many balls (%d >= %d)", j,Nball);
		if (7 !=sscanf(buf+1," %f,%f,%f,%f,%f,%f,%f", &bl[j].s.cent.x,
			&bl[j].v.x, &bl[j].s.cent.y, &bl[j].v.y,&bl[j].s.cent.z,
			&bl[j].v.z, &bl[j].s.rad)) goto errors;
		j++;
		break;
	    case ('o'):		/* optical ball properties */
		if (h >= Nball)
			panic("**** ERROR: Too many balls (%d >= %d)", h,Nball);
		if(8 !=sscanf(buf+1," %f,%f,%f,%f,%f,%f,%f,%f", &bl[h].ior,
			&bl[h].rfr, &bl[h].rfl, &bl[h].dif, &bl[h].amb, 
			&bl[h].red, &bl[h].green, &bl[h].blue)) goto errors;
	        bl[h].ior *= bl[h].ior;
		h++;
		break;
	    case ('v'):		/* define viewpoint */
		if (3 != sscanf(buf+1, " %f,%f,%f", &vp.x, &vp.y, &vp.z))
			goto errors;
		break;
	    case ('l'):  /* define light source (rad = how fuzzy shadows are) */
		if (4 != sscanf(buf+1, " %f,%f,%f,%f", &ls.cent.x, &ls.cent.y,
			&ls.cent.z, &ls.rad)) goto errors;
		break;
	    case ('r'):  /* define number of rays per pixel and color scheme */
		if (2 != sscanf(buf+1, " %f, %d", &rPp, &colorInd))
			goto errors;
		colorInd = colorInd<1? 1: colorInd>=3? 3: colorInd;
		rPp = sqrt(rPp>16.0? 16.0: rPp);
		break;
	    case ('g'):	 /* gravity, etc */
		if (3 != sscanf(buf+1, " %f, %f, %d", &gravity, &attraction, 
			&bounce)) goto errors;
		gravity /= Step * 10;
		break;	
	    case ('f'):	 /* define the filename */
		while(*(++buf) == ' ');	/* skip leading spaces */
		for (i=0; i<5 && *buf!=','; i++)
			outname[i] = *buf++;
		outname[i] = 0;

		if (2 != sscanf(buf+1, "%d,%d", &fr, &frames))
			goto errors;
		break;
	    case ('\t'):  /* comment */
	    case (' '):
	    case ('\0'):
		break;
	    default:
	    errors:
		printf("**** ERROR in line %d:\n%s\n", linenum, buf);
    }	}
    fclose(fp);	
    if (h != j)
	panic("Number of 'o' lines (%ld) mismatches\n number of 'p' lines (%ld)\n", 
		(long)h, (long)j);
    nob = j;
    return(1);
}

write_ckpt(name)
char *name;
{	int i;
	if ((fp = fopen(name, "w")) != 0)
	{
	    fprintf(fp, "f %s, %d, %d\n", outname, fr+1, frames);
	    for (i=0; i<nob; i++)
	    {	fprintf(fp, "p %f,%f,%f,%f,%f,%f,%f\n", bl[i].s.cent.x, 
			bl[i].v.x, bl[i].s.cent.y, bl[i].v.y, bl[i].s.cent.z, 
			bl[i].v.z, bl[i].s.rad);
		fprintf(fp, "o %f,%f,%f,%f,%f,%f, %f,%f,%f\n\n",sqrt(bl[i].ior),
			bl[i].rfr, bl[i].rfl, bl[i].dif, bl[i].amb, 
			bl[i].red, bl[i].green,bl[i].blue);
	    }
	    fprintf(fp, "v %f,%f,%f\n", vp.x, vp.y,vp.z);
	    fprintf(fp, "l %f,%f,%f,%f\n", 
		ls.cent.x, ls.cent.y, ls.cent.z, ls.rad);
	    fprintf(fp, "r %f, %d\n", rPp*rPp, colorInd);
	    fprintf(fp, "g %f, %f, %d\n", gravity*Step* 10, attraction, bounce);
	    fclose(fp);	
}	}

char getline(str, sizeofstr)
char *str;
int sizeofstr;
{   char *buf, c;
    int k, i;

    buf = str;
    for (k = sizeofstr; k > 0;)
    {	c = i = Cconin();
	if (c == '\n' || c == '\r' || c == ' ')
		return (c);
	else if (c == '\003' || c == '\214')	/* ^c or Undo */
		return ('\003');
	else if (c == '\b')
	{	if (k < sizeofstr)
		{	*--buf = 0;
			k++;
		}else
			printf(" ");
	}
	else
	{	*buf++ = c;
		*buf = '\0';
		k--;
}   }	}

panic(s,l1,l2)
char *s;
long l1,l2;
{	printf("\n");
	printf(s,l1,l2);
	printf("\npress any key to continue");
	Cconin();

	if (wHandle)
	{	w_lClut(oldClut, (long)0);
		w_close();
	}
	exit(1);
	atoi();
}
