// 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
		\brief Implements the k3d::ri::render class, which provides a default implementation of k3d::ri::irender
		\author Tim Shead (tshead@k-3d.com)
		\author Romain Behar (romainbehar@yahoo.com)
*/

#include "algebra.h"
#include "imaterial.h"
#include "mesh.h"
#include "renderman.h"
#include "vectors.h"

#include <boost/array.hpp>

#include <iostream>
#include <numeric>
#include <set>

namespace boost
{

std::ostream& operator<<(std::ostream& Stream, const array<double, 16>& RHS)
{
	std::copy(RHS.begin(), RHS.end(), std::ostream_iterator<double>(Stream, " "));
	return Stream;
}

} // namespace boost

namespace
{

long& indentation_storage(std::ios& Stream)
{
	static const int index = std::ios::xalloc();
	return Stream.iword(index);
}

std::ostream& reset_indentation(std::ostream& Stream)
{
	indentation_storage(Stream) = 0;
	return Stream;
}

std::ostream& push_indent(std::ostream& Stream)
{
	indentation_storage(Stream)++;
	return Stream;
}

std::ostream& pop_indent(std::ostream& Stream)
{
	long& indent = indentation_storage(Stream);
	indent -= (indent > 0);
	return Stream;
}

std::ostream& indentation(std::ostream& Stream)
{
	const long& indent = indentation_storage(Stream);
	for(long i = 0; i < indent; i++)
		Stream << "   ";

	return Stream;
}

long& inline_types_storage(std::ios& Stream)
{
	static const int index = std::ios_base::xalloc();
	return Stream.iword(index);
}

bool inline_types(std::ostream& Stream)
{
	return inline_types_storage(Stream);
}

bool set_inline_types(std::ostream& Stream, const bool Enabled)
{
	bool old_state = inline_types_storage(Stream);

	inline_types_storage(Stream) = Enabled;

	return old_state;
}

/// Formats a string with real-quotes for inclusion in a RIB file; designed to be used as an inline formatting object
class format_string
{
public:
	explicit format_string(const k3d::ri::string& Token) :
		token(Token)
	{
	}

	friend std::ostream& operator<<(std::ostream& Stream, const format_string& RHS)
	{
		Stream << "\"" << RHS.token << "\"";
		return Stream;
	}

private:
	const k3d::ri::string& token;
};

/// Encapsulates a RenderMan RIB parameter type
struct predefined_type
{
	explicit predefined_type(const k3d::ri::parameter& Parameter) :
		storage_class(Parameter.storage_class),
		name(Parameter.name),
		type(Parameter.value.type()),
		array_dimension(Parameter.array_dimension)
	{
	}

	explicit predefined_type(const k3d::ri::storage_class_t StorageClass, const k3d::ri::string& Name, const std::type_info& Type, const k3d::ri::unsigned_integer ArrayDimension) :
		storage_class(StorageClass),
		name(Name),
		type(Type),
		array_dimension(ArrayDimension)
	{
	}

	friend bool operator<(const predefined_type& LHS, const predefined_type& RHS)
	{
		if(LHS.storage_class != RHS.storage_class)
			return LHS.storage_class < RHS.storage_class;

		if(LHS.array_dimension != RHS.array_dimension)
			return LHS.array_dimension < RHS.array_dimension;

		if(LHS.type != RHS.type)
			return LHS.type.before(RHS.type);

		return LHS.name < RHS.name;
	}

	k3d::ri::storage_class_t storage_class;
	const k3d::ri::string name;
	const std::type_info& type;
	const k3d::ri::unsigned_integer array_dimension;
};

/// A collection of RenderMan RIB parameter types
typedef std::set<predefined_type> predefined_types_t;

/// Returns the set of standard predefined RIB parameter types
predefined_types_t& predefined_types()
{
	static predefined_types_t types;
	if(types.empty())
		{
			types.insert(predefined_type(k3d::ri::VERTEX, "P", typeid(k3d::ri::point), 1));
			types.insert(predefined_type(k3d::ri::VERTEX, "P", typeid(k3d::ri::points), 1));
			types.insert(predefined_type(k3d::ri::VERTEX, "Pz", typeid(k3d::ri::real), 1));
			types.insert(predefined_type(k3d::ri::VERTEX, "Pz", typeid(k3d::ri::reals), 1));
			types.insert(predefined_type(k3d::ri::VERTEX, "Pw", typeid(k3d::ri::hpoint), 1));
			types.insert(predefined_type(k3d::ri::VERTEX, "Pw", typeid(k3d::ri::hpoints), 1));

			types.insert(predefined_type(k3d::ri::VARYING, "N", typeid(k3d::ri::normal), 1));
			types.insert(predefined_type(k3d::ri::VARYING, "N", typeid(k3d::ri::normals), 1));

			types.insert(predefined_type(k3d::ri::VARYING, "Cs", typeid(k3d::ri::color), 1));
			types.insert(predefined_type(k3d::ri::VARYING, "Cs", typeid(k3d::ri::colors), 1));
			types.insert(predefined_type(k3d::ri::VARYING, "Os", typeid(k3d::ri::color), 1));
			types.insert(predefined_type(k3d::ri::VARYING, "Os", typeid(k3d::ri::colors), 1));

			types.insert(predefined_type(k3d::ri::VARYING, "s", typeid(k3d::ri::real), 1));
			types.insert(predefined_type(k3d::ri::VARYING, "s", typeid(k3d::ri::reals), 1));
			types.insert(predefined_type(k3d::ri::VARYING, "t", typeid(k3d::ri::real), 1));
			types.insert(predefined_type(k3d::ri::VARYING, "t", typeid(k3d::ri::reals), 1));

			types.insert(predefined_type(k3d::ri::VARYING, "blur", typeid(k3d::ri::real), 1));
			types.insert(predefined_type(k3d::ri::VARYING, "blur", typeid(k3d::ri::reals), 1));
			types.insert(predefined_type(k3d::ri::VARYING, "sblur", typeid(k3d::ri::real), 1));
			types.insert(predefined_type(k3d::ri::VARYING, "sblur", typeid(k3d::ri::reals), 1));
			types.insert(predefined_type(k3d::ri::VARYING, "tblur", typeid(k3d::ri::real), 1));
			types.insert(predefined_type(k3d::ri::VARYING, "tblur", typeid(k3d::ri::reals), 1));

			types.insert(predefined_type(k3d::ri::UNIFORM, "width", typeid(k3d::ri::real), 1));
			types.insert(predefined_type(k3d::ri::UNIFORM, "width", typeid(k3d::ri::reals), 1));
			types.insert(predefined_type(k3d::ri::UNIFORM, "swidth", typeid(k3d::ri::real), 1));
			types.insert(predefined_type(k3d::ri::UNIFORM, "swidth", typeid(k3d::ri::reals), 1));
			types.insert(predefined_type(k3d::ri::UNIFORM, "twidth", typeid(k3d::ri::real), 1));
			types.insert(predefined_type(k3d::ri::UNIFORM, "twidth", typeid(k3d::ri::reals), 1));

			types.insert(predefined_type(k3d::ri::UNIFORM, "filter", typeid(k3d::ri::string), 1));
			types.insert(predefined_type(k3d::ri::UNIFORM, "filter", typeid(k3d::ri::strings), 1));

			types.insert(predefined_type(k3d::ri::UNIFORM, "fill", typeid(k3d::ri::real), 1));
			types.insert(predefined_type(k3d::ri::UNIFORM, "fill", typeid(k3d::ri::reals), 1));

			types.insert(predefined_type(k3d::ri::UNIFORM, "fov", typeid(k3d::ri::real), 1));
			types.insert(predefined_type(k3d::ri::UNIFORM, "fov", typeid(k3d::ri::reals), 1));

			types.insert(predefined_type(k3d::ri::UNIFORM, "shader", typeid(k3d::ri::string), 1));
		}

	return types;
}

/// Formats a parameter name for inclusion in a RIB file
class format_parameter_name
{
public:
	explicit format_parameter_name(const k3d::ri::parameter& Parameter) :
		parameter(Parameter)
	{
	}

	friend std::ostream& operator<<(std::ostream& Stream, const format_parameter_name& RHS)
	{
		Stream << "\"";

		// Only generate type information if inlining is enabled ...
		if(inline_types(Stream))
			{
				// Only generate type information if this type isn't already predefined ...
				if(!predefined_types().count(predefined_type(RHS.parameter)))
					{
						Stream << RHS.parameter.storage_class << " ";

						const std::type_info& type = RHS.parameter.value.type();

						if(RHS.parameter.value.empty())
							{
								Stream << "unknown ";
								std::cerr << __PRETTY_FUNCTION__ << ": cannot deduce type for empty parameter" << std::endl;
							}
						else if(typeid(k3d::ri::integer) == type || typeid(k3d::ri::integers) == type)
							{
								Stream << "integer";
							}
						else if(typeid(k3d::ri::real) == type || typeid(k3d::ri::reals) == type)
							{
								Stream << "float";
							}
						else if(typeid(k3d::ri::string) == type || typeid(k3d::ri::strings) == type)
							{
								Stream << "string";
							}
						else if(typeid(k3d::ri::point) == type || typeid(k3d::ri::points) == type)
							{
								Stream << "point";
							}
						else if(typeid(k3d::ri::vector) == type || typeid(k3d::ri::vectors) == type)
							{
								Stream << "vector";
							}
						else if(typeid(k3d::ri::normal) == type || typeid(k3d::ri::normals) == type)
							{
								Stream << "normal";
							}
						else if(typeid(k3d::ri::color) == type || typeid(k3d::ri::colors) == type)
							{
								Stream << "color";
							}
						else if(typeid(k3d::ri::hpoint) == type || typeid(k3d::ri::hpoints) == type)
							{
								Stream << "hpoint";
							}
						else if(typeid(k3d::ri::matrix) == type || typeid(k3d::ri::matrices) == type)
							{
								Stream << "matrix";
							}
						else
							{
								Stream << "unknown";
								std::cerr << __PRETTY_FUNCTION__ << ": cannot deduce parameter type for [" << RHS.parameter.name << "]" << std::endl;
							}

						if(RHS.parameter.array_dimension > 1)
							Stream << "[" << RHS.parameter.array_dimension << "]";

						Stream << " ";
					}
			}

		Stream << RHS.parameter.name;

		Stream << "\"";

		return Stream;
	}

private:
	const k3d::ri::parameter& parameter;
};

/// Formats an array of values within square brackets for inclusion in a RIB file; designed to be used as an inline formatting object
template<typename iterator_t, typename value_t>
class format_array_t
{
public:
	format_array_t(const iterator_t Begin, const iterator_t End) :
		begin(Begin),
		end(End)
	{
	}

