#ifndef lint
static char *sccsid = "@(#)cast.c	1.5 (Steve Hill) 5/24/90";
#endif

/* cast.c
 *
 * This module is concerned with generating the initial rays
 * from a world description.
 */

#include <stdio.h>

#include "basetype.h"
#include "cartesian.h"
#include "matrix.h"
#include "point.h"
#include "vector.h"
#include "pointvector.h"
#include "indexlist.h"
#include "ray.h"
#include "colour.h"
#include "quadric.h"
#include "surface.h"
#include "bitmap.h"
#include "pattern.h"
#include "solid.h"
#include "bbox.h"
#include "light.h"
#include "world.h"
#include "list.h"
#include "hitlist.h"
#include "hitdata.h"
#include "intersect.h"
#include "shading.h"
#include "output.h"
#include "cast.h"

/* StartPixel
 *
 * Given the three perpendicular vectors, and the location of
 * the camera, and the distance of the view plane from the camera,
 * this function calculates the top left corner of the view plane.
 *
 * NOTE: It assumes that the horz, vert, and view vectors have
 * been normalised.
 */

point_t *
StartPixel(world)
world_t		*world;
{
	point_t		*start;
	vector_t	half_horz, half_vert, d_view;

	VectorScale(&half_horz, - world->view_x / REAL_TWO, world->horz_vector);
	VectorScale(&half_vert, world->view_y / REAL_TWO, world->vert_vector);
	VectorScale(&d_view   , world->view_plane, world->view_vector);

	start = AddVector(PointZero, &half_horz, world->view_point);
	start = AddVector(start,     &half_vert, start);
	start = AddVector(start,     &d_view,    start);

	return(start);
}

/* Dither
 *
 * Experimental colour ditherer.
 */

void
Dither(colour)
colour_t	*colour;
{
	long	l;
	real_t	r;

	l = rand();
	r = (real_t) (l % 10) - 5.0;

	colour->r = colour->r * (100 + r) / 100;
	colour->g = colour->g * (100 + r) / 100;
	colour->b = colour->b * (100 + r) / 100;
}

/* RayTrace
 *
 * Cast rays for whole screen and gather colours.
 */

void
RayTrace(world)
world_t	*world;
{
	vector_t	dir, h_pixel, v_pixel;
	point_t		pixel, *origin_pixel;
	int		x, y;
	ray_t		ray;
	pixel_t		*pixels;

	srand(123);

	pixels = Pixels(world->stop_x - world->start_x + 1);

	if (world->file_name != NULL)
	{
		world->file = fopen(world->file_name, "w");
		if (world->file == NULL)
			FatalError(__FILE__, __LINE__,
				   "Cannot open output file");
	}

	OutHeader(world->file, world->stop_x - world->start_x + 1,
			       world->stop_y - world->start_y + 1);

	VectorScale(&h_pixel, (world->view_x / world->pixels_x),
					  world->horz_vector);
	VectorScale(&v_pixel, (-world->view_y / world->pixels_y),
					  world->vert_vector);

	origin_pixel = StartPixel(world);

	RaySet(&ray, world->view_point, &dir, IndexListNull);

	for (y = world->start_y; y <= world->stop_y; y++)
	{
		for (x = world->start_x; x <= world->stop_x; x++)
		{
			colour_t	colour;
			vector_t	horiz, vert;

			PointLet(&pixel, origin_pixel);
			VectorScale(&horiz, x, &h_pixel);
			VectorScale(&vert, y, &v_pixel);
			AddVector(&pixel, &horiz, &pixel);
			AddVector(&pixel, &vert, &pixel);

			colour.r = colour.g = colour.b = REAL_ZERO;
			MakeVector(&dir, world->view_point, &pixel);

			Trace(&colour, &ray, world, REAL_ONE);
			Dither(&colour);
			PixelColour(&pixels[x], &colour);
		}
		OutScanline(world->file, y, world->start_x, world->stop_x, pixels);
		if (world->report_progress)
		{
			fputc('.', stderr);
			fflush(stderr);
		}
	}
	if (world->report_progress)
	{
		fputc('\n', stderr);
		fflush(stderr);
	}
}
