#define PATCHLEVEL 0
/*
**    ScEd: A Constraint Based Scene Editor.
**    Copyright (C) 1994-1995  Stephen Chenney (stephen@cs.su.oz.au)
**
**    This program is free software; you can redistribute it and/or modify
**    it under the terms of the GNU General Public License as published by
**    the Free Software Foundation; either version 2 of the License, or
**    (at your option) any later version.
**
**    This program is distributed in the hope that it will be useful,
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**    GNU General Public License for more details.
**
**    You should have received a copy of the GNU General Public License
**    along with this program; if not, write to the Free Software
**    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
**	sced: A Constraint Based Object Scene Editor
**
**	rayshade.c : export functions for rayshade.
**
**	External function (there's just one).
**
**	void
**	Export_Rayshade(FILE *outfile)
**	Exports all the relevant info into outfile.
*/

#include <math.h>
#include <sced.h>
#include <attributes.h>
#include <base_objects.h>
#include <csg.h>
#include <hash.h>
#include <time.h>

static int	Export_Camera(FILE*, Camera);
static int	Export_Light(FILE*, ObjectInstancePtr);
static int	Export_Basetypes(FILE*, BaseObjectPtr);
static int	Export_CSG_Tree(FILE*, CSGNodePtr);
static int	Export_Wireframe(FILE*, WireframePtr);
static int	Export_Instances(FILE*, InstanceList);
static void Export_Object_Type(FILE*, ObjectInstancePtr);
static void Export_Transformation(FILE*, ObjectInstancePtr);
static void Export_Attributes(FILE*, AttributePtr);
static void	Export_Texture(FILE*, ObjectInstancePtr);
static int	Export_Declarations(FILE*);


static HashTable	base_hash;



/*	int
**	Export_Rayshade(FILE *outfile, ScenePtr scene)
**	Exports all the relevant info into outfile.
*/
int
Export_Rayshade(FILE *outfile, ScenePtr scene)
{
	time_t	current_time;

	time(&current_time);

	base_hash = Hash_New_Table();

	if ( fprintf(outfile, "/* File generated by sced */\n") < 0 ||
		 fprintf(outfile, "/* %s */\n\n", ctime(&current_time)) < 0 ||
		 fprintf(outfile, "\nname my_cylinder list\n") < 0 ||
		 fprintf(outfile, "\tcylinder 1 0.0 0.0 -1.0 0.0 0.0 1.0\n") < 0 ||
		 fprintf(outfile, "\tdisc 1 0.0 0.0 1.0 0.0 0.0 1.0\n") < 0 ||
		 fprintf(outfile, "\tdisc 1 0.0 0.0 -1.0 0.0 0.0 -1.0\n") < 0 ||
		 fprintf(outfile, "end\n\n") < 0 ||
		 fprintf(outfile, "\nname my_cone list\n") < 0 ||
		 fprintf(outfile, "\tcone 1 0.0 0.0 -1.0 0.0 0.0 0.0 1.0\n") < 0 ||
		 fprintf(outfile, "\tdisc 1 0.0 0.0 -1.0 0.0 0.0 -1.0\n") < 0 ||
		 fprintf(outfile, "end\n\n") < 0 ||
		 Export_Camera(outfile, scene->camera) < 0 ||
		 fprintf(outfile, "light %g %g %g ambient\n\n",
				scene->ambient.red, scene->ambient.green,
				scene->ambient.blue) < 0 ||
		 Export_Declarations(outfile) < 0 ||
		 Export_Light(outfile, scene->light) < 0 ||
		 Export_Instances(outfile, scene->instances) < 0)
 	{
 		Popup_Error("Write Failed!", main_window.shell, "Error");
 		return 0;
 	}

	Hash_Free(base_hash);

	return 1;
}



static int
Export_Camera(FILE *outfile, Camera cam)
{
	if ( ! cam.defined ) return 1;

	/* Rayshade camera info just goes at the top. */
	fprintf(outfile, "/* Camera definitions. */\n");
	fprintf(outfile, "screen %d %d\n", cam.scr_width, cam.scr_height);
	fprintf(outfile, "eyep ");	VPrint(outfile, cam.location);
	fprintf(outfile, "lookp ");	VPrint(outfile, cam.look_at);
	fprintf(outfile, "up ");	VPrint(outfile, cam.look_up);
	fprintf(outfile, "fov %g %g\n", cam.horiz_fov, cam.vert_fov);
	fprintf(outfile, "focaldist %g\n", cam.eye_dist);

	return fprintf(outfile, "\n");
}