	friend std::ostream& operator << (std::ostream& Stream, const format_array_t& RHS)
	{
		Stream << "[ ";
		std::copy(RHS.begin, RHS.end, std::ostream_iterator<value_t>(Stream, " "));
		Stream << "]";

		return Stream;
	}

private:
	const iterator_t begin;
	const iterator_t end;
};

/// Partial specialization of format_array_t for use with string values
template<typename iterator_t>
class format_array_t<iterator_t, k3d::ri::string>
{
public:
	format_array_t(const iterator_t Begin, const iterator_t End) :
		begin(Begin),
		end(End)
	{
	}

	friend std::ostream& operator << (std::ostream& Stream, const format_array_t& RHS)
	{
		Stream << "[ ";
		for(iterator_t element = RHS.begin; element != RHS.end; ++element)
			Stream << format_string(*element) << " ";
		Stream << "]";

		return Stream;
	}

private:
	const iterator_t begin;
	const iterator_t end;
};

/// Convenience factory function for creating format_array_t objects
template<typename iterator_t>
format_array_t<iterator_t, typename std::iterator_traits<iterator_t>::value_type> format_array(const iterator_t Begin, const iterator_t End)
{
	return format_array_t<iterator_t, typename std::iterator_traits<iterator_t>::value_type>(Begin, End);
}

} // namespace

namespace k3d
{

namespace ri
{

std::ostream& operator<<(std::ostream& Stream, const storage_class_t RHS)
{
	switch(RHS)
		{
			case k3d::ri::CONSTANT:
				Stream << "constant";
				break;
			case k3d::ri::UNIFORM:
				Stream << "uniform";
				break;
			case k3d::ri::VARYING:
				Stream << "varying";
				break;
			case k3d::ri::VERTEX:
				Stream << "vertex";
				break;
			case k3d::ri::FACEVARYING:
				Stream << "facevarying";
				break;
			default:
				assert_not_reached();
		}

	return Stream;
}

std::ostream& operator<<(std::ostream& Stream, const parameter& RHS)
{
	Stream << format_parameter_name(RHS) << " ";

	if(RHS.value.empty())
		{
			Stream << "[ ]";
		}
	else if(typeid(integer) == RHS.value.type())
		{
			Stream << "[ " << boost::any_cast<integer>(RHS.value) << " ]";
		}
	else if(typeid(real) == RHS.value.type())
		{
			Stream << "[ " << boost::any_cast<real>(RHS.value) << " ]";
		}
	else if(typeid(string) == RHS.value.type())
		{
			Stream << "[ " << format_string(boost::any_cast<string>(RHS.value)) << " ]";
		}
	else if(typeid(point) == RHS.value.type())
		{
			Stream << "[ " << boost::any_cast<point>(RHS.value) << " ]";
		}
	else if(typeid(vector) == RHS.value.type())
		{
			Stream << "[ " << boost::any_cast<vector>(RHS.value) << " ]";
		}
	else if(typeid(normal) == RHS.value.type())
		{
			Stream << "[ " << boost::any_cast<normal>(RHS.value) << " ]";
		}
	else if(typeid(color) == RHS.value.type())
		{
			Stream << "[ " << boost::any_cast<color>(RHS.value) << " ]";
		}
	else if(typeid(hpoint) == RHS.value.type())
		{
			Stream << "[ " << boost::any_cast<hpoint>(RHS.value) << " ]";
		}
	else if(typeid(matrix) == RHS.value.type())
		{
			Stream << "[ " << boost::any_cast<matrix>(RHS.value) << " ]";
		}
	else if(typeid(integers) == RHS.value.type())
		{
			const integers* const p = boost::any_cast<integers>(&RHS.value);
			Stream << format_array(p->begin(), p->end());
		}
	else if(typeid(reals) == RHS.value.type())
		{
			const reals* const p = boost::any_cast<reals>(&RHS.value);
			Stream << format_array(p->begin(), p->end());
		}
	else if(typeid(strings) == RHS.value.type())
		{
			const strings* const p = boost::any_cast<strings>(&RHS.value);
			Stream << format_array(p->begin(), p->end());
		}
	else if(typeid(points) == RHS.value.type())
		{
			const points* const p = boost::any_cast<points>(&RHS.value);
			Stream << format_array(p->begin(), p->end());
		}
	else if(typeid(vectors) == RHS.value.type())
		{
			const vectors* const p = boost::any_cast<vectors>(&RHS.value);
			Stream << format_array(p->begin(), p->end());
		}
	else if(typeid(normals) == RHS.value.type())
		{
			const normals* const p = boost::any_cast<normals>(&RHS.value);
			Stream << format_array(p->begin(), p->end());
		}
	else if(typeid(colors) == RHS.value.type())
		{
			const colors* const p = boost::any_cast<colors>(&RHS.value);
			Stream << format_array(p->begin(), p->end());
		}
	else if(typeid(hpoints) == RHS.value.type())
		{
			const hpoints* const p = boost::any_cast<hpoints>(&RHS.value);
			Stream << format_array(p->begin(), p->end());
		}
	else if(typeid(matrices) == RHS.value.type())
		{
			const matrices* const p = boost::any_cast<matrices>(&RHS.value);
			Stream << format_array(p->begin(), p->end());
		}
	else
		{
			Stream << "[ ]";
			std::cerr << __PRETTY_FUNCTION__ << ": unknown parameter type for [" << RHS.name << "] will not be serialized" << std::endl;
		}

	return Stream;
}

std::ostream& operator<<(std::ostream& Stream, const parameter_list& RHS)
{
	std::copy(RHS.begin(), RHS.end(), std::ostream_iterator<parameter>(Stream, " "));
	return Stream;
}

/////////////////////////////////////////////////////////////////////////////
// motion_begin

void motion_begin(const render_state& State)
{
	if(motion_blur(State))
		State.engine.RiMotionBeginV(State.sample_times);
}

/////////////////////////////////////////////////////////////////////////////
// motion_end

void motion_end(const render_state& State)
{
	if(motion_blur(State))
		State.engine.RiMotionEnd();
}

/////////////////////////////////////////////////////////////////////////////
// motion_blur

bool motion_blur(const render_state& State)
{
	return State.sample_times.size() > 1;
}

/////////////////////////////////////////////////////////////////////////////
// first_sample

bool first_sample(const render_state& State)
{
	return 0 == State.sample_index;
}

/////////////////////////////////////////////////////////////////////////////
// last_sample

bool last_sample(const render_state& State)
{
	return State.sample_index == State.sample_times.size() - 1;
}

/////////////////////////////////////////////////////////////////////////////
// convert

const matrix convert(const k3d::matrix4& Matrix)
{
	matrix result;

	k3d::matrix4 temp(Matrix.Transpose());
	real* const m = temp;
	std::copy(m, m+16, result.begin());

	return result;
}

/////////////////////////////////////////////////////////////////////////////
// setup_material

void setup_material(iunknown* const Material, const render_state& State)
{
	k3d::ri::imaterial* const material = dynamic_cast<k3d::ri::imaterial*>(Material);
	if(material)
		{
			material->setup_renderman_material(State);
		}
	else
		{
			// We only generate RIB on the final sample ...
			if(!last_sample(State))
				return;

			State.engine.RiSurfaceV("null");
		}
}

namespace detail
{

class same_type
{
public:
	same_type(const std::type_info& Type) :
		m_type(Type)
	{
	}

