/************************************************************************
 *									*
 *		    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"

/************************************************************************/
/********************* All ray tracing is in this module ****************/
/************************************************************************/


/* See if object s has 1 or more lights shining on it */
void getlights(s,intersect,normal,diffuse,newdir,color)
NODE	*s;
VEC	intersect,normal,diffuse,newdir,color;
    {
    register NODE	*l;
    register FLTDBL	dist, eta, eta2;
    VEC			lint,ldir,v1;
    int			i;
    LIGHT		*lp;
    FLTDBL		tmin;

    /* Look at each light */
    for (lp=lights; lp; lp = lp->next) {

	/* get the dir,distance to the light */
	l = lp->child;
	vcomb(-1.0,intersect,l->cen,ldir);
	dist = vdot(ldir,ldir);
	vscale(1.0/sqrt(dist),ldir,ldir);

	/* if not a point source, compute new light intensity */
	if (l->att->dist > 0.0) {

	    /* get the distance to the light */
	    if (dist == 0.0) dist = 0.0001;

	    /* use inverse square law to factor intensity */
	    vscale(l->att->dist / dist,l->att->color,lint);
	    }
	else vcopy(l->att->color,lint);

	/* see if there are any reflections to do */
	eta = eta2 = 0.0;
	if (s->att->kd > 0.0) eta  = vdot(normal,ldir);
	if (s->att->ks > 0.0) eta2 = vdot(ldir,newdir);
	if (eta > 0.00 || eta2 > 0.00)
	    if (intersection(intersect, ldir, &tmin) != l)
		eta = eta2 = 0.0;

	/* do diffuse reflections */
	if (eta > 0.00) {
	    vmul(lint,diffuse,v1);
	    eta *= s->att->kd*l->att->kl;
	    vcomb(eta,v1,color,color);
	    }

	/* do specular reflections */
	if (eta2 > 0.00) {
	    if (s->att->kf == 0.0) eta = 1000.0;
	    else {
		eta = 2.0/s->att->kf;
		if (eta < 1.0) eta = 1.0;
		}
	    eta2  = pow(eta2,eta);
	    eta2 *= s->att->ks*l->att->kl;
	    if (eta2 > TOL) {
		vcomb(eta2,lint,color,color);
		}
	    }
	}
    }

/* Do any specular reflections off of object s */
void getspecular(level,atten,s,d,normal,intersect,dir,newdir,color)
NODE	*s;
FLTDBL	d,atten;
VEC	normal,intersect,dir,newdir,color;
    {
    FLTDBL  eta;
    VEC	    newcolor;

    /* and see where it goes */
    trace(level,atten,intersect,newdir,newcolor);

    /* scale by specular factor */
    vscale(s->att->ks,newcolor,newcolor);

    /* Add sparkle */
    eta = vdot(normal,newdir);
    if (eta >= 0.0) {
	eta = 1.0 - eta;
	eta = eta * eta * eta * eta * eta;
	vcomb(eta,newcolor,newcolor,newcolor);
	}
    vcomb(1.0,newcolor,color,color);
    }

/* If object is transparent, compute what's on the other side */
void gettrans(level,atten,s,eflip,d,normal,intersect,dir,color)
NODE	    *s;
FLTDBL	    d,atten;
VEC	    normal,intersect,dir,color;
    {
    FLTDBL	eta;
    VEC		v1;

    /* get the correct index of refrection */
    if (eflip)  eta = 1.0 / s->att->ir;
    else	eta = s->att->ir;

    /* compute the angle of the new ray */
    vcomb(d,normal,dir,v1);
    vscale(eta,v1,v1);
    eta = vdot(v1,v1);

    /* make sure that we aren't past critical angle */
    if (eta < 1.0) {

	/* get the vector through the surface */
	vcomb(-sqrt(1.0-eta),normal,v1,v1);

	/* see who's visable */
	trace(level,atten,intersect,v1,v1);

	/* add it to the return color */
	vcomb(s->att->kt, v1, color, color);
	}
    }

/* main raytrace routine */
void trace(level, atten, frm, dir, retval)
VEC	frm, dir, retval;
FLTDBL	atten;
    {
    register NODE   *s;
    register FLTDBL  d, eta;
    register FLTDBL  latten;
    VEC	     normal, color, diffuse, v1, v2, intersect, newdir;
    int	     eflip;
    FLTDBL  tmin;

    /* have we recursed too far? */
    if (++level == depth) {
	vcopy(BLACK,retval);
	return; 
	}

    /* If we don't hit anything then return background */
    if (!(s = intersection(frm, dir, &tmin))) {
	getamb(frm,dir,retval);
    	return;
	}

    /* get the intersection point */
    vcomb(tmin, dir, frm, intersect);

    /* compute the surface normal */
    d = findnormal(s,dir,intersect,normal,&eflip);

    /* get a base diffuse level (if necessary) */
    if (s->att->kl > 0.0 || s->att->kd > 0.0) {

	/* get base color (procedural texture) of the object */
	gettex(s,intersect,normal,diffuse);

	/* if we're a light, then use the light amount else the base amount */
	if (s->att->kl > 0.0) {
	    vscale(s->att->kl,diffuse,color);
	    }
	else {
	    vscale(base,diffuse,color);
	    }
	}

    /* otherwise start out with no color */
    else {
	vcopy(BLACK,color);
	}

    /* bounce the ray off the surface for specular/mirror reflection */
    if (s->att->ks > 0.0 || s->att->kd > 0.0) {
	vcomb(2.0*d,normal,dir,newdir);
	}

    /* Do mirror/specular computations */
    if ((latten = s->att->ks*atten) > 0.01) {
	getspecular(level,latten,s,d,normal,intersect,dir,newdir,color);
	}

    /* Do reflection computations (any lights shining on this point?) */
    if (s->att->kd > 0.0 || s->att->ks > 0.0) {
	getlights(s,intersect,normal,diffuse,newdir,color);
	}

    /* compute transparency (if necessary) */
    if ((latten = s->att->kt*atten) > 0.01 && s->att->ir != 1.0) {
	gettrans(level,latten,s,eflip,d,normal,intersect,dir,color);
	}

    /* return the results */
    vcopy(color,retval);
    }

