/************************************************************************
 *									*
 *		    Copyright (c) 1988, David B. Wecker			*
 *			    All Rights Reserved				*
 *									*
 * This file is part of DBW_uRAY					*
 *									*
 * DBW_uRAY is distributed in the hope that it will be useful, but	*
 * WITHOUT ANY WARRANTY. No author or distributor accepts		*
 * responsibility to anyone for the consequences of using it or for	*
 * whether it serves any particular purpose or works at all, unless	*
 * he says so in writing. Refer to the DBW_uRAY General Public		*
 * License for full details.						*
 *									*
 * Everyone is granted permission to copy, modify and redistribute	*
 * DBW_uRAY, but only under the conditions described in the		*
 * DBW_uRAY General Public License. A copy of this license is		*
 * supposed to have been given to you along with DBW_uRAY so you	*
 * can know your rights and responsibilities. It should be in a file	*
 * named COPYING. Among other things, the copyright notice and this	*
 * notice must be preserved on all copies.				*
 ************************************************************************
 *									*
 * Authors:								*
 *	DBW - David B. Wecker						*
 *									*
 * Versions:								*
 *	V1.0 881023 DBW	- First released version			*
 *	V1.1 881110 DBW - Fixed scan coherence code			*
 *	V1.2 881125 DBW - Removed ALL scan coherence code (useless)	*
 *			  added "fat" extent boxes			*
 *									*
 ************************************************************************/

#include "uray.h"

/************************************************************************/
/********************* support (misc) routines **************************/
/************************************************************************/


/* leave the program. if msg is not NULL then an error occurred */
/* also, return any used resources at this point */

void leave(msg,arg1,arg2)
char	*msg,*arg1,*arg2;
    {

    /* are we writing a .tmp file */
    if (bpp && fp) fclose(fp);

    /* are we writing a .ilbm file (go back and put in the colortable) */
    if (fp2) {
	fseek(fp2,(long)pos2,0); wfil(fp2,4,&lsize);
	lsize -= BODYsize;
	lsize += FORMsize;
	fseek(fp2,(long)pos1,0); wfil(fp2,4,&lsize);
	fseek(fp2,(long)pos3,0);
	fwrite(colors,1,48,fp2);
	fclose(fp2);
	}

    /* is there an error message */
    if (msg) {
	fprintf(stderr,"\nURAY: ");
	fprintf(stderr,msg,arg1,arg2);
	fprintf(stderr,"\n");
	exit(-2);
	}
    exit(1);
    }

/* do a malloc and make sure that we got the memory */
char *my_malloc(size)
    {
    char    *result,*malloc();

    if (!(result = malloc(size))) leave("Can't malloc memory");
    return result;
    }

/* do a calloc and make sure that we got the memory */
char *my_calloc(size,num)
    {
    char    *result,*calloc();

    if (!(result = calloc(size,num))) leave("Can't calloc memory");
    return result;
    }

/* allocate and initialize a node (for input routine) */
NODE	*doalloc(typ) {
    NODE    *nod;
    EXTENT  *newe;

    /* get memory for the new object */
    switch (typ) {
	case TYP_E:	nod = (NODE *)my_malloc(sizeof(EXTENT));    break;
	case TYP_S:	nod = (NODE *)my_malloc(sizeof(SPHERE));    break;
	case TYP_T:	nod = (NODE *)my_malloc(sizeof(TRIANGLE));  break;
	case TYP_Q:	nod = (NODE *)my_malloc(sizeof(QUAD));	    break;
	case TYP_R:	nod = (NODE *)my_malloc(sizeof(RING));	    break;
	case TYP_L:	nod = (NODE *)my_malloc(sizeof(LIGHT));	    break;
	}

    if (typ == TYP_L) return;

    nod->next	= NULL;
    nod->typ	= typ;

    /* if this is an extent, just return it */
    if (typ == TYP_E) {
	extcnt++;
	return nod;
	}

    /* else get memory for an extent around the object (and count the obj) */
    newe	= (EXTENT *)doalloc(TYP_E);
    newe->child	= nod;
    objcnt++;

    /* and link it in to the extent tree */
    newe->next	= nodes;
    nodes	= (NODE *)newe;

    /* return the object to the user */
    return nod;
    }

