// K-3D
// Copyright (c) 1995-2004, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

/** \file
		\author Tim Shead <tshead@k-3d.com>
*/

#include "ilight.h"
#include "imaterial.h"

#include <k3dsdk/algebra.h>
#include <k3dsdk/classes.h>
#include <k3dsdk/frames.h>
#include <k3dsdk/ianimation_render_engine.h>
#include <k3dsdk/imaterial.h>
#include <k3dsdk/imaterial_collection.h>
#include <k3dsdk/imesh_source.h>
#include <k3dsdk/iprojection.h>
#include <k3dsdk/irender_farm.h>
#include <k3dsdk/istill_render_engine.h>
#include <k3dsdk/iviewport_host.h>
#include <k3dsdk/measurement.h>
#include <k3dsdk/object.h>
#include <k3dsdk/persistence.h>
#include <k3dsdk/mesh.h>
#include <k3dsdk/module.h>
#include <k3dsdk/property.h>
#include <k3dsdk/time_source.h>
#include <k3dsdk/transformable.h>
#include <k3dsdk/viewport.h>

#include <sdpgl/sdpgl.h>

#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/path.hpp>

#include <fstream>
#include <iomanip>
#include <iterator>

namespace libk3dyafray
{

/////////////////////////////////////////////////////////////////////////////
// render_engine

class render_engine :
	public k3d::viewport::drawable<k3d::transformable<k3d::persistent<k3d::object> > >,
	public k3d::istill_render_engine,
	public k3d::ianimation_render_engine,
	public k3d::iviewport_host
{
	typedef k3d::viewport::drawable<k3d::transformable<k3d::persistent<k3d::object> > >  base;

public:
	render_engine(k3d::idocument& Document) :
		base(Document),
		m_pixel_width(k3d::init_name("pixel_width") + k3d::init_description("Output pixel width [positive integer]") + k3d::init_value(320) + k3d::init_document(Document) + k3d::init_precision(0) + k3d::init_step_increment(1) + k3d::init_units(typeid(k3d::measurement::scalar)) + k3d::init_constraint(k3d::constraint::minimum(1UL))),
		m_pixel_height(k3d::init_name("pixel_height") + k3d::init_description("Output pixel height [positive integer]") + k3d::init_value(240) + k3d::init_document(Document) + k3d::init_precision(0) + k3d::init_step_increment(1) + k3d::init_units(typeid(k3d::measurement::scalar)) + k3d::init_constraint(k3d::constraint::minimum(1UL)))
	{
		enable_serialization(k3d::persistence::proxy(m_pixel_width));
		enable_serialization(k3d::persistence::proxy(m_pixel_height));

		register_property(m_pixel_width);
		register_property(m_pixel_height);
	
		m_input_matrix.changed_signal().connect(SigC::slot(*this, &render_engine::async_redraw_all));
		m_position.changed_signal().connect(SigC::slot(*this, &render_engine::async_redraw_all));
		m_orientation.changed_signal().connect(SigC::slot(*this, &render_engine::async_redraw_all));
		m_scale.changed_signal().connect(SigC::slot(*this, &render_engine::async_redraw_all));
		
		m_pixel_width.changed_signal().connect(m_aspect_ratio_changed_signal.slot());
		m_pixel_height.changed_signal().connect(m_aspect_ratio_changed_signal.slot());
	}

	void constrain_screen_aspect_ratio(double& Ratio)
	{
		/** \todo Figure out the camera model provided by yafray so we can implement this*/
		// Constrain the screen aspect ratio to match our output image ...
//		Ratio = static_cast<double>(m_pixel_width.property_value()) / static_cast<double>(m_pixel_height.property_value());
	}

	aspect_ratio_changed_signal_t& aspect_ratio_changed_signal()
	{
		return m_aspect_ratio_changed_signal;
	}

	k3d::iprojection* projection()
	{
		/** \todo Figure out the camera model provided by yafray so we can implement this*/
		return 0;
	}

	void on_viewport_draw(const k3d::viewport::render_state& State)
	{
		glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_1D);
		glDisable(GL_TEXTURE_2D);

		if(is_selected())
			glColor3d(1, 1, 1);
		else
			glColor3d(0, 0, 0);

		glLineWidth(1.0f);
		glDisable(GL_LINE_STIPPLE);

		draw_geometry();
	}