static int
Export_Light(FILE *outfile, ObjectInstancePtr light)
{
	double	radius;
	double	cos_rad;
	Vector	vect1, vect2;
	Vector	direction;

	if ( ! light ) return 1;

	fprintf(outfile, "light ");
	fprintf(outfile, "%g %g %g ",
			((LightInfoPtr)light->o_attribs)->red,
			((LightInfoPtr)light->o_attribs)->green,
			((LightInfoPtr)light->o_attribs)->blue);

	if ( light->o_parent->b_class == light_obj )
	{
		fprintf(outfile, "point ");
		VPrint(outfile, light->o_transform.displacement);
	}

	if ( light->o_parent->b_class == spotlight_obj )
	{
		/* Calculate the radius. */
		VSub(light->o_world_verts[0], light->o_world_verts[9], vect1);
		VSub(light->o_world_verts[8], light->o_world_verts[9], vect2);
		VUnit(vect1, radius, vect1);
		VUnit(vect2, radius, vect2);
		cos_rad = VDot(vect1, vect2);
		radius = acos(cos_rad) * 180 / M_PI;

		if ( ((LightInfoPtr)light->o_attribs)->flag )
		{
			/* Invert it. */
			/* vect2 still points toward direction. */
			VScalarMul(vect2, -1.0, vect2);
			VAdd(vect2, light->o_world_verts[9], direction);

			radius += 90.0;
		}
		else
			direction = light->o_world_verts[8];

		fprintf(outfile, "spot ");
		VPrint(outfile, light->o_transform.displacement);
		fprintf(outfile, "\t");
		VPrint(outfile, direction);
		fprintf(outfile, "\t%g %g %g\n",
				((LightInfoPtr)light->o_attribs)->val2,
				radius,
				radius * ((LightInfoPtr)light->o_attribs)->val1);
	}

	if ( light->o_parent->b_class == arealight_obj )
	{
		fprintf(outfile, "\tarea ");
		VPrint(outfile, light->o_world_verts[1]);
		fprintf(outfile, "\t");
		VPrint(outfile, light->o_world_verts[0]);
		fprintf(outfile, "\t%d\n\t",
				(int)(((LightInfoPtr)light->o_attribs)->val1));
		VPrint(outfile, light->o_world_verts[2]);
		fprintf(outfile, "\t%d\n",
				(int)(((LightInfoPtr)light->o_attribs)->val2));
	}

	if ( light->o_parent->b_class == dirlight_obj )
	{
		fprintf(outfile, "\tdirectional ");
		VSub(light->o_world_verts[5], light->o_world_verts[0], vect1);
		VPrint(outfile, vect1);
	}

	return fprintf(outfile, "\n");
}


static void
Export_CSG_Bases(FILE *outfile, CSGNodePtr tree)
{
	if ( ! tree ) return;

	if ( tree->csg_op == csg_leaf_op )
	{
		if ( tree->csg_instance->o_parent->b_class == csg_obj ||
			 tree->csg_instance->o_parent->b_class == wireframe_obj )
			Export_Basetypes(outfile, tree->csg_instance->o_parent);
	}
	else
	{
		Export_CSG_Bases(outfile, tree->csg_left_child);
		Export_CSG_Bases(outfile, tree->csg_right_child);
	}
}