/* add a light to the list of lights (for fast lookup) */
void addlight(nod)
NODE	*nod;
    {
    LIGHT   *l;

    if (nod->att->kl == 0.0) return;

    l = (LIGHT *)doalloc(TYP_L);
    l->next	= lights;
    l->child	= nod;
    lights	= l;
    }

/* debug routine (see DEBUG_dumpnodes in uray.h) for dumping nodes */
void dumpnodes(n,lev)
NODE	*n;
    {
    int	    i;
    EXTENT  *e;

    /* do each node at this level */
    while (n) {

	/* space over the current level amount */
	for (i=0; i<lev; i++) printf(".");

	/* print out type of node */
	printf(" %c",TYPstr[n->typ]);

	/* if an extent, then go down recursively */
	if (n->typ == TYP_E) {
	    e = (EXTENT *)n;
	    if (e->child->typ != TYP_E)	printf("%c",TYPstr[e->child->typ]);
	    else			printf(" ");

	    /* print extent range */
	    printf(" [%7.4f,%7.4f] [%7.4f,%7.4f] [%7.4f,%7.4f]\n",
		e->min[0],e->max[0],e->min[1],e->max[1],
		     e->min[2],e->max[2]);

	    /* do recursion */
	    if (e->child->typ == TYP_E) dumpnodes(e->child,lev+1);
	    }

	/* get next node at this level */
	n = n->next;
	}
    }

/* make vector into unit vector */
void vunit(A,B)
VEC A,B;
    {
    register FLTDBL  tmp;

    tmp = 1.0 / vnorm(A);
    vscale(tmp, A, B);
    }

/* find the normal to a sphere */
void spherenormal(intersect,s,normal)
VEC	intersect,normal;
SPHERE	*s;
    {
    VEC	v1;

    /* get the direction from the center */
    vcomb(-1.0, intersect, s->cen, v1);

    /* then normalize */
    vunit(v1,normal);
    }	

/* find the normal to a plane */
void planenormal(s,normal)
QUAD	*s;
VEC	normal;
    {
    VEC	v1;

    /* first do vector cross product */
    vcross(s->v1,s->v2,v1);

    /* then normalize */
    vunit(v1,normal);
    }