	void on_viewport_select(const k3d::viewport::render_state& State)
	{
		draw_geometry();
	}

	void draw_geometry()
	{
		// Our dimensions
		const double bodylength = 0.5 * 0.5;
		const double bodywidth = 0.25 * 0.5;
		const double bodyheight = 0.25 * 0.5;
		const double lenslength = 0.25 * 0.5;
		const double lenswidth = 0.15 * 0.5;
		const double lensheight = 0.1 * 0.5;
		const double lensoffset = 0.05;
		const double filmradius = 0.2;
		const double filmwidth = 0.125 * 0.5;

		// Draw the camera body ...
		glBegin(GL_LINE_LOOP);
		glVertex3d(-bodywidth, bodyheight, bodylength);
		glVertex3d(bodywidth, bodyheight, bodylength);
		glVertex3d(bodywidth, bodyheight, -bodylength);
		glVertex3d(-bodywidth, bodyheight, -bodylength);
		glEnd();

		glBegin(GL_LINE_LOOP);
		glVertex3d(-bodywidth, -bodyheight, bodylength);
		glVertex3d(bodywidth, -bodyheight, bodylength);
		glVertex3d(bodywidth, -bodyheight, -bodylength);
		glVertex3d(-bodywidth, -bodyheight, -bodylength);
		glEnd();

		glBegin(GL_LINES);
		glVertex3d(-bodywidth, bodyheight, bodylength);
		glVertex3d(-bodywidth, -bodyheight, bodylength);
		glVertex3d(bodywidth, bodyheight, bodylength);
		glVertex3d(bodywidth, -bodyheight, bodylength);
		glVertex3d(bodywidth, bodyheight, -bodylength);
		glVertex3d(bodywidth, -bodyheight, -bodylength);
		glVertex3d(-bodywidth, bodyheight, -bodylength);
		glVertex3d(-bodywidth, -bodyheight, -bodylength);
		glEnd();

		// Draw the camera lens ...
		glBegin(GL_LINE_LOOP);
		glVertex3d(-lenswidth, lensheight, bodylength);
		glVertex3d(lenswidth, lensheight, bodylength);
		glVertex3d(lenswidth, -lensheight, bodylength);
		glVertex3d(-lenswidth, -lensheight, bodylength);
		glEnd();

		glBegin(GL_LINE_LOOP);
		glVertex3d(-lenswidth - lensoffset, lensheight + lensoffset, bodylength + lenslength);
		glVertex3d(lenswidth + lensoffset, lensheight + lensoffset, bodylength + lenslength);
		glVertex3d(lenswidth + lensoffset, -lensheight - lensoffset, bodylength + lenslength);
		glVertex3d(-lenswidth - lensoffset, -lensheight - lensoffset, bodylength + lenslength);
		glEnd();

		glBegin(GL_LINES);
		glVertex3d(-lenswidth, lensheight, bodylength);
		glVertex3d(-lenswidth - lensoffset, lensheight + lensoffset, bodylength + lenslength);
		glVertex3d(lenswidth, lensheight, bodylength);
		glVertex3d(lenswidth + lensoffset, lensheight + lensoffset, bodylength + lenslength);
		glVertex3d(lenswidth, -lensheight, bodylength);
		glVertex3d(lenswidth + lensoffset, -lensheight - lensoffset, bodylength + lenslength);
		glVertex3d(-lenswidth, -lensheight, bodylength);
		glVertex3d(-lenswidth - lensoffset, -lensheight - lensoffset, bodylength + lenslength);
		glEnd();

		// Draw the film can ...
		glBegin(GL_LINE_LOOP);
		glVertex3d(-filmwidth, bodyheight, -bodylength);
		glVertex3d(-filmwidth, bodyheight, -bodylength + filmradius);
		glVertex3d(-filmwidth, bodyheight + 0.8 * filmradius, -bodylength + 0.8 * filmradius);
		glVertex3d(-filmwidth, bodyheight + filmradius, -bodylength);
		glVertex3d(-filmwidth, bodyheight + 0.8 * filmradius, -bodylength - 0.8 * filmradius);
		glVertex3d(-filmwidth, bodyheight, -bodylength - filmradius);
		glVertex3d(-filmwidth, bodyheight - 0.8 * filmradius, -bodylength - 0.8 * filmradius);
		glVertex3d(-filmwidth, bodyheight - filmradius, -bodylength);
		glEnd();

		glBegin(GL_LINE_LOOP);
		glVertex3d(filmwidth, bodyheight, -bodylength);
		glVertex3d(filmwidth, bodyheight, -bodylength + filmradius);
		glVertex3d(filmwidth, bodyheight + 0.8 * filmradius, -bodylength + 0.8 * filmradius);
		glVertex3d(filmwidth, bodyheight + filmradius, -bodylength);
		glVertex3d(filmwidth, bodyheight + 0.8 * filmradius, -bodylength - 0.8 * filmradius);
		glVertex3d(filmwidth, bodyheight, -bodylength - filmradius);
		glVertex3d(filmwidth, bodyheight - 0.8 * filmradius, -bodylength - 0.8 * filmradius);
		glVertex3d(filmwidth, bodyheight - filmradius, -bodylength);
		glEnd();

		glBegin(GL_LINES);
		glVertex3d(-filmwidth, bodyheight, -bodylength + filmradius);
		glVertex3d(filmwidth, bodyheight, -bodylength + filmradius);
		glVertex3d(-filmwidth, bodyheight + 0.8 * filmradius, -bodylength + 0.8 * filmradius);
		glVertex3d(filmwidth, bodyheight + 0.8 * filmradius, -bodylength + 0.8 * filmradius);
		glVertex3d(-filmwidth, bodyheight + filmradius, -bodylength);
		glVertex3d(filmwidth, bodyheight + filmradius, -bodylength);
		glVertex3d(-filmwidth, bodyheight + 0.8 * filmradius, -bodylength - 0.8 * filmradius);
		glVertex3d(filmwidth, bodyheight + 0.8 * filmradius, -bodylength - 0.8 * filmradius);
		glVertex3d(-filmwidth, bodyheight, -bodylength - filmradius);
		glVertex3d(filmwidth, bodyheight, -bodylength - filmradius);
		glVertex3d(-filmwidth, bodyheight - 0.8 * filmradius, -bodylength - 0.8 * filmradius);
		glVertex3d(filmwidth, bodyheight - 0.8 * filmradius, -bodylength - 0.8 * filmradius);
		glVertex3d(-filmwidth, bodyheight - filmradius, -bodylength);
		glVertex3d(filmwidth, bodyheight - filmradius, -bodylength);
		glEnd();
	}