static int
Export_Basetypes(FILE *outfile, BaseObjectPtr base_obj)
{
	CSGNodePtr	new_tree;
	int			gridsize;
	Boolean		did_grid = FALSE;

	if ( Hash_Get_Value(base_hash, (unsigned long)base_obj) != (void*)-1 )
		return 1;

	if ( base_obj->b_class == csg_obj )
	{
		new_tree = CSG_Contract_Tree(base_obj->b_csgptr);
		Export_CSG_Bases(outfile, new_tree);
	}

 	fprintf(outfile, "name %s\n", base_obj->b_label);
	if ( base_obj->b_class == csg_obj )
	{
		gridsize =
			(int)floor(pow((double)CSG_Count_Tree_Size(new_tree), 0.3333));
		if ( gridsize > 1 )
		{
			did_grid = TRUE;
			fprintf(outfile, "grid %d %d %d\n", gridsize, gridsize, gridsize);
		}
		Export_CSG_Tree(outfile, new_tree);
		CSG_Delete_Tree(new_tree, FALSE);
	}
	else
	{
		gridsize =
			(int)floor(pow((double)base_obj->b_major_wires[0]->num_faces,
						   0.3333));
		if ( gridsize > 1 )
		{
			did_grid = TRUE;
			fprintf(outfile, "grid %d %d %d\n", gridsize, gridsize, gridsize);
		}
		Export_Wireframe(outfile, base_obj->b_major_wires[0]);
	}
	if ( did_grid )
		fprintf(outfile, "end\n");

	Hash_Insert(base_hash, (unsigned long)base_obj, (void*)base_obj);

	return fprintf(outfile, "\n");
}


static int
Export_CSG_Tree(FILE *outfile, CSGNodePtr tree)
{
	if ( ! tree ) return 1;

	if ( tree->csg_op == csg_leaf_op )
	{
		if ( tree->csg_instance->o_parent->b_class == csg_obj )
			Export_Basetypes(outfile, tree->csg_instance->o_parent);

		fprintf(outfile, "/* %s */\n", tree->csg_instance->o_label);

		Export_Object_Type(outfile, tree->csg_instance);
		Export_Transformation(outfile, tree->csg_instance);
		if ( ((AttributePtr)tree->csg_instance->o_attribs)->defined &&
			 ((AttributePtr)tree->csg_instance->o_attribs)->use_extension &&
			 ((AttributePtr)tree->csg_instance->o_attribs)->extension[Rayshade])
			Export_Texture(outfile, tree->csg_instance);
	}
	else
	{
		switch ( tree->csg_op )
		{
			case csg_union_op: 
				fprintf(outfile, "union\n");
				break;
			case csg_intersection_op: 
				fprintf(outfile, "intersect\n");
				break;
			case csg_difference_op: 
				fprintf(outfile, "difference\n");
				break;
			default:;
		}

		Export_CSG_Tree(outfile, tree->csg_left_child);
		Export_CSG_Tree(outfile, tree->csg_right_child);

		return fprintf(outfile, "end\n");
	}

	return 1;

}


static void
Export_Face(FILE *outfile, WireframePtr src, FacePtr face)
{
	int		i;
	Vector	temp_v1, temp_v2, norm;

	VSub(src->vertices[face->vertices[0]], src->vertices[face->vertices[1]],
		 temp_v1);
	VSub(src->vertices[face->vertices[0]], src->vertices[face->vertices[2]],
		 temp_v2);
	VCross(temp_v1, temp_v2, norm);
	if ( VZero(norm) )
		return;

	if ( src->vertex_normals && face->num_vertices > 3 )
	{
		/* Triangulate it. */
		WireframePtr face_wire = Face_Triangulate(src, face);

		for ( i = 0 ; i < face_wire->num_faces ; i++ )
			Export_Face(outfile, face_wire, face_wire->faces + i);

		Wireframe_Destroy(face_wire);
	}
	else if ( src->vertex_normals )
	{
		fprintf(outfile, "triangle\n");
		if ( face->face_attribs && face->face_attribs->defined )
			Export_Attributes(outfile, face->face_attribs);
		VPrint(outfile, src->vertices[face->vertices[0]]);
		VPrint(outfile, src->vertex_normals[face->vertices[0]]);
		VPrint(outfile, src->vertices[face->vertices[2]]);
		VPrint(outfile, src->vertex_normals[face->vertices[2]]);
		VPrint(outfile, src->vertices[face->vertices[1]]);
		VPrint(outfile, src->vertex_normals[face->vertices[1]]);
		if ( face->face_attribs && face->face_attribs->defined &&
			 face->face_attribs->use_extension &&
			 face->face_attribs->extension[Rayshade] )
			fprintf(outfile, "\t%s\n", face->face_attribs->extension[Rayshade]);
	}
	else
	{
		fprintf(outfile, "poly\n");
		if ( face->face_attribs && face->face_attribs->defined )
			Export_Attributes(outfile, face->face_attribs);
		for ( i = face->num_vertices - 1 ; i >= 0 ; i-- )
			VPrint(outfile, src->vertices[face->vertices[i]]);
		if ( face->face_attribs && face->face_attribs->defined &&
			 face->face_attribs->use_extension &&
			 face->face_attribs->extension[Rayshade] )
			fprintf(outfile, "\t%s\n", face->face_attribs->extension[Rayshade]);
	}
}


