/*
From harvard!husc6!caip!nike!ucbcad!ucbvax!sdcsvax!net1!fritzz 25 Jul 86 21:35:59 GMT
Article 2915 of net.sources:
Relay-Version: version B 2.10.2 9/18/84; site bbnccv.UUCP
Path: bbnccv!harvard!husc6!caip!nike!ucbcad!ucbvax!sdcsvax!net1!fritzz
>From: fritzz@net1.UCSD.EDU (Friedrich Knauss)
Newsgroups: net.sources
Subject: basic ray tracing: part 1 (tracer.c)
Message-ID: <190@net1.UCSD.EDU>
Date: 25 Jul 86 21:35:59 GMT
Date-Received: 27 Jul 86 01:07:29 GMT
Reply-To: fritzz@net1.UUCP (Friedrich Knauss)
Organization: University of California, San Diego
*/
/*
 * this subroutine does all the gritty work- it calculates 
 * what shade each pixel should be. I like recursion.
 */

#include <math.h>
#include "rtd.h"
#include "extern.h"
#define time(x) 0

/* LEVEL defines number of levels of recursion */
#define LEVEL 5
int debug;
#define dprintf if (debug) printf

shade (r, red, green, blue)
struct ray *r;
double *red, *green, *blue;
{   int     i, c,
            refract ();
    struct ray  refr;
    double  rd, gn, blu, tmp;
    double  lght_red, lght_green, lght_blue,
            x, y, z,
            l, k,
            dot (), find (), findo ();
    struct vector   new, norm;
    struct mat  trans;
    struct sphere   ss;

    if (++level <= LEVEL)
    {	c = -1;
	l = HUGE;

	dprintf("LEVEL %d: ", level);
        /* get vector length and xz component for mt() */
	vecl (&r->dir);
	vexzl (&r->dir);

        /* make a transform matrix that rotates something in space so
            that the ray will be aligned with the x axis */

	mt (&r->dir, &trans);

        /* for starters we find out whether we hit anything. */

	for (i = 0; i < nob; i++)
	{   ss.rad = bl[i].s.rad;
	    sv (&ss.cent, &bl[i].s.cent, &r->org);
	    if ((k = find (&trans, &ss)) > 0.0 && k < l)
	    {	c = i;
		l = k;
	    }
	}
	if (c >= 0.0) 		/* WE HIT SOMETHING */
	{   x = l * trans.x.x;
	    y = l * trans.x.y;
	    z = l * trans.x.z;
	    mv (x, y, z, &new);

	    /* move the new orgin of the ray to the intersection */

	    av (&refr.org, &new, &r->org);
	    av (&r->org, &new, &r->org);
	    mv (r->dir.x, r->dir.y, r->dir.z, &refr.dir);

	    /* get a normal vector for the intersection point */

	    sv (&norm, &r->org, &bl[c].s.cent);
	    vecl (&norm); 

	    /* ambient lighting */

	    tmp = 255.0 * bl[c].amb;
	    lght_red = tmp * bl[c].red;
	    lght_green = tmp * bl[c].green;
	    lght_blue = tmp * bl[c].blue;
	    dprintf(" ball[%d]", c);
	    dprintf(", ambient=%d,%d,%d\n",
		(int)lght_red, (int)lght_green, (int)lght_blue);

	    /* shaded lighting (diffuse). subroutine shadow is in find.c */

	    if (bl[c].dif != 0.0)
	    {	sv (&new, &ls.cent, &r->org);
		vecl (&new); 
		if ((k = dot (&new, &norm)) > 0.0)
		{   shadow (&r->org, &rd, &gn, &blu);
		    dprintf("diffuse = %d, %d, %d\n",(int)rd,(int)gn,(int)blu);
		    tmp = bl[c].dif * k / (new.l * norm.l);
		    lght_red   += tmp * rd * bl[c].red;
		    lght_green += tmp * rd * bl[c].green;
		    lght_blue  += tmp * rd * bl[c].blue;
	    }   }

	    /*reflection... easy */

	    if (bl[c].rfl != 0.0)
	    {	vecl (&norm); 
	  	/* make the normal unit length */
		scamult ((1.0 / norm.l), &norm);
		/* get the length of the ray's component in the normal direction */
 		x = 2.0 * dot (&norm, &r->dir);
		scamult (x, &norm);
		/* subtract double the normal component- !reflection! */
		sv (&r->dir, &r->dir, &norm);
		shade (r, &rd, &gn, &blu);
		dprintf("reflection = %d, %d, %d, tmp=%d\n", 
			(int)rd, (int)gn, (int)blu, (int)tmp);
		tmp = bl[c].rfl;
		lght_red   += tmp * rd;
		lght_green += tmp * gn;
		lght_blue  += tmp * blu;
	    }

	    /* refraction. this is ugly, which is why I choose to deal with
		   it in it's own subroutine which comes after this one */

	    if (bl[c].rfr != 0.0)
	    {	dprintf(" refraction\n");
	    	refract (&refr, &bl[c], &rd, &gn, &blu);
		tmp = bl[c].rfr;
		dprintf("refraction = %d, %d, %d\n",(int)rd,(int)gn,(int)blu);
		lght_red   += tmp * rd;
		lght_green += tmp * gn;
		lght_blue  += tmp * blu;
	}   }
	else 			/* hit no objects... */
	{   if ((r->dir.y) < 0.0) /* crosses floor */
	    {	z = -(r->org.y) / (r->dir.y);
		(r->org.x) += z * (r->dir.x);
		(r->org.z) += z * (r->dir.z);
		(r->org.y) = 0.0;
		dprintf("floor (%d, %d): ", (int)r->org.x, (int)r->org.z);
		if (  ((int) ((r->org.x) / 9.0) % 8 == 0) 
		   || ((int) ((r->org.z) / 9.0) % 8 == 0))
		{   lght_red = lght_green = lght_blue = 0.0;
		    dprintf("black. 0,0,0\n"); 
		}		/* this is for texture, grid on the ground */
		else 		/* get shading for the squares... */
		{   sv (&new, &ls.cent, &r->org);
		    vecl (&new); 
		    shadow (&r->org, &rd, &gn, &blu);
		    tmp = 0.6 * new.y / new.l;
		    lght_blue   = tmp * blu + 100.0;	 /* tile pattern: */
		    if ((int)r->org.x/72 & (int)r->org.z/72 & 0x15)
		    {	lght_red = lght_green = 0.0;		/* blue */
		        dprintf("blue. %d, %d, %d\n", 
				(int)lght_red, (int)lght_green, (int)lght_blue);
		    }
		    else
		    {	lght_red   = tmp * rd + 100.0;		/* white */
			lght_green = tmp * gn + 100.0;
			dprintf("white. %d,%d,%d\n", 
			    (int)lght_red, (int)lght_green, (int)lght_blue);
	    }	}   }
	    else	/* didn't hit ground... sky */
	    {	lght_red = lght_green = lght_blue = 0.0;
		dprintf("sky. %d,%d,%d\n", 
			(int)lght_red, (int)lght_green, (int)lght_blue);
	    }
	    dprintf("lght_= %d,%d,%d\n", 
		(int)lght_red, (int)lght_green, (int)lght_blue);
    }	}   
    /* to many levels return 0 cause it shouldn't matter */
    else
    {	lght_red = lght_green = lght_blue = 0.0;
	dprintf(">5 levels. 0, 0, 0\n");
    }
    level--;

    if (lght_red < 0.0) 	lght_red = 0.0;
    if (lght_red > 255.0)	lght_red = 255.0;

    if (lght_green < 0.0) 	lght_green = 0.0;
    if (lght_green > 255.0)	lght_green = 255.0;

    if (lght_blue < 0.0) 	lght_blue = 0.0;
    if (lght_blue > 255.0)	lght_blue = 255.0;

    *red = lght_red;
    *green = lght_green;
    *blue = lght_blue;

    dprintf("returned: %d, %d, %d\n", (int)*red, (int)*green, (int)*blue);
}