	bool operator()(const boost::any& Value)
	{
		return Value.type() == m_type;
	}

private:
	const std::type_info& m_type;
};

typedef std::vector<boost::any> values_t;

typedef std::map<std::string, values_t> grouped_parameters_t;

template<typename data_t, typename container_t>
const container_t build_array(const values_t& Values)
{
	container_t result;

	for(values_t::const_iterator value = Values.begin(); value != Values.end(); ++value)
		result.push_back(boost::any_cast<data_t>(*value));

	return result;
}

k3d::ri::parameter_list build_parameters(const grouped_parameters_t& Parameters, const k3d::ri::storage_class_t StorageClass)
{
	k3d::ri::parameter_list results;

	// For each group of values ...
	for(grouped_parameters_t::const_iterator group = Parameters.begin(); group != Parameters.end(); ++group)
		{
			// Get some information about the group ...
			const std::string& name = group->first;
			const values_t& values = group->second;
			const std::type_info& type = values.front().type();

			// Check to see that all values have the same type; if not, skip the group ...
			if(values.size() != static_cast<size_t>(std::count_if(values.begin(), values.end(), same_type(type))))
				{
					std::cerr << __PRETTY_FUNCTION__ << ": parameter [" << name << "] contains multiple types and will be ignored" << std::endl;
					continue;
				}

			// OK, everything looks good so let's turn the group into a set of parameters ...
			if(typeid(k3d::ri::integer) == type)
				{
					results.push_back(k3d::ri::parameter(name, StorageClass, build_array<k3d::ri::integer, k3d::ri::integers>(values)));
				}
			else if(typeid(k3d::ri::real) == type)
				{
					results.push_back(k3d::ri::parameter(name, StorageClass, build_array<k3d::ri::real, k3d::ri::reals>(values)));
				}
			else if(typeid(k3d::ri::string) == type)
				{
					results.push_back(k3d::ri::parameter(name, StorageClass, build_array<k3d::ri::string, k3d::ri::strings>(values)));
				}
			else if(typeid(k3d::ri::point) == type)
				{
					results.push_back(k3d::ri::parameter(name, StorageClass, build_array<k3d::ri::point, k3d::ri::points>(values)));
				}
			else if(typeid(k3d::ri::vector) == type)
				{
					results.push_back(k3d::ri::parameter(name, StorageClass, build_array<k3d::ri::vector, k3d::ri::vectors>(values)));
				}
			else if(typeid(k3d::ri::normal) == type)
				{
					results.push_back(k3d::ri::parameter(name, StorageClass, build_array<k3d::ri::normal, k3d::ri::normals>(values)));
				}
			else if(typeid(k3d::ri::color) == type)
				{
					results.push_back(k3d::ri::parameter(name, StorageClass, build_array<k3d::ri::color, k3d::ri::colors>(values)));
				}
			else if(typeid(k3d::ri::hpoint) == type)
				{
					results.push_back(k3d::ri::parameter(name, StorageClass, build_array<k3d::ri::hpoint, k3d::ri::hpoints>(values)));
				}
			else if(typeid(k3d::ri::matrix) == type)
				{
					results.push_back(k3d::ri::parameter(name, StorageClass, build_array<k3d::ri::matrix, k3d::ri::matrices>(values)));
				}
			else
				{
					std::cerr << __PRETTY_FUNCTION__ << ": cannot deduce parameter type for [" << name << "]" << std::endl;
				}
		}

	return results;
}

void build_tags(const k3d::parameters_t::const_iterator Begin, const k3d::parameters_t::const_iterator& End, k3d::ri::strings& Tags, k3d::ri::unsigned_integers& TagCounts, k3d::ri::integers& TagIntegers, k3d::ri::reals& TagReals)
{

	for(k3d::parameters_t::const_iterator tag = Begin; tag != End; ++tag)
		{
			if(tag->first == "interpolateboundary" && (tag->second.type() == typeid(bool)))
				{
					if(boost::any_cast<bool>(tag->second))
						{
							Tags.push_back("interpolateboundary");
							TagCounts.push_back(0);
							TagCounts.push_back(0);
							continue;
						}
				}

			std::cerr << warning << "Unknown or incorrectly typed tag [" << tag->first << "] will be ignored" << std::endl;
		}
}

void build_tags(const k3d::polyhedron::faces_t::const_iterator Begin, const k3d::polyhedron::faces_t::const_iterator& End, std::map<k3d::face*, k3d::ri::unsigned_integer>& FaceMap, k3d::ri::strings& Tags, k3d::ri::unsigned_integers& TagCounts, k3d::ri::integers& TagIntegers, k3d::ri::reals& TagReals)
{
	for(k3d::polyhedron::faces_t::const_iterator face = Begin; face != End; ++face)
		{
			for(k3d::parameters_t::const_iterator tag = (*face)->tags.begin(); tag != (*face)->tags.end(); ++tag)
				{
					if(tag->first == "hole" && (tag->second.type() == typeid(bool)))
						{
							if(boost::any_cast<bool>(tag->second))
								{
									Tags.push_back("hole");
									TagCounts.push_back(1);
									TagCounts.push_back(0);
									TagIntegers.push_back(FaceMap[*face]);
									continue;
								}
						}

					std::cerr << warning << "Unknown or incorrectly typed face tag [" << tag->first << "] will be ignored" << std::endl;
				}
		}
}

void build_tags(const k3d::polyhedron::edges_t::const_iterator Begin, const k3d::polyhedron::edges_t::const_iterator& End, std::map<k3d::point*, k3d::ri::unsigned_integer>& PointMap, k3d::ri::strings& Tags, k3d::ri::unsigned_integers& TagCounts, k3d::ri::integers& TagIntegers, k3d::ri::reals& TagReals)
{
	// First, get the set of all "joined" edges (i.e. eliminate companions)
	typedef std::set<k3d::split_edge*> joined_edges_t;
	joined_edges_t joined_edges;
	for(k3d::polyhedron::edges_t::const_iterator e = Begin; e != End; ++e)
		joined_edges.insert(std::max((*e), (*e)->companion));
	joined_edges.erase(0);

	for(joined_edges_t::const_iterator e = joined_edges.begin(); e != joined_edges.end(); ++e)
		{
			k3d::split_edge& edge = **e;

			for(k3d::parameters_t::const_iterator tag = edge.tags.begin(); tag != edge.tags.end(); ++tag)
				{
					if(tag->first == "crease" && (tag->second.type() == typeid(k3d::ri::real)) && edge.vertex && edge.face_clockwise && edge.face_clockwise->vertex)
						{
							Tags.push_back("crease");
							TagCounts.push_back(2);
							TagCounts.push_back(1);
							TagIntegers.push_back(PointMap[edge.vertex]);
							TagIntegers.push_back(PointMap[edge.face_clockwise->vertex]);
							TagReals.push_back(boost::any_cast<k3d::ri::real>(tag->second));
							continue;
						}

					std::cerr << warning << "Unknown or incorrectly typed edge tag [" << tag->first << "] will be ignored" << std::endl;
				}
		}
}

void build_tags(const std::vector<k3d::point*>::const_iterator Begin, const std::vector<k3d::point*>::const_iterator End, std::map<k3d::point*, k3d::ri::unsigned_integer>& PointMap, k3d::ri::strings& Tags, k3d::ri::unsigned_integers& TagCounts, k3d::ri::integers& TagIntegers, k3d::ri::reals& TagReals)
{
	for(std::vector<k3d::point*>::const_iterator point = Begin; point != End; ++point)
		{
			for(k3d::parameters_t::const_iterator tag = (*point)->tags.begin(); tag != (*point)->tags.end(); ++tag)
				{
					if(tag->first == "corner" && (tag->second.type() == typeid(k3d::ri::real)))
						{
							Tags.push_back("corner");
							TagCounts.push_back(1);
							TagCounts.push_back(1);
							TagIntegers.push_back(PointMap[*point]);
							TagReals.push_back(boost::any_cast<k3d::ri::real>(tag->second));
							continue;
						}

					std::cerr << warning << "Unknown or incorrectly-typed point tag [" << tag->first << "] will be ignored" << std::endl;
				}
		}
}

k3d::ri::parameter_list build_parameters(const k3d::parameters_t::const_iterator& Begin, const k3d::parameters_t::const_iterator& End, const k3d::ri::storage_class_t StorageClass)
{
	grouped_parameters_t grouped_parameters;

	for(k3d::parameters_t::const_iterator parameter = Begin; parameter != End; ++parameter)
		grouped_parameters[parameter->first].push_back(parameter->second);

	return build_parameters(grouped_parameters, StorageClass);
}

k3d::ri::parameter_list build_parameters(const boost::array<k3d::parameters_t, 4>::const_iterator& Begin, const boost::array<k3d::parameters_t, 4>::const_iterator& End, const k3d::ri::storage_class_t StorageClass)
{
	grouped_parameters_t grouped_parameters;

	for(boost::array<k3d::parameters_t, 4>::const_iterator parameters = Begin; parameters != End; ++parameters)
		{
			for(k3d::parameters_t::const_iterator parameter = parameters->begin(); parameter != parameters->end(); ++parameter)
				grouped_parameters[parameter->first].push_back(parameter->second);
		}

	return build_parameters(grouped_parameters, StorageClass);
}

k3d::ri::parameter_list build_parameters(k3d::point** Begin, k3d::point** End, const k3d::ri::storage_class_t StorageClass)
{
	// Sanity check ...
	assert(k3d::ri::VERTEX == StorageClass);

	grouped_parameters_t grouped_parameters;

	for(k3d::point** point = Begin; point != End; ++point)
		{
			for(k3d::parameters_t::const_iterator parameter = (*point)->vertex_data.begin(); parameter != (*point)->vertex_data.end(); ++parameter)
				grouped_parameters[parameter->first].push_back(parameter->second);
		}

	return build_parameters(grouped_parameters, StorageClass);
}

k3d::ri::parameter_list build_parameters(const std::vector<k3d::point*>::const_iterator Begin, const std::vector<k3d::point*>::const_iterator End, const k3d::ri::storage_class_t StorageClass)
{
	// Sanity check ...
	assert(k3d::ri::VERTEX == StorageClass);

	grouped_parameters_t grouped_parameters;

	for(std::vector<k3d::point*>::const_iterator point = Begin; point != End; ++point)
		{
			for(k3d::parameters_t::const_iterator parameter = (*point)->vertex_data.begin(); parameter != (*point)->vertex_data.end(); ++parameter)
				grouped_parameters[parameter->first].push_back(parameter->second);
		}

	return build_parameters(grouped_parameters, StorageClass);
}

k3d::ri::parameter_list build_parameters(const std::vector<k3d::split_edge*>::const_iterator Begin, const std::vector<k3d::split_edge*>::const_iterator End, const k3d::ri::storage_class_t StorageClass)
{
	// Sanity check ...
	assert(k3d::ri::FACEVARYING == StorageClass);

	grouped_parameters_t grouped_parameters;

	for(std::vector<k3d::split_edge*>::const_iterator edge = Begin; edge != End; ++edge)
		{
			for(k3d::parameters_t::const_iterator parameter = (*edge)->facevarying_data.begin(); parameter != (*edge)->facevarying_data.end(); ++parameter)
				grouped_parameters[parameter->first].push_back(parameter->second);
		}

	return build_parameters(grouped_parameters, StorageClass);
}

k3d::ri::parameter_list build_parameters(const k3d::polyhedron::faces_t::const_iterator Begin, const k3d::polyhedron::faces_t::const_iterator End, const k3d::ri::storage_class_t StorageClass)
{
	// Sanity check ...
	assert(k3d::ri::UNIFORM == StorageClass);

	grouped_parameters_t grouped_parameters;

	for(k3d::polyhedron::faces_t::const_iterator face = Begin; face != End; ++face)
		{
			for(k3d::parameters_t::const_iterator parameter = (*face)->uniform_data.begin(); parameter != (*face)->uniform_data.end(); ++parameter)
				grouped_parameters[parameter->first].push_back(parameter->second);
		}

	return build_parameters(grouped_parameters, StorageClass);
}

k3d::ri::parameter_list build_parameters(const k3d::linear_curve_group::curves_t::const_iterator Begin, const k3d::linear_curve_group::curves_t::const_iterator End, const k3d::ri::storage_class_t StorageClass)
{
	grouped_parameters_t grouped_parameters;

	if(k3d::ri::UNIFORM == StorageClass)
		{
			for(k3d::linear_curve_group::curves_t::const_iterator curve = Begin; curve != End; ++curve)
				{
					for(k3d::parameters_t::const_iterator parameter = (*curve)->uniform_data.begin(); parameter != (*curve)->uniform_data.end(); ++parameter)
						grouped_parameters[parameter->first].push_back(parameter->second);
				}
		}
	else if(k3d::ri::VARYING == StorageClass)
		{
			for(k3d::linear_curve_group::curves_t::const_iterator curve = Begin; curve != End; ++curve)
				{
					for(k3d::linear_curve::varying_t::const_iterator varying_data = (*curve)->varying_data.begin(); varying_data != (*curve)->varying_data.end(); ++varying_data)
						{
							for(k3d::parameters_t::const_iterator parameter = varying_data->begin(); parameter != varying_data->end(); ++parameter)
								grouped_parameters[parameter->first].push_back(parameter->second);
						}
				}
		}

	return build_parameters(grouped_parameters, StorageClass);
}

k3d::ri::parameter_list build_parameters(const k3d::cubic_curve_group::curves_t::const_iterator Begin, const k3d::cubic_curve_group::curves_t::const_iterator End, const k3d::ri::storage_class_t StorageClass)
{
	grouped_parameters_t grouped_parameters;

	if(k3d::ri::UNIFORM == StorageClass)
		{
			for(k3d::cubic_curve_group::curves_t::const_iterator curve = Begin; curve != End; ++curve)
				{
					for(k3d::parameters_t::const_iterator parameter = (*curve)->uniform_data.begin(); parameter != (*curve)->uniform_data.end(); ++parameter)
						grouped_parameters[parameter->first].push_back(parameter->second);
				}
		}
	else if(k3d::ri::VARYING == StorageClass)
		{
			for(k3d::cubic_curve_group::curves_t::const_iterator curve = Begin; curve != End; ++curve)
				{
					for(k3d::cubic_curve::varying_t::const_iterator varying_data = (*curve)->varying_data.begin(); varying_data != (*curve)->varying_data.end(); ++varying_data)
						{
							for(k3d::parameters_t::const_iterator parameter = varying_data->begin(); parameter != varying_data->end(); ++parameter)
								grouped_parameters[parameter->first].push_back(parameter->second);
						}
				}
		}

	return build_parameters(grouped_parameters, StorageClass);
}

// RiBlobby

void push_matrix(const k3d::matrix4& Matrix, reals& Floats)
{
	const matrix m = convert(Matrix);
	for(matrix::const_iterator v = m.begin(); v != m.end(); v++)
		Floats.push_back(*v);
}

void push_vector3(const k3d::vector3& Vector, reals& Floats)
{
	Floats.push_back(Vector[0]);
	Floats.push_back(Vector[1]);
	Floats.push_back(Vector[2]);
}

/// Blobby virtual machine - builds the RiBlobby arrays
class blobby_vm :
	private k3d::blobby::visitor
{
public:
	blobby_vm(k3d::blobby& Blobby, unsigned_integer& NLeaf, unsigned_integers& Codes, reals& Floats, strings& Strings, grouped_parameters_t& Parameters) :
		nleaf(NLeaf),
		codes(Codes),
		floats(Floats),
		names(Strings),
		grouped_parameters(Parameters)
	{
		m_opcode_id = 0;
		Blobby.accept(*this);
	}

	virtual ~blobby_vm() {}

private:
	void visit_constant(k3d::blobby::constant& Constant)
	{
		codes.push_back(1000);
		codes.push_back(floats.size());
		floats.push_back(Constant.value);

		m_opcodes.push(m_opcode_id++);
		nleaf++;
	}

	void visit_ellipsoid(k3d::blobby::ellipsoid& Ellipsoid)
	{
		codes.push_back(1001);
		codes.push_back(floats.size());

		// Output matrix ...
		push_matrix(k3d::translation3D(Ellipsoid.origin->position) * Ellipsoid.transformation, floats);

		// Save parameters ...
		for(parameters_t::const_iterator parameter = Ellipsoid.vertex_data.begin(); parameter != Ellipsoid.vertex_data.end(); parameter++)
			grouped_parameters[parameter->first].push_back(parameter->second);

		m_opcodes.push(m_opcode_id++);
		nleaf++;
	}

	void visit_segment(k3d::blobby::segment& Segment)
	{
		codes.push_back(1002);
		codes.push_back(floats.size());

		// Output parameters ...
		push_vector3(Segment.start->position, floats);
		push_vector3(Segment.end->position, floats);
		floats.push_back(Segment.radius);
		push_matrix(Segment.transformation, floats);

		// Save extra parameters ...
		for(parameters_t::const_iterator parameter = Segment.vertex_data.begin(); parameter != Segment.vertex_data.end(); parameter++)
			grouped_parameters[parameter->first].push_back(parameter->second);

		m_opcodes.push(m_opcode_id++);
		nleaf++;
	}

	void visit_subtract(k3d::blobby::subtract& Subtract)
	{
		// Note - order matters, here !
		Subtract.subtrahend->accept(*this);
		Subtract.minuend->accept(*this);

		codes.push_back(4);
		unsigned_integer opcode2 = m_opcodes.top();
		m_opcodes.pop();
		unsigned_integer opcode1 = m_opcodes.top();
		m_opcodes.pop();

		codes.push_back(opcode1);
		codes.push_back(opcode2);

		m_opcodes.push(m_opcode_id++);
	}

	void visit_divide(k3d::blobby::divide& Divide)
	{
		// Note - order matters, here !
		Divide.dividend->accept(*this);
		Divide.divisor->accept(*this);

		codes.push_back(5);
		unsigned_integer opcode2 = m_opcodes.top();
		m_opcodes.pop();
		unsigned_integer opcode1 = m_opcodes.top();
		m_opcodes.pop();

		codes.push_back(opcode1);
		codes.push_back(opcode2);

		m_opcodes.push(m_opcode_id++);
	}

	void visit_add(k3d::blobby::add& Add)
	{
		Add.operands_accept(*this);

		codes.push_back(0);
		codes.push_back(Add.operands.size());

		for(unsigned long n = 0; n < Add.operands.size(); ++n)
			{
				codes.push_back(m_opcodes.top());
				m_opcodes.pop();
			}

		m_opcodes.push(m_opcode_id++);
	}

	void visit_multiply(k3d::blobby::multiply& Multiply)
	{
		Multiply.operands_accept(*this);

		codes.push_back(1);
		codes.push_back(Multiply.operands.size());

		for(unsigned long n = 0; n < Multiply.operands.size(); n++)
			{
				codes.push_back(m_opcodes.top());
				m_opcodes.pop();
			}

		m_opcodes.push(m_opcode_id++);
	}

	void visit_max(k3d::blobby::max& Max)
	{
		Max.operands_accept(*this);

		codes.push_back(2);
		codes.push_back(Max.operands.size());

		for(unsigned long n = 0; n < Max.operands.size(); n++)
			{
				codes.push_back(m_opcodes.top());
				m_opcodes.pop();
			}

		m_opcodes.push(m_opcode_id++);
	}

	void visit_min(k3d::blobby::min& Min)
	{
		Min.operands_accept(*this);

		codes.push_back(3);
		codes.push_back(Min.operands.size());

		for(unsigned long n = 0; n < Min.operands.size(); n++)
			{
				codes.push_back(m_opcodes.top());
				m_opcodes.pop();
			}

		m_opcodes.push(m_opcode_id++);
	}

	std::stack<unsigned long> m_opcodes;
	unsigned long m_opcode_id;

	unsigned_integer& nleaf;
	unsigned_integers& codes;
	reals& floats;
	strings& names;
	grouped_parameters_t& grouped_parameters;
};

} // namespace detail

/////////////////////////////////////////////////////////////////////////////
// render

void render(const k3d::mesh& Mesh, const render_state& State)
{
	// For each point cloud in the mesh ...
	for(k3d::mesh::point_groups_t::const_iterator point_group = Mesh.point_groups.begin(); point_group != Mesh.point_groups.end(); ++point_group)
		{
			// Set the point cloud material ...
			k3d::ri::setup_material((*point_group)->material, State);

			// Setup point cloud parameters ...
			k3d::ri::parameter_list parameters;

			// Setup constant parameters ...
			parameters += detail::build_parameters((*point_group)->constant_data.begin(), (*point_group)->constant_data.end(), k3d::ri::CONSTANT);

			// Setup vertex parameters ...
			parameters += detail::build_parameters((*point_group)->points.begin(), (*point_group)->points.end(), k3d::ri::VERTEX);

			// Setup points ...
			k3d::ri::points points;
			for(k3d::point_group::points_t::const_iterator point = (*point_group)->points.begin(); point != (*point_group)->points.end(); ++point)
				points.push_back((*point)->position);
			parameters.push_back(k3d::ri::parameter(k3d::ri::RI_P(), k3d::ri::VERTEX, points));

			State.engine.RiPointsV(points.size(), parameters);
		}

	// For each polyhedron in the mesh ...
	for(k3d::mesh::polyhedra_t::const_iterator polyhedron = Mesh.polyhedra.begin(); polyhedron != Mesh.polyhedra.end(); ++polyhedron)
		{
			// Set the polyhedron material ...
			k3d::ri::setup_material((*polyhedron)->material, State);

			/// Render as regular-ole' polygons
			if(k3d::polyhedron::POLYGONS == (*polyhedron)->type)
				{
					// Setup geometry ...
					k3d::ri::unsigned_integers loop_counts;
					k3d::ri::unsigned_integers vertex_counts;
					k3d::ri::unsigned_integers vertex_ids;
					k3d::ri::points ripoints;

					std::vector<k3d::point*> points;
					std::vector<k3d::split_edge*> edges;
					std::map<k3d::point*, k3d::ri::unsigned_integer> point_map;

					// For each polygon face ...
					for(k3d::polyhedron::faces_t::const_iterator face = (*polyhedron)->faces.begin(); face != (*polyhedron)->faces.end(); ++face)
						{
							// List vertices for the face ...
							k3d::ri::unsigned_integer vertex_count = 0;
							for(k3d::split_edge* edge = (*face)->first_edge; edge; edge = edge->face_clockwise)
								{
									++vertex_count;

									edges.push_back(edge);

									if(!point_map.count(edge->vertex))
										{
											point_map.insert(std::make_pair(edge->vertex, points.size()));
											points.push_back(edge->vertex);
											ripoints.push_back(edge->vertex->position);
										}

									vertex_ids.push_back(point_map[edge->vertex]);

									if((*face)->first_edge == edge->face_clockwise)
										break;
								}
							vertex_counts.push_back(vertex_count);

							// For each hole in the face ...
							for(k3d::face::holes_t::const_iterator hole = (*face)->holes.begin(); hole != (*face)->holes.end(); ++hole)
								{
									// List vertices for the hole ...
									k3d::ri::unsigned_integer vertex_count = 0;
									for(k3d::split_edge* edge = *hole; edge; edge = edge->face_clockwise)
										{
											++vertex_count;

											edges.push_back(edge);

											if(!point_map.count(edge->vertex))
												{
													point_map.insert(std::make_pair(edge->vertex, points.size()));
													points.push_back(edge->vertex);
													ripoints.push_back(edge->vertex->position);
												}

											vertex_ids.push_back(point_map[edge->vertex]);

											if(*hole == edge->face_clockwise)
												break;
										}
									vertex_counts.push_back(vertex_count);
								}

							// Total number of loops in the face (including holes) ...
							loop_counts.push_back(1 + (*face)->holes.size());
						}

					if(loop_counts.size())
						{
							// Setup the polyhedron parameters ...
							k3d::ri::parameter_list parameters;

							// Setup constant data ...
							parameters += detail::build_parameters((*polyhedron)->constant_data.begin(), (*polyhedron)->constant_data.end(), k3d::ri::CONSTANT);

							// Setup uniform data ...
							parameters += detail::build_parameters((*polyhedron)->faces.begin(), (*polyhedron)->faces.end(), k3d::ri::UNIFORM);

							// Setup vertex data ...
							parameters += detail::build_parameters(points.begin(), points.end(), k3d::ri::VERTEX);

							// Setup points ...
							parameters.push_back(k3d::ri::parameter(k3d::ri::RI_P(), k3d::ri::VERTEX, ripoints));

							// Setup facevarying data ...
							parameters += detail::build_parameters(edges.begin(), edges.end(), k3d::ri::FACEVARYING);

							State.engine.RiPointsGeneralPolygonsV(loop_counts, vertex_counts, vertex_ids, parameters);
						}
				}
			// Render as a subdivision mesh ...
			else if(k3d::polyhedron::CATMULL_CLARK_SUBDIVISION_MESH == (*polyhedron)->type)
				{
					k3d::ri::unsigned_integers vertex_counts;
					k3d::ri::unsigned_integers vertex_ids;
					k3d::ri::points ripoints;

					std::vector<k3d::point*> points;
					std::vector<k3d::split_edge*> edges;
					std::map<k3d::point*, k3d::ri::unsigned_integer> point_map;
					std::map<k3d::face*, k3d::ri::unsigned_integer> face_map;

					// For each polygon face ...
					for(k3d::polyhedron::faces_t::const_iterator face = (*polyhedron)->faces.begin(); face != (*polyhedron)->faces.end(); ++face)
						{
							face_map.insert(std::make_pair(*face, face_map.size()));

							// List vertices for the face ...
							k3d::ri::unsigned_integer vertex_count = 0;
							for(k3d::split_edge* edge = (*face)->first_edge; edge; edge = edge->face_clockwise)
								{
									++vertex_count;

									edges.push_back(edge);

									if(!point_map.count(edge->vertex))
										{
											point_map.insert(std::make_pair(edge->vertex, points.size()));
											points.push_back(edge->vertex);
											ripoints.push_back(edge->vertex->position);
										}

									vertex_ids.push_back(point_map[edge->vertex]);

									if((*face)->first_edge == edge->face_clockwise)
										break;
								}
							vertex_counts.push_back(vertex_count);
						}

					// Setup parameters ...
					k3d::ri::parameter_list parameters;

					// Setup constant data ...
					parameters += detail::build_parameters((*polyhedron)->constant_data.begin(), (*polyhedron)->constant_data.end(), k3d::ri::CONSTANT);

					// Setup uniform data ...
					parameters += detail::build_parameters((*polyhedron)->faces.begin(), (*polyhedron)->faces.end(), k3d::ri::UNIFORM);

					// Setup vertex data ...
					parameters += detail::build_parameters(points.begin(), points.end(), k3d::ri::VERTEX);

					// Setup points ...
					parameters.push_back(k3d::ri::parameter(k3d::ri::RI_P(), k3d::ri::VERTEX, ripoints));

					// Setup facevarying data ...
					parameters += detail::build_parameters(edges.begin(), edges.end(), k3d::ri::FACEVARYING);

					// Setup tags ...
					k3d::ri::strings tags;
					k3d::ri::unsigned_integers tag_counts;
					k3d::ri::integers tag_integers;
					k3d::ri::reals tag_reals;
					detail::build_tags((*polyhedron)->tags.begin(), (*polyhedron)->tags.end(), tags, tag_counts, tag_integers, tag_reals);
					detail::build_tags((*polyhedron)->faces.begin(), (*polyhedron)->faces.end(), face_map, tags, tag_counts, tag_integers, tag_reals);
					detail::build_tags((*polyhedron)->edges.begin(), (*polyhedron)->edges.end(), point_map, tags, tag_counts, tag_integers, tag_reals);
					detail::build_tags(points.begin(), points.end(), point_map, tags, tag_counts, tag_integers, tag_reals);


					State.engine.RiSubdivisionMeshV("catmull-clark", vertex_counts, vertex_ids, tags, tag_counts, tag_integers, tag_reals, parameters);
				}
		}

	// For each linear curve group in the mesh ...
	for(k3d::mesh::linear_curve_groups_t::const_iterator group = Mesh.linear_curve_groups.begin(); group != Mesh.linear_curve_groups.end(); ++group)
		{
			// Set the group material ...
			k3d::ri::setup_material((*group)->material, State);

			// Keep track of curve control points ...
			std::vector<k3d::point*> points;
			k3d::ri::points ripoints;
			k3d::ri::unsigned_integers point_counts;

			// For each linear curve in the group ...
			for(k3d::linear_curve_group::curves_t::const_iterator curve = (*group)->curves.begin(); curve != (*group)->curves.end(); ++curve)
				{
					for(k3d::linear_curve::control_points_t::const_iterator control_point = (*curve)->control_points.begin(); control_point != (*curve)->control_points.end(); ++control_point)
						{
							points.push_back(*control_point);
							ripoints.push_back((*control_point)->position);
						}

					point_counts.push_back((*curve)->control_points.size());
				}

			// Setup group parameters ...
			k3d::ri::parameter_list parameters;

			// Setup constant parameters ...
			parameters += detail::build_parameters((*group)->constant_data.begin(), (*group)->constant_data.end(), k3d::ri::CONSTANT);

			// Setup uniform parameters ...
			parameters += detail::build_parameters((*group)->curves.begin(), (*group)->curves.end(), k3d::ri::UNIFORM);

			// Setup varying parameters ...
			parameters += detail::build_parameters((*group)->curves.begin(), (*group)->curves.end(), k3d::ri::VARYING);

			// Setup vertex parameters ...
			parameters += detail::build_parameters(points.begin(), points.end(), k3d::ri::VERTEX);

			// Setup points ...
			parameters.push_back(k3d::ri::parameter(k3d::ri::RI_P(), k3d::ri::VERTEX, ripoints));

			State.engine.RiCurvesV("linear", point_counts, (*group)->wrap ? "periodic" : "nonperiodic", parameters);
		}

	// For each cubic curve group in the mesh ...
	for(k3d::mesh::cubic_curve_groups_t::const_iterator group = Mesh.cubic_curve_groups.begin(); group != Mesh.cubic_curve_groups.end(); ++group)
		{
			// Set the group material ...
			k3d::ri::setup_material((*group)->material, State);

			// Keep track of curve control points ...
			std::vector<k3d::point*> points;
			k3d::ri::points ripoints;
			k3d::ri::unsigned_integers point_counts;

			// For each cubic curve in the group ...
			for(k3d::cubic_curve_group::curves_t::const_iterator curve = (*group)->curves.begin(); curve != (*group)->curves.end(); ++curve)
				{
					for(k3d::cubic_curve::control_points_t::const_iterator control_point = (*curve)->control_points.begin(); control_point != (*curve)->control_points.end(); ++control_point)
						{
							points.push_back(*control_point);
							ripoints.push_back((*control_point)->position);
						}

					point_counts.push_back((*curve)->control_points.size());
				}

			// Setup group parameters ...
			k3d::ri::parameter_list parameters;

			// Setup constant parameters ...
			parameters += detail::build_parameters((*group)->constant_data.begin(), (*group)->constant_data.end(), k3d::ri::CONSTANT);

			// Setup uniform parameters ...
			parameters += detail::build_parameters((*group)->curves.begin(), (*group)->curves.end(), k3d::ri::UNIFORM);

			// Setup varying parameters ...
			parameters += detail::build_parameters((*group)->curves.begin(), (*group)->curves.end(), k3d::ri::VARYING);

			// Setup vertex parameters ...
			parameters += detail::build_parameters(points.begin(), points.end(), k3d::ri::VERTEX);

			// Setup points ...
			parameters.push_back(k3d::ri::parameter(k3d::ri::RI_P(), k3d::ri::VERTEX, ripoints));

			// At the moment, we force all cubic curves to be Bezier ...
			State.engine.RiBasis("bezier", 3, "bezier", 3);
			State.engine.RiCurvesV("cubic", point_counts, (*group)->wrap ? "periodic" : "nonperiodic", parameters);
		}

	// For each bilinear patch in the mesh ...
	for(k3d::mesh::bilinear_patches_t::const_iterator patch = Mesh.bilinear_patches.begin(); patch != Mesh.bilinear_patches.end(); ++patch)
		{
			// Set the polyhedron material ...
			k3d::ri::setup_material((*patch)->material, State);

			// Setup patch parameters ...
			k3d::ri::parameter_list parameters;

			// Setup uniform parameters ...
			parameters += detail::build_parameters((*patch)->uniform_data.begin(), (*patch)->uniform_data.end(), k3d::ri::UNIFORM);

			// Setup varying parameters ...
			parameters += detail::build_parameters((*patch)->varying_data.begin(), (*patch)->varying_data.end(), k3d::ri::VARYING);

			// Setup vertex parameters ...
			parameters += detail::build_parameters((*patch)->control_points.begin(), (*patch)->control_points.end(), k3d::ri::VERTEX);

			// Setup points ...
			k3d::ri::points points;
			for(unsigned int i = 0; i != 4; ++i)
				points.push_back((*patch)->control_points[i]->position);
			parameters.push_back(k3d::ri::parameter(k3d::ri::RI_P(), k3d::ri::VERTEX, points));

			State.engine.RiPatchV("bilinear", parameters);
		}

	// For each bicubic patch in the mesh ...
	for(k3d::mesh::bicubic_patches_t::const_iterator patch = Mesh.bicubic_patches.begin(); patch != Mesh.bicubic_patches.end(); ++patch)
		{
			// Set the polyhedron material ...
			k3d::ri::setup_material((*patch)->material, State);

			// Setup patch parameters ...
			k3d::ri::parameter_list parameters;

			// Setup uniform parameters ...
			parameters += detail::build_parameters((*patch)->uniform_data.begin(), (*patch)->uniform_data.end(), k3d::ri::UNIFORM);

			// Setup varying parameters ...
			parameters += detail::build_parameters((*patch)->varying_data.begin(), (*patch)->varying_data.end(), k3d::ri::VARYING);

			// Setup vertex parameters ...
			parameters += detail::build_parameters((*patch)->control_points.begin(), (*patch)->control_points.end(), k3d::ri::VERTEX);

			// Setup points ...
			k3d::ri::points points;
			for(unsigned int i = 0; i != 16; ++i)
				points.push_back((*patch)->control_points[i]->position);
			parameters.push_back(k3d::ri::parameter(k3d::ri::RI_P(), k3d::ri::VERTEX, points));

			// At the moment, we force all bicubic patches to be Bezier ...
			State.engine.RiBasis("bezier", 3, "bezier", 3);
			State.engine.RiPatchV("bicubic", parameters);
		}

	// For each NURBS patch in the mesh ...
	for(k3d::mesh::nupatches_t::const_iterator nupatch = Mesh.nupatches.begin(); nupatch != Mesh.nupatches.end(); ++nupatch)
		{
			const k3d::nupatch& patch = **nupatch;

			// Set the patch material ...
			k3d::ri::setup_material(patch.material, State);

			// Setup patch parameters ...
			k3d::ri::parameter_list parameters;

			const k3d::ri::unsigned_integer u_count = patch.u_knots.size() - patch.u_order;
			const k3d::ri::unsigned_integer v_count = patch.v_knots.size() - patch.v_order;
			const k3d::ri::unsigned_integer u_order = patch.u_order;
			const k3d::ri::unsigned_integer v_order = patch.v_order;

			// Setup points ...
			k3d::ri::hpoints points;
			for(k3d::nupatch::control_points_t::const_iterator control_point = patch.control_points.begin(); control_point != patch.control_points.end(); ++control_point)
				points.push_back(k3d::vector4(control_point->position->position * control_point->weight, control_point->weight));
			parameters.push_back(k3d::ri::parameter(k3d::ri::RI_PW(), k3d::ri::VERTEX, points));

			State.engine.RiNuPatchV(
				u_count,
				u_order,
				patch.u_knots,
				patch.u_knots[u_order-1],
				patch.u_knots[u_count],
				v_count,
				v_order,
				patch.v_knots,
				patch.v_knots[v_order-1],
				patch.v_knots[v_count],
				parameters
				);
		}

	// For each blobby in the mesh ...
	for(k3d::mesh::blobbies_t::const_iterator blobby = Mesh.blobbies.begin(); blobby != Mesh.blobbies.end(); ++blobby)
		{
			// Setup blobby parameters ...
			unsigned_integer nleaf = 0;
			unsigned_integers codes;
			reals floats;
			strings names;
			detail::grouped_parameters_t grouped_parameters;

			detail::blobby_vm(**blobby, nleaf, codes, floats, names, grouped_parameters);

			parameter_list parameters;
			parameters += detail::build_parameters(grouped_parameters, k3d::ri::VERTEX);
			parameters += detail::build_parameters(grouped_parameters, k3d::ri::VARYING);

			State.engine.RiBlobbyV(nleaf, codes, floats, names, parameters);
		}
}

/////////////////////////////////////////////////////////////////////////////
// render_engine::implementation

class render_engine::implementation
{
public:
	implementation(std::ostream& Stream) :
		m_stream(Stream),
		m_light_handle(0),
		m_object_handle(0),
		m_frame_block(false),
		m_world_block(false),
		m_object_block(false),
		m_motion_block(false)
	{
	}