static int
Export_Wireframe(FILE *outfile, WireframePtr wire)
{
	int	i;

	for ( i = 0 ; i < wire->num_faces ; i++ )
		Export_Face(outfile, wire, wire->faces + i);

	return 1;
}


static int
Export_Aliased_Object(FILE *outfile, ObjectInstancePtr obj)
{
	char	*alias = (char*)obj->o_aliases[POVray];
	int		index;

	fprintf(outfile, "/* %s */\n", obj->o_label);

	index = 0;
	while ( alias[index] != '\0' )
	{
		if ( alias[index] == '(' && alias[index + 1] == '*' )
		{
			if ( ! strncmp(alias + index, "(*transform*)", 13) )
			{
				Export_Transformation(outfile, obj);
				index += 13;
			}
			else
				fputc((int)alias[index++], outfile);
		}
		else
			fputc((int)alias[index++], outfile);
	}

	return 1;
}


static int
Export_Instances(FILE *outfile, InstanceList insts)
{
	InstanceList		inst_elmt;
	ObjectInstancePtr	inst;

	fprintf(outfile, "/* Instances */\n");

	for ( inst_elmt = insts ; inst_elmt != NULL ; inst_elmt = inst_elmt->next )
	{
		inst = inst_elmt->the_instance;

		if ( Obj_Is_Construction(inst) )
			continue;

		if ( Obj_Is_Light(inst) )
			Export_Light(outfile, inst);
		else if ( Obj_Is_Aliased(inst, Rayshade) )
			Export_Aliased_Object(outfile, inst);
		else
		{
			if ( inst->o_parent->b_class == csg_obj ||
				 inst->o_parent->b_class == wireframe_obj )
				Export_Basetypes(outfile, inst->o_parent);

			fprintf(outfile, "/* %s */\n", inst->o_label);

			Export_Object_Type(outfile, inst);
			Export_Transformation(outfile, inst);
			if ( ((AttributePtr)inst->o_attribs)->defined &&
				 ((AttributePtr)inst->o_attribs)->use_extension &&
				 ((AttributePtr)inst->o_attribs)->extension[Rayshade] )
				Export_Texture(outfile, inst);
		}
	}

	return fprintf(outfile, "\n");
}