	bool render_preview()
	{
		// Start a new render job ...
		k3d::irender_job& job = k3d::application().render_farm().create_job("k3d-preview");

		// Add a single render frame to the job ...
		k3d::irender_frame& frame = job.create_frame("frame");

		// Create an output image path ...
		const boost::filesystem::path outputimagepath = frame.add_output_file("salida.tga");
		return_val_if_fail(!outputimagepath.empty(), false);

		// View the output image when it's done ...
		frame.add_view_operation(outputimagepath);

		// Render it (visible rendering) ...
		return_val_if_fail(render(frame, outputimagepath, true), false);

		// Start the job running ...
		k3d::application().render_farm().start_job(job);

		return true;
	}

	bool render_frame(const boost::filesystem::path& OutputImage, const bool ViewImage)
	{
		// Sanity checks ...
		return_val_if_fail(!OutputImage.empty(), false);

		// Start a new render job ...
		k3d::irender_job& job = k3d::application().render_farm().create_job("k3d-render-frame");

		// Add a single render frame to the job ...
		k3d::irender_frame& frame = job.create_frame("frame");

		// Create an output image path ...
		const boost::filesystem::path outputimagepath = frame.add_output_file("salida.tga");
		return_val_if_fail(!outputimagepath.empty(), false);

		// Copy the output image to its requested destination ...
		frame.add_copy_operation(outputimagepath, OutputImage);

		// View the output image when it's done ...
		if(ViewImage)
			frame.add_view_operation(OutputImage);

		// Render it (hidden rendering) ...
		return_val_if_fail(render(frame, outputimagepath, false), false);

		// Start the job running ...
		k3d::application().render_farm().start_job(job);

		return true;
	}