/* read the input .dat file  */
void readinput()
    {
    int		i,j;
    FLTDBL	val;
    int		tmp1,tmp2;

    /* get the input file open */
    printf("Creating objects\n");
    sprintf(str,"%s.dat",basnam);
    fp = fopen(str,"r");
    if (!fp) leave("Bad input file");

    /* read each line of the file */
    while (fgets(str,80,fp)) {

	/* every legal (~comment) line must start with a string and a num */
	if (sscanf(str,SCAN1,cmd,&val) != 2) continue;

	/* read in the global parameters */
	if (!strcmp(cmd,"DEPTH"))	depth	    = (int)val;
	else if (!strcmp(cmd,"ROWS"))	rows	    = (short)val;
	else if (!strcmp(cmd,"START"))	startrow    = (short)val;
	else if (!strcmp(cmd,"END"))	endrow	    = (short)val;
	else if (!strcmp(cmd,"COLS"))	cols	    = (short)val;
	else if (!strcmp(cmd,"BPP"))	bpp	    = (short)val;
	else if (!strcmp(cmd,"AOV"))	aov	    = (int)val;
	else if (!strcmp(cmd,"ASPECT"))	aspect	    = val;
	else if (!strcmp(cmd,"NEAR"))
	    sscanf(str,SCAN2,cmd,&NEAR[0],&NEAR[1],&NEAR[2]);
	else if (!strcmp(cmd,"FAR"))
	    sscanf(str,SCAN2,cmd,&FAR[0],&FAR[1],&FAR[2]);
	else if (!strcmp(cmd,"GROUND"))
	    sscanf(str,SCAN2,cmd,&GROUND[0],&GROUND[1],&GROUND[2]);
	else if (!strcmp(cmd,"BASE"))	base	    = val;

	/* read in attributes */
	else if (!strcmp(cmd,"ATTRIBUTES")) {
	    natts   = (int)val;
	    atts    = (ATT *)my_calloc(sizeof(ATT),natts);

	    /* read in each attribute line */
	    for (i=0; i<natts; i++) {
		fgets(str,80,fp);
		if (sscanf(str,SCAN3,
		    &atts[i].color[0],&atts[i].color[1],&atts[i].color[2],
		    &atts[i].kd,&atts[i].ks,&atts[i].kt,&atts[i].ir,
		    &atts[i].kl,&atts[i].dist,&atts[i].kf,
		    &tmp1,&tmp2,
		    &atts[i].p1[0],&atts[i].p1[1],&atts[i].p1[2],
		    &atts[i].p2[0],&atts[i].p2[1],&atts[i].p2[2])
		    < 12
		    ) leave("Bad attribute");
		atts[i].wave = (short)tmp1;
		atts[i].tex  = (short)tmp2;
		}
	    }

	/* read in waves */
	else if (!strcmp(cmd,"WAVES")) {
	    nwaves  = (int)val;
	    waves   = (WAVE *)my_calloc(sizeof(WAVE),nwaves);

	    /* read in each wave line */
	    for (i=0; i<nwaves; i++) {
		fgets(str,80,fp);
		if (sscanf(str,SCAN4,
		    &waves[i].cen[0],&waves[i].cen[1],&waves[i].cen[2],
		    &waves[i].amp,&waves[i].phase,&waves[i].length,
		    &waves[i].damp)
		    < 7) leave("Bad wave");
		}
	    }

	/* read in an object - SPHERE */
	else if (!strcmp(cmd,"SPHERE")) {
	    sph		= (SPHERE *)doalloc(TYP_S);
	    sscanf(str,SCAN5,cmd,&j,
		&sph->cen[0],&sph->cen[1],&sph->cen[2],
		&sph->rad);
	    sph->rad *= sph->rad;
	    sph->att  = &atts[j];
	    if (sph->rad < TOL) sph->rad = TOL;
	    addlight(sph);
	    }

	/* read in an object - QUAD */
	else if (!strcmp(cmd,"QUAD")) {
	    qua		= (QUAD *)doalloc(TYP_Q);
	    sscanf(str,SCAN6,cmd,&j,
		&qua->p0[0],&qua->p0[1],&qua->p0[2],
		&qua->v1[0],&qua->v1[1],&qua->v1[2],
		&qua->v2[0],&qua->v2[1],&qua->v2[2]);
	    qua->att = &atts[j];
	    addlight(qua);
	    }

	/* read in an object - TRIANGLE */
	else if (!strcmp(cmd,"TRIANGLE")) {
	    tri		= (TRIANGLE *)doalloc(TYP_T);
	    sscanf(str,SCAN6,cmd,&j,
		&tri->p0[0],&tri->p0[1],&tri->p0[2],
		&tri->v1[0],&tri->v1[1],&tri->v1[2],
		&tri->v2[0],&tri->v2[1],&tri->v2[2]);
	    tri->att = &atts[j];
	    addlight(tri);
	    }

	/* read in an object - RING */
	else if (!strcmp(cmd,"RING")) {
	    rin		= (RING *)doalloc(TYP_R);
	    sscanf(str,SCAN7,cmd,&j,
		&rin->p0[0],&rin->p0[1],&rin->p0[2],
		&rin->v1[0],&rin->v1[1],&rin->v1[2],
		&rin->v2[0],&rin->v2[1],&rin->v2[2],
		&rin->rad1,&rin->rad2);
	    rin->rad1 *= rin->rad1;
	    rin->rad2 *= rin->rad2;
	    rin->att = &atts[j];
	    vunit(rin->v1,rin->v1);
	    vunit(rin->v2,rin->v2);
	    addlight(rin);
	    }
	}

    /* fix up global parameters */
    if (endrow > rows)	    endrow   = rows;
    if (startrow >= endrow) startrow = endrow - 1;
    if (startrow < 0)	    startrow = 0;
    cols &= 0xFFF8;

    /* print out what we read in (sanity check) */
    printf("\n");
    printf("    Input file name: %14s\n",basnam);
    printf("    Maximum recursion depth: %6d\n",depth);
    printf("    Dimensions:              %6d rows (%d,%d)  %d columns\n",
	rows,startrow,endrow,cols);
    printf("    Bits/Pixel:              %6d\n",bpp);
    printf("    Angle of view:           %6d degrees\n",aov);
    printf("    Aspect ratio:            %6.3f\n",(double)aspect);
    printf("    Number of attributes:    %6d\n",natts);
    printf("    Number of waves:         %6d\n",nwaves);
    printf("\n");

    /* terminate the input file */
    fclose(fp);
    fp = NULL;
    }