static void
Export_Object_Type(FILE *outfile, ObjectInstancePtr inst)
{
	WireframePtr	bezier_wire;

	switch ( inst->o_parent->b_class )
	{
		case sphere_obj:
			fprintf(outfile, "sphere\n");
			if ( ((AttributePtr)inst->o_attribs)->defined )
				Export_Attributes(outfile, ((AttributePtr)inst->o_attribs));
			fprintf(outfile, "1 0.0 0.0 0.0\n");
			break;
		case cylinder_obj:
			if ( ((AttributePtr)inst->o_attribs)->defined &&
				 ((AttributePtr)inst->o_attribs)->open)
				fprintf(outfile, "\tcylinder\n");
			else
				fprintf(outfile, "object\n");
			if ( ((AttributePtr)inst->o_attribs)->defined )
				Export_Attributes(outfile, ((AttributePtr)inst->o_attribs));
			if ( ((AttributePtr)inst->o_attribs)->defined &&
				 ((AttributePtr)inst->o_attribs)->open)
				fprintf(outfile, "\t1 0.0 0.0 -1.0 0.0 0.0 1.0\n");
			else
				fprintf(outfile, "\tmy_cylinder\n");
			break;
		case cone_obj:
			if ( ((AttributePtr)inst->o_attribs)->defined &&
				 ((AttributePtr)inst->o_attribs)->open)
				fprintf(outfile, "\tcone\n");
			else
				fprintf(outfile, "object\n");
			if ( ((AttributePtr)inst->o_attribs)->defined )
				Export_Attributes(outfile, ((AttributePtr)inst->o_attribs));
			if ( ((AttributePtr)inst->o_attribs)->defined &&
				 ((AttributePtr)inst->o_attribs)->open)
				fprintf(outfile, "\t1 0.0 0.0 -1.0 0.0 0.0 0.0 1.0\n");
			else
				fprintf(outfile, "\tmy_cone\n");
			break;
		case cube_obj:
			fprintf(outfile, "box\n");
			if ( ((AttributePtr)inst->o_attribs)->defined )
				Export_Attributes(outfile, ((AttributePtr)inst->o_attribs));
			fprintf(outfile, "-1 -1 -1 1 1 1\n");
			break;
		case torus_obj:
			fprintf(outfile, "torus\n");
			if ( ((AttributePtr)inst->o_attribs)->defined )
				Export_Attributes(outfile, ((AttributePtr)inst->o_attribs));
			fprintf(outfile, "%1.15g 1.0 0 0 0 0 0 1\n",
					((TorusPtr)inst->o_hook)->major_radius);
			break;
		case plane_obj:
			fprintf(outfile, "plane\n");
			if ( ((AttributePtr)inst->o_attribs)->defined )
				Export_Attributes(outfile, ((AttributePtr)inst->o_attribs));
			fprintf(outfile, "0 0 0 0 0 1\n");
			break;
		case triangle_obj:
			fprintf(outfile, "poly\n");
			if ( ((AttributePtr)inst->o_attribs)->defined )
				Export_Attributes(outfile, ((AttributePtr)inst->o_attribs));
			VPrint(outfile, control_part(inst)->control_verts[2]);
			VPrint(outfile, control_part(inst)->control_verts[1]);
			VPrint(outfile, control_part(inst)->control_verts[0]);
			break;
		case csg_obj: 
		case wireframe_obj:
			fprintf(outfile, "object\n");
			if ( ((AttributePtr)inst->o_attribs)->defined )
				Export_Attributes(outfile, ((AttributePtr)inst->o_attribs));
			fprintf(outfile, "\t%s\n", inst->o_parent->b_label);
			break;
		case bezier_obj:
			fprintf(outfile, "grid %d %d 1\n",
					Wireframe_Density_Level(inst) + 2,
					Wireframe_Density_Level(inst) + 2);
			bezier_wire = Object_To_Wireframe(inst, FALSE, FALSE);
			Export_Wireframe(outfile, bezier_wire);
			Wireframe_Destroy(bezier_wire);
			fprintf(outfile, "end\n\n");
			break;
		default:;
	}
}


static void
Export_Transformation(FILE *outfile, ObjectInstancePtr inst)
{
	Matrix			transp;

	fprintf(outfile, "\ttransform\n");
	MTrans(inst->o_transform.matrix, transp);
	fprintf(outfile, "\t");	VPrint(outfile, transp.x);
	fprintf(outfile, "\t");	VPrint(outfile, transp.y);
	fprintf(outfile, "\t");	VPrint(outfile, transp.z);
	fprintf(outfile, "\t"); VPrint(outfile, inst->o_transform.displacement);
}


static void
Export_Attributes(FILE *outfile, AttributePtr attribs)
{
	fprintf(outfile, "\tambient %g %g %g\n",
		attribs->colour.red, attribs->colour.green, attribs->colour.blue);
	fprintf(outfile, "\tdiffuse %g %g %g\n",
		attribs->colour.red * attribs->diff_coef,
		attribs->colour.green * attribs->diff_coef,
		attribs->colour.blue * attribs->diff_coef);
	fprintf(outfile, "\tspecular %g %g %g\n",
		attribs->colour.red * attribs->spec_coef,
		attribs->colour.green * attribs->spec_coef,
		attribs->colour.blue * attribs->spec_coef);
	fprintf(outfile, "\tspecpow %g\n", attribs->spec_power);
	fprintf(outfile, "\treflect %g\n", attribs->reflect_coef);
	fprintf(outfile, "\ttransp %g\n", attribs->transparency);
	fprintf(outfile, "\tindex %g\n", attribs->refract_index);
}


static void
Export_Texture(FILE *outfile, ObjectInstancePtr inst)
{
	fprintf(outfile, "\t%s\n",
			((AttributePtr)inst->o_attribs)->extension[Rayshade]);
	if ( ((AttributePtr)inst->o_attribs)->use_obj_trans )
		Export_Transformation(outfile, inst);
}


static int
Export_Declarations(FILE *outfile)
{
	if ( declarations[Rayshade] )
		return fprintf(outfile, "%s\n", declarations[Rayshade]);
	return 1;
}
