/* implementation of the raytracer class */



#include "headers.h"
#include <iostream.h>
#include <math.h>
extern xyz nor;
void ray_tracer :: set_up()
{
	
	
//background_color= new color(20,20,20);
objects[0]=new sphere(20.0,170.0,-30.0,-230.0,&surface5);
cur_object++;
objects[1]=new sphere(10.0,130.0,-40.0,-240,&surface2);
cur_object++;


//enter the points of the polygon in clockwise direction

objects[2]=new quad(0,-100,-300,250,-100,-300,250,-100,-150,0,-100,-150,&surface2);
cur_object++;

//objects[3]=new sphere(10.0,140.0,-10.0,-240,&surface1);
//cur_object++;

objects[3]=new sphere(7.0,160.0,-30,-200,&surface3);
cur_object++;
objects[4]=new quad(0,50,-300,250,50,-300,250,-100,-300,0,-100,-300,&surface2);
cur_object++;
/*objects[6]=new sphere(40.0,140.0,0.0,0,&surface3);
cur_object++;
objects[7]=new sphere(10.0,100.0,0.0,200,&surface3);
cur_object++;
*/
//lights[1]= new point_light(200,100,20,.6,0);
//cur_light++;
//lights[0]= new point_light(0,15,20,1,0);
//cur_light++;
lights[0]= new area_light(0,30,20,0,30,-20,0,0,-20,0,0,20,.6,1);
cur_light++;
//lights[1]= new area_light(250,150,300,250,150,250,300,150,250,300,150,300,.3,1);
//cur_light++;

if(enable_texturing==1)
{

	tex.load_texturefile();
	tex.bilinear=1;  // 1 - bilinear filtering is on  0 - bilinear filtering is off
}

viewing();
}



void ray_tracer :: viewing()
{
double distance;
xyz s_zaxis,ray1,ray2,ray3;
s_zaxis.x=focus.x-camera.x;
s_zaxis.y=focus.y-camera.y;
s_zaxis.z=focus.z-camera.z;
distance = s_zaxis.normalize();
s_xaxis=&ray2;
cross_product(s_xaxis,&s_zaxis,&up_vector);
s_xaxis->normalize();
s_yaxis=&ray3;
cross_product(s_yaxis,s_xaxis,&s_zaxis);
s_yaxis->normalize();
distance=distance*2.0;
magnitude_x = distance * tan(hor_fov * 0.01745329)/hor_res;  /* 0.1745329 is the constant to convert degrees to radians */
s_xaxis->x *= magnitude_x;
s_xaxis->y *= magnitude_x;
s_xaxis->z *= magnitude_x;
magnitude_y= distance * tan(ver_fov * 0.01745329)/ver_res;
s_yaxis->x *= magnitude_y;
s_yaxis->y *= magnitude_y;
s_yaxis->z *= magnitude_y;
ray1.x=focus.x-camera.x;
ray1.y=focus.y-camera.y;
ray1.z=focus.z-camera.z;
ray1.x += ver_res/2 * s_yaxis->x - hor_res/2 * s_xaxis->x;
ray1.y += ver_res/2 * s_yaxis->y - hor_res/2 * s_xaxis->y;
ray1.z += ver_res/2 * s_yaxis->z - hor_res/2 * s_xaxis->z;
start_ray=&ray1;
}