	bool render_animation(const boost::filesystem::path& OutputImages, const bool ViewCompletedImages)
	{
		// Sanity checks ...
		return_val_if_fail(!OutputImages.empty(), false);

		// Ensure that the document has animation capabilities, first ...
		k3d::iproperty* const start_time_property = k3d::get_start_time(document());
		k3d::iproperty* const end_time_property = k3d::get_end_time(document());
		k3d::iproperty* const frame_rate_property = k3d::get_frame_rate(document());
		k3d::iwritable_property* const time_property = dynamic_cast<k3d::iwritable_property*>(k3d::get_time(document()));
		return_val_if_fail(start_time_property && end_time_property && frame_rate_property && time_property, false);

		// Test the output images filepath to make sure it can hold all the frames we're going to generate ...
		const double start_time = boost::any_cast<double>(k3d::get_property_value(document().dag(), *start_time_property));
		const double end_time = boost::any_cast<double>(k3d::get_property_value(document().dag(), *end_time_property));
		const double frame_rate = boost::any_cast<double>(k3d::get_property_value(document().dag(), *frame_rate_property));
	
		const long start_frame = static_cast<long>(k3d::round(frame_rate * start_time));
		const long end_frame = static_cast<long>(k3d::round(frame_rate * end_time));
	
		k3d::frames frames(OutputImages, start_frame, end_frame);
		return_val_if_fail(frames.max_frame() >= end_frame, false);

		// Start a new render job ...
		k3d::irender_job& job = k3d::application().render_farm().create_job("k3d-render-animation");

		// For each frame to be rendered ...
		for(long view_frame = start_frame; view_frame < end_frame; ++view_frame)
			{
				// Set the frame time ...
				time_property->set_value(view_frame / frame_rate);

				// Redraw everything ...
				k3d::viewport::redraw_all(document(), k3d::iviewport::SYNCHRONOUS);

				// Add a render frame to the job ...
				std::stringstream buffer;
				buffer << "frame-" << std::setw(frames.frame_digits()) << std::setfill('0') << view_frame;
				k3d::irender_frame& frame = job.create_frame(buffer.str());

				// Create an output image path ...
				const boost::filesystem::path outputimagepath = frame.add_output_file("salida.tga");
				return_val_if_fail(!outputimagepath.empty(), false);

				// Copy the output image to its requested destination ...
				boost::filesystem::path destination;
				frames.frame(view_frame, destination);
				frame.add_copy_operation(outputimagepath, destination);

				// View the output image when it's done ...
				if(ViewCompletedImages)
					frame.add_view_operation(destination);

				// Render it (hidden rendering) ...
				return_val_if_fail(render(frame, outputimagepath, false), false);
			}

		// Start the job running ...
		k3d::application().render_farm().start_job(job);

		return true;
	}

	k3d::iplugin_factory& factory()
	{
		return get_factory();
	}

	static k3d::iplugin_factory& get_factory()
	{
		static k3d::plugin_factory<
			k3d::document_plugin<render_engine>,
			k3d::interface_list<k3d::iviewport_host,
			k3d::interface_list<k3d::itransform_source,
			k3d::interface_list<k3d::itransform_sink,
			k3d::interface_list<k3d::ianimation_render_engine,
			k3d::interface_list<k3d::istill_render_engine > > > > > > factory(
			k3d::uuid(0xef38bf93, 0x66654f9f, 0x992ca91b, 0x62bae139),
			"YafRayEngine",
			"YafRay Render Engine",
			"Objects",
			k3d::iplugin_factory::STABLE);

		return factory;
	}

private:
	const std::string shader_name(k3d::iunknown& Object)
	{
		k3d::imaterial_collection* const material_collection = dynamic_cast<k3d::imaterial_collection*>(&Object);
		if(material_collection)
			{
				libk3dyafray::imaterial* const material = dynamic_cast<libk3dyafray::imaterial*>(material_collection->material());
				if(material)
					{
						return dynamic_cast<k3d::iobject*>(material)->name();
					}
			}

		return "k3d_default_shader";
	}

