
/*
 * (c) 1988 by George Kyriazis
 */

/* 
 * This is the actual ray-tracing part
 */

#include	"ray.h"
#include	"vector.h"
#include	<stdio.h>
#include	<math.h>

struct	intersect	intersect();
struct	color	shade();
struct	color	bgcolor();

double	quickcos(x)
double	x;
{
	double	val;

	val = 1 - x*x/2.4684;

	return val;
}

double	quickinvcos(x)
double	x;
{
	double	val;

	val = sqrt(2.4684*(1-x));

	return val;
}

/*
 * floating point random number generator
 */
double	rnd()
{
	double	t;

	t = (double)( random() & 0xffffff ) / 16777215;

	return t;
}

/*
 * Random number between -1 and 1
 */
double	rand1()
{
	return ( (rnd()-.5) * 2);
}

/*
 * approximate a gaussian random number generator between -1 and 1
 */
double	grand()
{
	double	t;

/* get a random number between -1 and 1 */
	t = ( rnd() - .5 ) * 2;

/* and then square it.  DOn't lose the sign! */
	return (t*ABS(t));
}

/*
 * pick a random ray somewhere inside the solid angle
 */
struct	ray	sample_ray(r, theta)
struct	ray	r;
double	theta;
{
	double	phi1, phi2;
	struct	vector	c;	/* directional cosines */
	struct	ray	r2;

/*
 * adds the following 2 angles in 2 of the 3 angles specified by the
 * directional cosines
 */
	phi1 = grand() * theta;
	phi2 = grand() * theta;

	c = r.dir;

/* choose two of them that are linearly independent */
	if( c.x > .5 ) {
		c.x = quickcos(quickinvcos(c.x) + phi1);
		c.y = quickcos(quickinvcos(c.y) + phi2);
	} else
		if( c.y < .5 ) {
			c.y = quickcos(quickinvcos(c.y) + phi1);
			c.z = quickcos(quickinvcos(c.z) + phi2);
		} else {
			c.z = quickcos(quickinvcos(c.z) + phi1);
			c.x = quickcos(quickinvcos(c.x) + phi2);
		}

/* the third directional cosine is fixed when normalizing */
	r2.pos = r.pos;
	r2.dir = norm( c );
	r2.theta = 0;
	r2.obj = r.obj;

	return r2;
}

/*
 * trace a single strait ray
 */
struct	color	trace_a_ray(r, n)
struct	ray	r;
int	n;		/* recursion level */
{
	struct	intersect	inter;
	struct	color	col;
	double	m;

/* update stats */
	rayline++;
/* check for intersection with the object */
	inter = intersect(r);
/* if no intersection, return some background color */
	if( inter.obj == NULL )
		return bgcolor(r);
/* more stats */
	intersectline++;
/* else calculate the shading function there */
	col = shade(inter, r, n);
/* if the color > 1, that means that the components are too big. Normalize. */
	m = col.r;
	if( col.g > m )
		m = col.g;
	if( col.b > m )
		m = col.b;
	if( m > 1 ) {
/* overflow condition */
/* normalize it! */
		col.r /= m;
		col.g /= m;
		col.b /= m;
	}
	return col;
}

/*
 * Trace a ray within the specified solid angle
 */
struct	color	trace(r, n)
struct	ray	r;
int	n;
{
	struct	ray	r2;

	r2 = sample_ray(r, r.theta);

	return ( trace_a_ray(r2, n) );
}

/*
 * raytrace the whole scene
 */
raytrace(fname)
char	*fname;
{
	int	x, y, x1, y1;
	double	xr, yr, xstep, ystep;
	struct	color	col, color;
	struct	ray	ray, r2;
	int	r, g, b;
	double	m;
	FILE	*f;
	int	i;
	double	p_w;

	p_w = fov / MIN(xres, yres);	/* pixel width in radians */

	hor = norm( vcross(up, eye_dir) );	/* the x screen vector */
	ver = norm( vcross( eye_dir, hor) );	/* the y screen vector */

	f = fopen(fname, "w");
	if(f == NULL) {
		perror("fopen");
		exit(1);
	}

	ray.pos = eye;		/* eye is the beginning of the ray */

	ray.obj = NULL;		/* not coming from an object */

	fwrite(&xres, sizeof(int), 1, f);
	fwrite(&yres, sizeof(int), 1, f);

	yr = 1.;
	xstep = 2. / xres; ystep = 2. / yres;
	for(y = 0; y < yres;y++) {
		xr = -1.;
		for(x = 0; x < xres; x++) {
		/* ray direction calculations */
			ray.dir = vadd( svproduct(xr*fov, hor),
				svproduct(yr*fov, ver) );
			ray.dir = norm( vadd( ray.dir, eye_dir) );
			ray.theta = 0;
			col.r = col.g = col.b = 0;

/* the time blur has to be done in linear time and not randomly */
/* randomization is used so we won't get the strobo effect      */
			for( i = tries; i--; ) {
				Time = (time2+time1)/2 +
					(time2-time1)*(i+rand1())/tries;
				r2 = sample_ray(ray, p_w);
				color = trace_a_ray(r2, 0);
			/* sum all the intensities together */
				col.r += color.r / tries;
				col.g += color.g / tries;
				col.b += color.b / tries;
			}
		/* calc the integer value to be put to the file */
			r = col.r * 255;
			g = col.g * 255;
			b = col.b * 255;
			putc(r,f);
			putc(g,f);
			putc(b,f);

			xr += xstep;
		}
		yr -= ystep;
		printf("Finished scanline %d. r:%.2lf/%d sh:%d rl:%d rr:%d int:%d\n",
			y, (double)rayline / yres,  rayline, shadowline,
			reflectline, refractline, intersectline);
	/* update statistics */
		raycount += rayline; rayline = 0;
		shadowcount += shadowline; shadowline = 0;
		reflectcount += reflectline; reflectline = 0;
		refractcount += refractline; refractline = 0;
		intersectcount += intersectline; intersectline = 0;
		objtestcount += objtestline; objtestline = 0;
	}
	printf("\nTotal number of rays traced: %d\n", raycount);
	printf("Total number of not shadowed intersections: %d\n", shadowcount);
	printf("Total reflected rays traced: %d\n", reflectcount);
	printf("Total refracted rays traced: %d\n", refractcount);
	printf("Total object intersections: %d\n", intersectcount);
	printf("Total objects tested: %d\n", objtestcount);
}