color ray_tracer::get_intersection(int source,xyz* ray,xyz* origin)
{

	if(source==-1)
	{
		cur_level=0;
	}

	int object_hit,object_test;
	double closest_hit,current_hit,ci,ci2,second_hit;
	surface *surf;
	xyz *refract,*reflect,*refract_out;
	xyz hit_point,normal,hit_point2,flip_normal,refract1;
	color color,color_reflect,color_refract,glassy_color,spec_color;
	closest_hit = 99.99E+20;
	object_hit = -1;
	for(object_test=0;object_test<cur_object;object_test++)
	{
		if(object_test != source)
		{
			current_hit=objects[object_test]->intersect(origin,ray);
			if((current_hit>0.0) && (current_hit<=closest_hit))
			{
				object_hit=object_test;
				closest_hit=current_hit;
			}
		}
	}
if(prev_object_hit_primaryray!=object_hit&&source==-1)
{
flag=1;
}
	
if(prev_object_hit!=object_hit && source!=-1)	
{

flag=1;
			
}
		
		
	if(object_hit<0)
	{
	
			if(source==-1)
				prev_object_hit_primaryray=object_hit;
			
			if(source!=-1)
				prev_object_hit=object_hit;
		
		

		return(background_color);
	}



	hit_point.x=origin->x+closest_hit*ray->x;
	hit_point.y=origin->y+closest_hit*ray->y;
	hit_point.z=origin->z+closest_hit*ray->z;

	objects[object_hit]->normal(&hit_point,&normal);

	surf=&(objects[object_hit]->get_surface());

	ci=dot_product(ray,&normal);

		if(ci>0)
	{
		normal.x=-normal.x;
		normal.y=-normal.y;
		normal.z=-normal.z;

	}



			if(surf->reflectivity==0 && surf->transparency==0)
				prev_object_hit_primaryray=object_hit;
			
			if(source!=-1)
				prev_object_hit=object_hit;
double refl,transp;

		if(surf->glassy != 0)
		{
	 refl= fresnel(medium_refractiveindex,surf->refractive_index,ray,&normal);
	 transp= 1- refl;
		}

//	adds transparency

	if(surf->glassy!=0)
	{

	refract=&(refraction(medium_refractiveindex,surf->refractive_index,ray,&normal));
	
	second_hit=objects[object_hit]->intersect(&hit_point,refract);



	hit_point2.x=hit_point.x+second_hit*refract->x;
	hit_point2.y=hit_point.y+second_hit*refract->y;
	hit_point2.z=hit_point.z+second_hit*refract->z;

	objects[object_hit]->normal(&hit_point2,&flip_normal);

	ci2=dot_product(refract,&flip_normal);

		if(ci2>0)
	{
		flip_normal.x=-flip_normal.x;
		flip_normal.y=-flip_normal.y;
		flip_normal.z=-flip_normal.z;

	}
	
	refract_out=&(refraction(surf->refractive_index,medium_refractiveindex,refract,&flip_normal));

	color_refract=get_intersection(object_hit,refract_out,&hit_point2);


	}

//adds reflection

	if((surf->reflectivity!=0 || surf->glassy!=0) && cur_level < max_level)
	{
	cur_level++;
	reflect = &(reflection(ray,&normal));
	color_reflect=get_intersection(object_hit,reflect,&hit_point);
	}

	shade(&hit_point,ray,&normal,surf,&color,object_hit,&spec_color);

	
	//color.r+=surf->transparency*color_refract.r;
	//color.g+=surf->transparency*color_refract.g;
	//color.b+=surf->transparency*color_refract.b;
	


	if(surf->glassy!=0)
	{
	glassy_color.r=spec_color.r+(transp*color_refract.r)+(refl*color_reflect.r);
	glassy_color.g=spec_color.g+(transp*color_refract.g)+(refl*color_reflect.g);
	glassy_color.b=spec_color.b+(transp*color_refract.b)+(refl*color_reflect.b);
	color.r=(surf->diffuse*color.r+surf->glassy*glassy_color.r);
	color.g=(surf->diffuse*color.g+surf->glassy*glassy_color.g);
	color.b=(surf->diffuse*color.b+surf->glassy*glassy_color.b);
	}

	if(surf->reflectivity!=0)
	{
	color.r=(1-surf->diffuse)*spec_color.r+(surf->diffuse*color.r + surf->reflectivity*color_reflect.r);
	color.g=(1-surf->diffuse)*spec_color.g+(surf->diffuse*color.g + surf->reflectivity*color_reflect.g);
	color.b=(1-surf->diffuse)*spec_color.b+(surf->diffuse*color.b + surf->reflectivity*color_reflect.b);
	}

	return(color);
}