int     refract (r, bll, red, green, blue)
struct ray *r;
struct ball *bll;
double *red, *green, *blue;
{
    struct vector   new, norm;
    struct mat  trans;
    double  l,
            refk ();
    struct sphere   ss;

    sv (&norm, &r->org, &bll->s.cent);
    vecl (&norm);
    vecl (&r->dir);

    /* get the addition factor for the normal */

    scamult (refk (&norm, &r->dir, bll->ior), &norm);
    av (&r->dir, &r->dir, &norm);

    /* find the point where the ray leaves the sphere. just like shade. */

    vexzl (&r->dir);
    vecl (&r->dir);
    mt (&r->dir, &trans);
    ss.rad = bll->s.rad;
    sv (&ss.cent, &bll->s.cent, &r->org);
    l = findo (&trans, &ss);
    mv (l * trans.x.x, l * trans.x.y, l * trans.x.z, &new);
    av (&r->org, &r->org, &new);

    /* redirect the ray and continue tracing */

    sv (&norm, &r->org, &bll->s.cent);
    vecl (&norm);
    vecl (&r->dir);
    scamult (refk (&norm, &r->dir, 1.0 / bll->ior), &norm);
    av (&r->dir, &r->dir, &norm);

    shade (r, red, green, blue);
}


double  refk (nrm, in, ior)
struct vector  *nrm, *in;
double  ior;
{   double  dt, ln, li, ret;

    dt = dot (nrm, in);
    ln = nrm->x * nrm->x + nrm->y * nrm->y + nrm->z * nrm->z;
    li = in->x * in->x + in->y * in->y + in->z * in->z;
    ret = dt * dt - ln * li * (1 - ior);
    if (ret < 0.0)
	ret = 0.0;
    if (dt < 0)
	ret = (-dt - sqrt (ret)) / ln;
    else
	ret = (-dt + sqrt (ret)) / ln;
    return (ret);
}

printv(s, v)
char *s;
struct vector *v;
{	printf("%s.x = %f, .y = %f, .z = %f\n", s, v->x, v->y, v->z);
}
