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

/************************************************************************/
/**************** This module computes textures of objects **************/
/************************************************************************/

/* get the procedure texture */
void gettex(n,intersect,normal,diffuse)
NODE	*n;
VEC	intersect,normal,diffuse;
    {
    int	    i,j,k;
    FLTDBL  d;

    /* start with the default diffuse color */
    vcopy(n->att->color,diffuse);
    switch (n->att->tex) {

	/* no texture */
	case 0:
	break;

	/* checker board, p1 = alternate color, p2 = scale */
	case 1:
	for (i=j=0; i<3; i++) {
	    if (n->att->p2[i] > 0.0) {
		k = (int)(intersect[i] / n->att->p2[i]);
		if (k < 0) j -= k;
		else	   j += k;
		}
	    }
	if (j & 1) { vcopy(n->att->p1,diffuse);  }
	break;

	/* random mottled texture, p1 = alternate color */
	case 2:
	for (i=0; i<3; i++) {
	    diffuse[i] *= (d=rnd());
	    diffuse[i] += (1.0-d) * n->att->p1[i];
	    }
	break;

	/* blend textures */
	case 3:	    /* X axis blend, p1 = alternate color, p2 = coordinate */
	case 4:	    /* Y axis blend, p1 = alternate color, p2 = coordinate */
	case 5:	    /* Z axis blend, p1 = alternate color, p2 = coordinate */
	i = n->att->tex - 3;
	if (intersect[i] > n->att->p2[1])   d = n->att->p2[2] - n->att->p2[1];
	else 				    d = n->att->p2[0] - n->att->p2[1];

	if (d == 0.0) break;
	d = (intersect[i] - n->att->p2[1]) / d;
	
	if (d > 1.0) d = 1.0;
	if (d < 0.0) d = 0.0;

	for (i=0; i<3; i++) {
	    diffuse[i]	*= d;
	    diffuse[i]	+= (1.0-d) * n->att->p1[i];
	    }
	break;
	}
    }

/* return background value (computes sky and ground colors) */
void getamb(frm, dir, retval)
VEC frm, dir, retval;
    {
    FLTDBL  v;
    int	    i;

    if (dir[1] < 0.0) {
	vcopy(GROUND,retval);
	return;
	}
    if (dir[2] < 0.0)	v = 0.0;
    else {
	v = (1.0 - dir[1]) * dir[2];
	if (v > 1.0) v = 1.0;
	else	     v *= v;
	}
    for (i=0; i<3; i++) {
	retval[i]  = NEAR[i] * (1.0 - v);
	retval[i] +=  FAR[i] * v;
	}    
    }

/* figure out ripple for current point inside of a wave surface */
void calcripple(point,w,ripple)
VEC	point;
int	w;
VEC	ripple;
    {
    FLTDBL  riprad,damper,tmp;
    int	    i,iriprad;

    riprad = 0.0;
    for (i=0; i<3; i++) {
	ripple[i] = tmp = waves[w].cen[i] - point[i];
	riprad	 += tmp * tmp;
	}
    /* unit vector pointing away from wave center */
    vunit(ripple,ripple);

    riprad  =  sqrt(riprad);	    /* distance from center to point */
    riprad +=  waves[w].phase * waves[w].length;  /* move it */

    riprad /= waves[w].length;  /* scale half-wave crest to 0..1 */
    if (waves[w].damp == 1.0)	damper = 1.0;
    else			damper = pow(waves[w].damp,riprad);

    iriprad = (int)riprad;
    if (iriprad & 1)  {/* negate it */
	vscale(-1.0,ripple,ripple);
	}
    riprad -= (FLTDBL) iriprad;		    /* just get fraction 0..1 */
    riprad -= 0.5;			    /* scale to -0.5 .. +0.5 */
    if (riprad < 0.0) riprad = -riprad;	    /* absolute value */
    riprad  = 0.5 - riprad;		    /* invert */
    riprad *= damper;
    riprad *= waves[w].amp;
    vscale(riprad,ripple,ripple);	    /* scale bend */
    }

/* find surface normal for any object */
FLTDBL findnormal(s,dir,intersect,normal,eflip)
NODE	*s;
VEC	dir,intersect,normal;
int	*eflip;
    {
    int	    i;
    FLTDBL  d;
    VEC	    ripple,fuzzy;

    /* first get the standard normal */
    switch (s->typ) {
	case TYP_S: 	 spherenormal(intersect,s,normal); break;
	default:	 planenormal(s,normal);
	}

    /* do any desired waves */
    if (s->att->wave != -1) { /* don't bother checking if there's no waves*/
	if (s->att->wave == -2) { /* fiddle with normal for all ripples */
	    for (i = 0; i < nwaves; i++) {
		calcripple(intersect,i,ripple);  /* calculate the wave perturbation */
		vadd(ripple,normal,normal);  /* add ripple bend to normal */
		}
	    }

	/* fiddle with normal for one ripple */
    	else if (s->att->wave >= 0) {
	    calcripple(intersect,s->att->wave,ripple);  /* calc wave perturbation */
	    vadd(ripple,normal,normal);  /* add ripple bend to normal */
	    }
	vunit(normal,normal);  /* make sure it's a unit vector after all that */
	}

    /* Add any general normal perturbations */
    if (s->att->kf > 0.0) {

	/* Perturb the normal randomly to produce fuzzy surfaces */
	fuzzy[0] = rnd();  /* 0..1 */
	fuzzy[1] = rnd();
	fuzzy[2] = rnd();
	if (rnd() < 0.5) fuzzy[0] = -fuzzy[0];
	if (rnd() < 0.5) fuzzy[1] = -fuzzy[1];
	if (rnd() < 0.5) fuzzy[2] = -fuzzy[2];

	/* 'fuzzy' is now approximately a random unit vector */
	vcomb(rnd() * s->att->kf,fuzzy,normal,normal);
	vunit(normal,normal);  /* make sure it's a unit vector after all that */
	}

    /* make sure the normal is on the correct side of the surface */
    d	    = -vdot(dir,normal);
    *eflip  = 0;
    if (d < 0.0) {
	vscale(-1.0, normal, normal);
	*eflip	= 1;
	d	= -d;
	}

    return d;
    }