void ray_tracer::shade(xyz *hit_point,xyz *ray,xyz *normal,surface *surf,color *color1,int object_hit,color* spec_color)
{
	xyz reflected,light_ray,*ashadow_ray;
	int light_test,object_test,object_test1,x,y;
	double diffuse,bright,specular,k,obscure=0;


	if(surf->texture==0)
	{
	color1->r=surf->ar;
	color1->g=surf->ag;
	color1->b=surf->ab;
	}
	else if(surf->texture==1 && enable_texturing==1)
	{

		double u=0,v=0;
		xy *cord;
	cord=&(objects[object_hit]->get_uv(hit_point));

	u=cord->x;
	v=1-cord->y;
	nor.x=u;
	nor.y=v;
	texture_color=&(tex.return_texturecolor(u,v));
	//color1->r=texture_color->r;
	//color1->g=texture_color->g;
	//color1->b=texture_color->b;
	}


	k= -2.0 * dot_product(ray,normal);
	reflected.x=k* normal->x + ray->x;
	reflected.y=k* normal->y + ray->y;
	reflected.z=k* normal->z + ray->z;
	cur_obscure=0;
	double inc=shadow_samples*shadow_samples;
	inc=1/inc;

	for(light_test=0;light_test<cur_light;light_test++)
	{

		//check for area light
	
		if(lights[light_test]->type==1)
		{
		
			for(y=0;y<shadow_samples;y++)
			for(x=0;x<shadow_samples;x++)
			{
	
			ashadow_ray=&(lights[light_test]->calc_brightness(y,x,hit_point,shadow_samples));
			

			for(object_test1=0;object_test1<cur_object;object_test1++)
			{
			
			if(	(objects[object_test1]->intersect(hit_point,ashadow_ray))>.001)
			{

			obscure=obscure+inc;

			break;

			}

			}
			

			}

		}


		
//check for point light

		if(lights[light_test]->type==0) 
		{

		lights[light_test]->hit_to_light(hit_point,&light_ray);	

/*checks for the points that are in the shadows.. the small value .001 to avoid
self intersection of hit points on their own objects*/

for(object_test=0;object_test<cur_object;object_test++)
{

	if(	(objects[object_test]->intersect(hit_point,&light_ray))>.001)
	{
		
		obscure=1;

		cur_obscure++;
		break;
	
	}
}// check for point light end

		} 

	bright=lights[light_test]->brightness();

	if(lights[light_test]->type==1)
	{


		light_ray=lights[light_test]->calc_brightness(shadow_samples/2,shadow_samples/2,hit_point,shadow_samples);
		bright=bright*(1-obscure);

	}

		diffuse = dot_product(normal,&light_ray);

			if(surf->texture==1 && enable_texturing==1)
		{
				if(diffuse > 0.0 )
				{
		diffuse = bright*diffuse;
		color1->r += texture_color->r *(1-obscure) * diffuse;
		color1->g += texture_color->g *(1-obscure) * diffuse;
		color1->b += texture_color->b *(1-obscure) * diffuse;
				}
		}

if(obscure<1)
{
	

		if(diffuse > 0.0 )
		{
	    	if(!(surf->texture==1 && enable_texturing==1))
			{
			diffuse *= bright;
			color1->r += surf->dr * diffuse;
			color1->g += surf->dg * diffuse;
			color1->b += surf->db * diffuse;
			}


		specular = dot_product(&reflected,&light_ray);

		if(specular > 0.0)
			{
			specular = bright * pow(specular,surf->spec_coef);
			color1->r += surf->sr * specular;
			color1->g += surf->sg * specular;
			color1->b += surf->sb * specular;
			spec_color->r += surf->sr * specular;
			spec_color->g += surf->sg * specular;
			spec_color->b += surf->sb * specular;
		}
		}

}


	}
	if(prev_obscure!=cur_obscure)
{
	flag=1;
	prev_obscure=cur_obscure;
}

}