	/// Stores the RIB output stream
	std::ostream& m_stream;
	/// Stores the current light handle
	light_handle m_light_handle;
	/// Stores the current object handle
	object_handle m_object_handle;
	/// Set to true within a frame block
	bool m_frame_block;
	/// Set to true within a world block
	bool m_world_block;
	/// Set to true within an object block
	bool m_object_block;
	/// Set to true within a motion block
	bool m_motion_block;
	/// Stores the set of shaders in-use
	shaders_t m_shaders;
};

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

render_engine::render_engine(std::ostream& Stream) :
	m_implementation(new implementation(Stream))
{
	// Enable inline type declarations by default ...
	::set_inline_types(m_implementation->m_stream, true);

	// Start out without any indentation ...
	::reset_indentation(m_implementation->m_stream);

	// Start writing the RIB file header ...
	RiStructure("RenderMan RIB-Structure 1.0");
	m_implementation->m_stream << "version 3.03" << "\n";
}

render_engine::~render_engine()
{
	delete m_implementation;
}

const render_engine::shaders_t render_engine::shaders()
{
	return m_implementation->m_shaders;
}

bool render_engine::set_inline_types(const bool Inline)
{
	return ::set_inline_types(m_implementation->m_stream, Inline);
}

void render_engine::RiDeclare(const string& Name, const string& Type)
{
	// Sanity checks ...
	return_if_fail(Name.size());
	return_if_fail(Type.size());

	m_implementation->m_stream << ::indentation << "Declare " << format_string(Name) << " " << format_string(Type) << "\n";
}

void render_engine::RiFrameBegin(const unsigned_integer FrameNumber)
{
	// Sanity checks ...
	if(m_implementation->m_frame_block)
		{
			std::cerr << error << "Cannot nest calls to RiFrameBegin()" << std::endl;
			return;
		}

	m_implementation->m_frame_block = true;
	m_implementation->m_stream << ::indentation << ::indentation << "FrameBegin " << FrameNumber << "\n";
	::push_indent(m_implementation->m_stream);
}

void render_engine::RiFrameEnd()
{
	::pop_indent(m_implementation->m_stream);
	m_implementation->m_stream << ::indentation << "FrameEnd" << "\n";
	m_implementation->m_frame_block = false;
}

void render_engine::RiWorldBegin()
{
	// Sanity checks ...
	if(m_implementation->m_world_block)
		{
			std::cerr << error << "Cannot nest calls to RiWorldBegin()" << std::endl;
			return;
		}

	m_implementation->m_world_block = true;
	m_implementation->m_stream << ::indentation << "WorldBegin" << "\n";
	::push_indent(m_implementation->m_stream);
}

void render_engine::RiWorldEnd()
{
	::pop_indent(m_implementation->m_stream);
	m_implementation->m_stream << ::indentation << "WorldEnd" << "\n";
	m_implementation->m_world_block = false;
}

void render_engine::RiFormat(const unsigned_integer XResolution, const unsigned_integer YResolution, const real AspectRatio)
{
	m_implementation->m_stream << ::indentation << "Format " << XResolution << " " << YResolution << " " << AspectRatio << "\n";
}

void render_engine::RiFrameAspectRatio(real AspectRatio)
{
	m_implementation->m_stream << ::indentation << "FrameAspectRatio " << AspectRatio << "\n";
}

void render_engine::RiScreenWindow(real Left, real Right, real Bottom, real Top)
{
	m_implementation->m_stream << ::indentation << "ScreenWindow " << Left << " " << Right << " " << Bottom << " " << Top << "\n";
}

void render_engine::RiCropWindow(real XMin, real XMax, real YMin, real YMax)
{
	m_implementation->m_stream << ::indentation << "CropWindow " << XMin << " " << XMax << " " << YMin << " " << YMax << "\n";
}

void render_engine::RiProjectionV(const string& Name, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Projection " << format_string(Name) << " " << Parameters << "\n";
}

void render_engine::RiClipping(real NearPlane, real FarPlane)
{
	m_implementation->m_stream << ::indentation << "Clipping " << NearPlane << " " << FarPlane << "\n";
}

void render_engine::RiDepthOfField(real FStop, real FocalLength, real FocalDistance)
{
	m_implementation->m_stream << ::indentation << "DepthOfField " << FStop << " " << FocalLength << " " << FocalDistance << "\n";
}

void render_engine::RiShutter(real OpenTime, real CloseTime)
{
	m_implementation->m_stream << ::indentation << "Shutter " << OpenTime << " " << CloseTime << "\n";
}

void render_engine::RiPixelFilter(const string& FilterName, real XWidth, real YWidth)
{
	m_implementation->m_stream << ::indentation << "PixelFilter " << format_string(FilterName) << " " << XWidth << " " << YWidth << "\n";
}

void render_engine::RiPixelVariance(real Variation)
{
	m_implementation->m_stream << ::indentation << "PixelVariance " << Variation << "\n";
}
void render_engine::RiPixelSamples(real XSamples, real YSamples)
{
	m_implementation->m_stream << ::indentation << "PixelSamples " << XSamples << " " << YSamples << "\n";
}

void render_engine::RiExposure(real Gain, real Gamma)
{
	m_implementation->m_stream << ::indentation << "Exposure " << Gain << " " << Gamma << "\n";
}

void render_engine::RiImagerV(const string& Name, const parameter_list& Parameters)
{
	m_implementation->m_shaders.insert(Name);
	m_implementation->m_stream << ::indentation << "Imager " << format_string(Name) << " " << Parameters << "\n";
}

void render_engine::RiQuantize(const string& Type, integer One, integer QMin, integer QMax, real Amplitude)
{
	m_implementation->m_stream << ::indentation << "Quantize " << format_string(Type) << " " << One << " " << QMin << " " << QMax << " " << Amplitude << "\n";
}

void render_engine::RiDisplayV(const string& Name, const string& Type, const string& Mode, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Display " << format_string(Name) << " " << format_string(Type) << " " << format_string(Mode) << " " << Parameters << "\n";
}

void render_engine::RiHiderV(const string& Type, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Hider " << format_string(Type) << " " << Parameters << "\n";
}

void render_engine::RiColorSamples(const unsigned_integer ParameterCount, const reals& nRGB, const reals& RGBn)
{
	return_if_fail(ParameterCount == nRGB.size());
	return_if_fail(ParameterCount == RGBn.size());

	m_implementation->m_stream << ::indentation << "ColorSamples " << format_array(nRGB.begin(), nRGB.end()) << " " << format_array(RGBn.begin(), RGBn.end()) << "\n";
}

void render_engine::RiRelativeDetail(real RelativeDetail)
{
	m_implementation->m_stream << ::indentation << "RelativeDetail " << RelativeDetail << "\n";
}

void render_engine::RiOptionV(const string& Name, const parameter_list& Parameters)
{
	const bool old_state = ::set_inline_types(m_implementation->m_stream, false);

	m_implementation->m_stream << ::indentation << "Option " << format_string(Name) << " " << Parameters << "\n";

	::set_inline_types(m_implementation->m_stream, old_state);
}

void render_engine::RiAttributeBegin()
{
	m_implementation->m_stream << ::indentation << "AttributeBegin" << "\n";
	::push_indent(m_implementation->m_stream);
}

void render_engine::RiAttributeEnd()
{
	::pop_indent(m_implementation->m_stream);
	m_implementation->m_stream << ::indentation << "AttributeEnd" << "\n";
}

void render_engine::RiColor(const color& Color)
{
	m_implementation->m_stream << ::indentation << "Color " << Color << "\n";
}

void render_engine::RiOpacity(const color& Opacity)
{
	m_implementation->m_stream << ::indentation << "Opacity " << Opacity << "\n";
}

void render_engine::RiTextureCoordinates(real S1, real T1, real S2, real T2, real S3, real T3, real S4, real T4)
{
	m_implementation->m_stream << ::indentation << "TextureCoordinates " << S1 << " " << T1 << " " << S2 << " " << T2 << " " << S3 << " " << T3 << " " << S4 << " " << T4 << "\n";
}

const light_handle render_engine::RiLightSourceV(const string& Name, const parameter_list& Parameters)
{
	m_implementation->m_shaders.insert(Name);
	m_implementation->m_stream << ::indentation << "LightSource " << format_string(Name) << " " << ++m_implementation->m_light_handle << " " << Parameters << "\n";
	return m_implementation->m_light_handle;
}

const light_handle render_engine::RiAreaLightSourceV(const string& Name, const parameter_list& Parameters)
{
	m_implementation->m_shaders.insert(Name);
	m_implementation->m_stream << ::indentation << "AreaLightSource " << format_string(Name) << " " << ++m_implementation->m_light_handle << " " << Parameters << "\n";
	return m_implementation->m_light_handle;
}

void render_engine::RiIlluminate(const light_handle LightHandle, bool OnOff)
{
	m_implementation->m_stream << ::indentation << "Illuminate " << LightHandle << " " << OnOff << "\n";
}

void render_engine::RiSurfaceV(const string& Name, const parameter_list& Parameters)
{
	m_implementation->m_shaders.insert(Name);
	m_implementation->m_stream << ::indentation << "Surface " << format_string(Name) << " " << Parameters << "\n";
}

void render_engine::RiAtmosphereV(const string& Name, const parameter_list& Parameters)
{
	m_implementation->m_shaders.insert(Name);
	m_implementation->m_stream << ::indentation << "Atmosphere " << format_string(Name) << " " << Parameters << "\n";
}

void render_engine::RiInteriorV(const string& Name, const parameter_list& Parameters)
{
	m_implementation->m_shaders.insert(Name);
	m_implementation->m_stream << ::indentation << "Interior " << format_string(Name) << " " << Parameters << "\n";
}

void render_engine::RiExteriorV(const string& Name, const parameter_list& Parameters)
{
	m_implementation->m_shaders.insert(Name);
	m_implementation->m_stream << ::indentation << "Exterior " << format_string(Name) << " " << Parameters << "\n";
}

void render_engine::RiShadingRate(real Size)
{
	m_implementation->m_stream << ::indentation << "ShadingRate " << Size << "\n";
}

void render_engine::RiShadingInterpolation(const string& Type)
{
	m_implementation->m_stream << ::indentation << "ShadingInterpolation " << format_string(Type) << "\n";
}

void render_engine::RiMatte(bool OnOff)
{
	m_implementation->m_stream << ::indentation << "Matte " << OnOff << "\n";
}

void render_engine::RiBound(const boost::array<real, 6>& Bound)
{
	m_implementation->m_stream << ::indentation << "Bound " << format_array(Bound.begin(), Bound.end()) << "\n";
}

void render_engine::RiDetail(const boost::array<real, 6>& Bound)
{
	m_implementation->m_stream << ::indentation << "Detail " << format_array(Bound.begin(), Bound.end()) << "\n";
}

void render_engine::RiDetailRange(const real MinVis, const real LowTran, const real UpTran, const real MaxVis)
{
	m_implementation->m_stream << ::indentation << "DetailRange " << MinVis << " " << LowTran << " " << UpTran << " " << MaxVis << "\n";
}

void render_engine::RiGeometricApproximation(const string& Type, real Value)
{
	m_implementation->m_stream << ::indentation << "GeometricApproximation " << format_string(Type) << " " << Value << "\n";
}

void render_engine::RiGeometricRepresentation(const string& Type)
{
	m_implementation->m_stream << ::indentation << "GeometricRepresentation " << format_string(Type) << "\n";
}

void render_engine::RiOrientation(const string& Orientation)
{
	m_implementation->m_stream << ::indentation << "Orientation " << format_string(Orientation) << "\n";
}

void render_engine::RiReverseOrientation()
{
	m_implementation->m_stream << ::indentation << "ReverseOrientation" << "\n";
}

void render_engine::RiSides(const unsigned_integer Sides)
{
	m_implementation->m_stream << ::indentation << "Sides " << Sides << "\n";
}

void render_engine::RiIdentity()
{
	m_implementation->m_stream << ::indentation << "Identity" << "\n";
}

void render_engine::RiTransform(const matrix& Transform)
{
	m_implementation->m_stream << ::indentation << "Transform " << format_array(Transform.begin(), Transform.end()) << "\n";
}

void render_engine::RiConcatTransform(const matrix& Transform)
{
	m_implementation->m_stream << ::indentation << "ConcatTransform " << format_array(Transform.begin(), Transform.end()) << "\n";
}

void render_engine::RiPerspective(real FieldOfView)
{
	m_implementation->m_stream << ::indentation << "Perspective " << FieldOfView << "\n";
}

void render_engine::RiTranslate(real DX, real DY, real DZ)
{
	m_implementation->m_stream << ::indentation << "Translate " << DX << " " << DY << " " << DZ << "\n";
}

void render_engine::RiRotate(real Angle, real DX, real DY, real DZ)
{
	m_implementation->m_stream << ::indentation << "Rotate " << Angle << " " << DX << " " << DY << " " << DZ << "\n";
}

void render_engine::RiScale(real DX, real DY, real DZ)
{
	m_implementation->m_stream << ::indentation << "Scale " << DX << " " << DY << " " << DZ << "\n";
}

void render_engine::RiSkew(real Angle, real DX1, real DY1, real DZ1, real DX2, real DY2, real DZ2)
{
	m_implementation->m_stream << ::indentation << "Skew " << Angle << " " << DX1 << " " << DY1 << " " << DZ1 << " " << DX2 << " " << DY2 << " " << DZ2 << "\n";
}

void render_engine::RiDeformationV(const string& Name, const parameter_list& Parameters)
{
	m_implementation->m_shaders.insert(Name);
	m_implementation->m_stream << ::indentation << "Deformation " << format_string(Name) << " " << Parameters << "\n";
}

void render_engine::RiDisplacementV(const string& Name, const parameter_list& Parameters)
{
	m_implementation->m_shaders.insert(Name);
	m_implementation->m_stream << ::indentation << "Displacement " << format_string(Name) << " " << Parameters << "\n";
}

void render_engine::RiCoordinateSystem(const string& Space)
{
	m_implementation->m_stream << ::indentation << "CoordinateSystem " << format_string(Space) << "\n";
}

void render_engine::RiCoordSysTransform(const string& Space)
{
	m_implementation->m_stream << ::indentation << "CoordSysTransform " << format_string(Space) << "\n";
}

void render_engine::RiTransformBegin()
{
	m_implementation->m_stream << ::indentation << "TransformBegin" << "\n";
	::push_indent(m_implementation->m_stream);
}

void render_engine::RiTransformEnd()
{
	::pop_indent(m_implementation->m_stream);
	m_implementation->m_stream << ::indentation << "TransformEnd" << "\n";
}

void render_engine::RiAttributeV(const string& Name, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Attribute " << format_string(Name) << " " << Parameters << "\n";
}

void render_engine::RiPointsV(const unsigned_integer VertexCount, const parameter_list& Parameters)
{
	// Sanity checks ...
	return_if_fail(VertexCount);

	m_implementation->m_stream << ::indentation << "Points " << Parameters << "\n";
}

void render_engine::RiPolygonV(const unsigned_integer VertexCount, const parameter_list& Parameters)
{
	// Sanity checks ...
	return_if_fail(VertexCount);

	m_implementation->m_stream << ::indentation << "Polygon " << Parameters << "\n";
}

void render_engine::RiGeneralPolygonV(const unsigned_integers& VertexCounts, const parameter_list& Parameters)
{
	// Do some simple sanity checks ...
	return_if_fail(VertexCounts.size());

	m_implementation->m_stream << ::indentation << "GeneralPolygon " << format_array(VertexCounts.begin(), VertexCounts.end()) << " " << Parameters << "\n";
}

void render_engine::RiPointsPolygonsV(const unsigned_integers& VertexCounts, const unsigned_integers& VertexIDs, const parameter_list& Parameters)
{
	// Sanity checks ...
	return_if_fail(VertexCounts.size());
	return_if_fail(VertexIDs.size() == std::accumulate(VertexCounts.begin(), VertexCounts.end(), 0UL));

	m_implementation->m_stream << ::indentation << "PointsPolygons " << format_array(VertexCounts.begin(), VertexCounts.end()) << " " << format_array(VertexIDs.begin(), VertexIDs.end()) << " " << Parameters << "\n";
}

void render_engine::RiPointsGeneralPolygonsV(const unsigned_integers& LoopCounts, const unsigned_integers& VertexCounts, const unsigned_integers& VertexIDs, const parameter_list& Parameters)
{
	// Sanity checks ...
	return_if_fail(LoopCounts.size());
	return_if_fail(VertexCounts.size() == std::accumulate(LoopCounts.begin(), LoopCounts.end(), 0UL));
	return_if_fail(VertexIDs.size() == std::accumulate(VertexCounts.begin(), VertexCounts.end(), 0UL));

	m_implementation->m_stream << ::indentation << "PointsGeneralPolygons " << format_array(LoopCounts.begin(), LoopCounts.end()) << " " << format_array(VertexCounts.begin(), VertexCounts.end()) << " " << format_array(VertexIDs.begin(), VertexIDs.end()) << " " << Parameters << "\n";
}

void render_engine::RiBasis(const matrix& UBasis, const unsigned_integer UStep, const matrix& VBasis, const unsigned_integer VStep)
{
	m_implementation->m_stream << ::indentation << "Basis " << format_array(UBasis.begin(), UBasis.end()) << " " << UStep << " " << format_array(VBasis.begin(), VBasis.end()) << " " << VStep << "\n";
}

void render_engine::RiBasis(const string& UBasis, const unsigned_integer UStep, const string& VBasis, const unsigned_integer VStep)
{
	m_implementation->m_stream << ::indentation << "Basis " << format_string(UBasis) << " " << UStep << " " << format_string(VBasis) << " " << VStep << "\n";
}

void render_engine::RiPatchV(const string& Type, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Patch " << format_string(Type) << " " << Parameters << "\n";
}

void render_engine::RiPatchMeshV(const string& Type, const unsigned_integer UCount, const string& UWrap, const unsigned_integer VCount, const string& VWrap, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "PatchMesh " << format_string(Type) << " " << UCount << " " << format_string(UWrap) << " " << VCount << " " << format_string(VWrap) << " " << Parameters << "\n";
}

void render_engine::RiNuPatchV(const unsigned_integer UCount, const unsigned_integer UOrder, const reals& UKnot, const real UMin, const real UMax, const unsigned_integer VCount, const unsigned_integer VOrder, const reals& VKnot, const real VMin, const real VMax, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "NuPatch " << UCount << " " << UOrder << " " << format_array(UKnot.begin(), UKnot.end()) << " " << UMin << " " << UMax << " " << VCount << " " << VOrder << " " << format_array(VKnot.begin(), VKnot.end()) << " " << VMin << " " << VMax << " " << Parameters << "\n";
}

void render_engine::RiTrimCurve(const unsigned_integer LoopCount, const unsigned_integers& CurveCounts, const unsigned_integers& Orders, const reals& Knots, const reals& Minimums, const reals& Maximums, const unsigned_integers& KnotCounts, const reals& U, const reals& V, const reals& W)
{
	m_implementation->m_stream << ::indentation << "TrimCurve " << LoopCount << " " << format_array(CurveCounts.begin(), CurveCounts.end()) << " " << format_array(Orders.begin(), Orders.end()) << " " << format_array(Knots.begin(), Knots.end()) << " " << format_array(Minimums.begin(), Minimums.end()) << " " << format_array(Maximums.begin(), Maximums.end()) << " " << format_array(KnotCounts.begin(), KnotCounts.end()) << " " << format_array(U.begin(), U.end()) << " " << format_array(V.begin(), V.end()) << " " << format_array(W.begin(), W.end()) << "\n";
}

void render_engine::RiSphereV(real Radius, real ZMin, real ZMax, real ThetaMax, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Sphere " << Radius << " " << ZMin << " " << ZMax << " " << ThetaMax << " " << Parameters << "\n";
}

void render_engine::RiConeV(real Height, real Radius, real ThetaMax, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Cone " << Height << " " << Radius << " " << ThetaMax << " " << Parameters << "\n";
}

void render_engine::RiCylinderV(real Radius, real ZMin, real ZMax, real ThetaMax, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Cylinder " << Radius << " " << ZMin << " " << ZMax << " " << ThetaMax << " " << Parameters << "\n";
}

void render_engine::RiHyperboloidV(const point& Point1, const point& Point2, real ThetaMax, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Hyperboloid " << Point1 << " " << Point2 << " " << ThetaMax << " " << Parameters << "\n";
}

void render_engine::RiParaboloidV(real RMax, real ZMin, real ZMax, real ThetaMax, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Paraboloid " << RMax << " " << ZMin << " " << ZMax << " " << ThetaMax << " " << Parameters << "\n";
}

void render_engine::RiDiskV(real Height, real Radius, real ThetaMax, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Disk " << Height << " " << Radius << " " << ThetaMax << " " << Parameters << "\n";
}

void render_engine::RiTorusV(real MajorRadius, real MinorRadius, real PhiMin, real PhiMax, real ThetaMax, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Torus " << MajorRadius << " " << MinorRadius << " " << PhiMin << " " << PhiMax << " " << ThetaMax << " " << Parameters << "\n";
}

void render_engine::RiCurvesV(const string& Type, const unsigned_integers& VertexCounts, const string& Wrap, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Curves " << format_string(Type) << " " << format_array(VertexCounts.begin(), VertexCounts.end()) << " " << format_string(Wrap) << " " << Parameters << "\n";
}

void render_engine::RiGeometryV(const string& Type, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Geometry " << format_string(Type) << " " << Parameters << "\n";
}

void render_engine::RiSolidBegin(const string& Type)
{
	m_implementation->m_stream << ::indentation << "SolidBegin " << format_string(Type) << "\n";
	::push_indent(m_implementation->m_stream);
}

void render_engine::RiSolidEnd()
{
	::pop_indent(m_implementation->m_stream);
	m_implementation->m_stream << ::indentation << "SolidEnd" << "\n";
}

const object_handle render_engine::RiObjectBegin()
{
	// Sanity checks ...
	if(m_implementation->m_object_block)
		{
			std::cerr << error << "Cannot nest calls to RiObjectBegin()" << std::endl;
			return 0;
		}

	m_implementation->m_object_block = true;
	m_implementation->m_stream << ::indentation << "ObjectBegin " << ++m_implementation->m_object_handle << "\n";
	::push_indent(m_implementation->m_stream);
	return m_implementation->m_object_handle;
}

void render_engine::RiObjectEnd()
{
	::pop_indent(m_implementation->m_stream);
	m_implementation->m_stream << ::indentation << "ObjectEnd" << "\n";
	m_implementation->m_object_block = false;
}

void render_engine::RiObjectInstance(const object_handle Handle)
{
	m_implementation->m_stream << ::indentation << "ObjectInstance " << Handle << "\n";
}

void render_engine::RiMotionBeginV(const sample_times_t& Times)
{
	// Sanity checks ...
	if(m_implementation->m_motion_block)
		{
			std::cerr << error << "Cannot nest calls to RiMotionBegin()" << std::endl;
			return;
		}

	m_implementation->m_motion_block = true;
	m_implementation->m_stream << ::indentation << "MotionBegin " << format_array(Times.begin(), Times.end()) << "\n";
	::push_indent(m_implementation->m_stream);
}

void render_engine::RiMotionEnd()
{
	::pop_indent(m_implementation->m_stream);
	m_implementation->m_stream << ::indentation << "MotionEnd" << "\n";
	m_implementation->m_motion_block = false;
}

void render_engine::RiErrorHandler(const string& Style)
{
	m_implementation->m_stream << ::indentation << "ErrorHandler " << format_string(Style) << "\n";
}

void render_engine::RiComment(const string& Comment)
{
	m_implementation->m_stream << ::indentation << "#" << Comment << "\n";
}

void render_engine::RiNewline()
{
	m_implementation->m_stream << "\n";
}

void render_engine::RiReadArchive(const string& Archive)
{
	m_implementation->m_stream << ::indentation << "ReadArchive " << Archive << "\n";
}

void render_engine::RiStructure(const string& Structure)
{
	m_implementation->m_stream << "##" << Structure << "\n";
}

void render_engine::RiSubdivisionMeshV(const string& Scheme, const unsigned_integers& VertexCounts, const unsigned_integers& VertexIDs, const strings& Tags, const unsigned_integers& ArgCounts, const integers& IntegerArgs, const reals& FloatArgs, const parameter_list& Parameters)
{
	// Sanity checks ...
	return_if_fail(VertexIDs.size() == std::accumulate(VertexCounts.begin(), VertexCounts.end(), 0UL));

	m_implementation->m_stream << ::indentation << "SubdivisionMesh " << format_string(Scheme) << " " << format_array(VertexCounts.begin(), VertexCounts.end()) << " " << format_array(VertexIDs.begin(), VertexIDs.end()) << " " << format_array(Tags.begin(), Tags.end()) << " " << format_array(ArgCounts.begin(), ArgCounts.end()) << " " << format_array(IntegerArgs.begin(), IntegerArgs.end()) << " " << format_array(FloatArgs.begin(), FloatArgs.end()) << " " << Parameters << "\n";
}

void render_engine::RiMakeCubeFaceEnvironmentV(const string& px, const string& nx, const string& py, const string& ny, const string& pz, const string& nz, const string& texturename, const real fov, const string& swrap, const string& twrap, const string& filterfunc, const real swidth, const real twidth, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "MakeCubeFaceEnvironment " << format_string(px) << " " << format_string(nx) << " " << format_string(py) << " " << format_string(ny) << " " << format_string(pz) << " " << format_string(nz) << " " << format_string(texturename) << " ";
	m_implementation->m_stream << fov << " " << format_string(swrap) << " " << format_string(twrap) << " " << format_string(filterfunc) << " " << swidth << " " << twidth << " " << Parameters;
}

void render_engine::RiMakeLatLongEnvironmentV(const string& picturename, const string& texturename, const string& filterfunc, const real swidth, const real twidth, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "MakeLatLongEnvironment " << format_string(picturename) << " " << format_string(texturename) << " " << format_string(filterfunc) << " " << swidth << " " << twidth << " " << Parameters;
}

void render_engine::RiMakeShadowV(const string& picturename, const string& texturename, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "MakeShadow " << format_string(picturename) << " " << format_string(texturename) << " " << Parameters;
}

void render_engine::RiMakeTextureV(const string& picturename, const string& texturename, const string& swrap, const string& twrap, const string& filterfunc, const real swidth, const real twidth, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "MakeTexture " << format_string(picturename) << " " << format_string(texturename) << " " << format_string(swrap) << " " << format_string(twrap) << " " << format_string(filterfunc) << " " << swidth << " " << twidth << " " << Parameters;
}

void render_engine::RiBlobbyV(const unsigned_integer NLeaf, const unsigned_integers& Codes, const reals& Floats, const strings& Strings, const parameter_list& Parameters)
{
	m_implementation->m_stream << ::indentation << "Blobby " << NLeaf << " " << format_array(Codes.begin(), Codes.end()) << " " << format_array(Floats.begin(), Floats.end()) << " " << format_array(Strings.begin(), Strings.end()) << " " << Parameters << "\n";
}

parameter_list& operator+=(parameter_list& LHS, const parameter_list& RHS)
{
	LHS.insert(LHS.end(), RHS.begin(), RHS.end());
	return LHS;
}

parameter_list operator+(const parameter_list& LHS, const parameter_list& RHS)
{
	parameter_list results(LHS);
	results += RHS;

	return results;
}

} // namespace ri

} // namespace k3d