	bool render(k3d::irender_frame& Frame, const boost::filesystem::path& OutputImagePath, const bool VisibleRender)
	{
		// Sanity checks ...
		return_val_if_fail(!OutputImagePath.empty(), false);

		// Start our YafRay XML file ...
		const boost::filesystem::path filepath = Frame.add_input_file("world.xml");
		return_val_if_fail(!filepath.empty(), false);

		// Open the RIB file stream ...
		boost::filesystem::ofstream stream(filepath);
		return_val_if_fail(stream.good(), false);

		// Setup the frame for YafRay rendering ...
		Frame.add_render_operation("yafray", "yafray", filepath, VisibleRender);

		// Setup a YafRay scene description ...
		stream << "<scene>" << std::endl;

		// YafRay segfaults when a scene doesn't contain at least one shader, one object and one light, so keep track of what we create as we go ...
		bool found_object = false;
		bool found_light = false;

		// Get the document contents ...
		const k3d::objects_t objects(document().objects().collection());

		// Setup shaders  ...
		for(k3d::objects_t::const_iterator object = objects.begin(); object != objects.end(); ++object)
			{
				libk3dyafray::imaterial* const material = dynamic_cast<libk3dyafray::imaterial*>(*object);
				if(material)
					material->setup_material(stream);
			}

		// Add a default shader ...
		stream << "<shader type=\"generic\" name=\"k3d_default_shader\">" << std::endl;
		stream << "	<attributes>" << std::endl;
		stream << "		<color r=\"1\" g=\"1\" b=\"1\"/>" << std::endl;
		stream << "	</attributes>" << std::endl;
		stream << "</shader>" << std::endl;

		// Setup lights ...
		for(k3d::objects_t::const_iterator object = objects.begin(); object != objects.end(); ++object)
			{
				libk3dyafray::ilight* const light = dynamic_cast<libk3dyafray::ilight*>(*object);
				if(light)
					{
						found_light = true;
						light->setup_light(stream);
					}
			}

		// YafRay segfaults if there isn't at least one light, so create something to keep it happy ...
		if(!found_light)
			{
				stream << "<light type=\"pointlight\" name=\"k3d_null_light\" power=\"0\">" << std::endl;
				stream << "	<from x=\"0\" y=\"0\" z=\"0\"/>" << std::endl;
				stream << "	<color r=\"1\" g=\"1\" b=\"1\"/>" << std::endl;
				stream << "</light>" << std::endl;
			}

		// Setup geometry
		for(k3d::objects_t::const_iterator object = objects.begin(); object != objects.end(); ++object)
			{
				// Render sphere objects ...
				if((*object)->factory().class_id() == k3d::classes::Sphere())
					{
						const k3d::vector3 sphere_center = k3d::object_to_world_matrix(**object) * k3d::vector3(0, 0, 0);
						const boost::any sphere_radius(k3d::get_property_value(**object, "radius"));
						if(typeid(double) == sphere_radius.type())
							{
								stream << "<object name=\"" << (*object)->name() << "\" shader_name=\"" << shader_name(**object) << "\">" << std::endl;
								stream << "	<attributes>" << std::endl;
								stream << "	</attributes>" << std::endl;
								stream << "	<sphere radius=\"" << boost::any_cast<double>(sphere_radius) << "\">" << std::endl;
								stream << "		<center x=\"" << std::fixed << -sphere_center[0] << "\" y=\"" << std::fixed << sphere_center[1] << "\" z=\"" << std::fixed << sphere_center[2] << "\"/>" << std::endl;
								stream << "	</sphere>" << std::endl;
								stream << "</object>" << std::endl;
							
								found_object = true;
							}

						continue;
					}

				// Render mesh objects ...
				if((*object)->factory().class_id() == k3d::classes::MeshInstance())
					{
						k3d::imesh_source* const mesh_source = dynamic_cast<k3d::imesh_source*>(*object);
						if(!mesh_source)
							continue;

						k3d::mesh* const mesh = boost::any_cast<k3d::mesh*>(k3d::get_property_value(document().dag(), mesh_source->mesh_source_output()));
						if(!mesh)
							continue;

						k3d::polyhedron::faces_t new_faces;
						k3d::polyhedron::edges_t new_edges;
						k3d::mesh::points_t new_points;
						
						for(k3d::mesh::polyhedra_t::const_iterator polyhedron = mesh->polyhedra.begin(); polyhedron != mesh->polyhedra.end(); ++polyhedron)
							k3d::triangulate((*polyhedron)->faces.begin(), (*polyhedron)->faces.end(), std::back_inserter(new_faces), std::back_inserter(new_edges), std::back_inserter(new_points));

						k3d::mesh::points_t all_points;
						all_points.insert(all_points.end(), mesh->points.begin(), mesh->points.end());
						all_points.insert(all_points.end(), new_points.begin(), new_points.end());
																																																						
						stream << "<object name=\"" << (*object)->name() << "\" shader_name=\"" << shader_name(**object) << "\">" << std::endl;
						stream << "	<attributes>" << std::endl;
						stream << "	</attributes>" << std::endl;
						stream << "	<mesh>" << std::endl;
						
						stream << "		<points>" << std::endl;
						std::map<k3d::point*, unsigned long> point_map;
						const k3d::matrix4 mesh_matrix = k3d::object_to_world_matrix(**object);
						for(k3d::mesh::points_t::const_iterator point = all_points.begin(); point != all_points.end(); ++point)
							{
								point_map.insert(std::make_pair(*point, point_map.size()));
								const k3d::vector3 position = mesh_matrix * (*point)->position;
								stream << "			<p x=\"" << std::fixed << -position[0] << "\" y=\"" << std::fixed << position[1] << "\" z=\"" << std::fixed << position[2] << "\"/>" << std::endl;
							}
						stream << "		</points>" << std::endl;

						stream << "		<faces>" << std::endl;
						for(k3d::polyhedron::faces_t::const_iterator face = new_faces.begin(); face != new_faces.end(); ++face)
							{
								k3d::split_edge* const e0 = (*face)->first_edge;
								k3d::split_edge* const e1 = e0 ? e0->face_clockwise : 0;
								k3d::split_edge* const e2 = e1 ? e1->face_clockwise : 0;
									
								stream << "			<f a=\""<< point_map[e0->vertex] << "\" b=\"" << point_map[e1->vertex] << "\" c=\""<< point_map[e2->vertex] << "\"/>" << std::endl;
							}
						stream << "		</faces>" << std::endl;
						
						stream << "	</mesh>" << std::endl;
						stream << "</object>" << std::endl;

						std::for_each(new_faces.begin(), new_faces.end(), k3d::delete_object());
						std::for_each(new_edges.begin(), new_edges.end(), k3d::delete_object());
						std::for_each(new_points.begin(), new_points.end(), k3d::delete_object());

						found_object = true;
						continue;
					}
			}

		// YafRay segfaults if there isn't at least one object, so create something invisible to keep it happy ...
		if(!found_object)
			{
				stream << "<object name=\"default_object\" shader_name=\"k3d_default_shader\">" << std::endl;
				stream << "	<attributes>" << std::endl;
				stream << "	</attributes>" << std::endl;
				stream << "	<sphere radius=\"0.0\">" << std::endl;
				stream << "		<center x=\"0\" y=\"0\" z=\"0\"/>" << std::endl;
				stream << "	</sphere>" << std::endl;
				stream << "</object>" << std::endl;
			}

		// Setup the camera ...
		const k3d::matrix4 camera_matrix(matrix());
		const k3d::vector3 camera_position = k3d::extractTranslation(camera_matrix);
		const k3d::vector3 camera_to_vector = camera_matrix * k3d::vector3(0, 0, 1);
		const k3d::vector3 camera_up_vector = camera_matrix * k3d::vector3(0, 1, 0);

		stream << "<camera name=\"camera\" resx=\"" << m_pixel_width.property_value() << "\" resy=\"" << m_pixel_height.property_value() << "\" focal=\"0.7\">" << std::endl;
		stream << "	<from x=\"" << -camera_position[0] << "\" y=\"" << camera_position[1] << "\" z=\"" << camera_position[2] << "\"/>" << std::endl;
		stream << "	<to x=\"" << -camera_to_vector[0] << "\" y=\"" << camera_to_vector[1] << "\" z=\"" << camera_to_vector[2] << "\"/>" << std::endl;
		stream << "	<up x=\"" << -camera_up_vector[0] << "\" y=\"" << camera_up_vector[1] << "\" z=\"" << camera_up_vector[2] << "\"/>" << std::endl;
		stream << "</camera>" << std::endl;

		// Generate the output file ...
		stream << "<render camera_name=\"camera\" output=\"" << OutputImagePath.native_file_string() << "\">" << std::endl;
		stream << "</render>" << std::endl;

		// Finish the scene ...
		stream << "</scene>" << std::endl;

		return true;
	}

	k3d_measurement_property(unsigned long, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::with_constraint) m_pixel_width;
	k3d_measurement_property(unsigned long, k3d::immutable_name, k3d::change_signal, k3d::with_undo, k3d::local_storage, k3d::with_constraint) m_pixel_height;
	
	aspect_ratio_changed_signal_t m_aspect_ratio_changed_signal;
};

k3d::iplugin_factory& render_engine_factory()
{
	return render_engine::get_factory();
}

} // namespace libk3dyafray


